about summary refs log tree commit diff
path: root/users
diff options
context:
space:
mode:
Diffstat (limited to 'users')
-rw-r--r--users/Profpatsch/.envrc5
-rw-r--r--users/Profpatsch/.gitignore1
-rw-r--r--users/Profpatsch/.hlint.yaml357
-rw-r--r--users/Profpatsch/.vscode/launch.json18
-rw-r--r--users/Profpatsch/.vscode/settings.json25
-rw-r--r--users/Profpatsch/OWNERS8
-rw-r--r--users/Profpatsch/README.md10
-rw-r--r--users/Profpatsch/alacritty.nix27
-rw-r--r--users/Profpatsch/aliases.nix88
-rw-r--r--users/Profpatsch/arglib/ArglibNetencode.hs22
-rw-r--r--users/Profpatsch/arglib/arglib-netencode.cabal65
-rw-r--r--users/Profpatsch/arglib/netencode.nix99
-rw-r--r--users/Profpatsch/atomically-write.nix29
-rw-r--r--users/Profpatsch/blog/README.md7
-rw-r--r--users/Profpatsch/blog/default.nix583
-rw-r--r--users/Profpatsch/blog/notes/an-idealized-conflang.md298
-rw-r--r--users/Profpatsch/blog/notes/private-trackers-are-markets.md46
-rw-r--r--users/Profpatsch/blog/notes/rust-string-conversions.md1
-rw-r--r--users/Profpatsch/blog/posts/2017-05-04-ligature-emulation-in-emacs.md123
-rw-r--r--users/Profpatsch/cabal.project14
-rw-r--r--users/Profpatsch/cas-serve/CasServe.hs247
-rw-r--r--users/Profpatsch/cas-serve/cas-serve.cabal73
-rw-r--r--users/Profpatsch/cas-serve/default.nix38
-rw-r--r--users/Profpatsch/cas-serve/schema.sql38
-rw-r--r--users/Profpatsch/cas-serve/wordlist.json1
-rw-r--r--users/Profpatsch/cas-serve/wordlist.sqlitebin0 -> 36864 bytes
-rw-r--r--users/Profpatsch/cdb.nix93
-rw-r--r--users/Profpatsch/declib/.eslintrc.json14
-rw-r--r--users/Profpatsch/declib/.gitignore6
-rw-r--r--users/Profpatsch/declib/.prettierrc8
-rw-r--r--users/Profpatsch/declib/README.md4
-rw-r--r--users/Profpatsch/declib/build.ninja16
-rw-r--r--users/Profpatsch/declib/index.ts245
-rw-r--r--users/Profpatsch/declib/package.json25
-rw-r--r--users/Profpatsch/declib/tsconfig.json25
-rw-r--r--users/Profpatsch/dhall/lib.dhall84
-rw-r--r--users/Profpatsch/emacs-tree-sitter-move/README.md5
-rw-r--r--users/Profpatsch/emacs-tree-sitter-move/default.nix4
-rw-r--r--users/Profpatsch/emacs-tree-sitter-move/shell.nix7
-rw-r--r--users/Profpatsch/exactSource.nix90
-rw-r--r--users/Profpatsch/execline/ExecHelpers.hs48
-rw-r--r--users/Profpatsch/execline/default.nix68
-rw-r--r--users/Profpatsch/execline/exec-helpers.cabal14
-rw-r--r--users/Profpatsch/execline/exec-helpers/Cargo.lock7
-rw-r--r--users/Profpatsch/execline/exec-helpers/Cargo.toml8
-rw-r--r--users/Profpatsch/execline/exec-helpers/default.nix6
-rw-r--r--users/Profpatsch/execline/exec-helpers/exec_helpers.rs (renamed from users/Profpatsch/execline/exec_helpers.rs)84
-rw-r--r--users/Profpatsch/fafo.jpgbin0 -> 26139 bytes
-rw-r--r--users/Profpatsch/git-db/default.nix10
-rw-r--r--users/Profpatsch/git-db/git-db.rs90
-rw-r--r--users/Profpatsch/haskell-module-deps/README.md5
-rw-r--r--users/Profpatsch/haskell-module-deps/default.nix55
-rw-r--r--users/Profpatsch/haskell-module-deps/example-output-dhall-haskell.pngbin0 -> 415873 bytes
-rw-r--r--users/Profpatsch/hie.yaml36
-rw-r--r--users/Profpatsch/htmx-experiment/Main.hs4
-rw-r--r--users/Profpatsch/htmx-experiment/default.nix46
-rw-r--r--users/Profpatsch/htmx-experiment/htmx-experiment.cabal89
-rw-r--r--users/Profpatsch/htmx-experiment/src/HtmxExperiment.hs377
-rw-r--r--users/Profpatsch/htmx-experiment/src/ServerErrors.hs244
-rw-r--r--users/Profpatsch/htmx-experiment/src/ValidationParseT.hs40
-rw-r--r--users/Profpatsch/httzip/Httzip.hs66
-rw-r--r--users/Profpatsch/httzip/default.nix40
-rw-r--r--users/Profpatsch/httzip/httzip.cabal73
-rw-r--r--users/Profpatsch/ical-smolify/IcalSmolify.hs124
-rw-r--r--users/Profpatsch/ical-smolify/README.md5
-rw-r--r--users/Profpatsch/ical-smolify/default.nix23
-rw-r--r--users/Profpatsch/ical-smolify/ical-smolify.cabal18
-rw-r--r--users/Profpatsch/imap-idle.nix23
-rw-r--r--users/Profpatsch/imap-idle.rs40
-rw-r--r--users/Profpatsch/importDhall.nix93
-rw-r--r--users/Profpatsch/ini/default.nix6
-rw-r--r--users/Profpatsch/ini/ini.dhall36
-rw-r--r--users/Profpatsch/jaeger.nix46
-rw-r--r--users/Profpatsch/jbovlaste-sqlite/JbovlasteSqlite.hs389
-rw-r--r--users/Profpatsch/jbovlaste-sqlite/default.nix33
-rw-r--r--users/Profpatsch/jbovlaste-sqlite/jbovlaste-sqlite.cabal76
-rw-r--r--users/Profpatsch/lens.nix137
-rw-r--r--users/Profpatsch/lib.nix98
-rw-r--r--users/Profpatsch/lorri-wait-for-eval/LorriWaitForEval.hs173
-rw-r--r--users/Profpatsch/lorri-wait-for-eval/README.md7
-rw-r--r--users/Profpatsch/lorri-wait-for-eval/default.nix20
-rw-r--r--users/Profpatsch/mailbox-org/MailboxOrg.hs523
-rw-r--r--users/Profpatsch/mailbox-org/README.md7
-rw-r--r--users/Profpatsch/mailbox-org/default.nix38
-rw-r--r--users/Profpatsch/mailbox-org/mailbox-org.cabal95
-rw-r--r--users/Profpatsch/mailbox-org/src/AesonQQ.hs24
-rw-r--r--users/Profpatsch/my-prelude/README.md42
-rw-r--r--users/Profpatsch/my-prelude/default.nix51
-rw-r--r--users/Profpatsch/my-prelude/my-prelude.cabal120
-rw-r--r--users/Profpatsch/my-prelude/src/Aeson.hs176
-rw-r--r--users/Profpatsch/my-prelude/src/AtLeast.hs51
-rw-r--r--users/Profpatsch/my-prelude/src/MyPrelude.hs776
-rw-r--r--users/Profpatsch/my-prelude/src/Parse.hs174
-rw-r--r--users/Profpatsch/my-prelude/src/Postgres/Decoder.hs94
-rw-r--r--users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs760
-rw-r--r--users/Profpatsch/my-prelude/src/Pretty.hs108
-rw-r--r--users/Profpatsch/my-prelude/src/Seconds.hs55
-rw-r--r--users/Profpatsch/my-prelude/src/Test.hs115
-rw-r--r--users/Profpatsch/my-prelude/src/Tool.hs75
-rw-r--r--users/Profpatsch/my-prelude/src/ValidationParseT.hs16
-rw-r--r--users/Profpatsch/my-webstuff/default.nix27
-rw-r--r--users/Profpatsch/my-webstuff/my-webstuff.cabal72
-rw-r--r--users/Profpatsch/my-webstuff/src/Multipart2.hs220
-rw-r--r--users/Profpatsch/my-xmonad/Xmonad.hs127
-rw-r--r--users/Profpatsch/my-xmonad/default.nix25
-rw-r--r--users/Profpatsch/my-xmonad/my-xmonad.cabal62
-rw-r--r--users/Profpatsch/netencode/Netencode.hs433
-rw-r--r--users/Profpatsch/netencode/Netencode/Parse.hs102
-rw-r--r--users/Profpatsch/netencode/README.md28
-rw-r--r--users/Profpatsch/netencode/default.nix149
-rw-r--r--users/Profpatsch/netencode/gen.nix40
-rw-r--r--users/Profpatsch/netencode/netencode-mustache.rs29
-rw-r--r--users/Profpatsch/netencode/netencode.cabal74
-rw-r--r--users/Profpatsch/netencode/netencode.rs627
-rw-r--r--users/Profpatsch/netencode/pretty.rs73
-rw-r--r--users/Profpatsch/netstring/default.nix29
-rw-r--r--users/Profpatsch/netstring/tests/default.nix29
-rw-r--r--users/Profpatsch/nix-home/README.md7
-rw-r--r--users/Profpatsch/nix-home/default.nix212
-rw-r--r--users/Profpatsch/nix-tools.nix159
-rw-r--r--users/Profpatsch/nixpkgs-rewriter/MetaStdenvLib.hs80
-rw-r--r--users/Profpatsch/nixpkgs-rewriter/default.nix112
-rw-r--r--users/Profpatsch/openlab-tools/Main.hs6
-rw-r--r--users/Profpatsch/openlab-tools/default.nix69
-rw-r--r--users/Profpatsch/openlab-tools/openlab-tools.cabal111
-rw-r--r--users/Profpatsch/openlab-tools/src/OpenlabTools.hs551
-rw-r--r--users/Profpatsch/read-http.nix25
-rw-r--r--users/Profpatsch/read-http.rs154
-rw-r--r--users/Profpatsch/reverse-haskell-deps.nix26
-rw-r--r--users/Profpatsch/reverse-haskell-deps/README.md3
-rw-r--r--users/Profpatsch/reverse-haskell-deps/ReverseHaskellDeps.hs (renamed from users/Profpatsch/reverse-haskell-deps.hs)72
-rw-r--r--users/Profpatsch/reverse-haskell-deps/default.nix32
-rw-r--r--users/Profpatsch/reverse-haskell-deps/reverse-haskell-deps.cabal16
-rw-r--r--users/Profpatsch/shell.nix110
-rw-r--r--users/Profpatsch/shortcuttable/default.nix172
-rw-r--r--users/Profpatsch/struct-edit/default.nix13
-rw-r--r--users/Profpatsch/struct-edit/main.go431
-rw-r--r--users/Profpatsch/sync-abfall-ics-aichach-friedberg/README.md3
-rw-r--r--users/Profpatsch/sync-abfall-ics-aichach-friedberg/default.nix31
-rw-r--r--users/Profpatsch/sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall139
-rw-r--r--users/Profpatsch/sync-abfall-ics-aichach-friedberg/sync-ics-to-dir.py133
-rw-r--r--users/Profpatsch/tagtime/README.md18
-rw-r--r--users/Profpatsch/toINI.nix79
-rw-r--r--users/Profpatsch/tree-sitter.nix176
-rw-r--r--users/Profpatsch/whatcd-resolver/Main.hs6
-rw-r--r--users/Profpatsch/whatcd-resolver/README.md21
-rw-r--r--users/Profpatsch/whatcd-resolver/build.ninja20
-rw-r--r--users/Profpatsch/whatcd-resolver/default.nix76
-rw-r--r--users/Profpatsch/whatcd-resolver/notes.org48
-rw-r--r--users/Profpatsch/whatcd-resolver/server-notes.org2
-rw-r--r--users/Profpatsch/whatcd-resolver/services/.gitignore3
-rwxr-xr-xusers/Profpatsch/whatcd-resolver/services/jaeger/run3
-rwxr-xr-xusers/Profpatsch/whatcd-resolver/services/reverse-proxy/run2
-rw-r--r--users/Profpatsch/whatcd-resolver/src/AppT.hs151
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Html.hs69
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Http.hs129
-rw-r--r--users/Profpatsch/whatcd-resolver/src/JsonLd.hs138
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Optional.hs18
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Redacted.hs537
-rw-r--r--users/Profpatsch/whatcd-resolver/src/Transmission.hs306
-rw-r--r--users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs698
-rw-r--r--users/Profpatsch/whatcd-resolver/whatcd-resolver.cabal121
-rw-r--r--users/Profpatsch/writers/default.nix140
-rw-r--r--users/Profpatsch/writers/tests/default.nix42
-rw-r--r--users/Profpatsch/ytextr/README.md5
-rw-r--r--users/Profpatsch/ytextr/create-symlink-farm.nix19
-rw-r--r--users/Profpatsch/ytextr/default.nix82
-rw-r--r--users/aaqaishtyaq/OWNERS3
-rw-r--r--users/aspen/OWNERS3
-rw-r--r--users/aspen/achilles/.envrc2
-rw-r--r--users/aspen/achilles/.gitignore (renamed from users/grfn/achilles/.gitignore)0
-rw-r--r--users/aspen/achilles/Cargo.lock (renamed from users/grfn/achilles/Cargo.lock)407
-rw-r--r--users/aspen/achilles/Cargo.toml (renamed from users/grfn/achilles/Cargo.toml)0
-rw-r--r--users/aspen/achilles/ach/.gitignore (renamed from users/grfn/achilles/ach/.gitignore)0
-rw-r--r--users/aspen/achilles/ach/Makefile (renamed from users/grfn/achilles/ach/Makefile)0
-rw-r--r--users/aspen/achilles/ach/externs.ach (renamed from users/grfn/achilles/ach/externs.ach)0
-rw-r--r--users/aspen/achilles/ach/functions.ach (renamed from users/grfn/achilles/ach/functions.ach)0
-rw-r--r--users/aspen/achilles/ach/simple.ach (renamed from users/grfn/achilles/ach/simple.ach)0
-rw-r--r--users/aspen/achilles/ach/units.ach (renamed from users/grfn/achilles/ach/units.ach)0
-rw-r--r--users/aspen/achilles/default.nix (renamed from users/grfn/achilles/default.nix)5
-rw-r--r--users/aspen/achilles/shell.nix (renamed from users/grfn/achilles/shell.nix)2
-rw-r--r--users/aspen/achilles/src/ast/hir.rs (renamed from users/grfn/achilles/src/ast/hir.rs)0
-rw-r--r--users/aspen/achilles/src/ast/mod.rs (renamed from users/grfn/achilles/src/ast/mod.rs)0
-rw-r--r--users/aspen/achilles/src/codegen/llvm.rs (renamed from users/grfn/achilles/src/codegen/llvm.rs)0
-rw-r--r--users/aspen/achilles/src/codegen/mod.rs (renamed from users/grfn/achilles/src/codegen/mod.rs)0
-rw-r--r--users/aspen/achilles/src/commands/check.rs (renamed from users/grfn/achilles/src/commands/check.rs)0
-rw-r--r--users/aspen/achilles/src/commands/compile.rs (renamed from users/grfn/achilles/src/commands/compile.rs)0
-rw-r--r--users/aspen/achilles/src/commands/eval.rs (renamed from users/grfn/achilles/src/commands/eval.rs)6
-rw-r--r--users/aspen/achilles/src/commands/mod.rs (renamed from users/grfn/achilles/src/commands/mod.rs)0
-rw-r--r--users/aspen/achilles/src/common/env.rs (renamed from users/grfn/achilles/src/common/env.rs)0
-rw-r--r--users/aspen/achilles/src/common/error.rs (renamed from users/grfn/achilles/src/common/error.rs)0
-rw-r--r--users/aspen/achilles/src/common/mod.rs (renamed from users/grfn/achilles/src/common/mod.rs)0
-rw-r--r--users/aspen/achilles/src/common/namer.rs (renamed from users/grfn/achilles/src/common/namer.rs)0
-rw-r--r--users/aspen/achilles/src/compiler.rs (renamed from users/grfn/achilles/src/compiler.rs)0
-rw-r--r--users/aspen/achilles/src/interpreter/error.rs (renamed from users/grfn/achilles/src/interpreter/error.rs)0
-rw-r--r--users/aspen/achilles/src/interpreter/mod.rs (renamed from users/grfn/achilles/src/interpreter/mod.rs)0
-rw-r--r--users/aspen/achilles/src/interpreter/value.rs (renamed from users/grfn/achilles/src/interpreter/value.rs)0
-rw-r--r--users/aspen/achilles/src/main.rs (renamed from users/grfn/achilles/src/main.rs)0
-rw-r--r--users/aspen/achilles/src/parser/expr.rs (renamed from users/grfn/achilles/src/parser/expr.rs)3
-rw-r--r--users/aspen/achilles/src/parser/macros.rs (renamed from users/grfn/achilles/src/parser/macros.rs)0
-rw-r--r--users/aspen/achilles/src/parser/mod.rs (renamed from users/grfn/achilles/src/parser/mod.rs)0
-rw-r--r--users/aspen/achilles/src/parser/type_.rs (renamed from users/grfn/achilles/src/parser/type_.rs)0
-rw-r--r--users/aspen/achilles/src/parser/util.rs (renamed from users/grfn/achilles/src/parser/util.rs)0
-rw-r--r--users/aspen/achilles/src/passes/hir/mod.rs (renamed from users/grfn/achilles/src/passes/hir/mod.rs)0
-rw-r--r--users/aspen/achilles/src/passes/hir/monomorphize.rs (renamed from users/grfn/achilles/src/passes/hir/monomorphize.rs)0
-rw-r--r--users/aspen/achilles/src/passes/hir/strip_positive_units.rs (renamed from users/grfn/achilles/src/passes/hir/strip_positive_units.rs)0
-rw-r--r--users/aspen/achilles/src/passes/mod.rs (renamed from users/grfn/achilles/src/passes/mod.rs)0
-rw-r--r--users/aspen/achilles/src/tc/mod.rs (renamed from users/grfn/achilles/src/tc/mod.rs)0
-rw-r--r--users/aspen/achilles/tests/compile.rs (renamed from users/grfn/achilles/tests/compile.rs)0
-rw-r--r--users/aspen/bbbg/.clj-kondo/config.edn1
-rw-r--r--users/aspen/bbbg/.envrc (renamed from users/grfn/achilles/.envrc)0
-rw-r--r--users/aspen/bbbg/.gitignore9
-rw-r--r--users/aspen/bbbg/Makefile2
-rw-r--r--users/aspen/bbbg/README.md129
-rw-r--r--users/aspen/bbbg/arion-compose.nix15
-rw-r--r--users/aspen/bbbg/arion-pkgs.nix2
-rw-r--r--users/aspen/bbbg/default.nix82
-rw-r--r--users/aspen/bbbg/deps.edn70
-rw-r--r--users/aspen/bbbg/deps.nix1494
-rw-r--r--users/aspen/bbbg/env/dev/bbbg-signup/env.clj3
-rw-r--r--users/aspen/bbbg/env/dev/logback.xml15
-rw-r--r--users/aspen/bbbg/env/prod/bbbg-signup/env.clj3
-rw-r--r--users/aspen/bbbg/env/prod/logback.xml31
-rw-r--r--users/aspen/bbbg/env/test/bbbg-signup/env.clj3
-rw-r--r--users/aspen/bbbg/env/test/logback.xml11
-rw-r--r--users/aspen/bbbg/module.nix137
-rw-r--r--users/aspen/bbbg/pom.xml42
-rw-r--r--users/aspen/bbbg/resources/base.css152
-rw-r--r--users/aspen/bbbg/resources/migrations/20211212165646-init-schema.down.sql14
-rw-r--r--users/aspen/bbbg/resources/migrations/20211212165646-init-schema.up.sql32
-rw-r--r--users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql1
-rw-r--r--users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql7
-rw-r--r--users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql1
-rw-r--r--users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql2
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woffbin0 -> 23576 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2bin0 -> 19272 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woffbin0 -> 24056 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2bin0 -> 19624 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woffbin0 -> 23628 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2bin0 -> 19264 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woffbin0 -> 23872 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2bin0 -> 19440 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woffbin0 -> 24404 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2bin0 -> 19836 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woffbin0 -> 24012 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2bin0 -> 19660 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woffbin0 -> 23480 bytes
-rw-r--r--users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2bin0 -> 19172 bytes
-rw-r--r--users/aspen/bbbg/resources/public/main.js73
-rw-r--r--users/aspen/bbbg/resources/public/robots.txt2
-rw-r--r--users/aspen/bbbg/shell.nix29
-rw-r--r--users/aspen/bbbg/src/bbbg/attendee.clj10
-rw-r--r--users/aspen/bbbg/src/bbbg/attendee_check.clj4
-rw-r--r--users/aspen/bbbg/src/bbbg/core.clj69
-rw-r--r--users/aspen/bbbg/src/bbbg/db.clj366
-rw-r--r--users/aspen/bbbg/src/bbbg/db/attendee.clj85
-rw-r--r--users/aspen/bbbg/src/bbbg/db/attendee_check.clj55
-rw-r--r--users/aspen/bbbg/src/bbbg/db/event.clj94
-rw-r--r--users/aspen/bbbg/src/bbbg/db/event_attendee.clj17
-rw-r--r--users/aspen/bbbg/src/bbbg/db/user.clj19
-rw-r--r--users/aspen/bbbg/src/bbbg/discord.clj44
-rw-r--r--users/aspen/bbbg/src/bbbg/discord/auth.clj90
-rw-r--r--users/aspen/bbbg/src/bbbg/event.clj4
-rw-r--r--users/aspen/bbbg/src/bbbg/event_attendee.clj6
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/attendee_checks.clj68
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/attendees.clj162
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/core.clj91
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/events.clj259
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/home.clj52
-rw-r--r--users/aspen/bbbg/src/bbbg/handlers/signup_form.clj93
-rw-r--r--users/aspen/bbbg/src/bbbg/meetup/import.clj125
-rw-r--r--users/aspen/bbbg/src/bbbg/meetup_user.clj6
-rw-r--r--users/aspen/bbbg/src/bbbg/styles.clj407
-rw-r--r--users/aspen/bbbg/src/bbbg/user.clj8
-rw-r--r--users/aspen/bbbg/src/bbbg/util/core.clj138
-rw-r--r--users/aspen/bbbg/src/bbbg/util/dev_secrets.clj59
-rw-r--r--users/aspen/bbbg/src/bbbg/util/display.clj23
-rw-r--r--users/aspen/bbbg/src/bbbg/util/spec.clj16
-rw-r--r--users/aspen/bbbg/src/bbbg/util/sql.clj5
-rw-r--r--users/aspen/bbbg/src/bbbg/util/time.clj152
-rw-r--r--users/aspen/bbbg/src/bbbg/views/flash.clj39
-rw-r--r--users/aspen/bbbg/src/bbbg/web.clj140
-rw-r--r--users/aspen/bbbg/test/bbbg/meetup/import_test.clj7
-rw-r--r--users/aspen/bbbg/tf.nix96
-rw-r--r--users/aspen/emacs.d/+bindings.el (renamed from users/grfn/emacs.d/+bindings.el)20
-rw-r--r--users/aspen/emacs.d/+commands.el (renamed from users/grfn/emacs.d/+commands.el)0
-rw-r--r--users/aspen/emacs.d/+private.el.gpg (renamed from users/grfn/emacs.d/+private.el.gpg)bin1115 -> 1115 bytes
-rw-r--r--users/aspen/emacs.d/.gitignore (renamed from users/grfn/emacs.d/.gitignore)0
-rw-r--r--users/aspen/emacs.d/autoload/evil.el (renamed from users/grfn/emacs.d/autoload/evil.el)0
-rw-r--r--users/aspen/emacs.d/autoload/hlissner.el (renamed from users/grfn/emacs.d/autoload/hlissner.el)0
-rw-r--r--users/aspen/emacs.d/clocked-in-elt.el (renamed from users/grfn/emacs.d/clocked-in-elt.el)0
-rw-r--r--users/aspen/emacs.d/clojure.el (renamed from users/grfn/emacs.d/clojure.el)1
-rw-r--r--users/aspen/emacs.d/company-sql.el (renamed from users/grfn/emacs.d/company-sql.el)0
-rw-r--r--users/aspen/emacs.d/config.el (renamed from users/grfn/emacs.d/config.el)179
-rw-r--r--users/aspen/emacs.d/cpp.el (renamed from users/grfn/emacs.d/cpp.el)10
-rw-r--r--users/aspen/emacs.d/email.el (renamed from users/grfn/emacs.d/email.el)11
-rw-r--r--users/aspen/emacs.d/github-org.el (renamed from users/grfn/emacs.d/github-org.el)0
-rw-r--r--users/aspen/emacs.d/google-c-style.el (renamed from users/grfn/emacs.d/google-c-style.el)0
-rw-r--r--users/aspen/emacs.d/grid.el (renamed from users/grfn/emacs.d/grid.el)0
-rw-r--r--users/aspen/emacs.d/init.el (renamed from users/grfn/emacs.d/init.el)95
-rw-r--r--users/aspen/emacs.d/irc.el (renamed from users/grfn/emacs.d/irc.el)0
-rw-r--r--users/aspen/emacs.d/lisp.el (renamed from users/grfn/emacs.d/lisp.el)0
-rwxr-xr-xusers/aspen/emacs.d/nix-clangd.sh (renamed from users/grfn/emacs.d/nix-clangd.sh)0
-rw-r--r--users/aspen/emacs.d/nix.el (renamed from users/grfn/emacs.d/nix.el)0
-rw-r--r--users/aspen/emacs.d/org-alerts.el (renamed from users/grfn/emacs.d/org-alerts.el)0
-rw-r--r--users/aspen/emacs.d/org-config.el (renamed from users/grfn/emacs.d/org-config.el)62
-rw-r--r--users/aspen/emacs.d/org-gcal.el (renamed from users/grfn/emacs.d/org-gcal.el)0
-rw-r--r--users/aspen/emacs.d/org-query.el (renamed from users/grfn/emacs.d/org-query.el)51
-rw-r--r--users/aspen/emacs.d/packages.el (renamed from users/grfn/emacs.d/packages.el)4
-rw-r--r--users/aspen/emacs.d/rust.el (renamed from users/grfn/emacs.d/rust.el)13
-rw-r--r--users/aspen/emacs.d/show-matching-paren.el (renamed from users/grfn/emacs.d/show-matching-paren.el)0
-rw-r--r--users/aspen/emacs.d/slack-snippets.el (renamed from users/grfn/emacs.d/slack-snippets.el)0
-rw-r--r--users/aspen/emacs.d/slack.el (renamed from users/grfn/emacs.d/slack.el)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/annotation (renamed from users/grfn/emacs.d/snippets/haskell-mode/annotation)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/benchmark-module (renamed from users/grfn/emacs.d/snippets/haskell-mode/benchmark-module)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/header (renamed from users/grfn/emacs.d/snippets/haskell-mode/header)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/hedgehog-generator (renamed from users/grfn/emacs.d/snippets/haskell-mode/hedgehog-generator)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/hedgehog-property (renamed from users/grfn/emacs.d/snippets/haskell-mode/hedgehog-property)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/hlint (renamed from users/grfn/emacs.d/snippets/haskell-mode/hlint)2
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/import-i (renamed from users/grfn/emacs.d/snippets/haskell-mode/import-i)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/inl (renamed from users/grfn/emacs.d/snippets/haskell-mode/inl)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/inline (renamed from users/grfn/emacs.d/snippets/haskell-mode/inline)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/language pragma (renamed from users/grfn/emacs.d/snippets/haskell-mode/language pragma)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/lens.field (renamed from users/grfn/emacs.d/snippets/haskell-mode/lens.field)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/module (renamed from users/grfn/emacs.d/snippets/haskell-mode/module)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/shut up, hlint (renamed from users/grfn/emacs.d/snippets/haskell-mode/shut up, hlint)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/test-group (renamed from users/grfn/emacs.d/snippets/haskell-mode/test-group)2
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/test-module (renamed from users/grfn/emacs.d/snippets/haskell-mode/test-module)0
-rw-r--r--users/aspen/emacs.d/snippets/haskell-mode/undefined (renamed from users/grfn/emacs.d/snippets/haskell-mode/undefined)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/action-type (renamed from users/grfn/emacs.d/snippets/js2-mode/action-type)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/before (renamed from users/grfn/emacs.d/snippets/js2-mode/before)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/context (renamed from users/grfn/emacs.d/snippets/js2-mode/context)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/describe (renamed from users/grfn/emacs.d/snippets/js2-mode/describe)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/expect (renamed from users/grfn/emacs.d/snippets/js2-mode/expect)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/function (renamed from users/grfn/emacs.d/snippets/js2-mode/function)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/header (renamed from users/grfn/emacs.d/snippets/js2-mode/header)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/it (renamed from users/grfn/emacs.d/snippets/js2-mode/it)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/it-pending (renamed from users/grfn/emacs.d/snippets/js2-mode/it-pending)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/module (renamed from users/grfn/emacs.d/snippets/js2-mode/module)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/record (renamed from users/grfn/emacs.d/snippets/js2-mode/record)0
-rw-r--r--users/aspen/emacs.d/snippets/js2-mode/test (renamed from users/grfn/emacs.d/snippets/js2-mode/test)0
-rw-r--r--users/aspen/emacs.d/snippets/nix-mode/fetchFromGitHub (renamed from users/grfn/emacs.d/snippets/nix-mode/fetchFromGitHub)2
-rw-r--r--users/aspen/emacs.d/snippets/nix-mode/pythonPackage (renamed from users/grfn/emacs.d/snippets/nix-mode/pythonPackage)0
-rw-r--r--users/aspen/emacs.d/snippets/nix-mode/sha256 (renamed from users/grfn/emacs.d/snippets/nix-mode/sha256)2
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/SQL source block (renamed from users/grfn/emacs.d/snippets/org-mode/SQL source block)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/combat (renamed from users/grfn/emacs.d/snippets/org-mode/combat)2
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/date (renamed from users/grfn/emacs.d/snippets/org-mode/date)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/date-time (renamed from users/grfn/emacs.d/snippets/org-mode/date-time)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/description (renamed from users/grfn/emacs.d/snippets/org-mode/description)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/nologdone (renamed from users/grfn/emacs.d/snippets/org-mode/nologdone)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/python source block (renamed from users/grfn/emacs.d/snippets/org-mode/python source block)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/reveal (renamed from users/grfn/emacs.d/snippets/org-mode/reveal)0
-rw-r--r--users/aspen/emacs.d/snippets/org-mode/transaction (renamed from users/grfn/emacs.d/snippets/org-mode/transaction)0
-rw-r--r--users/aspen/emacs.d/snippets/prolog-mode/use-module7
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/add_column (renamed from users/grfn/emacs.d/snippets/python-mode/add_column)0
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/decorate (renamed from users/grfn/emacs.d/snippets/python-mode/decorate)2
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/dunder (renamed from users/grfn/emacs.d/snippets/python-mode/dunder)2
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/name (renamed from users/grfn/emacs.d/snippets/python-mode/name)2
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/op.get_bind.execute (renamed from users/grfn/emacs.d/snippets/python-mode/op.get_bind.execute)0
-rw-r--r--users/aspen/emacs.d/snippets/python-mode/pdb (renamed from users/grfn/emacs.d/snippets/python-mode/pdb)2
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/#[macro_use] (renamed from users/grfn/emacs.d/snippets/rust-mode/#[macro_use])0
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/async test10
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/benchmark (renamed from users/grfn/emacs.d/snippets/rust-mode/benchmark)2
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/proptest (renamed from users/grfn/emacs.d/snippets/rust-mode/proptest)2
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/test-module11
-rw-r--r--users/aspen/emacs.d/snippets/rust-mode/tests (renamed from users/grfn/emacs.d/snippets/rust-mode/tests)0
-rw-r--r--users/aspen/emacs.d/snippets/snippet-mode/indent (renamed from users/grfn/emacs.d/snippets/snippet-mode/indent)0
-rw-r--r--users/aspen/emacs.d/snippets/sql-mode/count(*) group by (renamed from users/grfn/emacs.d/snippets/sql-mode/count(*) group by)0
-rw-r--r--users/aspen/emacs.d/snippets/terraform-mode/variable (renamed from users/grfn/emacs.d/snippets/terraform-mode/variable)2
-rw-r--r--users/aspen/emacs.d/snippets/text-mode/date (renamed from users/grfn/emacs.d/snippets/text-mode/date)0
-rw-r--r--users/aspen/emacs.d/splitjoin.el (renamed from users/grfn/emacs.d/splitjoin.el)0
-rw-r--r--users/aspen/emacs.d/sql-strings.el (renamed from users/grfn/emacs.d/sql-strings.el)0
-rw-r--r--users/aspen/emacs.d/terraform.el (renamed from users/grfn/emacs.d/terraform.el)0
-rw-r--r--users/aspen/emacs.d/tests/splitjoin_test.el (renamed from users/grfn/emacs.d/tests/splitjoin_test.el)0
-rw-r--r--users/aspen/emacs.d/themes/grfn-solarized-light-theme.el (renamed from users/grfn/emacs.d/themes/grfn-solarized-light-theme.el)0
-rw-r--r--users/aspen/emacs.d/utils.el (renamed from users/grfn/emacs.d/utils.el)0
-rw-r--r--users/aspen/emacs.d/vterm.el (renamed from users/grfn/emacs.d/vterm.el)0
-rw-r--r--users/aspen/emacs/.gitignore2
-rw-r--r--users/aspen/emacs/config.org1393
-rw-r--r--users/aspen/emacs/init.el199
-rw-r--r--users/aspen/emacs/org-config.el141
-rw-r--r--users/aspen/emacs/packages.el14
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/annotation5
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/benchmark-module26
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/header5
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/hedgehog-generator8
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/hedgehog-property9
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/hlint8
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/import-i4
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/inl6
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/inline5
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/language pragma6
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/lens.field7
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/module32
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/shut up, hlint6
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/test-group9
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/test-module27
-rw-r--r--users/aspen/emacs/snippets/haskell-mode/undefined6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/action-type4
-rw-r--r--users/aspen/emacs/snippets/js2-mode/before7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/context7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/describe6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/expect5
-rw-r--r--users/aspen/emacs/snippets/js2-mode/function6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/header6
-rw-r--r--users/aspen/emacs/snippets/js2-mode/it7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/it-pending5
-rw-r--r--users/aspen/emacs/snippets/js2-mode/module12
-rw-r--r--users/aspen/emacs/snippets/js2-mode/record7
-rw-r--r--users/aspen/emacs/snippets/js2-mode/test7
-rw-r--r--users/aspen/emacs/snippets/nix-mode/fetchFromGitHub12
-rw-r--r--users/aspen/emacs/snippets/nix-mode/pythonPackage16
-rw-r--r--users/aspen/emacs/snippets/nix-mode/sha2567
-rw-r--r--users/aspen/emacs/snippets/org-mode/SQL source block6
-rw-r--r--users/aspen/emacs/snippets/org-mode/combat13
-rw-r--r--users/aspen/emacs/snippets/org-mode/date5
-rw-r--r--users/aspen/emacs/snippets/org-mode/date-time5
-rw-r--r--users/aspen/emacs/snippets/org-mode/description7
-rw-r--r--users/aspen/emacs/snippets/org-mode/nologdone5
-rw-r--r--users/aspen/emacs/snippets/org-mode/python source block6
-rw-r--r--users/aspen/emacs/snippets/org-mode/reveal6
-rw-r--r--users/aspen/emacs/snippets/org-mode/transaction7
-rw-r--r--users/aspen/emacs/snippets/prolog-mode/tests11
-rw-r--r--users/aspen/emacs/snippets/prolog-mode/use-module7
-rw-r--r--users/aspen/emacs/snippets/python-mode/add_column5
-rw-r--r--users/aspen/emacs/snippets/python-mode/decorate15
-rw-r--r--users/aspen/emacs/snippets/python-mode/dunder7
-rw-r--r--users/aspen/emacs/snippets/python-mode/name7
-rw-r--r--users/aspen/emacs/snippets/python-mode/op.get_bind.execute7
-rw-r--r--users/aspen/emacs/snippets/python-mode/pdb7
-rw-r--r--users/aspen/emacs/snippets/rust-mode/#[macro_use]5
-rw-r--r--users/aspen/emacs/snippets/rust-mode/async test10
-rw-r--r--users/aspen/emacs/snippets/rust-mode/benchmark10
-rw-r--r--users/aspen/emacs/snippets/rust-mode/proptest10
-rw-r--r--users/aspen/emacs/snippets/rust-mode/test-module11
-rw-r--r--users/aspen/emacs/snippets/rust-mode/tests9
-rw-r--r--users/aspen/emacs/snippets/snippet-mode/indent5
-rw-r--r--users/aspen/emacs/snippets/sql-mode/count(*) group by5
-rw-r--r--users/aspen/emacs/snippets/terraform-mode/variable11
-rw-r--r--users/aspen/emacs/snippets/text-mode/date5
-rw-r--r--users/aspen/emacs/snippets/tuareg-mode/expect-test9
-rw-r--r--users/aspen/emacs/snippets/tuareg-mode/module9
-rw-r--r--users/aspen/emacs/snippets/tuareg-mode/test-module10
-rw-r--r--users/aspen/goodcry-band/flower-icon.svg1
-rw-r--r--users/aspen/goodcry-band/flower-leaves.svg1
-rw-r--r--users/aspen/goodcry-band/flower.pngbin0 -> 285590 bytes
-rw-r--r--users/aspen/goodcry-band/index.css170
-rw-r--r--users/aspen/goodcry-band/index.html68
-rw-r--r--users/aspen/goodcry-band/reset.css45
-rw-r--r--users/aspen/keyboard/.gitignore (renamed from users/grfn/keyboard/.gitignore)0
-rw-r--r--users/aspen/keyboard/README.org (renamed from users/grfn/keyboard/README.org)0
-rw-r--r--users/aspen/keyboard/default.nix (renamed from users/grfn/keyboard/default.nix)12
-rwxr-xr-xusers/aspen/keyboard/flash2
-rw-r--r--users/aspen/keyboard/increase-tapping-delay.patch (renamed from users/grfn/keyboard/increase-tapping-delay.patch)0
-rw-r--r--users/aspen/keyboard/keymap.c (renamed from users/grfn/keyboard/keymap.c)8
-rw-r--r--users/aspen/keys.nix (renamed from users/grfn/keys.nix)3
-rw-r--r--users/aspen/org-clubhouse/.gitignore (renamed from users/grfn/org-clubhouse/.gitignore)0
-rw-r--r--users/aspen/org-clubhouse/CODE_OF_CONDUCT.org (renamed from users/grfn/org-clubhouse/CODE_OF_CONDUCT.org)0
-rw-r--r--users/aspen/org-clubhouse/LICENSE (renamed from users/grfn/org-clubhouse/LICENSE)0
-rw-r--r--users/aspen/org-clubhouse/README.org (renamed from users/grfn/org-clubhouse/README.org)0
-rw-r--r--users/aspen/org-clubhouse/org-clubhouse.el (renamed from users/grfn/org-clubhouse/org-clubhouse.el)0
-rw-r--r--users/aspen/pkgs/cargo-hakari.nix27
-rw-r--r--users/aspen/pkgs/cargo-nextest.nix27
-rw-r--r--users/aspen/pkgs/notmuch-extract-patch.nix18
-rw-r--r--users/aspen/pkgs/py3status.nix12
-rw-r--r--users/aspen/resume/chimera.png (renamed from users/grfn/resume/chimera.png)bin40602 -> 40602 bytes
-rw-r--r--users/aspen/resume/collection.sty (renamed from users/grfn/resume/collection.sty)0
-rw-r--r--users/aspen/resume/default.nix40
-rw-r--r--users/aspen/resume/helvetica.sty (renamed from users/grfn/resume/helvetica.sty)0
-rw-r--r--users/aspen/resume/moderncv.cls (renamed from users/grfn/resume/moderncv.cls)3
-rw-r--r--users/aspen/resume/moderncvcolorblack.sty (renamed from users/grfn/resume/moderncvcolorblack.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorblue.sty (renamed from users/grfn/resume/moderncvcolorblue.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorgreen.sty (renamed from users/grfn/resume/moderncvcolorgreen.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorgrey.sty (renamed from users/grfn/resume/moderncvcolorgrey.sty)0
-rw-r--r--users/aspen/resume/moderncvcolororange.sty (renamed from users/grfn/resume/moderncvcolororange.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorpurple.sty (renamed from users/grfn/resume/moderncvcolorpurple.sty)0
-rw-r--r--users/aspen/resume/moderncvcolorred.sty (renamed from users/grfn/resume/moderncvcolorred.sty)0
-rw-r--r--users/aspen/resume/moderncvcompatibility.sty (renamed from users/grfn/resume/moderncvcompatibility.sty)0
-rw-r--r--users/aspen/resume/moderncviconsletters.sty (renamed from users/grfn/resume/moderncviconsletters.sty)0
-rw-r--r--users/aspen/resume/moderncviconsmarvosym.sty (renamed from users/grfn/resume/moderncviconsmarvosym.sty)0
-rw-r--r--users/aspen/resume/moderncvstylebanking.sty (renamed from users/grfn/resume/moderncvstylebanking.sty)0
-rw-r--r--users/aspen/resume/moderncvstylecasual.sty (renamed from users/grfn/resume/moderncvstylecasual.sty)3
-rw-r--r--users/aspen/resume/moderncvstyleclassic.sty (renamed from users/grfn/resume/moderncvstyleclassic.sty)0
-rw-r--r--users/aspen/resume/moderncvstyleempty.sty (renamed from users/grfn/resume/moderncvstyleempty.sty)0
-rw-r--r--users/aspen/resume/moderncvstyleoldstyle.sty (renamed from users/grfn/resume/moderncvstyleoldstyle.sty)0
-rw-r--r--users/aspen/resume/picture.png (renamed from users/grfn/resume/picture.png)bin14848 -> 14848 bytes
-rw-r--r--users/aspen/resume/resume.tex (renamed from users/grfn/resume/resume.tex)118
-rw-r--r--users/aspen/resume/tweaklist.sty (renamed from users/grfn/resume/tweaklist.sty)0
-rw-r--r--users/aspen/secrets/.envrc1
-rw-r--r--users/aspen/secrets/bbbg.agebin0 -> 733 bytes
-rw-r--r--users/aspen/secrets/buildkite-ssh-key.agebin0 -> 3883 bytes
-rw-r--r--users/aspen/secrets/buildkite-token.agebin0 -> 623 bytes
-rw-r--r--users/aspen/secrets/cloudflare.age9
-rw-r--r--users/aspen/secrets/ddclient-password.agebin0 -> 429 bytes
-rw-r--r--users/aspen/secrets/default.nix2
-rw-r--r--users/aspen/secrets/secrets.nix15
-rw-r--r--users/aspen/secrets/shell.nix8
-rw-r--r--users/aspen/secrets/windtunnel-bot-github-token.age11
-rw-r--r--users/aspen/system/.gitignore (renamed from users/grfn/system/.gitignore)0
-rw-r--r--users/aspen/system/home/.skip-subtree (renamed from users/grfn/system/home/.skip-subtree)0
-rw-r--r--users/aspen/system/home/common/solarized.nix18
-rw-r--r--users/aspen/system/home/default.nix (renamed from users/grfn/system/home/default.nix)14
-rw-r--r--users/aspen/system/home/home.nix (renamed from users/grfn/system/home/home.nix)0
-rw-r--r--users/aspen/system/home/machines/dobharchu.nix (renamed from users/grfn/system/home/machines/dobharchu.nix)5
-rw-r--r--users/aspen/system/home/machines/lusca.nix34
-rw-r--r--users/aspen/system/home/machines/ogopogo.nix78
-rw-r--r--users/aspen/system/home/machines/roswell.nix (renamed from users/grfn/system/home/machines/roswell.nix)19
-rw-r--r--users/aspen/system/home/machines/yeren.nix (renamed from users/grfn/system/home/machines/yeren.nix)36
-rw-r--r--users/aspen/system/home/modules/.gitignore1
-rw-r--r--users/aspen/system/home/modules/alacritty.nix (renamed from users/grfn/system/home/modules/alacritty.nix)6
-rw-r--r--users/aspen/system/home/modules/common.nix88
-rw-r--r--users/aspen/system/home/modules/desktop.nix40
-rw-r--r--users/aspen/system/home/modules/development.nix (renamed from users/grfn/system/home/modules/development.nix)55
-rw-r--r--users/aspen/system/home/modules/development/agda.nix (renamed from users/grfn/system/home/modules/development/agda.nix)6
-rw-r--r--users/aspen/system/home/modules/development/kube.nix (renamed from users/grfn/system/home/modules/development/kube.nix)2
-rw-r--r--users/aspen/system/home/modules/development/ocaml.nix17
-rw-r--r--users/aspen/system/home/modules/development/readyset.nix39
-rw-r--r--users/aspen/system/home/modules/development/rust.nix (renamed from users/grfn/system/home/modules/development/rust.nix)25
-rw-r--r--users/aspen/system/home/modules/emacs.nix (renamed from users/grfn/system/home/modules/emacs.nix)32
-rw-r--r--users/aspen/system/home/modules/email.nix86
-rw-r--r--users/aspen/system/home/modules/firefox.nix (renamed from users/grfn/system/home/modules/firefox.nix)0
-rw-r--r--users/aspen/system/home/modules/games.nix (renamed from users/grfn/system/home/modules/games.nix)28
-rw-r--r--users/aspen/system/home/modules/i3.nix397
-rw-r--r--users/aspen/system/home/modules/lib/cloneRepo.nix76
-rw-r--r--users/aspen/system/home/modules/lib/zshFunctions.nix (renamed from users/grfn/system/home/modules/lib/zshFunctions.nix)14
-rw-r--r--users/aspen/system/home/modules/obs.nix18
-rw-r--r--users/aspen/system/home/modules/ptt.nix (renamed from users/grfn/system/home/modules/ptt.nix)0
-rwxr-xr-xusers/aspen/system/home/modules/pure.zsh-theme (renamed from users/grfn/system/home/modules/pure.zsh-theme)4
-rw-r--r--users/aspen/system/home/modules/rtlsdr.nix23
-rw-r--r--users/aspen/system/home/modules/shell.nix (renamed from users/grfn/system/home/modules/shell.nix)102
-rw-r--r--users/aspen/system/home/modules/tarsnap.nix64
-rw-r--r--users/aspen/system/home/modules/tmux.nix42
-rw-r--r--users/aspen/system/home/modules/twitter.nix (renamed from users/grfn/system/home/modules/twitter.nix)4
-rw-r--r--users/aspen/system/home/modules/vim.nix (renamed from users/grfn/system/home/modules/vim.nix)0
-rw-r--r--users/aspen/system/home/modules/vimrc (renamed from users/grfn/system/home/modules/vimrc)2
-rw-r--r--users/aspen/system/home/modules/zshrc (renamed from users/grfn/system/home/modules/zshrc)0
-rw-r--r--users/aspen/system/home/platforms/darwin.nix (renamed from users/grfn/system/home/platforms/darwin.nix)2
-rw-r--r--users/aspen/system/home/platforms/linux.nix (renamed from users/grfn/system/home/platforms/linux.nix)29
-rwxr-xr-xusers/aspen/system/install (renamed from users/grfn/system/install)0
-rw-r--r--users/aspen/system/system/.skip-subtree (renamed from users/grfn/system/system/.skip-subtree)0
-rw-r--r--users/aspen/system/system/configuration.nix (renamed from users/grfn/system/system/configuration.nix)0
-rw-r--r--users/aspen/system/system/default.nix (renamed from users/grfn/system/system/default.nix)14
-rw-r--r--users/aspen/system/system/iso.nix22
-rw-r--r--users/aspen/system/system/machines/bumblebee.nix (renamed from users/grfn/system/system/machines/bumblebee.nix)2
-rw-r--r--users/aspen/system/system/machines/lusca.nix142
-rw-r--r--users/aspen/system/system/machines/mugwump.nix306
-rw-r--r--users/aspen/system/system/machines/ogopogo.nix107
-rw-r--r--users/aspen/system/system/machines/roswell.nix27
-rw-r--r--users/aspen/system/system/machines/yeren.nix (renamed from users/grfn/system/system/machines/yeren.nix)24
-rw-r--r--users/aspen/system/system/modules/common.nix97
-rw-r--r--users/aspen/system/system/modules/desktop.nix (renamed from users/grfn/system/system/modules/desktop.nix)2
-rw-r--r--users/aspen/system/system/modules/development.nix15
-rw-r--r--users/aspen/system/system/modules/fcitx.nix (renamed from users/grfn/system/system/modules/fcitx.nix)0
-rw-r--r--users/aspen/system/system/modules/fonts.nix (renamed from users/grfn/system/system/modules/fonts.nix)5
-rw-r--r--users/aspen/system/system/modules/laptop.nix23
-rw-r--r--users/aspen/system/system/modules/reusable/README.org (renamed from users/grfn/system/system/modules/reusable/README.org)0
-rw-r--r--users/aspen/system/system/modules/rtlsdr.nix (renamed from users/grfn/system/system/modules/rtlsdr.nix)0
-rw-r--r--users/aspen/system/system/modules/sound.nix (renamed from users/grfn/system/system/modules/sound.nix)0
-rw-r--r--users/aspen/system/system/modules/tvl.nix35
-rw-r--r--users/aspen/system/system/modules/wireshark.nix9
-rw-r--r--users/aspen/system/system/modules/xserver.nix (renamed from users/grfn/system/system/modules/xserver.nix)2
-rw-r--r--users/aspen/terraform/globals.nix27
-rw-r--r--users/aspen/terraform/nixosMachine.nix208
-rw-r--r--users/aspen/terraform/workspace.nix107
-rw-r--r--users/aspen/web/.envrc2
-rw-r--r--users/aspen/web/.gitignore (renamed from users/grfn/gws.fyi/.gitignore)0
-rw-r--r--users/aspen/web/Makefile (renamed from users/grfn/gws.fyi/Makefile)15
-rw-r--r--users/aspen/web/config.el (renamed from users/grfn/gws.fyi/config.el)0
-rw-r--r--users/aspen/web/default.nix62
-rw-r--r--users/aspen/web/index.org40
-rw-r--r--users/aspen/web/main.css (renamed from users/grfn/gws.fyi/main.css)0
-rw-r--r--users/aspen/web/orgExportHTML.nix (renamed from users/grfn/gws.fyi/orgExportHTML.nix)19
-rw-r--r--users/aspen/web/pubkey.gpg206
-rw-r--r--users/aspen/web/recipes/tomato-sauce.org (renamed from users/grfn/gws.fyi/recipes/tomato-sauce.org)4
-rw-r--r--users/aspen/web/shell.nix (renamed from users/grfn/gws.fyi/shell.nix)2
-rw-r--r--users/aspen/web/site.nix (renamed from users/grfn/gws.fyi/site.nix)2
-rw-r--r--users/aspen/wigglydonke.rs/.well-known/cf-2fa-verify.txt1
-rw-r--r--users/aspen/wigglydonke.rs/index.html (renamed from users/grfn/wigglydonke.rs/index.html)0
-rw-r--r--users/aspen/wigglydonke.rs/wd.png (renamed from users/grfn/wigglydonke.rs/wd.png)bin1624030 -> 1624030 bytes
-rw-r--r--users/aspen/xanthous/.envrc (renamed from users/grfn/gws.fyi/.envrc)0
-rw-r--r--users/aspen/xanthous/.github/actions/nix-build/Dockerfile (renamed from users/grfn/xanthous/.github/actions/nix-build/Dockerfile)0
-rwxr-xr-xusers/aspen/xanthous/.github/actions/nix-build/entrypoint.sh (renamed from users/grfn/xanthous/.github/actions/nix-build/entrypoint.sh)0
-rw-r--r--users/aspen/xanthous/.github/workflows/haskell.yml (renamed from users/grfn/xanthous/.github/workflows/haskell.yml)0
-rw-r--r--users/aspen/xanthous/.gitignore (renamed from users/grfn/xanthous/.gitignore)3
-rw-r--r--users/aspen/xanthous/LICENSE (renamed from users/grfn/xanthous/LICENSE)0
-rw-r--r--users/aspen/xanthous/README.org (renamed from users/grfn/xanthous/README.org)0
-rw-r--r--users/aspen/xanthous/Setup.hs (renamed from users/grfn/xanthous/Setup.hs)0
-rw-r--r--users/aspen/xanthous/app/Main.hs (renamed from users/grfn/xanthous/app/Main.hs)30
-rw-r--r--users/aspen/xanthous/bench/Bench.hs (renamed from users/grfn/xanthous/bench/Bench.hs)0
-rw-r--r--users/aspen/xanthous/bench/Bench/Prelude.hs (renamed from users/grfn/xanthous/bench/Bench/Prelude.hs)0
-rw-r--r--users/aspen/xanthous/bench/Xanthous/Generators/UtilBench.hs (renamed from users/grfn/xanthous/bench/Xanthous/Generators/UtilBench.hs)0
-rw-r--r--users/aspen/xanthous/bench/Xanthous/RandomBench.hs (renamed from users/grfn/xanthous/bench/Xanthous/RandomBench.hs)0
-rw-r--r--users/aspen/xanthous/build/generic-arbitrary-export-garbitrary.patch (renamed from users/grfn/xanthous/build/generic-arbitrary-export-garbitrary.patch)0
-rw-r--r--users/aspen/xanthous/build/hgeometry-fix-haddock.patch (renamed from users/grfn/xanthous/build/hgeometry-fix-haddock.patch)0
-rw-r--r--users/aspen/xanthous/build/update-comonad-extras.patch (renamed from users/grfn/xanthous/build/update-comonad-extras.patch)0
-rw-r--r--users/aspen/xanthous/default.nix27
-rw-r--r--users/aspen/xanthous/docs/raw-types.org24
-rw-r--r--users/aspen/xanthous/hie.yaml (renamed from users/grfn/xanthous/hie.yaml)2
-rw-r--r--users/aspen/xanthous/nixpkgs.nix (renamed from users/grfn/xanthous/nixpkgs.nix)0
-rw-r--r--users/aspen/xanthous/package.yaml (renamed from users/grfn/xanthous/package.yaml)2
-rw-r--r--users/aspen/xanthous/pkg.nix349
-rw-r--r--users/aspen/xanthous/server/.envrc1
-rw-r--r--users/aspen/xanthous/server/.gitignore1
-rw-r--r--users/aspen/xanthous/server/Cargo.lock1874
-rw-r--r--users/aspen/xanthous/server/Cargo.toml29
-rw-r--r--users/aspen/xanthous/server/default.nix24
-rw-r--r--users/aspen/xanthous/server/docker.nix21
-rw-r--r--users/aspen/xanthous/server/module.nix49
-rw-r--r--users/aspen/xanthous/server/shell.nix11
-rw-r--r--users/aspen/xanthous/server/src/main.rs385
-rw-r--r--users/aspen/xanthous/server/src/metrics.rs24
-rw-r--r--users/aspen/xanthous/server/src/pty.rs172
-rw-r--r--users/aspen/xanthous/shell.nix (renamed from users/grfn/xanthous/shell.nix)8
-rw-r--r--users/aspen/xanthous/src/Data/Aeson/Generic/DerivingVia.hs (renamed from users/grfn/xanthous/src/Data/Aeson/Generic/DerivingVia.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/AI/Gormlak.hs (renamed from users/grfn/xanthous/src/Xanthous/AI/Gormlak.hs)60
-rw-r--r--users/aspen/xanthous/src/Xanthous/App.hs (renamed from users/grfn/xanthous/src/Xanthous/App.hs)235
-rw-r--r--users/aspen/xanthous/src/Xanthous/App/Autocommands.hs (renamed from users/grfn/xanthous/src/Xanthous/App/Autocommands.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/App/Common.hs (renamed from users/grfn/xanthous/src/Xanthous/App/Common.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/App/Prompt.hs (renamed from users/grfn/xanthous/src/Xanthous/App/Prompt.hs)109
-rw-r--r--users/aspen/xanthous/src/Xanthous/App/Time.hs (renamed from users/grfn/xanthous/src/Xanthous/App/Time.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Command.hs145
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data.hs (renamed from users/grfn/xanthous/src/Xanthous/Data.hs)140
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/App.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/App.hs)4
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/Entities.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/Entities.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/EntityChar.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/EntityChar.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/EntityMap.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/EntityMap.hs)8
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs)36
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/Levels.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/Levels.hs)22
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/Memo.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/Memo.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/NestedMap.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/NestedMap.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Data/VectorBag.hs (renamed from users/grfn/xanthous/src/Xanthous/Data/VectorBag.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Character.hs241
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Common.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Character.hs)328
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Creature.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Creature.hs)23
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs)1
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Draw/Util.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Draw/Util.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Entities.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Entities.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Entities.hs-boot (renamed from users/grfn/xanthous/src/Xanthous/Entities/Entities.hs-boot)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Environment.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Environment.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Item.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Item.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Marker.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Marker.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/RawTypes.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/RawTypes.hs)123
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws.hs (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws.hs)11
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml24
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/husk.yaml26
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/noodles.yaml (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws/noodles.yaml)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/ooze.yaml (renamed from users/grfn/xanthous/src/Xanthous/Entities/Raws/ooze.yaml)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/rock.yaml10
-rw-r--r--users/aspen/xanthous/src/Xanthous/Entities/Raws/stick.yaml22
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game.hs (renamed from users/grfn/xanthous/src/Xanthous/Game.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Arbitrary.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Arbitrary.hs)1
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Draw.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Draw.hs)96
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Env.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Env.hs)22
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Lenses.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Lenses.hs)5
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Memo.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Memo.hs)2
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/Prompt.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/Prompt.hs)125
-rw-r--r--users/aspen/xanthous/src/Xanthous/Game/State.hs (renamed from users/grfn/xanthous/src/Xanthous/Game/State.hs)5
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level.hs)12
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/Dungeon.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/Dungeon.hs)2
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/LevelContents.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/LevelContents.hs)71
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/Util.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/Util.hs)36
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Level/Village.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Level/Village.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Generators/Speech.hs (renamed from users/grfn/xanthous/src/Xanthous/Generators/Speech.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Messages.hs (renamed from users/grfn/xanthous/src/Xanthous/Messages.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Messages/Template.hs (renamed from users/grfn/xanthous/src/Xanthous/Messages/Template.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Monad.hs (renamed from users/grfn/xanthous/src/Xanthous/Monad.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Orphans.hs (renamed from users/grfn/xanthous/src/Xanthous/Orphans.hs)18
-rw-r--r--users/aspen/xanthous/src/Xanthous/Physics.hs71
-rw-r--r--users/aspen/xanthous/src/Xanthous/Prelude.hs (renamed from users/grfn/xanthous/src/Xanthous/Prelude.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Random.hs (renamed from users/grfn/xanthous/src/Xanthous/Random.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util.hs (renamed from users/grfn/xanthous/src/Xanthous/Util.hs)18
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Comonad.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Comonad.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Graph.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Graph.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Graphics.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Graphics.hs)3
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Inflection.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Inflection.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/JSON.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/JSON.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/Optparse.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/Optparse.hs)0
-rw-r--r--users/aspen/xanthous/src/Xanthous/Util/QuickCheck.hs (renamed from users/grfn/xanthous/src/Xanthous/Util/QuickCheck.hs)10
-rw-r--r--users/aspen/xanthous/src/Xanthous/keybindings.yaml22
-rw-r--r--users/aspen/xanthous/src/Xanthous/messages.yaml (renamed from users/grfn/xanthous/src/Xanthous/messages.yaml)34
-rw-r--r--users/aspen/xanthous/test/Spec.hs (renamed from users/grfn/xanthous/test/Spec.hs)10
-rw-r--r--users/aspen/xanthous/test/Test/Prelude.hs (renamed from users/grfn/xanthous/test/Test/Prelude.hs)2
-rw-r--r--users/aspen/xanthous/test/Xanthous/CommandSpec.hs40
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/EntitiesSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/EntitiesSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/EntityCharSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/EntityCharSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/EntityMapSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/EntityMapSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/LevelsSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/LevelsSpec.hs)6
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/MemoSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/MemoSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Data/NestedMapSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Data/NestedMapSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/DataSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/DataSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Entities/CharacterSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Entities/CharacterSpec.hs)18
-rw-r--r--users/aspen/xanthous/test/Xanthous/Entities/CommonSpec.hs65
-rw-r--r--users/aspen/xanthous/test/Xanthous/Entities/RawTypesSpec.hs45
-rw-r--r--users/aspen/xanthous/test/Xanthous/Entities/RawsSpec.hs30
-rw-r--r--users/aspen/xanthous/test/Xanthous/Game/PromptSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Game/PromptSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Game/StateSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Game/StateSpec.hs)7
-rw-r--r--users/aspen/xanthous/test/Xanthous/GameSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/GameSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs)45
-rw-r--r--users/aspen/xanthous/test/Xanthous/MessageSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/MessageSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Messages/TemplateSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Messages/TemplateSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/OrphansSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/OrphansSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/RandomSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/RandomSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Util/GraphSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Util/GraphSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Util/GraphicsSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Util/GraphicsSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/Util/InflectionSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/Util/InflectionSpec.hs)0
-rw-r--r--users/aspen/xanthous/test/Xanthous/UtilSpec.hs (renamed from users/grfn/xanthous/test/Xanthous/UtilSpec.hs)0
-rw-r--r--users/aspen/xanthous/xanthous.cabal (renamed from users/grfn/xanthous/xanthous.cabal)18
-rw-r--r--users/cynthia/OWNERS6
-rw-r--r--users/cynthia/keys.nix2
-rw-r--r--users/edef/OWNERS6
-rw-r--r--users/edef/depot-scan/wrap.nix7
-rw-r--r--users/edef/refscan/.gitignore5
-rw-r--r--users/edef/refscan/Cargo.lock7
-rw-r--r--users/edef/refscan/Cargo.lock.license3
-rw-r--r--users/edef/refscan/Cargo.toml10
-rw-r--r--users/edef/refscan/LICENSES/CC0-1.0.txt121
-rw-r--r--users/edef/refscan/LICENSES/MPL-2.0.txt373
-rw-r--r--users/edef/refscan/src/lib.rs154
-rw-r--r--users/edef/refscan/src/main.rs58
-rw-r--r--users/edef/refscan/testdata/.gitignore6
-rwxr-xr-xusers/edef/refscan/testdata/generate.sh8
-rw-r--r--users/ericvolp12/OWNERS6
-rw-r--r--users/espes/OWNERS1
-rw-r--r--users/eta/OWNERS6
-rw-r--r--users/eta/keys.nix2
-rw-r--r--users/firefly/OWNERS6
-rw-r--r--users/flokli/OWNERS6
-rw-r--r--users/flokli/archeology/OWNERS1
-rw-r--r--users/flokli/archeology/README.md5
-rw-r--r--users/flokli/archeology/default.nix51
-rw-r--r--users/flokli/archeology/parse_bucket_logs.rs42
-rw-r--r--users/flokli/archivist/OWNERS1
-rw-r--r--users/flokli/archivist/default.nix28
-rw-r--r--users/flokli/ipu6-softisp/README.md29
-rw-r--r--users/flokli/ipu6-softisp/config.nix74
-rw-r--r--users/flokli/ipu6-softisp/default.nix57
-rw-r--r--users/flokli/ipu6-softisp/kernel/.skip-tree (renamed from users/riking/dotfiles/regolith/flags/first-time-setup-r1-4-1)0
-rw-r--r--users/flokli/ipu6-softisp/kernel/softisp.patch18077
-rw-r--r--users/flokli/ipu6-softisp/libcamera/.skip-tree (renamed from users/riking/dotfiles/regolith/flags/show-shortcuts)0
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch82
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch350
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch169
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch69
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch403
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch523
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch255
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch825
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch506
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch507
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch240
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch302
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch203
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch234
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch127
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch237
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch132
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch396
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch239
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch95
-rw-r--r--users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch42
-rw-r--r--users/flokli/keyboards/dilemma/default.nix45
-rw-r--r--users/flokli/keyboards/dilemma/enable-taps.patch24
-rw-r--r--users/flokli/keyboards/dilemma/keymap.c220
-rw-r--r--users/flokli/keyboards/dilemma/rules.mk2
-rw-r--r--users/flokli/keyboards/k6_pro/default.nix39
-rw-r--r--users/flokli/keyboards/k6_pro/keymap.c76
-rw-r--r--users/flokli/keyboards/k6_pro/rules.mk2
-rw-r--r--users/flokli/nixos/.envrc1
-rw-r--r--users/flokli/nixos/.skip-subtree (renamed from users/riking/dotfiles/regolith/flags/term-profile)0
-rw-r--r--users/flokli/nixos/archeology-ec2/OWNERS1
-rw-r--r--users/flokli/nixos/archeology-ec2/configuration.nix35
-rw-r--r--users/flokli/nixos/archeology-ec2/hardware-configuration.nix36
-rw-r--r--users/flokli/nixos/archeology-ec2/parse-bucket-logs-continuously.py62
-rw-r--r--users/flokli/nixos/default.nix32
-rw-r--r--users/flokli/nixos/profiles/archeology.nix37
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/.gitignore1
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/architecture.dot5
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/cppnix-example-lexer.cpp13
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/crate-deps.dot19
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/default.nix37
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/presentation.md294
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/tvix-store-graph.svg17
-rw-r--r--users/flokli/presentations/2023-09-09-nixcon-tvix/tvixbolt.webmbin0 -> 983213 bytes
-rw-r--r--users/flokli/presentations/2023-09-13-asg-tvix-store/default.nix32
-rw-r--r--users/flokli/presentations/2023-09-13-asg-tvix-store/presentation.md138
-rw-r--r--users/flokli/presentations/2023-09-13-asg-tvix-store/tvix-store-graph-blob-directory.svg17
-rw-r--r--users/fogti/.gitignore2
-rw-r--r--users/fogti/OWNERS3
-rw-r--r--users/fogti/dbwospof.md112
-rw-r--r--users/fogti/store-ref-scanner/.gitignore1
-rw-r--r--users/fogti/store-ref-scanner/Cargo.toml11
-rw-r--r--users/fogti/store-ref-scanner/default.nix49
-rw-r--r--users/fogti/store-ref-scanner/fuzz/.gitignore2
-rw-r--r--users/fogti/store-ref-scanner/fuzz/Cargo.lock44
-rw-r--r--users/fogti/store-ref-scanner/fuzz/Cargo.toml36
-rw-r--r--users/fogti/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs10
-rw-r--r--users/fogti/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs9
-rw-r--r--users/fogti/store-ref-scanner/src/hbm.rs167
-rw-r--r--users/fogti/store-ref-scanner/src/lib.rs215
-rw-r--r--users/fogti/store-ref-scanner/src/spec.rs40
-rw-r--r--users/grfn/OWNERS3
-rw-r--r--users/grfn/emacs.d/snippets/rust-mode/async test10
-rw-r--r--users/grfn/gws.fyi/default.nix35
-rw-r--r--users/grfn/gws.fyi/index.org38
-rwxr-xr-xusers/grfn/keyboard/flash2
-rw-r--r--users/grfn/resume/default.nix37
-rw-r--r--users/grfn/system/home/common/solarized.nix18
-rw-r--r--users/grfn/system/home/modules/alsi.nix58
-rw-r--r--users/grfn/system/home/modules/common.nix105
-rw-r--r--users/grfn/system/home/modules/development/readyset.nix16
-rw-r--r--users/grfn/system/home/modules/email.nix87
-rw-r--r--users/grfn/system/home/modules/i3.nix369
-rw-r--r--users/grfn/system/home/modules/lib/cloneRepo.nix67
-rw-r--r--users/grfn/system/home/modules/nixos-logo.txt26
-rw-r--r--users/grfn/system/home/modules/obs.nix66
-rw-r--r--users/grfn/system/home/modules/rtlsdr.nix21
-rw-r--r--users/grfn/system/home/modules/tarsnap.nix64
-rw-r--r--users/grfn/system/system/iso.nix17
-rw-r--r--users/grfn/system/system/machines/mugwump.nix251
-rw-r--r--users/grfn/system/system/machines/roswell.nix17
-rw-r--r--users/grfn/system/system/modules/common.nix75
-rw-r--r--users/grfn/system/system/modules/development.nix6
-rw-r--r--users/grfn/system/system/modules/kernel.nix39
-rw-r--r--users/grfn/system/system/modules/laptop.nix15
-rw-r--r--users/grfn/system/system/modules/reusable/battery.nix32
-rw-r--r--users/grfn/system/system/modules/tvl.nix37
-rw-r--r--users/grfn/system/system/modules/work/kolide.debbin25094998 -> 0 bytes
-rw-r--r--users/grfn/system/system/modules/work/kolide.nix49
-rw-r--r--users/grfn/xanthous/.envrc1
-rw-r--r--users/grfn/xanthous/default.nix23
-rw-r--r--users/grfn/xanthous/pkg.nix80
-rw-r--r--users/grfn/xanthous/src/Xanthous/Command.hs77
-rw-r--r--users/grfn/xanthous/src/Xanthous/Entities/Raws/stick.yaml18
-rw-r--r--users/grfn/xanthous/test/Xanthous/Entities/RawsSpec.hs16
-rw-r--r--users/isomer/OWNERS3
-rw-r--r--users/isomer/keys.nix7
-rw-r--r--users/j4m3s/OWNERS3
-rw-r--r--users/j4m3s/keys.nix7
-rw-r--r--users/lukegb/OWNERS6
-rw-r--r--users/lukegb/keys.nix5
-rw-r--r--users/padraic-o-mhuiris/OWNERS3
-rw-r--r--users/picnoir/tvix-daemon/.gitignore1
-rw-r--r--users/picnoir/tvix-daemon/Cargo.lock1541
-rw-r--r--users/picnoir/tvix-daemon/Cargo.nix5754
-rw-r--r--users/picnoir/tvix-daemon/Cargo.toml15
-rw-r--r--users/picnoir/tvix-daemon/README.md16
-rw-r--r--users/picnoir/tvix-daemon/default.nix43
-rw-r--r--users/picnoir/tvix-daemon/shell.nix21
-rw-r--r--users/picnoir/tvix-daemon/src/main.rs116
-rw-r--r--users/picnoir/tvix-daemon/vm-test/README.md5
-rw-r--r--users/picnoir/tvix-daemon/vm-test/default.nix28
-rw-r--r--users/qyliss/OWNERS6
-rw-r--r--users/riking/OWNERS3
-rw-r--r--users/riking/adventofcode-2020/.gitignore2
-rw-r--r--users/riking/adventofcode-2020/day01/Cargo.lock14
-rw-r--r--users/riking/adventofcode-2020/day01/default.nix10
-rw-r--r--users/riking/adventofcode-2020/day01/src/main.rs85
-rw-r--r--users/riking/dotfiles/.mybashrc53
-rw-r--r--users/riking/dotfiles/fish/conf.d/nix-env.fish141
-rw-r--r--users/riking/dotfiles/fish/config.fish8
-rw-r--r--users/riking/dotfiles/fish/fish_variables32
-rw-r--r--users/riking/dotfiles/fish/functions/ddate.fish3
-rw-r--r--users/riking/dotfiles/fish/functions/gh-clone.fish18
-rw-r--r--users/riking/dotfiles/fish/functions/prodaccess.fish6
-rw-r--r--users/riking/dotfiles/fish/functions/reset-audio.fish4
-rw-r--r--users/riking/dotfiles/fish/functions/tvl-push.fish3
-rw-r--r--users/riking/dotfiles/regolith/Xresources5
-rw-r--r--users/riking/dotfiles/regolith/flags/ui-fingerprint1
-rwxr-xr-xusers/riking/dotfiles/regolith/initrc3
-rw-r--r--users/riking/dotfiles/tmux.conf6
-rw-r--r--users/riking/keys.nix20
-rw-r--r--users/sterni/OWNERS6
-rw-r--r--users/sterni/clhs-lookup/default.nix2
-rw-r--r--users/sterni/dot-time-man-pages/OWNERS4
-rw-r--r--users/sterni/dot-time-man-pages/default.nix12
-rw-r--r--users/sterni/emacs/default.nix115
-rw-r--r--users/sterni/emacs/init.el231
-rw-r--r--users/sterni/emacs/subscriptions.el152
-rw-r--r--users/sterni/exercises/aoc/.gitignore2
-rw-r--r--users/sterni/exercises/aoc/2021/default.nix10
-rwxr-xr-xusers/sterni/exercises/aoc/2021/solutions.bqn484
-rw-r--r--users/sterni/exercises/aoc/2022/.skip-subtree1
-rw-r--r--users/sterni/exercises/aoc/2022/01/1.bqn7
-rw-r--r--users/sterni/exercises/aoc/2022/01/1.k1
-rw-r--r--users/sterni/exercises/aoc/2022/02/2.bqn7
-rw-r--r--users/sterni/exercises/aoc/2022/02/2.k1
-rw-r--r--users/sterni/exercises/aoc/2022/03/3.bqn8
-rw-r--r--users/sterni/exercises/aoc/2022/03/3.k1
-rw-r--r--users/sterni/exercises/aoc/2022/04/4.bqn11
-rw-r--r--users/sterni/exercises/aoc/2022/05/5.bqn18
-rw-r--r--users/sterni/exercises/aoc/2022/06/6.bqn4
-rw-r--r--users/sterni/exercises/aoc/2022/06/6.k1
-rw-r--r--users/sterni/exercises/aoc/2022/07/7.bqn24
-rw-r--r--users/sterni/exercises/aoc/2022/08/8.bqn15
-rw-r--r--users/sterni/exercises/aoc/2022/09/9.bqn17
-rw-r--r--users/sterni/exercises/aoc/2022/10/10.bqn25
-rw-r--r--users/sterni/exercises/aoc/2022/11/11.bqn41
-rw-r--r--users/sterni/exercises/aoc/2022/12/12.bqn16
-rw-r--r--users/sterni/exercises/aoc/2022/13/13.bqn14
-rw-r--r--users/sterni/exercises/aoc/2022/15/15.bqn18
-rw-r--r--users/sterni/exercises/aoc/2022/16/16.k21
-rw-r--r--users/sterni/exercises/aoc/2022/17/17.bqn51
-rw-r--r--users/sterni/exercises/aoc/2022/18/18.bqn14
-rw-r--r--users/sterni/exercises/aoc/2022/20/20.bqn13
-rw-r--r--users/sterni/exercises/aoc/2022/21/21.bqn25
-rw-r--r--users/sterni/exercises/aoc/2022/25/25.bqn4
-rw-r--r--users/sterni/exercises/aoc/2022/25/25.k1
-rw-r--r--users/sterni/exercises/aoc/2022/README.md8
-rw-r--r--users/sterni/exercises/aoc/2022/default.nix53
-rw-r--r--users/sterni/exercises/aoc/lib.bqn18
-rw-r--r--users/sterni/external/flipdot-gschichtler.nix9
-rw-r--r--users/sterni/external/likely-music.nix11
-rw-r--r--users/sterni/external/sources.json26
-rw-r--r--users/sterni/external/sources.nix197
-rw-r--r--users/sterni/htmlman/default.nix188
-rw-r--r--users/sterni/keys.nix1
-rw-r--r--users/sterni/lv/gopher/default.nix8
-rw-r--r--users/sterni/machines/.skip-subtree1
-rw-r--r--users/sterni/machines/default.nix81
-rw-r--r--users/sterni/machines/edwin/default.nix19
-rw-r--r--users/sterni/machines/edwin/hardware.nix63
-rw-r--r--users/sterni/machines/edwin/network.nix62
-rw-r--r--users/sterni/machines/ingeborg/default.nix33
-rw-r--r--users/sterni/machines/ingeborg/gopher.nix19
-rw-r--r--users/sterni/machines/ingeborg/hardware.nix76
-rw-r--r--users/sterni/machines/ingeborg/http/code.sterni.lv.nix263
-rw-r--r--users/sterni/machines/ingeborg/http/fcgiwrap.nix15
-rw-r--r--users/sterni/machines/ingeborg/http/flipdot.openlab-augsburg.de.nix36
-rw-r--r--users/sterni/machines/ingeborg/http/likely-music.sterni.lv.nix23
-rw-r--r--users/sterni/machines/ingeborg/http/nginx.nix30
-rw-r--r--users/sterni/machines/ingeborg/http/sterni.lv.nix34
-rw-r--r--users/sterni/machines/ingeborg/irccat.nix23
-rw-r--r--users/sterni/machines/ingeborg/minecraft.nix125
-rw-r--r--users/sterni/machines/ingeborg/monitoring.nix152
-rw-r--r--users/sterni/machines/ingeborg/network.nix62
-rw-r--r--users/sterni/machines/ingeborg/quassel.nix18
-rw-r--r--users/sterni/machines/ingeborg/tv.nix13
-rw-r--r--users/sterni/mblog/.gitignore5
-rw-r--r--users/sterni/mblog/LICENSE674
-rw-r--r--users/sterni/mblog/cli.lisp84
-rw-r--r--users/sterni/mblog/config.lisp31
-rw-r--r--users/sterni/mblog/default.nix24
-rw-r--r--users/sterni/mblog/maildir.lisp20
-rw-r--r--users/sterni/mblog/mblog.lisp147
-rw-r--r--users/sterni/mblog/note.lisp118
-rw-r--r--users/sterni/mblog/packages.lisp59
-rw-r--r--users/sterni/mblog/transformer.lisp17
-rw-r--r--users/sterni/modules/backup-minecraft-fabric.nix125
-rw-r--r--users/sterni/modules/common.nix79
-rw-r--r--users/sterni/modules/default.nix2
-rw-r--r--users/sterni/modules/minecraft-fabric.nix532
-rw-r--r--users/sterni/nix/build/buildGopherHole/default.nix109
-rw-r--r--users/sterni/nix/char/default.nix32
-rw-r--r--users/sterni/nix/char/tests/default.nix12
-rw-r--r--users/sterni/nix/float/default.nix23
-rw-r--r--users/sterni/nix/float/tests/default.nix49
-rw-r--r--users/sterni/nix/flow/default.nix9
-rw-r--r--users/sterni/nix/flow/tests/default.nix8
-rw-r--r--users/sterni/nix/fun/default.nix196
-rw-r--r--users/sterni/nix/fun/tests/default.nix59
-rw-r--r--users/sterni/nix/html/default.nix19
-rw-r--r--users/sterni/nix/html/tests/default.nix73
-rw-r--r--users/sterni/nix/int/default.nix56
-rw-r--r--users/sterni/nix/int/tests/default.nix402
-rw-r--r--users/sterni/nix/list/default.nix30
-rw-r--r--users/sterni/nix/misc/default.nix18
l---------users/sterni/nix/misc/guinea-pig1
-rw-r--r--users/sterni/nix/num/default.nix17
-rw-r--r--users/sterni/nix/num/tests/default.nix26
-rw-r--r--users/sterni/nix/string/default.nix39
-rw-r--r--users/sterni/nix/string/tests/default.nix14
-rw-r--r--users/sterni/nix/url/default.nix41
-rw-r--r--users/sterni/nix/url/tests/default.nix18
-rw-r--r--users/sterni/nix/utf8/default.nix276
-rw-r--r--users/sterni/nix/utf8/tests/default.nix69
-rw-r--r--users/sterni/nixpkgs-crate-holes/default.nix233
-rw-r--r--users/sterni/nixpkgs-crate-holes/format-audit-result.jq59
-rw-r--r--users/sterni/secrets/default.nix3
-rw-r--r--users/sterni/secrets/minecraft-rcon.agebin0 -> 388 bytes
-rw-r--r--users/sterni/secrets/secrets.nix15
-rw-r--r--users/sterni/secrets/warteraum-salt.agebin0 -> 587 bytes
-rw-r--r--users/sterni/secrets/warteraum-tokens.age11
-rw-r--r--users/tazjin/OWNERS6
-rw-r--r--users/tazjin/aoc2019/default.nix22
-rw-r--r--users/tazjin/aoc2020/default.nix16
-rw-r--r--users/tazjin/aoc2022/day1.rs27
-rw-r--r--users/tazjin/aoc2023/day1.el52
-rw-r--r--users/tazjin/aoc2023/day2.el64
-rw-r--r--users/tazjin/aoc2023/day3.el110
-rw-r--r--users/tazjin/blog/default.nix42
-rw-r--r--users/tazjin/blog/posts.nix23
-rw-r--r--users/tazjin/blog/posts/best-tools.md84
-rw-r--r--users/tazjin/blog/posts/nixery-layers.md6
-rw-r--r--users/tazjin/blog/posts/reliably-switch-buffers.md18
-rw-r--r--users/tazjin/blog/posts/reversing-watchguard-vpn.md2
-rw-r--r--users/tazjin/blog/posts/thoughts.md142
-rw-r--r--users/tazjin/blog/posts/tvix-eval-talk-2023.md19
-rw-r--r--users/tazjin/chase-geese/default.nix13
-rw-r--r--users/tazjin/covid/us_mortality.jq36
-rw-r--r--users/tazjin/default.nix30
-rw-r--r--users/tazjin/dns/default.nix9
-rw-r--r--users/tazjin/docs/install-zfs.md116
-rw-r--r--users/tazjin/dotfiles/default.nix3
-rw-r--r--users/tazjin/dotfiles/dunstrc54
-rw-r--r--users/tazjin/dt/CMakeLists.txt16
-rw-r--r--users/tazjin/dt/README.md11
-rw-r--r--users/tazjin/dt/default.nix13
-rw-r--r--users/tazjin/dt/dt.cc79
-rw-r--r--users/tazjin/elisp-deps/deps.el83
-rw-r--r--users/tazjin/emacs/config/bindings.el65
-rw-r--r--users/tazjin/emacs/config/custom.el6
-rw-r--r--users/tazjin/emacs/config/desktop.el297
-rw-r--r--users/tazjin/emacs/config/functions.el194
-rw-r--r--users/tazjin/emacs/config/init.el167
-rw-r--r--users/tazjin/emacs/config/look-and-feel.el102
-rw-r--r--users/tazjin/emacs/config/mail-setup.el10
-rw-r--r--users/tazjin/emacs/config/modes.el37
-rw-r--r--users/tazjin/emacs/config/settings.el41
-rw-r--r--users/tazjin/emacs/default.nix293
-rw-r--r--users/tazjin/finito/default.nix4
-rw-r--r--users/tazjin/finito/finito-core/src/lib.rs41
-rw-r--r--users/tazjin/finito/finito-door/src/lib.rs34
-rw-r--r--users/tazjin/finito/finito-postgres/src/error.rs26
-rw-r--r--users/tazjin/finito/finito-postgres/src/lib.rs191
-rw-r--r--users/tazjin/finito/finito-postgres/src/tests.rs11
-rw-r--r--users/tazjin/generator-example/.gitignore1
-rw-r--r--users/tazjin/generator-example/Cargo.lock124
-rw-r--r--users/tazjin/generator-example/Cargo.toml (renamed from users/riking/adventofcode-2020/day01/Cargo.toml)7
-rw-r--r--users/tazjin/generator-example/README.md11
-rw-r--r--users/tazjin/generator-example/src/main.rs115
-rw-r--r--users/tazjin/gio-list-apps/.gitignore1
-rw-r--r--users/tazjin/gio-list-apps/Cargo.lock616
-rw-r--r--users/tazjin/gio-list-apps/Cargo.toml11
-rw-r--r--users/tazjin/gio-list-apps/default.nix14
-rw-r--r--users/tazjin/gio-list-apps/src/lib.rs31
-rw-r--r--users/tazjin/hanebuschtag.txt3
-rw-r--r--users/tazjin/home/khamovnik.nix10
-rw-r--r--users/tazjin/home/persistence.nix42
-rw-r--r--users/tazjin/home/shared.nix91
-rw-r--r--users/tazjin/home/tverskoy.nix18
-rw-r--r--users/tazjin/home/zamalek.nix11
-rw-r--r--users/tazjin/homepage/default.nix62
-rw-r--r--users/tazjin/homepage/entries.nix111
-rw-r--r--users/tazjin/homepage/feed.nix28
-rw-r--r--users/tazjin/homepage/header.html18
-rw-r--r--users/tazjin/homepage/static/tazjin.css57
-rw-r--r--users/tazjin/keys.nix10
-rw-r--r--users/tazjin/keys/default.nix12
-rw-r--r--users/tazjin/kinesis/README.md10
-rwxr-xr-xusers/tazjin/kinesis/advantage2/qwerty.txt6
-rw-r--r--users/tazjin/nittredir/background.js10
-rw-r--r--users/tazjin/nittredir/manifest.json15
-rw-r--r--users/tazjin/nix.svg4
-rw-r--r--users/tazjin/nixos/.gitignore1
-rw-r--r--users/tazjin/nixos/camden/default.nix84
-rw-r--r--users/tazjin/nixos/default.nix8
-rw-r--r--users/tazjin/nixos/frog/default.nix35
-rw-r--r--users/tazjin/nixos/khamovnik/default.nix133
-rw-r--r--users/tazjin/nixos/koptevo/default.nix187
-rw-r--r--users/tazjin/nixos/modules/airsonic.nix32
-rw-r--r--users/tazjin/nixos/modules/chromium.nix30
-rw-r--r--users/tazjin/nixos/modules/default.nix2
-rw-r--r--users/tazjin/nixos/modules/desktop.nix55
-rw-r--r--users/tazjin/nixos/modules/fonts.nix24
-rw-r--r--users/tazjin/nixos/modules/geesefs.nix38
-rw-r--r--users/tazjin/nixos/modules/hidpi.nix19
-rw-r--r--users/tazjin/nixos/modules/home-config.nix19
-rw-r--r--users/tazjin/nixos/modules/laptop.nix15
-rw-r--r--users/tazjin/nixos/modules/miniflux.nix22
-rw-r--r--users/tazjin/nixos/modules/persistence.nix26
-rw-r--r--users/tazjin/nixos/modules/physical.nix105
-rw-r--r--users/tazjin/nixos/modules/predlozhnik.nix10
-rw-r--r--users/tazjin/nixos/modules/tgsa.nix29
-rw-r--r--users/tazjin/nixos/tverskoy/default.nix315
-rw-r--r--users/tazjin/nixos/zamalek/default.nix88
-rw-r--r--users/tazjin/presentations/bootstrapping-2018/default.nix38
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/README.md12
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/cppnix-example-lexer.cpp13
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/cppnix-example-smuggling.cpp12
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/default.nix63
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/presentation.pdfpc98
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/presentation.tex148
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/.gitignore2
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.lock899
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.toml7
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/index.html7
-rw-r--r--users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/src/main.rs41
-rw-r--r--users/tazjin/rlox/src/bytecode/compiler.rs89
-rw-r--r--users/tazjin/rlox/src/bytecode/mod.rs5
-rw-r--r--users/tazjin/rlox/src/bytecode/vm.rs33
-rw-r--r--users/tazjin/rlox/src/main.rs17
-rw-r--r--users/tazjin/rlox/src/scanner.rs13
-rw-r--r--users/tazjin/rlox/src/treewalk/interpreter.rs106
-rw-r--r--users/tazjin/rlox/src/treewalk/parser.rs122
-rw-r--r--users/tazjin/rlox/src/treewalk/resolver.rs43
-rw-r--r--users/tazjin/russian/russian.el97
-rw-r--r--users/tazjin/russian/words.el323
-rw-r--r--users/tazjin/rustfmt.toml22
-rw-r--r--users/tazjin/secrets/default.nix3
-rw-r--r--users/tazjin/secrets/geesefs-tazjins-files.age18
-rw-r--r--users/tazjin/secrets/miniflux.age14
-rw-r--r--users/tazjin/secrets/secrets.nix16
-rw-r--r--users/tazjin/secrets/tgsa-yandex.agebin0 -> 3082 bytes
-rw-r--r--users/tazjin/tgsa/.gitignore3
-rw-r--r--users/tazjin/tgsa/Cargo.lock1567
-rw-r--r--users/tazjin/tgsa/Cargo.toml18
-rw-r--r--users/tazjin/tgsa/default.nix17
-rw-r--r--users/tazjin/tgsa/src/main.rs403
-rw-r--r--users/tazjin/tgsa/src/translate.rs191
-rw-r--r--users/tazjin/tvix-eval-value.d298
-rw-r--r--users/tazjin/yddns/.gitignore1
-rw-r--r--users/tazjin/yddns/Cargo.lock1425
-rw-r--r--users/tazjin/yddns/Cargo.toml12
-rw-r--r--users/tazjin/yddns/default.nix9
-rw-r--r--users/tazjin/yddns/src/main.rs142
-rw-r--r--users/wpcarro/.envrc5
-rw-r--r--users/wpcarro/.gitignore35
-rw-r--r--users/wpcarro/.gitsecret/keys/pubring.kbxbin0 -> 6799 bytes
-rw-r--r--users/wpcarro/.gitsecret/keys/pubring.kbx~bin0 -> 32 bytes
-rw-r--r--users/wpcarro/.gitsecret/keys/trustdb.gpgbin0 -> 1200 bytes
-rw-r--r--users/wpcarro/.gitsecret/paths/mapping.cfg1
-rw-r--r--users/wpcarro/OWNERS1
-rw-r--r--users/wpcarro/README.md47
-rw-r--r--users/wpcarro/assessments/brilliant/.ghci2
-rw-r--r--users/wpcarro/assessments/brilliant/App.hs41
-rw-r--r--users/wpcarro/assessments/brilliant/Keyboard.hs58
-rw-r--r--users/wpcarro/assessments/brilliant/Main.hs43
-rw-r--r--users/wpcarro/assessments/brilliant/README.md82
-rw-r--r--users/wpcarro/assessments/brilliant/Spec.hs103
-rw-r--r--users/wpcarro/assessments/brilliant/Transforms.hs52
-rw-r--r--users/wpcarro/assessments/brilliant/Utils.hs13
-rw-r--r--users/wpcarro/assessments/brilliant/default.nix16
-rw-r--r--users/wpcarro/assessments/brilliant/shell.nix12
-rw-r--r--users/wpcarro/assessments/dotted-squares/.envrc2
-rw-r--r--users/wpcarro/assessments/dotted-squares/.ghci1
-rw-r--r--users/wpcarro/assessments/dotted-squares/Main.hs218
-rw-r--r--users/wpcarro/assessments/dotted-squares/README.md21
-rw-r--r--users/wpcarro/assessments/dotted-squares/Spec.hs80
-rw-r--r--users/wpcarro/assessments/dotted-squares/colliding-moves.txt7
-rw-r--r--users/wpcarro/assessments/dotted-squares/game.txt7
-rw-r--r--users/wpcarro/assessments/dotted-squares/input-a.txt5
-rw-r--r--users/wpcarro/assessments/dotted-squares/shell.nix8
-rw-r--r--users/wpcarro/assessments/dotted-squares/too-few-moves.txt6
-rw-r--r--users/wpcarro/assessments/dotted-squares/too-many-moves.txt7
-rw-r--r--users/wpcarro/assessments/ramp/solution-emacs-elixir-format.py29
-rw-r--r--users/wpcarro/assessments/ramp/solution.py87
-rw-r--r--users/wpcarro/assessments/semiprimes/.gitignore1
-rw-r--r--users/wpcarro/assessments/semiprimes/README.md44
-rw-r--r--users/wpcarro/assessments/semiprimes/server/.formatter.exs4
-rw-r--r--users/wpcarro/assessments/semiprimes/server/.gitignore24
-rw-r--r--users/wpcarro/assessments/semiprimes/server/lib/app.ex8
-rw-r--r--users/wpcarro/assessments/semiprimes/server/lib/cache.ex41
-rw-r--r--users/wpcarro/assessments/semiprimes/server/lib/extras.ex22
-rw-r--r--users/wpcarro/assessments/semiprimes/server/lib/math.ex26
-rw-r--r--users/wpcarro/assessments/semiprimes/server/lib/router.ex86
-rw-r--r--users/wpcarro/assessments/semiprimes/server/lib/server.ex33
-rw-r--r--users/wpcarro/assessments/semiprimes/server/lib/sup.ex23
-rw-r--r--users/wpcarro/assessments/semiprimes/server/mix.exs32
-rw-r--r--users/wpcarro/assessments/semiprimes/server/mix.lock14
-rw-r--r--users/wpcarro/assessments/semiprimes/server/test/extras_test.exs18
-rw-r--r--users/wpcarro/assessments/semiprimes/server/test/math_test.exs30
-rw-r--r--users/wpcarro/assessments/semiprimes/server/test/server_test.exs34
-rw-r--r--users/wpcarro/assessments/semiprimes/server/test/test_helper.exs1
-rw-r--r--users/wpcarro/assessments/tt/.gitignore6
-rw-r--r--users/wpcarro/assessments/tt/README.md50
-rw-r--r--users/wpcarro/assessments/tt/client/.gitignore3
-rw-r--r--users/wpcarro/assessments/tt/client/README.md18
-rw-r--r--users/wpcarro/assessments/tt/client/elm.json40
-rw-r--r--users/wpcarro/assessments/tt/client/index.css142
-rw-r--r--users/wpcarro/assessments/tt/client/index.html38
-rw-r--r--users/wpcarro/assessments/tt/client/print.css3
-rw-r--r--users/wpcarro/assessments/tt/client/shell.nix10
-rw-r--r--users/wpcarro/assessments/tt/client/src/Admin.elm189
-rw-r--r--users/wpcarro/assessments/tt/client/src/Common.elm37
-rw-r--r--users/wpcarro/assessments/tt/client/src/Login.elm199
-rw-r--r--users/wpcarro/assessments/tt/client/src/Main.elm62
-rw-r--r--users/wpcarro/assessments/tt/client/src/Manager.elm70
-rw-r--r--users/wpcarro/assessments/tt/client/src/Shared.elm7
-rw-r--r--users/wpcarro/assessments/tt/client/src/State.elm1014
-rw-r--r--users/wpcarro/assessments/tt/client/src/Tailwind.elm29
-rw-r--r--users/wpcarro/assessments/tt/client/src/UI.elm318
-rw-r--r--users/wpcarro/assessments/tt/client/src/User.elm245
-rw-r--r--users/wpcarro/assessments/tt/client/src/Utils.elm109
-rw-r--r--users/wpcarro/assessments/tt/data/accounts.csv2
-rw-r--r--users/wpcarro/assessments/tt/data/trips.csv3
-rw-r--r--users/wpcarro/assessments/tt/populate.sqlite37
-rw-r--r--users/wpcarro/assessments/tt/shell.nix18
-rw-r--r--users/wpcarro/assessments/tt/src/.ghci2
-rw-r--r--users/wpcarro/assessments/tt/src/API.hs75
-rw-r--r--users/wpcarro/assessments/tt/src/Accounts.hs49
-rw-r--r--users/wpcarro/assessments/tt/src/App.hs270
-rw-r--r--users/wpcarro/assessments/tt/src/Auth.hs64
-rw-r--r--users/wpcarro/assessments/tt/src/Email.hs46
-rw-r--r--users/wpcarro/assessments/tt/src/Invitations.hs21
-rw-r--r--users/wpcarro/assessments/tt/src/LoginAttempts.hs30
-rw-r--r--users/wpcarro/assessments/tt/src/Main.hs13
-rw-r--r--users/wpcarro/assessments/tt/src/PendingAccounts.hs32
-rw-r--r--users/wpcarro/assessments/tt/src/Sessions.hs74
-rw-r--r--users/wpcarro/assessments/tt/src/Trips.hs42
-rw-r--r--users/wpcarro/assessments/tt/src/Types.hs544
-rw-r--r--users/wpcarro/assessments/tt/src/Utils.hs9
-rw-r--r--users/wpcarro/assessments/tt/src/init.sql67
-rwxr-xr-xusers/wpcarro/assessments/tt/tests/create-accounts.sh21
-rw-r--r--users/wpcarro/assessments/tt/todo.org18
-rw-r--r--users/wpcarro/boilerplate/README.md21
-rw-r--r--users/wpcarro/boilerplate/clojure/.envrc2
-rw-r--r--users/wpcarro/boilerplate/clojure/.gitignore4
-rw-r--r--users/wpcarro/boilerplate/clojure/README.md33
-rw-r--r--users/wpcarro/boilerplate/clojure/project.clj2
-rw-r--r--users/wpcarro/boilerplate/clojure/shell.nix7
-rw-r--r--users/wpcarro/boilerplate/clojure/src/main.clj8
-rw-r--r--users/wpcarro/boilerplate/elm/.envrc2
-rw-r--r--users/wpcarro/boilerplate/elm/.gitignore3
-rw-r--r--users/wpcarro/boilerplate/elm/README.md18
-rw-r--r--users/wpcarro/boilerplate/elm/elm.json30
-rw-r--r--users/wpcarro/boilerplate/elm/index.css3
-rw-r--r--users/wpcarro/boilerplate/elm/index.html15
-rw-r--r--users/wpcarro/boilerplate/elm/shell.nix9
-rw-r--r--users/wpcarro/boilerplate/elm/src/Landing.elm13
-rw-r--r--users/wpcarro/boilerplate/elm/src/Login.elm13
-rw-r--r--users/wpcarro/boilerplate/elm/src/Main.elm31
-rw-r--r--users/wpcarro/boilerplate/elm/src/State.elm43
-rw-r--r--users/wpcarro/boilerplate/typescript/.envrc2
-rw-r--r--users/wpcarro/boilerplate/typescript/.gitignore3
-rw-r--r--users/wpcarro/boilerplate/typescript/README.md26
-rw-r--r--users/wpcarro/boilerplate/typescript/default.nix23
-rw-r--r--users/wpcarro/boilerplate/typescript/package.json27
-rw-r--r--users/wpcarro/boilerplate/typescript/postcss.config.js7
-rw-r--r--users/wpcarro/boilerplate/typescript/shell.nix8
-rw-r--r--users/wpcarro/boilerplate/typescript/src/App.tsx52
-rw-r--r--users/wpcarro/boilerplate/typescript/src/index.css3
-rw-r--r--users/wpcarro/boilerplate/typescript/src/index.html11
-rw-r--r--users/wpcarro/boilerplate/typescript/src/index.tsx12
-rw-r--r--users/wpcarro/boilerplate/typescript/src/store.ts26
-rw-r--r--users/wpcarro/boilerplate/typescript/tailwind.config.js7
-rw-r--r--users/wpcarro/boilerplate/typescript/tsconfig.json25
-rw-r--r--users/wpcarro/boilerplate/typescript/yarn.lock5670
-rw-r--r--users/wpcarro/buildHaskell/default.nix35
-rw-r--r--users/wpcarro/ci/pipelines/post-receive.nix14
-rw-r--r--users/wpcarro/ci/secret-patterns.txt9
-rw-r--r--users/wpcarro/common.nix83
-rw-r--r--users/wpcarro/configs/.config/nixpkgs/config.nix3
-rw-r--r--users/wpcarro/configs/.config/nvim/init.vim668
-rw-r--r--users/wpcarro/configs/.config/nvim/templates/boilerplate.c6
-rw-r--r--users/wpcarro/configs/.config/nvim/templates/boilerplate.rs5
-rw-r--r--users/wpcarro/configs/.config/systemd/user/clipmenud.service18
l---------users/wpcarro/configs/.config/systemd/user/default.target.wants/clipmenud.service1
-rw-r--r--users/wpcarro/configs/.config/systemd/user/lieer-google.service7
-rw-r--r--users/wpcarro/configs/.config/systemd/user/lieer-google.timer9
l---------users/wpcarro/configs/.config/systemd/user/timers.target.wants/lieer-google.timer1
-rw-r--r--users/wpcarro/configs/.gnupg/crls.d/DIR.txt1
-rw-r--r--users/wpcarro/configs/.gnupg/exported/ownertrust.txt3
-rw-r--r--users/wpcarro/configs/.gnupg/exported/public.asc225
-rw-r--r--users/wpcarro/configs/.gnupg/pubring.kbxbin0 -> 11397 bytes
-rw-r--r--users/wpcarro/configs/.gnupg/trustdb.gpgbin0 -> 1280 bytes
-rw-r--r--users/wpcarro/configs/.sqliterc2
-rw-r--r--users/wpcarro/configs/.xsecurelockrc5
-rw-r--r--users/wpcarro/configs/default.nix73
-rwxr-xr-xusers/wpcarro/configs/install5
-rwxr-xr-xusers/wpcarro/configs/uninstall5
-rw-r--r--users/wpcarro/dotfiles/config.fish44
-rw-r--r--users/wpcarro/dotfiles/default.nix5
-rw-r--r--users/wpcarro/dotfiles/dunstrc53
-rw-r--r--users/wpcarro/dotfiles/gitconfig9
-rw-r--r--users/wpcarro/dotfiles/prompt.fish87
-rw-r--r--users/wpcarro/emacs/.emacs.d/init.el15
-rw-r--r--users/wpcarro/emacs/.emacs.d/opam-user-setup.el145
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/c-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/c-mode/stdio5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/c-mode/stdlib5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/c-mode/struct7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/elisp-module-docs11
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/function8
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/generic-header7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/library-header7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/provide-footer6
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/derive-safe-copy5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/import-qualified5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/instance-defn6
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/language-extension5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/separator5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/undefined5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/html-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/html-mode/index-boilerplate18
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/java-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/java-mode/public-static-void-main7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/defpackage9
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/function7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/typed-function8
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/nix-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/nix-mode/shell-nix12
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/org-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/org-mode/code-snippet7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/org-mode/href5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/python-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/python-mode/dunder-main6
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/python-mode/function6
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/python-mode/header7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/python-mode/init6
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/python-mode/shebang6
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/python-mode/utf-85
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/racket-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/racket-mode/function5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/racket-mode/lambda5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/racket-mode/lambda-symbol5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/reason-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/reason-mode/function7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/reason-mode/switch7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/action-extractor5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/console-log5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/const-defn5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/const-function7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/destructure-const5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/fat-arrow5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/fat-arrow-function7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-destructured5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-react5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-type5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-x-from-y5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-y5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/jest-describe-test10
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/jest-test7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/react-class-component11
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/redux-action5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/typed-redux-action5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rust-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rust-mode/for-loop7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/rust-mode/match7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/sh-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/sh-mode/function7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/text-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/text-mode/check-mark5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/text-mode/x-mark5
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/web-mode/.yas-parents1
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/web-mode/header7
-rw-r--r--users/wpcarro/emacs/.emacs.d/snippets/web-mode/index-boilerplate18
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/>.el28
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/buffer.el174
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/clipboard.el40
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/constants.el29
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/display.el103
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/email.el76
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/fonts.el99
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/ivy-helpers.el67
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/kbd.el85
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/keybindings.el495
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/keyboard.el138
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/modeline.el68
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/prelude.el144
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/pulse-audio.el69
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/region.el23
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/screen-brightness.el57
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/scrot.el54
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/ssh.el67
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/window-manager.el228
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-clojure.el71
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-company.el41
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-dired.el51
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-dotnet.el16
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-elixir.el27
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-flycheck.el17
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-golang.el42
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-haskell.el53
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-javascript.el93
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-language-support.el36
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-lisp.el123
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-misc.el330
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-nix.el37
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-org.el39
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-package.el32
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-prolog.el19
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-python.el24
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-rust.el30
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-shell.el31
-rw-r--r--users/wpcarro/emacs/.emacs.d/wpc/wpc-ui.el186
-rw-r--r--users/wpcarro/emacs/AppIcon.icnsbin0 -> 449758 bytes
-rw-r--r--users/wpcarro/emacs/README.md15
-rw-r--r--users/wpcarro/emacs/ci.el44
-rw-r--r--users/wpcarro/emacs/default.nix267
-rw-r--r--users/wpcarro/emacs/elisp-conventions.md20
-rw-r--r--users/wpcarro/emacs/keybindings.md47
-rw-r--r--users/wpcarro/emacs/pkgs/al/al.el227
-rw-r--r--users/wpcarro/emacs/pkgs/al/default.nix28
-rw-r--r--users/wpcarro/emacs/pkgs/al/tests.el53
-rw-r--r--users/wpcarro/emacs/pkgs/bag/bag.el78
-rw-r--r--users/wpcarro/emacs/pkgs/bag/default.nix26
-rw-r--r--users/wpcarro/emacs/pkgs/bag/tests.el32
-rw-r--r--users/wpcarro/emacs/pkgs/bookmark/bookmark.el50
-rw-r--r--users/wpcarro/emacs/pkgs/bookmark/default.nix13
-rw-r--r--users/wpcarro/emacs/pkgs/bytes/bytes.el94
-rw-r--r--users/wpcarro/emacs/pkgs/bytes/default.nix25
-rw-r--r--users/wpcarro/emacs/pkgs/bytes/tests.el18
-rw-r--r--users/wpcarro/emacs/pkgs/cycle/README.md7
-rw-r--r--users/wpcarro/emacs/pkgs/cycle/cycle.el194
-rw-r--r--users/wpcarro/emacs/pkgs/cycle/default.nix36
-rw-r--r--users/wpcarro/emacs/pkgs/cycle/tests.el79
-rw-r--r--users/wpcarro/emacs/pkgs/fs/default.nix29
-rw-r--r--users/wpcarro/emacs/pkgs/fs/fs.el47
-rw-r--r--users/wpcarro/emacs/pkgs/fs/tests.el26
-rw-r--r--users/wpcarro/emacs/pkgs/list/README.md19
-rw-r--r--users/wpcarro/emacs/pkgs/list/default.nix26
-rw-r--r--users/wpcarro/emacs/pkgs/list/list.el219
-rw-r--r--users/wpcarro/emacs/pkgs/list/tests.el107
-rw-r--r--users/wpcarro/emacs/pkgs/macros/default.nix10
-rw-r--r--users/wpcarro/emacs/pkgs/macros/macros.el45
-rw-r--r--users/wpcarro/emacs/pkgs/math/default.nix30
-rw-r--r--users/wpcarro/emacs/pkgs/math/math.el63
-rw-r--r--users/wpcarro/emacs/pkgs/math/tests.el25
-rw-r--r--users/wpcarro/emacs/pkgs/maybe/default.nix24
-rw-r--r--users/wpcarro/emacs/pkgs/maybe/maybe.el54
-rw-r--r--users/wpcarro/emacs/pkgs/maybe/tests.el25
-rw-r--r--users/wpcarro/emacs/pkgs/passage/README.md12
-rw-r--r--users/wpcarro/emacs/pkgs/passage/default.nix12
-rw-r--r--users/wpcarro/emacs/pkgs/passage/passage.el65
-rw-r--r--users/wpcarro/emacs/pkgs/set/default.nix32
-rw-r--r--users/wpcarro/emacs/pkgs/set/set.el116
-rw-r--r--users/wpcarro/emacs/pkgs/set/tests.el69
-rw-r--r--users/wpcarro/emacs/pkgs/string/default.nix27
-rw-r--r--users/wpcarro/emacs/pkgs/string/string.el98
-rw-r--r--users/wpcarro/emacs/pkgs/string/tests.el22
-rw-r--r--users/wpcarro/emacs/pkgs/struct/README.md6
-rw-r--r--users/wpcarro/emacs/pkgs/struct/default.nix29
-rw-r--r--users/wpcarro/emacs/pkgs/struct/struct.el65
-rw-r--r--users/wpcarro/emacs/pkgs/struct/tests.el44
-rw-r--r--users/wpcarro/emacs/pkgs/symbol/default.nix24
-rw-r--r--users/wpcarro/emacs/pkgs/symbol/symbol.el38
-rw-r--r--users/wpcarro/emacs/pkgs/symbol/tests.el22
-rw-r--r--users/wpcarro/emacs/pkgs/theme/default.nix14
-rw-r--r--users/wpcarro/emacs/pkgs/theme/theme.el78
-rw-r--r--users/wpcarro/emacs/pkgs/tuple/default.nix10
-rw-r--r--users/wpcarro/emacs/pkgs/tuple/tuple.el93
-rw-r--r--users/wpcarro/emacs/pkgs/vector/default.nix21
-rw-r--r--users/wpcarro/emacs/pkgs/vector/tests.el20
-rw-r--r--users/wpcarro/emacs/pkgs/vector/vector.el58
-rw-r--r--users/wpcarro/emacs/pkgs/vterm-mgt/README.md17
-rw-r--r--users/wpcarro/emacs/pkgs/vterm-mgt/default.nix19
-rw-r--r--users/wpcarro/emacs/pkgs/vterm-mgt/vterm-mgt.el140
-rw-r--r--users/wpcarro/emacs/pkgs/zle/default.nix10
-rw-r--r--users/wpcarro/emacs/pkgs/zle/zle.el90
-rw-r--r--users/wpcarro/emacs/snippets.md22
-rw-r--r--users/wpcarro/emacs/workspace.josh0
-rw-r--r--users/wpcarro/go/.envrc2
-rw-r--r--users/wpcarro/go/actors.go45
-rw-r--r--users/wpcarro/go/atomic-counters.go26
-rw-r--r--users/wpcarro/go/channels.go81
-rw-r--r--users/wpcarro/go/mutex.go53
-rw-r--r--users/wpcarro/go/shell.nix9
-rw-r--r--users/wpcarro/go/waitgroups.go24
-rw-r--r--users/wpcarro/gopkgs/kv/default.nix8
-rw-r--r--users/wpcarro/gopkgs/kv/kv.go39
-rw-r--r--users/wpcarro/gopkgs/utils/default.nix8
-rw-r--r--users/wpcarro/gopkgs/utils/utils.go131
-rw-r--r--users/wpcarro/haskell-file/.envrc2
-rw-r--r--users/wpcarro/haskell-file/README.md7
-rw-r--r--users/wpcarro/haskell-file/f-todo.org67
-rw-r--r--users/wpcarro/haskell-file/f.hs64
-rw-r--r--users/wpcarro/haskell-file/shell.nix5
-rw-r--r--users/wpcarro/haskell-file/tests.hs39
-rw-r--r--users/wpcarro/keys.nix20
-rw-r--r--users/wpcarro/lib/default.nix5
-rw-r--r--users/wpcarro/lisp/README.md16
-rw-r--r--users/wpcarro/lisp/prelude.lisp14
-rw-r--r--users/wpcarro/lisp/prelude.nix8
-rw-r--r--users/wpcarro/nixos/ava/ava.el61
-rw-r--r--users/wpcarro/nixos/ava/default.nix150
-rw-r--r--users/wpcarro/nixos/default.nix24
-rw-r--r--users/wpcarro/nixos/iso.nix17
-rw-r--r--users/wpcarro/nixos/kyoko/default.nix153
-rw-r--r--users/wpcarro/nixos/kyoko/kyoko.el61
-rw-r--r--users/wpcarro/nixos/marcus/default.nix169
-rw-r--r--users/wpcarro/nixos/marcus/hardware.nix29
-rw-r--r--users/wpcarro/nixos/marcus/marcus.el40
-rw-r--r--users/wpcarro/nixos/modules/.skip-subtree1
-rw-r--r--users/wpcarro/nixos/modules/hadrian-cache.nix17
-rw-r--r--users/wpcarro/nixos/modules/hardware/dell-emc-egw-5200.nix47
-rw-r--r--users/wpcarro/nixos/modules/hardware/nopn.nix53
-rw-r--r--users/wpcarro/nixos/modules/laptop.nix15
-rw-r--r--users/wpcarro/nixos/modules/nginx.nix45
-rw-r--r--users/wpcarro/nixos/tarasco/default.nix144
-rw-r--r--users/wpcarro/nixos/tarasco/tarasco.el61
-rw-r--r--users/wpcarro/playbooks/README.md3
-rw-r--r--users/wpcarro/playbooks/first-of-the-month.org12
-rw-r--r--users/wpcarro/playbooks/habits.org49
-rw-r--r--users/wpcarro/playbooks/hip_opening_challenge/poses.pdfbin0 -> 2853812 bytes
-rw-r--r--users/wpcarro/playbooks/hip_opening_challenge/progress.org65
-rw-r--r--users/wpcarro/playbooks/nix_gcr/README.md62
-rw-r--r--users/wpcarro/playbooks/nix_gcr/cloud_run.nix14
-rw-r--r--users/wpcarro/playbooks/nix_gcr/config.lisp21
-rw-r--r--users/wpcarro/playbooks/shell.md12
-rw-r--r--users/wpcarro/playbooks/sqlite3.md115
-rw-r--r--users/wpcarro/scratch/README.md6
-rw-r--r--users/wpcarro/scratch/advent-of-code-2019/README.md4
-rw-r--r--users/wpcarro/scratch/advent-of-code-2019/day_1.py119
-rw-r--r--users/wpcarro/scratch/advent-of-code-2019/day_2.py32
-rw-r--r--users/wpcarro/scratch/advent-of-code-2019/day_3.py137
-rw-r--r--users/wpcarro/scratch/advent-of-code-2019/day_4.py35
-rw-r--r--users/wpcarro/scratch/advent-of-code-2019/day_5.py170
-rw-r--r--users/wpcarro/scratch/advent-of-code-2019/day_6.py155
-rw-r--r--users/wpcarro/scratch/advent-of-code-2019/day_7.py49
-rw-r--r--users/wpcarro/scratch/blockchain/default.nix14
-rw-r--r--users/wpcarro/scratch/blockchain/main.py263
-rw-r--r--users/wpcarro/scratch/blockchain/setup.py10
-rw-r--r--users/wpcarro/scratch/compiler/.envrc3
-rw-r--r--users/wpcarro/scratch/compiler/.gitignore5
-rw-r--r--users/wpcarro/scratch/compiler/debug.ml66
-rw-r--r--users/wpcarro/scratch/compiler/expr_parser.ml187
-rw-r--r--users/wpcarro/scratch/compiler/inference.ml183
-rw-r--r--users/wpcarro/scratch/compiler/parser.ml47
-rw-r--r--users/wpcarro/scratch/compiler/prettify.ml9
-rw-r--r--users/wpcarro/scratch/compiler/register_vm.ml129
-rw-r--r--users/wpcarro/scratch/compiler/register_vm.py161
-rw-r--r--users/wpcarro/scratch/compiler/shell.nix9
-rw-r--r--users/wpcarro/scratch/compiler/tests.ml43
-rw-r--r--users/wpcarro/scratch/compiler/type_parser.ml104
-rw-r--r--users/wpcarro/scratch/compiler/types.ml31
-rw-r--r--users/wpcarro/scratch/compiler/vec.ml127
-rw-r--r--users/wpcarro/scratch/crack_the_coding_interview/11_1.py40
-rw-r--r--users/wpcarro/scratch/crack_the_coding_interview/to_tree.hs11
-rw-r--r--users/wpcarro/scratch/cryptopals/.gitignore1
-rw-r--r--users/wpcarro/scratch/cryptopals/README.md3
-rw-r--r--users/wpcarro/scratch/cryptopals/set1/4.txt327
-rw-r--r--users/wpcarro/scratch/cryptopals/set1/c1.py19
-rw-r--r--users/wpcarro/scratch/cryptopals/set1/c2.py20
-rw-r--r--users/wpcarro/scratch/cryptopals/set1/c3.py50
-rw-r--r--users/wpcarro/scratch/cryptopals/set1/c4.py23
-rw-r--r--users/wpcarro/scratch/cryptopals/set1/c5.py16
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/array-traversals.py87
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/balanced-binary-tree.py145
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/bit-manipulation.py32
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/bracket-validator.py63
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/bst-checker.py121
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/cafe-order-checker.py91
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/cake-thief.py71
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/coins.py57
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/conways-game-of-life.py78
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/delete-node.py60
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/dft.py65
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/dijkstra-shortest-path.py48
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/find-duplicate-optimize-for-space-beast.py56
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/find-duplicate-optimize-for-space.py61
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/find-rotation-point.py59
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/find-unique-int-among-duplicates.py45
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/fixtures.py110
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/graph-coloring.py180
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/graph-to-graphviz.py39
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/highest-product-of-3.py89
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/inflight-entertainment.py35
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/knapsack-0-1.py38
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/kth-to-last.py82
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/largest-stack.py107
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/linked-list-cycles.py88
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/memo.py60
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/merge-sort.py28
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/merging-ranges.py94
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/mesh-message.gv11
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/mesh-message.py97
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/norman.py78
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/nth-fibonacci.py59
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/optimal-stopping.py49
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/perm-tree.py83
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/permutation-palindrome.py49
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/permutations.py55
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/plot.py9
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/product-of-other-numbers.py68
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/queue-two-stacks.py66
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/rectangular-love.py246
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/recursive-string-permutations.py37
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/reverse-linked-list.py79
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/reverse-words.py181
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/second-largest-item-bst.py179
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/shortest-path-inject-vertices.py94
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/shuffle.py34
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/string-reverse.py22
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/temperature-tracker.py84
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/test.txt1
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/top-scores.py25
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/topo-sort.py31
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/trickling-water.py38
-rw-r--r--users/wpcarro/scratch/data_structures_and_algorithms/which-appears-twice.py33
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/balanced-binary-tree.py123
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/dijkstra.py26
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/efficiency.org6
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/find-rotation-point.py55
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/inflight-entertainment.py51
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/kth-to-last.py64
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/merging-ranges.py59
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/recursive-string-permutations.py56
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/reverse-linked-list.py74
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/stock-price.py51
-rw-r--r--users/wpcarro/scratch/deepmind/part_one/which-appears-twice.py29
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/.envrc2
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/balanced-binary-tree.py126
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/bst-checker.py110
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/cafe-order-checker.py64
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/coin.ts102
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/delete-node.py57
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/find-duplicate-optimize-for-space-beast-mode.py114
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/find-duplicate-optimize-for-space.ts70
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/find-rotation-point.ts68
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/graph-coloring.ts232
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/highest-product-of-3.py81
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/inflight-entertainment.ts85
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/merge-sorted-arrays.ts63
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/merging-ranges.py115
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/mesh-message.py183
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/misc/matrix-traversals.py104
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/nth-fibonacci.py72
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/package-lock.json79
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/package.json16
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/permutation-palindrome.py37
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/product-of-other-numbers.py68
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/recursive-string-permutations.ts85
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/reverse-string-in-place.ts13
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/reverse-words.py74
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/second-largest-item-in-bst.ts219
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/shell.nix10
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/shuffle.py20
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/stock-price.py54
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/todo.org77
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/top-scores.py47
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/top-scores.ts57
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/tsconfig.json7
-rw-r--r--users/wpcarro/scratch/deepmind/part_two/word-cloud.py79
-rw-r--r--users/wpcarro/scratch/facebook/anglocize-int.py71
-rw-r--r--users/wpcarro/scratch/facebook/balanced-binary-tree.py70
-rw-r--r--users/wpcarro/scratch/facebook/breakfast-generator.py112
-rw-r--r--users/wpcarro/scratch/facebook/bst-checker.py49
-rw-r--r--users/wpcarro/scratch/facebook/cafe-order-checker.py19
-rw-r--r--users/wpcarro/scratch/facebook/cake_thief.py61
-rw-r--r--users/wpcarro/scratch/facebook/camping-knapsack.py46
-rw-r--r--users/wpcarro/scratch/facebook/coin.py50
-rw-r--r--users/wpcarro/scratch/facebook/count-islands.py53
-rw-r--r--users/wpcarro/scratch/facebook/delete-node.py19
-rw-r--r--users/wpcarro/scratch/facebook/dijkstras.py38
-rw-r--r--users/wpcarro/scratch/facebook/edit-distance.py47
-rw-r--r--users/wpcarro/scratch/facebook/evaluator.hs39
-rw-r--r--users/wpcarro/scratch/facebook/evaluator.py234
-rw-r--r--users/wpcarro/scratch/facebook/find-duplicate-beast-mode.py57
-rw-r--r--users/wpcarro/scratch/facebook/find-duplicate-optimize-for-space.py22
-rw-r--r--users/wpcarro/scratch/facebook/find-rotation-point.py47
-rw-r--r--users/wpcarro/scratch/facebook/find-unique-int-among-duplicates.py17
-rw-r--r--users/wpcarro/scratch/facebook/graph-coloring.py60
-rw-r--r--users/wpcarro/scratch/facebook/hard/binary-adder.py22
-rw-r--r--users/wpcarro/scratch/facebook/hard/fisher-yates.py7
-rw-r--r--users/wpcarro/scratch/facebook/hard/random-choice.py50
-rw-r--r--users/wpcarro/scratch/facebook/hard/suffix-tree.py93
-rw-r--r--users/wpcarro/scratch/facebook/heap.py30
-rw-r--r--users/wpcarro/scratch/facebook/highest-product-of-3.py20
-rw-r--r--users/wpcarro/scratch/facebook/infix-to-postfix.py51
-rw-r--r--users/wpcarro/scratch/facebook/inflight-entertainment.py29
-rw-r--r--users/wpcarro/scratch/facebook/intersecting-linked-lists.py34
-rw-r--r--users/wpcarro/scratch/facebook/interview-cake/bst-checker.py14
-rw-r--r--users/wpcarro/scratch/facebook/interview-cake/cafe-order-checker.py34
-rw-r--r--users/wpcarro/scratch/facebook/interview-cake/linked-list-cycles.py70
-rw-r--r--users/wpcarro/scratch/facebook/interview-cake/merge-sorted-arrays.py30
-rw-r--r--users/wpcarro/scratch/facebook/interview-cake/nth-fibonacci.py6
-rw-r--r--users/wpcarro/scratch/facebook/interview-cake/permutation-palindrome.py8
-rw-r--r--users/wpcarro/scratch/facebook/interview-cake/queue-two-stacks.py17
-rw-r--r--users/wpcarro/scratch/facebook/knapsack-faq.py42
-rw-r--r--users/wpcarro/scratch/facebook/kth-to-last-node-in-singly-linked-list.py26
-rw-r--r--users/wpcarro/scratch/facebook/language.py70
-rw-r--r--users/wpcarro/scratch/facebook/language2.py50
-rw-r--r--users/wpcarro/scratch/facebook/largest-contiguous-sum.py15
-rw-r--r--users/wpcarro/scratch/facebook/largest-stack.py49
-rw-r--r--users/wpcarro/scratch/facebook/leetcode.org163
-rw-r--r--users/wpcarro/scratch/facebook/linked-list-cycles.py26
-rw-r--r--users/wpcarro/scratch/facebook/linked_list.py22
-rw-r--r--users/wpcarro/scratch/facebook/london-knapsack.py42
-rw-r--r--users/wpcarro/scratch/facebook/longest-common-substring.py20
-rw-r--r--users/wpcarro/scratch/facebook/merge-sorted-arrays.py44
-rw-r--r--users/wpcarro/scratch/facebook/merging-ranges.py23
-rw-r--r--users/wpcarro/scratch/facebook/mesh-message.py40
-rw-r--r--users/wpcarro/scratch/facebook/moderate/decompress-xml.py98
-rw-r--r--users/wpcarro/scratch/facebook/moderate/find-pairs-for-sum.py19
-rw-r--r--users/wpcarro/scratch/facebook/moderate/parser.py37
-rw-r--r--users/wpcarro/scratch/facebook/moderate/rand7.py25
-rw-r--r--users/wpcarro/scratch/facebook/moderate/tic-tac-toe-checker.py99
-rw-r--r--users/wpcarro/scratch/facebook/moderate/unsorted-substring.py67
-rw-r--r--users/wpcarro/scratch/facebook/move-zeroes-to-end.py62
-rw-r--r--users/wpcarro/scratch/facebook/mst.py71
-rw-r--r--users/wpcarro/scratch/facebook/n-queens.py46
-rw-r--r--users/wpcarro/scratch/facebook/nearby-words.py33
-rw-r--r--users/wpcarro/scratch/facebook/node.py38
-rw-r--r--users/wpcarro/scratch/facebook/nth-fibonacci.py13
-rw-r--r--users/wpcarro/scratch/facebook/onsite.txt22
-rw-r--r--users/wpcarro/scratch/facebook/parsing/json.py121
-rw-r--r--users/wpcarro/scratch/facebook/parsing/parser.py28
-rw-r--r--users/wpcarro/scratch/facebook/parsing/regex.py184
-rw-r--r--users/wpcarro/scratch/facebook/permutation-palindrome.py17
-rw-r--r--users/wpcarro/scratch/facebook/polynomial-rolling-hash.py72
-rw-r--r--users/wpcarro/scratch/facebook/product-of-all-other-numbers.py33
-rw-r--r--users/wpcarro/scratch/facebook/queue-two-stacks.py20
-rw-r--r--users/wpcarro/scratch/facebook/rabin-karp.py27
-rw-r--r--users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/magic-index.py33
-rw-r--r--users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/making-change.py56
-rw-r--r--users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/paint-fill.py36
-rw-r--r--users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/parenthesize-bools.py114
-rw-r--r--users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/permutations.py13
-rw-r--r--users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/robot-grid-traversal.py28
-rw-r--r--users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/staircase.py1
-rw-r--r--users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/subsets.py41
-rw-r--r--users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/valid-parens.py50
-rw-r--r--users/wpcarro/scratch/facebook/recursive-string-permutations.py19
-rw-r--r--users/wpcarro/scratch/facebook/reverse-linked-list.py25
-rw-r--r--users/wpcarro/scratch/facebook/reverse-string-in-place.py14
-rw-r--r--users/wpcarro/scratch/facebook/reverse-words.py8
-rw-r--r--users/wpcarro/scratch/facebook/scratch.py94
-rw-r--r--users/wpcarro/scratch/facebook/second-largest-item-in-bst.py22
-rw-r--r--users/wpcarro/scratch/facebook/shuffle.py17
-rw-r--r--users/wpcarro/scratch/facebook/stack.py25
-rw-r--r--users/wpcarro/scratch/facebook/stacking-boxes.py50
-rw-r--r--users/wpcarro/scratch/facebook/stock-price.py16
-rw-r--r--users/wpcarro/scratch/facebook/todo.org60
-rw-r--r--users/wpcarro/scratch/facebook/top-scores.py20
-rw-r--r--users/wpcarro/scratch/facebook/topo-sort.py61
-rw-r--r--users/wpcarro/scratch/facebook/traversals.py100
-rw-r--r--users/wpcarro/scratch/facebook/utils.py19
-rw-r--r--users/wpcarro/scratch/facebook/word-cloud.py32
-rw-r--r--users/wpcarro/scratch/groceries/.envrc2
-rw-r--r--users/wpcarro/scratch/groceries/export.hs22
-rw-r--r--users/wpcarro/scratch/groceries/list.org112
-rw-r--r--users/wpcarro/scratch/groceries/shell.nix5
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/.envrc2
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/.ghci1
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/applicative.hs213
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/basic-libraries.hs60
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/composing-types.hs75
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/foldable.hs107
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/io.hs35
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/monad-transformers.hs183
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/monad.hs178
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/non-strictness.hs6
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/reader.hs149
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/shell.nix8
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/state.hs93
-rw-r--r--users/wpcarro/scratch/haskell-programming-from-first-principles/traversable.hs131
-rw-r--r--users/wpcarro/scratch/picoctf/.skip-subtree0
-rw-r--r--users/wpcarro/scratch/picoctf/README.md3
-rw-r--r--users/wpcarro/scratch/picoctf/challenge_144.py11
-rw-r--r--users/wpcarro/scratch/picoctf/challenge_156.py13
-rw-r--r--users/wpcarro/scratch/picoctf/challenge_166/ende.py60
-rw-r--r--users/wpcarro/scratch/picoctf/challenge_166/flag.txt.en1
-rw-r--r--users/wpcarro/scratch/picoctf/challenge_166/pw.txt1
-rw-r--r--users/wpcarro/scratch/picoctf/challenge_166/shell.nix8
-rw-r--r--users/wpcarro/scratch/picoctf/challenge_170/README.md11
-rw-r--r--users/wpcarro/scratch/rust/.gitignore1
-rw-r--r--users/wpcarro/scratch/rust/Cargo.lock89
-rw-r--r--users/wpcarro/scratch/rust/Cargo.toml8
-rw-r--r--users/wpcarro/scratch/rust/README.md11
-rw-r--r--users/wpcarro/scratch/rust/shell.nix7
-rw-r--r--users/wpcarro/scratch/rust/src/display/mod.rs13
-rw-r--r--users/wpcarro/scratch/rust/src/json/mod.rs81
-rw-r--r--users/wpcarro/scratch/rust/src/main.rs15
-rw-r--r--users/wpcarro/scratch/rust/src/rc/mod.rs12
-rw-r--r--users/wpcarro/scratch/rust/src/stdin/mod.rs22
-rw-r--r--users/wpcarro/scratch/simple-select/README.md71
-rw-r--r--users/wpcarro/scratch/simple-select/main.py262
-rw-r--r--users/wpcarro/scratch/simple-select/parser.py31
-rw-r--r--users/wpcarro/scratch/simple-select/scanner.py27
-rw-r--r--users/wpcarro/secrets.json.secretbin0 -> 1142 bytes
-rw-r--r--users/wpcarro/slx.js/.gitignore3
-rw-r--r--users/wpcarro/slx.js/README.md55
-rw-r--r--users/wpcarro/slx.js/default.nix11
-rw-r--r--users/wpcarro/slx.js/index.html13
-rw-r--r--users/wpcarro/slx.js/index.js455
-rw-r--r--users/wpcarro/slx.js/package.json14
-rw-r--r--users/wpcarro/slx.js/tests.js68
-rw-r--r--users/wpcarro/slx.js/yarn.lock1495
-rw-r--r--users/wpcarro/terraform/.gitignore4
-rw-r--r--users/wpcarro/terraform/default.nix192
-rw-r--r--users/wpcarro/todo-lists/cta-curriculum.csv108
-rw-r--r--users/wpcarro/todo-lists/imdb/db.sqlite3bin0 -> 24576 bytes
-rw-r--r--users/wpcarro/todo-lists/imdb/imdb-top-250.org256
-rw-r--r--users/wpcarro/todo-lists/imdb/scratch.sql65
-rw-r--r--users/wpcarro/todo-lists/paul-graham-essays.org190
-rw-r--r--users/wpcarro/todo-lists/travel-hitlist.md83
-rw-r--r--users/wpcarro/tools/monzo_ynab/.envrc9
-rw-r--r--users/wpcarro/tools/monzo_ynab/.gitignore3
-rw-r--r--users/wpcarro/tools/monzo_ynab/README.md41
-rw-r--r--users/wpcarro/tools/monzo_ynab/auth.go101
-rw-r--r--users/wpcarro/tools/monzo_ynab/main.go48
-rw-r--r--users/wpcarro/tools/monzo_ynab/monzo/client.go52
-rw-r--r--users/wpcarro/tools/monzo_ynab/monzo/serde.go72
-rw-r--r--users/wpcarro/tools/monzo_ynab/requests.txt80
-rw-r--r--users/wpcarro/tools/monzo_ynab/tokens.go279
-rw-r--r--users/wpcarro/tools/monzo_ynab/ynab/client.go9
-rw-r--r--users/wpcarro/tools/monzo_ynab/ynab/serde.go44
-rw-r--r--users/wpcarro/tools/rfcToKindle/LICENSE202
-rw-r--r--users/wpcarro/tools/rfcToKindle/README.md30
-rw-r--r--users/wpcarro/tools/rfcToKindle/default.nix11
-rw-r--r--users/wpcarro/tools/rfcToKindle/main.go89
-rw-r--r--users/wpcarro/tools/run/.envrc2
-rw-r--r--users/wpcarro/tools/run/README.md30
-rw-r--r--users/wpcarro/tools/run/default.nix11
-rw-r--r--users/wpcarro/tools/run/main.go49
-rw-r--r--users/wpcarro/tools/run/shell.nix9
-rw-r--r--users/wpcarro/tools/simple_vim/config.vim101
-rw-r--r--users/wpcarro/tools/simple_vim/default.nix5
-rw-r--r--users/wpcarro/tools/symlinkManager/README.md12
-rw-r--r--users/wpcarro/tools/symlinkManager/default.nix14
-rw-r--r--users/wpcarro/tools/symlinkManager/main.go61
-rw-r--r--users/wpcarro/tools/systemd-shell/default.nix8
-rw-r--r--users/wpcarro/tools/systemd-shell/setup.py9
-rw-r--r--users/wpcarro/tools/systemd-shell/systemd-shell36
-rw-r--r--users/wpcarro/tools/url-blocker/.envrc2
-rw-r--r--users/wpcarro/tools/url-blocker/Main.hs205
-rw-r--r--users/wpcarro/tools/url-blocker/README.md47
-rw-r--r--users/wpcarro/tools/url-blocker/default.nix34
-rw-r--r--users/wpcarro/tools/url-blocker/rules.json28
-rw-r--r--users/wpcarro/tools/url-blocker/shell.nix10
-rw-r--r--users/wpcarro/tools/wpcarro-deps.nix8
-rw-r--r--users/wpcarro/utils/README.md8
-rw-r--r--users/wpcarro/utils/builder.nix12
-rw-r--r--users/wpcarro/utils/default.nix15
-rw-r--r--users/wpcarro/utils/fs.nix42
-rw-r--r--users/wpcarro/website/README.md3
-rw-r--r--users/wpcarro/website/blog/.skip-subtree1
-rw-r--r--users/wpcarro/website/blog/default.nix47
-rw-r--r--users/wpcarro/website/blog/fragments/.skip-subtree0
-rw-r--r--users/wpcarro/website/blog/fragments/post.html8
-rw-r--r--users/wpcarro/website/blog/fragments/posts.html10
-rw-r--r--users/wpcarro/website/blog/posts.nix116
-rw-r--r--users/wpcarro/website/blog/posts/auto-reboot-nixos.md40
-rw-r--r--users/wpcarro/website/blog/posts/cell-phone-experiment.md274
-rw-r--r--users/wpcarro/website/blog/posts/csharp-unused-variables.md38
-rw-r--r--users/wpcarro/website/blog/posts/git-filter-repo-note.md59
-rw-r--r--users/wpcarro/website/blog/posts/git-rev-refs.md85
-rw-r--r--users/wpcarro/website/blog/posts/importing-subtrees.md147
-rw-r--r--users/wpcarro/website/blog/posts/nginx-curl-note.md5
-rw-r--r--users/wpcarro/website/blog/posts/nix-env-note.md33
-rw-r--r--users/wpcarro/website/blog/posts/nix-shell-note.md50
-rw-r--r--users/wpcarro/website/blog/posts/nixos-disk-full-note.md113
-rw-r--r--users/wpcarro/website/blog/posts/quassel-google-vm.md34
-rw-r--r--users/wpcarro/website/blog/posts/restic.md91
-rw-r--r--users/wpcarro/website/blog/posts/send-mail-as-2fa.md43
-rw-r--r--users/wpcarro/website/blog/posts/ssh-oddities.md59
-rw-r--r--users/wpcarro/website/blog/posts/tcp-tunneling-note.md63
-rw-r--r--users/wpcarro/website/blog/posts/tee-time.md16
-rw-r--r--users/wpcarro/website/default.nix77
-rw-r--r--users/wpcarro/website/fragments/.skip-subtree0
-rw-r--r--users/wpcarro/website/fragments/homepage.html20
-rw-r--r--users/wpcarro/website/fragments/template.html96
-rw-r--r--users/wpcarro/website/habit-screens/.envrc2
-rw-r--r--users/wpcarro/website/habit-screens/.gitignore2
-rw-r--r--users/wpcarro/website/habit-screens/README.md31
-rw-r--r--users/wpcarro/website/habit-screens/default.nix63
-rw-r--r--users/wpcarro/website/habit-screens/design.md43
-rw-r--r--users/wpcarro/website/habit-screens/elm-srcs.nix77
-rw-r--r--users/wpcarro/website/habit-screens/elm.json32
-rw-r--r--users/wpcarro/website/habit-screens/index.css3
-rw-r--r--users/wpcarro/website/habit-screens/index.html21
-rw-r--r--users/wpcarro/website/habit-screens/output.css103571
-rw-r--r--users/wpcarro/website/habit-screens/registry.datbin0 -> 103324 bytes
-rw-r--r--users/wpcarro/website/habit-screens/shell.nix9
-rw-r--r--users/wpcarro/website/habit-screens/src/Habits.elm463
-rw-r--r--users/wpcarro/website/habit-screens/src/Main.elm29
-rw-r--r--users/wpcarro/website/habit-screens/src/State.elm195
-rw-r--r--users/wpcarro/website/habit-screens/src/UI.elm9
-rw-r--r--users/wpcarro/website/habit-screens/src/Utils.elm37
-rw-r--r--users/wpcarro/website/sandbox/contentful/.envrc5
-rw-r--r--users/wpcarro/website/sandbox/contentful/.gitignore2
-rw-r--r--users/wpcarro/website/sandbox/contentful/README.md18
-rw-r--r--users/wpcarro/website/sandbox/contentful/default.nix24
-rw-r--r--users/wpcarro/website/sandbox/contentful/package.json26
-rw-r--r--users/wpcarro/website/sandbox/contentful/postcss.config.js5
-rw-r--r--users/wpcarro/website/sandbox/contentful/shell.nix8
-rw-r--r--users/wpcarro/website/sandbox/contentful/src/App.tsx49
-rw-r--r--users/wpcarro/website/sandbox/contentful/src/contentful.ts27
-rw-r--r--users/wpcarro/website/sandbox/contentful/src/index.css3
-rw-r--r--users/wpcarro/website/sandbox/contentful/src/index.html11
-rw-r--r--users/wpcarro/website/sandbox/contentful/src/index.tsx12
-rw-r--r--users/wpcarro/website/sandbox/contentful/src/store.ts36
-rw-r--r--users/wpcarro/website/sandbox/contentful/tailwind.config.js7
-rw-r--r--users/wpcarro/website/sandbox/contentful/tsconfig.json19
-rw-r--r--users/wpcarro/website/sandbox/contentful/yarn.lock5717
-rw-r--r--users/wpcarro/website/sandbox/covid-uk/default.nix.ignore16
-rw-r--r--users/wpcarro/website/sandbox/covid-uk/index.html99
-rw-r--r--users/wpcarro/website/sandbox/covid-uk/package.json16
-rw-r--r--users/wpcarro/website/sandbox/covid-uk/shell.nix8
-rw-r--r--users/wpcarro/website/sandbox/covid-uk/styles.css28
-rw-r--r--users/wpcarro/website/sandbox/covid-uk/tailwind.config.js7
-rw-r--r--users/wpcarro/website/sandbox/covid-uk/yarn.lock542
-rw-r--r--users/wpcarro/website/sandbox/default.nix.ignore13
-rw-r--r--users/wpcarro/website/sandbox/github-issues-service/README.md28
-rw-r--r--users/wpcarro/website/sandbox/index.html15
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/.envrc2
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/.gitignore2
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/README.md57
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/default.nix63
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/elm-srcs.nix67
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/elm.json30
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/ideas.org3
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/index.css3
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/index.html15
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/output.css103571
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/registry.datbin0 -> 93710 bytes
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/shell.nix9
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/FlashCard.elm42
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Icon.elm44
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Main.elm44
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Misc.elm59
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Overview.elm122
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Piano.elm194
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Practice.elm61
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Preferences.elm148
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Responsive.elm19
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/State.elm179
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Tailwind.elm29
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Tempo.elm33
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/Theory.elm1100
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/UI.elm159
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/.envrc7
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/.ghci7
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/API.hs16
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/App.hs57
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/Fixtures.hs67
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/GoogleSignIn.hs111
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/Main.hs37
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/Spec.hs74
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/Stripe.hs29
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/TestUtils.hs17
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/Types.hs146
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/Utils.hs8
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/default.nix28
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/index.html35
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/init.sql41
-rw-r--r--users/wpcarro/website/sandbox/learnpianochords/src/server/shell.nix18
-rw-r--r--users/wpcarro/website/sandbox/typo-po/README.md10
-rw-r--r--users/wpcarro/ynabsql/dataviz/.gitignore5
-rw-r--r--users/wpcarro/ynabsql/dataviz/.parcelrc3
l---------users/wpcarro/ynabsql/dataviz/cdn1
-rw-r--r--users/wpcarro/ynabsql/dataviz/components.jsx1256
-rw-r--r--users/wpcarro/ynabsql/dataviz/index.html19
-rw-r--r--users/wpcarro/ynabsql/dataviz/package.json15
-rw-r--r--users/wpcarro/ynabsql/dataviz/yarn.lock1540
-rw-r--r--users/wpcarro/zoo/.envrc2
-rw-r--r--users/wpcarro/zoo/.ghci5
-rw-r--r--users/wpcarro/zoo/Main.hs160
-rw-r--r--users/wpcarro/zoo/Spec.hs54
-rw-r--r--users/wpcarro/zoo/default.nix21
-rw-r--r--users/wpcarro/zoo/shell.nix10
1948 files changed, 347576 insertions, 7490 deletions
diff --git a/users/Profpatsch/.envrc b/users/Profpatsch/.envrc
new file mode 100644
index 0000000000..c91f923756
--- /dev/null
+++ b/users/Profpatsch/.envrc
@@ -0,0 +1,5 @@
+if pass apps/declib/mastodon_access_token >/dev/null; then
+    export DECLIB_MASTODON_ACCESS_TOKEN=$(pass apps/declib/mastodon_access_token)
+fi
+
+eval "$(lorri direnv)"
diff --git a/users/Profpatsch/.gitignore b/users/Profpatsch/.gitignore
new file mode 100644
index 0000000000..c33954f53a
--- /dev/null
+++ b/users/Profpatsch/.gitignore
@@ -0,0 +1 @@
+dist-newstyle/
diff --git a/users/Profpatsch/.hlint.yaml b/users/Profpatsch/.hlint.yaml
new file mode 100644
index 0000000000..f00f78c525
--- /dev/null
+++ b/users/Profpatsch/.hlint.yaml
@@ -0,0 +1,357 @@
+# HLint configuration file
+# https://github.com/ndmitchell/hlint
+# Run `hlint --default` to see the example configuration file.
+##########################
+
+# WARNING: These need to be synced with the default-extensions field
+# in the cabal file.
+- arguments: [-XGHC2021, -XOverloadedRecordDot]
+
+# Ignore some builtin hints
+
+# often functions are more readable with explicit arguments
+- ignore: { name: Eta reduce }
+
+# these redundancy warnings are just completely irrelevant
+- ignore: { name: Redundant bracket }
+- ignore: { name: Move brackets to avoid $ }
+- ignore: { name: Redundant $ }
+- ignore: { name: Redundant do }
+- ignore: { name: Redundant multi-way if }
+
+# allow case-matching on bool, because why not
+- ignore: { name: Use if }
+
+# hlint cannot distinguish actual newtypes from data types
+# that accidentally have only one field
+# (but might have more in the future).
+# Since itโ€™s a mostly irrelevant runtime optimization, we donโ€™t care.
+- ignore: { name: Use newtype instead of data }
+
+# these lead to harder-to-read/more implicit code
+- ignore: { name: Use fmap }
+- ignore: { name: Use <$> }
+- ignore: { name: Use tuple-section }
+- ignore: { name: Use forM_ }
+- ignore: { name: Functor law }
+# fst and snd are usually a code smell and should be explicit matches, _naming the ignored side.
+- ignore: { name: Use fst }
+- ignore: { name: Use snd }
+- ignore: { name: Use fromMaybe }
+- ignore: { name: Use const }
+- ignore: { name: Replace case with maybe }
+- ignore: { name: Replace case with fromMaybe }
+- ignore: { name: Avoid lambda }
+- ignore: { name: Avoid lambda using `infix` }
+- ignore: { name: Use curry }
+- ignore: { name: Use uncurry }
+- ignore: { name: Use first }
+- ignore: { name: Redundant first }
+- ignore: { name: Use second }
+- ignore: { name: Use bimap }
+# just use `not x`
+- ignore: { name: Use unless }
+- ignore: { name: Redundant <&> }
+
+# list comprehensions are a seldomly used part of the Haskell language
+# and they introduce syntactic overhead that is usually not worth the conciseness
+- ignore: { name: Use list comprehension }
+
+# Seems to be buggy in cases
+- ignore: { name: Use section }
+
+# multiple maps in a row are usually used for clarity,
+# and the compiler will optimize them away, thank you very much.
+- ignore: { name: Use map once }
+- ignore: { name: Fuse foldr/map }
+- ignore: { name: Fuse traverse/map }
+- ignore: { name: Fuse traverse_/map }
+- ignore: { name: Fuse traverse/<$> }
+
+# this is silly, why would I use a special function if I can just (heh) `== Nothing`
+- ignore: { name: Use isNothing }
+
+# The duplication heuristic is not very smart
+# and more annoying than helpful.
+# see https://github.com/ndmitchell/hlint/issues/1009
+- ignore: { name: Reduce duplication }
+
+# Stops the pattern match trick
+- ignore: { name: Use record patterns }
+- ignore: { name: Use null }
+- ignore: { name: Use uncurry }
+
+# we donโ€™t want void, see below
+- ignore: { name: Use void }
+
+- functions:
+    # disallow Enum instance functions, they are partial
+    - name: Prelude.succ
+      within: [Relude.Extra.Enum]
+      message: "Dangerous, will fail for highest element"
+    - name: Prelude.pred
+      within: [Relude.Extra.Enum]
+      message: "Dangerous, will fail for lowest element"
+    - name: Prelude.toEnum
+      within: []
+      message: "Extremely partial"
+    - name: Prelude.fromEnum
+      within: []
+      message: "Dangerous for most uses"
+    - name: Prelude.enumFrom
+      within: []
+    - name: Prelude.enumFromThen
+      within: []
+    - name: Prelude.enumFromThenTo
+      within: []
+    - name: Prelude.oundedEnumFrom
+      within: []
+    - name: Prelude.boundedEnumFromThen
+      within: []
+
+    - name: Text.Read.readMaybe
+      within:
+        # The BSON ObjectId depends on Read for parsing
+        - Milkmap.Milkmap
+        - Milkmap.FieldData.Value
+      message: "`readMaybe` is probably not what you want for parsing values, please use the `FieldParser` module."
+
+    # `void` discards its argument and is polymorphic,
+    # thus making it brittle in the face of code changes.
+    # (see https://tech.freckle.com/2020/09/23/void-is-a-smell/)
+    # Use an explicit `_ <- โ€ฆ` instead.
+    - name: Data.Functor.void
+      within: []
+      message: "`void` leads to bugs. Use an explicit `_ <- โ€ฆ` instead"
+
+    - name: Data.Foldable.length
+      within: ["MyPrelude"]
+      message: "`Data.Foldable.length` is dangerous to use, because it also works on types you wouldnโ€™t expect, like `length (3,4) == 1` and `length (Just 2) == 1`. Use the `length` function for your specific type instead, for example `List.length` or `Map.length`."
+
+    - name: Prelude.length
+      within: ["MyPrelude"]
+      message: "`Prelude.length` is dangerous to use, because it also works on types you wouldnโ€™t expect, like `length (3,4) == 1` and `length (Just 2) == 1`. Use the `length` function for your specific type instead, for example `List.length` or `Map.length`."
+
+    # Using an explicit lambda with its argument โ€œunderscoredโ€
+    # is more clear in every case.
+    # e.g. `const True` => `\_request -> True`
+    # shows the reader that the ignored argument was a request.
+    - name: Prelude.const
+      within: []
+      message: "Replace `const` with an explicit lambda with type annotation for code clarity and type safety, e.g.: `const True` => `\\(_ :: Request) -> True`. If you really donโ€™t want to spell out the type (which might lead to bugs!), you can also use something like `\_request -> True`."
+
+    - name: Data.List.nub
+      within: []
+      message: "O(nยฒ), use `Data.Containers.ListUtils.nubOrd"
+
+    - name: Prelude.maximum
+      within: []
+      message: "`maximum` crashes on empty list; use non-empty lists and `maximum1`"
+
+    - name: Data.List.maximum
+      within: []
+      message: "`maximum` crashes on empty list; use non-empty lists and `maximum1`"
+
+    - name: Prelude.minimum
+      within: []
+      message: "`minimum` crashes on empty list; use non-empty lists and `minimum1`"
+
+    - name: Data.List.minimum
+      within: []
+      message: "`minimum` crashes on empty list; use non-empty lists and `minimum1`"
+
+    - name: Data.Foldable.maximum
+      within: []
+      message: "`maximum` crashes on empty foldable stucture; use Foldable1 and `maximum1`."
+
+    - name: Data.Foldable.minimum
+      within: []
+      message: "`minimum` crashes on empty foldable stucture; use Foldable1 and `minimum1`."
+
+    # Using prelude functions instead of stdlib functions
+
+    - name: "Data.Text.Encoding.encodeUtf8"
+      within: ["MyPrelude"]
+      message: "Use `textToBytesUtf8`"
+
+    - name: "Data.Text.Lazy.Encoding.encodeUtf8"
+      within: ["MyPrelude"]
+      message: "Use `textToBytesUtf8Lazy`"
+
+    - name: "Data.Text.Encoding.decodeUtf8'"
+      within: ["MyPrelude"]
+      message: "Use `bytesToTextUtf8`"
+
+    - name: "Data.Text.Encoding.Lazy.decodeUtf8'"
+      within: ["MyPrelude"]
+      message: "Use `bytesToTextUtf8Lazy`"
+
+    - name: "Data.Text.Encoding.decodeUtf8"
+      within: ["MyPrelude"]
+      message: "Either check for errors with `bytesToTextUtf8`, decode leniently with unicode replacement characters with `bytesToTextUtf8Lenient` or use the crashing version `bytesToTextUtf8Unsafe` (discouraged)."
+
+    - name: "Data.Text.Encoding.Lazy.decodeUtf8"
+      within: ["MyPrelude"]
+      message: "Either check for errors with `bytesToTextUtf8Lazy`, decode leniently with unicode replacement characters with `bytesToTextUtf8LenientLazy` or use the crashing version `bytesToTextUtf8UnsafeLazy` (discouraged)."
+
+    - name: "Data.Text.Lazy.toStrict"
+      within: ["MyPrelude"]
+      message: "Use `toStrict`"
+
+    - name: "Data.Text.Lazy.fromStrict"
+      within: ["MyPrelude"]
+      message: "Use `toLazy`"
+
+    - name: "Data.ByteString.Lazy.toStrict"
+      within: ["MyPrelude"]
+      message: "Use `toStrictBytes`"
+
+    - name: "Data.ByteString.Lazy.fromStrict"
+      within: ["MyPrelude"]
+      message: "Use `toLazyBytes`"
+
+    - name: "Data.Text.unpack"
+      within: ["MyPrelude"]
+      message: "Use `textToString`"
+
+    - name: "Data.Text.pack"
+      within: ["MyPrelude"]
+      message: "Use `stringToText`"
+
+    - name: "Data.Maybe.listToMaybe"
+      within: []
+      message: |
+        `listToMaybe`` throws away everything but the first element of a list (it is essentially `safeHead`).
+        If that is what you want, please use a pattern match like
+
+        ```
+        case xs of
+          [] -> โ€ฆ
+          (x:_) -> โ€ฆ
+        ```
+
+    - name: "Data.List.head"
+      within: []
+      message: |
+        `List.head` fails on an empty list. I didnโ€™t think I have to say this, but please use a pattern match on the list, like:
+
+        ```
+        case xs of
+          [] -> โ€ฆ error handling โ€ฆ
+          (x:_) -> โ€ฆ
+        ```
+
+        Also think about why the rest of the list should be ignored.
+
+    - name: "Prelude.head"
+      within: []
+      message: |
+        `List.head` fails on an empty list. I didnโ€™t think I have to say this, but please use a pattern match on the list, like.
+
+        ```
+        case xs of
+          [] -> โ€ฆ error handling โ€ฆ
+          (x:_) -> โ€ฆ
+        ```
+
+        Also think about why the rest of the list should be ignored.
+
+    - name: "Data.Maybe.fromJust"
+      within: []
+      message: |
+        `Maybe.fromJust` is obviously partial. Please use a pattern match.
+
+        In case you actually want to throw an error on an empty list,
+        please add an error message, like so:
+
+        ```
+        myMaybe & annotate "my error message" & unwrapError
+        ```
+
+        If you are in `IO`, use `unwrapIOError` instead,
+        or throw a monad-specific error.
+
+    - name: "Data.Either.fromLeft"
+      within: []
+      message: |
+        `Either.fromLeft` is obviously partial. Please use a pattern match.
+
+    - name: "Data.Either.fromRight"
+      within: []
+      message: |
+        `Either.fromRight` is obviously partial. Please use a pattern match.
+
+# Make restricted functions into an error if found
+- error: { name: "Avoid restricted function, see comment in .hlint.yaml" }
+
+# Some functions that have (more modern) aliases.
+# They are not dangerous per se,
+# but we want to make it easier to read our code so we should
+# make sure we donโ€™t use too many things that are renames.
+
+- hint:
+    lhs: "undefined"
+    rhs: "todo"
+    note: "`undefined` is a silent error, `todo` will display a warning as long as it exists in the code."
+
+- hint:
+    lhs: "return"
+    rhs: "pure"
+    note: "Use `pure` from `Applicative` instead, itโ€™s the exact same function."
+
+- hint:
+    lhs: "mapM"
+    rhs: "traverse"
+    note: "Use `traverse` from `Traversable` instead. Itโ€™s the exact same function."
+
+- hint:
+    lhs: "mapM_"
+    rhs: "traverse_"
+    note: "Use `traverse_` from `Traversable` instead. Itโ€™s the exact same function."
+
+- hint:
+    lhs: "forM"
+    rhs: "for"
+    note: "Use `for` from `Traversable` instead. Itโ€™s the exact same function."
+
+- hint:
+    lhs: "forM_"
+    rhs: "for_"
+    note: "Use `for_` from `Traversable` instead. Itโ€™s the exact same function."
+
+- hint:
+    lhs: "stringToText (show x)"
+    rhs: "showToText x"
+
+- hint:
+    lhs: "Data.Set.toList (Data.Set.fromList x)"
+    rhs: "List.nubOrd x"
+    note: "`nubOrd` removes duplicate elements from a list."
+
+- modules:
+    # Disallowed Modules
+    - name: Data.Map
+      within: []
+      message: "Lazy maps leak space, use `import Data.Map.Strict as Map` instead"
+    - name: Control.Monad.Writer
+      within: []
+      message: "Lazy writers leak space, use `Control.Monad.Trans.Writer.CPS` instead"
+    - name: Control.Monad.Trans.Writer.Lazy
+      within: []
+      message: "Lazy writers leak space, use `Control.Monad.Trans.Writer.CPS` instead"
+    - name: Control.Monad.Trans.Writer.Strict
+      within: []
+      message: "Even strict writers leak space, use `Control.Monad.Trans.Writer.CPS` instead"
+
+    # Qualified module imports
+    - { name: Data.Map.Strict, as: Map }
+    - { name: Data.HashMap.Strict, as: HashMap }
+    - { name: Data.Set, as: Set }
+    - { name: Data.ByteString.Char8, as: Char8 }
+    - { name: Data.ByteString.Lazy.Char8, as: Char8.Lazy }
+    - { name: Data.Text, as: Text }
+    - { name: Data.Vector, as: Vector }
+    - { name: Data.Vault.Lazy, as: Vault }
+    - { name: Data.Aeson, as: Json }
+    - { name: Data.Aeson.Types, as: Json }
+    - { name: Data.Aeson.BetterErrors as Json }
diff --git a/users/Profpatsch/.vscode/launch.json b/users/Profpatsch/.vscode/launch.json
new file mode 100644
index 0000000000..baa087d437
--- /dev/null
+++ b/users/Profpatsch/.vscode/launch.json
@@ -0,0 +1,18 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "run declib",
+            "type": "node",
+            "cwd": "${workspaceFolder}/declib",
+            "request": "launch",
+            "runtimeExecutable": "ninja",
+            "runtimeArgs": [
+                "run",
+            ],
+        }
+    ]
+}
diff --git a/users/Profpatsch/.vscode/settings.json b/users/Profpatsch/.vscode/settings.json
new file mode 100644
index 0000000000..7984076c16
--- /dev/null
+++ b/users/Profpatsch/.vscode/settings.json
@@ -0,0 +1,25 @@
+{
+    "sqltools.connections": [
+        {
+            "previewLimit": 50,
+            "driver": "SQLite",
+            "name": "cas-serve",
+            "database": "${workspaceFolder:Profpatsch}/cas-serve/data.sqlite"
+        }
+    ],
+    "sqltools.useNodeRuntime": true,
+    "editor.formatOnSave": true,
+    "[typescript]": {
+        "editor.defaultFormatter": "esbenp.prettier-vscode"
+    },
+    "[javascript]": {
+        "editor.defaultFormatter": "esbenp.prettier-vscode"
+    },
+    "[json]": {
+        "editor.defaultFormatter": "esbenp.prettier-vscode"
+    },
+    "purescript.codegenTargets": [
+        "corefn"
+    ],
+    "purescript.foreignExt": "nix"
+}
diff --git a/users/Profpatsch/OWNERS b/users/Profpatsch/OWNERS
index 5a73d4c3a1..ac23e72256 100644
--- a/users/Profpatsch/OWNERS
+++ b/users/Profpatsch/OWNERS
@@ -1,4 +1,4 @@
-inherited: false
-owners:
-  - Profpatsch
-  - sterni
+set noparent
+
+Profpatsch
+sterni
diff --git a/users/Profpatsch/README.md b/users/Profpatsch/README.md
new file mode 100644
index 0000000000..5bb74cd758
--- /dev/null
+++ b/users/Profpatsch/README.md
@@ -0,0 +1,10 @@
+# Profpatschโ€™s assemblage of peculiarities and curiosities
+
+Welcome, Welcome.
+
+Welcome to my user dir, where we optimize f*** around, in order to optimize finding out.
+
+![fafo graph](./fafo.jpg)
+
+DISCLAIMER: All of this code is of the โ€œdo not try at workโ€ sort, unless noted otherwise.
+You might try at home, however. Get inspired or get grossed out, whichever you like.
diff --git a/users/Profpatsch/alacritty.nix b/users/Profpatsch/alacritty.nix
new file mode 100644
index 0000000000..d3461c4aad
--- /dev/null
+++ b/users/Profpatsch/alacritty.nix
@@ -0,0 +1,27 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.alacritty [ "alacritty" ];
+
+  config =
+    {
+      alacritty-config = { font.size = 18; scrolling.history = 100000; };
+      #  This disables the dpi-sensitive scaling (cause otherwise the font will be humongous on my laptop screen)
+      alacritty-env.WINIT_X11_SCALE_FACTOR = 1;
+    };
+
+
+  config-file = (pkgs.formats.toml { }).generate "alacritty.conf" config.alacritty-config;
+
+  alacritty = depot.nix.writeExecline "alacritty" { } (
+    (lib.concatLists (lib.mapAttrsToList (k: v: [ "export" k (toString v) ]) config.alacritty-env))
+    ++ [
+      bins.alacritty
+      "--config-file"
+      config-file
+      "$@"
+    ]
+  );
+
+in
+alacritty
diff --git a/users/Profpatsch/aliases.nix b/users/Profpatsch/aliases.nix
new file mode 100644
index 0000000000..109de8ce33
--- /dev/null
+++ b/users/Profpatsch/aliases.nix
@@ -0,0 +1,88 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.findutils [ "find" ];
+
+in
+depot.nix.readTree.drvTargets {
+
+  findia = depot.nix.writeExecline "findia"
+    {
+      readNArgs = 1;
+      # TODO: comment out, thanks to sterni blocking the runExecline change
+      # meta.description = ''
+      #   Find case-insensitive anywhere (globbing)
+
+      #   Usage: findia <pattern> <more find(1) arguments>
+      # '';
+    } [
+    bins.find
+    "-iname"
+    "*\${1}*"
+    "$@"
+  ];
+
+  findial = depot.nix.writeExecline "findial"
+    {
+      readNArgs = 1;
+      # TODO: comment out, thanks to sterni blocking the runExecline change
+      # meta.description = ''
+      #   Find case-insensitive anywhere (globbing), follow symlinks";
+
+      #   Usage: findial <pattern> <more find(1) arguments>
+      # '';
+    } [
+    bins.find
+    "-L"
+    "-iname"
+    "*\${1}*"
+    "$@"
+  ];
+
+  findian = depot.nix.writeExecline "findian"
+    {
+      readNArgs = 2;
+      # TODO: comment out, thanks to sterni blocking the runExecline change
+      # meta.description = ''
+      #   Find case-insensitive anywhere (globbing) in directory
+
+      #   Usage: findian <directory> <pattern> <more find(1) arguments>
+      # '';
+    } [
+    bins.find
+    "$1"
+    "-iname"
+    "*\${2}*"
+    "$@"
+  ];
+
+  findiap = depot.nix.writeExecline "findiap"
+    {
+      readNArgs = 2;
+      # TODO: comment out, thanks to sterni blocking the runExecline change
+      # meta.description = ''
+      #   Find case-insensitive anywhere (globbing) in directory, the pattern allows for paths.
+
+      #   Usage: findiap <directory> <pattern> <more find(1) arguments>
+      # '';
+    } [
+    bins.find
+    "$1"
+    "-ipath"
+    "*\${2}*"
+    "$@"
+  ];
+
+  bell = depot.nix.writeExecline "bell" { } [
+    "if"
+    [
+      "pactl"
+      "upload-sample"
+      "${pkgs.sound-theme-freedesktop}/share/sounds/freedesktop/stereo/complete.oga"
+      "bell-window-system"
+    ]
+    "pactl"
+    "play-sample"
+    "bell-window-system"
+  ];
+}
diff --git a/users/Profpatsch/arglib/ArglibNetencode.hs b/users/Profpatsch/arglib/ArglibNetencode.hs
new file mode 100644
index 0000000000..4531151ca2
--- /dev/null
+++ b/users/Profpatsch/arglib/ArglibNetencode.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module ArglibNetencode where
+
+import Data.Attoparsec.ByteString qualified as Atto
+import ExecHelpers
+import Label
+import Netencode qualified
+import PossehlAnalyticsPrelude
+import System.Posix.Env.ByteString qualified as ByteEnv
+
+arglibNetencode :: CurrentProgramName -> Maybe (Label "arglibEnvvar" Text) -> IO Netencode.T
+arglibNetencode progName mEnvvar = do
+  let envvar = mEnvvar <&> (.arglibEnvvar) & fromMaybe "ARGLIB_NETENCODE" & textToBytesUtf8
+  ByteEnv.getEnv envvar >>= \case
+    Nothing -> dieUserError progName [fmt|could not read args, envvar {envvar} not set|]
+    Just bytes ->
+      case Atto.parseOnly (Netencode.netencodeParser <* Atto.endOfInput) bytes of
+        Left err -> dieEnvironmentProblem progName [fmt|arglib parsing error: {err}|]
+        Right t -> do
+          ByteEnv.unsetEnv envvar
+          pure t
diff --git a/users/Profpatsch/arglib/arglib-netencode.cabal b/users/Profpatsch/arglib/arglib-netencode.cabal
new file mode 100644
index 0000000000..42b524f405
--- /dev/null
+++ b/users/Profpatsch/arglib/arglib-netencode.cabal
@@ -0,0 +1,65 @@
+cabal-version:      3.0
+name:               arglib-netencode
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+    exposed-modules:          ArglibNetencode
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        pa-label,
+        netencode,
+        exec-helpers,
+        attoparsec,
+        unix
diff --git a/users/Profpatsch/arglib/netencode.nix b/users/Profpatsch/arglib/netencode.nix
index 7712bbd5bb..83a94ddd6c 100644
--- a/users/Profpatsch/arglib/netencode.nix
+++ b/users/Profpatsch/arglib/netencode.nix
@@ -1,40 +1,81 @@
 { depot, pkgs, lib, ... }:
 
 let
-  netencode = {
-    rust = depot.nix.writers.rustSimpleLib {
+
+  # Add the given nix arguments to the program as ARGLIB_NETENCODE envvar
+  #
+  # Calls `netencode.gen.dwim` on the provided nix args value.
+  with-args = name: args: prog: depot.nix.writeExecline "${name}-with-args" { } [
+    "export"
+    "ARGLIB_NETENCODE"
+    (depot.users.Profpatsch.netencode.gen.dwim args)
+    prog
+  ];
+
+  rust = depot.nix.writers.rustSimpleLib
+    {
       name = "arglib-netencode";
       dependencies = [
         depot.users.Profpatsch.execline.exec-helpers
         depot.users.Profpatsch.netencode.netencode-rs
       ];
     } ''
-      extern crate netencode;
-      extern crate exec_helpers;
-
-      use netencode::{T};
-      use std::os::unix::ffi::OsStrExt;
-
-      pub fn arglib_netencode(prog_name: &str, env: Option<&std::ffi::OsStr>) -> T {
-          let env = match env {
-              None => std::ffi::OsStr::from_bytes("ARGLIB_NETENCODE".as_bytes()),
-              Some(a) => a
-          };
-          let t = match std::env::var_os(env) {
-              None => exec_helpers::die_user_error(prog_name, format!("could not read args, envvar {} not set", env.to_string_lossy())),
-              // TODO: good error handling for the different parser errors
-              Some(soup) => match netencode::parse::t_t(soup.as_bytes()) {
-                  Ok((remainder, t)) => match remainder.is_empty() {
-                      true => t,
-                      false => exec_helpers::die_environment_problem(prog_name, format!("arglib: there was some unparsed bytes remaining: {:?}", remainder))
-                  },
-                  Err(err) => exec_helpers::die_environment_problem(prog_name, format!("arglib parsing error: {:?}", err))
-              }
-          };
-          std::env::remove_var(env);
-          t
-      }
-    '';
+    extern crate netencode;
+    extern crate exec_helpers;
+
+    use netencode::{T};
+    use std::os::unix::ffi::OsStrExt;
+
+    pub fn arglib_netencode(prog_name: &str, env: Option<&std::ffi::OsStr>) -> T {
+        let env = match env {
+            None => std::ffi::OsStr::from_bytes("ARGLIB_NETENCODE".as_bytes()),
+            Some(a) => a
+        };
+        let t = match std::env::var_os(env) {
+            None => exec_helpers::die_user_error(prog_name, format!("could not read args, envvar {} not set", env.to_string_lossy())),
+            // TODO: good error handling for the different parser errors
+            Some(soup) => match netencode::parse::t_t(soup.as_bytes()) {
+                Ok((remainder, t)) => match remainder.is_empty() {
+                    true => t,
+                    false => exec_helpers::die_environment_problem(prog_name, format!("arglib: there was some unparsed bytes remaining: {:?}", remainder))
+                },
+                Err(err) => exec_helpers::die_environment_problem(prog_name, format!("arglib parsing error: {:?}", err))
+            }
+        };
+        std::env::remove_var(env);
+        t
+    }
+  '';
+
+  haskell = pkgs.haskellPackages.mkDerivation {
+    pname = "arglib-netencode";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./arglib-netencode.cabal
+      ./ArglibNetencode.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
+      depot.users.Profpatsch.netencode.netencode-hs
+      depot.users.Profpatsch.execline.exec-helpers-hs
+    ];
+
+    isLibrary = true;
+    license = lib.licenses.mit;
+
+
   };
 
-in depot.nix.utils.drvTargets netencode
+
+in
+depot.nix.readTree.drvTargets {
+  inherit
+    with-args
+    rust
+    haskell
+    ;
+}
diff --git a/users/Profpatsch/atomically-write.nix b/users/Profpatsch/atomically-write.nix
new file mode 100644
index 0000000000..c4d07cfbb1
--- /dev/null
+++ b/users/Profpatsch/atomically-write.nix
@@ -0,0 +1,29 @@
+{ depot, pkgs, ... }:
+# Atomically write a file (just `>` redirection in bash
+# empties a file even if the command crashes).
+#
+# Maybe there is an existing tool for that?
+# But itโ€™s easy enough to implement.
+#
+# Example:
+#   atomically-write
+#     ./to
+#     echo "foo"
+#
+# will atomically write the string "foo" into ./to
+let
+  atomically-write = pkgs.writers.writeDash "atomically-write" ''
+    set -e
+    to=$1
+    shift
+    # assumes that the tempfile is on the same file system, (or in memory)
+    # for the `mv` at the end to be more-or-less atomic.
+    tmp=$(${pkgs.coreutils}/bin/mktemp -d)
+    trap 'rm -r "$tmp"' EXIT
+    "$@" \
+      > "$tmp/out"
+    mv "$tmp/out" "$to"
+  '';
+
+in
+atomically-write
diff --git a/users/Profpatsch/blog/README.md b/users/Profpatsch/blog/README.md
new file mode 100644
index 0000000000..0753ebdea5
--- /dev/null
+++ b/users/Profpatsch/blog/README.md
@@ -0,0 +1,7 @@
+# (Parts of) my website
+
+This is a part of https://profpatsch.de/, notably the blog posts.
+
+The other parts can be found in [vuizvui](https://github.com/openlab-aux/vuizvui/tree/master/pkgs/profpatsch/profpatsch.de). Itโ€™s a mess.
+
+And yes, this implements a webserver & routing engine with nix, execline & s6 utils. โ€œBis einer weintโ€, as we say in German.
diff --git a/users/Profpatsch/blog/default.nix b/users/Profpatsch/blog/default.nix
index 9d22e7f770..f233eda9bb 100644
--- a/users/Profpatsch/blog/default.nix
+++ b/users/Profpatsch/blog/default.nix
@@ -2,128 +2,366 @@
 
 let
   bins = depot.nix.getBins pkgs.lowdown [ "lowdown" ]
-      // depot.nix.getBins pkgs.cdb [ "cdbget" "cdbmake" "cdbdump" ]
-      // depot.nix.getBins pkgs.coreutils [ "mv" "cat" "printf" "tee" "env" "test" "echo" "printenv" ]
-      // depot.nix.getBins pkgs.bash [ "bash" ]
-      // depot.nix.getBins pkgs.s6-networking [ "s6-tcpserver" ]
-      // depot.nix.getBins pkgs.time [ "time" ]
-      ;
-
-  me = depot.users.Profpatsch;
-
-  renderNote = name: note: depot.nix.runExecline "${name}.html" {} [
-    "importas" "out" "out"
-    bins.lowdown "-s" "-Thtml" "-o" "$out" note
+    // depot.nix.getBins pkgs.cdb [ "cdbget" "cdbmake" "cdbdump" ]
+    // depot.nix.getBins pkgs.coreutils [ "mv" "cat" "printf" "test" ]
+    // depot.nix.getBins pkgs.s6-networking [ "s6-tcpserver" ]
+    // depot.nix.getBins pkgs.time [ "time" ]
+  ;
+
+  # /
+  # TODO: use
+  toplevel = [
+    {
+      route = [ "notes" ];
+      name = "Notes";
+      page = { cssFile }: router cssFile;
+    }
+    {
+      route = [ "projects" ];
+      name = "Projects";
+      # page = projects;
+    }
   ];
 
+  # /notes/*
   notes = [
     {
-      route = [ "notes" "preventing-oom" ];
-      name = "Preventing OOM";
-      page = renderNote "preventing-oom" ./notes/preventing-oom.md;
+      route = [ "notes" "private-trackers-are-markets" ];
+      name = "Private bittorrent trackers are markets";
+      page = { cssFile }: markdownToHtml {
+        name = "private-trackers-are-markets";
+        markdown = ./notes/private-trackers-are-markets.md;
+        inherit cssFile;
+      };
+    }
+    {
+      route = [ "notes" "an-idealized-conflang" ];
+      name = "An Idealized Configuration Language";
+      page = { cssFile }: markdownToHtml {
+        name = "an-idealized-conflang";
+        markdown = ./notes/an-idealized-conflang.md;
+        inherit cssFile;
+      };
     }
     {
       route = [ "notes" "rust-string-conversions" ];
       name = "Converting between different String types in Rust";
-      page = renderNote "rust-string-conversions" ./notes/rust-string-conversions.md;
+      page = { cssFile }: markdownToHtml {
+        name = "rust-string-conversions";
+        markdown = ./notes/rust-string-conversions.md;
+        inherit cssFile;
+      };
+    }
+    {
+      route = [ "notes" "preventing-oom" ];
+      name = "Preventing out-of-memory (OOM) errors on Linux";
+      page = { cssFile }: markdownToHtml {
+        name = "preventing-oom";
+        markdown = ./notes/preventing-oom.md;
+        inherit cssFile;
+      };
+    }
+  ];
+
+  projects = [
+    {
+      name = "lorri";
+      description = "<code>nix-shell</code> replacement for projects";
+      link = "https://github.com/nix-community/lorri";
     }
+    {
+      name = "netencode";
+      description = ''A human-readble nested data exchange format inspired by <a href="https://en.wikipedia.org/wiki/Netstring">netstrings</a> and <a href="https://en.wikipedia.org/wiki/Bencode">bencode</a>.'';
+      link = depotCgitLink { relativePath = "users/Profpatsch/netencode/README.md"; };
+    }
+    {
+      name = "yarn2nix";
+      description = ''nix dependency generator for the <a href="https://yarnpkg.com/"><code>yarn</code> Javascript package manager</a>'';
+      link = "https://github.com/Profpatsch/yarn2nix";
+    }
+  ];
+
+  posts = [
+    {
+      date = "2017-05-04";
+      title = "Ligature Emulation in Emacs";
+      subtitle = "Itโ€™s not pretty, but the results are";
+      description = "How to set up ligatures using <code>prettify-symbols-mode</code> and the Hasklig/FiraCode fonts.";
+      page = { cssFile }: markdownToHtml {
+        name = "2017-05-04-ligature-emluation-in-emacs";
+        markdown = ./posts/2017-05-04-ligature-emulation-in-emacs.md;
+        inherit cssFile;
+      };
+      route = [ "posts" "2017-05-04-ligature-emluation-in-emacs" ];
+      tags = [ "emacs" ];
+    }
+  ];
+
+  # convert a markdown file to html via lowdown
+  markdownToHtml =
+    { name
+    , # the file to convert
+      markdown
+    , # css file to add to the final result, as { route }
+      cssFile
+    }:
+    depot.nix.runExecline "${name}.html" { } ([
+      "importas"
+      "out"
+      "out"
+      (depot.users.Profpatsch.lib.debugExec "")
+      bins.lowdown
+      "-s"
+      "-Thtml"
+    ] ++
+    (lib.optional (cssFile != null) ([ "-M" "css=${mkRoute cssFile.route}" ]))
+    ++ [
+      "-o"
+      "$out"
+      markdown
+    ]);
+
+  # takes a { route โ€ฆ } attrset and converts the route lists to an absolute path
+  fullRoute = attrs: lib.pipe attrs [
+    (map (x@{ route, ... }: x // { route = mkRoute route; }))
   ];
 
-  router = lib.pipe notes [
-    (map (x@{route, ...}: x // { route = mkRoute route; }))
+  # a cdb from route to a netencoded version of data for each route
+  router = cssFile: lib.pipe (notes ++ posts) [
+    (map (r: with depot.users.Profpatsch.lens;
+    lib.pipe r [
+      (over (field "route") mkRoute)
+      (over (field "page") (_ { inherit cssFile; }))
+    ]))
     (map (x: {
       name = x.route;
-      value = me.netencode.gen.dwim x;
+      value = depot.users.Profpatsch.netencode.gen.dwim x;
     }))
     lib.listToAttrs
-    (cdbMake "notes-router")
+    (cdbMake "router")
   ];
 
-  router-lookup = depot.nix.writeExecline "router-lookup" { readNArgs = 1; } [
-    cdbLookup router "$1"
+  # Create a link to the given source file/directory, given the relative path in the depot repo.
+  # Checks that the file exists at evaluation time.
+  depotCgitLink =
+    {
+      # relative path from the depot root (without leading /).
+      relativePath
+    }:
+      assert
+      (lib.assertMsg
+        (builtins.pathExists (depot.path.origSrc + ("/" + relativePath)))
+        "depotCgitLink: path /${relativePath} does not exist in depot, and depot.path was ${toString depot.path}");
+      "https://code.tvl.fyi/tree/${relativePath}";
+
+  # look up a route by path ($1)
+  router-lookup = cssFile: depot.nix.writeExecline "router-lookup" { readNArgs = 1; } [
+    cdbLookup
+    (router cssFile)
+    "$1"
   ];
 
   runExeclineStdout = name: args: cmd: depot.nix.runExecline name args ([
-    "importas" "-ui" "out" "out"
-    "redirfd" "-w" "1" "$out"
+    "importas"
+    "-ui"
+    "out"
+    "out"
+    "redirfd"
+    "-w"
+    "1"
+    "$out"
   ] ++ cmd);
 
-  index = runExeclineStdout "index" {} [
-    "backtick" "-in" "TEMPLATE_DATA" [ cdbDumpNetencode router ]
-    "pipeline" [
-      bins.printf ''
-        <ul>
-        {{#.}}
-          <li><a href="{{key}}">{{val}}<a></li>
-        {{/.}}
-        </ul>
-      ''
-    ]
-    me.netencode.netencode-mustache
-  ];
+  notes-index-html =
+    let o = fullRoute notes;
+    in ''
+      <ul>
+      ${scope o (o: ''
+        <li><a href="${str o.route}">${esc o.name}</a></li>
+      '')}
+      </ul>
+    '';
+
+  notes-index = pkgs.writeText "notes-index.html" notes-index-html;
+
+  # A simple mustache-inspired string interpolation combinator
+  # that takes an object and a template (a function from o to string)
+  # and returns a string.
+  scope = o: tpl:
+    if builtins.typeOf o == "list" then
+      lib.concatMapStringsSep "\n" tpl o
+    else if builtins.typeOf o == "set" then
+      tpl o
+    else throw "${lib.generators.toPretty {} o} not allowed in template";
+
+  # string-escape html (TODO)
+  str = s: s;
+  # html-escape (TODO)
+  esc = s: s;
+  html = s: s;
+
+  projects-index-html =
+    let o = projects;
+    in ''
+      <dl>
+      ${scope o (o: ''
+        <dt><a href="${str o.link}">${esc o.name}</a></dt>
+        <dd>${html o.description}</dd>
+      '')}
+      </dl>
+    '';
+
+  projects-index = pkgs.writeText "projects-index.html" projects-index-html;
+
+  posts-index-html =
+    let o = fullRoute posts;
+    in ''
+      <dl>
+      ${scope o (o: ''
+        <dt>${str o.date} <a href="${str o.route}">${esc o.title}</a></dt>
+        <dd>${html o.description}</dd>
+      '')}
+      </dl>
+    '';
+
+  posts-index = pkgs.writeText "projects-index.html" posts-index-html;
 
   arglibNetencode = val: depot.nix.writeExecline "arglib-netencode" { } [
-    "export" "ARGLIB_NETENCODE" (me.netencode.gen.dwim val)
+    "export"
+    "ARGLIB_NETENCODE"
+    (depot.users.Profpatsch.netencode.gen.dwim val)
     "$@"
   ];
 
-  notes-server = { port }: depot.nix.writeExecline "blog-server" {} [
-    (me.lib.runInEmptyEnv [ "PATH" ])
-    bins.s6-tcpserver "127.0.0.1" port
-    bins.time "--format=time: %es" "--"
-    runOr return400
-    "pipeline" [
+  # A simple http server that serves the site. Yes, itโ€™s horrible.
+  site-server = { cssFile, port }: depot.nix.writeExecline "blog-server" { } [
+    (depot.users.Profpatsch.lib.runInEmptyEnv [ "PATH" ])
+    bins.s6-tcpserver
+    "127.0.0.1"
+    port
+    bins.time
+    "--format=time: %es"
+    "--"
+    runOr
+    return400
+    "pipeline"
+    [
       (arglibNetencode {
         what = "request";
       })
-      me.read-http
+      depot.users.Profpatsch.read-http
     ]
-    me.netencode.record-splice-env
-    runOr return500
-    "importas" "-i" "path" "path"
-    "if" [ depot.tools.eprintf "GET \${path}\n" ]
-    runOr return404
-    "backtick" "-ni" "TEMPLATE_DATA" [
-      "ifelse" [ bins.test "$path" "=" "/notes" ]
-        [ "export" "content-type" "text/html"
-          "export" "serve-file" index
-          me.netencode.env-splice-record
-        ]
+    depot.users.Profpatsch.netencode.record-splice-env
+    runOr
+    return500
+    "importas"
+    "-i"
+    "path"
+    "path"
+    "if"
+    [ depot.tools.eprintf "GET \${path}\n" ]
+    runOr
+    return404
+    "backtick"
+    "-ni"
+    "TEMPLATE_DATA"
+    [
+      # TODO: factor this out of here, this is routing not serving
+      "ifelse"
+      [ bins.test "$path" "=" "/notes" ]
+      [
+        "export"
+        "content-type"
+        "text/html"
+        "export"
+        "serve-file"
+        notes-index
+        depot.users.Profpatsch.netencode.env-splice-record
+      ]
+      "ifelse"
+      [ bins.test "$path" "=" "/projects" ]
+      [
+        "export"
+        "content-type"
+        "text/html"
+        "export"
+        "serve-file"
+        projects-index
+        depot.users.Profpatsch.netencode.env-splice-record
+      ]
+      "ifelse"
+      [ bins.test "$path" "=" "/posts" ]
+      [
+        "export"
+        "content-type"
+        "text/html"
+        "export"
+        "serve-file"
+        posts-index
+        depot.users.Profpatsch.netencode.env-splice-record
+      ]
       # TODO: ignore potential query arguments. See 404 message
-      "pipeline" [ router-lookup "$path" ]
-      me.netencode.record-splice-env
-      "importas" "-ui" "page" "page"
-      "export" "content-type" "text/html"
-      "export" "serve-file" "$page"
-      me.netencode.env-splice-record
+      "pipeline"
+      [ (router-lookup cssFile) "$path" ]
+      depot.users.Profpatsch.netencode.record-splice-env
+      "importas"
+      "-ui"
+      "page"
+      "page"
+      "export"
+      "content-type"
+      "text/html"
+      "export"
+      "serve-file"
+      "$page"
+      depot.users.Profpatsch.netencode.env-splice-record
     ]
-    runOr return500
-    "if" [
-      "pipeline" [ bins.printf ''
-        HTTP/1.1 200 OK
-        Content-Type: {{{content-type}}}; charset=UTF-8
-        Connection: close
-
-      '' ]
-      me.netencode.netencode-mustache
+    runOr
+    return500
+    "if"
+    [
+      "pipeline"
+      [
+        bins.printf
+        ''
+          HTTP/1.1 200 OK
+          Content-Type: {{{content-type}}}; charset=UTF-8
+          Connection: close
+
+        ''
+      ]
+      depot.users.Profpatsch.netencode.netencode-mustache
     ]
-    "pipeline" [ "importas" "t" "TEMPLATE_DATA" bins.printf "%s" "$t" ]
-    me.netencode.record-splice-env
-    "importas" "-ui" "serve-file" "serve-file"
-    bins.cat "$serve-file"
+    "pipeline"
+    [ "importas" "t" "TEMPLATE_DATA" bins.printf "%s" "$t" ]
+    depot.users.Profpatsch.netencode.record-splice-env
+    "importas"
+    "-ui"
+    "serve-file"
+    "serve-file"
+    bins.cat
+    "$serve-file"
   ];
 
+  # run argv or $1 if argv returns a failure status code.
   runOr = depot.nix.writeExecline "run-or" { readNArgs = 1; } [
-    "foreground" [ "$@" ]
-    "importas" "?" "?"
-    "ifelse" [ bins.test "$?" "-eq" "0" ]
-    []
-    "if" [ depot.tools.eprintf "runOr: exited \${?}, running \${1}\n" ]
+    "foreground"
+    [ "$@" ]
+    "importas"
+    "?"
+    "?"
+    "ifelse"
+    [ bins.test "$?" "-eq" "0" ]
+    [ ]
+    "if"
+    [ depot.tools.eprintf "runOr: exited \${?}, running \${1}\n" ]
     "$1"
   ];
 
-  return400 = depot.nix.writeExecline "return400" {} [
-    bins.printf "%s" ''
+  return400 = depot.nix.writeExecline "return400" { } [
+    bins.printf
+    "%s"
+    ''
       HTTP/1.1 400 Bad Request
       Content-Type: text/plain; charset=UTF-8
       Connection: close
@@ -131,8 +369,10 @@ let
     ''
   ];
 
-  return404 = depot.nix.writeExecline "return404" {} [
-    bins.printf "%s" ''
+  return404 = depot.nix.writeExecline "return404" { } [
+    bins.printf
+    "%s"
+    ''
       HTTP/1.1 404 Not Found
       Content-Type: text/plain; charset=UTF-8
       Connection: close
@@ -141,8 +381,10 @@ let
     ''
   ];
 
-  return500 = depot.nix.writeExecline "return500" {} [
-    bins.printf "%s" ''
+  return500 = depot.nix.writeExecline "return500" { } [
+    bins.printf
+    "%s"
+    ''
       HTTP/1.1 500 Internal Server Error
       Content-Type: text/plain; charset=UTF-8
       Connection: close
@@ -151,15 +393,11 @@ let
     ''
   ];
 
-  split-stdin = depot.nix.writeExecline "split-stdin" { argMode = "env"; } [
-    "pipeline" [ "runblock" "1" bins.bash "-c" ''${bins.tee} >("$@")'' "bash-split-stdin" ]
-    "runblock" "-r" "1"
-  ];
-
-  capture-stdin = depot.nix.writers.rustSimple {
-    name = "capture-stdin";
-    dependencies = [ me.execline.exec-helpers ];
-  } ''
+  capture-stdin = depot.nix.writers.rustSimple
+    {
+      name = "capture-stdin";
+      dependencies = [ depot.users.Profpatsch.execline.exec-helpers ];
+    } ''
     extern crate exec_helpers;
     use std::io::Read;
     fn main() {
@@ -171,11 +409,7 @@ let
     }
   '';
 
-  on-stdin = depot.nix.writeExecline "on-stdin" { readNArgs = 1; } [
-    "pipeline" [ bins.printf "%s" "$1" ]
-    "$@"
-  ];
-
+  # go from a list of path elements to an absolute route string
   mkRoute = route: "/" + lib.concatMapStringsSep "/" urlencodeAscii route;
 
   # urlencodes, but only ASCII characters
@@ -191,132 +425,57 @@ let
     builtins.replaceStrings raw enc urlPiece;
 
 
+  # create a cdb record entry, as required by the cdbmake tool
   cdbRecord = key: val:
     "+${toString (builtins.stringLength key)},${toString (builtins.stringLength val)}:"
     + "${key}->${val}\n";
+
+  # create a full cdbmake input from an attribute set of keys to values (strings)
   cdbRecords =
     with depot.nix.yants;
     defun [ (attrs (either drv string)) string ]
-    (attrs:
-      (lib.concatStrings (lib.mapAttrsToList cdbRecord attrs)) + "\n");
-
-  cdbMake = name: attrs: depot.nix.runExecline "${name}.cdb" {
-    stdin = cdbRecords attrs;
-  } [
-    "importas" "out" "out"
-    me.lib.eprint-stdin
-    "if" [ bins.cdbmake "db" "tmp" ]
-    bins.mv "db" "$out"
+      (attrs:
+        (lib.concatStrings (lib.mapAttrsToList cdbRecord attrs)) + "\n");
+
+  # run cdbmake on a list of key/value pairs (strings
+  cdbMake = name: attrs: depot.nix.runExecline "${name}.cdb"
+    {
+      stdin = cdbRecords attrs;
+    } [
+    "importas"
+    "out"
+    "out"
+    depot.users.Profpatsch.lib.eprint-stdin
+    "if"
+    [ bins.cdbmake "db" "tmp" ]
+    bins.mv
+    "db"
+    "$out"
   ];
 
+  # look up a key ($2) in the given cdb ($1)
   cdbLookup = depot.nix.writeExecline "cdb-lookup" { readNArgs = 2; } [
     # cdb ($1) on stdin
-    "redirfd" "-r" "0" "$1"
+    "redirfd"
+    "-r"
+    "0"
+    "$1"
     # key ($2) lookup
-    bins.cdbget "$2"
-  ];
-
-  cdbDumpNetencode = depot.nix.writeExecline "cdb-dump-netencode" { readNArgs = 1; } [
-    # cdb ($1) on stdin
-    "pipeline" [
-      "redirfd" "-r" "0" "$1"
-      bins.cdbdump
-    ]
-    cdbListToNetencode
+    bins.cdbget
+    "$2"
   ];
 
-  cdbListToNetencode = depot.nix.writers.rustSimple {
-    name = "cdb-list-to-netencode";
-    dependencies = [
-      depot.third_party.rust-crates.nom
-      me.execline.exec-helpers
-      me.netencode.netencode-rs
-    ];
-  } ''
-    extern crate nom;
-    extern crate exec_helpers;
-    extern crate netencode;
-    use std::collections::HashMap;
-    use std::io::BufRead;
-    use nom::{IResult};
-    use nom::sequence::{tuple};
-    use nom::bytes::complete::{tag, take};
-    use nom::character::complete::{digit1, char};
-    use nom::error::{context, ErrorKind, ParseError};
-    use nom::combinator::{map_res};
-    use netencode::{T, Tag};
-
-    fn usize_t(s: &[u8]) -> IResult<&[u8], usize> {
-        context(
-            "usize",
-            map_res(
-                map_res(digit1, |n| std::str::from_utf8(n)),
-                |s| s.parse::<usize>())
-        )(s)
-    }
-
-    fn parse_cdb_record(s: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
-        let (s, (_, klen, _, vlen, _)) = tuple((
-            char('+'),
-            usize_t,
-            char(','),
-            usize_t,
-            char(':')
-        ))(s)?;
-        let (s, (key, _, val)) = tuple((
-            take(klen),
-            tag("->"),
-            take(vlen),
-        ))(s)?;
-        Ok((s, (key, val)))
-    }
-
-    fn main() {
-        let mut res = vec![];
-        let stdin = std::io::stdin();
-        let mut lines = stdin.lock().split(b'\n');
-        loop {
-            match lines.next() {
-                None => exec_helpers::die_user_error("cdb-list-to-netencode", "stdin ended but we didnโ€™t receive the empty line to signify the end of the cdbdump input!"),
-                Some(Err(err)) => exec_helpers::die_temporary("cdb-list-to-netencode", format!("could not read from stdin: {}", err)),
-                Some(Ok(line)) =>
-                    if &line == b"" {
-                        // the cdbdump input ends after an empty line (double \n)
-                        break;
-                    } else {
-                        match parse_cdb_record(&line) {
-                            Ok((b"", (key, val))) => {
-                                let (key, val) = match
-                                    std::str::from_utf8(key)
-                                    .and_then(|k| std::str::from_utf8(val).map(|v| (k, v))) {
-                                    Ok((key, val)) => (key.to_owned(), val.to_owned()),
-                                    Err(err) => exec_helpers::die_user_error("cdb-list-to-netencode", format!("cannot decode line {:?}, we only support utf8-encoded key/values pairs for now: {}", String::from_utf8_lossy(&line), err)),
-                                };
-                                let _ = res.push((key, val));
-                            },
-                            Ok((rest, _)) => exec_helpers::die_user_error("cdb-list-to-netencode", format!("could not decode record line {:?}, had some trailing bytes", String::from_utf8_lossy(&line))),
-                            Err(err) => exec_helpers::die_user_error("cdb-list-to-netencode", format!("could not decode record line {:?}: {:?}", String::from_utf8_lossy(&line), err)),
-                        }
-                    }
-            }
-        }
-        let list = T::List(res.into_iter().map(
-            |(k, v)| T::Record(vec![(String::from("key"), T::Text(k)), (String::from("val"), T::Text(v))].into_iter().collect())
-        ).collect());
-        netencode::encode(&mut std::io::stdout(), &list.to_u());
-    }
-
-  '';
-
-
-in depot.nix.utils.drvTargets {
-   inherit
+in
+depot.nix.readTree.drvTargets {
+  inherit
     router
-    notes-server
-    split-stdin
-    cdbListToNetencode
-    index
-    router-lookup
+    depotCgitLink
+    site-server
+    notes-index
+    notes-index-html
+    projects-index
+    projects-index-html
+    posts-index-html
     ;
 
 }
diff --git a/users/Profpatsch/blog/notes/an-idealized-conflang.md b/users/Profpatsch/blog/notes/an-idealized-conflang.md
new file mode 100644
index 0000000000..5c6b39f6e8
--- /dev/null
+++ b/users/Profpatsch/blog/notes/an-idealized-conflang.md
@@ -0,0 +1,298 @@
+tags: netencode, json
+date: 2022-03-31
+certainty: likely
+status: initial
+title: An idealized Configuration Language
+
+# An Idealized Configuration Language
+
+JSON brought us one step closer to what an idealized configuration language is,
+which I define as โ€œdata, stripped of all externalities of the system it is working inโ€.
+
+Specifically, JSON is very close to what I consider the minimal properties to represent structured data.
+
+## A short history, according to me
+
+In the beginning, Lisp defined s-expressions as a stand-in for an actual syntax.
+Then, people figured out that itโ€™s also a way to represent structured data.
+It has scalars, which can be nested into lists, recursively.
+
+```
+(this is (a (list) (of lists)))
+```
+
+This provides the first three rules of our idealized language:
+
+1. A **scalar** is a primitive value that is domain-specific.
+   We can assume a bunch of bytes here, or a text or an integer.
+   
+2. A **list** gives an ordering to `0..n` (or `1..n`) values
+   
+3. Both a scalar and a list are the *same kind* of โ€œthingโ€ (from here on called **value**),
+   lists can be created from arbitrary values *recursively*
+   (for example scalars, or lists of scalars and other lists)
+
+
+Later, ASN.1 came and had the important insight that the same idealized data structure
+can be represented in different fashions,
+for example as a binary-efficient version and a human-readable format.
+
+Then, XML โ€œgracedโ€ the world for a decade or two, and the main lesson from it was
+that you donโ€™t want to mix markup languages and configuration languages,
+and that you donโ€™t want a committee to design these things.
+
+---
+
+In the meantime, Brendan Eich designed Javascript. Its prototype-based object system
+arguably stripped down the rituals of existing OO-systems.
+Douglas Crockford later extracted the object format (minus functions) into a syntax, and we got JSON.
+
+```
+{
+  "foo": [
+    { "nested": "attrs" },
+    "some text"
+  ],
+  "bar": 42
+}
+```
+
+JSON adds another fundamental idea into the mix:
+
+4. **Records** are unordered collections of `name`/`value` pairs.
+   A `name` is defined to be a unicode string, so a semantic descriptor of the nested `value`.
+
+Unfortunately, the JSON syntax does not actually specify any semantics of records (`objects` in JSON lingo),
+in particular it does not mention what the meaning is if a `name` appears twice in one record.
+
+If records can have multiple entries with the same `name`, suddenly ordering becomes important!
+But wait, remember earlier we defined *lists* to impose ordering on two values.
+So in order to rectify that problem, we say that
+
+5. A `name` can only appear in a record *once*, names must be unique.
+
+This is the current state of the programming community at large,
+where most โ€œmodernโ€ configuration languages basically use a version of the JSON model
+as their underlying data structure. (However not all of them use the same version.)
+
+## Improving JSONโ€™s data model
+
+We are not yet at the final โ€œidealizedโ€ configuration language, though.
+
+Modern languages like Standard ML define their data types as a mixture of 
+
+* *records* (โ€œstructsโ€ in the C lingo)
+* and *sums* (which you can think about as enums that can hold more `value`s inside them)
+
+This allows to express the common pattern where some fields in a record are only meaningful
+if another fieldโ€”the so-called `tag`-fieldโ€”is set to a specific value.
+
+An easy example: if a request can fail with an error message or succeed with a result.
+
+You could model that as 
+
+```
+{
+  "was_error": true,
+  "error_message": "there was an error"
+}
+```
+
+or
+
+```
+{
+  "was_error": false,
+  "result": 42
+}
+```
+
+in your JSON representation.
+
+But in a ML-like language (like, for example, Rust), you would instead model it as
+
+```
+type RequestResult 
+  = Error { error_message: String }
+  | Success { result: i64 }
+```
+
+where the distinction in `Error` or `Success` makes it clear that `error_message` and `result`
+only exist in one of these cases, not the other.
+
+We *can* encode exactly that idea into JSON in multiple ways, but not a โ€œblessedโ€ way.
+
+For example, another way to encode the above would be
+
+```
+{ 
+  "Error": { 
+    "error_message": "there was an error"
+  }
+}
+```
+
+and
+
+```
+{ 
+  "Success": { 
+    "result": 42
+  }
+}
+```
+
+Particularly notice the difference between the language representation, where the type is โ€œclosedโ€only `Success` or `Error` can happenโ€”
+and the data representation where the type is โ€œopenโ€, more cases could potentially exist.
+
+This is an important differentiation from a type system:
+Our idealized configuration language just gives more structure to a bag of data,
+it does not restrict which value can be where.
+Think of a value in an unityped language, like Python.
+
+
+So far we have the notion of 
+
+1. a scalar (a primitive)
+2. a list (ordering on values)
+3. a record (unordered collection of named values)
+
+and in order to get the โ€œopenโ€ `tag`ged enumeration values, we introduce
+
+4. a `tag`, which gives a name to a value
+
+We can then redefine `record` to mean โ€œan unordered collection of `tag`ged valuesโ€,
+which further reduces the amount of concepts needed.
+
+And thatโ€™s it, this is the full idealized configuration language.
+
+
+## Some examples of data modelling with tags
+
+This is all well and good, but what does it look like in practice?
+
+For these examples I will be using JSON with a new `< "tag": value >` syntax
+to represent `tag`s.
+
+From a compatibility standpoint, `tag`s (or sum types) have dual properties to record types.
+
+With a record, when you have a producer that *adds* a field to it, the consumer will still be able to handle the record (provided the semantics of the existing fields is not changed by the new field).
+
+With a tag, *removing* a tag from the producer will mean that the consumer will still be able to handle the tag. It might do one โ€œdeadโ€ check on the removed `tag`, but can still handle the remaining ones just fine.
+
+<!-- TODO: some illustration here -->
+    
+An example of how that is applied in practice is that in `protobuf3`, fields of a record are *always* optional fields.
+
+We can model optional fields by wrapping them in `< "Some": value >` or `< "None": {} >` (where the actual value of the `None` is ignored or always an empty record).
+
+So a protobuf with the fields `foo: int` and `bar: string` has to be parsed by the receiver als containing *four* possibilities:
+
+โ„–|foo|bar|
+|--:|---|---|
+|1|`<"None":{}>`|`<"None":{}>`|
+|2|`<"Some":42>`|`<"None":{}>`|
+|3|`<"None":{}>`|`<"Some":"x">`|
+|4|`<"Some":42>`|`<"Some":"x">`|
+
+Now, iff the receiver actually handles all four possibilities
+(and doesnโ€™t just crash if a field is not set, as customary in million-dollar-mistake languages),
+itโ€™s easy to see how removing a field from the producer is semantically equal to always setting it to `<"None":{}>`.
+Since all receivers should be ready to receive `None` for every field, this provides a simple forward-compatibility scheme.
+
+We can abstract this to any kind of tag value:
+If you start with โ€œmoreโ€ tags, you give yourself space to remove them later without breaking compatibility, typically called โ€œforward compatibilityโ€.
+
+
+## To empty list/record or not to
+
+Something to think about is whether records and fields should be defined
+to always contain at least one element.
+
+As it stands, JSON has multiple ways of expressing the โ€œempty valueโ€:
+
+* `null`
+* `[]`
+* `{}`
+* `""`
+* *leave out the field*
+
+and two of those come from the possibility of having empty structured values.
+
+## Representations of this language
+
+This line of thought originally fell out of me designing [`netencode`](https://code.tvl.fyi/tree/users/Profpatsch/netencode/README.md)
+as a small human-debuggable format for pipeline serialization.
+
+In addition to the concepts mentioned here (especially tags),
+it provides a better set of scalars than JSON (specifically arbitrary bytestrings),
+but it cannot practically be written or modified by hand,
+which might be a good thing depending on how you look at it.
+
+---
+
+The way that is compatible with the rest of the ecosystem is probably to use a subset of json
+to represent our idealized language.
+
+There is multiple ways of encoding tags in json, which each have their pros and cons.
+
+The most common is probably the โ€œtag fieldโ€ variant, where the tag is pulled into the nested record:
+
+```
+{
+  "_tag": "Success",
+  "result": 42
+}
+```
+
+Which has the advantage that people know how to deal with it and that itโ€™s easy to โ€œjust add another fieldโ€,
+plus it is backward-compatible when you had a record in the first place.
+
+It has multiple disadvantages however:
+
+* If your value wasnโ€™t a record (e.g. an int) before, you have to put it in a record and assign an arbitrary name to its field
+* People are not forced to โ€œunwrapโ€ the tag first, so they are going to forget to check it
+* The magic โ€œ_tagโ€ name cannot be used by any of the recordโ€™s fields
+
+
+An in-between version of this with less downsides is to always push a json record onto the stack:
+
+```
+{
+  "tag": "Success",
+  "value": {
+    "result": 42
+  }
+}
+```
+
+This makes it harder for people to miss checking the `tag`, but still possible of course.
+It also makes it easily possible to inspect the contents of `value` without knowing the
+exhaustive list of `tag`s, which can be useful in practice (though often not sound!).
+It also gets rid of the โ€œ_tagโ€ field name clash problem.
+
+Disadvantages:
+
+* Breaks the backwards-compatibility with an existing record-based approach if you want to introduce `tag`s
+* Verbosity of representation
+* hard to distinguish a record with the `tag` and `value` fields from a `tag`ed value (though you know the type layout of your data on a higher level, donโ€™t you? ;) )
+
+
+The final, โ€œmost pureโ€ representation is the one I gave in the original introduction:
+
+```
+{
+  "Success": {
+    "result": 42
+  }
+}
+```
+
+Now you *have* to match on the `tag` name first, before you can actually access your data,
+and itโ€™s less verbose than the above representation.
+
+Disavantages:
+
+* You also have to *know* what `tag`s to expect, itโ€™s harder to query cause you need to extract the keys and values from the dict and then take the first one.
+* Doing a โ€œtag backwards compatโ€ check is harder,
+  because you canโ€™t just check whether `_tag` or `tag`/`value` are the keys in the dict.
diff --git a/users/Profpatsch/blog/notes/private-trackers-are-markets.md b/users/Profpatsch/blog/notes/private-trackers-are-markets.md
new file mode 100644
index 0000000000..88fe5f07e5
--- /dev/null
+++ b/users/Profpatsch/blog/notes/private-trackers-are-markets.md
@@ -0,0 +1,46 @@
+# Private bittorrent trackers are markets
+
+Private bittorrent trackers have a currency called ratio,
+which is the bits you upload divided the bits you download.
+
+You have to keep the ratio above a certain lower limit,
+otherwise you get banned from the market or have to cut a deal with the moderators โ†’ bancruptcy
+
+New liquidity (?) is introduced to the market by so-called โ€œfreeleechโ€ events or tokens,
+which essentially allow you to exchange a token (or some time in the case of time-restricted freeleech)
+for some data, which can then be seeded to generate future profits without spending ratio.
+
+Sometimes, ratio is pulled from the market by allowing to exchange it into website perks,
+like forum titles or other benefits like chat-memberships. This has a deflationary effect.
+It could be compared to โ€œvanity itemsโ€ in MMOs, which donโ€™t grant a mechanical advantage in the market.
+Is there a real-world equivalent? i.e. allowing rich people to exchange some of their worth
+for vanity items instead of investing it for future gain?
+
+Sometimes, ratio can be traded for more than just transferred bits,
+for example by requesting a torrent for a certain album or movie,
+paying some ratio for the fulfillment of the request.
+
+---
+
+Based on how bittorrent works, usually multiple people โ€œseedโ€ a torrent.
+This means multiple people can answer a request for trading ratio.
+Part of the request (i.e. the first 30% of a movie)
+can be fulfilled by one party, part of it by a second or even more parties.
+
+For small requests (e.g. albums), often the time between announcing the trade
+and filling the trade is important for who is able to fill it.
+Getting a 1 second head-start vastly increases your chance of a handshake
+and starting the transmission, so on average you get a vastly higher ratio gain from that torrent.
+Meaning that using a bittorrent client which is fast to answer as a seeder will lead to better outcomes.
+This could be compared to mechanisms seen in high-speed trading.
+
+---
+
+Of course these market-mechanisms are in service of a wider policy goal,
+which is to ensure the constant availability of as much high-quality data as possible.
+There is more mechanisms at play on these trackers that all contribute to this goal
+(possible keywords to research: trumping, freeleech for underseeded torrents).
+
+In general, it is important to remember that markets are only a tool,
+never an end in themselves, as neoliberalists would like us to believe.
+They always are in service of a wider goal or policy. We live in a society.
diff --git a/users/Profpatsch/blog/notes/rust-string-conversions.md b/users/Profpatsch/blog/notes/rust-string-conversions.md
index ac8c8f8925..99071ef9d3 100644
--- a/users/Profpatsch/blog/notes/rust-string-conversions.md
+++ b/users/Profpatsch/blog/notes/rust-string-conversions.md
@@ -14,6 +14,7 @@ From       To         Use                                    Comment
 &str     -> String    String::from(st)
 &str     -> &[u8]     st.as_bytes()
 &str     -> Vec<u8>   st.as_bytes().to_owned()               via &[u8]
+&str     -> &OsStr    OsStr::new(st)
 
 String   -> &str      &s                                     alt. s.as_str()
 String   -> &[u8]     s.as_bytes()
diff --git a/users/Profpatsch/blog/posts/2017-05-04-ligature-emulation-in-emacs.md b/users/Profpatsch/blog/posts/2017-05-04-ligature-emulation-in-emacs.md
new file mode 100644
index 0000000000..ba80888bad
--- /dev/null
+++ b/users/Profpatsch/blog/posts/2017-05-04-ligature-emulation-in-emacs.md
@@ -0,0 +1,123 @@
+title: Ligature Emulation in Emacs
+date: 2017-05-04
+
+Monday was (yet another)
+[NixOS hackathon][hackathon] at [OpenLab Augsburg][ola].
+[Maximilian][mhuber] was there and to my amazement
+he got working ligatures in his Haskell files in Emacs! Ever since Hasklig
+updated its format to use ligatures and private Unicode code points a while ago,
+the hack I had used in my config stopped working.
+
+Encouraged by that I decided to take a look on Tuesday. Long story short, I was
+able to [get it working in a pretty satisfying way][done].
+
+[hackathon]: https://www.meetup.com/Munich-NixOS-Meetup/events/239077247/
+[mhuber]: https://github.com/maximilianhuber
+[ola]: https://openlab-augsburg.de
+[done]: https://github.com/i-tu/Hasklig/issues/84#issuecomment-298803495
+
+Whatโ€™s left to do is package it into a module and push to melpa.
+
+
+### elisp still sucks, but itโ€™s bearable, sometimes
+
+Iโ€™m the kind of person who, when trying to fix something elisp related, normally
+gives up two hours later and three macro calls deep. Yes, homoiconic,
+non-lexically-scoped, self-rewriting code is not exactly my fetish.
+This time the task and the library (`prettify-symbols-mode`) were simple enough
+for that to not happen.
+
+Some interesting technical trivia:
+
+- elisp literal character syntax is `?c`. `?\t` is the tab character
+- You join characters by `(string c1 c2 c3 ...)`
+- [dash.el][dash] is pretty awesome and does what a functional programmer
+  expects. Also, Rainbow Dash.
+- Hasklig and FiraCode multi-column symbols actually [only occupy one column, on
+  the far right of the glyph][glyph]. `my-correct-symbol-bounds` fixes emacsโ€™
+  rendering in that case.
+
+
+[dash]: https://github.com/magnars/dash.el
+[glyph]: https://github.com/tonsky/FiraCode/issues/211#issuecomment-239082368
+
+
+## Appendix A
+
+For reference, hereโ€™s the complete code as it stands now. Feel free to paste
+into your config; letโ€™s make it [MIT][mit]. Maybe link to this site, in case there are
+updates.
+
+[mit]: https://opensource.org/licenses/MIT
+
+```elisp
+ (defun my-correct-symbol-bounds (pretty-alist)
+    "Prepend a TAB character to each symbol in this alist,
+this way compose-region called by prettify-symbols-mode
+will use the correct width of the symbols
+instead of the width measured by char-width."
+    (mapcar (lambda (el)
+              (setcdr el (string ?\t (cdr el)))
+              el)
+            pretty-alist))
+
+  (defun my-ligature-list (ligatures codepoint-start)
+    "Create an alist of strings to replace with
+codepoints starting from codepoint-start."
+    (let ((codepoints (-iterate '1+ codepoint-start (length ligatures))))
+      (-zip-pair ligatures codepoints)))
+
+  ; list can be found at https://github.com/i-tu/Hasklig/blob/master/GlyphOrderAndAliasDB#L1588
+  (setq my-hasklig-ligatures
+    (let* ((ligs '("&&" "***" "*>" "\\\\" "||" "|>" "::"
+                   "==" "===" "==>" "=>" "=<<" "!!" ">>"
+                   ">>=" ">>>" ">>-" ">-" "->" "-<" "-<<"
+                   "<*" "<*>" "<|" "<|>" "<$>" "<>" "<-"
+                   "<<" "<<<" "<+>" ".." "..." "++" "+++"
+                   "/=" ":::" ">=>" "->>" "<=>" "<=<" "<->")))
+      (my-correct-symbol-bounds (my-ligature-list ligs #Xe100))))
+
+  ;; nice glyphs for haskell with hasklig
+  (defun my-set-hasklig-ligatures ()
+    "Add hasklig ligatures for use with prettify-symbols-mode."
+    (setq prettify-symbols-alist
+          (append my-hasklig-ligatures prettify-symbols-alist))
+    (prettify-symbols-mode))
+
+  (add-hook 'haskell-mode-hook 'my-set-hasklig-ligatures)
+```
+
+## Appendix B (Update 1): FiraCode integration
+
+I also created a mapping for [FiraCode][fira]. You need to grab the [additional
+symbol font][symbol] that adds (most) ligatures to the unicode private use area.
+Consult your system documentation on how to add it to your font cache.
+Next add `"Fira Code"` and `"Fira Code Symbol"` to your font preferences. Symbol
+only contains the additional characters, so you need both.
+
+If you are on NixOS, the font package should be on the main branch shortly, [I
+added a package][symbol-pkg].
+
+[fira]: https://github.com/tonsky/FiraCode/
+[symbol]: https://github.com/tonsky/FiraCode/issues/211#issuecomment-239058632
+[symbol-pkg]: https://github.com/NixOS/nixpkgs/pull/25517
+
+Hereโ€™s the mapping adjusted for FiraCode:
+
+```elisp
+  (setq my-fira-code-ligatures
+    (let* ((ligs '("www" "**" "***" "**/" "*>" "*/" "\\\\" "\\\\\\"
+                  "{-" "[]" "::" ":::" ":=" "!!" "!=" "!==" "-}"
+                  "--" "---" "-->" "->" "->>" "-<" "-<<" "-~"
+                  "#{" "#[" "##" "###" "####" "#(" "#?" "#_" "#_("
+                  ".-" ".=" ".." "..<" "..." "?=" "??" ";;" "/*"
+                  "/**" "/=" "/==" "/>" "//" "///" "&&" "||" "||="
+                  "|=" "|>" "^=" "$>" "++" "+++" "+>" "=:=" "=="
+                  "===" "==>" "=>" "=>>" "<=" "=<<" "=/=" ">-" ">="
+                  ">=>" ">>" ">>-" ">>=" ">>>" "<*" "<*>" "<|" "<|>"
+                  "<$" "<$>" "<!--" "<-" "<--" "<->" "<+" "<+>" "<="
+                  "<==" "<=>" "<=<" "<>" "<<" "<<-" "<<=" "<<<" "<~"
+                  "<~~" "</" "</>" "~@" "~-" "~=" "~>" "~~" "~~>" "%%"
+                  "x" ":" "+" "+" "*")))
+      (my-correct-symbol-bounds (my-ligature-list ligs #Xe100))))
+```
diff --git a/users/Profpatsch/cabal.project b/users/Profpatsch/cabal.project
new file mode 100644
index 0000000000..26b6186969
--- /dev/null
+++ b/users/Profpatsch/cabal.project
@@ -0,0 +1,14 @@
+packages:
+  ./my-prelude/my-prelude.cabal
+  ./my-webstuff/my-webstuff.cabal
+  ./netencode/netencode.cabal
+  ./arglib/arglib-netencode.cabal
+  ./execline/exec-helpers.cabal
+  ./htmx-experiment/htmx-experiment.cabal
+  ./mailbox-org/mailbox-org.cabal
+  ./cas-serve/cas-serve.cabal
+  ./jbovlaste-sqlite/jbovlaste-sqlite.cabal
+  ./whatcd-resolver/whatcd-resolver.cabal
+  ./openlab-tools/openlab-tools.cabal
+  ./httzip/httzip.cabal
+  ./my-xmonad/my-xmonad.cabal
diff --git a/users/Profpatsch/cas-serve/CasServe.hs b/users/Profpatsch/cas-serve/CasServe.hs
new file mode 100644
index 0000000000..62636fe9c1
--- /dev/null
+++ b/users/Profpatsch/cas-serve/CasServe.hs
@@ -0,0 +1,247 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Main where
+
+import ArglibNetencode (arglibNetencode)
+import Control.Applicative
+import Control.Monad.Reader
+import Crypto.Hash qualified as Crypto
+import Data.ByteArray qualified as ByteArray
+import Data.ByteString.Lazy qualified as ByteString.Lazy
+import Data.ByteString.Lazy qualified as Lazy
+import Data.Functor.Compose
+import Data.Int (Int64)
+import Data.List qualified as List
+import Data.Text qualified as Text
+import Data.Text.IO qualified as Text
+import Database.SQLite.Simple (NamedParam ((:=)))
+import Database.SQLite.Simple qualified as Sqlite
+import Database.SQLite.Simple.FromField qualified as Sqlite
+import Database.SQLite.Simple.QQ qualified as Sqlite
+import Label
+import Netencode.Parse qualified as Net
+import Network.HTTP.Types qualified as Http
+import Network.Wai qualified as Wai
+import Network.Wai.Handler.Warp qualified as Warp
+import PossehlAnalyticsPrelude
+import System.IO (stderr)
+
+parseArglib = do
+  let env = label @"arglibEnvvar" "CAS_SERVE_ARGS"
+  let asApi =
+        Net.asRecord >>> do
+          address <- label @"bindToAddress" <$> (Net.key "bindToAddress" >>> Net.asText)
+          port <- label @"port" <$> (Net.key "port" >>> Net.asText)
+          pure (T2 address port)
+  arglibNetencode "cas-serve" (Just env)
+    <&> Net.runParse
+      [fmt|Cannot parse arguments in "{env.arglibEnvvar}"|]
+      ( Net.asRecord >>> do
+          publicApi <- label @"publicApi" <$> (Net.key "publicApi" >>> asApi)
+          privateApi <- label @"privateApi" <$> (Net.key "privateApi" >>> asApi)
+          pure $ T2 publicApi privateApi
+      )
+
+main :: IO ()
+main = do
+  withEnv $ \env ->
+    Warp.runSettings
+      (Warp.defaultSettings & Warp.setPort 7070)
+      (api env)
+
+withEnv :: (Env -> IO a) -> IO a
+withEnv inner = do
+  withSqlite "./data.sqlite" $ \envData -> do
+    withSqlite "./wordlist.sqlite" $ \envWordlist -> inner Env {..}
+
+withSqlite :: String -> (Sqlite.Connection -> IO a) -> IO a
+withSqlite fileName inner = Sqlite.withConnection fileName $ \conn -> do
+  Sqlite.setTrace conn (Just (\msg -> Text.hPutStrLn stderr [fmt|{fileName}: {msg}|]))
+  Sqlite.execute conn [Sqlite.sql|PRAGMA foreign_keys = ON|] ()
+  inner conn
+
+api :: Env -> Wai.Application
+api env req respond = do
+  case runHandler (getById <|> insertById) req env of
+    Nothing -> respond $ Wai.responseLBS Http.status404 [] "endpoint does not exist."
+    Just handler' -> do
+      handler' >>= \case
+        Left (status, err) -> respond $ Wai.responseLBS status [] (err & toLazyBytes)
+        Right (headers, body) ->
+          respond $
+            Wai.responseLBS
+              Http.status200
+              headers
+              (body & toLazyBytes)
+
+data Env = Env
+  { envWordlist :: Sqlite.Connection,
+    envData :: Sqlite.Connection
+  }
+
+-- | I donโ€™t need any fancy routing in this, so a handler is just something that returns a @Just (IO a)@ if it wants to handle the request.
+newtype Handler a
+  = Handler (ReaderT (Wai.Request, Env) (Compose Maybe IO) a)
+  deriving newtype (Functor, Applicative, Alternative)
+
+handler :: ((Wai.Request, Env) -> Maybe (IO a)) -> Handler a
+handler f = Handler (ReaderT (Compose . f))
+
+runHandler :: Handler a -> Wai.Request -> Env -> Maybe (IO a)
+runHandler (Handler handler') req env = getCompose $ handler' & (\readerT -> runReaderT readerT (req, env))
+
+getById ::
+  Handler
+    ( Either
+        (Http.Status, ByteString)
+        ([(Http.HeaderName, ByteString)], ByteString)
+    )
+getById = handler $ \(req, env) -> do
+  guard ((req & Wai.requestMethod) == Http.methodGet)
+  case req & Wai.pathInfo of
+    ["v0", "by-id", filename] -> Just $ do
+      Sqlite.queryNamed
+        @( T3
+             "mimetype"
+             Text
+             "content"
+             ByteString
+             "size"
+             Int
+         )
+        (env.envData)
+        [Sqlite.sql|
+        SELECT
+          mimetype,
+          cast (content AS blob) as content,
+          size
+        FROM file_content
+        JOIN file_references
+          ON file_references.file_content = file_content.hash_sha256
+        WHERE
+          file_references.reference_type = 'by-id'
+          AND (file_references.name || file_references.extension) = :filename
+       |]
+        [":filename" Sqlite.:= filename]
+        <&> \case
+          [] -> Left (Http.status404, "File not found.")
+          [res] ->
+            Right
+              ( [ ("Content-Type", res.mimetype & textToBytesUtf8),
+                  ("Content-Length", res.size & showToText & textToBytesUtf8)
+                ],
+                -- TODO: should this be lazy/streamed?
+                res.content
+              )
+          _more -> Left "file_references must be unique (in type and name)" & unwrapError
+    _ -> Nothing
+
+insertById :: Handler (Either a ([(Http.HeaderName, ByteString)], ByteString))
+insertById = handler $ \(req, env) -> do
+  guard ((req & Wai.requestMethod) == Http.methodPost)
+  case req & Wai.pathInfo of
+    ["v0", "by-id"] -> Just $ do
+      let maybeText bytes = case bytesToTextUtf8 bytes of
+            Left _err -> Nothing
+            Right t -> Just t
+      let mimeType =
+            ( (req & Wai.requestHeaders & List.lookup "X-Cas-Serve-Mimetype" >>= maybeText)
+                <|> (req & Wai.requestHeaders & List.lookup "Content-Type" >>= maybeText)
+            )
+              & fromMaybe "application/octet-stream"
+
+      let magicFileEnding mimeType' = case Text.split (== '/') mimeType' of
+            [_, ""] -> Nothing
+            ["", _] -> Nothing
+            [_, "any"] -> Nothing
+            ["image", ty] -> Just (Text.cons '.' ty)
+            ["video", ty] -> Just (Text.cons '.' ty)
+            ["text", "plain"] -> Just ".txt"
+            ["text", "html"] -> Just ".html"
+            ["application", "pdf"] -> Just ".pdf"
+            ["application", "json"] -> Just ".json"
+            _ -> Nothing
+
+      let extension =
+            ( (req & Wai.requestHeaders & List.lookup "X-Cas-Serve-FileExtension" >>= maybeText)
+                <|> ( (req & Wai.requestHeaders & List.lookup "Content-Type")
+                        >>= maybeText
+                        >>= magicFileEnding
+                    )
+            )
+              -- Just the empty extension if we canโ€™t figure it out.
+              & fromMaybe ""
+
+      body <- Wai.consumeRequestBodyStrict req
+      let hash :: Crypto.Digest Crypto.SHA256 = Crypto.hashlazy body
+      let hashBytes = hash & ByteArray.convert @(Crypto.Digest Crypto.SHA256) @ByteString
+      let len = ByteString.Lazy.length body
+      name <- getNameFromWordlist env
+      let fullname = name <> extension
+
+      let conn = env.envData
+      Sqlite.withTransaction conn $ do
+        Sqlite.executeNamed
+          conn
+          [Sqlite.sql|
+            INSERT INTO file_content
+              (content, hash_sha256, size)
+              VALUES
+              (:content, :hash_sha256, :size)
+              ON CONFLICT (hash_sha256) DO NOTHING
+          |]
+          [ ":content" := (body :: Lazy.ByteString),
+            ":hash_sha256" := (hashBytes :: ByteString),
+            ":size" := (len :: Int64)
+          ]
+
+        -- TODO: we are not checking if the name already exists,
+        -- we just assume that 1633^3 is enough to not get any collisions for now.
+        -- If the name exists, the user gets a 500.
+        Sqlite.executeNamed
+          conn
+          [Sqlite.sql|
+            INSERT INTO file_references
+              (file_content, reference_type, name, extension, mimetype)
+            VALUES
+              (:file_content, :reference_type, :name, :extension, :mimetype)
+          |]
+          [ ":file_content" := (hashBytes :: ByteString),
+            ":reference_type" := ("by-id" :: Text),
+            ":name" := name,
+            ":extension" := (extension :: Text),
+            ":mimetype" := (mimeType :: Text)
+          ]
+      pure $
+        Right
+          ( [("Content-Type", "text/plain")],
+            [fmt|/v0/by-id/{fullname}|]
+          )
+    _ -> Nothing
+
+-- Get a random name from a wordlist, that is three words connected by @-@.
+getNameFromWordlist :: Env -> IO Text
+getNameFromWordlist env =
+  do
+    let numberOfWords = 3 :: Int
+    Sqlite.queryNamed @(Sqlite.Only Text)
+      (env.envWordlist)
+      [Sqlite.sql|SELECT word FROM wordlist ORDER BY RANDOM() LIMIT :words|]
+      [":words" Sqlite.:= numberOfWords]
+    <&> map Sqlite.fromOnly
+    <&> Text.intercalate "-"
+
+-- | We can use a Rec with a named list of types to parse a returning row of sqlite!!
+instance
+  ( Sqlite.FromField t1,
+    Sqlite.FromField t2,
+    Sqlite.FromField t3
+  ) =>
+  Sqlite.FromRow (T3 l1 t1 l2 t2 l3 t3)
+  where
+  fromRow = do
+    T3
+      <$> (label @l1 <$> Sqlite.field)
+      <*> (label @l2 <$> Sqlite.field)
+      <*> (label @l3 <$> Sqlite.field)
diff --git a/users/Profpatsch/cas-serve/cas-serve.cabal b/users/Profpatsch/cas-serve/cas-serve.cabal
new file mode 100644
index 0000000000..d14776700a
--- /dev/null
+++ b/users/Profpatsch/cas-serve/cas-serve.cabal
@@ -0,0 +1,73 @@
+cabal-version:      3.0
+name:               cas-serve
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+executable cas-serve
+    import: common-options
+
+    main-is:          CasServe.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        pa-label,
+        arglib-netencode,
+        netencode,
+        text,
+        sqlite-simple,
+        http-types,
+        wai,
+        warp,
+        mtl,
+        bytestring,
+        memory,
+        crypton,
diff --git a/users/Profpatsch/cas-serve/default.nix b/users/Profpatsch/cas-serve/default.nix
new file mode 100644
index 0000000000..14c3e4aa13
--- /dev/null
+++ b/users/Profpatsch/cas-serve/default.nix
@@ -0,0 +1,38 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.sqlite [ "sqlite3" ];
+
+  cas-serve = pkgs.haskellPackages.mkDerivation {
+    pname = "cas-serve";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./cas-serve.cabal
+      ./CasServe.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.crypton
+      pkgs.haskellPackages.wai
+      pkgs.haskellPackages.warp
+      pkgs.haskellPackages.sqlite-simple
+      depot.users.Profpatsch.arglib.netencode.haskell
+      depot.users.Profpatsch.netencode.netencode-hs
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+  create-cas-database = depot.nix.writeExecline "create-cas-database" { readNArgs = 1; } [
+    bins.sqlite3
+    "$1"
+    "-init"
+    ./schema.sql
+  ];
+in
+cas-serve
diff --git a/users/Profpatsch/cas-serve/schema.sql b/users/Profpatsch/cas-serve/schema.sql
new file mode 100644
index 0000000000..b61a7a1ad5
--- /dev/null
+++ b/users/Profpatsch/cas-serve/schema.sql
@@ -0,0 +1,38 @@
+-- SQLite
+.dump
+
+PRAGMA foreign_keys = ON;
+
+BEGIN transaction;
+
+create table if not exists file_content (
+  content blob NOT NULL,
+  hash_sha256 blob PRIMARY KEY,
+  size integer NOT NULL
+) WITHOUT ROWID;
+
+
+create table if not exists file_references (
+  rowid integer PRIMARY KEY,
+  file_content NOT NULL REFERENCES file_content ON DELETE CASCADE,
+  reference_type text NOT NULL,
+  name text NOT NULL,
+  extension text NOT NULL,
+  mimetype text NOT NULL
+);
+
+create unique index if not exists file_references_type_name_unique on file_references (reference_type, name);
+
+-- insert into file_content values ('mycontent', 'myhash', 9);
+-- insert into file_references values (NULL, 'myhash', 'by-id', 'myschranz', '.txt', 'text/plain');
+-- insert into file_content values (readfile('/home/philip/Pictures/screenshot.png'), 'anotherhash', 999);
+-- insert into file_references values (NULL, 'anotherhash', 'by-id', 'img', '.png', 'image/png');
+
+select * from file_content;
+
+select * from file_references;
+
+COMMIT;
+
+-- drop table file_content;
+-- drop table file_references;
diff --git a/users/Profpatsch/cas-serve/wordlist.json b/users/Profpatsch/cas-serve/wordlist.json
new file mode 100644
index 0000000000..cc4bc62ad1
--- /dev/null
+++ b/users/Profpatsch/cas-serve/wordlist.json
@@ -0,0 +1 @@
+ [ "acrobat", "africa", "alaska", "albert", "albino", "album", "alcohol", "alex", "alpha", "amadeus", "amanda", "amazon", "america", "analog", "animal", "antenna", "antonio", "apollo", "april", "aroma", "artist", "aspirin", "athlete", "atlas", "banana", "bandit", "banjo", "bikini", "bingo", "bonus", "camera", "canada", "carbon", "casino", "catalog", "cinema", "citizen", "cobra", "comet", "compact", "complex", "context", "credit", "critic", "crystal", "culture", "david", "delta", "dialog", "diploma", "doctor", "domino", "dragon", "drama", "extra", "fabric", "final", "focus", "forum", "galaxy", "gallery", "global", "harmony", "hotel", "humor", "index", "japan", "kilo", "lemon", "liter", "lotus", "mango", "melon", "menu", "meter", "metro", "mineral", "model", "music", "object", "piano", "pirate", "plastic", "radio", "report", "signal", "sport", "studio", "subject", "super", "tango", "taxi", "tempo", "tennis", "textile", "tokyo", "total", "tourist", "video", "visa", "academy", "alfred", "atlanta", "atomic", "barbara", "bazaar", "brother", "budget", "cabaret", "cadet", "candle", "capsule", "caviar", "channel", "chapter", "circle", "cobalt", "comrade", "condor", "crimson", "cyclone", "darwin", "declare", "denver", "desert", "divide", "dolby", "domain", "double", "eagle", "echo", "eclipse", "editor", "educate", "edward", "effect", "electra", "emerald", "emotion", "empire", "eternal", "evening", "exhibit", "expand", "explore", "extreme", "ferrari", "forget", "freedom", "friday", "fuji", "galileo", "genesis", "gravity", "habitat", "hamlet", "harlem", "helium", "holiday", "hunter", "ibiza", "iceberg", "imagine", "infant", "isotope", "jackson", "jamaica", "jasmine", "java", "jessica", "kitchen", "lazarus", "letter", "license", "lithium", "loyal", "lucky", "magenta", "manual", "marble", "maxwell", "mayor", "monarch", "monday", "money", "morning", "mother", "mystery", "native", "nectar", "nelson", "network", "nikita", "nobel", "nobody", "nominal", "norway", "nothing", "number", "october", "office", "oliver", "opinion", "option", "order", "outside", "package", "pandora", "panther", "papa", "pattern", "pedro", "pencil", "people", "phantom", "philips", "pioneer", "pluto", "podium", "portal", "potato", "process", "proxy", "pupil", "python", "quality", "quarter", "quiet", "rabbit", "radical", "radius", "rainbow", "ramirez", "ravioli", "raymond", "respect", "respond", "result", "resume", "richard", "river", "roger", "roman", "rondo", "sabrina", "salary", "salsa", "sample", "samuel", "saturn", "savage", "scarlet", "scorpio", "sector", "serpent", "shampoo", "sharon", "silence", "simple", "society", "sonar", "sonata", "soprano", "sparta", "spider", "sponsor", "abraham", "action", "active", "actor", "adam", "address", "admiral", "adrian", "agenda", "agent", "airline", "airport", "alabama", "aladdin", "alarm", "algebra", "alibi", "alice", "alien", "almond", "alpine", "amber", "amigo", "ammonia", "analyze", "anatomy", "angel", "annual", "answer", "apple", "archive", "arctic", "arena", "arizona", "armada", "arnold", "arsenal", "arthur", "asia", "aspect", "athena", "audio", "august", "austria", "avenue", "average", "axiom", "aztec", "bagel", "baker", "balance", "ballad", "ballet", "bambino", "bamboo", "baron", "basic", "basket", "battery", "belgium", "benefit", "berlin", "bermuda", "bernard", "bicycle", "binary", "biology", "bishop", "blitz", "block", "blonde", "bonjour", "boris", "boston", "bottle", "boxer", "brandy", "bravo", "brazil", "bridge", "british", "bronze", "brown", "bruce", "bruno", "brush", "burger", "burma", "cabinet", "cactus", "cafe", "cairo", "calypso", "camel", "campus", "canal", "cannon", "canoe", "cantina", "canvas", "canyon", "capital", "caramel", "caravan", "career", "cargo", "carlo", "carol", "carpet", "cartel", "cartoon", "castle", "castro", "cecilia", "cement", "center", "century", "ceramic", "chamber", "chance", "change", "chaos", "charlie", "charm", "charter", "cheese", "chef", "chemist", "cherry", "chess", "chicago", "chicken", "chief", "china", "cigar", "circus", "city", "clara", "classic", "claudia", "clean", "client", "climax", "clinic", "clock", "club", "cockpit", "coconut", "cola", "collect", "colombo", "colony", "color", "combat", "comedy", "command", "company", "concert", "connect", "consul", "contact", "contour", "control", "convert", "copy", "corner", "corona", "correct", "cosmos", "couple", "courage", "cowboy", "craft", "crash", "cricket", "crown", "cuba", "dallas", "dance", "daniel", "decade", "decimal", "degree", "delete", "deliver", "delphi", "deluxe", "demand", "demo", "denmark", "derby", "design", "detect", "develop", "diagram", "diamond", "diana", "diego", "diesel", "diet", "digital", "dilemma", "direct", "disco", "disney", "distant", "dollar", "dolphin", "donald", "drink", "driver", "dublin", "duet", "dynamic", "earth", "east", "ecology", "economy", "edgar", "egypt", "elastic", "elegant", "element", "elite", "elvis", "email", "empty", "energy", "engine", "english", "episode", "equator", "escape", "escort", "ethnic", "europe", "everest", "evident", "exact", "example", "exit", "exotic", "export", "express", "factor", "falcon", "family", "fantasy", "fashion", "fiber", "fiction", "fidel", "fiesta", "figure", "film", "filter", "finance", "finish", "finland", "first", "flag", "flash", "florida", "flower", "fluid", "flute", "folio", "ford", "forest", "formal", "formula", "fortune", "forward", "fragile", "france", "frank", "fresh", "friend", "frozen", "future", "gabriel", "gamma", "garage", "garcia", "garden", "garlic", "gemini", "general", "genetic", "genius", "germany", "gloria", "gold", "golf", "gondola", "gong", "good", "gordon", "gorilla", "grand", "granite", "graph", "green", "group", "guide", "guitar", "guru", "hand", "happy", "harbor", "harvard", "havana", "hawaii", "helena", "hello", "henry", "hilton", "history", "horizon", "house", "human", "icon", "idea", "igloo", "igor", "image", "impact", "import", "india", "indigo", "input", "insect", "instant", "iris", "italian", "jacket", "jacob", "jaguar", "janet", "jargon", "jazz", "jeep", "john", "joker", "jordan", "judo", "jumbo", "june", "jungle", "junior", "jupiter", "karate", "karma", "kayak", "kermit", "king", "koala", "korea", "labor", "lady", "lagoon", "laptop", "laser", "latin", "lava", "lecture", "left", "legal", "level", "lexicon", "liberal", "libra", "lily", "limbo", "limit", "linda", "linear", "lion", "liquid", "little", "llama", "lobby", "lobster", "local", "logic", "logo", "lola", "london", "lucas", "lunar", "machine", "macro", "madam", "madonna", "madrid", "maestro", "magic", "magnet", "magnum", "mailbox", "major", "mama", "mambo", "manager", "manila", "marco", "marina", "market", "mars", "martin", "marvin", "mary", "master", "matrix", "maximum", "media", "medical", "mega", "melody", "memo", "mental", "mentor", "mercury", "message", "metal", "meteor", "method", "mexico", "miami", "micro", "milk", "million", "minimum", "minus", "minute", "miracle", "mirage", "miranda", "mister", "mixer", "mobile", "modem", "modern", "modular", "moment", "monaco", "monica", "monitor", "mono", "monster", "montana", "morgan", "motel", "motif", "motor", "mozart", "multi", "museum", "mustang", "natural", "neon", "nepal", "neptune", "nerve", "neutral", "nevada", "news", "next", "ninja", "nirvana", "normal", "nova", "novel", "nuclear", "numeric", "nylon", "oasis", "observe", "ocean", "octopus", "olivia", "olympic", "omega", "opera", "optic", "optimal", "orange", "orbit", "organic", "orient", "origin", "orlando", "oscar", "oxford", "oxygen", "ozone", "pablo", "pacific", "pagoda", "palace", "pamela", "panama", "pancake", "panda", "panel", "panic", "paradox", "pardon", "paris", "parker", "parking", "parody", "partner", "passage", "passive", "pasta", "pastel", "patent", "patient", "patriot", "patrol", "pegasus", "pelican", "penguin", "pepper", "percent", "perfect", "perfume", "period", "permit", "person", "peru", "phone", "photo", "picasso", "picnic", "picture", "pigment", "pilgrim", "pilot", "pixel", "pizza", "planet", "plasma", "plaza", "pocket", "poem", "poetic", "poker", "polaris", "police", "politic", "polo", "polygon", "pony", "popcorn", "popular", "postage", "precise", "prefix", "premium", "present", "price", "prince", "printer", "prism", "private", "prize", "product", "profile", "program", "project", "protect", "proton", "public", "pulse", "puma", "pump", "pyramid", "queen", "radar", "ralph", "random", "rapid", "rebel", "record", "recycle", "reflex", "reform", "regard", "regular", "relax", "reptile", "reverse", "ricardo", "right", "ringo", "risk", "ritual", "robert", "robot", "rocket", "rodeo", "romeo", "royal", "russian", "safari", "salad", "salami", "salmon", "salon", "salute", "samba", "sandra", "santana", "sardine", "school", "scoop", "scratch", "screen", "script", "scroll", "second", "secret", "section", "segment", "select", "seminar", "senator", "senior", "sensor", "serial", "service", "shadow", "sharp", "sheriff", "shock", "short", "shrink", "sierra", "silicon", "silk", "silver", "similar", "simon", "single", "siren", "slang", "slogan", "smart", "smoke", "snake", "social", "soda", "solar", "solid", "solo", "sonic", "source", "soviet", "special", "speed", "sphere", "spiral", "spirit", "spring", "static", "status", "stereo", "stone", "stop", "street", "strong", "student", "style", "sultan", "susan", "sushi", "suzuki", "switch", "symbol", "system", "tactic", "tahiti", "talent", "tarzan", "telex", "texas", "theory", "thermos", "tiger", "titanic", "tomato", "topic", "tornado", "toronto", "torpedo", "totem", "tractor", "traffic", "transit", "trapeze", "travel", "tribal", "trick", "trident", "trilogy", "tripod", "tropic", "trumpet", "tulip", "tuna", "turbo", "twist", "ultra", "uniform", "union", "uranium", "vacuum", "valid", "vampire", "vanilla", "vatican", "velvet", "ventura", "venus", "vertigo", "veteran", "victor", "vienna", "viking", "village", "vincent", "violet", "violin", "virtual", "virus", "vision", "visitor", "visual", "vitamin", "viva", "vocal", "vodka", "volcano", "voltage", "volume", "voyage", "water", "weekend", "welcome", "western", "window", "winter", "wizard", "wolf", "world", "xray", "yankee", "yoga", "yogurt", "yoyo", "zebra", "zero", "zigzag", "zipper", "zodiac", "zoom", "acid", "adios", "agatha", "alamo", "alert", "almanac", "aloha", "andrea", "anita", "arcade", "aurora", "avalon", "baby", "baggage", "balloon", "bank", "basil", "begin", "biscuit", "blue", "bombay", "botanic", "brain", "brenda", "brigade", "cable", "calibre", "carmen", "cello", "celtic", "chariot", "chrome", "citrus", "civil", "cloud", "combine", "common", "cool", "copper", "coral", "crater", "cubic", "cupid", "cycle", "depend", "door", "dream", "dynasty", "edison", "edition", "enigma", "equal", "eric", "event", "evita", "exodus", "extend", "famous", "farmer", "food", "fossil", "frog", "fruit", "geneva", "gentle", "george", "giant", "gilbert", "gossip", "gram", "greek", "grille", "hammer", "harvest", "hazard", "heaven", "herbert", "heroic", "hexagon", "husband", "immune", "inca", "inch", "initial", "isabel", "ivory", "jason", "jerome", "joel", "joshua", "journal", "judge", "juliet", "jump", "justice", "kimono", "kinetic", "leonid", "leopard", "lima", "maze", "medusa", "member", "memphis", "michael", "miguel", "milan", "mile", "miller", "mimic", "mimosa", "mission", "monkey", "moral", "moses", "mouse", "nancy", "natasha", "nebula", "nickel", "nina", "noise", "orchid", "oregano", "origami", "orinoco", "orion", "othello", "paper", "paprika", "prelude", "prepare", "pretend", "promise", "prosper", "provide", "puzzle", "remote", "repair", "reply", "rival", "riviera", "robin", "rose", "rover", "rudolf", "saga", "sahara", "scholar", "shelter", "ship", "shoe", "sigma", "sister", "sleep", "smile", "spain", "spark", "split", "spray", "square", "stadium", "star", "storm", "story", "strange", "stretch", "stuart", "subway", "sugar", "sulfur", "summer", "survive", "sweet", "swim", "table", "taboo", "target", "teacher", "telecom", "temple", "tibet", "ticket", "tina", "today", "toga", "tommy", "tower", "trivial", "tunnel", "turtle", "twin", "uncle", "unicorn", "unique", "update", "valery", "vega", "version", "voodoo", "warning", "william", "wonder", "year", "yellow", "young", "absent", "absorb", "absurd", "accent", "alfonso", "alias", "ambient", "anagram", "andy", "anvil", "appear", "apropos", "archer", "ariel", "armor", "arrow", "austin", "avatar", "axis", "baboon", "bahama", "bali", "balsa", "barcode", "bazooka", "beach", "beast", "beatles", "beauty", "before", "benny", "betty", "between", "beyond", "billy", "bison", "blast", "bless", "bogart", "bonanza", "book", "border", "brave", "bread", "break", "broken", "bucket", "buenos", "buffalo", "bundle", "button", "buzzer", "byte", "caesar", "camilla", "canary", "candid", "carrot", "cave", "chant", "child", "choice", "chris", "cipher", "clarion", "clark", "clever", "cliff", "clone", "conan", "conduct", "congo", "costume", "cotton", "cover", "crack", "current", "danube", "data", "decide", "deposit", "desire", "detail", "dexter", "dinner", "donor", "druid", "drum", "easy", "eddie", "enjoy", "enrico", "epoxy", "erosion", "except", "exile", "explain", "fame", "fast", "father", "felix", "field", "fiona", "fire", "fish", "flame", "flex", "flipper", "float", "flood", "floor", "forbid", "forever", "fractal", "frame", "freddie", "front", "fuel", "gallop", "game", "garbo", "gate", "gelatin", "gibson", "ginger", "giraffe", "gizmo", "glass", "goblin", "gopher", "grace", "gray", "gregory", "grid", "griffin", "ground", "guest", "gustav", "gyro", "hair", "halt", "harris", "heart", "heavy", "herman", "hippie", "hobby", "honey", "hope", "horse", "hostel", "hydro", "imitate", "info", "ingrid", "inside", "invent", "invest", "invite", "ivan", "james", "jester", "jimmy", "join", "joseph", "juice", "julius", "july", "kansas", "karl", "kevin", "kiwi", "ladder", "lake", "laura", "learn", "legacy", "legend", "lesson", "life", "light", "list", "locate", "lopez", "lorenzo", "love", "lunch", "malta", "mammal", "margin", "margo", "marion", "mask", "match", "mayday", "meaning", "mercy", "middle", "mike", "mirror", "modest", "morph", "morris", "mystic", "nadia", "nato", "navy", "needle", "neuron", "never", "newton", "nice", "night", "nissan", "nitro", "nixon", "north", "oberon", "octavia", "ohio", "olga", "open", "opus", "orca", "oval", "owner", "page", "paint", "palma", "parent", "parlor", "parole", "paul", "peace", "pearl", "perform", "phoenix", "phrase", "pierre", "pinball", "place", "plate", "plato", "plume", "pogo", "point", "polka", "poncho", "powder", "prague", "press", "presto", "pretty", "prime", "promo", "quest", "quick", "quiz", "quota", "race", "rachel", "raja", "ranger", "region", "remark", "rent", "reward", "rhino", "ribbon", "rider", "road", "rodent", "round", "rubber", "ruby", "rufus", "sabine", "saddle", "sailor", "saint", "salt", "scale", "scuba", "season", "secure", "shake", "shallow", "shannon", "shave", "shelf", "sherman", "shine", "shirt", "side", "sinatra", "sincere", "size", "slalom", "slow", "small", "snow", "sofia", "song", "sound", "south", "speech", "spell", "spend", "spoon", "stage", "stamp", "stand", "state", "stella", "stick", "sting", "stock", "store", "sunday", "sunset", "support", "supreme", "sweden", "swing", "tape", "tavern", "think", "thomas", "tictac", "time", "toast", "tobacco", "tonight", "torch", "torso", "touch", "toyota", "trade", "tribune", "trinity", "triton", "truck", "trust", "type", "under", "unit", "urban", "urgent", "user", "value", "vendor", "venice", "verona", "vibrate", "virgo", "visible", "vista", "vital", "voice", "vortex", "waiter", "watch", "wave", "weather", "wedding", "wheel", "whiskey", "wisdom", "android", "annex", "armani", "cake", "confide", "deal", "define", "dispute", "genuine", "idiom", "impress", "include", "ironic", "null", "nurse", "obscure", "prefer", "prodigy", "ego", "fax", "jet", "job", "rio", "ski", "yes" ]
diff --git a/users/Profpatsch/cas-serve/wordlist.sqlite b/users/Profpatsch/cas-serve/wordlist.sqlite
new file mode 100644
index 0000000000..5074474ba0
--- /dev/null
+++ b/users/Profpatsch/cas-serve/wordlist.sqlite
Binary files differdiff --git a/users/Profpatsch/cdb.nix b/users/Profpatsch/cdb.nix
new file mode 100644
index 0000000000..86e0a2d58f
--- /dev/null
+++ b/users/Profpatsch/cdb.nix
@@ -0,0 +1,93 @@
+{ depot, pkgs, ... }:
+
+let
+  cdbListToNetencode = depot.nix.writers.rustSimple
+    {
+      name = "cdb-list-to-netencode";
+      dependencies = [
+        depot.third_party.rust-crates.nom
+        depot.users.Profpatsch.execline.exec-helpers
+        depot.users.Profpatsch.netencode.netencode-rs
+      ];
+    } ''
+    extern crate nom;
+    extern crate exec_helpers;
+    extern crate netencode;
+    use std::collections::HashMap;
+    use std::io::BufRead;
+    use nom::{IResult};
+    use nom::sequence::{tuple};
+    use nom::bytes::complete::{tag, take};
+    use nom::character::complete::{digit1, char};
+    use nom::error::{context, ErrorKind, ParseError};
+    use nom::combinator::{map_res};
+    use netencode::{T, Tag};
+
+    fn usize_t(s: &[u8]) -> IResult<&[u8], usize> {
+        context(
+            "usize",
+            map_res(
+                map_res(digit1, |n| std::str::from_utf8(n)),
+                |s| s.parse::<usize>())
+        )(s)
+    }
+
+    fn parse_cdb_record(s: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
+        let (s, (_, klen, _, vlen, _)) = tuple((
+            char('+'),
+            usize_t,
+            char(','),
+            usize_t,
+            char(':')
+        ))(s)?;
+        let (s, (key, _, val)) = tuple((
+            take(klen),
+            tag("->"),
+            take(vlen),
+        ))(s)?;
+        Ok((s, (key, val)))
+    }
+
+    fn main() {
+        let mut res = vec![];
+        let stdin = std::io::stdin();
+        let mut lines = stdin.lock().split(b'\n');
+        loop {
+            match lines.next() {
+                None => exec_helpers::die_user_error("cdb-list-to-netencode", "stdin ended but we didnโ€™t receive the empty line to signify the end of the cdbdump input!"),
+                Some(Err(err)) => exec_helpers::die_temporary("cdb-list-to-netencode", format!("could not read from stdin: {}", err)),
+                Some(Ok(line)) =>
+                    if &line == b"" {
+                        // the cdbdump input ends after an empty line (double \n)
+                        break;
+                    } else {
+                        match parse_cdb_record(&line) {
+                            Ok((b"", (key, val))) => {
+                                let (key, val) = match
+                                    std::str::from_utf8(key)
+                                    .and_then(|k| std::str::from_utf8(val).map(|v| (k, v))) {
+                                    Ok((key, val)) => (key.to_owned(), val.to_owned()),
+                                    Err(err) => exec_helpers::die_user_error("cdb-list-to-netencode", format!("cannot decode line {:?}, we only support utf8-encoded key/values pairs for now: {}", String::from_utf8_lossy(&line), err)),
+                                };
+                                let _ = res.push((key, val));
+                            },
+                            Ok((rest, _)) => exec_helpers::die_user_error("cdb-list-to-netencode", format!("could not decode record line {:?}, had some trailing bytes", String::from_utf8_lossy(&line))),
+                            Err(err) => exec_helpers::die_user_error("cdb-list-to-netencode", format!("could not decode record line {:?}: {:?}", String::from_utf8_lossy(&line), err)),
+                        }
+                    }
+            }
+        }
+        let list = T::List(res.into_iter().map(
+            |(k, v)| T::Record(vec![(String::from("key"), T::Text(k)), (String::from("val"), T::Text(v))].into_iter().collect())
+        ).collect());
+        netencode::encode(&mut std::io::stdout(), &list.to_u());
+    }
+
+  '';
+
+in
+{
+  inherit
+    cdbListToNetencode
+    ;
+}
diff --git a/users/Profpatsch/declib/.eslintrc.json b/users/Profpatsch/declib/.eslintrc.json
new file mode 100644
index 0000000000..9cffc711db
--- /dev/null
+++ b/users/Profpatsch/declib/.eslintrc.json
@@ -0,0 +1,14 @@
+{
+  "extends": ["eslint:recommended", "plugin:@typescript-eslint/strict-type-checked"],
+  "parser": "@typescript-eslint/parser",
+  "plugins": ["@typescript-eslint"],
+  "parserOptions": {
+    "project": true
+  },
+  "root": true,
+  "rules": {
+    "no-unused-vars": "warn",
+    "prefer-const": "warn",
+    "@typescript-eslint/no-unused-vars": "warn"
+  }
+}
diff --git a/users/Profpatsch/declib/.gitignore b/users/Profpatsch/declib/.gitignore
new file mode 100644
index 0000000000..8b56bf4ede
--- /dev/null
+++ b/users/Profpatsch/declib/.gitignore
@@ -0,0 +1,6 @@
+/node_modules/
+/.ninja/
+/output/
+
+# ignore for now
+/package.lock.json
diff --git a/users/Profpatsch/declib/.prettierrc b/users/Profpatsch/declib/.prettierrc
new file mode 100644
index 0000000000..7258fb81e0
--- /dev/null
+++ b/users/Profpatsch/declib/.prettierrc
@@ -0,0 +1,8 @@
+{
+  "trailingComma": "all",
+  "tabWidth": 2,
+  "semi": true,
+  "singleQuote": true,
+  "printWidth": 100,
+  "arrowParens": "avoid"
+}
diff --git a/users/Profpatsch/declib/README.md b/users/Profpatsch/declib/README.md
new file mode 100644
index 0000000000..11a8bf21a5
--- /dev/null
+++ b/users/Profpatsch/declib/README.md
@@ -0,0 +1,4 @@
+# Decentralized Library
+
+https://en.wikipedia.org/wiki/Distributed_library
+https://faculty.ist.psu.edu/jjansen/academic/pubs/ride98/ride98.html
diff --git a/users/Profpatsch/declib/build.ninja b/users/Profpatsch/declib/build.ninja
new file mode 100644
index 0000000000..f8844fc9be
--- /dev/null
+++ b/users/Profpatsch/declib/build.ninja
@@ -0,0 +1,16 @@
+
+builddir = .ninja
+
+outdir = ./output
+jsdir = $outdir/js
+
+rule tsc
+  command = node_modules/.bin/tsc
+
+build $outdir/index.js: tsc | index.ts tsconfig.json
+
+rule run
+  command = node $in
+
+build run: run $outdir/index.js
+  pool = console
diff --git a/users/Profpatsch/declib/index.ts b/users/Profpatsch/declib/index.ts
new file mode 100644
index 0000000000..c6a26f0922
--- /dev/null
+++ b/users/Profpatsch/declib/index.ts
@@ -0,0 +1,245 @@
+import generator, { MegalodonInterface } from 'megalodon';
+import { Account } from 'megalodon/lib/src/entities/account';
+import * as masto from 'megalodon/lib/src/entities/notification';
+import { Status } from 'megalodon/lib/src/entities/status';
+import * as rxjs from 'rxjs';
+import { Observable } from 'rxjs';
+import { NodeEventHandler } from 'rxjs/internal/observable/fromEvent';
+import * as sqlite from 'sqlite';
+import sqlite3 from 'sqlite3';
+import * as parse5 from 'parse5';
+import { mergeMap } from 'rxjs/operators';
+
+type Events =
+  | { type: 'connect'; event: [] }
+  | { type: 'update'; event: Status }
+  | { type: 'notification'; event: Notification }
+  | { type: 'delete'; event: number }
+  | { type: 'error'; event: Error }
+  | { type: 'heartbeat'; event: [] }
+  | { type: 'close'; event: [] }
+  | { type: 'parser-error'; event: Error };
+
+type Notification = masto.Notification & {
+  type: 'favourite' | 'reblog' | 'status' | 'mention' | 'poll' | 'update';
+  status: NonNullable<masto.Notification['status']>;
+  account: NonNullable<masto.Notification['account']>;
+};
+
+class Main {
+  private client: MegalodonInterface;
+  private socket: Observable<Events>;
+  private state!: State;
+  private config: {
+    databaseFile?: string;
+    baseServer: string;
+  };
+
+  private constructor() {
+    this.config = {
+      databaseFile: process.env['DECLIB_DATABASE_FILE'],
+      baseServer: process.env['DECLIB_MASTODON_SERVER'] ?? 'mastodon.xyz',
+    };
+    const ACCESS_TOKEN = process.env['DECLIB_MASTODON_ACCESS_TOKEN'];
+
+    if (!ACCESS_TOKEN) {
+      console.error('Please set DECLIB_MASTODON_ACCESS_TOKEN');
+      process.exit(1);
+    }
+    this.client = generator('mastodon', `https://${this.config.baseServer}`, ACCESS_TOKEN);
+    const websocket = this.client.publicSocket();
+    function mk<Name extends string, Type>(name: Name): Observable<{ type: Name; event: Type }> {
+      const wrap =
+        (h: NodeEventHandler) =>
+        (event: Type): void => {
+          h({ type: name, event });
+        };
+      return rxjs.fromEventPattern<{ type: Name; event: Type }>(
+        hdl => websocket.on(name, wrap(hdl)),
+        hdl => websocket.removeListener(name, wrap(hdl)),
+      );
+    }
+    this.socket = rxjs.merge(
+      mk<'connect', []>('connect'),
+      mk<'update', Status>('update'),
+      mk<'notification', Notification>('notification'),
+      mk<'delete', number>('delete'),
+      mk<'error', Error>('error'),
+      mk<'heartbeat', []>('heartbeat'),
+      mk<'close', []>('close'),
+      mk<'parser-error', Error>('parser-error'),
+    );
+  }
+
+  static async init(): Promise<Main> {
+    const self = new Main();
+    self.state = await State.init(self.config);
+    return self;
+  }
+
+  public main() {
+    // const res = await this.getAcc({ username: 'grindhold', server: 'chaos.social' });
+    // const res = await this.getAcc({ username: 'Profpatsch', server: 'mastodon.xyz' });
+    // const res = await this.getStatus('111862170899069698');
+    this.socket
+      .pipe(
+        mergeMap(async event => {
+          switch (event.type) {
+            case 'update': {
+              await this.state.addStatus(event.event);
+              console.log(`${event.event.account.acct}: ${event.event.content}`);
+              console.log(await this.state.databaseInternal.all(`SELECT * from status`));
+              break;
+            }
+            case 'notification': {
+              console.log(`NOTIFICATION (${event.event.type}):`);
+              console.log(event.event);
+              console.log(event.event.status.content);
+              const content = parseContent(event.event.status.content);
+              if (content) {
+                switch (content.command) {
+                  case 'addbook': {
+                    if (content.content[0]) {
+                      const book = {
+                        $owner: event.event.account.acct,
+                        $bookid: content.content[0],
+                      };
+                      console.log('adding book', book);
+                      await this.state.addBook(book);
+                      await this.client.postStatus(
+                        `@${event.event.account.acct} I have inserted book "${book.$bookid}" for you.`,
+                        {
+                          in_reply_to_id: event.event.status.id,
+                          visibility: 'direct',
+                        },
+                      );
+                    }
+                  }
+                }
+              }
+              break;
+            }
+            default: {
+              console.log(event);
+            }
+          }
+        }),
+      )
+      .subscribe();
+  }
+
+  private async getStatus(id: string): Promise<Status | null> {
+    return (await this.client.getStatus(id)).data;
+  }
+
+  private async getAcc(user: { username: string; server: string }): Promise<Account | null> {
+    const fullAccount = `${user.username}@${user.server}`;
+    const res = await this.client.searchAccount(fullAccount, {
+      limit: 10,
+    });
+    const accs = res.data.filter(acc =>
+      this.config.baseServer === user.server
+        ? (acc.acct = user.username)
+        : acc.acct === fullAccount,
+    );
+    return accs[0] ?? null;
+  }
+}
+
+type Interaction = {
+  originalStatus: { id: string };
+  lastStatus: { id: string };
+};
+
+class State {
+  db!: sqlite.Database;
+  private constructor() {}
+
+  static async init(config: { databaseFile?: string }): Promise<State> {
+    const s = new State();
+    s.db = await sqlite.open({
+      filename: config.databaseFile ?? ':memory:',
+      driver: sqlite3.Database,
+    });
+    await s.db.run('CREATE TABLE books (owner text, bookid text)');
+    await s.db.run('CREATE TABLE status (id text primary key, content json)');
+    return s;
+  }
+
+  async addBook(opts: { $owner: string; $bookid: string }) {
+    return await this.db.run('INSERT INTO books (owner, bookid) VALUES ($owner, $bookid)', opts);
+  }
+
+  async addStatus($status: Status) {
+    return await this.db.run(
+      `
+      INSERT INTO status (id, content) VALUES ($id, $status)
+      ON CONFLICT (id) DO UPDATE SET id = $id, content = $status
+      `,
+      {
+        $id: $status.id,
+        $status: JSON.stringify($status),
+      },
+    );
+  }
+
+  get databaseInternal() {
+    return this.db;
+  }
+}
+
+/** Parse the message; take the plain text, first line is the command any any successive lines are content */
+function parseContent(html: string): { command: string; content: string[] } | null {
+  const plain = contentToPlainText(html).split('\n');
+  if (plain[0]) {
+    return { command: plain[0].replace(' ', '').trim(), content: plain.slice(1) };
+  } else {
+    return null;
+  }
+}
+
+/** Convert the Html content to a plain text (best effort), keeping line breaks */
+function contentToPlainText(html: string): string {
+  const queue: parse5.DefaultTreeAdapterMap['childNode'][] = [];
+  queue.push(...parse5.parseFragment(html).childNodes);
+  let res = '';
+  let endOfP = false;
+  for (const el of queue) {
+    switch (el.nodeName) {
+      case '#text': {
+        res += (el as parse5.DefaultTreeAdapterMap['textNode']).value;
+        break;
+      }
+      case 'br': {
+        res += '\n';
+        break;
+      }
+      case 'p': {
+        if (endOfP) {
+          res += '\n';
+          endOfP = false;
+        }
+        queue.push(...el.childNodes);
+        endOfP = true;
+        break;
+      }
+      case 'span': {
+        break;
+      }
+      default: {
+        console.warn('unknown element in message: ', el);
+        break;
+      }
+    }
+  }
+  return res.trim();
+}
+
+Main.init().then(
+  m => {
+    m.main();
+  },
+  rej => {
+    throw rej;
+  },
+);
diff --git a/users/Profpatsch/declib/package.json b/users/Profpatsch/declib/package.json
new file mode 100644
index 0000000000..93176e8581
--- /dev/null
+++ b/users/Profpatsch/declib/package.json
@@ -0,0 +1,25 @@
+{
+  "name": "declib",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.ts",
+  "type": "commonjs",
+  "scripts": {
+    "run": "ninja run"
+  },
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "megalodon": "^9.2.2",
+    "parse5": "^7.1.2",
+    "rxjs": "^7.8.1",
+    "sqlite": "^5.1.1",
+    "sqlite3": "^5.1.7"
+  },
+  "devDependencies": {
+    "@typescript-eslint/eslint-plugin": "^6.21.0",
+    "@typescript-eslint/parser": "^6.21.0",
+    "eslint": "^8.56.0",
+    "typescript": "^5.3.3"
+  }
+}
diff --git a/users/Profpatsch/declib/tsconfig.json b/users/Profpatsch/declib/tsconfig.json
new file mode 100644
index 0000000000..b7f2f4c18b
--- /dev/null
+++ b/users/Profpatsch/declib/tsconfig.json
@@ -0,0 +1,25 @@
+{
+  "compilerOptions": {
+    "strict": true,
+    "module": "NodeNext",
+    "sourceMap": true,
+    "outDir": "output",
+    "target": "ES6",
+    "lib": [],
+    "typeRoots": ["node_modules/@types", "shims/@types"],
+    "moduleResolution": "NodeNext",
+
+    // importHelpers & downlevelIteration will reduce the generated javascript for new language features.
+    // `importHelpers` requires the `tslib` dependency.
+    // "downlevelIteration": true,
+    // "importHelpers": true
+    "noFallthroughCasesInSwitch": true,
+    "noImplicitOverride": true,
+    "noImplicitReturns": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "noUncheckedIndexedAccess": true,
+
+  },
+
+  "files": ["index.ts"]
+}
diff --git a/users/Profpatsch/dhall/lib.dhall b/users/Profpatsch/dhall/lib.dhall
new file mode 100644
index 0000000000..fb97ba6070
--- /dev/null
+++ b/users/Profpatsch/dhall/lib.dhall
@@ -0,0 +1,84 @@
+let List/map
+    : โˆ€(a : Type) โ†’ โˆ€(b : Type) โ†’ (a โ†’ b) โ†’ List a โ†’ List b
+    = ฮป(a : Type) โ†’
+      ฮป(b : Type) โ†’
+      ฮป(f : a โ†’ b) โ†’
+      ฮป(xs : List a) โ†’
+        List/build
+          b
+          ( ฮป(list : Type) โ†’
+            ฮป(cons : b โ†’ list โ†’ list) โ†’
+              List/fold a xs list (ฮป(x : a) โ†’ cons (f x))
+          )
+
+let
+
+    --| Concatenate a `List` of `List`s into a single `List`
+    List/concat
+    : โˆ€(a : Type) โ†’ List (List a) โ†’ List a
+    = ฮป(a : Type) โ†’
+      ฮป(xss : List (List a)) โ†’
+        List/build
+          a
+          ( ฮป(list : Type) โ†’
+            ฮป(cons : a โ†’ list โ†’ list) โ†’
+            ฮป(nil : list) โ†’
+              List/fold
+                (List a)
+                xss
+                list
+                (ฮป(xs : List a) โ†’ ฮป(ys : list) โ†’ List/fold a xs list cons ys)
+                nil
+          )
+
+let
+
+
+    -- Transform a list by applying a function to each element and flattening the results
+    List/concatMap
+    : โˆ€(a : Type) โ†’ โˆ€(b : Type) โ†’ (a โ†’ List b) โ†’ List a โ†’ List b
+    = ฮป(a : Type) โ†’
+      ฮป(b : Type) โ†’
+      ฮป(f : a โ†’ List b) โ†’
+      ฮป(xs : List a) โ†’
+        List/build
+          b
+          ( ฮป(list : Type) โ†’
+            ฮป(cons : b โ†’ list โ†’ list) โ†’
+              List/fold a xs list (ฮป(x : a) โ†’ List/fold b (f x) list cons)
+          )
+
+let Status = < Empty | NonEmpty : Text >
+
+let
+
+    {-|
+    Transform each value in a `List` to `Text` and then concatenate them with a
+    separator in between each value
+    -}
+    Text/concatMapSep
+    : โˆ€(separator : Text) โ†’ โˆ€(a : Type) โ†’ (a โ†’ Text) โ†’ List a โ†’ Text
+    = ฮป(separator : Text) โ†’
+      ฮป(a : Type) โ†’
+      ฮป(f : a โ†’ Text) โ†’
+      ฮป(elements : List a) โ†’
+        let status =
+              List/fold
+                a
+                elements
+                Status
+                ( ฮป(x : a) โ†’
+                  ฮป(status : Status) โ†’
+                    merge
+                      { Empty = Status.NonEmpty (f x)
+                      , NonEmpty =
+                          ฮป(result : Text) โ†’
+                            Status.NonEmpty (f x ++ separator ++ result)
+                      }
+                      status
+                )
+                Status.Empty
+
+        in  merge { Empty = "", NonEmpty = ฮป(result : Text) โ†’ result } status
+
+in  { List/map, List/concat, List/concatMap, Text/concatMapSep }
diff --git a/users/Profpatsch/emacs-tree-sitter-move/README.md b/users/Profpatsch/emacs-tree-sitter-move/README.md
new file mode 100644
index 0000000000..ae8d763d61
--- /dev/null
+++ b/users/Profpatsch/emacs-tree-sitter-move/README.md
@@ -0,0 +1,5 @@
+# emacs-tree-sitter-move
+
+An experiment in whether we can implement structural editing in emacs using the tree-sitter parser.
+
+What currently works: loading a tree-sitter gramma, navigating the AST left/right/up/down.
diff --git a/users/Profpatsch/emacs-tree-sitter-move/default.nix b/users/Profpatsch/emacs-tree-sitter-move/default.nix
index fdc059c089..a9f259d96d 100644
--- a/users/Profpatsch/emacs-tree-sitter-move/default.nix
+++ b/users/Profpatsch/emacs-tree-sitter-move/default.nix
@@ -1,3 +1,3 @@
 # nothing yet (TODO: expose shell & tool)
-{...}:
-{}
+{ ... }:
+{ }
diff --git a/users/Profpatsch/emacs-tree-sitter-move/shell.nix b/users/Profpatsch/emacs-tree-sitter-move/shell.nix
index 81d622ac73..f400d5c021 100644
--- a/users/Profpatsch/emacs-tree-sitter-move/shell.nix
+++ b/users/Profpatsch/emacs-tree-sitter-move/shell.nix
@@ -1,14 +1,15 @@
-{ pkgs ? import ../../../third_party {}, ... }:
+{ pkgs ? import ../../../third_party { }, ... }:
 let
   inherit (pkgs) lib;
 
-  treeSitterGrammars = pkgs.runCommandLocal "grammars" {} ''
+  treeSitterGrammars = pkgs.runCommandLocal "grammars" { } ''
     mkdir -p $out/bin
     ${lib.concatStringsSep "\n"
       (lib.mapAttrsToList (name: src: "ln -s ${src}/parser $out/bin/${name}.so") pkgs.tree-sitter.builtGrammars)};
   '';
 
-in pkgs.mkShell {
+in
+pkgs.mkShell {
   buildInputs = [
     pkgs.tree-sitter.builtGrammars.python
   ];
diff --git a/users/Profpatsch/exactSource.nix b/users/Profpatsch/exactSource.nix
new file mode 100644
index 0000000000..5c713b5b1c
--- /dev/null
+++ b/users/Profpatsch/exactSource.nix
@@ -0,0 +1,90 @@
+{ ... }:
+# SPDX-License-Identifier: MIT
+# Created by Graham Christensen
+# version from https://github.com/grahamc/mayday/blob/c48f7583e622fe2e695a2a929de34679e5818816/exact-source.nix
+
+let
+  # Require that every path specified does exist.
+  #
+  # By default, Nix won't complain if you refer to a missing file
+  # if you don't actually use it:
+  #
+  #     nix-repl> ./bogus
+  #     /home/grahamc/playground/bogus
+  #
+  #     nix-repl> toString ./bogus
+  #     "/home/grahamc/playground/bogus"
+  #
+  # so in order for this interface to be *exact*, we must
+  # specifically require every provided path exists:
+  #
+  #     nix-repl> "${./bogus}"
+  #     error: getting attributes of path
+  #     '/home/grahamc/playground/bogus': No such file or
+  #     directory
+  requireAllPathsExist = paths:
+    let
+      validation = builtins.map (path: "${path}") paths;
+    in
+    builtins.deepSeq validation paths;
+
+  # Break down a given path in to a list of all of the path and
+  # its parent directories.
+  #
+  # `builtins.path` / `builtins.filterSource` will ask about
+  # a containing directory, and we must say YES otherwise it will
+  # not include anything below it.
+  #
+  # Concretely, convert: "/foo/baz/tux" in to:
+  #     [ "/foo/baz/tux" "/foo/baz" "/foo" ]
+  recursivelyPopDir = path:
+    if path == "/" then [ ]
+    else [ path ] ++ (recursivelyPopDir (builtins.dirOf path));
+
+  # Given a list of of strings, dedup the list and return a
+  # list of all unique strings.
+  #
+  # Note: only works on strings ;):
+  #
+  # First convert [ "foo" "foo" "bar" ] in to:
+  #     [
+  #       { name = "foo"; value = ""; }
+  #       { name = "foo"; value = ""; }
+  #       { name = "bar"; value = ""; }
+  #     ]
+  # then convert that to { "foo" = ""; "bar" = ""; }
+  # then get the attribute names, "foo" and "bar".
+  dedup = strings:
+    let
+      name_value_pairs = builtins.map
+        (string: { name = string; value = ""; })
+        strings;
+      attrset_of_strings = builtins.listToAttrs name_value_pairs;
+    in
+    builtins.attrNames attrset_of_strings;
+
+  exactSource = source_root: paths:
+    let
+      all_possible_paths =
+        let
+          # Convert all the paths in to relative paths on disk.
+          # ie: stringPaths will contain [ "/home/grahamc/playground/..." ];
+          # instead of /nix/store paths.
+          string_paths = builtins.map toString
+            (requireAllPathsExist paths);
+
+          all_paths_with_duplicates = builtins.concatMap
+            recursivelyPopDir
+            string_paths;
+        in
+        dedup all_paths_with_duplicates;
+
+      pathIsSpecified = path:
+        builtins.elem path all_possible_paths;
+    in
+    builtins.path {
+      path = source_root;
+      filter = (path: _type: pathIsSpecified path);
+    };
+in
+exactSource
diff --git a/users/Profpatsch/execline/ExecHelpers.hs b/users/Profpatsch/execline/ExecHelpers.hs
new file mode 100644
index 0000000000..438047b2b9
--- /dev/null
+++ b/users/Profpatsch/execline/ExecHelpers.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TypeApplications #-}
+
+module ExecHelpers where
+
+import Data.String (IsString)
+import MyPrelude
+import qualified System.Exit as Sys
+
+newtype CurrentProgramName = CurrentProgramName { unCurrentProgramName :: Text }
+  deriving newtype (Show, Eq, Ord, IsString)
+
+-- | Exit 1 to signify a generic expected error
+-- (e.g. something that sometimes just goes wrong, like a nix build).
+dieExpectedError :: CurrentProgramName -> Text -> IO a
+dieExpectedError = dieWith 1
+
+-- | Exit 100 to signify a user error (โ€œthe user is holding it wrongโ€).
+--  This is a permanent error, if the program is executed the same way
+-- it should crash with 100 again.
+dieUserError :: CurrentProgramName -> Text -> IO a
+dieUserError = dieWith 100
+
+-- |  Exit 101 to signify an unexpected crash (failing assertion or panic).
+diePanic :: CurrentProgramName -> Text -> IO a
+diePanic = dieWith 101
+
+-- | Exit 111 to signify a temporary error (such as resource exhaustion)
+dieTemporary :: CurrentProgramName -> Text -> IO a
+dieTemporary = dieWith 111
+
+-- |  Exit 126 to signify an environment problem
+-- (the user has set up stuff incorrectly so the program cannot work)
+dieEnvironmentProblem :: CurrentProgramName -> Text -> IO a
+dieEnvironmentProblem = dieWith 126
+
+-- | Exit 127 to signify a missing executable.
+dieMissingExecutable :: CurrentProgramName -> Text -> IO a
+dieMissingExecutable = dieWith 127
+
+dieWith :: Natural -> CurrentProgramName -> Text -> IO a
+dieWith status currentProgramName msg = do
+  putStderrLn [fmt|{currentProgramName & unCurrentProgramName}: {msg}|]
+  Sys.exitWith
+    (Sys.ExitFailure (status & fromIntegral @Natural @Int))
diff --git a/users/Profpatsch/execline/default.nix b/users/Profpatsch/execline/default.nix
index 2d1b911373..04d07895c6 100644
--- a/users/Profpatsch/execline/default.nix
+++ b/users/Profpatsch/execline/default.nix
@@ -1,12 +1,70 @@
 { depot, pkgs, lib, ... }:
 
 let
-  exec-helpers = depot.nix.writers.rustSimpleLib {
-    name = "exec-helpers";
-  } (builtins.readFile ./exec_helpers.rs);
+  exec-helpers-hs = pkgs.haskellPackages.mkDerivation {
+    pname = "exec-helpers";
+    version = "0.1.0";
 
-in depot.nix.utils.drvTargets {
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./exec-helpers.cabal
+      ./ExecHelpers.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-prelude
+    ];
+
+    isLibrary = true;
+    license = lib.licenses.mit;
+  };
+
+  print-one-env = depot.nix.writers.rustSimple
+    {
+      name = "print-one-env";
+      dependencies = [
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
+    extern crate exec_helpers;
+    use std::os::unix::ffi::OsStrExt;
+    use std::io::Write;
+
+    fn main() {
+      let args = exec_helpers::args("print-one-env", 1);
+      let valname = std::ffi::OsStr::from_bytes(&args[0]);
+      match std::env::var_os(&valname) {
+        None => exec_helpers::die_user_error("print-one-env", format!("Env variable `{:?}` is not set", valname)),
+        Some(val) => std::io::stdout().write_all(&val.as_bytes()).unwrap()
+      }
+    }
+  '';
+
+  setsid = depot.nix.writers.rustSimple
+    {
+      name = "setsid";
+      dependencies = [
+        depot.users.Profpatsch.execline.exec-helpers
+        depot.third_party.rust-crates.libc
+      ];
+    } ''
+    use std::os::unix::ffi::OsStrExt;
+    use std::ffi::OsStr;
+
+    fn main() {
+      let (args, prog) = exec_helpers::args_for_exec("setsid", 1);
+      let envvar = OsStr::from_bytes(&args.get(0).expect("first argument must be envvar name to set"));
+      let sid: i32 = unsafe { libc::setsid() };
+      std::env::set_var(envvar, format!("{}", sid));
+      let env: Vec<(&[u8], &[u8])> = vec![];
+      exec_helpers::exec_into_args("getid", prog, env);
+    }
+  '';
+
+in
+depot.nix.readTree.drvTargets {
   inherit
-    exec-helpers
+    exec-helpers-hs
+    print-one-env
+    setsid
     ;
 }
diff --git a/users/Profpatsch/execline/exec-helpers.cabal b/users/Profpatsch/execline/exec-helpers.cabal
new file mode 100644
index 0000000000..b472ff6bd5
--- /dev/null
+++ b/users/Profpatsch/execline/exec-helpers.cabal
@@ -0,0 +1,14 @@
+cabal-version:      3.0
+name:               exec-helpers
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+library
+    exposed-modules:          ExecHelpers
+
+    build-depends:
+        base >=4.15 && <5,
+        my-prelude
+
+    default-language: Haskell2010
diff --git a/users/Profpatsch/execline/exec-helpers/Cargo.lock b/users/Profpatsch/execline/exec-helpers/Cargo.lock
new file mode 100644
index 0000000000..1753cc949d
--- /dev/null
+++ b/users/Profpatsch/execline/exec-helpers/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "exec_helpers"
+version = "0.1.0"
diff --git a/users/Profpatsch/execline/exec-helpers/Cargo.toml b/users/Profpatsch/execline/exec-helpers/Cargo.toml
new file mode 100644
index 0000000000..6642b66ee3
--- /dev/null
+++ b/users/Profpatsch/execline/exec-helpers/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "exec_helpers"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "exec_helpers"
+path = "exec_helpers.rs"
diff --git a/users/Profpatsch/execline/exec-helpers/default.nix b/users/Profpatsch/execline/exec-helpers/default.nix
new file mode 100644
index 0000000000..5545d41d9d
--- /dev/null
+++ b/users/Profpatsch/execline/exec-helpers/default.nix
@@ -0,0 +1,6 @@
+{ depot, ... }:
+depot.nix.writers.rustSimpleLib
+{
+  name = "exec-helpers";
+}
+  (builtins.readFile ./exec_helpers.rs)
diff --git a/users/Profpatsch/execline/exec_helpers.rs b/users/Profpatsch/execline/exec-helpers/exec_helpers.rs
index b9e1f57973..a57cbca353 100644
--- a/users/Profpatsch/execline/exec_helpers.rs
+++ b/users/Profpatsch/execline/exec-helpers/exec_helpers.rs
@@ -1,13 +1,16 @@
-use std::os::unix::process::CommandExt;
 use std::ffi::OsStr;
-use std::os::unix::ffi::{OsStringExt, OsStrExt};
+use std::os::unix::ffi::{OsStrExt, OsStringExt};
+use std::os::unix::process::CommandExt;
 
 pub fn no_args(current_prog_name: &str) -> () {
     let mut args = std::env::args_os();
     // remove argv[0]
     let _ = args.nth(0);
     if args.len() > 0 {
-        die_user_error(current_prog_name, format!("Expected no arguments, got {:?}", args.collect::<Vec<_>>()))
+        die_user_error(
+            current_prog_name,
+            format!("Expected no arguments, got {:?}", args.collect::<Vec<_>>()),
+        )
     }
 }
 
@@ -16,31 +19,46 @@ pub fn args(current_prog_name: &str, no_of_positional_args: usize) -> Vec<Vec<u8
     // remove argv[0]
     let _ = args.nth(0);
     if args.len() != no_of_positional_args {
-        die_user_error(current_prog_name, format!("Expected {} arguments, got {}, namely {:?}", no_of_positional_args, args.len(), args.collect::<Vec<_>>()))
+        die_user_error(
+            current_prog_name,
+            format!(
+                "Expected {} arguments, got {}, namely {:?}",
+                no_of_positional_args,
+                args.len(),
+                args.collect::<Vec<_>>()
+            ),
+        )
     }
     args.map(|arg| arg.into_vec()).collect()
 }
 
-pub fn args_for_exec(current_prog_name: &str, no_of_positional_args: usize) -> (Vec<Vec<u8>>, Vec<Vec<u8>>) {
+pub fn args_for_exec(
+    current_prog_name: &str,
+    no_of_positional_args: usize,
+) -> (Vec<Vec<u8>>, Vec<Vec<u8>>) {
     let mut args = std::env::args_os();
     // remove argv[0]
     let _ = args.nth(0);
     let mut args = args.map(|arg| arg.into_vec());
     let mut pos_args = vec![];
     // get positional args
-    for i in 1..no_of_positional_args+1 {
-            pos_args.push(
-                args.nth(0).expect(
-                    &format!("{}: expects {} positional args, only got {}", current_prog_name, no_of_positional_args, i))
-            );
+    for i in 1..no_of_positional_args + 1 {
+        pos_args.push(args.nth(0).expect(&format!(
+            "{}: expects {} positional args, only got {}",
+            current_prog_name, no_of_positional_args, i
+        )));
     }
     // prog... is the rest of the iterator
-    let prog : Vec<Vec<u8>> = args.collect();
+    let prog: Vec<Vec<u8>> = args.collect();
     (pos_args, prog)
 }
 
-pub fn exec_into_args<'a, 'b, Args, Arg, Env, Key, Val>(current_prog_name: &str, args: Args, env_additions: Env) -> !
-    where
+pub fn exec_into_args<'a, 'b, Args, Arg, Env, Key, Val>(
+    current_prog_name: &str,
+    args: Args,
+    env_additions: Env,
+) -> !
+where
     Args: IntoIterator<Item = Arg>,
     Arg: AsRef<[u8]>,
     Env: IntoIterator<Item = (Key, Val)>,
@@ -50,27 +68,40 @@ pub fn exec_into_args<'a, 'b, Args, Arg, Env, Key, Val>(current_prog_name: &str,
     // TODO: is this possible without collecting into a Vec first, just leaving it an IntoIterator?
     let args = args.into_iter().collect::<Vec<Arg>>();
     let mut args = args.iter().map(|v| OsStr::from_bytes(v.as_ref()));
-    let prog = args.nth(0).expect(&format!("{}: first argument must be an executable", current_prog_name));
+    let prog = args.nth(0).expect(&format!(
+        "{}: first argument must be an executable",
+        current_prog_name
+    ));
     // TODO: same here
     let env = env_additions.into_iter().collect::<Vec<(Key, Val)>>();
-    let env = env.iter().map(|(k,v)| (OsStr::from_bytes(k.as_ref()), OsStr::from_bytes(v.as_ref())));
+    let env = env
+        .iter()
+        .map(|(k, v)| (OsStr::from_bytes(k.as_ref()), OsStr::from_bytes(v.as_ref())));
     let err = std::process::Command::new(prog).args(args).envs(env).exec();
-    die_missing_executable(current_prog_name, format!("exec failed: {}, while trying to execing into {:?}", err, prog));
+    die_missing_executable(
+        current_prog_name,
+        format!(
+            "exec failed: {}, while trying to execing into {:?}",
+            err, prog
+        ),
+    );
 }
 
 /// Exit 1 to signify a generic expected error
 /// (e.g. something that sometimes just goes wrong, like a nix build).
 pub fn die_expected_error<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
+where
+    S: AsRef<str>,
 {
-  die_with(1, current_prog_name, msg)
+    die_with(1, current_prog_name, msg)
 }
 
 /// Exit 100 to signify a user error (โ€œthe user is holding it wrongโ€).
 /// This is a permanent error, if the program is executed the same way
 /// it should crash with 100 again.
 pub fn die_user_error<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
+where
+    S: AsRef<str>,
 {
     die_with(100, current_prog_name, msg)
 }
@@ -78,14 +109,16 @@ where S: AsRef<str>
 /// Exit 101 to signify an unexpected crash (failing assertion or panic).
 /// This is the same exit code that `panic!()` emits.
 pub fn die_panic<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
+where
+    S: AsRef<str>,
 {
     die_with(101, current_prog_name, msg)
 }
 
 /// Exit 111 to signify a temporary error (such as resource exhaustion)
 pub fn die_temporary<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
+where
+    S: AsRef<str>,
 {
     die_with(111, current_prog_name, msg)
 }
@@ -93,20 +126,23 @@ where S: AsRef<str>
 /// Exit 126 to signify an environment problem
 /// (the user has set up stuff incorrectly so the program cannot work)
 pub fn die_environment_problem<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
+where
+    S: AsRef<str>,
 {
     die_with(126, current_prog_name, msg)
 }
 
 /// Exit 127 to signify a missing executable.
 pub fn die_missing_executable<S>(current_prog_name: &str, msg: S) -> !
-where S: AsRef<str>
+where
+    S: AsRef<str>,
 {
     die_with(127, current_prog_name, msg)
 }
 
 fn die_with<S>(status: i32, current_prog_name: &str, msg: S) -> !
-    where S: AsRef<str>
+where
+    S: AsRef<str>,
 {
     eprintln!("{}: {}", current_prog_name, msg.as_ref());
     std::process::exit(status)
diff --git a/users/Profpatsch/fafo.jpg b/users/Profpatsch/fafo.jpg
new file mode 100644
index 0000000000..78f11d208e
--- /dev/null
+++ b/users/Profpatsch/fafo.jpg
Binary files differdiff --git a/users/Profpatsch/git-db/default.nix b/users/Profpatsch/git-db/default.nix
new file mode 100644
index 0000000000..ad5d927677
--- /dev/null
+++ b/users/Profpatsch/git-db/default.nix
@@ -0,0 +1,10 @@
+{ depot, pkgs, lib, ... }:
+
+depot.nix.writers.rustSimple
+{
+  name = "git-db";
+  dependencies = [
+    depot.third_party.rust-crates.git2
+  ];
+}
+  (builtins.readFile ./git-db.rs)
diff --git a/users/Profpatsch/git-db/git-db.rs b/users/Profpatsch/git-db/git-db.rs
new file mode 100644
index 0000000000..c8019bf036
--- /dev/null
+++ b/users/Profpatsch/git-db/git-db.rs
@@ -0,0 +1,90 @@
+extern crate git2;
+use std::os::unix::ffi::OsStrExt;
+use std::path::PathBuf;
+
+const DEFAULT_BRANCH: &str = "refs/heads/main";
+
+fn main() {
+    let git_db_dir = std::env::var_os("GIT_DB_DIR").expect("set GIT_DB_DIR");
+    let git_db = PathBuf::from(git_db_dir).join("git");
+
+    std::fs::create_dir_all(&git_db).unwrap();
+
+    let repo = git2::Repository::init_opts(
+        &git_db,
+        git2::RepositoryInitOptions::new()
+            .bare(true)
+            .mkpath(true)
+            .description("git-db database")
+            .initial_head(DEFAULT_BRANCH),
+    )
+    .expect(&format!(
+        "unable to create or open bare git repo at {}",
+        &git_db.display()
+    ));
+
+    let mut index = repo.index().expect("cannot get the git index file");
+    eprintln!("{:#?}", index.version());
+    index.clear().expect("could not clean the index");
+
+    let now = std::time::SystemTime::now()
+        .duration_since(std::time::SystemTime::UNIX_EPOCH)
+        .expect("unable to get system time");
+
+    let now_git_time = git2::IndexTime::new(
+        now.as_secs() as i32, // lol
+        u32::from(now.subsec_nanos()),
+    );
+
+    let data = "hi, itโ€™s me".as_bytes();
+
+    index
+        .add_frombuffer(
+            &git2::IndexEntry {
+            mtime: now_git_time,
+            ctime: now_git_time,
+            // donโ€™t make sense
+            dev: 0,
+            ino: 0,
+            mode: /*libc::S_ISREG*/ 0b1000 << (3+9) | /* read write for owner */ 0o644,
+            uid: 0,
+            gid: 0,
+            file_size: data.len() as u32, // lol again
+            id: git2::Oid::zero(),
+            flags: 0,
+            flags_extended: 0,
+            path: "hi.txt".as_bytes().to_owned(),
+        },
+            data,
+        )
+        .expect("could not add data to index");
+
+    let oid = index.write_tree().expect("could not write index tree");
+
+    let to_add_tree = repo
+        .find_tree(oid)
+        .expect("we just created this tree, where did it go?");
+
+    let parent_commits = match repo.find_reference(DEFAULT_BRANCH) {
+        Ok(ref_) => vec![ref_.peel_to_commit().expect(&format!(
+            "reference {} does not point to a commit",
+            DEFAULT_BRANCH
+        ))],
+        Err(err) => match err.code() {
+            // no commit exists yet
+            git2::ErrorCode::NotFound => vec![],
+            _ => panic!("could not read latest commit from {}", DEFAULT_BRANCH),
+        },
+    };
+    repo.commit(
+        Some(DEFAULT_BRANCH),
+        &git2::Signature::now("Mr. Authorboy", "author@example.com").unwrap(),
+        &git2::Signature::now("Mr. Commiterboy", "committer@example.com").unwrap(),
+        "This is my first commit!\n\
+         \n\
+         I wonder if it supports extended commit descriptions?\n",
+        &to_add_tree,
+        &parent_commits.iter().collect::<Vec<_>>()[..],
+    )
+    .expect("could not commit the index we just wrote");
+}
diff --git a/users/Profpatsch/haskell-module-deps/README.md b/users/Profpatsch/haskell-module-deps/README.md
new file mode 100644
index 0000000000..b4f35beac5
--- /dev/null
+++ b/users/Profpatsch/haskell-module-deps/README.md
@@ -0,0 +1,5 @@
+# haskell-module-deps
+
+An executable that when run in a project directory containing `.hs` files in `./src` will output a png/graph of how those modules import each other, transitively.
+
+Useful for getting an overview, finding weird import edges, figuring out how to get more compilation parallelism into your Haskell project.
diff --git a/users/Profpatsch/haskell-module-deps/default.nix b/users/Profpatsch/haskell-module-deps/default.nix
new file mode 100644
index 0000000000..71cc0a5b0d
--- /dev/null
+++ b/users/Profpatsch/haskell-module-deps/default.nix
@@ -0,0 +1,55 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.zathura [ "zathura" ]
+    // depot.nix.getBins pkgs.haskellPackages.graphmod [ "graphmod" ]
+    // depot.nix.getBins pkgs.graphviz [ "dot" ]
+  ;
+
+  # Display a graph of all modules in a project and how they depend on each other.
+  # Takes the project directory as argument.
+  # Open in zathura.
+  haskell-module-deps = depot.nix.writeExecline "haskell-module-deps" { } [
+    "pipeline"
+    [ haskell-module-deps-with-filetype "pdf" "$@" ]
+    bins.zathura
+    "-"
+  ];
+
+  # Display a graph of all modules in a project and how they depend on each other.
+  # Takes the project directory as argument.
+  # Print a png to stdout.
+  haskell-module-deps-png = depot.nix.writeExecline "haskell-module-deps-png" { } [
+    haskell-module-deps-with-filetype
+    "png"
+    "$@"
+  ];
+
+  # Display a graph of all modules in a project and how they depend on each other.
+  # Takes the file type to generate as first argument
+  # and the project directory as second argument.
+  haskell-module-deps-with-filetype = pkgs.writers.writeBash "haskell-module-deps-with-filetype" ''
+    set -euo pipefail
+    shopt -s globstar
+    filetype="$1"
+    rootDir="$2"
+    ${bins.graphmod} \
+      ${/*silence warnings for missing external dependencies*/""} \
+      --quiet \
+      ${/*applies some kind of import simplification*/""} \
+      --prune-edges \
+      "$rootDir"/src/**/*.hs \
+      | ${bins.dot} \
+          ${/*otherwise itโ€™s a bit cramped*/""} \
+          -Gsize="20,20!" \
+          -T"$filetype"
+  '';
+
+in
+depot.nix.readTree.drvTargets {
+  inherit
+    haskell-module-deps
+    haskell-module-deps-png
+    haskell-module-deps-with-filetype
+    ;
+}
diff --git a/users/Profpatsch/haskell-module-deps/example-output-dhall-haskell.png b/users/Profpatsch/haskell-module-deps/example-output-dhall-haskell.png
new file mode 100644
index 0000000000..53725c49e8
--- /dev/null
+++ b/users/Profpatsch/haskell-module-deps/example-output-dhall-haskell.png
Binary files differdiff --git a/users/Profpatsch/hie.yaml b/users/Profpatsch/hie.yaml
new file mode 100644
index 0000000000..1b5ae942ad
--- /dev/null
+++ b/users/Profpatsch/hie.yaml
@@ -0,0 +1,36 @@
+cradle:
+  cabal:
+    - path: "./my-prelude"
+      component: "lib:my-prelude"
+    - path: "./my-webstuff"
+      component: "lib:my-webstuff"
+    - path: "./netencode"
+      component: "lib:netencode"
+    - path: "./arglib"
+      component: "lib:arglib-netencode"
+    - path: "./execline"
+      component: "lib:exec-helpers"
+    - path: "./htmx-experiment/src"
+      component: "lib:htmx-experiment"
+    - path: "./htmx-experiment/Main.hs"
+      component: "htmx-experiment:exe:htmx-experiment"
+    - path: "./mailbox-org/src"
+      component: "lib:mailbox-org"
+    - path: "./mailbox-org/MailboxOrg.hs"
+      component: "mailbox-org:exe:mailbox-org"
+    - path: "./cas-serve/CasServe.hs"
+      component: "cas-serve:exe:cas-serve"
+    - path: "./jbovlaste-sqlite/JbovlasteSqlite.hs"
+      component: "jbovlaste-sqlite:exe:jbovlaste-sqlite"
+    - path: "./whatcd-resolver/src"
+      component: "lib:whatcd-resolver"
+    - path: "./whatcd-resolver/Main.hs"
+      component: "whatcd-resolver:exe:whatcd-resolver"
+    - path: "./openlab-tools/src"
+      component: "lib:openlab-tools"
+    - path: "./openlab-tools/Main.hs"
+      component: "openlab-tools:exe:openlab-tools"
+    - path: "./httzip/Httzip.hs"
+      component: "httzip:exe:httzip"
+    - path: "./my-xmonad/Xmonad.hs"
+      component: "my-xmonad:exe:xmonad"
diff --git a/users/Profpatsch/htmx-experiment/Main.hs b/users/Profpatsch/htmx-experiment/Main.hs
new file mode 100644
index 0000000000..29ce8610ff
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/Main.hs
@@ -0,0 +1,4 @@
+import HtmxExperiment qualified
+
+main :: IO ()
+main = HtmxExperiment.main
diff --git a/users/Profpatsch/htmx-experiment/default.nix b/users/Profpatsch/htmx-experiment/default.nix
new file mode 100644
index 0000000000..ef1a28bd2b
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/default.nix
@@ -0,0 +1,46 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  htmx-experiment = pkgs.haskellPackages.mkDerivation {
+    pname = "htmx-experiment";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./htmx-experiment.cabal
+      ./Main.hs
+      ./src/HtmxExperiment.hs
+      ./src/ServerErrors.hs
+      ./src/ValidationParseT.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-webstuff
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.blaze-html
+      pkgs.haskellPackages.blaze-markup
+      pkgs.haskellPackages.bytestring
+      pkgs.haskellPackages.dlist
+      pkgs.haskellPackages.http-types
+      pkgs.haskellPackages.ihp-hsx
+      pkgs.haskellPackages.monad-logger
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.pa-field-parser
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.selective
+      pkgs.haskellPackages.text
+      pkgs.haskellPackages.unliftio
+      pkgs.haskellPackages.wai
+      pkgs.haskellPackages.warp
+
+    ];
+
+    isLibrary = false;
+    isExecutable = true;
+    license = lib.licenses.mit;
+  };
+
+
+in
+htmx-experiment
diff --git a/users/Profpatsch/htmx-experiment/htmx-experiment.cabal b/users/Profpatsch/htmx-experiment/htmx-experiment.cabal
new file mode 100644
index 0000000000..e9a0d93614
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/htmx-experiment.cabal
@@ -0,0 +1,89 @@
+cabal-version:      3.0
+name:               htmx-experiment
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+library
+    import: common-options
+    exposed-modules:
+      HtmxExperiment,
+      ServerErrors,
+      ValidationParseT
+    hs-source-dirs: ./src
+
+    build-depends:
+        base >=4.15 && <5,
+        -- http-api-data
+        blaze-html,
+        blaze-markup,
+        bytestring,
+        dlist,
+        http-types,
+        ihp-hsx,
+        monad-logger,
+        pa-error-tree,
+        pa-field-parser,
+        pa-label,
+        pa-prelude,
+        my-webstuff,
+        selective,
+        text,
+        unliftio,
+        wai,
+        warp
+
+
+executable htmx-experiment
+    import: common-options
+    main-is: Main.hs
+
+    build-depends:
+        htmx-experiment,
+        base >=4.15 && <5,
diff --git a/users/Profpatsch/htmx-experiment/src/HtmxExperiment.hs b/users/Profpatsch/htmx-experiment/src/HtmxExperiment.hs
new file mode 100644
index 0000000000..225206a584
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/src/HtmxExperiment.hs
@@ -0,0 +1,377 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE QuasiQuotes #-}
+
+module HtmxExperiment where
+
+import Control.Category qualified as Cat
+import Control.Exception qualified as Exc
+import Control.Monad.Logger
+import Control.Selective (Selective (select))
+import Control.Selective qualified as Selective
+import Data.ByteString qualified as Bytes
+import Data.DList (DList)
+import Data.Functor.Compose
+import Data.List qualified as List
+import Data.Maybe (maybeToList)
+import Data.Maybe qualified as Maybe
+import Data.Monoid qualified as Monoid
+import Data.Text qualified as Text
+import FieldParser hiding (nonEmpty)
+import GHC.TypeLits (KnownSymbol, symbolVal)
+import IHP.HSX.QQ (hsx)
+import Label
+import Multipart2 (FormValidation (FormValidation), FormValidationResult, MultipartParseT, failFormValidation)
+import Multipart2 qualified as Multipart
+import Network.HTTP.Types qualified as Http
+import Network.Wai qualified as Wai
+import Network.Wai.Handler.Warp qualified as Warp
+import PossehlAnalyticsPrelude
+import ServerErrors (ServerError (..), throwUserErrorTree)
+import Text.Blaze.Html5 (Html, docTypeHtml)
+import Text.Blaze.Renderer.Utf8 (renderMarkup)
+import UnliftIO (MonadUnliftIO (withRunInIO))
+import Prelude hiding (compare)
+
+-- data Routes
+--   = Root
+--   | Register
+--   | RegisterSubmit
+
+-- data Router url = Router
+--   { parse :: Routes.URLParser url,
+--     print :: url -> [Text]
+--   }
+
+-- routerPathInfo :: Routes.PathInfo a => Router a
+-- routerPathInfo =
+--   Router
+--     { parse = Routes.fromPathSegments,
+--       print = Routes.toPathSegments
+--     }
+
+-- subroute :: Text -> Router subUrl -> Router subUrl
+-- subroute path inner =
+--   Router
+--     { parse = Routes.segment path *> inner.parse,
+--       print = \url -> path : inner.print url
+--     }
+
+-- routerLeaf :: a -> Router a
+-- routerLeaf a =
+--   Router
+--     { parse = pure a,
+--       print = \_ -> []
+--     }
+
+-- routerToSite ::
+--   ((url -> [(Text, Maybe Text)] -> Text) -> url -> a) ->
+--   Router url ->
+--   Routes.Site url a
+-- routerToSite handler router =
+--   Routes.Site
+--     { handleSite = handler,
+--       formatPathSegments = (\x -> (x, [])) . router.print,
+--       parsePathSegments = Routes.parseSegments router.parse
+--     }
+
+-- handlers queryParams = \case
+--   Root -> "root"
+--   Register -> "register"
+--   RegisterSubmit -> "registersubmit"
+
+newtype Router handler from to = Router {unRouter :: from -> [Text] -> (Maybe handler, to)}
+  deriving
+    (Functor, Applicative)
+    via ( Compose
+            ((->) from)
+            ( Compose
+                ((->) [Text])
+                ((,) (Monoid.First handler))
+            )
+        )
+
+data Routes r handler = Routes
+  { users :: r (Label "register" handler)
+  }
+
+data Endpoint handler subroutes = Endpoint
+  { root :: handler,
+    subroutes :: subroutes
+  }
+  deriving stock (Show, Eq)
+
+data Handler = Handler {url :: Text}
+
+-- myRoute :: Router () from (Endpoint (Routes (Endpoint ()) Handler) b)
+-- myRoute =
+--   root $ do
+--     users <- fixed "users" () $ fixedFinal @"register" ()
+--     pure $ Routes {..}
+
+-- -- | the root and its children
+-- root :: routes from a -> routes from (Endpoint a b)
+-- root = todo
+
+-- | A fixed sub-route with children
+fixed :: Text -> handler -> Router handler from a -> Router handler from (Endpoint handler a)
+fixed route handler inner = Router $ \from -> \case
+  [final]
+    | route == final ->
+        ( Just handler,
+          Endpoint
+            { root = handler,
+              subroutes = (inner.unRouter from []) & snd
+            }
+        )
+  (this : more)
+    | route == this ->
+        ( (inner.unRouter from more) & fst,
+          Endpoint
+            { root = handler,
+              subroutes = (inner.unRouter from more) & snd
+            }
+        )
+  _ -> (Nothing, Endpoint {root = handler, subroutes = (inner.unRouter from []) & snd})
+
+-- integer ::
+--   forall routeName routes from a.
+--   Router (T2 routeName Integer "more" from) a ->
+--   Router from (Endpoint () a)
+-- integer inner = Router $ \case
+--   (path, []) ->
+--     runFieldParser Field.signedDecimal path
+--   (path, more) ->
+--     inner.unRouter more (runFieldParser Field.signedDecimal path)
+
+-- -- | A leaf route
+-- fixedFinal :: forall route handler from. (KnownSymbol route) => handler -> Router handler from (Label route Handler)
+-- fixedFinal handler = do
+--   let route = symbolText @route
+--   Rounter $ \from -> \case
+--     [final] | route == final -> (Just handler, label @route (Handler from))
+--     _ -> (Nothing, label @route handler)
+
+-- | Get the text of a symbol via TypeApplications
+symbolText :: forall sym. KnownSymbol sym => Text
+symbolText = do
+  symbolVal (Proxy :: Proxy sym)
+    & stringToText
+
+main :: IO ()
+main = runStderrLoggingT @IO $ do
+  withRunInIO @(LoggingT IO) $ \runInIO -> do
+    Warp.run 8080 $ \req respond -> catchServerError respond $ do
+      let respondOk res = Wai.responseLBS Http.ok200 [] (renderMarkup res)
+      let htmlRoot inner =
+            docTypeHtml
+              [hsx|
+            <head>
+              <script src="https://unpkg.com/htmx.org@1.9.2" integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h" crossorigin="anonymous"></script>
+            </head>
+            <body>
+              {inner}
+            </body>
+        |]
+      res <-
+        case req & Wai.pathInfo of
+          [] ->
+            pure $
+              respondOk $
+                htmlRoot
+                  [hsx|
+                      <div id="register_buttons">
+                        <button hx-get="/register" hx-target="body" hx-push-url="/register">Register an account</button>
+                        <button hx-get="/login" hx-target="body">Login</button>
+                      </div>
+              |]
+          ["register"] ->
+            pure $ respondOk $ fullEndpoint req $ \case
+              FullPage -> htmlRoot $ registerForm mempty
+              Snippet -> registerForm mempty
+          ["register", "submit"] -> do
+            FormValidation body <-
+              req
+                & parsePostBody
+                  registerFormValidate
+                & runInIO
+            case body of
+              -- if the parse succeeds, ignore any of the data
+              (_, Just a) -> pure $ respondOk $ htmlRoot [hsx|{a}|]
+              (errs, Nothing) -> pure $ respondOk $ htmlRoot $ registerForm errs
+          other ->
+            pure $ respondOk [hsx|no route here at {other}|]
+      respond $ res
+  where
+    catchServerError respond io =
+      Exc.catch io (\(ex :: ServerError) -> respond $ Wai.responseLBS ex.status [] ex.errBody)
+
+parsePostBody ::
+  (MonadIO m, MonadThrow m, MonadLogger m) =>
+  MultipartParseT backend m b ->
+  Wai.Request ->
+  m b
+parsePostBody parser req =
+  Multipart.parseMultipartOrThrow throwUserErrorTree parser req
+
+-- migrate :: IO (Label "numberOfRowsAffected" Natural)
+-- migrate =
+--   Init.runAppTest $ do
+--     runTransaction $
+--       execute
+--         [sql|
+--         CREATE TABLE IF NOT EXISTS experiments.users (
+--           id SERIAL PRIMARY KEY,
+--           email TEXT NOT NULL,
+--           registration_pending_token TEXT NULL
+--         )
+--         |]
+--         ()
+
+data HsxRequest
+  = Snippet
+  | FullPage
+
+fullEndpoint :: Wai.Request -> (HsxRequest -> t) -> t
+fullEndpoint req act = do
+  let isHxRequest = req & Wai.requestHeaders & List.find (\h -> (h & fst) == "HX-Request") & Maybe.isJust
+  if isHxRequest
+    then act Snippet
+    else act FullPage
+
+data FormField = FormField
+  { label_ :: Html,
+    required :: Bool,
+    id_ :: Text,
+    name :: ByteString,
+    type_ :: Text,
+    placeholder :: Maybe Text
+  }
+
+inputHtml ::
+  FormField ->
+  DList FormValidationResult ->
+  Html
+inputHtml (FormField {..}) validationResults = do
+  let validation =
+        validationResults
+          & toList
+          & mapMaybe
+            ( \v ->
+                if v.formFieldName == name
+                  then
+                    Just
+                      ( T2
+                          (label @"errors" (maybeToList v.hasError))
+                          (label @"originalValue" (Monoid.First (Just v.originalValue)))
+                      )
+                  else Nothing
+            )
+          & mconcat
+  let isFirstError =
+        validationResults
+          & List.find (\res -> Maybe.isJust res.hasError && res.formFieldName == name)
+          & Maybe.isJust
+  [hsx|
+      <label for={id_}>{label_}
+        <input
+          autofocus={isFirstError}
+          onfocus="this.select()"
+          required={required}
+          id={id_}
+          name={name}
+          type={type_}
+          placeholder={placeholder}
+          value={validation.originalValue.getFirst}
+        />
+        <p id="{id_}.validation">{validation.errors & nonEmpty <&> toList <&> map prettyError <&> Text.intercalate "; "}</p>
+      </label>
+  |]
+
+registerForm :: DList FormValidationResult -> Html
+registerForm validationErrors =
+  let fields =
+        mconcat
+          [ inputHtml $
+              FormField
+                { label_ = "Your Email:",
+                  required = True,
+                  id_ = "register_email",
+                  name = "email",
+                  type_ = "email",
+                  placeholder = Just "your@email.com"
+                },
+            inputHtml $
+              FormField
+                { label_ = "New password:",
+                  required = True,
+                  id_ = "register_password",
+                  name = "password",
+                  type_ = "password",
+                  placeholder = Just "hunter2"
+                },
+            inputHtml $
+              FormField
+                { label_ = "Repeated password:",
+                  required = True,
+                  id_ = "register_password_repeated",
+                  name = "password_repeated",
+                  type_ = "password",
+                  placeholder = Just "hunter2"
+                }
+          ]
+   in [hsx|
+  <form hx-post="/register/submit">
+    <fieldset>
+      <legend>Register user</legend>
+      {fields validationErrors}
+      <button id="register_submit_button" name="register">
+        Register
+      </button>
+    </fieldset>
+  </form>
+  |]
+
+registerFormValidate ::
+  Applicative m =>
+  MultipartParseT
+    w
+    m
+    (FormValidation (T2 "email" ByteString "password" ByteString))
+registerFormValidate = do
+  let emailFP = FieldParser $ \b ->
+        if
+            | Bytes.elem (charToWordUnsafe '@') b -> Right b
+            | otherwise -> Left [fmt|This is not an email address: "{b & bytesToTextUtf8Unsafe}"|]
+
+  getCompose @(MultipartParseT _ _) @FormValidation $ do
+    email <- Compose $ Multipart.fieldLabel' @"email" "email" emailFP
+    password <-
+      aEqB
+        "password_repeated"
+        "The two password fields must be the same"
+        (Compose $ Multipart.field' "password" Cat.id)
+        (\field -> Compose $ Multipart.field' field Cat.id)
+    pure $ T2 email (label @"password" password)
+  where
+    aEqB field validateErr fCompare fValidate =
+      Selective.fromMaybeS
+        -- TODO: this check only reached if the field itself is valid. Could we combine those errors?
+        (Compose $ pure $ failFormValidation (T2 (label @"formFieldName" field) (label @"originalValue" "")) validateErr)
+        $ do
+          compare <- fCompare
+          validate <- fValidate field
+          pure $ if compare == validate then Just validate else Nothing
+
+-- | A lifted version of 'Data.Maybe.fromMaybe'.
+fromMaybeS :: Selective f => f a -> f (Maybe a) -> f a
+fromMaybeS ifNothing fma =
+  select
+    ( fma <&> \case
+        Nothing -> Left ()
+        Just a -> Right a
+    )
+    ( do
+        a <- ifNothing
+        pure (\() -> a)
+    )
diff --git a/users/Profpatsch/htmx-experiment/src/ServerErrors.hs b/users/Profpatsch/htmx-experiment/src/ServerErrors.hs
new file mode 100644
index 0000000000..0fca7ab464
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/src/ServerErrors.hs
@@ -0,0 +1,244 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module ServerErrors where
+
+import Control.Exception (Exception)
+import Control.Monad.Logger (MonadLogger, logError, logWarn)
+import Data.ByteString.Lazy qualified as Bytes.Lazy
+import Data.Error.Tree
+import Network.HTTP.Types qualified as Http
+import PossehlAnalyticsPrelude
+
+data ServerError = ServerError
+  { status :: Http.Status,
+    errBody :: Bytes.Lazy.ByteString
+  }
+  deriving stock (Show)
+  deriving anyclass (Exception)
+
+emptyServerError :: Http.Status -> ServerError
+emptyServerError status = ServerError {status, errBody = ""}
+
+-- | Throw a user error.
+--
+-- โ€œUserโ€ here is a client using our API, not a human user.
+-- So we throw a `HTTP 400` error, which means the API was used incorrectly.
+--
+-- We also log the error as a warning, because it probably signifies a programming bug in our client.
+--
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+throwUserError ::
+  (MonadLogger m, MonadThrow m) =>
+  -- | The error to log & throw to the user
+  Error ->
+  m b
+throwUserError err = do
+  -- TODO: should we make this into a macro to keep the line numbers?
+  $logWarn (err & errorContext "There was a โ€œuser holding it wrongโ€ error, check the client code" & prettyError)
+  throwM
+    ServerError
+      { status = Http.badRequest400,
+        errBody = err & prettyError & textToBytesUtf8 & toLazyBytes
+      }
+
+-- | Throw a user error.
+--
+-- โ€œUserโ€ here is a client using our API, not a human user.
+-- So we throw a `HTTP 400` error, which means the API was used incorrectly.
+--
+-- We also log the error as a warning, because it probably signifies a programming bug in our client.
+--
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+throwUserErrorTree ::
+  (MonadLogger m, MonadThrow m) =>
+  -- | The error to log & throw to the user
+  ErrorTree ->
+  m b
+throwUserErrorTree err = do
+  -- TODO: should we make this into a macro to keep the line numbers?
+  $logWarn (err & nestedError "There was a โ€œuser holding it wrongโ€ error, check the client code" & prettyErrorTree)
+  throwM
+    ServerError
+      { status = Http.badRequest400,
+        errBody = err & prettyErrorTree & textToBytesUtf8 & toLazyBytes
+      }
+
+-- | Unwrap the `Either` and if `Left` throw a user error.
+--
+-- Intended to use in a pipeline, e.g.:
+--
+-- @@
+-- doSomething
+--   >>= orUserError "Oh no something did not work"
+--   >>= doSomethingElse
+-- @@
+--
+-- โ€œUserโ€ here is a client using our API, not a human user.
+-- So we throw a `HTTP 400` error, which means the API was used incorrectly.
+--
+-- We also log the error as a warning, because it probably signifies a programming bug in our client.
+--
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+orUserError ::
+  (MonadThrow m, MonadLogger m) =>
+  -- | The message to add as a context to the error being thrown
+  Text ->
+  -- | Result to unwrap and potentially throw
+  Either Error a ->
+  m a
+orUserError outerMsg eErrA =
+  orUserErrorTree outerMsg (first singleError eErrA)
+
+-- | Unwrap the `Either` and if `Left` throw a user error. Will pretty-print the 'ErrorTree'
+--
+-- Intended to use in a pipeline, e.g.:
+--
+-- @@
+-- doSomething
+--   >>= orUserErrorTree "Oh no something did not work"
+--   >>= doSomethingElse
+-- @@
+--
+-- โ€œUserโ€ here is a client using our API, not a human user.
+-- So we throw a `HTTP 400` error, which means the API was used incorrectly.
+--
+-- We also log the error as a warning, because it probably signifies a programming bug in our client.
+--
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+orUserErrorTree ::
+  (MonadThrow m, MonadLogger m) =>
+  -- | The message to add as a context to the 'ErrorTree' being thrown
+  Text ->
+  -- | Result to unwrap and potentially throw
+  Either ErrorTree a ->
+  m a
+orUserErrorTree outerMsg = \case
+  Right a -> pure a
+  Left err -> do
+    -- TODO: this outer message should probably be added as a separate root instead of adding to the root error?
+    let tree = errorTreeContext outerMsg err
+    -- TODO: should we make this into a macro to keep the line numbers?
+    $logWarn (errorTreeContext "There was a โ€œuser holding it wrongโ€ error, check the client code" tree & prettyErrorTree)
+    throwM
+      ServerError
+        { status = Http.badRequest400,
+          errBody = tree & prettyErrorTree & textToBytesUtf8 & toLazyBytes
+        }
+
+-- | Throw an internal error.
+--
+-- โ€œInternalโ€ here means some assertion that we depend on failed,
+-- e.g. some database request returned a wrong result/number of results
+-- or some invariant that we expect to hold failed.
+--
+-- This prints the full error to the log,
+-- and returns a โ€œHTTP 500โ€ error without the message.
+--
+-- If you want to signify a mishandling of the API (e.g. a wrong request), throw a `userError`.
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+throwInternalError ::
+  (MonadLogger m, MonadThrow m) =>
+  -- | The error to log internally
+  Error ->
+  m b
+throwInternalError err = do
+  -- TODO: should we make this into a macro to keep the line numbers?
+  $logError
+    (err & prettyError)
+  throwM $ emptyServerError Http.internalServerError500
+
+-- | Throw an internal error.
+--
+-- โ€œInternalโ€ here means some assertion that we depend on failed,
+-- e.g. some database request returned a wrong result/number of results
+-- or some invariant that we expect to hold failed.
+--
+-- This prints the full error to the log,
+-- and returns a โ€œHTTP 500โ€ error without the message.
+--
+-- If you want to signify a mishandling of the API (e.g. a wrong request), throw a `userError`.
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+throwInternalErrorTree ::
+  (MonadLogger m, MonadThrow m) =>
+  -- | The error to log internally
+  ErrorTree ->
+  m b
+throwInternalErrorTree err = do
+  -- TODO: should we make this into a macro to keep the line numbers?
+  $logError
+    (err & prettyErrorTree)
+  throwM $ emptyServerError   Http.internalServerError500
+
+-- | Unwrap the `Either` and if `Left` throw an internal error.
+--
+-- Intended to use in a pipeline, e.g.:
+--
+-- @@
+-- doSomething
+--   >>= orInternalError "Oh no something did not work"
+--   >>= doSomethingElse
+-- @@
+--
+-- โ€œInternalโ€ here means some assertion that we depend on failed,
+-- e.g. some database request returned a wrong result/number of results
+-- or some invariant that we expect to hold failed.
+--
+-- This prints the full error to the log,
+-- and returns a โ€œHTTP 500โ€ error without the message.
+--
+-- If you want to signify a mishandling of the API (e.g. a wrong request), throw a `userError`.
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+orInternalError ::
+  (MonadThrow m, MonadLogger m) =>
+  -- | The message to add as a context to the error being thrown
+  Text ->
+  -- | Result to unwrap and potentially throw
+  Either Error a ->
+  m a
+orInternalError outerMsg eErrA = orInternalErrorTree outerMsg (first singleError eErrA)
+
+-- | Unwrap the `Either` and if `Left` throw an internal error. Will pretty-print the 'ErrorTree'.
+--
+-- Intended to use in a pipeline, e.g.:
+--
+-- @@
+-- doSomething
+--   >>= orInternalErrorTree "Oh no something did not work"
+--   >>= doSomethingElse
+-- @@
+--
+-- โ€œInternalโ€ here means some assertion that we depend on failed,
+-- e.g. some database request returned a wrong result/number of results
+-- or some invariant that we expect to hold failed.
+--
+-- This prints the full error to the log,
+-- and returns a โ€œHTTP 500โ€ error without the message.
+--
+-- If you want to signify a mishandling of the API (e.g. a wrong request), throw a `userError`.
+-- If you need to display a message to a human user, return a `FrontendResponse`
+-- or a structured type with translation keys (so we can localize the errors).
+orInternalErrorTree ::
+  (MonadThrow m, MonadLogger m) =>
+  -- | The message to add as a context to the 'ErrorTree' being thrown
+  Text ->
+  -- | Result to unwrap and potentially throw
+  Either ErrorTree a ->
+  m a
+orInternalErrorTree outerMsg = \case
+  Right a -> pure a
+  Left err -> do
+    -- TODO: this outer message should probably be added as a separate root instead of adding to the root error?
+    let tree = errorTreeContext outerMsg err
+    -- TODO: should we make this into a macro to keep the line numbers?
+    $logError (tree & prettyErrorTree)
+    throwM $ emptyServerError   Http.internalServerError500
diff --git a/users/Profpatsch/htmx-experiment/src/ValidationParseT.hs b/users/Profpatsch/htmx-experiment/src/ValidationParseT.hs
new file mode 100644
index 0000000000..ffb6c2f395
--- /dev/null
+++ b/users/Profpatsch/htmx-experiment/src/ValidationParseT.hs
@@ -0,0 +1,40 @@
+module ValidationParseT where
+
+import Control.Monad.Logger (MonadLogger)
+import Control.Selective (Selective)
+import Data.Error.Tree
+import Data.Functor.Compose (Compose (..))
+import PossehlAnalyticsPrelude
+import ServerErrors
+
+-- | A simple way to create an Applicative parser that parses from some environment.
+--
+-- Use with DerivingVia. Grep codebase for examples.
+newtype ValidationParseT env m a = ValidationParseT {unValidationParseT :: env -> m (Validation (NonEmpty Error) a)}
+  deriving
+    (Functor, Applicative, Selective)
+    via ( Compose
+            ((->) env)
+            (Compose m (Validation (NonEmpty Error)))
+        )
+
+-- | Helper that runs the given parser and throws a user error if the parsing failed.
+runValidationParseTOrUserError ::
+  forall validationParseT env m a.
+  ( Coercible validationParseT (ValidationParseT env m a),
+    MonadLogger m,
+    MonadThrow m
+  ) =>
+  -- | toplevel error message to throw if the parsing fails
+  Error ->
+  -- | The parser which should be run
+  validationParseT ->
+  -- | input to the parser
+  env ->
+  m a
+{-# INLINE runValidationParseTOrUserError #-}
+runValidationParseTOrUserError contextError parser env =
+  (coerce @_ @(ValidationParseT _ _ _) parser).unValidationParseT env
+    >>= \case
+      Failure errs -> throwUserErrorTree (errorTree contextError errs)
+      Success a -> pure a
diff --git a/users/Profpatsch/httzip/Httzip.hs b/users/Profpatsch/httzip/Httzip.hs
new file mode 100644
index 0000000000..761cd1d2ea
--- /dev/null
+++ b/users/Profpatsch/httzip/Httzip.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Main where
+
+import Conduit ((.|))
+import Data.Binary.Builder qualified as Builder
+import Data.Conduit qualified as Cond
+import Data.Conduit.Combinators qualified as Cond
+import Data.Conduit.Process.Typed qualified as Cond
+import Data.Conduit.Process.Typed qualified as Proc
+import Data.List qualified as List
+import Data.Text qualified as Text
+import Network.HTTP.Types qualified as Http
+import Network.Wai qualified as Wai
+import Network.Wai.Conduit qualified as Wai.Conduit
+import Network.Wai.Handler.Warp qualified as Warp
+import PossehlAnalyticsPrelude
+import System.Directory qualified as Dir
+import System.FilePath ((</>))
+import System.FilePath qualified as File
+import System.Posix qualified as Unix
+
+-- Webserver that returns folders under CWD as .zip archives (recursively)
+main :: IO ()
+main = do
+  currentDirectory <- Dir.getCurrentDirectory >>= Dir.canonicalizePath
+  run currentDirectory
+
+run :: FilePath -> IO ()
+run dir = do
+  currentDirectory <- Dir.canonicalizePath dir
+  putStderrLn $ [fmt|current {show currentDirectory}|]
+  Warp.run 7070 $ \req respond -> do
+    let respondHtml status content = respond $ Wai.responseLBS status [("Content-Type", "text/html")] content
+    case req & Wai.pathInfo of
+      [] -> respond $ Wai.responseLBS Http.status200 [("Content-Type", "text/html")] "any directory will be returned as .zip!"
+      filePath -> do
+        absoluteWantedFilepath <- Dir.canonicalizePath (currentDirectory </> (File.joinPath (filePath <&> textToString)))
+        -- I hope this prevents any shenanigans lol
+        let noCurrentDirPrefix = List.stripPrefix (File.addTrailingPathSeparator currentDirectory) absoluteWantedFilepath
+        if
+            | (any (Text.elem '/') filePath) -> putStderrLn "tried %2F encoding" >> respondHtml Http.status400 "no"
+            | Nothing <- noCurrentDirPrefix -> putStderrLn "tried parent dir with .." >> respondHtml Http.status400 "no^2"
+            | Just wantedFilePath <- noCurrentDirPrefix -> do
+                putStderrLn $ [fmt|wanted {show wantedFilePath}|]
+                ex <- Unix.fileExist wantedFilePath
+                if ex
+                  then do
+                    status <- Unix.getFileStatus wantedFilePath
+                    if status & Unix.isDirectory
+                      then do
+                        zipDir <- zipDirectory wantedFilePath
+                        Proc.withProcessWait zipDir $ \process -> do
+                          let stream =
+                                Proc.getStdout process
+                                  .| Cond.map (\bytes -> Cond.Chunk $ Builder.fromByteString bytes)
+                          -- TODO: how to handle broken zip? Is it just gonna return a 500? But the stream is already starting, so hard!
+                          respond $ Wai.Conduit.responseSource Http.ok200 [("Content-Type", "application/zip")] stream
+                      else respondHtml Http.status404 "not found"
+                  else respondHtml Http.status404 "not found"
+  where
+    zipDirectory toZipDir = do
+      putStderrLn [fmt|running $ zip {show ["--recurse-paths", "-", toZipDir]}|]
+      pure $
+        Proc.proc "zip" ["--recurse-paths", "-", toZipDir]
+          & Proc.setStdout Cond.createSource
diff --git a/users/Profpatsch/httzip/default.nix b/users/Profpatsch/httzip/default.nix
new file mode 100644
index 0000000000..35d8a69d56
--- /dev/null
+++ b/users/Profpatsch/httzip/default.nix
@@ -0,0 +1,40 @@
+{ depot, pkgs, lib, ... }:
+
+let
+
+  httzip = pkgs.haskellPackages.mkDerivation {
+    pname = "httzip";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./httzip.cabal
+      ./Httzip.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.warp
+      pkgs.haskellPackages.wai
+      pkgs.haskellPackages.wai-conduit
+      pkgs.haskellPackages.conduit-extra
+      pkgs.haskellPackages.conduit
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+  bins = depot.nix.getBins httzip [ "httzip" ];
+
+in
+depot.nix.writeExecline "httzip-wrapped" { } [
+  "importas"
+  "-i"
+  "PATH"
+  "PATH"
+  "export"
+  "PATH"
+  "${pkgs.zip}/bin:$${PATH}"
+  bins.httzip
+]
diff --git a/users/Profpatsch/httzip/httzip.cabal b/users/Profpatsch/httzip/httzip.cabal
new file mode 100644
index 0000000000..c463a6a5fe
--- /dev/null
+++ b/users/Profpatsch/httzip/httzip.cabal
@@ -0,0 +1,73 @@
+cabal-version:      3.0
+name:               httzip
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+executable httzip
+    import: common-options
+
+    main-is:          Httzip.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        bytestring,
+        text,
+        warp,
+        wai,
+        http-types,
+        directory,
+        filepath,
+        unix,
+        wai-conduit,
+        conduit,
+        conduit-extra,
+        binary
diff --git a/users/Profpatsch/ical-smolify/IcalSmolify.hs b/users/Profpatsch/ical-smolify/IcalSmolify.hs
new file mode 100644
index 0000000000..77264d1693
--- /dev/null
+++ b/users/Profpatsch/ical-smolify/IcalSmolify.hs
@@ -0,0 +1,124 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Main where
+
+import qualified Data.ByteString.Lazy as Bytes.Lazy
+import qualified Data.CaseInsensitive as CaseInsensitive
+import qualified Data.Default as Default
+import qualified Data.Map.Strict as Map
+import qualified Data.Set as Set
+import ExecHelpers (dieUserError, CurrentProgramName)
+import MyPrelude
+import qualified System.Environment as Env
+import Text.ICalendar
+import Prelude hiding (log)
+
+main :: IO ()
+main = do
+  Env.getArgs >>= \case
+    [] -> dieUserError progName "First argument must be the ics file name"
+    (file : _) ->
+      do
+        parse file
+        >>= traverse_
+          ( \vcal ->
+              vcal
+                & stripSingleTimezone
+                & minify
+                & printICalendar Default.def
+                & Bytes.Lazy.putStr
+          )
+
+progName :: CurrentProgramName
+progName = "ical-smolify"
+
+log :: Error -> IO ()
+log err = do
+  putStderrLn (errorContext "ical-smolify" err & prettyError)
+
+parse :: FilePath -> IO [VCalendar]
+parse file = do
+  parseICalendarFile Default.def file >>= \case
+    Left err -> do
+      dieUserError progName [fmt|Cannot parse ical file: {err}|]
+    Right (cals, warnings) -> do
+      for_ warnings (\warn -> log [fmt|Warning: {warn}|])
+      pure cals
+
+-- | Converts a single timezone definition to the corresponding X-WR-Timezone field.
+stripSingleTimezone :: VCalendar -> VCalendar
+stripSingleTimezone vcal =
+  case vcal & vcTimeZones & Map.toList of
+    [] -> vcal
+    [(_, tz)] -> do
+      let xtz =
+            OtherProperty
+              { otherName = CaseInsensitive.mk "X-WR-TIMEZONE",
+                otherValue = tz & vtzId & tzidValue & textToBytesUtf8Lazy,
+                otherParams = OtherParams Set.empty
+              }
+      vcal
+        { vcOther =
+            vcal & vcOther
+              -- remove any existing x-wr-timezone fields
+              & Set.filter (\prop -> (prop & otherName) /= (xtz & otherName))
+              & Set.insert xtz,
+          vcTimeZones = Map.empty
+        }
+    _more -> vcal
+
+-- | Minify the vcalendar event by throwing away everything thatโ€™s not an event.
+minify :: VCalendar -> VCalendar
+minify vcal =
+  vcal
+    { vcProdId = ProdId "" (OtherParams Set.empty),
+      -- , vcVersion    :: ICalVersion
+      -- , vcScale      :: Scale
+      -- , vcMethod     :: Maybe Method
+      -- , vcOther      :: โ€ฆ
+      -- , vcTimeZones  :: Map Text VTimeZone
+      vcEvents = Map.map minifyEvent (vcal & vcEvents),
+      vcTodos = Map.empty,
+      vcJournals = Map.empty,
+      vcFreeBusys = Map.empty,
+      vcOtherComps = Set.empty
+    }
+
+minifyEvent :: VEvent -> VEvent
+minifyEvent vev =
+  vev
+--  { veDTStamp       :: DTStamp
+--   , veUID           :: UID
+--   , veClass         :: Class -- ^ 'def' = 'Public'
+--   , veDTStart       :: Maybe DTStart
+--   , veCreated       :: Maybe Created
+--   , veDescription   :: Maybe Description
+--   , veGeo           :: Maybe Geo
+--   , veLastMod       :: Maybe LastModified
+--   , veLocation      :: Maybe Location
+--   , veOrganizer     :: Maybe Organizer
+--   , vePriority      :: Priority -- ^ 'def' = 0
+--   , veSeq           :: Sequence -- ^ 'def' = 0
+--   , veStatus        :: Maybe EventStatus
+--   , veSummary       :: Maybe Summary
+--   , veTransp        :: TimeTransparency -- ^ 'def' = 'Opaque'
+--   , veUrl           :: Maybe URL
+--   , veRecurId       :: Maybe RecurrenceId
+--   , veRRule         :: Set RRule
+--   , veDTEndDuration :: Maybe (Either DTEnd DurationProp)
+--   , veAttach        :: Set Attachment
+--   , veAttendee      :: Set Attendee
+--   , veCategories    :: Set Categories
+--   , veComment       :: Set Comment
+--   , veContact       :: Set Contact
+--   , veExDate        :: Set ExDate
+--   , veRStatus       :: Set RequestStatus
+--   , veRelated       :: Set RelatedTo
+--   , veResources     :: Set Resources
+--   , veRDate         :: Set RDate
+--   , veAlarms        :: Set VAlarm
+--   , veOther         :: Set OtherProperty
+--   }
diff --git a/users/Profpatsch/ical-smolify/README.md b/users/Profpatsch/ical-smolify/README.md
new file mode 100644
index 0000000000..86c166d3c1
--- /dev/null
+++ b/users/Profpatsch/ical-smolify/README.md
@@ -0,0 +1,5 @@
+# ical-smolify
+
+Ensmallen an `ical` by stripping out redundant information like timezone definitions.
+
+The idea here was that after running through this preprocessor, it fits into a QR code (~2000bits) that can be scanned with your phone (for automatically adding to mobile calendar).
diff --git a/users/Profpatsch/ical-smolify/default.nix b/users/Profpatsch/ical-smolify/default.nix
new file mode 100644
index 0000000000..bf766db0e9
--- /dev/null
+++ b/users/Profpatsch/ical-smolify/default.nix
@@ -0,0 +1,23 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  ical-smolify = pkgs.writers.writeHaskell "ical-smolify"
+    {
+      libraries = [
+        pkgs.haskellPackages.iCalendar
+        depot.users.Profpatsch.my-prelude
+        depot.users.Profpatsch.execline.exec-helpers-hs
+
+      ];
+      ghcArgs = [ "-threaded" ];
+    } ./IcalSmolify.hs;
+
+in
+
+ical-smolify.overrideAttrs (old: {
+  meta = lib.recursiveUpdate old.meta or { } {
+    # Dependency iCalendar no longer builds in nixpkgs due to a lack of maintenance upstream
+    # https://github.com/nixos/nixpkgs/commit/13d10cc6e302e7d5800c6a08c1728b14c3801e26
+    ci.skip = true;
+  };
+})
diff --git a/users/Profpatsch/ical-smolify/ical-smolify.cabal b/users/Profpatsch/ical-smolify/ical-smolify.cabal
new file mode 100644
index 0000000000..d7a46c581d
--- /dev/null
+++ b/users/Profpatsch/ical-smolify/ical-smolify.cabal
@@ -0,0 +1,18 @@
+cabal-version:      3.0
+name:               ical-smolify
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+executable ical-smolify
+    main-is: IcalSmolify.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        my-prelude,
+        exec-helpers
+        data-default
+        case-insensitive
+        iCalendar
+
+    default-language: Haskell2010
diff --git a/users/Profpatsch/imap-idle.nix b/users/Profpatsch/imap-idle.nix
index 3ad5375d89..84af5d0e54 100644
--- a/users/Profpatsch/imap-idle.nix
+++ b/users/Profpatsch/imap-idle.nix
@@ -1,14 +1,17 @@
 { depot, pkgs, lib, ... }:
 
 let
-  imap-idle = depot.nix.writers.rustSimple {
-    name = "imap-idle";
-    dependencies = [
-      depot.users.Profpatsch.arglib.netencode.rust
-      depot.third_party.rust-crates.imap
-      depot.third_party.rust-crates.epoll
-      depot.users.Profpatsch.execline.exec-helpers
-    ];
-  } (builtins.readFile ./imap-idle.rs);
+  imap-idle = depot.nix.writers.rustSimple
+    {
+      name = "imap-idle";
+      dependencies = [
+        depot.users.Profpatsch.arglib.netencode.rust
+        depot.third_party.rust-crates.imap
+        depot.third_party.rust-crates.epoll
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    }
+    (builtins.readFile ./imap-idle.rs);
 
-in imap-idle
+in
+imap-idle
diff --git a/users/Profpatsch/imap-idle.rs b/users/Profpatsch/imap-idle.rs
index 9dce736d0d..937847b879 100644
--- a/users/Profpatsch/imap-idle.rs
+++ b/users/Profpatsch/imap-idle.rs
@@ -1,16 +1,16 @@
 extern crate exec_helpers;
 // extern crate arglib_netencode;
 // extern crate netencode;
-extern crate imap;
 extern crate epoll;
+extern crate imap;
 
 // use netencode::dec;
+use imap::extensions::idle::SetReadTimeout;
 use std::convert::TryFrom;
-use std::io::{Read, Write};
 use std::fs::File;
-use std::os::unix::io::{FromRawFd, AsRawFd, RawFd};
+use std::io::{Read, Write};
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
 use std::time::Duration;
-use imap::extensions::idle::SetReadTimeout;
 
 /// Implements an UCSPI client that wraps fd 6 & 7
 /// and implements Write and Read with a timeout.
@@ -33,7 +33,7 @@ impl UcspiClient {
                 read: File::from_raw_fd(6),
                 read_epoll_fd,
                 read_timeout: None,
-                write: File::from_raw_fd(7)
+                write: File::from_raw_fd(7),
             })
         }
     }
@@ -54,21 +54,23 @@ impl SetReadTimeout for UcspiClient {
 impl Read for UcspiClient {
     // TODO: test the epoll code with a short timeout
     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
-        const NO_DATA : u64 = 0;
+        const NO_DATA: u64 = 0;
         // in order to implement the read_timeout,
         // we use epoll to wait for either data or time out
         epoll::ctl(
             self.read_epoll_fd,
             epoll::ControlOptions::EPOLL_CTL_ADD,
             self.read.as_raw_fd(),
-            epoll::Event::new(epoll::Events::EPOLLIN, NO_DATA)
+            epoll::Event::new(epoll::Events::EPOLLIN, NO_DATA),
         )?;
         let UNUSED = epoll::Event::new(epoll::Events::EPOLLIN, NO_DATA);
         let wait = epoll::wait(
             self.read_epoll_fd,
             match self.read_timeout {
-                Some(duration) => i32::try_from(duration.as_millis()).expect("duration too big for epoll"),
-                None => -1 // infinite
+                Some(duration) => {
+                    i32::try_from(duration.as_millis()).expect("duration too big for epoll")
+                }
+                None => -1, // infinite
             },
             // event that was generated; but we donโ€™t care
             &mut vec![UNUSED; 1][..],
@@ -79,11 +81,14 @@ impl Read for UcspiClient {
             self.read_epoll_fd,
             epoll::ControlOptions::EPOLL_CTL_DEL,
             self.read.as_raw_fd(),
-            UNUSED
+            UNUSED,
         )?;
         match wait {
             // timeout happened (0 events)
-            Ok(0) => Err(std::io::Error::new(std::io::ErrorKind::TimedOut, "ucspi read timeout")),
+            Ok(0) => Err(std::io::Error::new(
+                std::io::ErrorKind::TimedOut,
+                "ucspi read timeout",
+            )),
             // its ready for reading, we can read
             Ok(_) => self.read.read(buf),
             // error
@@ -110,18 +115,21 @@ fn main() {
     let username = std::env::var("IMAP_USERNAME").expect("username");
     let password = std::env::var("IMAP_PASSWORD").expect("password");
 
-    let net = unsafe {
-        UcspiClient::new_from_6_and_7().expect("no ucspi client for you")
-    };
+    let net = unsafe { UcspiClient::new_from_6_and_7().expect("no ucspi client for you") };
     let client = imap::Client::new(net);
-    let mut session = client.login(username, password).map_err(|(err, _)| err).expect("unable to login");
+    let mut session = client
+        .login(username, password)
+        .map_err(|(err, _)| err)
+        .expect("unable to login");
     eprintln!("{:#?}", session);
     let list = session.list(None, Some("*"));
     eprintln!("{:#?}", list);
     let mailbox = session.examine("INBOX");
     eprintln!("{:#?}", mailbox);
     fn now() -> String {
-        String::from_utf8_lossy(&std::process::Command::new("date").output().unwrap().stdout).trim_right().to_string()
+        String::from_utf8_lossy(&std::process::Command::new("date").output().unwrap().stdout)
+            .trim_right()
+            .to_string()
     }
     loop {
         eprintln!("{}: idling on INBOX", now());
diff --git a/users/Profpatsch/importDhall.nix b/users/Profpatsch/importDhall.nix
new file mode 100644
index 0000000000..1947ad1ce1
--- /dev/null
+++ b/users/Profpatsch/importDhall.nix
@@ -0,0 +1,93 @@
+{ pkgs, depot, lib, ... }:
+let
+
+  # import the dhall file as nix expression via dhall-nix.
+  # Converts the normalized dhall expression to a nix file,
+  # puts it in the store and imports it.
+  # Types are erased, functions are converted to nix functions,
+  # unions values are nix functions that take a record of match
+  # functions for their alternatives.
+  # TODO: document better
+  importDhall =
+    {
+      # Root path of the dhall file tree to import (will be filtered by files)
+      root
+    , # A list of files which should be taken from `root` (relative paths).
+      # This is for minimizing the amount of things that have to be copied to the store.
+      # TODO: can you have directory prefixes?
+      files
+    , # The path of the dhall file which should be evaluated, relative to `root`, has to be in `files`
+      main
+    , # List of dependencies (TODO: what is a dependency?)
+      deps
+    , # dhall type of `main`, or `null` if anything should be possible.
+      type ? null
+    }:
+    let
+      absRoot = path: toString root + "/" + path;
+      src =
+        depot.users.Profpatsch.exactSource
+          root
+          # exactSource wants nix paths, but I think relative paths
+          # as strings are more intuitive.
+          ([ (absRoot main) ] ++ (map absRoot files));
+
+      cache = ".cache";
+      cacheDhall = "${cache}/dhall";
+
+      hadTypeAnnot = type != null;
+      typeAnnot = lib.optionalString hadTypeAnnot ": ${type}";
+
+      convert = pkgs.runCommandLocal "dhall-to-nix" { inherit deps; } ''
+        mkdir -p ${cacheDhall}
+        for dep in $deps; do
+          ${pkgs.xorg.lndir}/bin/lndir -silent $dep/${cacheDhall} ${cacheDhall}
+        done
+
+        export XDG_CACHE_HOME=$(pwd)/${cache}
+        # go into the source directory, so that the type can import files.
+        # TODO: This is a bit of a hack hrm.
+        cd "${src}"
+        printf 'Generating dhall nix code. Run
+        %s --file %s
+        to reproduce
+        ' \
+          ${pkgs.dhall}/bin/dhall \
+          ${absRoot main}
+        ${if hadTypeAnnot then ''
+            printf '%s' ${lib.escapeShellArg "${src}/${main} ${typeAnnot}"} \
+              | ${pkgs.dhall-nix}/bin/dhall-to-nix \
+              > $out
+          ''
+          else ''
+            printf 'No type annotation given, the dhall expression type was:\n'
+            ${pkgs.dhall}/bin/dhall type --file "${src}/${main}"
+            printf '%s' ${lib.escapeShellArg "${src}/${main}"} \
+              | ${pkgs.dhall-nix}/bin/dhall-to-nix \
+              > $out
+          ''}
+
+      '';
+    in
+    import convert;
+
+
+  # read dhall file in as JSON, then import as nix expression.
+  # The dhall file must not try to import from non-local URLs!
+  readDhallFileAsJson = dhallType: file:
+    let
+      convert = pkgs.runCommandLocal "dhall-to-json" { } ''
+        printf '%s' ${lib.escapeShellArg "${file} : ${dhallType}"} \
+          | ${pkgs.dhall-json}/bin/dhall-to-json \
+          > $out
+      '';
+    in
+    builtins.fromJSON (builtins.readFile convert);
+
+in
+{
+  inherit
+    importDhall
+    readDhallFileAsJson
+    ;
+}
diff --git a/users/Profpatsch/ini/default.nix b/users/Profpatsch/ini/default.nix
new file mode 100644
index 0000000000..e1a7a1a7b6
--- /dev/null
+++ b/users/Profpatsch/ini/default.nix
@@ -0,0 +1,6 @@
+{ depot, ... }:
+{
+  externs = {
+    renderIni = depot.users.Profpatsch.toINI { };
+  };
+}
diff --git a/users/Profpatsch/ini/ini.dhall b/users/Profpatsch/ini/ini.dhall
new file mode 100644
index 0000000000..f2efbc0af4
--- /dev/null
+++ b/users/Profpatsch/ini/ini.dhall
@@ -0,0 +1,36 @@
+let lib = ../dhall/lib.dhall
+
+let NameVal = ฮป(T : Type) โ†’ { name : Text, value : T }
+
+let ValueList = ฮป(T : Type) โ†’ List (NameVal T)
+
+let Section = ValueList Text
+
+let Sections = ValueList Section
+
+let Ini = { globalSection : Section, sections : Sections }
+
+let
+    -- Takes to INI files and merges their global sections and their section lists,
+    -- without duplicating by section name.
+    appendInis =
+      ฮป(inis : List Ini) โ†’
+          { globalSection =
+              lib.List/concat
+                (NameVal Text)
+                (lib.List/map Ini Section (ฮป(i : Ini) โ†’ i.globalSection) inis)
+          , sections =
+              lib.List/concat
+                (NameVal Section)
+                (lib.List/map Ini Sections (ฮป(i : Ini) โ†’ i.sections) inis)
+          }
+        : Ini
+
+let
+    -- Signatures of functions that are input via FFI.
+    Externs =
+      { -- given a dsl of functions to create an Ini, render the ini file
+        renderIni : Ini โ†’ Text
+      }
+
+in  { NameVal, ValueList, Section, Sections, Ini, appendInis, Externs }
diff --git a/users/Profpatsch/jaeger.nix b/users/Profpatsch/jaeger.nix
new file mode 100644
index 0000000000..374e40df1a
--- /dev/null
+++ b/users/Profpatsch/jaeger.nix
@@ -0,0 +1,46 @@
+{ depot, pkgs, ... }:
+let
+  drv =
+    pkgs.stdenv.mkDerivation {
+      pname = "jaeger";
+      version = "1.49.0";
+      src = pkgs.fetchurl {
+        url = "https://github.com/jaegertracing/jaeger/releases/download/v1.49.0/jaeger-1.49.0-linux-amd64.tar.gz";
+        hash = "sha256-QhxISDlk/t431EesgVkHWTe7yiw2B+yyfq//GLP0As4=";
+      };
+      phases = [ "unpackPhase" "installPhase" "fixupPhase" ];
+      installPhase = ''
+        mkdir -p $out/bin
+        install ./jaeger-all-in-one $out/bin
+      '';
+    };
+  image =
+    pkgs.dockerTools.buildImage {
+      name = "jaeger";
+      tag = "1.49.0";
+      copyToRoot = drv;
+      config = {
+        Cmd = [ "/bin/jaeger-all-in-one" ];
+      };
+
+    };
+
+  runner =
+    depot.nix.writeExecline "jaeger-docker-run" { } [
+      "if"
+      [ "docker" "load" "-i" image ]
+      "docker"
+      "run"
+      "--rm"
+      "--name"
+      "jaeger"
+      # Web UI
+      "-p"
+      "16686:16686"
+      # Opentelemetry
+      "-p"
+      "4318:4318"
+      "jaeger:1.49.0"
+    ];
+in
+runner
diff --git a/users/Profpatsch/jbovlaste-sqlite/JbovlasteSqlite.hs b/users/Profpatsch/jbovlaste-sqlite/JbovlasteSqlite.hs
new file mode 100644
index 0000000000..8dae9cd026
--- /dev/null
+++ b/users/Profpatsch/jbovlaste-sqlite/JbovlasteSqlite.hs
@@ -0,0 +1,389 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Main where
+
+import Conduit ((.|))
+import Conduit qualified as Cond
+import Control.Category qualified as Cat
+import Control.Foldl qualified as Fold
+import Data.ByteString.Internal qualified as Bytes
+import Data.Error.Tree
+import Data.Int (Int64)
+import Data.List qualified as List
+import Data.Map.Strict qualified as Map
+import Data.Maybe (catMaybes)
+import Data.Text qualified as Text
+import Data.Text.IO qualified as Text
+import Database.SQLite.Simple qualified as Sqlite
+import Database.SQLite.Simple.FromField qualified as Sqlite
+import Database.SQLite.Simple.QQ qualified as Sqlite
+import FieldParser qualified as Field
+import Label
+import Parse
+import PossehlAnalyticsPrelude
+import Text.XML (def)
+import Text.XML qualified as Xml
+import Prelude hiding (init, maybe)
+
+main :: IO ()
+main = do
+  f <- file
+  f.documentRoot
+    & filterDown
+    & toTree
+    & prettyErrorTree
+    & Text.putStrLn
+
+test :: IO ()
+test = do
+  withEnv $ \env -> do
+    migrate env
+    f <- file
+    parseJbovlasteXml f
+      & \case
+        Left errs -> Text.putStrLn $ prettyErrorTree errs
+        Right valsi -> insertValsi env valsi
+
+filterDown :: Xml.Element -> Xml.Element
+filterDown el =
+  el
+    & filterElementsRec noUsers
+    & downTo (T2 (label @"maxdepth" 5) (label @"maxlistitems" 30))
+
+data Valsi = Valsi
+  { word :: Text,
+    definition :: Text,
+    definitionId :: Natural,
+    typ :: Text,
+    selmaho :: Maybe Text,
+    notes :: Maybe Text,
+    glosswords :: [T2 "word" Text "sense" (Maybe Text)],
+    keywords :: [T3 "word" Text "place" Natural "sense" (Maybe Text)]
+  }
+  deriving stock (Show)
+
+insertValsi :: Env -> [Valsi] -> IO ()
+insertValsi env vs = do
+  Sqlite.withTransaction env.envData $
+    do
+      valsiIds <-
+        Cond.yieldMany vs
+          .| Cond.mapMC
+            ( \v ->
+                Sqlite.queryNamed
+                  @(Sqlite.Only Int64)
+                  env.envData
+                  [Sqlite.sql|
+                     INSERT INTO valsi
+                       (word , definition , type , selmaho , notes )
+                       VALUES
+                       (:word, :definition, :type, :selmaho, :notes)
+                       RETURNING (id)
+                   |]
+                  [ ":word" Sqlite.:= v.word,
+                    ":definition" Sqlite.:= v.definition,
+                    ":type" Sqlite.:= v.typ,
+                    ":selmaho" Sqlite.:= v.selmaho,
+                    ":notes" Sqlite.:= v.notes
+                  ]
+                  >>= \case
+                    [one] -> pure one
+                    _ -> error "more or less than one result"
+            )
+          .| Cond.sinkList
+          & Cond.runConduit
+      for_ (zip valsiIds vs) $ \(Sqlite.Only vId, v) -> do
+        for_ v.glosswords $ \g -> do
+          Sqlite.executeNamed
+            env.envData
+            [Sqlite.sql|
+                      INSERT INTO glosswords
+                        (valsi_id , word , sense )
+                        VALUES
+                        (:valsi_id, :word, :sense)
+                    |]
+            [ ":valsi_id" Sqlite.:= vId,
+              ":word" Sqlite.:= g.word,
+              ":sense" Sqlite.:= g.sense
+            ]
+      for_ (zip valsiIds vs) $ \(Sqlite.Only vId, v) -> do
+        for_ v.keywords $ \g -> do
+          Sqlite.executeNamed
+            env.envData
+            [Sqlite.sql|
+                      INSERT INTO keywords
+                        (valsi_id , word , place , sense )
+                        VALUES
+                        (:valsi_id, :word, :place, :sense)
+                    |]
+            [ ":valsi_id" Sqlite.:= vId,
+              ":word" Sqlite.:= g.word,
+              ":place" Sqlite.:= (g.place & fromIntegral @Natural @Int),
+              ":sense" Sqlite.:= g.sense
+            ]
+
+migrate :: (HasField "envData" p Sqlite.Connection) => p -> IO ()
+migrate env = do
+  let x q = Sqlite.execute env.envData q ()
+  x
+    [Sqlite.sql|
+      CREATE TABLE IF NOT EXISTS valsi (
+        id integer PRIMARY KEY,
+        word text NOT NULL,
+        definition text NOT NULL,
+        type text NOT NULL,
+        selmaho text NULL,
+        notes text NULL
+      )
+     |]
+  x
+    [Sqlite.sql|
+      CREATE TABLE IF NOT EXISTS glosswords (
+        id integer PRIMARY KEY,
+        valsi_id integer NOT NULL,
+        word text NOT NULL,
+        sense text NULL,
+        FOREIGN KEY(valsi_id) REFERENCES valsi(id)
+      )
+    |]
+  x
+    [Sqlite.sql|
+      CREATE TABLE IF NOT EXISTS keywords (
+        id integer PRIMARY KEY,
+        valsi_id integer NOT NULL,
+        word text NOT NULL,
+        place integer NOT NULL,
+        sense text NULL,
+        FOREIGN KEY(valsi_id) REFERENCES valsi(id)
+      )
+    |]
+
+data Env = Env
+  { envData :: Sqlite.Connection
+  }
+
+withEnv :: (Env -> IO a) -> IO a
+withEnv inner = do
+  withSqlite "./jbovlaste.sqlite" $ \envData -> inner Env {..}
+
+withSqlite :: String -> (Sqlite.Connection -> IO a) -> IO a
+withSqlite fileName inner = Sqlite.withConnection fileName $ \conn -> do
+  -- Sqlite.setTrace conn (Just (\msg -> Text.hPutStrLn IO.stderr [fmt|{fileName}: {msg}|]))
+  Sqlite.execute conn [Sqlite.sql|PRAGMA foreign_keys = ON|] ()
+  inner conn
+
+parseJbovlasteXml :: (HasField "documentRoot" r Xml.Element) => r -> Either ErrorTree [Valsi]
+parseJbovlasteXml xml =
+  xml.documentRoot
+    & runParse
+      "cannot parse jbovlaste.xml"
+      parser
+  where
+    parser =
+      (element "dictionary" <&> (.elementNodes) <&> mapMaybe nodeElementMay)
+        >>> ( find
+                ( element "direction"
+                    >>> do
+                      (attribute "from" >>> exactly showToText "lojban")
+                      *> (attribute "to" >>> exactly showToText "English")
+                      *> Cat.id
+                )
+                <&> (\x -> x.elementNodes <&> nodeElementMay)
+            )
+        >>> (multiple (maybe valsi) <&> catMaybes)
+    valsi =
+      element "valsi"
+        >>> do
+          let subNodes =
+                ( Cat.id
+                    <&> (.elementNodes)
+                    <&> mapMaybe nodeElementMay
+                )
+
+          let subElementContent elName =
+                subNodes
+                  >>> ( (find (element elName))
+                          <&> (.elementNodes)
+                      )
+                  >>> exactlyOne
+                  >>> content
+          let optionalSubElementContent elName =
+                subNodes
+                  >>> ((findAll (element elName) >>> zeroOrOne))
+                  >>> (maybe (lmap (.elementNodes) exactlyOne >>> content))
+
+          word <- attribute "word"
+          typ <- attribute "type"
+          selmaho <- optionalSubElementContent "selmaho"
+          definition <- subElementContent "definition"
+          definitionId <- subElementContent "definitionid" >>> fieldParser Field.decimalNatural
+          notes <- optionalSubElementContent "notes"
+          glosswords <-
+            (subNodes >>> findAll (element "glossword"))
+              >>> ( multiple $ do
+                      word' <- label @"word" <$> (attribute "word")
+                      sense <- label @"sense" <$> (attributeMay "sense")
+                      pure $ T2 word' sense
+                  )
+          keywords <-
+            (subNodes >>> findAll (element "keyword"))
+              >>> ( multiple $ do
+                      word' <- label @"word" <$> (attribute "word")
+                      place <- label @"place" <$> (attribute "place" >>> fieldParser Field.decimalNatural)
+                      sense <- label @"sense" <$> (attributeMay "sense")
+                      pure $ T3 word' place sense
+                  )
+
+          pure $ Valsi {..}
+
+file :: IO Xml.Document
+file = Xml.readFile def "./jbovlaste-en.xml"
+
+-- | Filter XML elements recursively based on the given predicate
+filterElementsRec :: (Xml.Element -> Bool) -> Xml.Element -> Xml.Element
+filterElementsRec f el =
+  el
+    { Xml.elementNodes =
+        mapMaybe
+          ( \case
+              Xml.NodeElement el' ->
+                if f el'
+                  then Just $ Xml.NodeElement $ filterElementsRec f el'
+                  else Nothing
+              other -> Just other
+          )
+          el.elementNodes
+    }
+
+-- | no <user> allowed
+noUsers :: Xml.Element -> Bool
+noUsers el = el.elementName.nameLocalName /= "user"
+
+downTo :: (T2 "maxdepth" Int "maxlistitems" Int) -> Xml.Element -> Xml.Element
+downTo n el =
+  if n.maxdepth > 0
+    then
+      el
+        { Xml.elementNodes =
+            ( do
+                let eleven = take (n.maxlistitems + 1) $ map down el.elementNodes
+                if List.length eleven == (n.maxlistitems + 1)
+                  then eleven <> [Xml.NodeComment "snip!"]
+                  else eleven
+            )
+        }
+    else el {Xml.elementNodes = [Xml.NodeComment "snip!"]}
+  where
+    down =
+      \case
+        Xml.NodeElement el' ->
+          Xml.NodeElement $
+            downTo
+              ( T2
+                  (label @"maxdepth" $ n.maxdepth - 1)
+                  (label @"maxlistitems" n.maxlistitems)
+              )
+              el'
+        more -> more
+
+toTree :: Xml.Element -> ErrorTree
+toTree el = do
+  case el.elementNodes & filter (not . isEmptyContent) & nonEmpty of
+    Nothing -> singleError (newError (prettyXmlElement el))
+    Just (n :| []) | not $ isElementNode n -> singleError $ errorContext (prettyXmlElement el) (nodeErrorNoElement n)
+    Just nodes -> nestedMultiError (newError (prettyXmlElement el)) (nodes <&> node)
+  where
+    isEmptyContent = \case
+      Xml.NodeContent c -> c & Text.all Bytes.isSpaceChar8
+      _ -> False
+    isElementNode = \case
+      Xml.NodeElement _ -> True
+      _ -> False
+
+    node :: Xml.Node -> ErrorTree
+    node = \case
+      Xml.NodeElement el' -> toTree el'
+      other -> singleError $ nodeErrorNoElement other
+
+    nodeErrorNoElement :: Xml.Node -> Error
+    nodeErrorNoElement = \case
+      Xml.NodeInstruction i -> [fmt|Instruction: {i & show}|]
+      Xml.NodeContent c -> [fmt|"{c & Text.replace "\"" "\\\""}"|]
+      Xml.NodeComment c -> [fmt|<!-- {c} -->|]
+      Xml.NodeElement _ -> error "NodeElement not allowed here"
+
+prettyXmlName :: Xml.Name -> Text
+prettyXmlName n = [fmt|{n.namePrefix & fromMaybe ""}{n.nameLocalName}|]
+
+prettyXmlElement :: Xml.Element -> Text
+prettyXmlElement el =
+  if not $ null el.elementAttributes
+    then [fmt|<{prettyXmlName el.elementName}: {attrs el.elementAttributes}>|]
+    else [fmt|<{prettyXmlName el.elementName}>|]
+  where
+    attrs :: Map Xml.Name Text -> Text
+    attrs a = a & Map.toList <&> (\(k, v) -> [fmt|{prettyXmlName k}={v}|]) & Text.intercalate ", " & \s -> [fmt|({s})|]
+
+nodeElementMay :: Xml.Node -> Maybe Xml.Element
+nodeElementMay = \case
+  Xml.NodeElement el -> Just el
+  _ -> Nothing
+
+element :: Text -> Parse Xml.Element Xml.Element
+element name = Parse $ \(ctx, el) ->
+  if el.elementName.nameLocalName == name
+    then Success (ctx & addContext (prettyXmlName el.elementName), el)
+    else Failure $ singleton [fmt|Expected element named <{name}> but got {el & prettyXmlElement} at {showContext ctx}|]
+
+content :: Parse Xml.Node Text
+content = Parse $ \(ctx, node) -> case node of
+  Xml.NodeContent t -> Success (ctx, t)
+  -- TODO: give an example of the node content?
+  n -> Failure $ singleton [fmt|Expected a content node, but got a {n & nodeType} node, at {showContext ctx}|]
+    where
+      nodeType = \case
+        Xml.NodeContent _ -> "content" :: Text
+        Xml.NodeComment _ -> "comment"
+        Xml.NodeInstruction _ -> "instruction"
+        Xml.NodeElement _ -> "element"
+
+attribute :: Text -> Parse Xml.Element Text
+attribute name = Parse $ \(ctx, el) ->
+  case el.elementAttributes & Map.mapKeys (.nameLocalName) & Map.lookup name of
+    Just a -> Success (ctx & addContext [fmt|{{attr:{name}}}|], a)
+    Nothing -> Failure $ singleton [fmt|Attribute "{name}" missing at {showContext ctx}|]
+
+attributeMay :: Text -> Parse Xml.Element (Maybe Text)
+attributeMay name = Parse $ \(ctx, el) ->
+  case el.elementAttributes & Map.mapKeys (.nameLocalName) & Map.lookup name of
+    Just a -> Success (ctx & addContext [fmt|{{attr:{name}}}|], Just a)
+    Nothing -> Success (ctx, Nothing)
+
+instance
+  ( Sqlite.FromField t1,
+    Sqlite.FromField t2,
+    Sqlite.FromField t3
+  ) =>
+  Sqlite.FromRow (T3 l1 t1 l2 t2 l3 t3)
+  where
+  fromRow = do
+    T3
+      <$> (label @l1 <$> Sqlite.field)
+      <*> (label @l2 <$> Sqlite.field)
+      <*> (label @l3 <$> Sqlite.field)
+
+foldRows ::
+  forall row params b.
+  (Sqlite.FromRow row, Sqlite.ToRow params) =>
+  Sqlite.Connection ->
+  Sqlite.Query ->
+  params ->
+  Fold.Fold row b ->
+  IO b
+foldRows conn qry params = Fold.purely f
+  where
+    f :: forall x. (x -> row -> x) -> x -> (x -> b) -> IO b
+    f acc init extract = do
+      x <- Sqlite.fold conn qry params init (\a r -> pure $ acc a r)
+      pure $ extract x
diff --git a/users/Profpatsch/jbovlaste-sqlite/default.nix b/users/Profpatsch/jbovlaste-sqlite/default.nix
new file mode 100644
index 0000000000..ea59fdec39
--- /dev/null
+++ b/users/Profpatsch/jbovlaste-sqlite/default.nix
@@ -0,0 +1,33 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  #   bins = depot.nix.getBins pkgs.sqlite ["sqlite3"];
+
+  jbovlaste-sqlite = pkgs.haskellPackages.mkDerivation {
+    pname = "jbovlaste-sqlite";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./jbovlaste-sqlite.cabal
+      ./JbovlasteSqlite.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.pa-field-parser
+      depot.users.Profpatsch.my-prelude
+      pkgs.haskellPackages.foldl
+      pkgs.haskellPackages.sqlite-simple
+      pkgs.haskellPackages.xml-conduit
+
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+in
+jbovlaste-sqlite
diff --git a/users/Profpatsch/jbovlaste-sqlite/jbovlaste-sqlite.cabal b/users/Profpatsch/jbovlaste-sqlite/jbovlaste-sqlite.cabal
new file mode 100644
index 0000000000..f677615a16
--- /dev/null
+++ b/users/Profpatsch/jbovlaste-sqlite/jbovlaste-sqlite.cabal
@@ -0,0 +1,76 @@
+cabal-version:      3.0
+name:               jbovlaste-sqlite
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+executable jbovlaste-sqlite
+    import: common-options
+
+    main-is:          JbovlasteSqlite.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        pa-label,
+        pa-error-tree,
+        pa-field-parser,
+        my-prelude,
+        containers,
+        selective,
+        semigroupoids,
+        validation-selective,
+        sqlite-simple,
+        foldl,
+        conduit,
+        bytestring,
+        text,
+        sqlite-simple,
+        xml-conduit,
diff --git a/users/Profpatsch/lens.nix b/users/Profpatsch/lens.nix
new file mode 100644
index 0000000000..28f7506bdd
--- /dev/null
+++ b/users/Profpatsch/lens.nix
@@ -0,0 +1,137 @@
+{ ... }:
+let
+  id = x: x;
+
+  const = x: y: x;
+
+  comp = f: g: x: f (g x);
+
+  _ = v: f: f v;
+
+  # Profunctor (p :: Type -> Type -> Type)
+  Profunctor = rec {
+    # dimap :: (a -> b) -> (c -> d) -> p b c -> p a d
+    dimap = f: g: x: lmap f (rmap g x);
+    # lmap :: (a -> b) -> p b c -> p a c
+    lmap = f: dimap f id;
+    # rmap :: (c -> d) -> p b c -> p b d
+    rmap = g: dimap id g;
+  };
+
+  # Profunctor (->)
+  profunctorFun = Profunctor // {
+    # dimap :: (a -> b) -> (c -> d) -> (b -> c) -> a -> d
+    dimap = ab: cd: bc: a: cd (bc (ab a));
+    # lmap :: (a -> b) -> (b -> c) -> (a -> c)
+    lmap = ab: bc: a: bc (ab a);
+    # rmap :: (c -> d) -> (b -> c) -> (b -> d)
+    rmap = cd: bc: b: cd (bc b);
+  };
+
+  tuple = fst: snd: {
+    inherit fst snd;
+  };
+
+  swap = { fst, snd }: {
+    fst = snd;
+    snd = fst;
+  };
+
+  # Profunctor p => Strong (p :: Type -> Type -> Type)
+  Strong = pro: pro // rec {
+    # firstP :: p a b -> p (a, c) (b, c)
+    firstP = pab: pro.dimap swap swap (pro.secondP pab);
+    # secondP :: p a b -> p (c, a) (c, b)
+    secondP = pab: pro.dimap swap swap (pro.firstP pab);
+  };
+
+  # Strong (->)
+  strongFun = Strong profunctorFun // {
+    # firstP :: (a -> b) -> (a, c) -> (b, c)
+    firstP = f: { fst, snd }: { fst = f fst; inherit snd; };
+    # secondP :: (a -> b) -> (c, a) -> (c, b)
+    secondP = f: { snd, fst }: { snd = f snd; inherit fst; };
+  };
+
+  # Iso s t a b :: forall p. Profunctor p -> p a b -> p s t
+
+  # iso :: (s -> a) -> (b -> t) -> Iso s t a b
+  iso = pro: pro.dimap;
+
+  # Lens s t a b :: forall p. Strong p -> p a b -> p s t
+
+  # lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
+  lens = strong: get: set: pab:
+    lensP
+      strong
+      (s: tuple (get s) (b: set s b))
+      pab;
+
+  # lensP :: (s -> (a, b -> t)) -> Lens s t a b
+  lensP = strong: to: pab:
+    strong.dimap
+      to
+      ({ fst, snd }: snd fst)
+      (strong.firstP pab);
+
+  # first element of a tuple
+  # _1 :: Lens (a, c) (b, c) a b
+  _1 = strong: strong.firstP;
+
+  # second element of a tuple
+  # _2 :: Lens (c, a) (c, b) a b
+  _2 = strong: strong.secondP;
+
+  # a the given field in the record
+  # field :: (f :: String) -> Lens { f :: a; ... } { f :: b; ... } a b
+  field = name: strong:
+    lens
+      strong
+      (attrs: attrs.${name})
+      (attrs: a: attrs // { ${name} = a; });
+
+  # Setter :: (->) a b -> (->) s t
+  # Setter :: (a -> b) -> (s -> t)
+
+
+  # Subclasses of profunctor for (->).
+  # We only have Strong for now, but when we implement Choice we need to add it here.
+  profunctorSubclassesFun = strongFun;
+
+  # over :: Setter s t a b -> (a -> b) -> s -> t
+  over = setter:
+    # A setter needs to be instanced to the profunctor-subclass instances of (->).
+    (setter profunctorSubclassesFun);
+
+  # set :: Setter s t a b -> b -> s -> t
+  set = setter: b: over setter (const b);
+
+  # combine a bunch of optics, for the subclass instance of profunctor you give it.
+  optic = accessors: profunctorSubclass:
+    builtins.foldl' comp id
+      (map (accessor: accessor profunctorSubclass) accessors);
+
+
+in
+{
+  inherit
+    id
+    _
+    const
+    comp
+    Profunctor
+    profunctorFun
+    Strong
+    strongFun
+    iso
+    lens
+    optic
+    _1
+    _2
+    field
+    tuple
+    swap
+    over
+    set
+    ;
+}
diff --git a/users/Profpatsch/lib.nix b/users/Profpatsch/lib.nix
index 5d5fb01294..879d87755d 100644
--- a/users/Profpatsch/lib.nix
+++ b/users/Profpatsch/lib.nix
@@ -1,58 +1,108 @@
 { depot, pkgs, ... }:
 let
-  bins = depot.nix.getBins pkgs.coreutils [ "printf" "echo" "cat" "printenv" ]
-      // depot.nix.getBins pkgs.fdtools [ "multitee" ]
-      ;
-
-  debugExec = msg: depot.nix.writeExecline "debug-exec" {} [
-    "if" [
-      "fdmove" "-c" "1" "2"
-      "if" [ bins.printf "%s: " msg ]
-      "if" [ bins.echo "$@" ]
+  bins = depot.nix.getBins pkgs.coreutils [ "printf" "echo" "cat" "printenv" "tee" ]
+    // depot.nix.getBins pkgs.bash [ "bash" ]
+    // depot.nix.getBins pkgs.fdtools [ "multitee" ]
+  ;
+
+  # Print `msg` and and argv to stderr, then execute into argv
+  debugExec = msg: depot.nix.writeExecline "debug-exec" { } [
+    "if"
+    [
+      "fdmove"
+      "-c"
+      "1"
+      "2"
+      "if"
+      [ bins.printf "%s: " msg ]
+      "if"
+      [ bins.echo "$@" ]
     ]
     "$@"
   ];
 
-  eprint-stdin = depot.nix.writeExecline "eprint-stdin" {} [
-    "pipeline" [ bins.multitee "0-1,2" ] "$@"
+  # Print stdin to stderr and stdout
+  eprint-stdin = depot.nix.writeExecline "eprint-stdin" { } [
+    "pipeline"
+    [ bins.multitee "0-1,2" ]
+    "$@"
   ];
 
-  eprint-stdin-netencode = depot.nix.writeExecline "eprint-stdin-netencode" {} [
-    "pipeline" [
+  # Assume the input on stdin is netencode, pretty print it to stderr and forward it to stdout
+  eprint-stdin-netencode = depot.nix.writeExecline "eprint-stdin-netencode" { } [
+    "pipeline"
+    [
       # move stdout to 3
-      "fdmove" "3" "1"
+      "fdmove"
+      "3"
+      "1"
       # the multitee copies stdin to 1 (the other pipeline end) and 3 (the stdout of the outer pipeline block)
-      "pipeline" [ bins.multitee "0-1,3" ]
+      "pipeline"
+      [ bins.multitee "0-1,3" ]
       # make stderr the stdout of pretty, merging with the stderr of pretty
-      "fdmove" "-c" "1" "2"
+      "fdmove"
+      "-c"
+      "1"
+      "2"
       depot.users.Profpatsch.netencode.pretty
     ]
     "$@"
   ];
 
+  # print the given environment variable in $1 to stderr, then execute into the rest of argv
   eprintenv = depot.nix.writeExecline "eprintenv" { readNArgs = 1; } [
-    "ifelse" [ "fdmove" "-c" "1" "2" bins.printenv "$1" ]
+    "ifelse"
+    [ "fdmove" "-c" "1" "2" bins.printenv "$1" ]
     [ "$@" ]
-    "if" [ depot.tools.eprintf "eprintenv: could not find \"\${1}\" in the environment\n" ]
+    "if"
+    [ depot.tools.eprintf "eprintenv: could not find \"\${1}\" in the environment\n" ]
     "$@"
   ];
 
+  # Split stdin into two commands, given by a block and the rest of argv
+  #
+  # Example (execline):
+  #
+  #   pipeline [ echo foo ]
+  #   split-stdin [ fdmove 1 2 foreground [ cat ] echo "bar" ] cat
+  #
+  #   stdout: foo\n
+  #   stderr: foo\nbar\n
+  split-stdin = depot.nix.writeExecline "split-stdin" { argMode = "env"; } [
+    "pipeline"
+    [
+      # this is horrible yes but the quickest way I knew how to implement it
+      "runblock"
+      "1"
+      bins.bash
+      "-c"
+      ''${bins.tee} >("$@")''
+      "bash-split-stdin"
+    ]
+    "runblock"
+    "-r"
+    "1"
+  ];
+
   # remove everything but a few selected environment variables
   runInEmptyEnv = keepVars:
     let
-        importas = pkgs.lib.concatMap (var: [ "importas" "-i" var var ]) keepVars;
-        # we have to explicitely call export here, because PATH is probably empty
-        export = pkgs.lib.concatMap (var: [ "${pkgs.execline}/bin/export" var ''''${${var}}'' ]) keepVars;
-    in depot.nix.writeExecline "empty-env" {}
-         (importas ++ [ "emptyenv" ] ++ export ++ [ "${pkgs.execline}/bin/exec" "$@" ]);
+      importas = pkgs.lib.concatMap (var: [ "importas" "-i" var var ]) keepVars;
+      # we have to explicitely call export here, because PATH is probably empty
+      export = pkgs.lib.concatMap (var: [ "${pkgs.execline}/bin/export" var ''''${${var}}'' ]) keepVars;
+    in
+    depot.nix.writeExecline "empty-env" { }
+      (importas ++ [ "emptyenv" ] ++ export ++ [ "${pkgs.execline}/bin/exec" "$@" ]);
 
 
-in {
+in
+{
   inherit
     debugExec
     eprint-stdin
     eprint-stdin-netencode
     eprintenv
+    split-stdin
     runInEmptyEnv
     ;
 }
diff --git a/users/Profpatsch/lorri-wait-for-eval/LorriWaitForEval.hs b/users/Profpatsch/lorri-wait-for-eval/LorriWaitForEval.hs
new file mode 100644
index 0000000000..a1a4586401
--- /dev/null
+++ b/users/Profpatsch/lorri-wait-for-eval/LorriWaitForEval.hs
@@ -0,0 +1,173 @@
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE NumericUnderscores #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Main where
+
+import Conduit
+import Conduit qualified as Cond
+import Control.Concurrent
+import Control.Concurrent.Async qualified as Async
+import Control.Monad
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Bifunctor
+import Data.Conduit.Binary qualified as Conduit.Binary
+import Data.Conduit.Combinators qualified as Cond
+import Data.Conduit.Process
+import Data.Error
+import Data.Function
+import Data.Functor
+import Data.Text.IO (hPutStrLn)
+import MyPrelude
+import System.Directory qualified as Dir
+import System.Environment qualified as Env
+import System.Exit qualified as Exit
+import System.FilePath (takeDirectory)
+import System.FilePath.Posix qualified as FilePath
+import System.IO (stderr)
+import System.Posix qualified as Posix
+import Prelude hiding (log)
+
+data LorriEvent = LorriEvent
+  { nixFile :: Text,
+    eventType :: LorriEventType
+  }
+  deriving stock (Show)
+
+data LorriEventType
+  = Completed
+  | Started
+  | EvalFailure
+  deriving stock (Show)
+
+main :: IO ()
+main = do
+  argv <- Env.getArgs <&> nonEmpty
+
+  dir <- Dir.getCurrentDirectory
+  shellNix <-
+    findShellNix dir >>= \case
+      Nothing -> Exit.die [fmt|could not find any shell.nix in or above the directory {dir}|]
+      Just s -> pure s
+  getEventChan :: MVar (Chan LorriEvent) <- newEmptyMVar
+  Async.race_
+    ( do
+        sendEventChan :: Chan LorriEvent <- newChan
+        (exitCode, ()) <-
+          sourceProcessWithConsumer
+            (proc "lorri" ["internal", "stream-events"])
+            $
+            -- first, we want to send a message over the chan that the process is running (for timeout)
+            liftIO (putMVar getEventChan sendEventChan)
+              *> Conduit.Binary.lines
+              .| Cond.mapC
+                ( \jsonBytes ->
+                    (jsonBytes :: ByteString)
+                      & Json.parseStrict
+                        ( Json.key
+                            "Completed"
+                            ( do
+                                nixFile <- Json.key "nix_file" Json.asText
+                                pure LorriEvent {nixFile, eventType = Completed}
+                            )
+                            Json.<|> Json.key
+                              "Started"
+                              ( do
+                                  nixFile <- Json.key "nix_file" Json.asText
+                                  pure LorriEvent {nixFile, eventType = Started}
+                              )
+                            Json.<|> Json.key
+                              "Failure"
+                              ( do
+                                  nixFile <- Json.key "nix_file" Json.asText
+                                  pure LorriEvent {nixFile, eventType = EvalFailure}
+                              )
+                        )
+                      & first Json.displayError'
+                      & first (map newError)
+                      & first (smushErrors [fmt|Cannot parse line returned by lorri: {jsonBytes & bytesToTextUtf8Lenient}|])
+                      & unwrapError
+                )
+              .| (Cond.mapM_ (\ev -> writeChan sendEventChan ev))
+
+        log [fmt|lorri internal stream-events exited {show exitCode}|]
+    )
+    ( do
+        let waitMs ms = threadDelay (ms * 1000)
+
+        -- log [fmt|Waiting for lorri event for {shellNix}|]
+
+        eventChan <- takeMVar getEventChan
+
+        let isOurEvent ev = FilePath.normalise (ev & nixFile & textToString) == FilePath.normalise shellNix
+
+        let handleEvent ev =
+              case ev & eventType of
+                Started ->
+                  log [fmt|waiting for lorri build to finish|]
+                Completed -> do
+                  log [fmt|build completed|]
+                  exec (inDirenvDir (takeDirectory shellNix) <$> argv)
+                EvalFailure -> do
+                  log [fmt|evaluation failed! for path {ev & nixFile}|]
+                  Exit.exitWith (Exit.ExitFailure 111)
+
+        -- wait for 100ms for the first message from lorri,
+        -- or else assume lorri is not building the project yet
+        Async.race
+          (waitMs 100)
+          ( do
+              -- find the first event that we can use
+              let go = do
+                    ev <- readChan eventChan
+                    if isOurEvent ev then pure ev else go
+              go
+          )
+          >>= \case
+            Left () -> do
+              log [fmt|No event received from lorri, assuming this is the first evaluation|]
+              exec argv
+            Right ch -> handleEvent ch
+
+        runConduit $
+          repeatMC (readChan eventChan)
+            .| filterC isOurEvent
+            .| mapM_C handleEvent
+    )
+  where
+    inDirenvDir dir' argv' = ("direnv" :| ["exec", dir']) <> argv'
+    exec = \case
+      Just (exe :| args') -> Posix.executeFile exe True args' Nothing
+      Nothing -> Exit.exitSuccess
+
+log :: Text -> IO ()
+log msg = hPutStrLn stderr [fmt|lorri-wait-for-eval: {msg}|]
+
+-- | Searches from the current directory upwards, until it finds the `shell.nix`.
+findShellNix :: FilePath -> IO (Maybe FilePath)
+findShellNix curDir = do
+  let go :: (FilePath -> IO (Maybe FilePath))
+      go dir = do
+        let file = dir FilePath.</> "shell.nix"
+        Dir.doesFileExist file >>= \case
+          True -> pure (Just file)
+          False -> do
+            let parent = FilePath.takeDirectory dir
+            if parent == dir
+              then pure Nothing
+              else go parent
+  go (FilePath.normalise curDir)
+
+smushErrors :: Foldable t => Text -> t Error -> Error
+smushErrors msg errs =
+  errs
+    -- hrm, pretty printing and then creating a new error is kinda shady
+    & foldMap (\err -> "\n- " <> prettyError err)
+    & newError
+    & errorContext msg
diff --git a/users/Profpatsch/lorri-wait-for-eval/README.md b/users/Profpatsch/lorri-wait-for-eval/README.md
new file mode 100644
index 0000000000..9c5d8ef9e3
--- /dev/null
+++ b/users/Profpatsch/lorri-wait-for-eval/README.md
@@ -0,0 +1,7 @@
+# lorri-wait-for-eval
+
+A helper script for [lorri](https://github.com/nix-community/lorri), which wraps a command and executes it once lorri is finished evaluating the current `shell.nix`, and uses the new environment.
+
+This is useful when you need the new shell environment to be in scope of the command, but donโ€™t want to waste time waiting for it to finish.
+
+This should really be a feature of lorri, but I couldnโ€™t be assed to touch rust :P
diff --git a/users/Profpatsch/lorri-wait-for-eval/default.nix b/users/Profpatsch/lorri-wait-for-eval/default.nix
new file mode 100644
index 0000000000..90c6365fed
--- /dev/null
+++ b/users/Profpatsch/lorri-wait-for-eval/default.nix
@@ -0,0 +1,20 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  lorri-wait-for-eval = pkgs.writers.writeHaskell "lorri-wait-for-eval"
+    {
+      libraries = [
+        depot.users.Profpatsch.my-prelude
+        pkgs.haskellPackages.async
+        pkgs.haskellPackages.aeson-better-errors
+        pkgs.haskellPackages.conduit-extra
+        pkgs.haskellPackages.error
+        pkgs.haskellPackages.PyF
+        pkgs.haskellPackages.unliftio
+      ];
+      ghcArgs = [ "-threaded" ];
+
+    } ./LorriWaitForEval.hs;
+
+in
+lorri-wait-for-eval
diff --git a/users/Profpatsch/mailbox-org/MailboxOrg.hs b/users/Profpatsch/mailbox-org/MailboxOrg.hs
new file mode 100644
index 0000000000..6c5820080c
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/MailboxOrg.hs
@@ -0,0 +1,523 @@
+{-# LANGUAGE ApplicativeDo #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DerivingVia #-}
+{-# LANGUAGE GHC2021 #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PackageImports #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE NoFieldSelectors #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Main where
+
+import Aeson (parseErrorTree)
+import AesonQQ (aesonQQ)
+import ArglibNetencode
+import Control.Exception (try)
+import Control.Monad (replicateM)
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.ByteString qualified as ByteString
+import Data.ByteString.Lazy qualified as Lazy
+import Data.Char qualified as Char
+import "pa-error-tree" Data.Error.Tree
+import Data.Functor.Compose
+import Data.List qualified as List
+import Data.Map.Strict qualified as Map
+import Data.Text qualified as Text
+import ExecHelpers
+import Label
+import Netencode qualified
+import Netencode.Parse qualified as NetParse
+import Network.HTTP.Conduit qualified as Client
+import Network.HTTP.Simple qualified as Client
+import PossehlAnalyticsPrelude
+import Pretty
+import System.Directory qualified as File
+import System.Environment qualified as Env
+import System.Exit (ExitCode (ExitFailure, ExitSuccess))
+import System.Exit qualified as Exit
+import System.FilePath ((</>))
+import System.Process.Typed qualified as Process
+import System.Random qualified as Random
+import System.Random.Stateful qualified as Random
+import Prelude hiding (log)
+
+secret :: Tools -> IO (T2 "email" ByteString "password" ByteString)
+secret tools = do
+  T2
+    (label @"email" "mail@profpatsch.de")
+    <$> (label @"password" <$> fromPass "email/mailbox.org")
+  where
+    fromPass name =
+      tools.pass & runToolExpect0 [name]
+
+progName :: CurrentProgramName
+progName = "mailbox-org"
+
+log :: Error -> IO ()
+log err = do
+  putStderrLn (errorContext progName.unCurrentProgramName err & prettyError)
+
+data Tools = Tools
+  { pass :: Tool
+  }
+  deriving stock (Show)
+
+newtype Tool = Tool {unTool :: FilePath}
+  deriving stock (Show)
+
+parseTools :: Applicative m => (Text -> m (Either Error Tool)) -> m (Either ErrorTree Tools)
+parseTools getTool = do
+  let parser =
+        ( do
+            pass <- get "pass"
+            pure Tools {..}
+        )
+  parser & finalize
+  where
+    get name = name & getTool <&> eitherToListValidation & Compose
+    finalize p =
+      p.getCompose
+        <&> first (errorTree "Error reading tools")
+        <&> validationToEither
+
+main :: IO ()
+main =
+  arglibNetencode progName Nothing
+    >>= parseToolsArglib
+    >>= secret
+    >>= run applyFilters
+
+run ::
+  ( HasField "email" dat ByteString,
+    HasField "password" dat ByteString
+  ) =>
+  (Session -> IO ()) ->
+  dat ->
+  IO ()
+run act loginData = do
+  session <- login loginData
+  act session
+
+listFilterConfig :: Session -> IO ()
+listFilterConfig session = do
+  mailfilter
+    session
+    "config"
+    mempty
+    (Json.key "data" Json.asObject)
+    ()
+    >>= printPretty
+
+applyFilterRule ::
+  (HasField "folderId" dat Text) =>
+  dat ->
+  Session ->
+  IO ()
+applyFilterRule dat session = do
+  mailfilter
+    session
+    "apply"
+    ( T2
+        (label @"extraQueryParams" [("folderId", Just (dat.folderId & textToBytesUtf8))])
+        mempty
+    )
+    (Json.key "data" Json.asArray >> pure ())
+    (Json.Object mempty)
+
+data FilterRule = FilterRule
+  { actioncmds :: NonEmpty Json.Object,
+    test :: NonEmpty Json.Object
+  }
+
+data MailfilterList = MailfilterList
+  { id_ :: Json.Value,
+    rulename :: Text
+  }
+  deriving stock (Show, Eq)
+
+simpleRule ::
+  ( HasField "rulename" r Text,
+    HasField "id" r Natural,
+    HasField "emailContains" r Text,
+    HasField "subjectStartsWith" r Text
+  ) =>
+  r ->
+  Json.Value
+simpleRule dat = do
+  [aesonQQ|{
+    "id": |dat.id & enc @Natural|,
+    "position": 3,
+    "rulename": |dat.rulename & enc @Text|,
+    "active": true,
+    "flags": [],
+    "test": {
+      "id": "allof",
+      "tests": [
+        {
+          "id": "from",
+          "comparison": "contains",
+          "values": [
+            |dat.emailContains & enc @Text|
+          ]
+        },
+        {
+          "id": "subject",
+          "comparison": "startswith",
+          "values": [
+            |dat.subjectStartsWith & enc @Text|
+          ]
+        }
+      ]
+    },
+    "actioncmds": [
+      {
+        "id": "move",
+        "into": "default0/Archive"
+      },
+      {
+        "id": "stop"
+      }
+    ]
+  }|]
+  where
+    enc :: forall a. Json.ToJSON a => a -> Lazy.ByteString
+    enc val = val & Json.toJSON & Json.encode
+
+applyFilters :: Session -> IO ()
+applyFilters session = do
+  filters <-
+    mailfilter
+      session
+      "list"
+      mempty
+      ( Json.key "data" $ do
+          ( Json.eachInArray $ asDat @"mailfilter" $ do
+              id_ <- Json.key "id" Json.asValue
+              rulename <- Json.key "rulename" Json.asText
+              pure MailfilterList {..}
+            )
+            <&> mapFromListOn (\dat -> getLabel @"rulename" dat.parsed)
+      )
+      ([] :: [()])
+  let goal = Map.fromList [(label @"rulename" "another", 32 :: Integer), (label @"rulename" "xyz", 23)]
+  let actions = declarativeUpdate goal filters
+  log [fmt|To * create: {actions.toCreate & Map.keys & show}, * update: {actions.toUpdate & Map.keys & show}, * delete: {actions.toDelete & Map.keys & show}|]
+
+-- where
+-- filters
+--   & Map.elems
+--   & traverse_
+--     ( updateIfDifferent
+--         session
+--         ( \el ->
+--             pure $
+--               el.original.mailfilter
+--                 & KeyMap.insert "active" (Json.Bool False)
+--         )
+--         (pure ())
+--     )
+
+-- updateIfDifferent ::
+--   forall label parsed.
+--   ( HasField "id_" parsed Json.Value,
+--     HasField "rulename" parsed Text
+--   ) =>
+--   Session ->
+--   (Dat label Json.Object parsed -> IO Json.Object) ->
+--   Json.Parse Error () ->
+--   Dat label Json.Object parsed ->
+--   IO ()
+-- updateIfDifferent session switcheroo parser dat = do
+--   new <- switcheroo dat
+--   if new /= getField @label dat.original
+--     then do
+--       log [fmt|Updating filter "{dat.parsed.rulename}" (id {dat.parsed.id_ & show @Json.Value})|]
+--       mailfilter
+--         session
+--         "update"
+--         mempty
+--         parser
+--         new
+--     else do
+--       log [fmt|Skipping updating filter "{dat.parsed.rulename}" (id {dat.parsed.id_ & show @Json.Value}) because nothing changed.|]
+
+-- | https://oxpedia.org/wiki/index.php?title=HTTP_API_MailFilter
+mailfilter ::
+  ( Json.ToJSON a,
+    Show b
+  ) =>
+  Session ->
+  ByteString ->
+  T2
+    "extraQueryParams"
+    Client.Query
+    "httpMethod"
+    (Maybe ByteString) ->
+  Json.Parse Error b ->
+  a ->
+  IO b
+mailfilter session action opts parser body = do
+  req <-
+    Client.parseRequest "https://office.mailbox.org/appsuite/api/mailfilter/v2"
+      <&> Client.setQueryString
+        ( [ ("action", Just action),
+            ("colums", Just "1")
+          ]
+            <> opts.extraQueryParams
+        )
+      <&> Client.setRequestMethod (opts.httpMethod & fromMaybe "PUT")
+      <&> Client.setRequestBodyJSON body
+      <&> addSession session
+  req
+    & httpJSON [fmt|Cannot parse result for {req & prettyRequestShort}|] parser
+    >>= okOrDie
+    -- >>= (\resp -> printPretty resp >> pure resp)
+    <&> Client.responseBody
+  where
+    prettyRequestShort :: Client.Request -> Text
+    prettyRequestShort req = [fmt|request {req & Client.method}: {req & Client.host}{req & Client.path}{req & Client.queryString}|]
+
+-- | Given a goal and the actual state, return which elements to delete, update and create.
+declarativeUpdate ::
+  Ord k =>
+  -- | goal map
+  Map k a ->
+  -- | actual map
+  Map k b ->
+  T3
+    "toCreate"
+    (Map k a)
+    "toDelete"
+    (Map k b)
+    "toUpdate"
+    (Map k a)
+declarativeUpdate goal actual =
+  T3
+    (label @"toCreate" $ goal `Map.difference` actual)
+    (label @"toDelete" $ actual `Map.difference` goal)
+    (label @"toUpdate" $ goal `Map.intersection` actual)
+
+newtype Session = Session Client.CookieJar
+
+httpJSON ::
+  Error ->
+  Json.Parse Error b ->
+  Client.Request ->
+  IO (Client.Response b)
+httpJSON errMsg parser req = do
+  req
+    & Client.httpJSON @_ @Json.Value
+    >>= traverse
+      ( \val -> do
+          case val of
+            Json.Object obj
+              | "error" `KeyMap.member` obj
+                  && "error_desc" `KeyMap.member` obj -> do
+                  printPretty obj
+                  diePanic' "Server returned above inline error"
+            _ -> pure ()
+          val & Json.parseValue parser & \case
+            Left errs ->
+              errs
+                & parseErrorTree errMsg
+                & diePanic'
+            Right a -> pure a
+      )
+
+data Dat label orig parsed = Dat
+  { original :: Label label orig,
+    parsed :: parsed
+  }
+  deriving stock (Show, Eq)
+
+asDat ::
+  forall label err m a.
+  Monad m =>
+  Json.ParseT err m a ->
+  Json.ParseT err m (Dat label Json.Object a)
+asDat parser = do
+  original <- label @label <$> Json.asObject
+  parsed <- parser
+  pure Dat {..}
+
+addSession :: Session -> Client.Request -> Client.Request
+addSession (Session jar) req = do
+  let sessionId =
+        jar
+          & Client.destroyCookieJar
+          & List.find (\c -> "open-xchange-session-" `ByteString.isPrefixOf` c.cookie_name)
+          & annotate "The cookie jar did not contain an open-exchange-session-*"
+          & unwrapError
+          & (.cookie_value)
+
+  let req' = req & Client.addToRequestQueryString [("session", Just sessionId)]
+  req' {Client.cookieJar = Just jar}
+
+-- | Log into the mailbox.org service, and return the session secret cookies.
+login :: (HasField "email" dat ByteString, HasField "password" dat ByteString) => dat -> IO Session
+login dat = do
+  rnd <- randomString
+  req <-
+    Client.parseRequest "https://office.mailbox.org/ajax/login"
+      <&> Client.setQueryString
+        [ ("action", Just "formlogin"),
+          ("authId", Just $ ("mbo-" <> rnd) & stringToText & textToBytesUtf8)
+        ]
+      <&> Client.urlEncodedBody
+        [ ("version", "Form+Login"),
+          ("autologin", "true"),
+          ("client", "open-xchange-appsuite"),
+          ("uiWebPath", "/appsuite/"),
+          ("login", dat.email),
+          ("password", dat.password)
+        ]
+  Client.httpNoBody req
+    >>= okOrDie
+    <&> Client.responseCookieJar
+    <&> Session
+  where
+    -- For some reason they want the client to pass a random string
+    -- which is used for the session?โ€ฝ!?
+    randomString = do
+      gen <- Random.newIOGenM =<< Random.newStdGen
+      let chars = ['a' .. 'z'] <> ['A' .. 'Z'] <> ['0' .. '9']
+      let len = 11
+      Random.uniformRM (0, List.length chars - 1) gen
+        & replicateM len
+        <&> map (\index -> chars !! index)
+
+okOrDie :: Show a => Client.Response a -> IO (Client.Response a)
+okOrDie resp =
+  case resp & Client.getResponseStatusCode of
+    200 -> pure resp
+    _ -> do
+      printPretty resp
+      diePanic' "non-200 result"
+
+diePanic' :: ErrorTree -> IO a
+diePanic' errs = errs & prettyErrorTree & diePanic progName
+
+-- | Parse the tools from the given arglib input, and check that the executables exist
+parseToolsArglib :: Netencode.T -> IO Tools
+parseToolsArglib t = do
+  let oneTool name =
+        NetParse.asText
+          <&> textToString
+          <&> ( \path ->
+                  path
+                    & File.getPermissions
+                    <&> File.executable
+                    <&> ( \case
+                            False -> Left $ [fmt|Tool "{name}" is not an executable|]
+                            True -> Right (Tool path)
+                        )
+              )
+  let allTools =
+        parseTools (\name -> Compose $ NetParse.key name >>> oneTool name)
+          & getCompose
+  t
+    & NetParse.runParse
+      "test"
+      -- TODO: a proper ParseT for netencode values
+      ( NetParse.asRecord
+          >>> NetParse.key "BINS"
+          >>> NetParse.asRecord
+          >>> allTools
+      )
+    & orDo diePanic'
+    & join @IO
+    >>= orDo (\errs -> errs & diePanic')
+
+-- | Just assume the tools exist by name in the environment.
+parseToolsToolname :: IO Tools
+parseToolsToolname =
+  parseTools
+    ( \name ->
+        checkInPath name <&> \case
+          False -> Left [fmt|"Cannot find "{name}" in PATH|]
+          True -> Right $ Tool (name & textToString)
+    )
+    >>= orDo diePanic'
+
+checkInPath :: Text -> IO Bool
+checkInPath name = do
+  Env.lookupEnv "PATH"
+    <&> annotate "No PATH set"
+    >>= orDo diePanic'
+    <&> stringToText
+    <&> Text.split (== ':')
+    <&> filter (/= "")
+    >>= traverse
+      ( \p ->
+          File.getPermissions ((textToString p) </> (textToString name))
+            <&> File.executable
+            & try @IOError
+            >>= \case
+              Left _ioError -> pure False
+              Right isExe -> pure isExe
+      )
+    <&> or
+
+orDo :: Applicative f => (t -> f a) -> Either t a -> f a
+orDo f = \case
+  Left e -> f e
+  Right a -> pure a
+
+runTool :: [Text] -> Tool -> IO (Exit.ExitCode, ByteString)
+runTool args tool = do
+  let bashArgs = prettyArgsForBash ((tool.unTool & stringToText) : args)
+  log [fmt|Running: $ {bashArgs}|]
+  Process.proc
+    tool.unTool
+    (args <&> textToString)
+    & Process.readProcessStdout
+    <&> second toStrictBytes
+    <&> second stripWhitespaceFromEnd
+
+-- | Like `runCommandExpect0`, run the given tool, given a tool accessor.
+runToolExpect0 :: [Text] -> Tool -> IO ByteString
+runToolExpect0 args tool =
+  tool & runTool args >>= \(ex, stdout) -> do
+    checkStatus0 tool.unTool ex
+    pure stdout
+
+-- | Check whether a command exited 0 or crash.
+checkStatus0 :: FilePath -> ExitCode -> IO ()
+checkStatus0 executable = \case
+  ExitSuccess -> pure ()
+  ExitFailure status -> do
+    diePanic' [fmt|Command `{executable}` did not exit with status 0 (success), but status {status}|]
+
+stripWhitespaceFromEnd :: ByteString -> ByteString
+stripWhitespaceFromEnd = ByteString.reverse . ByteString.dropWhile (\w -> w == charToWordUnsafe '\n') . ByteString.reverse
+
+-- | Pretty print a command line in a way that can be copied to bash.
+prettyArgsForBash :: [Text] -> Text
+prettyArgsForBash = Text.intercalate " " . map simpleBashEscape
+
+-- | Simple escaping for bash words. If they contain anything thatโ€™s not ascii chars
+-- and a bunch of often-used special characters, put the word in single quotes.
+simpleBashEscape :: Text -> Text
+simpleBashEscape t = do
+  case Text.find (not . isSimple) t of
+    Just _ -> escapeSingleQuote t
+    Nothing -> t
+  where
+    -- any word that is just ascii characters is simple (no spaces or control characters)
+    -- or contains a few often-used characters like - or .
+    isSimple c =
+      Char.isAsciiLower c
+        || Char.isAsciiUpper c
+        || Char.isDigit c
+        -- These are benign, bash will not interpret them as special characters.
+        || List.elem c ['-', '.', ':', '/']
+    -- Put the word in single quotes
+    -- If there is a single quote in the word,
+    -- close the single quoted word, add a single quote, open the word again
+    escapeSingleQuote t' = "'" <> Text.replace "'" "'\\''" t' <> "'"
diff --git a/users/Profpatsch/mailbox-org/README.md b/users/Profpatsch/mailbox-org/README.md
new file mode 100644
index 0000000000..b84e7b59c1
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/README.md
@@ -0,0 +1,7 @@
+# mailbox-org
+
+Interfacing with the API of [https://mailbox.org/]().
+
+They use [open-xchange](https://www.open-xchange.com/resources/oxpedia) as their App Suite, so we have to work with/reverse engineer their weird API.
+
+Intended so I have a way of uploading Sieve rules into their system semi-automatically.
diff --git a/users/Profpatsch/mailbox-org/default.nix b/users/Profpatsch/mailbox-org/default.nix
new file mode 100644
index 0000000000..73bd28292d
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/default.nix
@@ -0,0 +1,38 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  mailbox-org = pkgs.haskellPackages.mkDerivation {
+    pname = "mailbox-org";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./mailbox-org.cabal
+      ./src/AesonQQ.hs
+      ./MailboxOrg.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-prelude
+      depot.users.Profpatsch.execline.exec-helpers-hs
+      depot.users.Profpatsch.arglib.netencode.haskell
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.aeson
+      pkgs.haskellPackages.http-conduit
+      pkgs.haskellPackages.aeson-better-errors
+    ];
+
+    isLibrary = false;
+    isExecutable = true;
+    license = lib.licenses.mit;
+  };
+
+
+in
+lib.pipe mailbox-org [
+  (x: (depot.nix.getBins x [ "mailbox-org" ]).mailbox-org)
+  (depot.users.Profpatsch.arglib.netencode.with-args "mailbox-org" {
+    BINS = depot.nix.getBins pkgs.dovecot_pigeonhole [ "sieve-test" ];
+  })
+]
diff --git a/users/Profpatsch/mailbox-org/mailbox-org.cabal b/users/Profpatsch/mailbox-org/mailbox-org.cabal
new file mode 100644
index 0000000000..a1b041447b
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/mailbox-org.cabal
@@ -0,0 +1,95 @@
+cabal-version:      3.0
+name:               mailbox-org
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+
+    hs-source-dirs: src
+
+    exposed-modules:
+        AesonQQ
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        aeson,
+        PyF,
+        template-haskell
+
+
+
+executable mailbox-org
+    import: common-options
+    main-is: MailboxOrg.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        mailbox-org,
+        my-prelude,
+        pa-prelude,
+        pa-label,
+        pa-error-tree,
+        exec-helpers,
+        netencode,
+        text,
+        directory,
+        filepath,
+        arglib-netencode,
+        random,
+        http-conduit,
+        aeson,
+        aeson-better-errors,
+        bytestring,
+        typed-process,
+        containers,
diff --git a/users/Profpatsch/mailbox-org/src/AesonQQ.hs b/users/Profpatsch/mailbox-org/src/AesonQQ.hs
new file mode 100644
index 0000000000..2ac3d533ae
--- /dev/null
+++ b/users/Profpatsch/mailbox-org/src/AesonQQ.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE TemplateHaskellQuotes #-}
+
+module AesonQQ where
+
+import Data.Aeson qualified as Json
+import Language.Haskell.TH.Quote (QuasiQuoter)
+import PossehlAnalyticsPrelude
+import PyF qualified
+import PyF.Internal.QQ qualified as PyFConf
+
+aesonQQ :: QuasiQuoter
+aesonQQ =
+  PyF.mkFormatter
+    "aesonQQ"
+    PyF.defaultConfig
+      { PyFConf.delimiters = Just ('|', '|'),
+        PyFConf.postProcess = \exp_ -> do
+          -- TODO: this does not throw an error at compilation time if the json does not parse
+          [|
+            case Json.eitherDecodeStrict' @Json.Value $ textToBytesUtf8 $ stringToText $(exp_) of
+              Left err -> error err
+              Right a -> a
+            |]
+      }
diff --git a/users/Profpatsch/my-prelude/README.md b/users/Profpatsch/my-prelude/README.md
new file mode 100644
index 0000000000..2cc068579a
--- /dev/null
+++ b/users/Profpatsch/my-prelude/README.md
@@ -0,0 +1,42 @@
+# My Haskell Prelude
+
+Contains various modules Iโ€™ve found useful when writing Haskell.
+
+## Contents
+
+A short overview:
+
+### `MyPrelude.hs`
+
+A collection of re-exports and extra functions. This does *not* replace the `Prelude` module from `base`, but rather should be imported *in addition* to `Prelude`.
+
+Stuff like bad functions from prelude (partial stuff, or plain horrible stuff) are handled by a custom `.hlint` file, which you can find in [../.hlint.yaml]().
+
+The common style of haskell they try to enable is what I call โ€œleft-to-right Haskellโ€,
+where one mostly prefers forward-chaining operators like `&`/`<&>`/`>>=` to backwards operators like `$`/`<$>`/`<=<`. In addition, all transformation function should follow the scheme of `aToB` instead of `B.fromA`, e.g. `Text.unpack`/`Text.pack` -> `textToString`/`stringToText`. Includes a bunch of text conversion functions one needs all the time, in the same style.
+
+These have been battle-tested in a production codebase of ~30k lines of Haskell.
+
+### `Label.hs`
+
+A very useful collection of anonymous labbeled tuples and enums of size 2 and 3. Assumes GHC >9.2 for `RecordDotSyntax` support.
+
+### `Pretty.hs`
+
+Colorful multiline pretty-printing of Haskell values.
+
+### `Test.hs`
+
+A wrapper around `hspec` which produces colorful test diffs.
+
+### `Aeson.hs`
+
+Helpers around Json parsing.
+
+### `Data.Error.Tree`
+
+Collect errors (from [`Data.Error`](https://hackage.haskell.org/package/error-1.0.0.0/docs/Data-Error.html)) into a tree, then display them in a nested fashion. Super useful for e.g. collecting and displaying nested parsing errors.
+
+### `RunCommand.hs`
+
+A module wrapping the process API with some helpful defaults for executing commands and printing what is executed to stderr.
diff --git a/users/Profpatsch/my-prelude/default.nix b/users/Profpatsch/my-prelude/default.nix
new file mode 100644
index 0000000000..e445115416
--- /dev/null
+++ b/users/Profpatsch/my-prelude/default.nix
@@ -0,0 +1,51 @@
+{ depot, pkgs, lib, ... }:
+
+pkgs.haskellPackages.mkDerivation {
+  pname = "my-prelude";
+  version = "0.0.1-unreleased";
+
+  src = depot.users.Profpatsch.exactSource ./. [
+    ./my-prelude.cabal
+    ./src/Aeson.hs
+    ./src/AtLeast.hs
+    ./src/MyPrelude.hs
+    ./src/Test.hs
+    ./src/Parse.hs
+    ./src/Pretty.hs
+    ./src/Seconds.hs
+    ./src/Tool.hs
+    ./src/ValidationParseT.hs
+    ./src/Postgres/Decoder.hs
+    ./src/Postgres/MonadPostgres.hs
+  ];
+
+  isLibrary = true;
+
+  libraryHaskellDepends = [
+    pkgs.haskellPackages.pa-prelude
+    pkgs.haskellPackages.pa-label
+    pkgs.haskellPackages.pa-error-tree
+    pkgs.haskellPackages.pa-json
+    pkgs.haskellPackages.pa-pretty
+    pkgs.haskellPackages.pa-field-parser
+    pkgs.haskellPackages.aeson-better-errors
+    pkgs.haskellPackages.foldl
+    pkgs.haskellPackages.resource-pool
+    pkgs.haskellPackages.error
+    pkgs.haskellPackages.hs-opentelemetry-api
+    pkgs.haskellPackages.hspec
+    pkgs.haskellPackages.hspec-expectations-pretty-diff
+    pkgs.haskellPackages.monad-logger
+    pkgs.haskellPackages.postgresql-simple
+    pkgs.haskellPackages.profunctors
+    pkgs.haskellPackages.PyF
+    pkgs.haskellPackages.semigroupoids
+    pkgs.haskellPackages.these
+    pkgs.haskellPackages.unliftio
+    pkgs.haskellPackages.validation-selective
+    pkgs.haskellPackages.vector
+  ];
+
+  license = lib.licenses.mit;
+
+}
diff --git a/users/Profpatsch/my-prelude/my-prelude.cabal b/users/Profpatsch/my-prelude/my-prelude.cabal
new file mode 100644
index 0000000000..95a8399f37
--- /dev/null
+++ b/users/Profpatsch/my-prelude/my-prelude.cabal
@@ -0,0 +1,120 @@
+cabal-version:      3.0
+name:               my-prelude
+version:            0.0.1.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+    -- allows defining pattern synonyms, but also the `import Foo (pattern FooPattern)` import syntax
+    PatternSynonyms
+  default-language: GHC2021
+
+
+library
+    import: common-options
+    hs-source-dirs: src
+    exposed-modules:
+      MyPrelude
+      Aeson
+      AtLeast
+      Test
+      Postgres.Decoder
+      Postgres.MonadPostgres
+      ValidationParseT
+      Parse
+      Pretty
+      Seconds
+      Tool
+
+    -- Modules included in this executable, other than Main.
+    -- other-modules:
+
+    -- LANGUAGE extensions used by modules in this package.
+    -- other-extensions:
+    build-depends:
+       base >=4.15 && <5
+     , pa-prelude
+     , pa-label
+     , pa-error-tree
+     , pa-json
+     , pa-pretty
+     , pa-field-parser
+     , aeson
+     , aeson-better-errors
+     , bytestring
+     , containers
+     , foldl
+     , unordered-containers
+     , resource-pool
+     , resourcet
+     , scientific
+     , time
+     , error
+     , exceptions
+     , filepath
+     , hspec
+     , hspec-expectations-pretty-diff
+     , hs-opentelemetry-api
+     , monad-logger
+     , mtl
+     , postgresql-simple
+     , profunctors
+     , PyF
+     , semigroupoids
+     , selective
+     , template-haskell
+     , text
+     , these
+     , unix
+     , unliftio
+     , validation-selective
+     , vector
+     , ghc-boot
+     -- for Pretty
+     , aeson-pretty
+     , hscolour
+     , ansi-terminal
+     , nicify-lib
diff --git a/users/Profpatsch/my-prelude/src/Aeson.hs b/users/Profpatsch/my-prelude/src/Aeson.hs
new file mode 100644
index 0000000000..73d6116082
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Aeson.hs
@@ -0,0 +1,176 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE GHC2021 #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+module Aeson where
+
+import Data.Aeson (Value (..))
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.Error.Tree
+import Data.Maybe (catMaybes)
+import Data.Vector qualified as Vector
+import Label
+import PossehlAnalyticsPrelude
+import Test.Hspec (describe, it, shouldBe)
+import Test.Hspec qualified as Hspec
+
+-- | Convert a 'Json.ParseError' to a corresponding 'ErrorTree'
+parseErrorTree :: Error -> Json.ParseError Error -> ErrorTree
+parseErrorTree contextMsg errs =
+  errs
+    & Json.displayError prettyError
+    <&> newError
+    & nonEmpty
+    & \case
+      Nothing -> singleError contextMsg
+      Just errs' -> errorTree contextMsg errs'
+
+-- | Parse a key from the object, ร  la 'Json.key', return a labelled value.
+--
+-- We donโ€™t provide a version that infers the json object key,
+-- since that conflates internal naming with the external API, which is dangerous.
+--
+-- @@
+-- do
+--   txt <- keyLabel @"myLabel" "jsonKeyName" Json.asText
+--   pure (txt :: Label "myLabel" Text)
+-- @@
+keyLabel ::
+  forall label err m a.
+  Monad m =>
+  Text ->
+  Json.ParseT err m a ->
+  Json.ParseT err m (Label label a)
+keyLabel = do
+  keyLabel' (Proxy @label)
+
+-- | Parse a key from the object, ร  la 'Json.key', return a labelled value.
+-- Version of 'keyLabel' that requires a proxy.
+--
+-- @@
+-- do
+--   txt <- keyLabel' (Proxy @"myLabel") "jsonKeyName" Json.asText
+--   pure (txt :: Label "myLabel" Text)
+-- @@
+keyLabel' ::
+  forall label err m a.
+  Monad m =>
+  Proxy label ->
+  Text ->
+  Json.ParseT err m a ->
+  Json.ParseT err m (Label label a)
+keyLabel' Proxy key parser = label @label <$> Json.key key parser
+
+-- | Parse an optional key from the object, ร  la 'Json.keyMay', return a labelled value.
+--
+-- We donโ€™t provide a version that infers the json object key,
+-- since that conflates internal naming with the external API, which is dangerous.
+--
+-- @@
+-- do
+--   txt <- keyLabelMay @"myLabel" "jsonKeyName" Json.asText
+--   pure (txt :: Label "myLabel" (Maybe Text))
+-- @@
+keyLabelMay ::
+  forall label err m a.
+  Monad m =>
+  Text ->
+  Json.ParseT err m a ->
+  Json.ParseT err m (Label label (Maybe a))
+keyLabelMay = do
+  keyLabelMay' (Proxy @label)
+
+-- | Parse an optional key from the object, ร  la 'Json.keyMay', return a labelled value.
+-- Version of 'keyLabelMay' that requires a proxy.
+--
+-- @@
+-- do
+--   txt <- keyLabelMay' (Proxy @"myLabel") "jsonKeyName" Json.asText
+--   pure (txt :: Label "myLabel" (Maybe Text))
+-- @@
+keyLabelMay' ::
+  forall label err m a.
+  Monad m =>
+  Proxy label ->
+  Text ->
+  Json.ParseT err m a ->
+  Json.ParseT err m (Label label (Maybe a))
+keyLabelMay' Proxy key parser = label @label <$> Json.keyMay key parser
+
+-- | Like 'Json.key', but allows a list of keys that are tried in order.
+--
+-- This is intended for renaming keys in an object.
+-- The first key is the most up-to-date version of a key, the others are for backward-compatibility.
+--
+-- If a key (new or old) exists, the inner parser will always be executed for that key.
+keyRenamed :: Monad m => NonEmpty Text -> Json.ParseT err m a -> Json.ParseT err m a
+keyRenamed (newKey :| oldKeys) inner =
+  keyRenamedTryOldKeys oldKeys inner >>= \case
+    Nothing -> Json.key newKey inner
+    Just parse -> parse
+
+-- | Like 'Json.keyMay', but allows a list of keys that are tried in order.
+--
+-- This is intended for renaming keys in an object.
+-- The first key is the most up-to-date version of a key, the others are for backward-compatibility.
+--
+-- If a key (new or old) exists, the inner parser will always be executed for that key.
+keyRenamedMay :: Monad m => NonEmpty Text -> Json.ParseT err m a -> Json.ParseT err m (Maybe a)
+keyRenamedMay (newKey :| oldKeys) inner =
+  keyRenamedTryOldKeys oldKeys inner >>= \case
+    Nothing -> Json.keyMay newKey inner
+    Just parse -> Just <$> parse
+
+-- | Helper function for 'keyRenamed' and 'keyRenamedMay' that returns the parser for the first old key that exists, if any.
+keyRenamedTryOldKeys :: Monad m => [Text] -> Json.ParseT err m a -> Json.ParseT err m (Maybe (Json.ParseT err m a))
+keyRenamedTryOldKeys oldKeys inner = do
+  oldKeys & traverse tryOld <&> catMaybes <&> nonEmpty <&> \case
+    Nothing -> Nothing
+    Just (old :| _moreOld) -> Just old
+  where
+    tryOld key =
+      Json.keyMay key (pure ()) <&> \case
+        Just () -> Just $ Json.key key inner
+        Nothing -> Nothing
+
+test_keyRenamed :: Hspec.Spec
+test_keyRenamed = do
+  describe "keyRenamed" $ do
+    let parser = keyRenamed ("new" :| ["old"]) Json.asText
+    let p = Json.parseValue @() parser
+    it "accepts the new key and the old key" $ do
+      p (Object (KeyMap.singleton "new" (String "text")))
+        `shouldBe` (Right "text")
+      p (Object (KeyMap.singleton "old" (String "text")))
+        `shouldBe` (Right "text")
+    it "fails with the old key in the error if the inner parser is wrong" $ do
+      p (Object (KeyMap.singleton "old" Null))
+        `shouldBe` (Left (Json.BadSchema [Json.ObjectKey "old"] (Json.WrongType Json.TyString Null)))
+    it "fails with the new key in the error if the inner parser is wrong" $ do
+      p (Object (KeyMap.singleton "new" Null))
+        `shouldBe` (Left (Json.BadSchema [Json.ObjectKey "new"] (Json.WrongType Json.TyString Null)))
+    it "fails if the key is missing" $ do
+      p (Object KeyMap.empty)
+        `shouldBe` (Left (Json.BadSchema [] (Json.KeyMissing "new")))
+  describe "keyRenamedMay" $ do
+    let parser = keyRenamedMay ("new" :| ["old"]) Json.asText
+    let p = Json.parseValue @() parser
+    it "accepts the new key and the old key" $ do
+      p (Object (KeyMap.singleton "new" (String "text")))
+        `shouldBe` (Right (Just "text"))
+      p (Object (KeyMap.singleton "old" (String "text")))
+        `shouldBe` (Right (Just "text"))
+    it "allows the old and new key to be missing" $ do
+      p (Object KeyMap.empty)
+        `shouldBe` (Right Nothing)
+
+-- | Create a json array from a list of json values.
+jsonArray :: [Value] -> Value
+jsonArray xs = xs & Vector.fromList & Array
diff --git a/users/Profpatsch/my-prelude/src/AtLeast.hs b/users/Profpatsch/my-prelude/src/AtLeast.hs
new file mode 100644
index 0000000000..3857c3a7cf
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/AtLeast.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module AtLeast where
+
+import Data.Aeson (FromJSON (parseJSON))
+import Data.Aeson.BetterErrors qualified as Json
+import FieldParser (FieldParser)
+import FieldParser qualified as Field
+import GHC.Records (HasField (..))
+import GHC.TypeLits (KnownNat, natVal)
+import PossehlAnalyticsPrelude
+  ( Natural,
+    Proxy (Proxy),
+    fmt,
+    prettyError,
+    (&),
+  )
+
+-- | A natural number that must be at least as big as the type literal.
+newtype AtLeast (min :: Natural) num = AtLeast num
+  -- Just use the instances of the wrapped number type
+  deriving newtype (Eq, Show)
+
+-- | This is the โ€œdestructorโ€ for `AtLeast`, because of the phantom type (@min@) it cannot be inferred automatically.
+instance HasField "unAtLeast" (AtLeast min num) num where
+  getField (AtLeast num) = num
+
+parseAtLeast ::
+  forall min num.
+  (KnownNat min, Integral num, Show num) =>
+  FieldParser num (AtLeast min num)
+parseAtLeast =
+  let minInt = natVal (Proxy @min)
+   in Field.FieldParser $ \from ->
+        if from >= (minInt & fromIntegral)
+          then Right (AtLeast from)
+          else Left [fmt|Must be at least {minInt & show} but was {from & show}|]
+
+instance
+  (KnownNat min, FromJSON num, Integral num, Bounded num, Show num) =>
+  FromJSON (AtLeast min num)
+  where
+  parseJSON =
+    Json.toAesonParser
+      prettyError
+      ( do
+          num <- Json.fromAesonParser @_ @num
+          case Field.runFieldParser (parseAtLeast @min @num) num of
+            Left err -> Json.throwCustomError err
+            Right a -> pure a
+      )
diff --git a/users/Profpatsch/my-prelude/src/MyPrelude.hs b/users/Profpatsch/my-prelude/src/MyPrelude.hs
new file mode 100644
index 0000000000..880983c47e
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/MyPrelude.hs
@@ -0,0 +1,776 @@
+{-# LANGUAGE ImplicitParams #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module MyPrelude
+  ( -- * Text conversions
+    Text,
+    ByteString,
+    Word8,
+    fmt,
+    textToString,
+    stringToText,
+    stringToBytesUtf8,
+    showToText,
+    textToBytesUtf8,
+    textToBytesUtf8Lazy,
+    bytesToTextUtf8,
+    bytesToTextUtf8Lazy,
+    bytesToTextUtf8Lenient,
+    bytesToTextUtf8LenientLazy,
+    bytesToTextUtf8Unsafe,
+    bytesToTextUtf8UnsafeLazy,
+    toStrict,
+    toLazy,
+    toStrictBytes,
+    toLazyBytes,
+    charToWordUnsafe,
+
+    -- * IO
+    putStrLn,
+    putStderrLn,
+    exitWithMessage,
+
+    -- * WIP code
+    todo,
+
+    -- * Records
+    HasField,
+
+    -- * Control flow
+    doAs,
+    (&),
+    (<&>),
+    (<|>),
+    foldMap1,
+    foldMap',
+    join,
+    when,
+    unless,
+    guard,
+    ExceptT (..),
+    runExceptT,
+    MonadThrow,
+    throwM,
+    MonadIO,
+    liftIO,
+    MonadReader,
+    asks,
+    Bifunctor,
+    first,
+    second,
+    bimap,
+    both,
+    foldMap,
+    fold,
+    foldl',
+    fromMaybe,
+    mapMaybe,
+    findMaybe,
+    Traversable,
+    for,
+    for_,
+    traverse,
+    traverse_,
+    traverseFold,
+    traverseFold1,
+    traverseFoldDefault,
+    MonadTrans,
+    lift,
+
+    -- * Data types
+    Coercible,
+    coerce,
+    Proxy (Proxy),
+    Map,
+    annotate,
+    Validation (Success, Failure),
+    failure,
+    successes,
+    failures,
+    traverseValidate,
+    traverseValidateM,
+    traverseValidateM_,
+    eitherToValidation,
+    eitherToListValidation,
+    validationToEither,
+    These (This, That, These),
+    eitherToThese,
+    eitherToListThese,
+    validationToThese,
+    thenThese,
+    thenValidate,
+    thenValidateM,
+    NonEmpty ((:|)),
+    pattern IsEmpty,
+    pattern IsNonEmpty,
+    singleton,
+    nonEmpty,
+    nonEmptyDef,
+    overNonEmpty,
+    zipNonEmpty,
+    zipWithNonEmpty,
+    zip3NonEmpty,
+    zipWith3NonEmpty,
+    zip4NonEmpty,
+    toList,
+    lengthNatural,
+    maximum1,
+    minimum1,
+    maximumBy1,
+    minimumBy1,
+    Vector,
+    Generic,
+    Lift,
+    Semigroup,
+    sconcat,
+    Monoid,
+    mconcat,
+    ifTrue,
+    ifExists,
+    Void,
+    absurd,
+    Identity (Identity, runIdentity),
+    Natural,
+    intToNatural,
+    Scientific,
+    Contravariant,
+    contramap,
+    (>$<),
+    (>&<),
+    Profunctor,
+    dimap,
+    lmap,
+    rmap,
+    Semigroupoid,
+    Category,
+    (>>>),
+    (&>>),
+    Any,
+
+    -- * Enum definition
+    inverseFunction,
+    inverseMap,
+    enumerateAll,
+
+    -- * Map helpers
+    mapFromListOn,
+    mapFromListOnMerge,
+
+    -- * Error handling
+    HasCallStack,
+    module Data.Error,
+  )
+where
+
+import Control.Applicative ((<|>))
+import Control.Category (Category, (>>>))
+import Control.Foldl.NonEmpty qualified as Foldl1
+import Control.Monad (guard, join, unless, when)
+import Control.Monad.Catch (MonadThrow (throwM))
+import Control.Monad.Except
+  ( ExceptT (..),
+    runExceptT,
+  )
+import Control.Monad.IO.Class (MonadIO, liftIO)
+import Control.Monad.Identity (Identity (Identity))
+import Control.Monad.Reader (MonadReader, asks)
+import Control.Monad.Trans (MonadTrans (lift))
+import Data.Bifunctor (Bifunctor, bimap, first, second)
+import Data.ByteString
+  ( ByteString,
+  )
+import Data.ByteString.Lazy qualified
+import Data.Char qualified
+import Data.Coerce (Coercible, coerce)
+import Data.Data (Proxy (Proxy))
+import Data.Error
+import Data.Foldable (Foldable (foldMap', toList), fold, foldl', for_, sequenceA_, traverse_)
+import Data.Foldable qualified as Foldable
+import Data.Function ((&))
+import Data.Functor ((<&>))
+import Data.Functor.Contravariant (Contravariant (contramap), (>$<))
+import Data.Functor.Identity (Identity (runIdentity))
+import Data.List (zip4)
+import Data.List.NonEmpty (NonEmpty ((:|)), nonEmpty)
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Map.Strict
+  ( Map,
+  )
+import Data.Map.Strict qualified as Map
+import Data.Maybe (fromMaybe, mapMaybe)
+import Data.Maybe qualified as Maybe
+import Data.Profunctor (Profunctor, dimap, lmap, rmap)
+import Data.Scientific (Scientific)
+import Data.Semigroup (sconcat)
+import Data.Semigroup.Foldable (Foldable1 (fold1), foldMap1)
+import Data.Semigroup.Traversable (Traversable1)
+import Data.Semigroupoid (Semigroupoid (o))
+import Data.Text
+  ( Text,
+  )
+import Data.Text qualified
+import Data.Text.Encoding qualified
+import Data.Text.Encoding.Error qualified
+import Data.Text.Lazy qualified
+import Data.Text.Lazy.Encoding qualified
+import Data.These (These (That, These, This))
+import Data.Traversable (for)
+import Data.Vector (Vector)
+import Data.Void (Void, absurd)
+import Data.Word (Word8)
+import GHC.Exception (errorCallWithCallStackException)
+import GHC.Exts (Any, RuntimeRep, TYPE, raise#)
+import GHC.Generics (Generic)
+import GHC.Natural (Natural)
+import GHC.Records (HasField)
+import GHC.Stack (HasCallStack)
+import GHC.Utils.Encoding qualified as GHC
+import Language.Haskell.TH.Syntax (Lift)
+import PyF (fmt)
+import System.Exit qualified
+import System.IO qualified
+import Validation
+  ( Validation (Failure, Success),
+    eitherToValidation,
+    failure,
+    failures,
+    successes,
+    validationToEither,
+  )
+
+-- | Mark a `do`-block with the type of the Monad/Applicativ it uses.
+-- Only intended for reading ease and making code easier to understand,
+-- especially do-blocks that use unconventional monads (like Maybe or List).
+--
+-- Example:
+--
+-- @
+-- doAs @Maybe $ do
+--  a <- Just 'a'
+--  b <- Just 'b'
+--  pure (a, b)
+-- @
+doAs :: forall m a. m a -> m a
+doAs = id
+
+-- | Forward-applying 'contramap', like '&'/'$' and '<&>'/'<$>' but for '>$<'.
+(>&<) :: (Contravariant f) => f b -> (a -> b) -> f a
+(>&<) = flip contramap
+
+infixl 5 >&<
+
+-- | Forward semigroupoid application. The same as '(>>>)', but 'Semigroupoid' is not a superclass of 'Category' (yet).
+--
+-- Specialized examples:
+--
+-- @
+-- for functions : (a -> b) -> (b -> c) -> (a -> c)
+-- for Folds: Fold a b -> Fold b c -> Fold a c
+-- @
+(&>>) :: (Semigroupoid s) => s a b -> s b c -> s a c
+(&>>) = flip Data.Semigroupoid.o
+
+-- like >>>
+infixr 1 &>>
+
+-- | encode a Text to a UTF-8 encoded Bytestring
+textToBytesUtf8 :: Text -> ByteString
+textToBytesUtf8 = Data.Text.Encoding.encodeUtf8
+
+-- | encode a lazy Text to a UTF-8 encoded lazy Bytestring
+textToBytesUtf8Lazy :: Data.Text.Lazy.Text -> Data.ByteString.Lazy.ByteString
+textToBytesUtf8Lazy = Data.Text.Lazy.Encoding.encodeUtf8
+
+bytesToTextUtf8 :: ByteString -> Either Error Text
+bytesToTextUtf8 = first exceptionToError . Data.Text.Encoding.decodeUtf8'
+
+bytesToTextUtf8Lazy :: Data.ByteString.Lazy.ByteString -> Either Error Data.Text.Lazy.Text
+bytesToTextUtf8Lazy = first exceptionToError . Data.Text.Lazy.Encoding.decodeUtf8'
+
+-- | decode a Text from a ByteString that is assumed to be UTF-8 (crash if that is not the case)
+bytesToTextUtf8Unsafe :: ByteString -> Text
+bytesToTextUtf8Unsafe = Data.Text.Encoding.decodeUtf8
+
+-- | decode a Text from a ByteString that is assumed to be UTF-8 (crash if that is not the case)
+bytesToTextUtf8UnsafeLazy :: Data.ByteString.Lazy.ByteString -> Data.Text.Lazy.Text
+bytesToTextUtf8UnsafeLazy = Data.Text.Lazy.Encoding.decodeUtf8
+
+-- | decode a Text from a ByteString that is assumed to be UTF-8,
+-- replace non-UTF-8 characters with the replacment char U+FFFD.
+bytesToTextUtf8Lenient :: Data.ByteString.ByteString -> Data.Text.Text
+bytesToTextUtf8Lenient =
+  Data.Text.Encoding.decodeUtf8With Data.Text.Encoding.Error.lenientDecode
+
+-- | decode a lazy Text from a lazy ByteString that is assumed to be UTF-8,
+-- replace non-UTF-8 characters with the replacment char U+FFFD.
+bytesToTextUtf8LenientLazy :: Data.ByteString.Lazy.ByteString -> Data.Text.Lazy.Text
+bytesToTextUtf8LenientLazy =
+  Data.Text.Lazy.Encoding.decodeUtf8With Data.Text.Encoding.Error.lenientDecode
+
+-- | Make a lazy 'Text' strict.
+toStrict :: Data.Text.Lazy.Text -> Text
+toStrict = Data.Text.Lazy.toStrict
+
+-- | Make a strict 'Text' lazy.
+toLazy :: Text -> Data.Text.Lazy.Text
+toLazy = Data.Text.Lazy.fromStrict
+
+-- | Make a lazy 'ByteString' strict.
+toStrictBytes :: Data.ByteString.Lazy.ByteString -> ByteString
+toStrictBytes = Data.ByteString.Lazy.toStrict
+
+-- | Make a strict 'ByteString' lazy.
+toLazyBytes :: ByteString -> Data.ByteString.Lazy.ByteString
+toLazyBytes = Data.ByteString.Lazy.fromStrict
+
+-- | Convert a (performant) 'Text' into an (imperformant) list-of-char 'String'.
+--
+-- Some libraries (like @time@ or @network-uri@) still use the `String` as their interface. We only want to convert to string at the edges, otherwise use 'Text'.
+--
+-- ATTN: Donโ€™t use `String` in code if you can avoid it, prefer `Text` instead.
+textToString :: Text -> String
+textToString = Data.Text.unpack
+
+-- | Convert an (imperformant) list-of-char 'String' into a (performant) 'Text' .
+--
+-- Some libraries (like @time@ or @network-uri@) still use the `String` as their interface. We want to convert 'String' to 'Text' as soon as possible and only use 'Text' in our code.
+--
+-- ATTN: Donโ€™t use `String` in code if you can avoid it, prefer `Text` instead.
+stringToText :: String -> Text
+stringToText = Data.Text.pack
+
+-- | Encode a String to an UTF-8 encoded Bytestring
+--
+-- ATTN: Donโ€™t use `String` in code if you can avoid it, prefer `Text` instead.
+stringToBytesUtf8 :: String -> ByteString
+-- TODO(Profpatsch): use a stable interface
+stringToBytesUtf8 = GHC.utf8EncodeByteString
+
+-- | Like `show`, but generate a 'Text'
+--
+-- ATTN: This goes via `String` and thus is fairly inefficient.
+-- We should add a good display library at one point.
+--
+-- ATTN: unlike `show`, this forces the whole @'a
+-- so only use if you want to display the whole thing.
+showToText :: (Show a) => a -> Text
+showToText = stringToText . show
+
+-- | Unsafe conversion between 'Char' and 'Word8'. This is a no-op and
+-- silently truncates to 8 bits Chars > '\255'. It is provided as
+-- convenience for ByteString construction.
+--
+-- Use if you want to get the 'Word8' representation of a character literal.
+-- Donโ€™t use on arbitrary characters!
+--
+-- >>> charToWordUnsafe ','
+-- 44
+charToWordUnsafe :: Char -> Word8
+{-# INLINE charToWordUnsafe #-}
+charToWordUnsafe = fromIntegral . Data.Char.ord
+
+pattern IsEmpty :: [a]
+pattern IsEmpty <- (null -> True)
+  where
+    IsEmpty = []
+
+pattern IsNonEmpty :: NonEmpty a -> [a]
+pattern IsNonEmpty n <- (nonEmpty -> Just n)
+  where
+    IsNonEmpty n = toList n
+
+{-# COMPLETE IsEmpty, IsNonEmpty #-}
+
+-- | Single element in a (non-empty) list.
+singleton :: a -> NonEmpty a
+singleton a = a :| []
+
+-- | If the given list is empty, use the given default element and return a non-empty list.
+nonEmptyDef :: a -> [a] -> NonEmpty a
+nonEmptyDef def xs =
+  xs & nonEmpty & \case
+    Nothing -> def :| []
+    Just ne -> ne
+
+-- | If the list is not empty, run the given function with a NonEmpty list, otherwise just return []
+overNonEmpty :: (Applicative f) => (NonEmpty a -> f [b]) -> [a] -> f [b]
+overNonEmpty f xs = case xs of
+  IsEmpty -> pure []
+  IsNonEmpty xs' -> f xs'
+
+-- | Zip two non-empty lists.
+zipNonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty (a, b)
+{-# INLINE zipNonEmpty #-}
+zipNonEmpty ~(a :| as) ~(b :| bs) = (a, b) :| zip as bs
+
+-- | Zip two non-empty lists, combining them with the given function
+zipWithNonEmpty :: (a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
+{-# INLINE zipWithNonEmpty #-}
+zipWithNonEmpty = NonEmpty.zipWith
+
+-- | Zip three non-empty lists.
+zip3NonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty (a, b, c)
+{-# INLINE zip3NonEmpty #-}
+zip3NonEmpty ~(a :| as) ~(b :| bs) ~(c :| cs) = (a, b, c) :| zip3 as bs cs
+
+-- | Zip three non-empty lists, combining them with the given function
+zipWith3NonEmpty :: (a -> b -> c -> d) -> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d
+{-# INLINE zipWith3NonEmpty #-}
+zipWith3NonEmpty f ~(x :| xs) ~(y :| ys) ~(z :| zs) = f x y z :| zipWith3 f xs ys zs
+
+-- | Zip four non-empty lists
+zip4NonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d -> NonEmpty (a, b, c, d)
+{-# INLINE zip4NonEmpty #-}
+zip4NonEmpty ~(a :| as) ~(b :| bs) ~(c :| cs) ~(d :| ds) = (a, b, c, d) :| zip4 as bs cs ds
+
+-- | We donโ€™t want to use Foldableโ€™s `length`, because it is too polymorphic and can lead to bugs.
+-- Only list-y things should have a length.
+class (Foldable f) => Lengthy f
+
+instance Lengthy []
+
+instance Lengthy NonEmpty
+
+instance Lengthy Vector
+
+lengthNatural :: (Lengthy f) => f a -> Natural
+lengthNatural xs =
+  xs
+    & Foldable.length
+    -- length can never be negative or something went really, really wrong
+    & fromIntegral @Int @Natural
+
+-- | @O(n)@. Get the maximum element from a non-empty structure (strict).
+maximum1 :: (Foldable1 f, Ord a) => f a -> a
+maximum1 = Foldl1.fold1 Foldl1.maximum
+
+-- | @O(n)@. Get the maximum element from a non-empty structure, using the given comparator (strict).
+maximumBy1 :: (Foldable1 f) => (a -> a -> Ordering) -> f a -> a
+maximumBy1 f = Foldl1.fold1 (Foldl1.maximumBy f)
+
+-- | @O(n)@. Get the minimum element from a non-empty structure (strict).
+minimum1 :: (Foldable1 f, Ord a) => f a -> a
+minimum1 = Foldl1.fold1 Foldl1.minimum
+
+-- | @O(n)@. Get the minimum element from a non-empty structure, using the given comparator (strict).
+minimumBy1 :: (Foldable1 f) => (a -> a -> Ordering) -> f a -> a
+minimumBy1 f = Foldl1.fold1 (Foldl1.minimumBy f)
+
+-- | Annotate a 'Maybe' with an error message and turn it into an 'Either'.
+annotate :: err -> Maybe a -> Either err a
+annotate err = \case
+  Nothing -> Left err
+  Just a -> Right a
+
+-- | Map the same function over both sides of a Bifunctor (e.g. a tuple).
+both :: (Bifunctor bi) => (a -> b) -> bi a a -> bi b b
+both f = bimap f f
+
+-- | Find the first element for which pred returns `Just a`, and return the `a`.
+--
+-- Example:
+-- @
+-- >>> :set -XTypeApplications
+-- >>> import qualified Text.Read
+--
+-- >>> findMaybe (Text.Read.readMaybe @Int) ["foo"]
+-- Nothing
+-- >>> findMaybe (Text.Read.readMaybe @Int) ["foo", "34.40", "34", "abc"]
+-- Just 34
+findMaybe :: (Foldable t) => (a -> Maybe b) -> t a -> Maybe b
+findMaybe mPred list =
+  let pred' x = Maybe.isJust $ mPred x
+   in case Foldable.find pred' list of
+        Just a -> mPred a
+        Nothing -> Nothing
+
+-- | 'traverse' with a function returning 'Either' and collect all errors that happen, if they happen.
+--
+-- Does not shortcut on error, so will always traverse the whole list/'Traversable' structure.
+--
+-- This is a useful error handling function in many circumstances,
+-- because it wonโ€™t only return the first error that happens, but rather all of them.
+traverseValidate :: forall t a err b. (Traversable t) => (a -> Either err b) -> t a -> Either (NonEmpty err) (t b)
+traverseValidate f as =
+  as
+    & traverse @t @(Validation _) (eitherToListValidation . f)
+    & validationToEither
+
+-- | 'traverse' with a function returning 'm Either' and collect all errors that happen, if they happen.
+--
+-- Does not shortcut on error, so will always traverse the whole list/'Traversable' structure.
+--
+-- This is a useful error handling function in many circumstances,
+-- because it wonโ€™t only return the first error that happens, but rather all of them.
+traverseValidateM :: forall t m a err b. (Traversable t, Applicative m) => (a -> m (Either err b)) -> t a -> m (Either (NonEmpty err) (t b))
+traverseValidateM f as =
+  as
+    & traverse @t @m (\a -> a & f <&> eitherToListValidation)
+    <&> sequenceA @t @(Validation _)
+    <&> validationToEither
+
+-- | 'traverse_' with a function returning 'm Either' and collect all errors that happen, if they happen.
+--
+-- Does not shortcut on error, so will always traverse the whole list/'Traversable' structure.
+--
+-- This is a useful error handling function in many circumstances,
+-- because it wonโ€™t only return the first error that happens, but rather all of them.
+traverseValidateM_ :: forall t m a err. (Traversable t, Applicative m) => (a -> m (Either err ())) -> t a -> m (Either (NonEmpty err) ())
+traverseValidateM_ f as =
+  as
+    & traverse @t @m (\a -> a & f <&> eitherToListValidation)
+    <&> sequenceA_ @t @(Validation _)
+    <&> validationToEither
+
+-- | Like 'eitherToValidation', but puts the Error side into a NonEmpty list
+-- to make it combine with other validations.
+--
+-- See also 'validateEithers', if you have a list of Either and want to collect all errors.
+eitherToListValidation :: Either a c -> Validation (NonEmpty a) c
+eitherToListValidation = first singleton . eitherToValidation
+
+-- | Convert an 'Either' to a 'These'.
+eitherToThese :: Either err a -> These err a
+eitherToThese (Left err) = This err
+eitherToThese (Right a) = That a
+
+-- | Like 'eitherToThese', but puts the Error side into a NonEmpty list
+-- to make it combine with other theses.
+eitherToListThese :: Either err a -> These (NonEmpty err) a
+eitherToListThese (Left e) = This (singleton e)
+eitherToListThese (Right a) = That a
+
+-- | Convert a 'Validation' to a 'These'.
+validationToThese :: Validation err a -> These err a
+validationToThese (Failure err) = This err
+validationToThese (Success a) = That a
+
+-- | Nested '>>=' of a These inside some other @m@.
+--
+-- Use if you want to collect errors and successes, and want to chain multiple function returning 'These'.
+thenThese ::
+  (Monad m, Semigroup err) =>
+  (a -> m (These err b)) ->
+  m (These err a) ->
+  m (These err b)
+thenThese f x = do
+  th <- x
+  join <$> traverse f th
+
+-- | Nested validating bind-like combinator.
+--
+-- Use if you want to collect errors, and want to chain multiple functions returning 'Validation'.
+thenValidate ::
+  (a -> Validation err b) ->
+  Validation err a ->
+  Validation err b
+thenValidate f = \case
+  Success a -> f a
+  Failure err -> Failure err
+
+-- | Nested validating bind-like combinator inside some other @m@.
+--
+-- Use if you want to collect errors, and want to chain multiple functions returning 'Validation'.
+thenValidateM ::
+  (Monad m) =>
+  (a -> m (Validation err b)) ->
+  m (Validation err a) ->
+  m (Validation err b)
+thenValidateM f x =
+  eitherToValidation <$> do
+    x' <- validationToEither <$> x
+    case x' of
+      Left err -> pure $ Left err
+      Right a -> validationToEither <$> f a
+
+-- | Put the text to @stderr@.
+putStderrLn :: Text -> IO ()
+putStderrLn msg =
+  System.IO.hPutStrLn System.IO.stderr $ textToString msg
+
+exitWithMessage :: Text -> IO a
+exitWithMessage msg = do
+  putStderrLn msg
+  System.Exit.exitWith $ System.Exit.ExitFailure (-1)
+
+-- | Run some function producing applicative over a traversable data structure,
+-- then collect the results in a Monoid.
+--
+-- Very helpful with side-effecting functions returning @(Validation err a)@:
+--
+-- @
+-- let
+--   f :: Text -> IO (Validation (NonEmpty Error) Text)
+--   f t = pure $ if t == "foo" then Success t else Failure (singleton ("not foo: " <> t))
+--
+-- in traverseFold f [ "foo", "bar", "baz" ]
+--   == Failure ("not foo bar" :| ["not foo baz"])
+-- @
+--
+-- โ€ฆ since @(Semigroup err => Validation err a)@ is a @Semigroup@/@Monoid@ itself.
+traverseFold :: (Applicative ap, Traversable t, Monoid m) => (a -> ap m) -> t a -> ap m
+{-# INLINE traverseFold #-}
+traverseFold f xs =
+  -- note: could be weakened to (Foldable t) via `getAp . foldMap (Ap . f)`
+  fold <$> traverse f xs
+
+-- | Like 'traverseFold', but fold over a semigroup instead of a Monoid, by providing a starting element.
+traverseFoldDefault :: (Applicative ap, Traversable t, Semigroup m) => m -> (a -> ap m) -> t a -> ap m
+{-# INLINE traverseFoldDefault #-}
+traverseFoldDefault def f xs = foldDef def <$> traverse f xs
+  where
+    foldDef = foldr (<>)
+
+-- | Same as 'traverseFold', but with a 'Semigroup' and 'Traversable1' restriction.
+traverseFold1 :: (Applicative ap, Traversable1 t, Semigroup s) => (a -> ap s) -> t a -> ap s
+{-# INLINE traverseFold1 #-}
+-- note: cannot be weakened to (Foldable1 t) because there is no `Ap` for Semigroup (No `Apply` typeclass)
+traverseFold1 f xs = fold1 <$> traverse f xs
+
+-- | Use this in places where the code is still to be implemented.
+--
+-- It always type-checks and will show a warning at compile time if it was forgotten in the code.
+--
+-- Use instead of 'error' and 'undefined' for code that hasnโ€™t been written.
+--
+-- Uses the same trick as https://hackage.haskell.org/package/protolude-0.3.0/docs/src/Protolude.Error.html#error
+{-# WARNING todo "'todo' (undefined code) remains in code" #-}
+todo :: forall (r :: RuntimeRep). forall (a :: TYPE r). (HasCallStack) => a
+todo = raise# (errorCallWithCallStackException "This code was not yet implemented: TODO" ?callStack)
+
+-- | Convert an integer to a 'Natural' if possible
+--
+-- Named the same as the function from "GHC.Natural", but does not crash.
+intToNatural :: (Integral a) => a -> Maybe Natural
+intToNatural i =
+  if i < 0
+    then Nothing
+    else Just $ fromIntegral i
+
+-- | @inverseFunction f@ creates a function that is the inverse of a given function
+-- @f@. It does so by constructing 'M.Map' internally for each value @f a@. The
+-- implementation makes sure that the 'M.Map' is constructed only once and then
+-- shared for every call.
+--
+-- __Memory usage note:__ don't inverse functions that have types like 'Int'
+-- as their result. In this case the created 'M.Map' will have huge size.
+--
+-- The complexity of reversed mapping is \(\mathcal{O}(\log n)\).
+--
+-- __Performance note:__ make sure to specialize monomorphic type of your functions
+-- that use 'inverseFunction' to avoid 'M.Map' reconstruction.
+--
+-- One of the common 'inverseFunction' use-case is inverting the 'show' or a 'show'-like
+-- function.
+--
+-- >>> data Color = Red | Green | Blue deriving (Show, Enum, Bounded)
+-- >>> parse = inverseFunction show :: String -> Maybe Color
+-- >>> parse "Red"
+-- Just Red
+-- >>> parse "Black"
+-- Nothing
+--
+-- __Correctness note:__ 'inverseFunction' expects /injective function/ as its argument,
+-- i.e. the function must map distinct arguments to distinct values.
+--
+-- Typical usage of this function looks like this:
+--
+-- @
+-- __data__ GhcVer
+--    = Ghc802
+--    | Ghc822
+--    | Ghc844
+--    | Ghc865
+--    | Ghc881
+--    __deriving__ ('Eq', 'Ord', 'Show', 'Enum', 'Bounded')
+--
+-- showGhcVer :: GhcVer -> 'Text'
+-- showGhcVer = \\__case__
+--    Ghc802 -> "8.0.2"
+--    Ghc822 -> "8.2.2"
+--    Ghc844 -> "8.4.4"
+--    Ghc865 -> "8.6.5"
+--    Ghc881 -> "8.8.1"
+--
+-- parseGhcVer :: 'Text' -> 'Maybe' GhcVer
+-- parseGhcVer = 'inverseFunction' showGhcVer
+--
+-- Taken from reludeโ€™s @Relude.Extra.Enum@.
+inverseFunction ::
+  forall a k.
+  (Bounded a, Enum a, Ord k) =>
+  (a -> k) ->
+  (k -> Maybe a)
+inverseFunction f k = Map.lookup k $ inverseMap f
+
+-- | Like `inverseFunction`, but instead of returning the function
+-- it returns a mapping from all possible outputs to their possible inputs.
+--
+-- This has the same restrictions of 'inverseFunction'.
+inverseMap :: forall a k. (Bounded a, Enum a, Ord k) => (a -> k) -> Map k a
+inverseMap f = enumerateAll <&> (\a -> (f a, a)) & Map.fromList
+
+-- | All possible values in this enum.
+enumerateAll :: (Enum a, Bounded a) => [a]
+enumerateAll = [minBound .. maxBound]
+
+-- | Create a 'Map' from a list of values, extracting the map key from each value. Like 'Map.fromList'.
+--
+-- Attention: if the key is not unique, the earliest value with the key will be in the map.
+mapFromListOn :: (Ord key) => (a -> key) -> [a] -> Map key a
+mapFromListOn f xs = xs <&> (\x -> (f x, x)) & Map.fromList
+
+-- | Create a 'Map' from a list of values, merging multiple values at the same key with '<>' (left-to-right)
+--
+-- `f` has to extract the key and value. Value must be mergable.
+--
+-- Attention: if the key is not unique, the earliest value with the key will be in the map.
+mapFromListOnMerge :: (Ord key, Semigroup s) => (a -> (key, s)) -> [a] -> Map key s
+mapFromListOnMerge f xs =
+  xs
+    <&> (\x -> f x)
+    & Map.fromListWith
+      -- we have to flip (`<>`) because `Map.fromListWith` merges its values โ€œthe other way aroundโ€
+      (flip (<>))
+
+-- | If the predicate is true, return the @m@, else 'mempty'.
+--
+-- This can be used (together with `ifExists`) to e.g. create lists with optional elements:
+--
+-- >>> import Data.Monoid (Sum(..))
+--
+-- >>> :{ mconcat [
+--   ifTrue (1 == 1) [1],
+--   [2, 3, 4],
+--   ifTrue False [5],
+-- ]
+-- :}
+-- [1,2,3,4]
+--
+-- Or any other Monoid:
+--
+-- >>> mconcat [ Sum 1, ifTrue (1 == 1) (Sum 2), Sum 3 ]
+
+-- Sum {getSum = 6}
+
+ifTrue :: (Monoid m) => Bool -> m -> m
+ifTrue pred' m = if pred' then m else mempty
+
+-- | If the given @Maybe@ is @Just@, return the result of `f` wrapped in `pure`, else return `mempty`.
+
+-- This can be used (together with `ifTrue`) to e.g. create lists with optional elements:
+--
+-- >>> import Data.Monoid (Sum(..))
+--
+-- >>> :{ mconcat [
+-- unknown command '{'
+--
+-- Or any other Monoid:
+--
+-- >>> mconcat [ Sum 1, ifExists id (Just 2), Sum 3 ]
+-- Sum {getSum = 6}
+
+ifExists :: (Monoid (f b), Applicative f) => (a -> b) -> Maybe a -> f b
+ifExists f m = m & foldMap @Maybe (pure . f)
diff --git a/users/Profpatsch/my-prelude/src/Parse.hs b/users/Profpatsch/my-prelude/src/Parse.hs
new file mode 100644
index 0000000000..65a0b0d39e
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Parse.hs
@@ -0,0 +1,174 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Parse where
+
+import Control.Category qualified
+import Control.Selective (Selective)
+import Data.Error.Tree
+import Data.Functor.Compose
+import Data.List qualified as List
+import Data.Monoid (First (..))
+import Data.Semigroup.Traversable
+import Data.Semigroupoid qualified as Semigroupoid
+import Data.Text qualified as Text
+import FieldParser (FieldParser)
+import FieldParser qualified as Field
+import PossehlAnalyticsPrelude
+import Validation (partitionValidations)
+import Prelude hiding (init, maybe)
+import Prelude qualified
+
+-- | A generic applicative โ€œverticalโ€ parser.
+-- Similar to `FieldParser`, but made for parsing whole structures and collect all errors in an `ErrorTree`.
+newtype Parse from to = Parse ((Context, from) -> Validation (NonEmpty ErrorTree) (Context, to))
+  deriving
+    (Functor, Applicative, Selective)
+    via ( Compose
+            ( Compose
+                ((->) (Context, from))
+                (Validation (NonEmpty ErrorTree))
+            )
+            ((,) Context)
+        )
+
+-- | Every parser can add to the context, like e.g. an element parser will add the name of the element it should be parsing.
+-- This should be added to the error message of each parser, with `showContext`.
+newtype Context = Context (Maybe [Text])
+  deriving stock (Show)
+  deriving (Semigroup, Monoid) via (First [Text])
+
+instance Semigroupoid Parse where
+  o p2 p1 = Parse $ \from -> case runParse' p1 from of
+    Failure err -> Failure err
+    Success to1 -> runParse' p2 to1
+
+instance Category Parse where
+  (.) = Semigroupoid.o
+  id = Parse $ \t -> Success t
+
+instance Profunctor Parse where
+  lmap f (Parse p) = Parse $ lmap (second f) p
+  rmap = (<$>)
+
+runParse :: Error -> Parse from to -> from -> Either ErrorTree to
+runParse errMsg parser t =
+  (Context (Just ["$"]), t)
+    & runParse' parser
+    <&> snd
+    & first (nestedMultiError errMsg)
+    & validationToEither
+
+runParse' :: Parse from to -> (Context, from) -> Validation (NonEmpty ErrorTree) (Context, to)
+runParse' (Parse f) from = f from
+
+showContext :: Context -> Text
+showContext (Context context) = context & fromMaybe [] & List.reverse & Text.intercalate "."
+
+addContext :: Text -> Context -> Context
+addContext x (Context mxs) = Context (Just $ x : (mxs & fromMaybe []))
+
+mkParsePushContext :: Text -> ((Context, from) -> Either ErrorTree to) -> Parse from to
+mkParsePushContext toPush f = Parse $ \(ctx, from) -> case f (ctx, from) of
+  Right to -> Success (addContext toPush ctx, to)
+  Left err -> Failure $ singleton err
+
+mkParseNoContext :: (from -> Either ErrorTree to) -> Parse from to
+mkParseNoContext f = Parse $ \(ctx, from) -> case f from of
+  Right to -> Success (ctx, to)
+  Left err -> Failure $ singleton err
+
+-- | Accept only exactly the given value
+exactly :: (Eq from) => (from -> Text) -> from -> Parse from from
+exactly errDisplay from = Parse $ \(ctx, from') ->
+  if from == from'
+    then Success (ctx, from')
+    else Failure $ singleton [fmt|Field has to be exactly {errDisplay from}, was: {errDisplay from'} at {showContext ctx}|]
+
+-- | Make a parser to parse the whole list
+multiple :: Parse a1 a2 -> Parse [a1] [a2]
+multiple inner = dimap nonEmpty (Prelude.maybe [] toList) (maybe $ multipleNE inner)
+
+-- | Make a parser to parse the whole non-empty list
+multipleNE :: Parse from to -> Parse (NonEmpty from) (NonEmpty to)
+multipleNE inner = Parse $ \(ctx, from) ->
+  from
+    & zipIndex
+    & traverse (\(idx, f) -> runParse' inner (ctx, f) & first (singleton . nestedMultiError [fmt|{idx}|]))
+    -- we assume that, since the same parser is used everywhere, the context will be the same as well (TODO: correct?)
+    & second (\((ctx', y) :| ys) -> (ctx', y :| (snd <$> ys)))
+
+-- | Like '(>>>)', but returns the intermediate result alongside the final parse result.
+andParse :: Parse to to2 -> Parse from to -> Parse from (to, to2)
+andParse outer inner = Parse $ \from -> case runParse' inner from of
+  Failure err -> Failure err
+  Success (ctx, to) -> runParse' outer (ctx, to) <&> (second (to,))
+
+-- | Lift a parser into an optional value
+maybe :: Parse from to -> Parse (Maybe from) (Maybe to)
+maybe inner = Parse $ \(ctx, m) -> case m of
+  Nothing -> Success (ctx, Nothing)
+  Just a -> runParse' inner (ctx, a) & second (fmap Just)
+
+-- | Assert that there is exactly one element in the list
+exactlyOne :: Parse [from] from
+exactlyOne = Parse $ \(ctx, xs) -> case xs of
+  [] -> Failure $ singleton [fmt|Expected exactly 1 element, but got 0, at {ctx & showContext}|]
+  [one] -> Success (ctx, one)
+  _more -> Failure $ singleton [fmt|Expected exactly 1 element, but got 2, at {ctx & showContext}|]
+
+-- | Assert that there is exactly zero or one element in the list
+zeroOrOne :: Parse [from] (Maybe from)
+zeroOrOne = Parse $ \(ctx, xs) -> case xs of
+  [] -> Success (ctx, Nothing)
+  [one] -> Success (ctx, Just one)
+  _more -> Failure $ singleton [fmt|Expected exactly 1 element, but got 2, at {ctx & showContext}|]
+
+-- | Find the first element on which the sub-parser succeeds; if there was no match, return all error messages.
+find :: Parse from to -> Parse [from] to
+find inner = Parse $ \(ctx, xs) -> case xs of
+  [] -> failure [fmt|Wanted to get the first sub-parser that succeeds, but there were no elements in the list, at {ctx & showContext}|]
+  (y : ys) -> runParse' (findNE' inner) (ctx, y :| ys)
+
+-- | Find the first element on which the sub-parser succeeds; if there was no match, return all error messages.
+findNE' :: Parse from to -> Parse (NonEmpty from) to
+findNE' inner = Parse $ \(ctx, xs) ->
+  xs
+    <&> (\x -> runParse' inner (ctx, x))
+    & traverse1
+      ( \case
+          Success a -> Left a
+          Failure e -> Right e
+      )
+    & \case
+      Left a -> Success a
+      Right errs ->
+        errs
+          & zipIndex
+          <&> (\(idx, errs') -> nestedMultiError [fmt|{idx}|] errs')
+          & nestedMultiError [fmt|None of these sub-parsers succeeded|]
+          & singleton
+          & Failure
+
+-- | Find all elements on which the sub-parser succeeds; if there was no match, return an empty list
+findAll :: Parse from to -> Parse [from] [to]
+findAll inner = Parse $ \(ctx, xs) ->
+  xs
+    <&> (\x -> runParse' inner (ctx, x))
+    & partitionValidations
+    & \case
+      (_miss, []) ->
+        -- in this case we just arbitrarily forward the original context โ€ฆ
+        Success (ctx, [])
+      (_miss, (hitCtx, hit) : hits) -> Success (hitCtx, hit : (hits <&> snd))
+
+-- | convert a 'FieldParser' into a 'Parse'.
+fieldParser :: FieldParser from to -> Parse from to
+fieldParser fp = Parse $ \(ctx, from) -> case Field.runFieldParser fp from of
+  Right a -> Success (ctx, a)
+  Left err -> Failure $ singleton (singleError err)
+
+zipNonEmpty :: NonEmpty a -> NonEmpty b -> NonEmpty (a, b)
+zipNonEmpty (x :| xs) (y :| ys) = (x, y) :| zip xs ys
+
+zipIndex :: NonEmpty b -> NonEmpty (Natural, b)
+zipIndex = zipNonEmpty (1 :| [2 :: Natural ..])
diff --git a/users/Profpatsch/my-prelude/src/Postgres/Decoder.hs b/users/Profpatsch/my-prelude/src/Postgres/Decoder.hs
new file mode 100644
index 0000000000..008b89b4ba
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Postgres/Decoder.hs
@@ -0,0 +1,94 @@
+module Postgres.Decoder where
+
+import Control.Applicative (Alternative)
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Error.Tree
+import Data.Typeable (Typeable)
+import Database.PostgreSQL.Simple (Binary (fromBinary))
+import Database.PostgreSQL.Simple.FromField qualified as PG
+import Database.PostgreSQL.Simple.FromRow qualified as PG
+import Json qualified
+import Label
+import PossehlAnalyticsPrelude
+
+-- | A Decoder of postgres values. Allows embedding more complex parsers (like a 'Json.ParseT').
+newtype Decoder a = Decoder (PG.RowParser a)
+  deriving newtype (Functor, Applicative, Alternative, Monad)
+
+-- | Parse a `bytea` field, equivalent to @Binary ByteString@ but avoids the pitfall of having to use 'Binary'.
+bytea :: Decoder ByteString
+bytea = fromField @(Binary ByteString) <&> (.fromBinary)
+
+-- | Parse a nullable `bytea` field, equivalent to @Binary ByteString@ but avoids the pitfall of having to use 'Binary'.
+byteaMay :: Decoder (Maybe ByteString)
+byteaMay = fromField @(Maybe (Binary ByteString)) <&> fmap (.fromBinary)
+
+-- | Turn any type that implements 'PG.fromField' into a 'Decoder'. Use type applications to prevent accidental conversions:
+--
+-- @
+-- fromField @Text :: Decoder Text
+-- @
+fromField :: PG.FromField a => Decoder a
+fromField = Decoder $ PG.fieldWith PG.fromField
+
+-- | Turn any type that implements 'PG.fromField' into a 'Decoder' and wrap the result into the given 'Label'. Use type applications to prevent accidental conversions:
+--
+-- @
+-- fromField @"myField" @Text :: Decoder (Label "myField" Text)
+-- @
+fromFieldLabel :: forall lbl a. PG.FromField a => Decoder (Label lbl a)
+fromFieldLabel = label @lbl <$> fromField
+
+-- | Parse fields out of a json value returned from the database.
+--
+-- ATTN: The whole json record has to be transferred before it is parsed,
+-- so if you only need a tiny bit of it, use `->` and `->>` in your SQL statement
+-- and return only the fields you need from the query.
+--
+-- In that case pay attention to NULL though:
+--
+-- @
+-- SELECT '{"foo": {}}'::jsonb->>'foo' IS NULL
+-- โ†’ TRUE
+-- @
+--
+-- Also note: `->>` will coerce the json value to @text@, regardless of the content.
+-- So the JSON object @{"foo": {}}"@ would be returned as the text: @"{\"foo\": {}}"@.
+json :: Typeable a => Json.ParseT ErrorTree Identity a -> Decoder a
+json parser = Decoder $ PG.fieldWith $ \field bytes -> do
+  val <- PG.fromField @Json.Value field bytes
+  case Json.parseValue parser val of
+    Left err ->
+      PG.returnError
+        PG.ConversionFailed
+        field
+        (err & Json.parseErrorTree "Cannot decode jsonb column" & prettyErrorTree & textToString)
+    Right a -> pure a
+
+-- | Parse fields out of a nullable json value returned from the database.
+--
+-- ATTN: The whole json record has to be transferred before it is parsed,
+-- so if you only need a tiny bit of it, use `->` and `->>` in your SQL statement
+-- and return only the fields you need from the query.
+--
+-- In that case pay attention to NULL though:
+--
+-- @
+-- SELECT '{"foo": {}}'::jsonb->>'foo' IS NULL
+-- โ†’ TRUE
+-- @
+--
+-- Also note: `->>` will coerce the json value to @text@, regardless of the content.
+-- So the JSON object @{"foo": {}}"@ would be returned as the text: @"{\"foo\": {}}"@.
+jsonMay :: Typeable a => Json.ParseT ErrorTree Identity a -> Decoder (Maybe a)
+jsonMay parser = Decoder $ PG.fieldWith $ \field bytes -> do
+  val <- PG.fromField @(Maybe Json.Value) field bytes
+  case Json.parseValue parser <$> val of
+    Nothing -> pure Nothing
+    Just (Left err) ->
+      PG.returnError
+        PG.ConversionFailed
+        field
+        (err & Json.parseErrorTree "Cannot decode jsonb column" & prettyErrorTree & textToString)
+    Just (Right a) -> pure (Just a)
diff --git a/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs b/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs
new file mode 100644
index 0000000000..f83a6d7fcf
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Postgres/MonadPostgres.hs
@@ -0,0 +1,760 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# OPTIONS_GHC -Wno-orphans #-}
+
+module Postgres.MonadPostgres where
+
+import AtLeast (AtLeast)
+import Control.Exception
+import Control.Foldl qualified as Fold
+import Control.Monad.Logger.CallStack (MonadLogger, logDebug, logWarn)
+import Control.Monad.Reader (MonadReader (ask), ReaderT (..))
+import Control.Monad.Trans.Resource
+import Data.Aeson (FromJSON)
+import Data.Error.Tree
+import Data.HashMap.Strict qualified as HashMap
+import Data.Int (Int64)
+import Data.Kind (Type)
+import Data.List qualified as List
+import Data.Pool (Pool)
+import Data.Pool qualified as Pool
+import Data.Text qualified as Text
+import Data.Typeable (Typeable)
+import Database.PostgreSQL.Simple (Connection, FormatError, FromRow, Query, QueryError, ResultError, SqlError, ToRow)
+import Database.PostgreSQL.Simple qualified as PG
+import Database.PostgreSQL.Simple qualified as Postgres
+import Database.PostgreSQL.Simple.FromRow qualified as PG
+import Database.PostgreSQL.Simple.ToField (ToField)
+import Database.PostgreSQL.Simple.ToRow (ToRow (toRow))
+import Database.PostgreSQL.Simple.Types (Query (..))
+import GHC.Records (getField)
+import Label
+import OpenTelemetry.Trace.Core qualified as Otel hiding (inSpan, inSpan')
+import OpenTelemetry.Trace.Monad qualified as Otel
+import PossehlAnalyticsPrelude
+import Postgres.Decoder
+import Postgres.Decoder qualified as Dec
+import Pretty (showPretty)
+import Seconds
+import System.Exit (ExitCode (..))
+import Tool
+import UnliftIO (MonadUnliftIO (withRunInIO))
+import UnliftIO.Process qualified as Process
+import UnliftIO.Resource qualified as Resource
+import Prelude hiding (init, span)
+
+-- | Postgres queries/commands that can be executed within a running transaction.
+--
+-- These are implemented with the @postgresql-simple@ primitives of the same name
+-- and will behave the same unless othewise documented.
+class (Monad m) => MonadPostgres (m :: Type -> Type) where
+  -- | Execute an INSERT, UPDATE, or other SQL query that is not expected to return results.
+
+  -- Returns the number of rows affected.
+  execute ::
+    (ToRow params, Typeable params) =>
+    Query ->
+    params ->
+    Transaction m (Label "numberOfRowsAffected" Natural)
+
+  -- | Execute a multi-row INSERT, UPDATE, or other SQL query that is not expected to return results.
+  --
+  -- Returns the number of rows affected. If the list of parameters is empty,
+  -- this function will simply return 0 without issuing the query to the backend.
+  -- If this is not desired, consider using the 'PG.Values' constructor instead.
+  executeMany ::
+    (ToRow params, Typeable params) =>
+    Query ->
+    NonEmpty params ->
+    Transaction m (Label "numberOfRowsAffected" Natural)
+
+  -- | Execute INSERT ... RETURNING, UPDATE ... RETURNING,
+  -- or other SQL query that accepts multi-row input and is expected to return results.
+  -- Note that it is possible to write query conn "INSERT ... RETURNING ..." ...
+  -- in cases where you are only inserting a single row,
+  -- and do not need functionality analogous to 'executeMany'.
+  --
+  -- If the list of parameters is empty, this function will simply return [] without issuing the query to the backend. If this is not desired, consider using the 'PG.Values' constructor instead.
+  executeManyReturningWith :: (ToRow q) => Query -> NonEmpty q -> Decoder r -> Transaction m [r]
+
+  -- | Run a query, passing parameters and result row parser.
+  queryWith ::
+    (PG.ToRow params, Typeable params, Typeable r) =>
+    PG.Query ->
+    params ->
+    Decoder r ->
+    Transaction m [r]
+
+  -- | Run a query without any parameters and result row parser.
+  queryWith_ ::
+    (Typeable r) =>
+    PG.Query ->
+    Decoder r ->
+    Transaction m [r]
+
+  -- | Run a query, passing parameters, and fold over the resulting rows.
+  --
+  -- This doesnโ€™t have to realize the full list of results in memory,
+  -- rather results are streamed incrementally from the database.
+  --
+  -- When dealing with small results, it may be simpler (and perhaps faster) to use query instead.
+  --
+  -- This fold is _not_ strict. The stream consumer is responsible
+  -- for forcing the evaluation of its result to avoid space leaks.
+  --
+  -- If you can, prefer aggregating in the database itself.
+  foldRowsWithAcc ::
+    (ToRow params, Typeable row, Typeable params) =>
+    Query ->
+    params ->
+    Decoder row ->
+    a ->
+    (a -> row -> Transaction m a) ->
+    Transaction m a
+
+  -- | Run a given transaction in a transaction block, rolling back the transaction
+  -- if any exception (postgres or Haskell Exception) is thrown during execution.
+  --
+  -- Re-throws the exception.
+  --
+  -- Donโ€™t do any long-running things on the Haskell side during a transaction,
+  -- because it will block a database connection and potentially also lock
+  -- database tables from being written or read by other clients.
+  --
+  -- Nonetheless, try to push transactions as far out to the handlers as possible,
+  -- donโ€™t do something like @runTransaction $ query โ€ฆ@, because it will lead people
+  -- to accidentally start nested transactions (the inner transaction is run on a new connections,
+  -- thus canโ€™t see any changes done by the outer transaction).
+  -- Only handlers should run transactions.
+  runTransaction :: Transaction m a -> m a
+
+-- | Run a query, passing parameters. Prefer 'queryWith' if possible.
+query ::
+  forall m params r.
+  (PG.ToRow params, PG.FromRow r, Typeable params, Typeable r, MonadPostgres m) =>
+  PG.Query ->
+  params ->
+  Transaction m [r]
+query qry params = queryWith qry params (Decoder PG.fromRow)
+
+-- | Run a query without any parameters. Prefer 'queryWith' if possible.
+--
+-- TODO: I think(?) this can always be replaced by passing @()@ to 'query', remove?
+query_ ::
+  forall m r.
+  (Typeable r, PG.FromRow r, MonadPostgres m) =>
+  PG.Query ->
+  Transaction m [r]
+query_ qry = queryWith_ qry (Decoder PG.fromRow)
+
+-- TODO: implement via fold, so that the result doesnโ€™t have to be realized in memory
+querySingleRow ::
+  ( MonadPostgres m,
+    ToRow qParams,
+    Typeable qParams,
+    FromRow a,
+    Typeable a,
+    MonadThrow m
+  ) =>
+  Query ->
+  qParams ->
+  Transaction m a
+querySingleRow qry params = do
+  query qry params >>= ensureSingleRow
+
+-- TODO: implement via fold, so that the result doesnโ€™t have to be realized in memory
+querySingleRowWith ::
+  ( MonadPostgres m,
+    ToRow qParams,
+    Typeable qParams,
+    Typeable a,
+    MonadThrow m
+  ) =>
+  Query ->
+  qParams ->
+  Decoder a ->
+  Transaction m a
+querySingleRowWith qry params decoder = do
+  queryWith qry params decoder >>= ensureSingleRow
+
+-- TODO: implement via fold, so that the result doesnโ€™t have to be realized in memory
+querySingleRowMaybe ::
+  ( MonadPostgres m,
+    ToRow qParams,
+    Typeable qParams,
+    FromRow a,
+    Typeable a,
+    MonadThrow m
+  ) =>
+  Query ->
+  qParams ->
+  Transaction m (Maybe a)
+querySingleRowMaybe qry params = do
+  rows <- query qry params
+  case rows of
+    [] -> pure Nothing
+    [one] -> pure (Just one)
+    -- TODO: Should we MonadThrow this here? Itโ€™s really an implementation detail of MonadPostgres
+    -- that a database function can error out, should probably handled by the instances.
+    more -> throwM $ SingleRowError {numberOfRowsReturned = (List.length more)}
+
+ensureSingleRow ::
+  (MonadThrow m) =>
+  [a] ->
+  m a
+ensureSingleRow = \case
+  -- TODO: Should we MonadThrow this here? Itโ€™s really an implementation detail of MonadPostgres
+  -- that a database function can error out, should probably handled by the instances.
+  [] -> throwM (SingleRowError {numberOfRowsReturned = 0})
+  [one] -> pure one
+  more ->
+    throwM $
+      SingleRowError
+        { numberOfRowsReturned =
+            -- TODO: this is VERY bad, because it requires to parse the full database output, even if thereโ€™s 10000000000 elements
+            List.length more
+        }
+
+ensureNoneOrSingleRow ::
+  (MonadThrow m) =>
+  [a] ->
+  m (Maybe a)
+ensureNoneOrSingleRow = \case
+  -- TODO: Should we MonadThrow this here? Itโ€™s really an implementation detail of MonadPostgres
+  -- that a database function can error out, should probably handled by the instances.
+  [] -> pure Nothing
+  [one] -> pure $ Just one
+  more ->
+    throwM $
+      SingleRowError
+        { numberOfRowsReturned =
+            -- TODO: this is VERY bad, because it requires to parse the full database output, even if thereโ€™s 10000000000 elements
+            List.length more
+        }
+
+-- | Run a query, passing parameters, and fold over the resulting rows.
+--
+-- This doesnโ€™t have to realize the full list of results in memory,
+-- rather results are streamed incrementally from the database.
+--
+-- When dealing with small results, it may be simpler (and perhaps faster) to use query instead.
+--
+-- The results are folded strictly by the 'Fold.Fold' that is passed.
+--
+-- If you can, prefer aggregating in the database itself.
+foldRowsWith ::
+  forall row params m b.
+  ( MonadPostgres m,
+    PG.ToRow params,
+    Typeable row,
+    Typeable params
+  ) =>
+  PG.Query ->
+  params ->
+  Decoder row ->
+  Fold.Fold row b ->
+  Transaction m b
+foldRowsWith qry params decoder = Fold.purely f
+  where
+    f :: forall x. (x -> row -> x) -> x -> (x -> b) -> Transaction m b
+    f acc init extract = do
+      x <- foldRowsWithAcc qry params decoder init (\a r -> pure $ acc a r)
+      pure $ extract x
+
+newtype Transaction m a = Transaction {unTransaction :: (ReaderT Connection m a)}
+  deriving newtype
+    ( Functor,
+      Applicative,
+      Monad,
+      MonadThrow,
+      MonadLogger,
+      MonadIO,
+      MonadUnliftIO,
+      MonadTrans,
+      Otel.MonadTracer
+    )
+
+-- | [Resource Pool](http://hackage.haskell.org/package/resource-pool-0.2.3.2/docs/Data-Pool.html) configuration.
+data PoolingInfo = PoolingInfo
+  { -- | Minimal amount of resources that are
+    --   always available.
+    numberOfStripes :: AtLeast 1 Int,
+    -- | Time after which extra resources
+    --   (above minimum) can stay in the pool
+    --   without being used.
+    unusedResourceOpenTime :: Seconds,
+    -- | Max number of resources that can be
+    --   in the Pool at any time
+    maxOpenResourcesAcrossAllStripes :: AtLeast 1 Int
+  }
+  deriving stock (Generic, Eq, Show)
+  deriving anyclass (FromJSON)
+
+initMonadPostgres ::
+  (Text -> IO ()) ->
+  -- | Info describing the connection to the Postgres DB
+  Postgres.ConnectInfo ->
+  -- | Configuration info for pooling attributes
+  PoolingInfo ->
+  -- | Created Postgres connection pool
+  ResourceT IO (Pool Postgres.Connection)
+initMonadPostgres logInfoFn connectInfo poolingInfo = do
+  (_releaseKey, connPool) <-
+    Resource.allocate
+      (logInfoFn "Creating Postgres Connection Pool" >> createPGConnPool)
+      (\pool -> logInfoFn "Destroying Postgres Connection Pool" >> destroyPGConnPool pool)
+  pure connPool
+  where
+    -- \| Create a Postgres connection pool
+    createPGConnPool ::
+      IO (Pool Postgres.Connection)
+    createPGConnPool =
+      Pool.newPool $
+        Pool.defaultPoolConfig
+          {- resource init action -} poolCreateResource
+          {- resource destruction -} poolfreeResource
+          ( poolingInfo.unusedResourceOpenTime.unSeconds
+              & fromIntegral @Natural @Double
+          )
+          (poolingInfo.maxOpenResourcesAcrossAllStripes.unAtLeast)
+      where
+        poolCreateResource = Postgres.connect connectInfo
+        poolfreeResource = Postgres.close
+
+    -- \| Destroy a Postgres connection pool
+    destroyPGConnPool ::
+      -- \| Pool to be destroyed
+      (Pool Postgres.Connection) ->
+      IO ()
+    destroyPGConnPool p = Pool.destroyAllResources p
+
+-- | Improve a possible error message, by adding some context to it.
+--
+-- The given Exception type is caught, 'show'n and pretty-printed.
+--
+-- In case we get an `IOError`, we display it in a reasonable fashion.
+addErrorInformation ::
+  forall exc a.
+  (Exception exc) =>
+  Text.Text ->
+  IO a ->
+  IO a
+addErrorInformation msg io =
+  io
+    & try @exc
+    <&> first (showPretty >>> newError >>> errorContext msg)
+    & try @IOError
+    <&> first (showToError >>> errorContext "IOError" >>> errorContext msg)
+    <&> join @(Either Error)
+    >>= unwrapIOError
+
+-- | Catch any Postgres exception that gets thrown,
+-- print the query that was run and the query parameters,
+-- then rethrow inside an 'Error'.
+handlePGException ::
+  forall a params tools m.
+  ( ToRow params,
+    MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  tools ->
+  Text ->
+  Query ->
+  -- | Depending on whether we used `format` or `formatMany`.
+  Either params (NonEmpty params) ->
+  IO a ->
+  Transaction m a
+handlePGException tools queryType query' params io = do
+  withRunInIO $ \unliftIO ->
+    io
+      `catches` [ Handler $ unliftIO . logQueryException @SqlError,
+                  Handler $ unliftIO . logQueryException @QueryError,
+                  Handler $ unliftIO . logQueryException @ResultError,
+                  Handler $ unliftIO . logFormatException
+                ]
+  where
+    -- TODO: use throwInternalError here (after pulling it into the MonadPostgres class)
+    throwAsError = unwrapIOError . Left . newError
+    throwErr err = liftIO $ throwAsError $ prettyErrorTree $ nestedMultiError "A Postgres query failed" err
+    logQueryException :: (Exception e) => e -> Transaction m a
+    logQueryException exc = do
+      formattedQuery <- case params of
+        Left one -> pgFormatQuery' tools query' one
+        Right many -> pgFormatQueryMany' tools query' many
+      throwErr
+        ( singleError [fmt|Query Type: {queryType}|]
+            :| [ nestedError "Exception" (exc & showPretty & newError & singleError),
+                 nestedError "Query" (formattedQuery & newError & singleError)
+               ]
+        )
+    logFormatException :: FormatError -> Transaction m a
+    logFormatException fe = throwErr (fe & showPretty & newError & singleError & singleton)
+
+-- | Perform a Postgres action within a transaction
+withPGTransaction ::
+  -- | Postgres connection pool to be used for the action
+  (Pool Postgres.Connection) ->
+  -- | DB-action to be performed
+  (Postgres.Connection -> IO a) ->
+  -- | Result of the DB-action
+  IO a
+withPGTransaction connPool f =
+  Pool.withResource
+    connPool
+    (\conn -> Postgres.withTransaction conn (f conn))
+
+runPGTransactionImpl ::
+  (MonadUnliftIO m) =>
+  m (Pool Postgres.Connection) ->
+  Transaction m a ->
+  m a
+{-# INLINE runPGTransactionImpl #-}
+runPGTransactionImpl zoom (Transaction transaction) = do
+  pool <- zoom
+  withRunInIO $ \unliftIO ->
+    withPGTransaction pool $ \conn -> do
+      unliftIO $ runReaderT transaction conn
+
+executeImpl ::
+  (ToRow params, MonadUnliftIO m, MonadLogger m, HasField "pgFormat" tools Tool, Otel.MonadTracer m) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  params ->
+  Transaction m (Label "numberOfRowsAffected" Natural)
+{-# INLINE executeImpl #-}
+executeImpl zoomTools zoomDebugLogDatabaseQueries qry params =
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasSingleParam params)
+    conn <- Transaction ask
+    PG.execute conn qry params
+      & handlePGException tools "execute" qry (Left params)
+      >>= toNumberOfRowsAffected "executeImpl"
+
+executeImpl_ ::
+  (MonadUnliftIO m, MonadLogger m, HasField "pgFormat" tools Tool, Otel.MonadTracer m) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  Transaction m (Label "numberOfRowsAffected" Natural)
+{-# INLINE executeImpl_ #-}
+executeImpl_ zoomTools zoomDebugLogDatabaseQueries qry =
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled @() tools span logDatabaseQueries qry HasNoParams
+    conn <- Transaction ask
+    PG.execute_ conn qry
+      & handlePGException tools "execute_" qry (Left ())
+      >>= toNumberOfRowsAffected "executeImpl_"
+
+executeManyImpl ::
+  (ToRow params, MonadUnliftIO m, MonadLogger m, HasField "pgFormat" tools Tool, Otel.MonadTracer m) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  NonEmpty params ->
+  Transaction m (Label "numberOfRowsAffected" Natural)
+executeManyImpl zoomTools zoomDebugLogDatabaseQueries qry params =
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasMultiParams params)
+    conn <- Transaction ask
+    PG.executeMany conn qry (params & toList)
+      & handlePGException tools "executeMany" qry (Right params)
+      >>= toNumberOfRowsAffected "executeManyImpl"
+
+toNumberOfRowsAffected :: (MonadIO m) => Text -> Int64 -> m (Label "numberOfRowsAffected" Natural)
+toNumberOfRowsAffected functionName i64 =
+  i64
+    & intToNatural
+    & annotate [fmt|{functionName}: postgres returned a negative number of rows affected: {i64}|]
+    -- we throw this directly in IO here, because we donโ€™t want to e.g. have to propagate MonadThrow through user code (itโ€™s an assertion)
+    & unwrapIOError
+    & liftIO
+    <&> label @"numberOfRowsAffected"
+
+executeManyReturningWithImpl ::
+  (ToRow params, MonadUnliftIO m, MonadLogger m, HasField "pgFormat" tools Tool, Otel.MonadTracer m) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  NonEmpty params ->
+  Decoder r ->
+  Transaction m [r]
+{-# INLINE executeManyReturningWithImpl #-}
+executeManyReturningWithImpl zoomTools zoomDebugLogDatabaseQueries qry params (Decoder fromRow) = do
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasMultiParams params)
+    conn <- Transaction ask
+    PG.returningWith fromRow conn qry (params & toList)
+      & handlePGException tools "executeManyReturning" qry (Right params)
+
+foldRowsWithAccImpl ::
+  ( ToRow params,
+    MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool,
+    Otel.MonadTracer m
+  ) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  params ->
+  Decoder row ->
+  a ->
+  (a -> row -> Transaction m a) ->
+  Transaction m a
+{-# INLINE foldRowsWithAccImpl #-}
+foldRowsWithAccImpl zoomTools zoomDebugLogDatabaseQueries qry params (Decoder rowParser) accumulator f = do
+  Otel.inSpan' "Postgres Query (foldRowsWithAcc)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasSingleParam params)
+    conn <- Transaction ask
+    withRunInIO
+      ( \runInIO ->
+          do
+            PG.foldWithOptionsAndParser
+              PG.defaultFoldOptions
+              rowParser
+              conn
+              qry
+              params
+              accumulator
+              (\acc row -> runInIO $ f acc row)
+              & handlePGException tools "fold" qry (Left params)
+              & runInIO
+      )
+
+pgFormatQueryNoParams' ::
+  (MonadIO m, MonadLogger m, HasField "pgFormat" tools Tool) =>
+  tools ->
+  Query ->
+  Transaction m Text
+pgFormatQueryNoParams' tools q =
+  lift $ pgFormatQueryByteString tools q.fromQuery
+
+pgFormatQuery ::
+  (ToRow params, MonadIO m) =>
+  Query ->
+  params ->
+  Transaction m ByteString
+pgFormatQuery qry params = Transaction $ do
+  conn <- ask
+  liftIO $ PG.formatQuery conn qry params
+
+pgFormatQueryMany ::
+  (MonadIO m, ToRow params) =>
+  Query ->
+  NonEmpty params ->
+  Transaction m ByteString
+pgFormatQueryMany qry params = Transaction $ do
+  conn <- ask
+  liftIO $
+    PG.formatMany
+      conn
+      qry
+      ( params
+          -- upstream is partial on empty list, see https://github.com/haskellari/postgresql-simple/issues/129
+          & toList
+      )
+
+queryWithImpl ::
+  ( ToRow params,
+    MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool,
+    Otel.MonadTracer m
+  ) =>
+  m tools ->
+  m DebugLogDatabaseQueries ->
+  Query ->
+  params ->
+  Decoder r ->
+  Transaction m [r]
+{-# INLINE queryWithImpl #-}
+queryWithImpl zoomTools zoomDebugLogDatabaseQueries qry params (Decoder fromRow) = do
+  Otel.inSpan' "Postgres Query (execute)" Otel.defaultSpanArguments $ \span -> do
+    tools <- lift @Transaction zoomTools
+    logDatabaseQueries <- lift @Transaction zoomDebugLogDatabaseQueries
+    traceQueryIfEnabled tools span logDatabaseQueries qry (HasSingleParam params)
+    conn <- Transaction ask
+    PG.queryWith fromRow conn qry params
+      & handlePGException tools "query" qry (Left params)
+
+queryWithImpl_ ::
+  ( MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  m tools ->
+  Query ->
+  Decoder r ->
+  Transaction m [r]
+{-# INLINE queryWithImpl_ #-}
+queryWithImpl_ zoomTools qry (Decoder fromRow) = do
+  tools <- lift @Transaction zoomTools
+  conn <- Transaction ask
+  liftIO (PG.queryWith_ fromRow conn qry)
+    & handlePGException tools "query" qry (Left ())
+
+data SingleRowError = SingleRowError
+  { -- | How many columns were actually returned by the query
+    numberOfRowsReturned :: Int
+  }
+  deriving stock (Show)
+
+instance Exception SingleRowError where
+  displayException (SingleRowError {..}) = [fmt|Single row expected from SQL query result, {numberOfRowsReturned} rows were returned instead."|]
+
+pgFormatQuery' ::
+  ( MonadIO m,
+    ToRow params,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  tools ->
+  Query ->
+  params ->
+  Transaction m Text
+pgFormatQuery' tools q p =
+  pgFormatQuery q p
+    >>= lift . pgFormatQueryByteString tools
+
+pgFormatQueryMany' ::
+  ( MonadIO m,
+    ToRow params,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  tools ->
+  Query ->
+  NonEmpty params ->
+  Transaction m Text
+pgFormatQueryMany' tools q p =
+  pgFormatQueryMany q p
+    >>= lift . pgFormatQueryByteString tools
+
+-- | Read the executable name "pg_format"
+postgresToolsParser :: ToolParserT IO (Label "pgFormat" Tool)
+postgresToolsParser = label @"pgFormat" <$> readTool "pg_format"
+
+pgFormatQueryByteString ::
+  ( MonadIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool
+  ) =>
+  tools ->
+  ByteString ->
+  m Text
+pgFormatQueryByteString tools queryBytes = do
+  do
+    (exitCode, stdout, stderr) <-
+      Process.readProcessWithExitCode
+        tools.pgFormat.toolPath
+        ["-"]
+        (queryBytes & bytesToTextUtf8Lenient & textToString)
+    case exitCode of
+      ExitSuccess -> pure (stdout & stringToText)
+      ExitFailure status -> do
+        logWarn [fmt|pg_format failed with status {status} while formatting the query, using original query string. Is there a syntax error?|]
+        logDebug
+          ( prettyErrorTree
+              ( nestedMultiError
+                  "pg_format output"
+                  ( nestedError "stdout" (singleError (stdout & stringToText & newError))
+                      :| [(nestedError "stderr" (singleError (stderr & stringToText & newError)))]
+                  )
+              )
+          )
+        logDebug [fmt|pg_format stdout: stderr|]
+        pure (queryBytes & bytesToTextUtf8Lenient)
+
+data DebugLogDatabaseQueries
+  = -- | Do not log the database queries
+    DontLogDatabaseQueries
+  | -- | Log the database queries as debug output;
+    LogDatabaseQueries
+  | -- | Log the database queries as debug output and additionally the EXPLAIN output (from the query analyzer, not the actual values after execution cause thatโ€™s a bit harder to do)
+    LogDatabaseQueriesAndExplain
+  deriving stock (Show, Enum, Bounded)
+
+data HasQueryParams param
+  = HasNoParams
+  | HasSingleParam param
+  | HasMultiParams (NonEmpty param)
+
+-- | Log the postgres query depending on the given setting
+traceQueryIfEnabled ::
+  ( ToRow params,
+    MonadUnliftIO m,
+    MonadLogger m,
+    HasField "pgFormat" tools Tool,
+    Otel.MonadTracer m
+  ) =>
+  tools ->
+  Otel.Span ->
+  DebugLogDatabaseQueries ->
+  Query ->
+  HasQueryParams params ->
+  Transaction m ()
+traceQueryIfEnabled tools span logDatabaseQueries qry params = do
+  -- In case we have query logging enabled, we want to do that
+  let formattedQuery = case params of
+        HasNoParams -> pgFormatQueryNoParams' tools qry
+        HasSingleParam p -> pgFormatQuery' tools qry p
+        HasMultiParams ps -> pgFormatQueryMany' tools qry ps
+  let doLog errs =
+        Otel.addAttributes
+          span
+          $ HashMap.fromList
+          $ ( ("_.postgres.query", Otel.toAttribute @Text errs.query)
+                : ( errs.explain
+                      & foldMap
+                        ( \ex ->
+                            [("_.postgres.explain", Otel.toAttribute @Text ex)]
+                        )
+                  )
+            )
+  let doExplain = do
+        q <- formattedQuery
+        Otel.inSpan "Postgres EXPLAIN Query" Otel.defaultSpanArguments $ do
+          queryWithImpl_
+            (pure tools)
+            ( "EXPLAIN "
+                <> (
+                     -- TODO: this is not nice, but the only way to get the `executeMany` form to work with this
+                     -- because we need the query with all elements already interpolated.
+                     Query (q & textToBytesUtf8)
+                   )
+            )
+            (Dec.fromField @Text)
+            <&> Text.intercalate "\n"
+  case logDatabaseQueries of
+    DontLogDatabaseQueries -> pure ()
+    LogDatabaseQueries -> do
+      q <- formattedQuery
+      doLog (T2 (label @"query" q) (label @"explain" Nothing))
+    LogDatabaseQueriesAndExplain -> do
+      q <- formattedQuery
+      -- XXX: stuff like `CREATE SCHEMA` cannot be EXPLAINed, so we should catch exceptions here
+      -- and just ignore anything that errors (if it errors because of a problem with the query, it would have been caught by the query itself.
+      ex <- doExplain
+      doLog (T2 (label @"query" q) (label @"explain" (Just ex)))
+
+instance (ToField t1) => ToRow (Label l1 t1) where
+  toRow t2 = toRow $ PG.Only $ getField @l1 t2
+
+instance (ToField t1, ToField t2) => ToRow (T2 l1 t1 l2 t2) where
+  toRow t2 = toRow (getField @l1 t2, getField @l2 t2)
+
+instance (ToField t1, ToField t2, ToField t3) => ToRow (T3 l1 t1 l2 t2 l3 t3) where
+  toRow t3 = toRow (getField @l1 t3, getField @l2 t3, getField @l3 t3)
diff --git a/users/Profpatsch/my-prelude/src/Pretty.hs b/users/Profpatsch/my-prelude/src/Pretty.hs
new file mode 100644
index 0000000000..d9d4ce132b
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Pretty.hs
@@ -0,0 +1,108 @@
+module Pretty
+  ( -- * Pretty printing for error messages
+    Err,
+    showPretty,
+    showPrettyJson,
+    showedStringPretty,
+    printPretty,
+    printShowedStringPretty,
+    -- constructors hidden
+    prettyErrs,
+    message,
+    messageString,
+    pretty,
+    prettyString,
+    hscolour',
+  )
+where
+
+import Data.Aeson qualified as Json
+import Data.Aeson.Encode.Pretty qualified as Aeson.Pretty
+import Data.List qualified as List
+import Data.Text.Lazy.Builder qualified as Text.Builder
+import Language.Haskell.HsColour
+  ( Output (TTYg),
+    hscolour,
+  )
+import Language.Haskell.HsColour.ANSI (TerminalType (..))
+import Language.Haskell.HsColour.Colourise
+  ( defaultColourPrefs,
+  )
+import PossehlAnalyticsPrelude
+import System.Console.ANSI (setSGRCode)
+import System.Console.ANSI.Types
+  ( Color (Red),
+    ColorIntensity (Dull),
+    ConsoleLayer (Foreground),
+    SGR (Reset, SetColor),
+  )
+import Text.Nicify (nicify)
+
+-- | Print any 'Show'able type to stderr, formatted nicely and in color. Very helpful for debugging.
+printPretty :: (Show a) => a -> IO ()
+printPretty a =
+  a & showPretty & putStderrLn
+
+showPretty :: (Show a) => a -> Text
+showPretty a = a & pretty & (: []) & prettyErrs & stringToText
+
+-- | Pretty-print a string that was produced by `show` to stderr, formatted nicely and in color.
+printShowedStringPretty :: String -> IO ()
+printShowedStringPretty s = s & showedStringPretty & putStderrLn
+
+-- | Pretty-print a string that was produced by `show`
+showedStringPretty :: String -> Text
+showedStringPretty s = s & ErrPrettyString & (: []) & prettyErrs & stringToText
+
+showPrettyJson :: Json.Value -> Text
+showPrettyJson val =
+  val
+    & Aeson.Pretty.encodePrettyToTextBuilder
+    & Text.Builder.toLazyText
+    & toStrict
+
+-- | Display a list of 'Err's as a colored error message
+-- and abort the test.
+prettyErrs :: [Err] -> String
+prettyErrs errs = res
+  where
+    res = List.intercalate "\n" $ map one errs
+    one = \case
+      ErrMsg s -> color Red s
+      ErrPrettyString s -> prettyShowString s
+    -- Pretty print a String that was produced by 'show'
+    prettyShowString :: String -> String
+    prettyShowString = hscolour' . nicify
+
+-- | Small DSL for pretty-printing errors
+data Err
+  = -- | Message to display in the error
+    ErrMsg String
+  | -- | Pretty print a String that was produced by 'show'
+    ErrPrettyString String
+
+-- | Plain message to display, as 'Text'
+message :: Text -> Err
+message = ErrMsg . textToString
+
+-- | Plain message to display, as 'String'
+messageString :: String -> Err
+messageString = ErrMsg
+
+-- | Any 'Show'able to pretty print
+pretty :: (Show a) => a -> Err
+pretty x = ErrPrettyString $ show x
+
+-- | Pretty print a String that was produced by 'show'
+prettyString :: String -> Err
+prettyString s = ErrPrettyString s
+
+-- Prettifying Helpers, mostly stolen from
+-- https://hackage.haskell.org/package/hspec-expectations-pretty-diff-0.7.2.5/docs/src/Test.Hspec.Expectations.Pretty.html#prettyColor
+
+hscolour' :: String -> String
+hscolour' =
+  hscolour (TTYg Ansi16Colour) defaultColourPrefs False False "" False
+
+color :: Color -> String -> String
+color c s = setSGRCode [SetColor Foreground Dull c] ++ s ++ setSGRCode [Reset]
diff --git a/users/Profpatsch/my-prelude/src/Seconds.hs b/users/Profpatsch/my-prelude/src/Seconds.hs
new file mode 100644
index 0000000000..8d05f30be8
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Seconds.hs
@@ -0,0 +1,55 @@
+module Seconds where
+
+import Data.Aeson (FromJSON)
+import Data.Aeson qualified as Json
+import Data.Aeson.Types (FromJSON (parseJSON))
+import Data.Scientific
+import Data.Time (NominalDiffTime)
+import FieldParser
+import FieldParser qualified as Field
+import GHC.Natural (naturalToInteger)
+import PossehlAnalyticsPrelude
+
+-- | A natural number of seconds.
+newtype Seconds = Seconds {unSeconds :: Natural}
+  deriving stock (Eq, Show)
+
+-- | Parse a decimal number as a number of seconds
+textToSeconds :: FieldParser Text Seconds
+textToSeconds = Seconds <$> Field.decimalNatural
+
+scientificToSeconds :: FieldParser Scientific Seconds
+scientificToSeconds =
+  ( Field.boundedScientificIntegral @Int "Number of seconds"
+      >>> Field.integralToNatural
+  )
+    & rmap Seconds
+
+-- Microseconds, represented internally with a 64 bit Int
+newtype MicrosecondsInt = MicrosecondsInt {unMicrosecondsInt :: Int}
+  deriving stock (Eq, Show)
+
+-- | Try to fit a number of seconds into a MicrosecondsInt
+secondsToMicrosecondsInt :: FieldParser Seconds MicrosecondsInt
+secondsToMicrosecondsInt =
+  lmap
+    (\sec -> naturalToInteger sec.unSeconds * 1_000_000)
+    (Field.bounded "Could not fit into an Int after multiplying with 1_000_000 (seconds to microseconds)")
+    & rmap MicrosecondsInt
+
+secondsToNominalDiffTime :: Seconds -> NominalDiffTime
+secondsToNominalDiffTime sec =
+  sec.unSeconds
+    & naturalToInteger
+    & fromInteger @NominalDiffTime
+
+instance FromJSON Seconds where
+  parseJSON = Field.toParseJSON jsonNumberToSeconds
+
+-- | Parse a json number as a number of seconds.
+jsonNumberToSeconds :: FieldParser' Error Json.Value Seconds
+jsonNumberToSeconds = Field.jsonNumber >>> scientificToSeconds
+
+-- | Return the number of seconds in a week
+secondsInAWeek :: Seconds
+secondsInAWeek = Seconds (3600 * 24 * 7)
diff --git a/users/Profpatsch/my-prelude/src/Test.hs b/users/Profpatsch/my-prelude/src/Test.hs
new file mode 100644
index 0000000000..862ee16c25
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Test.hs
@@ -0,0 +1,115 @@
+{-# LANGUAGE LambdaCase #-}
+
+{- Generate Test suites.
+
+Restricted version of hspec, introduction: http://hspec.github.io/getting-started.html
+-}
+module Test
+  ( Spec,
+    runTest,
+    testMain,
+
+    -- * Structure
+    describe,
+    it,
+
+    -- * Expectations
+    Expectation,
+    testOk,
+    testErr,
+    shouldBe,
+    shouldNotBe,
+    shouldSatisfy,
+    shouldNotSatisfy,
+
+    -- * Setup & Teardown (hooks http://hspec.github.io/writing-specs.html#using-hooks)
+    before,
+    before_,
+    beforeWith,
+    beforeAll,
+    beforeAll_,
+    beforeAllWith,
+    after,
+    after_,
+    afterAll,
+    afterAll_,
+    around,
+    around_,
+    aroundWith,
+    aroundAll,
+    aroundAllWith,
+
+    -- * Common helpful predicates (use with 'shouldSatisfy')
+    isRight,
+    isLeft,
+
+    -- * Pretty printing of errors
+    errColored,
+    module Pretty,
+  )
+where
+
+-- export more expectations if needed
+
+import Data.Either
+  ( isLeft,
+    isRight,
+  )
+import Pretty
+import Test.Hspec
+  ( Expectation,
+    HasCallStack,
+    Spec,
+    after,
+    afterAll,
+    afterAll_,
+    after_,
+    around,
+    aroundAll,
+    aroundAllWith,
+    aroundWith,
+    around_,
+    before,
+    beforeAll,
+    beforeAllWith,
+    beforeAll_,
+    beforeWith,
+    before_,
+    describe,
+    hspec,
+    it,
+  )
+import Test.Hspec.Expectations.Pretty
+  ( expectationFailure,
+    shouldBe,
+    shouldNotBe,
+    shouldNotSatisfy,
+    shouldSatisfy,
+  )
+
+-- | Run a test directly (e.g. from the repl)
+runTest :: Spec -> IO ()
+runTest = hspec
+
+-- | Run a testsuite
+testMain ::
+  -- | Name of the test suite
+  String ->
+  -- | The tests in this test module
+  Spec ->
+  IO ()
+testMain testSuiteName tests = hspec $ describe testSuiteName tests
+
+-- | test successful
+testOk :: Expectation
+testOk = pure ()
+
+-- | Abort the test with an error message.
+-- If you want to display a Haskell type, use `errColored`.
+testErr :: HasCallStack => String -> Expectation
+testErr = expectationFailure
+
+-- | Display a list of 'Err's as a colored error message
+-- and abort the test.
+errColored :: [Pretty.Err] -> Expectation
+errColored = testErr . Pretty.prettyErrs
diff --git a/users/Profpatsch/my-prelude/src/Tool.hs b/users/Profpatsch/my-prelude/src/Tool.hs
new file mode 100644
index 0000000000..b773f4444e
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/Tool.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Tool where
+
+import Data.Error.Tree
+import Label
+import PossehlAnalyticsPrelude
+import System.Environment qualified as Env
+import System.Exit qualified as Exit
+import System.FilePath ((</>))
+import System.Posix qualified as Posix
+import ValidationParseT
+
+data Tool = Tool
+  { -- | absolute path to the executable
+    toolPath :: FilePath
+  }
+  deriving stock (Show)
+
+-- | Reads all tools from the @toolsEnvVar@ variable or aborts.
+readTools ::
+  Label "toolsEnvVar" Text ->
+  -- | Parser for Tools we bring with us at build time.
+  --
+  -- These are executables that we need available, and that we have to ship with the distribution of @pa-cli@.
+  ToolParserT IO tools ->
+  IO tools
+readTools env toolParser =
+  Env.lookupEnv (env.toolsEnvVar & textToString) >>= \case
+    Nothing -> do
+      Exit.die [fmt|Please set {env.toolsEnvVar} to a directory with all tools we need (see `Tools` in the code).|]
+    Just toolsDir ->
+      (Posix.fileExist toolsDir & ifTrueOrErr () [fmt|{env.toolsEnvVar} directory does not exist: {toolsDir}|])
+        & thenValidateM
+          ( \() ->
+              (Posix.getFileStatus toolsDir <&> Posix.isDirectory)
+                & ifTrueOrErr () [fmt|{env.toolsEnvVar} does not point to a directory: {toolsDir}|]
+          )
+        & thenValidateM
+          (\() -> toolParser.unToolParser toolsDir)
+        <&> first (errorTree [fmt|Could not find all tools in {env.toolsEnvVar}|])
+        >>= \case
+          Failure err -> Exit.die (err & prettyErrorTree & textToString)
+          Success t -> pure t
+
+newtype ToolParserT m a = ToolParserT
+  { unToolParser ::
+      FilePath ->
+      m (Validation (NonEmpty Error) a)
+  }
+  deriving
+    (Functor, Applicative)
+    via (ValidationParseT FilePath m)
+
+-- | Given a file path and the name of the tool executable, see whether it is an executable and return its full path.
+readTool :: Text -> ToolParserT IO Tool
+readTool exeName = ToolParserT $ \toolDir -> do
+  let toolPath :: FilePath = toolDir </> (exeName & textToString)
+  let read' = True
+  let write = False
+  let exec = True
+  Posix.fileExist toolPath
+    & ifTrueOrErr () [fmt|Tool does not exist: {toolPath}|]
+    & thenValidateM
+      ( \() ->
+          Posix.fileAccess toolPath read' write exec
+            & ifTrueOrErr (Tool {..}) [fmt|Tool is not readable/executable: {toolPath}|]
+      )
+
+-- | helper
+ifTrueOrErr :: (Functor f) => a -> Text -> f Bool -> f (Validation (NonEmpty Error) a)
+ifTrueOrErr true err io =
+  io <&> \case
+    True -> Success true
+    False -> Failure $ singleton $ newError err
diff --git a/users/Profpatsch/my-prelude/src/ValidationParseT.hs b/users/Profpatsch/my-prelude/src/ValidationParseT.hs
new file mode 100644
index 0000000000..593b7ebf39
--- /dev/null
+++ b/users/Profpatsch/my-prelude/src/ValidationParseT.hs
@@ -0,0 +1,16 @@
+module ValidationParseT where
+
+import Control.Selective (Selective)
+import Data.Functor.Compose (Compose (..))
+import PossehlAnalyticsPrelude
+
+-- | A simple way to create an Applicative parser that parses from some environment.
+--
+-- Use with DerivingVia. Grep codebase for examples.
+newtype ValidationParseT env m a = ValidationParseT {unValidationParseT :: env -> m (Validation (NonEmpty Error) a)}
+  deriving
+    (Functor, Applicative, Selective)
+    via ( Compose
+            ((->) env)
+            (Compose m (Validation (NonEmpty Error)))
+        )
diff --git a/users/Profpatsch/my-webstuff/default.nix b/users/Profpatsch/my-webstuff/default.nix
new file mode 100644
index 0000000000..0067235be2
--- /dev/null
+++ b/users/Profpatsch/my-webstuff/default.nix
@@ -0,0 +1,27 @@
+{ depot, pkgs, lib, ... }:
+
+pkgs.haskellPackages.mkDerivation {
+  pname = "my-webstuff";
+  version = "0.0.1-unreleased";
+
+  src = depot.users.Profpatsch.exactSource ./. [
+    ./my-webstuff.cabal
+    ./src/Multipart2.hs
+  ];
+
+  isLibrary = true;
+
+  libraryHaskellDepends = [
+    depot.users.Profpatsch.my-prelude
+    pkgs.haskellPackages.dlist
+    pkgs.haskellPackages.monad-logger
+    pkgs.haskellPackages.pa-error-tree
+    pkgs.haskellPackages.pa-field-parser
+    pkgs.haskellPackages.pa-prelude
+    pkgs.haskellPackages.selective
+    pkgs.haskellPackages.wai-extra
+  ];
+
+  license = lib.licenses.mit;
+
+}
diff --git a/users/Profpatsch/my-webstuff/my-webstuff.cabal b/users/Profpatsch/my-webstuff/my-webstuff.cabal
new file mode 100644
index 0000000000..fb42d9f6a5
--- /dev/null
+++ b/users/Profpatsch/my-webstuff/my-webstuff.cabal
@@ -0,0 +1,72 @@
+cabal-version:      3.0
+name:               my-webstuff
+version:            0.0.1.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+    hs-source-dirs: src
+    exposed-modules:
+      Multipart2
+
+    build-depends:
+       base >=4.15 && <5
+     , my-prelude
+     , pa-prelude
+     , pa-label
+     , pa-error-tree
+     , pa-field-parser
+     , bytestring
+     , monad-logger
+     , dlist
+     , selective
+     , wai
+     , wai-extra
diff --git a/users/Profpatsch/my-webstuff/src/Multipart2.hs b/users/Profpatsch/my-webstuff/src/Multipart2.hs
new file mode 100644
index 0000000000..5c283a3c1b
--- /dev/null
+++ b/users/Profpatsch/my-webstuff/src/Multipart2.hs
@@ -0,0 +1,220 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Multipart2 where
+
+import Control.Monad.Logger (MonadLogger)
+import Control.Selective (Selective)
+import Data.ByteString.Lazy qualified as Lazy
+import Data.DList (DList)
+import Data.DList qualified as DList
+import Data.Error.Tree
+import Data.Functor.Compose
+import Data.List qualified as List
+import FieldParser
+import Label
+import Network.Wai qualified as Wai
+import Network.Wai.Parse qualified as Wai
+import PossehlAnalyticsPrelude
+import ValidationParseT
+
+data FormFields = FormFields
+  { inputs :: [Wai.Param],
+    files :: [MultipartFile Lazy.ByteString]
+  }
+
+-- | A parser for a HTTP multipart form (a form sent by the browser)
+newtype MultipartParseT backend m a = MultipartParseT
+  { unMultipartParseT ::
+      FormFields ->
+      m (Validation (NonEmpty Error) a)
+  }
+  deriving
+    (Functor, Applicative, Selective)
+    via (ValidationParseT FormFields m)
+
+-- | After parsing a form, either we get the result or a list of form fields that failed
+newtype FormValidation a
+  = FormValidation
+      (DList FormValidationResult, Maybe a)
+  deriving (Functor, Applicative, Selective) via (Compose ((,) (DList FormValidationResult)) Maybe)
+  deriving stock (Show)
+
+data FormValidationResult = FormValidationResult
+  { hasError :: Maybe Error,
+    formFieldName :: ByteString,
+    originalValue :: ByteString
+  }
+  deriving stock (Show)
+
+mkFormValidationResult ::
+  ( HasField "formFieldName" form ByteString,
+    HasField "originalValue" form ByteString
+  ) =>
+  form ->
+  Maybe Error ->
+  FormValidationResult
+mkFormValidationResult form err =
+  FormValidationResult
+    { hasError = err,
+      formFieldName = form.formFieldName,
+      originalValue = form.originalValue
+    }
+
+eitherToFormValidation ::
+  ( HasField "formFieldName" form ByteString,
+    HasField "originalValue" form ByteString
+  ) =>
+  form ->
+  Either Error a ->
+  FormValidation a
+eitherToFormValidation form = \case
+  Left err ->
+    FormValidation $ (DList.singleton $ mkFormValidationResult form (Just err), Nothing)
+  Right a ->
+    FormValidation $ ((DList.singleton $ mkFormValidationResult form Nothing), Just a)
+
+failFormValidation ::
+  ( HasField "formFieldName" form ByteString,
+    HasField "originalValue" form ByteString
+  ) =>
+  form ->
+  Error ->
+  FormValidation a
+failFormValidation form err =
+  FormValidation (DList.singleton $ mkFormValidationResult form (Just err), Nothing)
+
+-- | Parse the multipart form or throw a user error with a descriptive error message.
+parseMultipartOrThrow ::
+  (MonadLogger m, MonadIO m) =>
+  (ErrorTree -> m a) ->
+  MultipartParseT backend m a ->
+  Wai.Request ->
+  m a
+parseMultipartOrThrow throwF parser req = do
+  -- TODO: this throws all errors with `error`, so leads to 500 on bad input โ€ฆ
+  formFields <-
+    liftIO $
+      Wai.parseRequestBodyEx
+        Wai.defaultParseRequestBodyOptions
+        Wai.lbsBackEnd
+        req
+  parser.unMultipartParseT
+    FormFields
+      { inputs = fst formFields,
+        files = map fileDataToMultipartFile $ snd formFields
+      }
+    >>= \case
+      Failure errs -> throwF (errorTree "Cannot parse the multipart form" errs)
+      Success a -> pure a
+
+-- | Parse the field out of the multipart message
+field :: (Applicative m) => ByteString -> FieldParser ByteString a -> MultipartParseT backend m a
+field fieldName fieldParser = MultipartParseT $ \mp ->
+  mp.inputs
+    & findMaybe (\input -> if fst input == fieldName then Just (snd input) else Nothing)
+    & annotate [fmt|Field "{fieldName}" does not exist in the multipart form|]
+    >>= runFieldParser fieldParser
+    & eitherToListValidation
+    & pure
+
+-- | Parse the field out of the multipart message
+field' :: (Applicative m) => ByteString -> FieldParser ByteString a -> MultipartParseT backend m (FormValidation a)
+field' fieldName fieldParser = MultipartParseT $ \mp ->
+  mp.inputs
+    & findMaybe (\input -> if fst input == fieldName then Just $ snd input else Nothing)
+    & annotate [fmt|Field "{fieldName}" does not exist in the multipart form|]
+    <&> ( \originalValue ->
+            originalValue
+              & runFieldParser fieldParser
+              & eitherToFormValidation
+                ( T2
+                    (label @"formFieldName" fieldName)
+                    (label @"originalValue" originalValue)
+                )
+        )
+    & eitherToListValidation
+    & pure
+
+-- | Parse the field out of the multipart message, and into a 'Label' of the given name.
+fieldLabel :: forall lbl backend m a. (Applicative m) => ByteString -> FieldParser ByteString a -> MultipartParseT backend m (Label lbl a)
+fieldLabel fieldName fieldParser = label @lbl <$> field fieldName fieldParser
+
+-- | Parse the field out of the multipart message, and into a 'Label' of the given name.
+fieldLabel' :: forall lbl backend m a. (Applicative m) => ByteString -> FieldParser ByteString a -> MultipartParseT backend m (FormValidation (Label lbl a))
+fieldLabel' fieldName fieldParser = fmap (label @lbl) <$> field' fieldName fieldParser
+
+-- | parse all fields out of the multipart message, with the same parser
+allFields :: (Applicative m) => FieldParser (T2 "key" ByteString "value" ByteString) b -> MultipartParseT backend m [b]
+allFields fieldParser = MultipartParseT $ \mp ->
+  mp.inputs
+    <&> tupToT2 @"key" @"value"
+    & traverseValidate (runFieldParser fieldParser)
+    & eitherToValidation
+    & pure
+
+tupToT2 :: forall l1 l2 t1 t2. (t1, t2) -> T2 l1 t1 l2 t2
+tupToT2 (a, b) = T2 (label a) (label b)
+
+-- | Parse a file by name out of the multipart message
+file ::
+  (Applicative m) =>
+  ByteString ->
+  MultipartParseT backend m (MultipartFile Lazy.ByteString)
+file fieldName = MultipartParseT $ \mp ->
+  mp.files
+    & List.find (\input -> input.multipartNameAttribute == fieldName)
+    & annotate [fmt|File "{fieldName}" does not exist in the multipart form|]
+    & ( \case
+          Left err -> Failure (singleton err)
+          Right filePath -> Success filePath
+      )
+    & pure
+
+-- | Return all files from the multipart message
+allFiles ::
+  (Applicative m) =>
+  MultipartParseT backend m [MultipartFile Lazy.ByteString]
+allFiles = MultipartParseT $ \mp -> do
+  pure $ Success $ mp.files
+
+-- | Ensure there is exactly one file and return it (ignoring the field name)
+exactlyOneFile ::
+  (Applicative m) =>
+  MultipartParseT backend m (MultipartFile Lazy.ByteString)
+exactlyOneFile = MultipartParseT $ \mp ->
+  mp.files
+    & \case
+      [] -> pure $ failParse "Expected to receive a file, but the multipart form did not contain any files"
+      [file_] -> pure $ Success file_
+      more -> pure $ failParse [fmt|Expected to receive exactly one file, but the multipart form contained {List.length more} files|]
+  where
+    -- \| Fail to parse the multipart form with the given error message.
+    failParse :: Text -> Validation (NonEmpty Error) a
+    failParse = Failure . singleton . newError
+
+newtype GetFileContent backend m content = GetFileContent
+  {unGetFileContent :: (Wai.Request -> m (Either Error content))}
+
+-- | A file field in a multipart message.
+data MultipartFile content = MultipartFile
+  { -- | @name@ attribute of the corresponding HTML @\<input\>@
+    multipartNameAttribute :: ByteString,
+    -- | name of the file on the client's disk
+    fileNameOnDisk :: ByteString,
+    -- | MIME type for the file
+    fileMimeType :: ByteString,
+    -- | Content of the file
+    content :: content
+  }
+
+-- | Convert the multipart library struct of a multipart file to our own.
+fileDataToMultipartFile ::
+  Wai.File Lazy.ByteString ->
+  (MultipartFile Lazy.ByteString)
+fileDataToMultipartFile (multipartNameAttribute, file_) = do
+  MultipartFile
+    { multipartNameAttribute,
+      fileNameOnDisk = file_.fileName,
+      fileMimeType = file_.fileContentType,
+      content = file_.fileContent
+    }
diff --git a/users/Profpatsch/my-xmonad/Xmonad.hs b/users/Profpatsch/my-xmonad/Xmonad.hs
new file mode 100644
index 0000000000..bb727ac2f1
--- /dev/null
+++ b/users/Profpatsch/my-xmonad/Xmonad.hs
@@ -0,0 +1,127 @@
+module Main where
+
+import Data.Function ((&))
+import XMonad
+import XMonad qualified as Xmonad
+import XMonad.Hooks.EwmhDesktops (ewmh)
+import XMonad.Layout.Decoration
+import XMonad.Layout.MultiToggle
+import XMonad.Layout.MultiToggle.Instances (StdTransformers (..))
+import XMonad.Layout.Tabbed (TabbedDecoration)
+import XMonad.Layout.Tabbed qualified as Tabbed
+import XMonad.StackSet qualified as StackSet
+import XMonad.Util.Cursor (setDefaultCursor)
+import XMonad.Util.EZConfig (additionalKeys, additionalKeysP, removeKeysP)
+
+data Mode = Normal | Presentation
+
+main :: IO ()
+main = do
+  let config = ewmh myConfig
+  dirs <- Xmonad.getDirectories
+  Xmonad.launch config dirs
+
+myConfig ::
+  XConfig
+    ( MultiToggle
+        ( HCons
+            StdTransformers
+            XMonad.Layout.MultiToggle.EOT
+        )
+        ( ModifiedLayout
+            ( Decoration
+                TabbedDecoration
+                DefaultShrinker
+            )
+            Tall
+        )
+    )
+myConfig =
+  conf
+    { modMask = modKey,
+      terminal = term Normal,
+      focusedBorderColor = "#859900",
+      layoutHook = layout,
+      startupHook = setDefaultCursor xC_heart,
+      workspaces = workspaceNames
+    }
+    `additionalKeysP` ( [
+                          -- fullscreen
+                          ("M-e", sendMessage $ Toggle NBFULL),
+                          -- i3-like keybindings, because Iโ€™m spoiled
+                          ("M-S-x", kill),
+                          -- exchange M-Ret and M-S-Ret
+                          ("M-<Return>", spawn $ term Normal),
+                          ("C-M-<Return>", spawn $ term Presentation),
+                          ("M-S-<Return>", windows StackSet.swapMaster)
+                          -- open simple exec dmenu
+                        ]
+                          ++
+                          -- something something workspaces
+                          [ (otherModMasks ++ "M-" ++ [key], action tag)
+                            | (tag, key) <- zip workspaceNames "123456789",
+                              (otherModMasks, action) <-
+                                [ ("", windows . StackSet.greedyView),
+                                  ("S-", windows . StackSet.shift)
+                                ]
+                          ]
+                          ++
+                          -- mod-{w,e,r} %! Switch to physical/Xinerama screens 1, 2, or 3
+                          -- mod-shift-{w,e,r} %! Move client to screen 1, 2, or 3
+                          [ ("M-v", focusToScreen 0),
+                            -- , ("M-l", focusToScreen 1)
+                            ("M-c", focusToScreen 2),
+                            ("M-S-v", windowToScreen 0),
+                            ("M-S-l", windowToScreen 1),
+                            ("M-S-c", windowToScreen 2)
+                          ]
+                          -- ((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))
+                          --   | (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]
+                          --    , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]
+                      )
+    `additionalKeys`
+    -- arrow keys should move as well (hjkl blindness)
+    [ ((modKey, xK_Up), windows StackSet.focusUp),
+      ((modKey, xK_Down), windows StackSet.focusDown)
+    ]
+    `removeKeysP` [
+                    -- previous kill command
+                    "M-S-c",
+                    -- It is way to easy to kill everything by default
+                    "M-S-q",
+                    -- no idea, I want to use it for Mozc
+                    "M-n"
+                  ]
+  where
+    conf = def
+    workspaceNames = conf & workspaces
+    modKey = mod4Mask
+    -- TODO: meh
+    term :: Mode -> String
+    -- TODO: get terminal-emulator from the system config (currently alacritty)
+    term Normal = "terminal-emulator"
+    term Presentation = "notify-send TODO: currently not terminal presentation mode implemented" -- "terminal- -u ~/.config/lilyterm/pres.conf"
+    toScreen with _number = screenWorkspace 0 >>= \ws -> whenJust ws (windows . with)
+    focusToScreen = toScreen StackSet.view
+    windowToScreen = toScreen StackSet.shift
+
+-- copied from Xmonad.Config
+layout ::
+  MultiToggle
+    (HCons StdTransformers EOT)
+    (ModifiedLayout (Decoration TabbedDecoration DefaultShrinker) Tall)
+    Window
+layout =
+  tiled
+    & Tabbed.addTabsBottom Tabbed.shrinkText def
+    & toggleFullscreen
+  where
+    -- default tiling algorithm partitions the screen into two panes
+    tiled = Tall nmaster delta ratio
+    -- The default number of windows in the master pane
+    nmaster = 1
+    -- Default proportion of screen occupied by master pane
+    ratio = 1 / 2
+    -- Percent of screen to increment by when resizing panes
+    delta = 3 / 100
+    toggleFullscreen = mkToggle1 NBFULL
diff --git a/users/Profpatsch/my-xmonad/default.nix b/users/Profpatsch/my-xmonad/default.nix
new file mode 100644
index 0000000000..708d50e960
--- /dev/null
+++ b/users/Profpatsch/my-xmonad/default.nix
@@ -0,0 +1,25 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  #   bins = depot.nix.getBins pkgs.sqlite ["sqlite3"];
+
+  my-xmonad = pkgs.haskellPackages.mkDerivation {
+    pname = "my-xmonad";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./my-xmonad.cabal
+      ./Xmonad.hs
+    ];
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.xmonad-contrib
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+in
+my-xmonad
diff --git a/users/Profpatsch/my-xmonad/my-xmonad.cabal b/users/Profpatsch/my-xmonad/my-xmonad.cabal
new file mode 100644
index 0000000000..175c6c1633
--- /dev/null
+++ b/users/Profpatsch/my-xmonad/my-xmonad.cabal
@@ -0,0 +1,62 @@
+cabal-version:      3.0
+name:               my-xmonad
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+executable xmonad
+    import: common-options
+
+    main-is: Xmonad.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        xmonad,
+        xmonad-contrib
diff --git a/users/Profpatsch/netencode/Netencode.hs b/users/Profpatsch/netencode/Netencode.hs
new file mode 100644
index 0000000000..ca93ab2fef
--- /dev/null
+++ b/users/Profpatsch/netencode/Netencode.hs
@@ -0,0 +1,433 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Netencode where
+
+import Control.Applicative (many)
+import Data.Attoparsec.ByteString qualified as Atto
+import Data.Attoparsec.ByteString.Char8 qualified as Atto.Char
+import Data.ByteString qualified as ByteString
+import Data.ByteString.Builder (Builder)
+import Data.ByteString.Builder qualified as Builder
+import Data.ByteString.Lazy qualified as ByteString.Lazy
+import Data.Fix (Fix (Fix))
+import Data.Fix qualified as Fix
+import Data.Functor.Classes (Eq1 (liftEq))
+import Data.Int (Int16, Int32, Int64, Int8)
+import Data.Map.NonEmpty (NEMap)
+import Data.Map.NonEmpty qualified as NEMap
+import Data.Semigroup qualified as Semi
+import Data.String (IsString)
+import Data.Word (Word16, Word32, Word64)
+import GHC.Exts (fromString)
+import Hedgehog qualified as Hedge
+import Hedgehog.Gen qualified as Gen
+import Hedgehog.Range qualified as Range
+import PossehlAnalyticsPrelude
+import Text.Show.Deriving
+import Prelude hiding (sum)
+
+-- | Netencode type base functor.
+--
+-- Recursive elements have a @rec@.
+data TF rec
+  = -- | Unit value
+    Unit
+  | -- | Boolean (2^1)
+    N1 Bool
+  | -- | Byte (2^3)
+    N3 Word8
+  | -- | 64-bit Natural (2^6)
+    N6 Word64
+  | -- | 64-bit Integer (2^6)
+    I6 Int64
+  | -- | Unicode Text
+    Text Text
+  | -- | Arbitrary Bytestring
+    Bytes ByteString
+  | -- | A constructor of a(n open) Sum
+    Sum (Tag Text rec)
+  | -- | Record
+    Record (NEMap Text rec)
+  | -- | List
+    List [rec]
+  deriving stock (Show, Eq, Functor)
+
+instance Eq1 TF where
+  liftEq _ Unit Unit = True
+  liftEq _ (N1 b) (N1 b') = b == b'
+  liftEq _ (N3 w8) (N3 w8') = w8 == w8'
+  liftEq _ (N6 w64) (N6 w64') = w64 == w64'
+  liftEq _ (I6 i64) (I6 i64') = i64 == i64'
+  liftEq _ (Text t) (Text t') = t == t'
+  liftEq _ (Bytes b) (Bytes b') = b == b'
+  liftEq eq (Sum t) (Sum t') = eq (t.tagVal) (t'.tagVal)
+  liftEq eq (Record m) (Record m') = liftEq eq m m'
+  liftEq eq (List xs) (List xs') = liftEq eq xs xs'
+  liftEq _ _ _ = False
+
+-- | A tagged value
+data Tag tag val = Tag
+  { tagTag :: tag,
+    tagVal :: val
+  }
+  deriving stock (Show, Eq, Functor)
+
+$(Text.Show.Deriving.deriveShow1 ''Tag)
+$(Text.Show.Deriving.deriveShow1 ''TF)
+
+-- | The Netencode type
+newtype T = T {unT :: Fix TF}
+  deriving stock (Eq, Show)
+
+-- | Create a unit
+unit :: T
+unit = T $ Fix Unit
+
+-- | Create a boolean
+n1 :: Bool -> T
+n1 = T . Fix . N1
+
+-- | Create a byte
+n3 :: Word8 -> T
+n3 = T . Fix . N3
+
+-- | Create a 64-bit natural
+n6 :: Word64 -> T
+n6 = T . Fix . N6
+
+-- | Create a 64-bit integer
+i6 :: Int64 -> T
+i6 = T . Fix . I6
+
+-- | Create a UTF-8 unicode text
+text :: Text -> T
+text = T . Fix . Text
+
+-- | Create an arbitrary bytestring
+bytes :: ByteString -> T
+bytes = T . Fix . Bytes
+
+-- | Create a tagged value from a tag name and a value
+tag :: Text -> T -> T
+tag key val = T $ Fix $ Sum $ coerce @(Tag Text T) @(Tag Text (Fix TF)) $ Tag key val
+
+-- | Create a record from a non-empty map
+record :: NEMap Text T -> T
+record = T . Fix . Record . coerce @(NEMap Text T) @(NEMap Text (Fix TF))
+
+-- | Create a list
+list :: [T] -> T
+list = T . Fix . List . coerce @[T] @([Fix TF])
+
+-- | Stable encoding of a netencode value. Record keys will be sorted lexicographically ascending.
+netencodeEncodeStable :: T -> Builder
+netencodeEncodeStable (T fix) = Fix.foldFix (netencodeEncodeStableF id) fix
+
+-- | Stable encoding of a netencode functor value. Record keys will be sorted lexicographically ascending.
+--
+-- The given function is used for encoding the recursive values.
+netencodeEncodeStableF :: (rec -> Builder) -> TF rec -> Builder
+netencodeEncodeStableF inner tf = builder go
+  where
+    -- TODO: directly pass in BL?
+    innerBL = fromBuilder . inner
+    go = case tf of
+      Unit -> "u,"
+      N1 False -> "n1:0,"
+      N1 True -> "n1:1,"
+      N3 w8 -> "n3:" <> fromBuilder (Builder.word8Dec w8) <> ","
+      N6 w64 -> "n6:" <> fromBuilder (Builder.word64Dec w64) <> ","
+      I6 i64 -> "i6:" <> fromBuilder (Builder.int64Dec i64) <> ","
+      Text t ->
+        let b = fromText t
+         in "t" <> builderLenDec b <> ":" <> b <> ","
+      Bytes b -> "b" <> builderLenDec (fromByteString b) <> ":" <> fromByteString b <> ","
+      Sum (Tag key val) -> encTag key val
+      Record m ->
+        -- NEMap uses Map internally, and that folds in lexicographic ascending order over the key.
+        -- Since these are `Text` in our case, this is stable.
+        let mBuilder = m & NEMap.foldMapWithKey encTag
+         in "{" <> builderLenDec mBuilder <> ":" <> mBuilder <> "}"
+      List xs ->
+        let xsBuilder = xs <&> innerBL & mconcat
+         in "[" <> builderLenDec xsBuilder <> ":" <> xsBuilder <> "]"
+      where
+        encTag key val =
+          let bKey = fromText key
+           in "<" <> builderLenDec bKey <> ":" <> bKey <> "|" <> innerBL val
+
+-- | A builder that knows its own size in bytes
+newtype BL = BL (Builder, Semi.Sum Natural)
+  deriving newtype (Monoid, Semigroup)
+
+instance IsString BL where
+  fromString s =
+    BL
+      ( fromString @Builder s,
+        fromString @ByteString s
+          & ByteString.length
+          & intToNatural
+          & fromMaybe 0
+          & Semi.Sum
+      )
+
+-- | Retrieve the builder
+builder :: BL -> Builder
+builder (BL (b, _)) = b
+
+-- | Retrieve the bytestring length
+builderLen :: BL -> Natural
+builderLen (BL (_, len)) = Semi.getSum $ len
+
+-- | Take a 'BL' and create a new 'BL' that represents the length as a decimal integer
+builderLenDec :: BL -> BL
+builderLenDec (BL (_, len)) =
+  let b = Builder.intDec $ (len & Semi.getSum & fromIntegral @Natural @Int)
+   in b & fromBuilder
+
+-- | Create a 'BL' from a 'Builder'.
+--
+-- Not efficient, goes back to a lazy bytestring to get the length
+fromBuilder :: Builder -> BL
+fromBuilder b =
+  BL
+    ( b,
+      b
+        & Builder.toLazyByteString
+        & ByteString.Lazy.length
+        & fromIntegral @Int64 @Natural
+        & Semi.Sum
+    )
+
+-- | Create a 'BL' from a 'ByteString'.
+fromByteString :: ByteString -> BL
+fromByteString b =
+  BL
+    ( Builder.byteString b,
+      b
+        & ByteString.length
+        & fromIntegral @Int @Natural
+        & Semi.Sum
+    )
+
+-- | Create a 'BL' from a 'Text'.
+fromText :: Text -> BL
+fromText t = t & textToBytesUtf8 & fromByteString
+
+-- | Parser for a netencode value.
+netencodeParser :: Atto.Parser T
+netencodeParser = T <$> go
+  where
+    go = Fix <$> netencodeParserF go
+
+-- | Parser for one level of a netencode value. Requires a parser for the recursion.
+netencodeParserF :: Atto.Parser rec -> Atto.Parser (TF rec)
+netencodeParserF inner = do
+  typeTag <- Atto.Char.anyChar
+  case typeTag of
+    't' -> Text <$> textParser
+    'b' -> Bytes <$> bytesParser
+    'u' -> unitParser
+    '<' -> Sum <$> tagParser
+    '{' -> Record <$> recordParser
+    '[' -> List <$> listParser
+    'n' -> naturalParser
+    'i' -> I6 <$> intParser
+    c -> fail ([c] <> " is not a valid netencode tag")
+  where
+    bytesParser = do
+      len <- boundedDecimalFail Atto.<?> "bytes is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "bytes did not have : after length"
+      bytes' <- Atto.take len
+      _ <- Atto.Char.char ',' Atto.<?> "bytes did not end with ,"
+      pure bytes'
+
+    textParser = do
+      len <- boundedDecimalFail Atto.<?> "text is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "text did not have : after length"
+      text' <-
+        Atto.take len <&> bytesToTextUtf8 >>= \case
+          Left err -> fail [fmt|cannot decode text as utf8: {err & prettyError}|]
+          Right t -> pure t
+      _ <- Atto.Char.char ',' Atto.<?> "text did not end with ,"
+      pure text'
+
+    unitParser = do
+      _ <- Atto.Char.char ',' Atto.<?> "unit did not end with ,"
+      pure $ Unit
+
+    tagParser = do
+      len <- boundedDecimalFail Atto.<?> "tag is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "tag did not have : after length"
+      tagTag <-
+        Atto.take len <&> bytesToTextUtf8 >>= \case
+          Left err -> fail [fmt|cannot decode tag key as utf8: {err & prettyError}|]
+          Right t -> pure t
+      _ <- Atto.Char.char '|' Atto.<?> "tag was missing the key/value separator (|)"
+      tagVal <- inner
+      pure $ Tag {..}
+
+    recordParser = do
+      -- TODO: the record does not use its inner length because we are descending into the inner parsers.
+      -- This is a smell! In theory it can be used to skip parsing the whole inner keys.
+      _len <- boundedDecimalFail Atto.<?> "record is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "record did not have : after length"
+      record' <-
+        many (Atto.Char.char '<' >> tagParser) <&> nonEmpty >>= \case
+          Nothing -> fail "record is not allowed to have 0 elements"
+          Just tags ->
+            pure $
+              tags
+                <&> (\t -> (t.tagTag, t.tagVal))
+                -- later keys are preferred if they are duplicates, according to the standard
+                & NEMap.fromList
+      _ <- Atto.Char.char '}' Atto.<?> "record did not end with }"
+      pure record'
+
+    listParser = do
+      -- TODO: the list does not use its inner length because we are descending into the inner parsers.
+      -- This is a smell! In theory it can be used to skip parsing the whole inner keys.
+      _len <- boundedDecimalFail Atto.<?> "list is missing a digit specifying the length"
+      _ <- Atto.Char.char ':' Atto.<?> "list did not have : after length"
+      -- TODO: allow empty lists?
+      list' <- many inner
+      _ <- Atto.Char.char ']' Atto.<?> "list did not end with ]"
+      pure list'
+
+    intParser = do
+      let p :: forall parseSize. (Bounded parseSize, Integral parseSize) => (Integer -> Atto.Parser Int64)
+          p n = do
+            _ <- Atto.Char.char ':' Atto.<?> [fmt|i{n & show} did not have : after length|]
+            isNegative <- Atto.option False (Atto.Char.char '-' <&> \_c -> True)
+            int <-
+              boundedDecimal @parseSize >>= \case
+                Nothing -> fail [fmt|cannot parse into i{n & show}, the number is too big (would overflow)|]
+                Just i ->
+                  pure $
+                    if isNegative
+                      then -- TODO: this should alread be done in the decimal parser, @minBound@ cannot be parsed cause itโ€™s one more than @(-maxBound)@!
+                        (-i)
+                      else i
+            _ <- Atto.Char.char ',' Atto.<?> [fmt|i{n & show} did not end with ,|]
+            pure $ fromIntegral @parseSize @Int64 int
+      digit <- Atto.Char.digit
+      case digit of
+        -- TODO: separate parser for i1 and i2 that makes sure the boundaries are right!
+        '1' -> p @Int8 1
+        '2' -> p @Int8 2
+        '3' -> p @Int8 3
+        '4' -> p @Int16 4
+        '5' -> p @Int32 5
+        '6' -> p @Int64 6
+        '7' -> fail [fmt|i parser only supports numbers up to size 6, was 7|]
+        '8' -> fail [fmt|i parser only supports numbers up to size 6, was 8|]
+        '9' -> fail [fmt|i parser only supports numbers up to size 6, was 9|]
+        o -> fail [fmt|i number with length {o & show} not possible|]
+
+    naturalParser = do
+      let p :: forall parseSize finalSize. (Bounded parseSize, Integral parseSize, Num finalSize) => (Integer -> Atto.Parser finalSize)
+          p n = do
+            _ <- Atto.Char.char ':' Atto.<?> [fmt|n{n & show} did not have : after length|]
+            int <-
+              boundedDecimal @parseSize >>= \case
+                Nothing -> fail [fmt|cannot parse into n{n & show}, the number is too big (would overflow)|]
+                Just i -> pure i
+
+            _ <- Atto.Char.char ',' Atto.<?> [fmt|n{n & show} did not end with ,|]
+            pure $ fromIntegral @parseSize @finalSize int
+      let b n = do
+            _ <- Atto.Char.char ':' Atto.<?> [fmt|n{n & show} did not have : after length|]
+            bool <-
+              (Atto.Char.char '0' >> pure False)
+                <|> (Atto.Char.char '1' >> pure True)
+            _ <- Atto.Char.char ',' Atto.<?> [fmt|n{n & show} did not end with ,|]
+            pure bool
+
+      digit <- Atto.Char.digit
+      case digit of
+        -- TODO: separate parser for n1 and n2 that makes sure the boundaries are right!
+        '1' -> N1 <$> b 1
+        '2' -> N3 <$> p @Word8 @Word8 2
+        '3' -> N3 <$> p @Word8 @Word8 3
+        '4' -> N6 <$> p @Word16 @Word64 4
+        '5' -> N6 <$> p @Word32 @Word64 5
+        '6' -> N6 <$> p @Word64 @Word64 6
+        '7' -> fail [fmt|n parser only supports numbers up to size 6, was 7|]
+        '8' -> fail [fmt|n parser only supports numbers up to size 6, was 8|]
+        '9' -> fail [fmt|n parser only supports numbers up to size 6, was 9|]
+        o -> fail [fmt|n number with length {o & show} not possible|]
+
+-- | Parser for a bounded decimal that does not overflow the decimal.
+--
+--  via https://www.extrema.is/blog/2021/10/20/parsing-bounded-integers
+boundedDecimal :: forall a. (Bounded a, Integral a) => Atto.Parser (Maybe a)
+boundedDecimal = do
+  i :: Integer <- decimal
+  pure $
+    if (i :: Integer) > fromIntegral (maxBound :: a)
+      then Nothing
+      else Just $ fromIntegral i
+  where
+    -- Copied from @Attoparsec.Text@ and adjusted to bytestring
+    decimal :: (Integral a2) => Atto.Parser a2
+    decimal = ByteString.foldl' step 0 <$> Atto.Char.takeWhile1 Atto.Char.isDigit
+      where
+        step a c = a * 10 + fromIntegral (c - 48)
+{-# SPECIALIZE boundedDecimal :: Atto.Parser (Maybe Int) #-}
+{-# SPECIALIZE boundedDecimal :: Atto.Parser (Maybe Int64) #-}
+{-# SPECIALIZE boundedDecimal :: Atto.Parser (Maybe Word8) #-}
+{-# SPECIALIZE boundedDecimal :: Atto.Parser (Maybe Word64) #-}
+
+-- | 'boundedDecimal', but fail the parser if the decimal overflows.
+boundedDecimalFail :: Atto.Parser Int
+boundedDecimalFail =
+  boundedDecimal >>= \case
+    Nothing -> fail "decimal out of range"
+    Just a -> pure a
+
+-- | Hedgehog generator for a netencode value.
+genNetencode :: Hedge.MonadGen m => m T
+genNetencode =
+  Gen.recursive
+    Gen.choice
+    [ -- these are bundled into one Gen, so that scalar elements get chosen less frequently, and the generator produces nicely nested examples
+      Gen.frequency
+        [ (1, pure unit),
+          (1, n1 <$> Gen.bool),
+          (1, n3 <$> Gen.element [0, 1, 5]),
+          (1, n6 <$> Gen.element [0, 1, 5]),
+          (1, i6 <$> Gen.element [-1, 1, 5]),
+          (2, text <$> Gen.text (Range.linear 1 10) Gen.lower),
+          (2, bytes <$> Gen.bytes (Range.linear 1 10))
+        ]
+    ]
+    [ do
+        key <- Gen.text (Range.linear 3 10) Gen.lower
+        val <- genNetencode
+        pure $ tag key val,
+      record
+        <$> ( let k = Gen.text (Range.linear 3 10) Gen.lower
+                  v = genNetencode
+               in NEMap.insertMap
+                    <$> k
+                    <*> v
+                    <*> ( (Gen.map (Range.linear 0 3)) $
+                            (,) <$> k <*> v
+                        )
+            )
+    ]
+
+-- | Hedgehog property: encoding a netencode value and parsing it again returns the same result.
+prop_netencodeRoundtrip :: Hedge.Property
+prop_netencodeRoundtrip = Hedge.property $ do
+  enc <- Hedge.forAll genNetencode
+  ( Atto.parseOnly
+      netencodeParser
+      ( netencodeEncodeStable enc
+          & Builder.toLazyByteString
+          & toStrictBytes
+      )
+    )
+    Hedge.=== (Right enc)
diff --git a/users/Profpatsch/netencode/Netencode/Parse.hs b/users/Profpatsch/netencode/Netencode/Parse.hs
new file mode 100644
index 0000000000..184fb5f912
--- /dev/null
+++ b/users/Profpatsch/netencode/Netencode/Parse.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Netencode.Parse where
+
+import Control.Category qualified
+import Control.Selective (Selective)
+import Data.Error.Tree
+import Data.Fix (Fix (..))
+import Data.Functor.Compose
+import Data.List qualified as List
+import Data.Map.NonEmpty (NEMap)
+import Data.Map.NonEmpty qualified as NEMap
+import Data.Semigroupoid qualified as Semigroupiod
+import Data.Semigroupoid qualified as Semigroupoid
+import Data.Text qualified as Text
+import Netencode qualified
+import PossehlAnalyticsPrelude
+import Prelude hiding (log)
+
+newtype Parse from to
+  = -- TODO: the way @Context = [Text]@ has to be forwarded to everything is kinda shitty.
+    -- This is essentially just a difference list, and can probably be treated as a function in the output?
+    Parse (([Text], from) -> Validation (NonEmpty ErrorTree) ([Text], to))
+  deriving
+    (Functor, Applicative, Selective)
+    via ( Compose
+            ( Compose
+                ((->) ([Text], from))
+                (Validation (NonEmpty ErrorTree))
+            )
+            ((,) [Text])
+        )
+
+instance Semigroupoid Parse where
+  o p2 p1 = Parse $ \from -> case runParse' p1 from of
+    Failure err -> Failure err
+    Success to1 -> runParse' p2 to1
+
+instance Category Parse where
+  (.) = Semigroupoid.o
+  id = Parse $ \t -> Success t
+
+runParse :: Error -> Parse from to -> from -> Either ErrorTree to
+runParse errMsg parser t =
+  (["$"], t)
+    & runParse' parser
+    <&> snd
+    & first (nestedMultiError errMsg)
+    & validationToEither
+
+runParse' :: Parse from to -> ([Text], from) -> Validation (NonEmpty ErrorTree) ([Text], to)
+runParse' (Parse f) from = f from
+
+parseEither :: (([Text], from) -> Either ErrorTree ([Text], to)) -> Parse from to
+parseEither f = Parse $ \from -> f from & eitherToListValidation
+
+tAs :: (Netencode.TF (Fix Netencode.TF) -> Either ([Text] -> ErrorTree) to) -> Parse Netencode.T to
+tAs f = parseEither ((\(context, Netencode.T (Fix tf)) -> f tf & bimap ($ context) (context,)))
+
+key :: Text -> Parse (NEMap Text to) to
+key name = parseEither $ \(context, rec) ->
+  rec
+    & NEMap.lookup name
+    & annotate (errorTreeContext (showContext context) [fmt|Key "{name}" does not exist|])
+    <&> (addContext name context,)
+
+showContext :: [Text] -> Text
+showContext context = context & List.reverse & Text.intercalate "."
+
+addContext :: a -> [a] -> [a]
+addContext = (:)
+
+asText :: Parse Netencode.T Text
+asText = tAs $ \case
+  Netencode.Text t -> pure t
+  other -> typeError "of text" other
+
+asBytes :: Parse Netencode.T ByteString
+asBytes = tAs $ \case
+  Netencode.Bytes b -> pure b
+  other -> typeError "of bytes" other
+
+asRecord :: Parse Netencode.T (NEMap Text (Netencode.T))
+asRecord = tAs $ \case
+  Netencode.Record rec -> pure (rec <&> Netencode.T)
+  other -> typeError "a record" other
+
+typeError :: Text -> Netencode.TF ignored -> (Either ([Text] -> ErrorTree) b)
+typeError should is = do
+  let otherS = is <&> (\_ -> ("โ€ฆ" :: String)) & show
+  Left $ \context -> errorTreeContext (showContext context) [fmt|Value is not {should}, but a {otherS}|]
+
+orThrowParseError ::
+  Parse (Either Error to) to
+orThrowParseError = Parse $ \case
+  (context, Left err) ->
+    err
+      & singleError
+      & errorTreeContext (showContext context)
+      & singleton
+      & Failure
+  (context, Right to) -> Success (context, to)
diff --git a/users/Profpatsch/netencode/README.md b/users/Profpatsch/netencode/README.md
index 3058e36eaf..3538a110a6 100644
--- a/users/Profpatsch/netencode/README.md
+++ b/users/Profpatsch/netencode/README.md
@@ -1,6 +1,6 @@
 # netencode 0.1-unreleased
 
-[bencode][] and [netstring][]-inspired pipe format that should be trivial go generate correctly in every context (only requires a `byte_length()` and a `printf()`), easy to parse (100 lines of code or less), mostly human-decipherable for easy debugging, and support nested record and sum types.
+[bencode][] and [netstring][]-inspired pipe format that should be trivial to generate correctly in every context (only requires a `byte_length()` and a `printf()`), easy to parse (100 lines of code or less), mostly human-decipherable for easy debugging, and support nested record and sum types.
 
 
 ## scalars
@@ -73,7 +73,11 @@ A tag (`<`) gives a value a name. The tag is UTF-8 encoded, starting with its le
 ### records (products/records), also maps
 
 A record (`{`) is a concatenation of tags (`<`). It needs to be closed with `}`.
-If tag names repeat the later ones should be ignored. Ordering does not matter.
+
+If tag names repeat the *earlier* ones should be ignored.
+Using the last tag corresponds with the way most languages handle converting a list of tuples to Maps, by using a for-loop and Map.insert without checking the contents first. Otherwise youโ€™d have to revert the list first or remember which keys you already inserted.
+
+Ordering of tags in a record does not matter.
 
 Similar to text, records start with the length of their *whole encoded content*, in bytes. This makes it possible to treat their contents as opaque bytestrings.
 
@@ -81,7 +85,7 @@ Similar to text, records start with the length of their *whole encoded content*,
 * A record with one empty field, `foo`: `{9:<3:foo|u,}`
 * A record with two fields, `foo` and `x`: `{21:<3:foo|u,<1:x|t3:baz,}`
 * The same record: `{21:<1:x|t3:baz,<3:foo|u,}`
-* The same record (later occurences of fields are ignored): `{28:<1:x|t3:baz,<3:foo|u,<1:x|u,}`
+* The same record (earlier occurences of fields are ignored): `{<1:x|u,28:<1:x|t3:baz,<3:foo|u,}`
 
 ### sums (tagged unions)
 
@@ -98,6 +102,24 @@ Similar to records, lists start with the length of their whole encoded content.
 * The list with text `foo` followed by i3 `-42`: `[14:t3:foo,i3:-42,]`
 * The list with `Some` and `None` tags: `[33:<4:Some|t3:foo,<4None|u,<4None|u,]`
 
+## parser security considerations
+
+The length field is a decimal number that is not length-restricted,
+meaning an attacker could give an infinitely long length (or extremely long)
+thus overflowing your parser if you are not careful.
+
+You should thus put a practical length limit to the length of length fields,
+which implicitely enforces a length limit on how long the value itself can be.
+
+Start by defining a max value length in bytes.
+Then count the number of decimals in that number.
+
+So if your max length is 1024 bytes, your length field can be a maximum `count_digits(1024) == 4` bytes long.
+
+Thus, if you restrict your parser to a length field of 4 bytes,
+it should also never parse anything longer than 1024 bytes for the value
+(plus 1 byte for the type tag, 4 bytes for the length, and 2 bytes for the separator & ending character).
+
 ## motivation
 
 TODO
diff --git a/users/Profpatsch/netencode/default.nix b/users/Profpatsch/netencode/default.nix
index db892cc9de..6e7dce489a 100644
--- a/users/Profpatsch/netencode/default.nix
+++ b/users/Profpatsch/netencode/default.nix
@@ -1,101 +1,133 @@
 { depot, pkgs, lib, ... }:
 
 let
-  netencode-rs = depot.nix.writers.rustSimpleLib {
+  netencode-rs = depot.nix.writers.rustSimpleLib
+    {
       name = "netencode";
       dependencies = [
         depot.third_party.rust-crates.nom
         depot.users.Profpatsch.execline.exec-helpers
       ];
-    } (builtins.readFile ./netencode.rs);
+    }
+    (builtins.readFile ./netencode.rs);
 
-  gen = import ./gen.nix { inherit lib; };
+  netencode-hs = pkgs.haskellPackages.mkDerivation {
+    pname = "netencode";
+    version = "0.1.0";
 
-  pretty-rs = depot.nix.writers.rustSimpleLib {
-    name = "netencode-pretty";
-    dependencies = [
-      netencode-rs
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./netencode.cabal
+      ./Netencode.hs
+      ./Netencode/Parse.hs
     ];
-  } (builtins.readFile ./pretty.rs);
-
-  pretty = depot.nix.writers.rustSimple {
-    name = "netencode-pretty";
-    dependencies = [
-      netencode-rs
-      pretty-rs
-      depot.users.Profpatsch.execline.exec-helpers
+
+    libraryHaskellDepends = [
+      pkgs.haskellPackages.hedgehog
+      pkgs.haskellPackages.nonempty-containers
+      pkgs.haskellPackages.deriving-compat
+      pkgs.haskellPackages.data-fix
+      pkgs.haskellPackages.bytestring
+      pkgs.haskellPackages.attoparsec
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-error-tree
     ];
-  } ''
+
+    isLibrary = true;
+    license = lib.licenses.mit;
+
+
+  };
+
+  gen = import ./gen.nix { inherit lib; };
+
+  pretty-rs = depot.nix.writers.rustSimpleLib
+    {
+      name = "netencode-pretty";
+      dependencies = [
+        netencode-rs
+      ];
+    }
+    (builtins.readFile ./pretty.rs);
+
+  pretty = depot.nix.writers.rustSimple
+    {
+      name = "netencode-pretty";
+      dependencies = [
+        netencode-rs
+        pretty-rs
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
     extern crate netencode;
     extern crate netencode_pretty;
     extern crate exec_helpers;
 
     fn main() {
       let (_, prog) = exec_helpers::args_for_exec("netencode-pretty", 0);
-      let mut buf = vec![];
-      let u = netencode::u_from_stdin_or_die_user_error("netencode-pretty", &mut buf);
-      match netencode_pretty::Pretty::from_u(u).print_multiline(&mut std::io::stdout()) {
+      let t = netencode::t_from_stdin_or_die_user_error("netencode-pretty");
+      match netencode_pretty::Pretty::from_u(t.to_u()).print_multiline(&mut std::io::stdout()) {
         Ok(()) => {},
         Err(err) => exec_helpers::die_temporary("netencode-pretty", format!("could not write to stdout: {}", err))
       }
     }
   '';
 
-  netencode-mustache = depot.nix.writers.rustSimple {
-    name = "netencode_mustache";
-    dependencies = [
-      depot.users.Profpatsch.arglib.netencode.rust
-      netencode-rs
-      depot.third_party.rust-crates.mustache
-    ];
-  } (builtins.readFile ./netencode-mustache.rs);
+  netencode-mustache = depot.nix.writers.rustSimple
+    {
+      name = "netencode_mustache";
+      dependencies = [
+        depot.users.Profpatsch.arglib.netencode.rust
+        netencode-rs
+        depot.third_party.rust-crates.mustache
+      ];
+    }
+    (builtins.readFile ./netencode-mustache.rs);
 
 
-  record-get = depot.nix.writers.rustSimple {
-    name = "record-get";
-    dependencies = [
-      netencode-rs
-      depot.users.Profpatsch.execline.exec-helpers
-      depot.users.Profpatsch.arglib.netencode.rust
-    ];
-  } ''
+  record-get = depot.nix.writers.rustSimple
+    {
+      name = "record-get";
+      dependencies = [
+        netencode-rs
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
     extern crate netencode;
-    extern crate arglib_netencode;
     extern crate exec_helpers;
     use netencode::{encode, dec};
     use netencode::dec::{Decoder, DecodeError};
 
     fn main() {
-        let mut buf = vec![];
         let args = exec_helpers::args("record-get", 1);
         let field = match std::str::from_utf8(&args[0]) {
             Ok(f) => f,
             Err(_e) => exec_helpers::die_user_error("record-get", format!("The field name needs to be valid unicode"))
         };
-        let u = netencode::u_from_stdin_or_die_user_error("record-get", &mut buf);
-        match (dec::RecordDot {field, inner: dec::AnyU }).dec(u) {
+        let t = netencode::t_from_stdin_or_die_user_error("record-get");
+        match (dec::RecordDot {field, inner: dec::AnyU }).dec(t.to_u()) {
             Ok(u) => encode(&mut std::io::stdout(), &u).expect("encoding to stdout failed"),
             Err(DecodeError(err)) => exec_helpers::die_user_error("record-get", err)
         }
     }
   '';
 
-  record-splice-env = depot.nix.writers.rustSimple {
-    name = "record-splice-env";
-    dependencies = [
-      netencode-rs
-      depot.users.Profpatsch.execline.exec-helpers
-    ];
-  } ''
+  record-splice-env = depot.nix.writers.rustSimple
+    {
+      name = "record-splice-env";
+      dependencies = [
+        netencode-rs
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
     extern crate netencode;
     extern crate exec_helpers;
     use netencode::dec::{Record, Try, ScalarAsBytes, Decoder, DecodeError};
 
     fn main() {
-        let mut buf = vec![];
-        let u = netencode::u_from_stdin_or_die_user_error("record-splice-env", &mut buf);
+        let t = netencode::t_from_stdin_or_die_user_error("record-splice-env");
         let (_, prog) = exec_helpers::args_for_exec("record-splice-env", 0);
-        match Record(Try(ScalarAsBytes)).dec(u) {
+        match Record(Try(ScalarAsBytes)).dec(t.to_u()) {
             Ok(map) => {
                 exec_helpers::exec_into_args(
                     "record-splice-env",
@@ -109,13 +141,14 @@ let
     }
   '';
 
-  env-splice-record = depot.nix.writers.rustSimple {
-    name = "env-splice-record";
-    dependencies = [
-      netencode-rs
-      depot.users.Profpatsch.execline.exec-helpers
-    ];
-  } ''
+  env-splice-record = depot.nix.writers.rustSimple
+    {
+      name = "env-splice-record";
+      dependencies = [
+        netencode-rs
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    } ''
     extern crate netencode;
     extern crate exec_helpers;
     use netencode::{T};
@@ -135,9 +168,11 @@ let
     }
   '';
 
-in depot.nix.utils.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   inherit
     netencode-rs
+    netencode-hs
     pretty-rs
     pretty
     netencode-mustache
diff --git a/users/Profpatsch/netencode/gen.nix b/users/Profpatsch/netencode/gen.nix
index 305ff7b08d..efc9629ca0 100644
--- a/users/Profpatsch/netencode/gen.nix
+++ b/users/Profpatsch/netencode/gen.nix
@@ -27,29 +27,33 @@ let
   concatStrings = builtins.concatStringsSep "";
 
   record = lokv: netstring "{" "}"
-    (concatStrings (map ({key, val}: tag key val) lokv));
+    (concatStrings (map ({ key, val }: tag key val) lokv));
 
   list = l: netstring "[" "]" (concatStrings l);
 
   dwim = val:
-    let match = {
-      "bool" = n1;
-      "int" = i6;
-      "string" = text;
-      "set" = attrs:
-        # it could be a derivation, then just return the path
-        if attrs.type or "" == "derivation" then text "${attrs}"
-        else
-          record (lib.mapAttrsToList
-          (k: v: {
-            key = k;
-            val = dwim v;
-          }) attrs);
-      "list" = l: list (map dwim l);
-    };
-    in match.${builtins.typeOf val} val;
+    let
+      match = {
+        "bool" = n1;
+        "int" = i6;
+        "string" = text;
+        "set" = attrs:
+          # it could be a derivation, then just return the path
+          if attrs.type or "" == "derivation" then text "${attrs}"
+          else
+            record (lib.mapAttrsToList
+              (k: v: {
+                key = k;
+                val = dwim v;
+              })
+              attrs);
+        "list" = l: list (map dwim l);
+      };
+    in
+    match.${builtins.typeOf val} val;
 
-in {
+in
+{
   inherit
     unit
     n1
diff --git a/users/Profpatsch/netencode/netencode-mustache.rs b/users/Profpatsch/netencode/netencode-mustache.rs
index ee7bafed22..73ed5be1de 100644
--- a/users/Profpatsch/netencode/netencode-mustache.rs
+++ b/users/Profpatsch/netencode/netencode-mustache.rs
@@ -1,12 +1,12 @@
-extern crate netencode;
-extern crate mustache;
 extern crate arglib_netencode;
+extern crate mustache;
+extern crate netencode;
 
-use mustache::{Data};
-use netencode::{T};
+use mustache::Data;
+use netencode::T;
 use std::collections::HashMap;
-use std::os::unix::ffi::{OsStrExt};
-use std::io::{Read};
+use std::io::Read;
+use std::os::unix::ffi::OsStrExt;
 
 fn netencode_to_mustache_data_dwim(t: T) -> Data {
     match t {
@@ -25,27 +25,26 @@ fn netencode_to_mustache_data_dwim(t: T) -> Data {
         T::Record(xs) => Data::Map(
             xs.into_iter()
                 .map(|(key, val)| (key, netencode_to_mustache_data_dwim(val)))
-                .collect::<HashMap<_,_>>()
+                .collect::<HashMap<_, _>>(),
         ),
         T::List(xs) => Data::Vec(
             xs.into_iter()
                 .map(|x| netencode_to_mustache_data_dwim(x))
-                .collect::<Vec<_>>()
+                .collect::<Vec<_>>(),
         ),
     }
 }
 
 pub fn from_stdin() -> () {
-    let data = netencode_to_mustache_data_dwim(
-        arglib_netencode::arglib_netencode("netencode-mustache", Some(std::ffi::OsStr::new("TEMPLATE_DATA")))
-    );
+    let data = netencode_to_mustache_data_dwim(arglib_netencode::arglib_netencode(
+        "netencode-mustache",
+        Some(std::ffi::OsStr::new("TEMPLATE_DATA")),
+    ));
     let mut stdin = String::new();
     std::io::stdin().read_to_string(&mut stdin).unwrap();
     mustache::compile_str(&stdin)
-        .and_then(|templ| templ.render_data(
-            &mut std::io::stdout(),
-            &data
-        )).unwrap()
+        .and_then(|templ| templ.render_data(&mut std::io::stdout(), &data))
+        .unwrap()
 }
 
 pub fn main() {
diff --git a/users/Profpatsch/netencode/netencode.cabal b/users/Profpatsch/netencode/netencode.cabal
new file mode 100644
index 0000000000..7bff4487bb
--- /dev/null
+++ b/users/Profpatsch/netencode/netencode.cabal
@@ -0,0 +1,74 @@
+cabal-version:      3.0
+name:               netencode
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+    exposed-modules:
+        Netencode,
+        Netencode.Parse
+
+    build-depends:
+        base >=4.15 && <5,
+        pa-prelude,
+        pa-label,
+        pa-error-tree,
+        hedgehog,
+        nonempty-containers,
+        deriving-compat,
+        data-fix,
+        bytestring,
+        attoparsec,
+        text,
+        semigroupoids,
+        selective
diff --git a/users/Profpatsch/netencode/netencode.rs b/users/Profpatsch/netencode/netencode.rs
index fcf642ca02..34a8fcef09 100644
--- a/users/Profpatsch/netencode/netencode.rs
+++ b/users/Profpatsch/netencode/netencode.rs
@@ -1,9 +1,9 @@
-extern crate nom;
 extern crate exec_helpers;
+extern crate nom;
 
 use std::collections::HashMap;
-use std::io::{Write, Read};
-use std::fmt::{Display, Debug};
+use std::fmt::{Debug, Display};
+use std::io::{Read, Write};
 
 #[derive(Debug, PartialEq, Eq, Clone)]
 pub enum T {
@@ -46,22 +46,19 @@ impl T {
             T::I7(i) => U::I7(*i),
             T::Text(t) => U::Text(t.as_str()),
             T::Binary(v) => U::Binary(v),
-            T::Sum(Tag { tag, val }) => U::Sum(
-                Tag { tag: tag.as_str(), val: Box::new(val.to_u()) }
-            ),
-            T::Record(map) => U::Record(
-                map.iter().map(|(k, v)| (k.as_str(), v.to_u())).collect()
-            ),
-            T::List(l) => U::List(
-                l.iter().map(|v| v.to_u()).collect::<Vec<U<'a>>>()
-            ),
+            T::Sum(Tag { tag, val }) => U::Sum(Tag {
+                tag: tag.as_str(),
+                val: Box::new(val.to_u()),
+            }),
+            T::Record(map) => U::Record(map.iter().map(|(k, v)| (k.as_str(), v.to_u())).collect()),
+            T::List(l) => U::List(l.iter().map(|v| v.to_u()).collect::<Vec<U<'a>>>()),
         }
     }
 
     pub fn encode<'a>(&'a self) -> Vec<u8> {
         match self {
             // TODO: donโ€™t go via U, inefficient
-            o => o.to_u().encode()
+            o => o.to_u().encode(),
         }
     }
 }
@@ -110,15 +107,16 @@ impl<'a> U<'a> {
             U::I7(i) => T::I7(*i),
             U::Text(t) => T::Text((*t).to_owned()),
             U::Binary(v) => T::Binary((*v).to_owned()),
-            U::Sum(Tag { tag, val }) => T::Sum(
-                Tag { tag: (*tag).to_owned(), val: Box::new(val.to_t()) }
-            ),
+            U::Sum(Tag { tag, val }) => T::Sum(Tag {
+                tag: (*tag).to_owned(),
+                val: Box::new(val.to_t()),
+            }),
             U::Record(map) => T::Record(
-                map.iter().map(|(k, v)| ((*k).to_owned(), v.to_t())).collect::<HashMap<String, T>>()
-            ),
-            U::List(l) => T::List(
-                l.iter().map(|v| v.to_t()).collect::<Vec<T>>()
+                map.iter()
+                    .map(|(k, v)| ((*k).to_owned(), v.to_t()))
+                    .collect::<HashMap<String, T>>(),
             ),
+            U::List(l) => T::List(l.iter().map(|v| v.to_t()).collect::<Vec<T>>()),
         }
     }
 }
@@ -127,16 +125,18 @@ impl<'a> U<'a> {
 pub struct Tag<S, A> {
     // TODO: make into &str
     pub tag: S,
-    pub val: Box<A>
+    pub val: Box<A>,
 }
 
 impl<S, A> Tag<S, A> {
     fn map<F, B>(self, f: F) -> Tag<S, B>
-        where F: Fn(A) -> B {
-          Tag {
-              tag: self.tag,
-              val: Box::new(f(*self.val))
-          }
+    where
+        F: Fn(A) -> B,
+    {
+        Tag {
+            tag: self.tag,
+            val: Box::new(f(*self.val)),
+        }
     }
 }
 
@@ -147,77 +147,170 @@ fn encode_tag<W: Write>(w: &mut W, tag: &str, val: &U) -> std::io::Result<()> {
 }
 
 pub fn encode<W: Write>(w: &mut W, u: &U) -> std::io::Result<()> {
-  match u {
-      U::Unit => write!(w, "u,"),
-      U::N1(b) => if *b { write!(w, "n1:1,") } else { write!(w, "n1:0,") },
-      U::N3(n) => write!(w, "n3:{},", n),
-      U::N6(n) => write!(w, "n6:{},", n),
-      U::N7(n) => write!(w, "n7:{},", n),
-      U::I3(i) => write!(w, "i3:{},", i),
-      U::I6(i) => write!(w, "i6:{},", i),
-      U::I7(i) => write!(w, "i7:{},", i),
-      U::Text(s) => {
-          write!(w, "t{}:", s.len());
-          w.write_all(s.as_bytes());
-          write!(w, ",")
-      }
-      U::Binary(s) => {
-          write!(w, "b{}:", s.len());
-          w.write_all(&s);
-          write!(w, ",")
-      },
-      U::Sum(Tag{tag, val}) => encode_tag(w, tag, val),
-      U::Record(m) => {
-          let mut c = std::io::Cursor::new(vec![]);
-          for (k, v) in m {
-              encode_tag(&mut c, k, v)?;
-          }
-          write!(w, "{{{}:", c.get_ref().len())?;
-          w.write_all(c.get_ref())?;
-          write!(w, "}}")
-      },
-      U::List(l) => {
-          let mut c = std::io::Cursor::new(vec![]);
-          for u in l {
-              encode(&mut c, u)?;
-          }
-          write!(w, "[{}:", c.get_ref().len())?;
-          w.write_all(c.get_ref())?;
-          write!(w, "]")
-      }
-  }
+    match u {
+        U::Unit => write!(w, "u,"),
+        U::N1(b) => {
+            if *b {
+                write!(w, "n1:1,")
+            } else {
+                write!(w, "n1:0,")
+            }
+        }
+        U::N3(n) => write!(w, "n3:{},", n),
+        U::N6(n) => write!(w, "n6:{},", n),
+        U::N7(n) => write!(w, "n7:{},", n),
+        U::I3(i) => write!(w, "i3:{},", i),
+        U::I6(i) => write!(w, "i6:{},", i),
+        U::I7(i) => write!(w, "i7:{},", i),
+        U::Text(s) => {
+            write!(w, "t{}:", s.len());
+            w.write_all(s.as_bytes());
+            write!(w, ",")
+        }
+        U::Binary(s) => {
+            write!(w, "b{}:", s.len());
+            w.write_all(&s);
+            write!(w, ",")
+        }
+        U::Sum(Tag { tag, val }) => encode_tag(w, tag, val),
+        U::Record(m) => {
+            let mut c = std::io::Cursor::new(vec![]);
+            for (k, v) in m {
+                encode_tag(&mut c, k, v)?;
+            }
+            write!(w, "{{{}:", c.get_ref().len())?;
+            w.write_all(c.get_ref())?;
+            write!(w, "}}")
+        }
+        U::List(l) => {
+            let mut c = std::io::Cursor::new(vec![]);
+            for u in l {
+                encode(&mut c, u)?;
+            }
+            write!(w, "[{}:", c.get_ref().len())?;
+            w.write_all(c.get_ref())?;
+            write!(w, "]")
+        }
+    }
 }
 
 pub fn text(s: String) -> T {
     T::Text(s)
 }
 
-pub fn u_from_stdin_or_die_user_error<'a>(prog_name: &'_ str, stdin_buf: &'a mut Vec<u8>) -> U<'a> {
-    std::io::stdin().lock().read_to_end(stdin_buf);
-    let u = match parse::u_u(stdin_buf) {
-        Ok((rest, u)) => match rest {
-            b"" => u,
-            _ => exec_helpers::die_user_error(prog_name, format!("stdin contained some soup after netencode value: {:?}", String::from_utf8_lossy(rest)))
-        },
-        Err(err) => exec_helpers::die_user_error(prog_name, format!("unable to parse netencode from stdin: {:?}", err))
-    };
-    u
+pub fn t_from_stdin_or_die_user_error<'a>(prog_name: &'_ str) -> T {
+    match t_from_stdin_or_die_user_error_with_rest(prog_name, &vec![]) {
+        None => exec_helpers::die_user_error(prog_name, "stdin was empty"),
+        Some((rest, t)) => {
+            if rest.is_empty() {
+                t
+            } else {
+                exec_helpers::die_user_error(
+                    prog_name,
+                    format!(
+                        "stdin contained some soup after netencode value: {:?}",
+                        String::from_utf8_lossy(&rest)
+                    ),
+                )
+            }
+        }
+    }
+}
+
+/// Read a netencode value from stdin incrementally, return bytes that could not be read.
+/// Nothing if there was nothing to read from stdin & no initial_bytes were provided.
+/// These can be passed back as `initial_bytes` if more values should be read.
+pub fn t_from_stdin_or_die_user_error_with_rest<'a>(
+    prog_name: &'_ str,
+    initial_bytes: &[u8],
+) -> Option<(Vec<u8>, T)> {
+    let mut chonker = Chunkyboi::new(std::io::stdin().lock(), 4096);
+    // The vec to pass to the parser on each step
+    let mut parser_vec: Vec<u8> = initial_bytes.to_vec();
+    // whether stdin was already empty
+    let mut was_empty: bool = false;
+    loop {
+        match chonker.next() {
+            None => {
+                if parser_vec.is_empty() {
+                    return None;
+                } else {
+                    was_empty = true
+                }
+            }
+            Some(Err(err)) => exec_helpers::die_temporary(
+                prog_name,
+                &format!("could not read from stdin: {:?}", err),
+            ),
+            Some(Ok(mut new_bytes)) => parser_vec.append(&mut new_bytes),
+        }
+
+        match parse::t_t(&parser_vec) {
+            Ok((rest, t)) => return Some((rest.to_owned(), t)),
+            Err(nom::Err::Incomplete(Needed)) => {
+                if was_empty {
+                    exec_helpers::die_user_error(
+                        prog_name,
+                        &format!(
+                            "unable to parse netencode from stdin, input incomplete: {:?}",
+                            parser_vec
+                        ),
+                    );
+                }
+                // read more from stdin and try parsing again
+                continue;
+            }
+            Err(err) => exec_helpers::die_user_error(
+                prog_name,
+                &format!("unable to parse netencode from stdin: {:?}", err),
+            ),
+        }
+    }
+}
+
+// iter helper
+// TODO: put into its own module
+struct Chunkyboi<T> {
+    inner: T,
+    buf: Vec<u8>,
+}
+
+impl<R: Read> Chunkyboi<R> {
+    fn new(inner: R, chunksize: usize) -> Self {
+        let buf = vec![0; chunksize];
+        Chunkyboi { inner, buf }
+    }
+}
+
+impl<R: Read> Iterator for Chunkyboi<R> {
+    type Item = std::io::Result<Vec<u8>>;
+
+    fn next(&mut self) -> Option<std::io::Result<Vec<u8>>> {
+        match self.inner.read(&mut self.buf) {
+            Ok(0) => None,
+            Ok(read) => {
+                // clone a new buffer so we can reuse the internal one
+                Some(Ok(self.buf[..read].to_owned()))
+            }
+            Err(err) => Some(Err(err)),
+        }
+    }
 }
 
 pub mod parse {
-    use super::{T, Tag, U};
+    use super::{Tag, T, U};
 
-    use std::str::FromStr;
-    use std::ops::Neg;
     use std::collections::HashMap;
+    use std::ops::Neg;
+    use std::str::FromStr;
 
-    use nom::{IResult};
-    use nom::branch::{alt};
+    use nom::branch::alt;
     use nom::bytes::streaming::{tag, take};
-    use nom::character::streaming::{digit1, char};
-    use nom::sequence::{tuple};
-    use nom::combinator::{map, map_res, flat_map, map_parser, opt};
+    use nom::character::streaming::{char, digit1};
+    use nom::combinator::{flat_map, map, map_parser, map_res, opt};
     use nom::error::{context, ErrorKind, ParseError};
+    use nom::sequence::tuple;
+    use nom::IResult;
 
     fn unit_t(s: &[u8]) -> IResult<&[u8], ()> {
         let (s, _) = context("unit", tag("u,"))(s)?;
@@ -227,9 +320,9 @@ pub mod parse {
     fn usize_t(s: &[u8]) -> IResult<&[u8], usize> {
         context(
             "usize",
-            map_res(
-                map_res(digit1, |n| std::str::from_utf8(n)),
-                |s| s.parse::<usize>())
+            map_res(map_res(digit1, |n| std::str::from_utf8(n)), |s| {
+                s.parse::<usize>()
+            }),
         )(s)
     }
 
@@ -238,87 +331,77 @@ pub mod parse {
             // This is the point where we check the descriminator;
             // if the beginning char does not match, we can immediately return.
             let (s, _) = char(begin)(s)?;
-            let (s, (len, _)) = tuple((
-                usize_t,
-                char(':')
-            ))(s)?;
-            let (s, (res, _)) = tuple((
-                take(len),
-                char(end)
-            ))(s)?;
+            let (s, (len, _)) = tuple((usize_t, char(':')))(s)?;
+            let (s, (res, _)) = tuple((take(len), char(end)))(s)?;
             Ok((s, res))
         }
     }
 
-
     fn uint_t<'a, I: FromStr + 'a>(t: &'static str) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], I> {
         move |s: &'a [u8]| {
             let (s, (_, _, int, _)) = tuple((
                 tag(t.as_bytes()),
                 char(':'),
-                map_res(
-                    map_res(digit1, |n: &[u8]| std::str::from_utf8(n)),
-                    |s| s.parse::<I>()
-                ),
-                char(',')
+                map_res(map_res(digit1, |n: &[u8]| std::str::from_utf8(n)), |s| {
+                    s.parse::<I>()
+                }),
+                char(','),
             ))(s)?;
             Ok((s, int))
         }
     }
 
     fn bool_t<'a>() -> impl Fn(&'a [u8]) -> IResult<&'a [u8], bool> {
-        context("bool", alt((
-            map(tag("n1:0,"), |_| false),
-            map(tag("n1:1,"), |_| true),
-        )))
-    }
-
-    fn int_t<'a, I: FromStr + Neg<Output=I>>(t: &'static str) -> impl Fn(&'a [u8]) -> IResult<&[u8], I> {
         context(
-            t,
-            move |s: &'a [u8]| {
-                let (s, (_, _, neg, int, _)) = tuple((
-                    tag(t.as_bytes()),
-                    char(':'),
-                    opt(char('-')),
-                    map_res(
-                        map_res(digit1, |n: &[u8]| std::str::from_utf8(n)),
-                        |s| s.parse::<I>()
-                    ),
-                    char(',')
-                ))(s)?;
-                let res = match neg {
-                    Some(_) => -int,
-                    None => int,
-                };
-                Ok((s, res))
-            }
+            "bool",
+            alt((map(tag("n1:0,"), |_| false), map(tag("n1:1,"), |_| true))),
         )
     }
 
+    fn int_t<'a, I: FromStr + Neg<Output = I>>(
+        t: &'static str,
+    ) -> impl Fn(&'a [u8]) -> IResult<&[u8], I> {
+        context(t, move |s: &'a [u8]| {
+            let (s, (_, _, neg, int, _)) = tuple((
+                tag(t.as_bytes()),
+                char(':'),
+                opt(char('-')),
+                map_res(map_res(digit1, |n: &[u8]| std::str::from_utf8(n)), |s| {
+                    s.parse::<I>()
+                }),
+                char(','),
+            ))(s)?;
+            let res = match neg {
+                Some(_) => -int,
+                None => int,
+            };
+            Ok((s, res))
+        })
+    }
+
     fn tag_t(s: &[u8]) -> IResult<&[u8], Tag<String, T>> {
         // recurses into the main parser
-        map(tag_g(t_t),
-            |Tag {tag, val}|
-            Tag {
-                tag: tag.to_string(),
-                val
-            })(s)
+        map(tag_g(t_t), |Tag { tag, val }| Tag {
+            tag: tag.to_string(),
+            val,
+        })(s)
     }
 
     fn tag_g<'a, P, O>(inner: P) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Tag<&'a str, O>>
     where
-        P: Fn(&'a [u8]) -> IResult<&'a [u8], O>
+        P: Fn(&'a [u8]) -> IResult<&'a [u8], O>,
     {
         move |s: &[u8]| {
             let (s, tag) = sized('<', '|')(s)?;
             let (s, val) = inner(s)?;
-            Ok((s, Tag {
-                tag: std::str::from_utf8(tag)
-                    .map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
-                val: Box::new(val)
-            }))
-
+            Ok((
+                s,
+                Tag {
+                    tag: std::str::from_utf8(tag)
+                        .map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
+                    val: Box::new(val),
+                },
+            ))
         }
     }
 
@@ -330,9 +413,9 @@ pub mod parse {
 
     fn text_g(s: &[u8]) -> IResult<&[u8], &str> {
         let (s, res) = sized('t', ',')(s)?;
-        Ok((s,
-            std::str::from_utf8(res)
-                .map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
+        Ok((
+            s,
+            std::str::from_utf8(res).map_err(|_| nom::Err::Failure((s, ErrorKind::Char)))?,
         ))
     }
 
@@ -374,22 +457,24 @@ pub mod parse {
     {
         map_parser(
             sized('[', ']'),
-            nom::multi::many0(inner_no_empty_string(inner))
+            nom::multi::many0(inner_no_empty_string(inner)),
         )
     }
 
     fn record_t<'a>(s: &'a [u8]) -> IResult<&'a [u8], HashMap<String, T>> {
         let (s, r) = record_g(t_t)(s)?;
-        Ok((s,
+        Ok((
+            s,
             r.into_iter()
-            .map(|(k, v)| (k.to_string(), v))
-            .collect::<HashMap<_,_>>()))
+                .map(|(k, v)| (k.to_string(), v))
+                .collect::<HashMap<_, _>>(),
+        ))
     }
 
     fn record_g<'a, P, O>(inner: P) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], HashMap<&'a str, O>>
     where
         O: Clone,
-        P: Fn(&'a [u8]) -> IResult<&'a [u8], O>
+        P: Fn(&'a [u8]) -> IResult<&'a [u8], O>,
     {
         move |s: &'a [u8]| {
             let (s, map) = map_parser(
@@ -397,19 +482,17 @@ pub mod parse {
                 nom::multi::fold_many0(
                     inner_no_empty_string(tag_g(&inner)),
                     HashMap::new(),
-                    |mut acc: HashMap<_,_>, Tag { tag, mut val }| {
-                        // ignore duplicated tag names that appear later
+                    |mut acc: HashMap<_, _>, Tag { tag, mut val }| {
+                        // ignore earlier tags with the same name
                         // according to netencode spec
-                        if ! acc.contains_key(tag) {
-                            acc.insert(tag, *val);
-                        }
+                        let _ = acc.insert(tag, *val);
                         acc
-                    }
-                )
+                    },
+                ),
             )(s)?;
             if map.is_empty() {
                 // records must not be empty, according to the spec
-                Err(nom::Err::Failure((s,nom::error::ErrorKind::Many1)))
+                Err(nom::Err::Failure((s, nom::error::ErrorKind::Many1)))
             } else {
                 Ok((s, map))
             }
@@ -424,7 +507,6 @@ pub mod parse {
             map(tag_g(u_u), |t| U::Sum(t)),
             map(list_g(u_u), U::List),
             map(record_g(u_u), U::Record),
-
             map(bool_t(), |u| U::N1(u)),
             map(uint_t("n3"), |u| U::N3(u)),
             map(uint_t("n6"), |u| U::N6(u)),
@@ -432,7 +514,6 @@ pub mod parse {
             map(int_t("i3"), |u| U::I3(u)),
             map(int_t("i6"), |u| U::I6(u)),
             map(int_t("i7"), |u| U::I7(u)),
-
             // less common
             map(uint_t("n2"), |u| U::N3(u)),
             map(uint_t("n4"), |u| U::N6(u)),
@@ -445,7 +526,7 @@ pub mod parse {
         ))(s)
     }
 
-    pub fn t_t(s: &[u8]) -> IResult<&[u8], T>  {
+    pub fn t_t(s: &[u8]) -> IResult<&[u8], T> {
         alt((
             text,
             binary(),
@@ -453,7 +534,6 @@ pub mod parse {
             map(tag_t, |t| T::Sum(t)),
             map(list_t, |l| T::List(l)),
             map(record_t, |p| T::Record(p)),
-
             map(bool_t(), |u| T::N1(u)),
             // 8, 64 and 128 bit
             map(uint_t("n3"), |u| T::N3(u)),
@@ -462,7 +542,6 @@ pub mod parse {
             map(int_t("i3"), |u| T::I3(u)),
             map(int_t("i6"), |u| T::I6(u)),
             map(int_t("i7"), |u| T::I7(u)),
-
             // less common
             map(uint_t("n2"), |u| T::N3(u)),
             map(uint_t("n4"), |u| T::N6(u)),
@@ -481,30 +560,18 @@ pub mod parse {
 
         #[test]
         fn test_parse_unit_t() {
-            assert_eq!(
-                unit_t("u,".as_bytes()),
-                Ok(("".as_bytes(), ()))
-            );
+            assert_eq!(unit_t("u,".as_bytes()), Ok(("".as_bytes(), ())));
         }
 
         #[test]
         fn test_parse_bool_t() {
-            assert_eq!(
-                bool_t()("n1:0,".as_bytes()),
-                Ok(("".as_bytes(), false))
-            );
-            assert_eq!(
-                bool_t()("n1:1,".as_bytes()),
-                Ok(("".as_bytes(), true))
-            );
+            assert_eq!(bool_t()("n1:0,".as_bytes()), Ok(("".as_bytes(), false)));
+            assert_eq!(bool_t()("n1:1,".as_bytes()), Ok(("".as_bytes(), true)));
         }
 
         #[test]
         fn test_parse_usize_t() {
-            assert_eq!(
-                usize_t("32foo".as_bytes()),
-                Ok(("foo".as_bytes(), 32))
-            );
+            assert_eq!(usize_t("32foo".as_bytes()), Ok(("foo".as_bytes(), 32)));
         }
 
         #[test]
@@ -515,7 +582,10 @@ pub mod parse {
             );
             assert_eq!(
                 uint_t::<u8>("n3")("n3:1024,abc".as_bytes()),
-                Err(nom::Err::Error(("1024,abc".as_bytes(), nom::error::ErrorKind::MapRes)))
+                Err(nom::Err::Error((
+                    "1024,abc".as_bytes(),
+                    nom::error::ErrorKind::MapRes
+                )))
             );
             assert_eq!(
                 int_t::<i64>("i6")("i6:-23,abc".as_bytes()),
@@ -544,18 +614,21 @@ pub mod parse {
             assert_eq!(
                 text("t5:hello,".as_bytes()),
                 Ok(("".as_bytes(), T::Text("hello".to_owned()))),
-                "{}", r"t5:hello,"
+                "{}",
+                r"t5:hello,"
             );
             assert_eq!(
                 text("t4:fo".as_bytes()),
                 // The content of the text should be 4 long
                 Err(nom::Err::Incomplete(nom::Needed::Size(4))),
-                "{}", r"t4:fo,"
+                "{}",
+                r"t4:fo,"
             );
             assert_eq!(
                 text("t9:ไปŠๆ—ฅใฏ,".as_bytes()),
                 Ok(("".as_bytes(), T::Text("ไปŠๆ—ฅใฏ".to_owned()))),
-                "{}", r"t9:ไปŠๆ—ฅใฏ,"
+                "{}",
+                r"t9:ไปŠๆ—ฅใฏ,"
             );
         }
 
@@ -564,24 +637,28 @@ pub mod parse {
             assert_eq!(
                 binary()("b5:hello,".as_bytes()),
                 Ok(("".as_bytes(), T::Binary(Vec::from("hello".to_owned())))),
-                "{}", r"b5:hello,"
+                "{}",
+                r"b5:hello,"
             );
             assert_eq!(
                 binary()("b4:fo".as_bytes()),
                 // The content of the byte should be 4 long
                 Err(nom::Err::Incomplete(nom::Needed::Size(4))),
-                "{}", r"b4:fo,"
+                "{}",
+                r"b4:fo,"
             );
             assert_eq!(
                 binary()("b4:foob".as_bytes()),
                 // The content is 4 bytes now, but the finishing , is missing
                 Err(nom::Err::Incomplete(nom::Needed::Size(1))),
-                    "{}", r"b4:fo,"
-                );
+                "{}",
+                r"b4:fo,"
+            );
             assert_eq!(
                 binary()("b9:ไปŠๆ—ฅใฏ,".as_bytes()),
                 Ok(("".as_bytes(), T::Binary(Vec::from("ไปŠๆ—ฅใฏ".as_bytes())))),
-                "{}", r"b9:ไปŠๆ—ฅใฏ,"
+                "{}",
+                r"b9:ไปŠๆ—ฅใฏ,"
             );
         }
 
@@ -590,25 +667,23 @@ pub mod parse {
             assert_eq!(
                 list_t("[0:]".as_bytes()),
                 Ok(("".as_bytes(), vec![])),
-                "{}", r"[0:]"
+                "{}",
+                r"[0:]"
             );
             assert_eq!(
                 list_t("[6:u,u,u,]".as_bytes()),
-                Ok(("".as_bytes(), vec![
-                    T::Unit,
-                    T::Unit,
-                    T::Unit,
-                ])),
-                "{}", r"[6:u,u,u,]"
+                Ok(("".as_bytes(), vec![T::Unit, T::Unit, T::Unit,])),
+                "{}",
+                r"[6:u,u,u,]"
             );
             assert_eq!(
                 list_t("[15:u,[7:t3:foo,]u,]".as_bytes()),
-                Ok(("".as_bytes(), vec![
-                    T::Unit,
-                    T::List(vec![T::Text("foo".to_owned())]),
-                    T::Unit,
-                ])),
-                "{}", r"[15:u,[7:t3:foo,]u,]"
+                Ok((
+                    "".as_bytes(),
+                    vec![T::Unit, T::List(vec![T::Text("foo".to_owned())]), T::Unit,]
+                )),
+                "{}",
+                r"[15:u,[7:t3:foo,]u,]"
             );
         }
 
@@ -616,27 +691,40 @@ pub mod parse {
         fn test_record() {
             assert_eq!(
                 record_t("{21:<1:a|u,<1:b|u,<1:c|u,}".as_bytes()),
-                Ok(("".as_bytes(), vec![
-                    ("a".to_owned(), T::Unit),
-                    ("b".to_owned(), T::Unit),
-                    ("c".to_owned(), T::Unit),
-                ].into_iter().collect::<HashMap<String, T>>())),
-                "{}", r"{21:<1:a|u,<1:b|u,<1:c|u,}"
+                Ok((
+                    "".as_bytes(),
+                    vec![
+                        ("a".to_owned(), T::Unit),
+                        ("b".to_owned(), T::Unit),
+                        ("c".to_owned(), T::Unit),
+                    ]
+                    .into_iter()
+                    .collect::<HashMap<String, T>>()
+                )),
+                "{}",
+                r"{21:<1:a|u,<1:b|u,<1:c|u,}"
             );
             // duplicated keys are ignored (first is taken)
             assert_eq!(
                 record_t("{25:<1:a|u,<1:b|u,<1:a|i1:-1,}".as_bytes()),
-                Ok(("".as_bytes(), vec![
-                    ("a".to_owned(), T::Unit),
-                    ("b".to_owned(), T::Unit),
-                ].into_iter().collect::<HashMap<_,_>>())),
-                "{}", r"{25:<1:a|u,<1:b|u,<1:a|i1:-1,}"
+                Ok((
+                    "".as_bytes(),
+                    vec![("a".to_owned(), T::I3(-1)), ("b".to_owned(), T::Unit),]
+                        .into_iter()
+                        .collect::<HashMap<_, _>>()
+                )),
+                "{}",
+                r"{25:<1:a|u,<1:b|u,<1:a|i1:-1,}"
             );
             // empty records are not allowed
             assert_eq!(
                 record_t("{0:}".as_bytes()),
-                Err(nom::Err::Failure(("".as_bytes(), nom::error::ErrorKind::Many1))),
-                "{}", r"{0:}"
+                Err(nom::Err::Failure((
+                    "".as_bytes(),
+                    nom::error::ErrorKind::Many1
+                ))),
+                "{}",
+                r"{0:}"
             );
         }
 
@@ -645,37 +733,62 @@ pub mod parse {
             assert_eq!(
                 t_t("n3:255,".as_bytes()),
                 Ok(("".as_bytes(), T::N3(255))),
-                "{}", r"n3:255,"
+                "{}",
+                r"n3:255,"
             );
             assert_eq!(
                 t_t("t6:halloo,".as_bytes()),
                 Ok(("".as_bytes(), T::Text("halloo".to_owned()))),
-                "{}", r"t6:halloo,"
+                "{}",
+                r"t6:halloo,"
             );
             assert_eq!(
                 t_t("<3:foo|t6:halloo,".as_bytes()),
-                Ok(("".as_bytes(), T::Sum (Tag {
-                    tag: "foo".to_owned(),
-                    val: Box::new(T::Text("halloo".to_owned()))
-                }))),
-                "{}", r"<3:foo|t6:halloo,"
+                Ok((
+                    "".as_bytes(),
+                    T::Sum(Tag {
+                        tag: "foo".to_owned(),
+                        val: Box::new(T::Text("halloo".to_owned()))
+                    })
+                )),
+                "{}",
+                r"<3:foo|t6:halloo,"
             );
             // { a: Unit
             // , foo: List <A: Unit | B: List i3> }
             assert_eq!(
                 t_t("{52:<1:a|u,<3:foo|[33:<1:A|u,<1:A|n1:1,<1:B|[7:i3:127,]]}".as_bytes()),
-                Ok(("".as_bytes(), T::Record(vec![
-                    ("a".to_owned(), T::Unit),
-                    ("foo".to_owned(), T::List(vec![
-                        T::Sum(Tag { tag: "A".to_owned(), val: Box::new(T::Unit) }),
-                        T::Sum(Tag { tag: "A".to_owned(), val: Box::new(T::N1(true)) }),
-                        T::Sum(Tag { tag: "B".to_owned(), val: Box::new(T::List(vec![T::I3(127)])) }),
-                    ]))
-                ].into_iter().collect::<HashMap<String, T>>()))),
-                "{}", r"{52:<1:a|u,<3:foo|[33:<1:A|u,<1:A|n1:1,<1:B|[7:i3:127,]]}"
+                Ok((
+                    "".as_bytes(),
+                    T::Record(
+                        vec![
+                            ("a".to_owned(), T::Unit),
+                            (
+                                "foo".to_owned(),
+                                T::List(vec![
+                                    T::Sum(Tag {
+                                        tag: "A".to_owned(),
+                                        val: Box::new(T::Unit)
+                                    }),
+                                    T::Sum(Tag {
+                                        tag: "A".to_owned(),
+                                        val: Box::new(T::N1(true))
+                                    }),
+                                    T::Sum(Tag {
+                                        tag: "B".to_owned(),
+                                        val: Box::new(T::List(vec![T::I3(127)]))
+                                    }),
+                                ])
+                            )
+                        ]
+                        .into_iter()
+                        .collect::<HashMap<String, T>>()
+                    )
+                )),
+                "{}",
+                r"{52:<1:a|u,<3:foo|[33:<1:A|u,<1:A|n1:1,<1:B|[7:i3:127,]]}"
             );
         }
-
     }
 }
 
@@ -690,8 +803,10 @@ pub mod dec {
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError>;
     }
 
+    /// Any netencode, as `T`.
     #[derive(Clone, Copy)]
     pub struct AnyT;
+    /// Any netencode, as `U`.
     #[derive(Clone, Copy)]
     pub struct AnyU;
 
@@ -709,8 +824,11 @@ pub mod dec {
         }
     }
 
+    /// A text
     #[derive(Clone, Copy)]
     pub struct Text;
+
+    /// A bytestring
     // TODO: rename to Bytes
     #[derive(Clone, Copy)]
     pub struct Binary;
@@ -730,11 +848,15 @@ pub mod dec {
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match u {
                 U::Binary(b) => Ok(b),
-                other => Err(DecodeError(format!("Cannot decode {:?} into Binary", other))),
+                other => Err(DecodeError(format!(
+                    "Cannot decode {:?} into Binary",
+                    other
+                ))),
             }
         }
     }
 
+    /// Any scalar, converted to bytes.
     #[derive(Clone, Copy)]
     pub struct ScalarAsBytes;
 
@@ -755,80 +877,93 @@ pub mod dec {
         }
     }
 
+    /// A map of Ts (TODO: rename to map)
     #[derive(Clone, Copy)]
     pub struct Record<T>(pub T);
 
     impl<'a, Inner> Decoder<'a> for Record<Inner>
-        where Inner: Decoder<'a>
+    where
+        Inner: Decoder<'a>,
     {
         type A = HashMap<&'a str, Inner::A>;
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match u {
-                U::Record(map) =>
-                    map.into_iter()
+                U::Record(map) => map
+                    .into_iter()
                     .map(|(k, v)| self.0.dec(v).map(|v2| (k, v2)))
                     .collect::<Result<Self::A, _>>(),
-                o => Err(DecodeError(format!("Cannot decode {:?} into record", o)))
+                o => Err(DecodeError(format!("Cannot decode {:?} into record", o))),
             }
         }
     }
 
+    /// Assume a record and project out the field with the given name and type.
     #[derive(Clone, Copy)]
     pub struct RecordDot<'a, T> {
         pub field: &'a str,
-        pub inner: T
+        pub inner: T,
     }
 
-    impl <'a, Inner> Decoder<'a> for RecordDot<'_, Inner>
-        where Inner: Decoder<'a> + Clone
+    impl<'a, Inner> Decoder<'a> for RecordDot<'_, Inner>
+    where
+        Inner: Decoder<'a> + Clone,
     {
         type A = Inner::A;
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match Record(self.inner.clone()).dec(u) {
                 Ok(mut map) => match map.remove(self.field) {
                     Some(inner) => Ok(inner),
-                    None => Err(DecodeError(format!("Cannot find `{}` in record map", self.field))),
+                    None => Err(DecodeError(format!(
+                        "Cannot find `{}` in record map",
+                        self.field
+                    ))),
                 },
                 Err(err) => Err(err),
             }
         }
     }
 
+    /// Equals one of the listed `A`s exactly, after decoding.
     #[derive(Clone)]
-    pub struct OneOf<T, A>{
+    pub struct OneOf<T, A> {
         pub inner: T,
         pub list: Vec<A>,
     }
 
-    impl <'a, Inner> Decoder<'a> for OneOf<Inner, Inner::A>
-        where Inner: Decoder<'a>,
-              Inner::A: Display + Debug + PartialEq
+    impl<'a, Inner> Decoder<'a> for OneOf<Inner, Inner::A>
+    where
+        Inner: Decoder<'a>,
+        Inner::A: Display + Debug + PartialEq,
     {
         type A = Inner::A;
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match self.inner.dec(u) {
                 Ok(inner) => match self.list.iter().any(|x| x.eq(&inner)) {
                     true => Ok(inner),
-                    false => Err(DecodeError(format!("{} is not one of {:?}", inner, self.list)))
+                    false => Err(DecodeError(format!(
+                        "{} is not one of {:?}",
+                        inner, self.list
+                    ))),
                 },
-                Err(err) => Err(err)
+                Err(err) => Err(err),
             }
         }
     }
 
+    /// Try decoding as `T`.
     #[derive(Clone)]
     pub struct Try<T>(pub T);
 
-    impl <'a, Inner> Decoder<'a> for Try<Inner>
-        where Inner: Decoder<'a>
+    impl<'a, Inner> Decoder<'a> for Try<Inner>
+    where
+        Inner: Decoder<'a>,
     {
         type A = Option<Inner::A>;
         fn dec(&self, u: U<'a>) -> Result<Self::A, DecodeError> {
             match self.0.dec(u) {
                 Ok(inner) => Ok(Some(inner)),
-                Err(err) => Ok(None)
+                Err(err) => Ok(None),
             }
         }
     }
-
 }
diff --git a/users/Profpatsch/netencode/pretty.rs b/users/Profpatsch/netencode/pretty.rs
index 8fec24a60e..935c3d4a8a 100644
--- a/users/Profpatsch/netencode/pretty.rs
+++ b/users/Profpatsch/netencode/pretty.rs
@@ -1,6 +1,6 @@
 extern crate netencode;
 
-use netencode::{U, T, Tag};
+use netencode::{Tag, T, U};
 
 pub enum Pretty {
     Single {
@@ -20,7 +20,7 @@ pub enum Pretty {
         r#type: char,
         length: String,
         vals: Vec<Pretty>,
-        trailer: char
+        trailer: char,
     },
 }
 
@@ -39,7 +39,7 @@ impl Pretty {
                 r#type: 't',
                 length: format!("{}:", s.len()),
                 val: s.to_string(),
-                trailer: ','
+                trailer: ',',
             },
             U::Binary(s) => Pretty::Single {
                 r#type: 'b',
@@ -47,15 +47,18 @@ impl Pretty {
                 // For pretty printing we want the string to be visible obviously.
                 // Instead of not supporting binary, letโ€™s use lossy conversion.
                 val: String::from_utf8_lossy(s).into_owned(),
-                trailer: ','
+                trailer: ',',
             },
-            U::Sum(Tag{tag, val}) => Self::pretty_tag(tag, Self::from_u(*val)),
+            U::Sum(Tag { tag, val }) => Self::pretty_tag(tag, Self::from_u(*val)),
             U::Record(m) => Pretty::Multi {
                 r#type: '{',
                 // TODO: we are losing the size here, should we recompute it? Keep it?
                 length: String::from(""),
-                vals: m.into_iter().map(|(k, v)| Self::pretty_tag(k, Self::from_u(v))).collect(),
-                trailer: '}'
+                vals: m
+                    .into_iter()
+                    .map(|(k, v)| Self::pretty_tag(k, Self::from_u(v)))
+                    .collect(),
+                trailer: '}',
             },
             U::List(l) => Pretty::Multi {
                 r#type: '[',
@@ -68,13 +71,14 @@ impl Pretty {
     }
 
     fn scalar<D>(r#type: char, length: &str, d: D) -> Pretty
-    where D: std::fmt::Display
+    where
+        D: std::fmt::Display,
     {
         Pretty::Single {
             r#type,
             length: length.to_string(),
             val: format!("{}", d),
-            trailer: ','
+            trailer: ',',
         }
     }
 
@@ -89,43 +93,62 @@ impl Pretty {
     }
 
     pub fn print_multiline<W>(&self, mut w: &mut W) -> std::io::Result<()>
-        where W: std::io::Write
+    where
+        W: std::io::Write,
     {
         Self::go(&mut w, self, 0, true);
         write!(w, "\n")
     }
 
     fn go<W>(mut w: &mut W, p: &Pretty, depth: usize, is_newline: bool) -> std::io::Result<()>
-        where W: std::io::Write
+    where
+        W: std::io::Write,
     {
-        const full : usize = 4;
-        const half : usize = 2;
-        let i = &vec![b' '; depth*full];
-        let iandhalf = &vec![b' '; depth*full + half];
-        let (i, iandhalf) = unsafe {(
-            std::str::from_utf8_unchecked(i),
-            std::str::from_utf8_unchecked(iandhalf),
-        )};
+        const full: usize = 4;
+        const half: usize = 2;
+        let i = &vec![b' '; depth * full];
+        let iandhalf = &vec![b' '; depth * full + half];
+        let (i, iandhalf) = unsafe {
+            (
+                std::str::from_utf8_unchecked(i),
+                std::str::from_utf8_unchecked(iandhalf),
+            )
+        };
         if is_newline {
             write!(&mut w, "{}", i);
         }
         match p {
-            Pretty::Single {r#type, length, val, trailer} =>
-                write!(&mut w, "{} {}{}", r#type, val, trailer),
-            Pretty::Tag { r#type, length, key, inner, val } => {
+            Pretty::Single {
+                r#type,
+                length,
+                val,
+                trailer,
+            } => write!(&mut w, "{} {}{}", r#type, val, trailer),
+            Pretty::Tag {
+                r#type,
+                length,
+                key,
+                inner,
+                val,
+            } => {
                 write!(&mut w, "{} {} {}", r#type, key, inner)?;
                 Self::go::<W>(&mut w, val, depth, false)
-            },
+            }
             // if the length is 0 or 1, we print on one line,
             // only if thereโ€™s more than one element we split the resulting value.
             // we never break lines on arbitrary column sizes, since that is just silly.
-            Pretty::Multi {r#type, length, vals, trailer} => match vals.len() {
+            Pretty::Multi {
+                r#type,
+                length,
+                vals,
+                trailer,
+            } => match vals.len() {
                 0 => write!(&mut w, "{} {}", r#type, trailer),
                 1 => {
                     write!(&mut w, "{} ", r#type);
                     Self::go::<W>(&mut w, &vals[0], depth, false)?;
                     write!(&mut w, "{}", trailer)
-                },
+                }
                 more => {
                     write!(&mut w, "\n{}{} \n", iandhalf, r#type)?;
                     for v in vals {
diff --git a/users/Profpatsch/netstring/default.nix b/users/Profpatsch/netstring/default.nix
index 2b21cde388..047fe6bae1 100644
--- a/users/Profpatsch/netstring/default.nix
+++ b/users/Profpatsch/netstring/default.nix
@@ -1,8 +1,16 @@
 { lib, pkgs, depot, ... }:
 let
-  python-netstring = depot.users.Profpatsch.writers.python3Lib {
-    name = "netstring";
-  } ''
+  toNetstring = depot.nix.netstring.fromString;
+
+  toNetstringList = xs:
+    lib.concatStrings (map toNetstring xs);
+
+  toNetstringKeyVal = depot.nix.netstring.attrsToKeyValList;
+
+  python-netstring = depot.users.Profpatsch.writers.python3Lib
+    {
+      name = "netstring";
+    } ''
     def read_netstring(bytes):
         (int_length, rest) = bytes.split(sep=b':', maxsplit=1)
         val = rest[:int(int_length)]
@@ -27,9 +35,10 @@ let
         return res
   '';
 
-  rust-netstring = depot.nix.writers.rustSimpleLib {
-    name = "netstring";
-  } ''
+  rust-netstring = depot.nix.writers.rustSimpleLib
+    {
+      name = "netstring";
+    } ''
     pub fn to_netstring(s: &[u8]) -> Vec<u8> {
         let len = s.len();
         // length of the integer as ascii
@@ -43,9 +52,13 @@ let
     }
   '';
 
-in depot.nix.utils.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   inherit
+    toNetstring
+    toNetstringList
+    toNetstringKeyVal
     python-netstring
     rust-netstring
-      ;
+    ;
 }
diff --git a/users/Profpatsch/netstring/tests/default.nix b/users/Profpatsch/netstring/tests/default.nix
index f64beb9e92..6a1062988f 100644
--- a/users/Profpatsch/netstring/tests/default.nix
+++ b/users/Profpatsch/netstring/tests/default.nix
@@ -2,12 +2,13 @@
 
 let
 
-  python-netstring-test = depot.users.Profpatsch.writers.python3 {
-    name = "python-netstring-test";
-    libraries = p: [
-      depot.users.Profpatsch.netstring.python-netstring
-    ];
-  } ''
+  python-netstring-test = depot.users.Profpatsch.writers.python3
+    {
+      name = "python-netstring-test";
+      libraries = p: [
+        depot.users.Profpatsch.netstring.python-netstring
+      ];
+    } ''
     import netstring
 
     def assEq(left, right):
@@ -33,12 +34,13 @@ let
     )
   '';
 
-  rust-netstring-test = depot.nix.writers.rustSimple {
-    name = "rust-netstring-test";
-    dependencies = [
-      depot.users.Profpatsch.netstring.rust-netstring
-    ];
-  } ''
+  rust-netstring-test = depot.nix.writers.rustSimple
+    {
+      name = "rust-netstring-test";
+      dependencies = [
+        depot.users.Profpatsch.netstring.rust-netstring
+      ];
+    } ''
     extern crate netstring;
 
     fn main() {
@@ -53,7 +55,8 @@ let
     }
   '';
 
-in depot.nix.utils.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   inherit
     python-netstring-test
     rust-netstring-test
diff --git a/users/Profpatsch/nix-home/README.md b/users/Profpatsch/nix-home/README.md
new file mode 100644
index 0000000000..222978bc8c
--- /dev/null
+++ b/users/Profpatsch/nix-home/README.md
@@ -0,0 +1,7 @@
+# nix-home
+
+My very much simplified version of [home-manager](https://github.com/nix-community/home-manager/).
+
+Only takes care about installing symlinks into `$HOME`, and uses [`GNU stow`](https://www.gnu.org/software/stow/) for doing the actual mutating.
+
+No support for services (yet).
diff --git a/users/Profpatsch/nix-home/default.nix b/users/Profpatsch/nix-home/default.nix
new file mode 100644
index 0000000000..ee154c549a
--- /dev/null
+++ b/users/Profpatsch/nix-home/default.nix
@@ -0,0 +1,212 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.stow [ "stow" ]
+    // depot.nix.getBins pkgs.coreutils [ "mkdir" "ln" "printenv" "rm" ]
+    // depot.nix.getBins pkgs.xe [ "xe" ]
+    // depot.nix.getBins pkgs.lr [ "lr" ]
+    // depot.nix.getBins pkgs.nix [ "nix-store" ]
+  ;
+
+  # run stow to populate the target directory with the given stow package, read from stowDir.
+  # Bear in mind that `stowDirOriginPath` should always be semantically bound to the given `stowDir`, otherwise stow might become rather confused.
+  runStow =
+    {
+      # โ€œstow packageโ€ to stow (see manpage)
+      # TODO: allow this function to un-stow multiple packages!
+      stowPackage
+    , # โ€œtarget directoryโ€ to stow in (see manpage)
+      targetDir
+    , # The โ€œstow directoryโ€ (see manpage), containing โ€œstow packagesโ€ (see manpage)
+      stowDir
+    , # representative directory for the stowDir in the file system, against which stow will create relative links.
+      # ATTN: this is always overwritten with the contents of `stowDir`! You shouldnโ€™t re-use the same `stowDirOriginPath` for different `stowDir`s, otherwise there might be surprises.
+      stowDirOriginPath
+    ,
+    }: depot.nix.writeExecline "stow-${stowPackage}" { } [
+      # first, create a temporary stow directory to use as source
+      # (stow will use it to determine the origin of files)
+      "if"
+      [ bins.mkdir "-p" stowDirOriginPath ]
+      # remove old symlinks
+      "if"
+      [
+        "pipeline"
+        [
+          bins.lr
+          "-0"
+          "-t"
+          "depth == 1 && type == l"
+          stowDirOriginPath
+        ]
+        bins.xe
+        "-0"
+        bins.rm
+      ]
+      # create an indirect gc root so our config is not cleaned under our asses by a garbage collect
+      "if"
+      [
+        bins.nix-store
+        "--realise"
+        "--indirect"
+        "--add-root"
+        "${stowDirOriginPath}/.nix-stowdir-gc-root"
+        stowDir
+      ]
+      # populate with new stow targets
+      "if"
+      [
+        "elglob"
+        "-w0"
+        "stowPackages"
+        "${stowDir}/*"
+        bins.ln
+        "--force"
+        "-st"
+        stowDirOriginPath
+        "$stowPackages"
+      ]
+      # stow always looks for $HOME/.stowrc to read more arguments
+      "export"
+      "HOME"
+      "/homeless-shelter"
+      bins.stow
+      # always run restow for now; this does more stat but will remove stale links
+      "--restow"
+      "--dir"
+      stowDirOriginPath
+      "--target"
+      targetDir
+      stowPackage
+    ];
+
+  # create a stow dir from a list of drv paths and a stow package name.
+  makeStowDir =
+    (with depot.nix.yants;
+    defun
+      [
+        (list (struct {
+          originalDir = drv;
+          stowPackage = string;
+        }))
+        drv
+      ])
+      (dirs:
+        depot.nix.runExecline "make-stow-dir"
+          {
+            stdin = lib.pipe dirs [
+              (map depot.users.Profpatsch.netencode.gen.dwim)
+              depot.users.Profpatsch.netstring.toNetstringList
+            ];
+          } [
+          "importas"
+          "out"
+          "out"
+          "if"
+          [ bins.mkdir "-p" "$out" ]
+          "forstdin"
+          "-d"
+          ""
+          "-o"
+          "0"
+          "line"
+          "pipeline"
+          [
+            depot.users.Profpatsch.execline.print-one-env
+            "line"
+          ]
+          depot.users.Profpatsch.netencode.record-splice-env
+          "importas"
+          "-ui"
+          "originalDir"
+          "originalDir"
+          "importas"
+          "-ui"
+          "stowPackage"
+          "stowPackage"
+          bins.ln
+          "-sT"
+          "$originalDir"
+          "\${out}/\${stowPackage}"
+        ]);
+
+  # this is a dumb way of generating a pure list of packages from a depot namespace.
+  readTreeNamespaceDrvs = namespace:
+    lib.pipe namespace [
+      (lib.filterAttrs (_: v: lib.isDerivation v))
+      (lib.mapAttrsToList (k: v: {
+        name = k;
+        drv = v;
+      }))
+    ];
+
+  scriptsStow =
+    lib.pipe { } [
+      (_: makeStowDir [{
+        stowPackage = "scripts";
+        originalDir = pkgs.linkFarm "scripts-farm"
+          ([
+            {
+              name = "scripts/ytextr";
+              path = depot.users.Profpatsch.ytextr;
+            }
+            {
+              name = "scripts/lorri-wait-for-eval";
+              path = depot.users.Profpatsch.lorri-wait-for-eval;
+            }
+            {
+              name = "scripts/lw";
+              path = depot.users.Profpatsch.lorri-wait-for-eval;
+            }
+
+          ]
+          ++
+          (lib.pipe depot.users.Profpatsch.aliases [
+            readTreeNamespaceDrvs
+            (map ({ name, drv }: {
+              name = "scripts/${name}";
+              path = drv;
+            }))
+          ]));
+      }])
+      (d: runStow {
+        stowDir = d;
+        stowPackage = "scripts";
+        targetDir = "/home/philip";
+        stowDirOriginPath = "/home/philip/.local/share/nix-home/stow-origin";
+      })
+    ];
+
+
+
+  terminalEmulatorStow =
+    lib.pipe { } [
+      (_: makeStowDir [{
+        stowPackage = "terminal-emulator";
+        originalDir = pkgs.linkFarm "terminal-emulator-farm"
+          ([
+            {
+              name = "bin/terminal-emulator";
+              path = depot.users.Profpatsch.alacritty;
+            }
+          ]);
+
+      }])
+      (d: runStow {
+        stowDir = d;
+        stowPackage = "terminal-emulator";
+        targetDir = "/home/philip";
+        # TODO: this should only be done once, in a single runStow instead of multiple
+        stowDirOriginPath = "/home/philip/.local/share/nix-home/stow-origin-terminal-emulator";
+      })
+    ];
+
+in
+
+# TODO: run multiple stows with runStow?
+  # TODO: temp setup
+depot.nix.writeExecline "nix-home" { } [
+  "if"
+  [ scriptsStow ]
+  terminalEmulatorStow
+]
diff --git a/users/Profpatsch/nix-tools.nix b/users/Profpatsch/nix-tools.nix
new file mode 100644
index 0000000000..4f29274573
--- /dev/null
+++ b/users/Profpatsch/nix-tools.nix
@@ -0,0 +1,159 @@
+{ depot, pkgs, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.nix [ "nix-build" "nix-instantiate" ];
+
+  # TODO: both of these donโ€™t prevent `result` from being created. good? bad?
+
+  # Usage (execline syntax):
+  #    nix-run { -A foo <more_nix_options> } args...
+  #
+  # Takes an execline block of `nix-build` arguments, which should produce an executable store path.
+  # Then runs the store path with `prog...`.
+  nix-run = depot.nix.writeExecline "nix-run" { argMode = "env"; } [
+    "backtick"
+    "-iE"
+    "storepath"
+    [
+      runblock
+      "1"
+      bins.nix-build
+    ]
+    runblock
+    "-r"
+    "2"
+    "$storepath"
+  ];
+
+  # Usage (execline syntax):
+  #    nix-run-bin { -A foo <more_nix_options> } <foo_bin_name> args...
+  #
+  # Takes an execline block of `nix-build` arguments, which should produce a store path with a bin/ directory in it.
+  # Then runs the given command line with the given arguments. All executables in the built storepathโ€™s bin directory are prepended to `PATH`.
+  nix-run-bin = depot.nix.writeExecline "nix-run-bin" { argMode = "env"; } [
+    "backtick"
+    "-iE"
+    "storepath"
+    [
+      runblock
+      "1"
+      bins.nix-build
+    ]
+    "importas"
+    "-ui"
+    "PATH"
+    "PATH"
+    "export"
+    "PATH"
+    "\${storepath}/bin:\${PATH}"
+    runblock
+    "-r"
+    "2"
+  ];
+
+  nix-eval = depot.nix.writeExecline "nix-eval" { } [
+    bins.nix-instantiate
+    "--read-write-mode"
+    "--eval"
+    "--strict"
+    "$@"
+  ];
+
+  # This is a rewrite of execlineโ€™s runblock.
+  # It adds the feature that instead of just
+  # executing the block it reads, it can also
+  # pass it as argv to given commands.
+  #
+  # This is going to be added to a future version
+  # of execline by skarnet, but for now itโ€™s easier
+  # to just dirtily reimplement it in Python.
+  #
+  # TODO: this was added to recent execline versions,
+  # but it doesnโ€™t seem to be a drop-in replacement,
+  # if I use execlineโ€™s runblock in nix-run-bin above,
+  # I get errors like
+  # > export: fatal: unable to exec runblock: Success
+  runblock = pkgs.writers.writePython3 "runblock"
+    {
+      flakeIgnore = [ "E501" "E226" ];
+    } ''
+    import sys
+    import os
+    from pathlib import Path
+
+    skip = False
+    one = sys.argv[1]
+    if one == "-r":
+        skip = True
+        block_number = int(sys.argv[2])
+        block_start = 3
+    elif one.startswith("-"):
+        print("runblock-python: only -r supported", file=sys.stderr)
+        sys.exit(100)
+    else:
+        block_number = int(one)
+        block_start = 2
+
+    execline_argv_no = int(os.getenvb(b"#"))
+    runblock_argv = [os.getenv(str(no)) for no in range(1, execline_argv_no + 1)]
+
+
+    def parse_block(args):
+        new_args = []
+        if args == []:
+            print(
+                "runblock-python: empty block",
+                file=sys.stderr
+            )
+            sys.exit(100)
+        for arg in args:
+            if arg == "":
+                break
+            elif arg.startswith(" "):
+                new_args.append(arg[1:])
+            else:
+                print(
+                    "runblock-python: unterminated block: {}".format(args),
+                    file=sys.stderr
+                )
+                sys.exit(100)
+        args_rest = args[len(new_args)+1:]
+        return (new_args, args_rest)
+
+
+    if skip:
+        rest = runblock_argv
+        for _ in range(0, block_number-1):
+            (_, rest) = parse_block(rest)
+        new_argv = rest
+    else:
+        new_argv = []
+        rest = runblock_argv
+        for _ in range(0, block_number):
+            (new_argv, rest) = parse_block(rest)
+
+    given_argv = sys.argv[block_start:]
+    run = given_argv + new_argv
+    if os.path.isabs(run[0]):
+        # TODO: ideally Iโ€™d check if itโ€™s an executable here, but it was too hard to figure out and I couldnโ€™t be bothered tbh
+        if not Path(run[0]).is_file():
+            print(
+                "runblock-python: Executable {} does not exist or is not a file.".format(run[0]),
+                file=sys.stderr
+            )
+            sys.exit(100)
+    os.execvp(
+        file=run[0],
+        args=run
+    )
+  '';
+
+
+in
+{
+  inherit
+    nix-run
+    nix-run-bin
+    nix-eval
+    ;
+}
diff --git a/users/Profpatsch/nixpkgs-rewriter/MetaStdenvLib.hs b/users/Profpatsch/nixpkgs-rewriter/MetaStdenvLib.hs
deleted file mode 100644
index 3ed96a7b6e..0000000000
--- a/users/Profpatsch/nixpkgs-rewriter/MetaStdenvLib.hs
+++ /dev/null
@@ -1,80 +0,0 @@
-{-# LANGUAGE PartialTypeSignatures #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE NamedFieldPuns #-}
-import Nix.Parser
-import Nix.Expr.Types
-import Nix.Expr.Types.Annotated
-import System.Environment (getArgs)
-import System.Exit (die)
-import Data.Fix (Fix(..))
-import qualified Data.Text as Text
-import qualified Data.ByteString.Lazy.Char8 as BL
-import qualified Data.Aeson as A
-import qualified Data.Aeson.Encoding as A
-import Data.Function ((&))
-import qualified System.IO as IO
-import qualified Text.Megaparsec.Pos as MP
-
-main = do
-  (nixFile:_) <- getArgs
-  (parseNixFileLoc nixFile :: IO _) >>= \case
-    Failure err -> do
-      ePutStrLn $ show err
-      die "oh no"
-    Success expr -> do
-      case snd $ match expr of
-        NoArguments -> do
-          ePutStrLn $ "NoArguments in " <> nixFile
-          printPairs mempty
-        YesLib vars -> do
-          ePutStrLn $ "lib in " <> show vars <> " in " <> nixFile
-          printPairs mempty
-        NoLib vars srcSpan -> do
-          ePutStrLn $ nixFile <> " needs lib added"
-          printPairs
-            $ "fileName" A..= nixFile
-            <> "fromLine" A..= (srcSpan & spanBegin & sourceLine)
-            <> "fromColumn" A..= (srcSpan & spanBegin & sourceColumn)
-            <> "toLine" A..= (srcSpan & spanEnd & sourceLine)
-            <> "toColumn" A..= (srcSpan & spanEnd & sourceColumn)
-
-printPairs pairs = BL.putStrLn $ A.encodingToLazyByteString $ A.pairs pairs
-
-ePutStrLn = IO.hPutStrLn IO.stderr
-
-data Descend = YesDesc | NoDesc
-  deriving Show
-data Matched =  NoArguments | NoLib [VarName] SrcSpan | YesLib [VarName]
-  deriving Show
-
-match :: Fix (Compose (Ann SrcSpan) NExprF) -> (Descend, Matched)
-match = \case
-  (AnnE outerSpan (NAbs (ParamSet params _ _) (AnnE innerSpan _))) -> (NoDesc,
-    let vars = map fst params in
-    case (any (== "lib") vars) of
-      True -> YesLib vars
-      False ->
-          -- The span of the arglist is from the beginning of the match
-          -- to the beginning of the inner expression
-          let varSpan = SrcSpan
-                { spanBegin = outerSpan & spanBegin
-                -- -1 to prevent the spans from overlapping
-                , spanEnd = sourcePosMinus1 (innerSpan & spanBegin) }
-          in NoLib vars varSpan)
-  _ -> (NoDesc, NoArguments)
-
--- | Remove one from a source positon.
---
--- That means if the current position is at the very beginning of a line,
--- jump to the previous line.
-sourcePosMinus1 :: SourcePos -> SourcePos
-sourcePosMinus1 src@(SourcePos { sourceLine, sourceColumn }) =
-  let
-    col = MP.mkPos $ max (MP.unPos sourceColumn - 1) 1
-    line = MP.mkPos $ case MP.unPos sourceColumn of
-      1 -> max (MP.unPos sourceLine - 1) 1
-      _ -> MP.unPos sourceLine
-  in src
-    { sourceLine = line
-    , sourceColumn = col }
diff --git a/users/Profpatsch/nixpkgs-rewriter/default.nix b/users/Profpatsch/nixpkgs-rewriter/default.nix
deleted file mode 100644
index 9dac018441..0000000000
--- a/users/Profpatsch/nixpkgs-rewriter/default.nix
+++ /dev/null
@@ -1,112 +0,0 @@
-{ depot, pkgs, ... }:
-let
-  inherit (depot.nix)
-    writeExecline
-    ;
-  inherit (depot.users.Profpatsch.lib)
-    debugExec
-    ;
-
-  bins = depot.nix.getBins pkgs.coreutils [ "head" "shuf" ]
-      // depot.nix.getBins pkgs.jq [ "jq" ]
-      // depot.nix.getBins pkgs.findutils [ "xargs" ]
-      // depot.nix.getBins pkgs.gnused [ "sed" ]
-      ;
-
-  export-json-object = pkgs.writers.writePython3 "export-json-object" {} ''
-    import json
-    import sys
-    import os
-
-    d = json.load(sys.stdin)
-
-    if d == {}:
-        sys.exit(0)
-
-    for k, v in d.items():
-        os.environ[k] = str(v)
-
-    os.execvp(sys.argv[1], sys.argv[1:])
-  '';
-
-  meta-stdenv-lib = pkgs.writers.writeHaskell "meta-stdenv-lib" {
-    libraries = [
-      pkgs.haskellPackages.hnix
-      pkgs.haskellPackages.aeson
-    ];
-  } ./MetaStdenvLib.hs;
-
-  replace-between-lines = writeExecline "replace-between-lines" { readNArgs = 1; } [
-    "importas" "-ui" "file" "fileName"
-    "importas" "-ui" "from" "fromLine"
-    "importas" "-ui" "to" "toLine"
-    "if" [ depot.tools.eprintf "%s-%s\n" "$from" "$to" ]
-    (debugExec "adding lib")
-    bins.sed
-      "-e" "\${from},\${to} \${1}"
-      "-i" "$file"
-  ];
-
-  add-lib-if-necessary = writeExecline "add-lib-if-necessary" { readNArgs = 1; } [
-    "pipeline" [ meta-stdenv-lib "$1" ]
-     export-json-object
-     # first replace any stdenv.lib mentions in the arg header
-     # if this is not done, the replace below kills these.
-     # Since we want it anyway ultimately, letโ€™s do it here.
-     "if" [ replace-between-lines "s/stdenv\.lib/lib/" ]
-     # then add the lib argument
-     # (has to be before stdenv, otherwise default arguments might be in the way)
-     replace-between-lines "s/stdenv/lib, stdenv/"
-  ];
-
-  metaString = ''meta = with stdenv.lib; {'';
-
-  replace-stdenv-lib = pkgs.writers.writeBash "replace-stdenv-lib" ''
-    set -euo pipefail
-    sourceDir="$1"
-    for file in $(
-      ${pkgs.ripgrep}/bin/rg \
-        --files-with-matches \
-        --fixed-strings \
-        -e '${metaString}' \
-        "$sourceDir"
-    )
-    do
-      echo "replacing stdenv.lib meta in $file" >&2
-      ${bins.sed} -e '/${metaString}/ s/stdenv.lib/lib/' \
-          -i "$file"
-      ${add-lib-if-necessary} "$file"
-    done
-  '';
-
-  instantiate-nixpkgs-randomly = writeExecline "instantiate-nixpkgs-randomly" { readNArgs = 1; } [
-    "export" "NIXPKGS_ALLOW_BROKEN" "1"
-    "export" "NIXPKGS_ALLOW_UNFREE" "1"
-    "export" "NIXPKGS_ALLOW_INSECURE" "1"
-    "export" "NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM" "1"
-    "pipeline" [
-      "nix"
-        "eval"
-        "--raw"
-        ''(
-          let pkgs = import ''${1} {};
-          in builtins.toJSON (builtins.attrNames pkgs)
-        )''
-    ]
-    "pipeline" [ bins.jq "-r" ".[]" ]
-    "pipeline" [ bins.shuf ]
-    "pipeline" [ bins.head "-n" "1000" ]
-    bins.xargs "-I" "{}" "-n1"
-    "if" [ depot.tools.eprintf "instantiating %s\n" "{}" ]
-    "nix-instantiate" "$1" "-A" "{}"
-  ];
-
-in depot.nix.utils.drvTargets {
-  inherit
-   instantiate-nixpkgs-randomly
-  # requires hnix, which we donโ€™t want in tvl for now
-  # uncomment manually if you want to use it.
-  #   meta-stdenv-lib
-  #   replace-stdenv-lib
-    ;
-}
diff --git a/users/Profpatsch/openlab-tools/Main.hs b/users/Profpatsch/openlab-tools/Main.hs
new file mode 100644
index 0000000000..d5f958a38a
--- /dev/null
+++ b/users/Profpatsch/openlab-tools/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import OpenlabTools qualified
+
+main :: IO ()
+main = OpenlabTools.main
diff --git a/users/Profpatsch/openlab-tools/default.nix b/users/Profpatsch/openlab-tools/default.nix
new file mode 100644
index 0000000000..82641989f7
--- /dev/null
+++ b/users/Profpatsch/openlab-tools/default.nix
@@ -0,0 +1,69 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  #   bins = depot.nix.getBins pkgs.sqlite ["sqlite3"];
+
+  openlab-tools = pkgs.haskellPackages.mkDerivation {
+    pname = "openlab-tools";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./openlab-tools.cabal
+      ./Main.hs
+      ./src/OpenlabTools.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-prelude
+      depot.users.Profpatsch.my-webstuff
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-json
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.pa-field-parser
+      pkgs.haskellPackages.pa-run-command
+      pkgs.haskellPackages.aeson-better-errors
+      pkgs.haskellPackages.blaze-html
+      pkgs.haskellPackages.deepseq
+      pkgs.haskellPackages.case-insensitive
+      pkgs.haskellPackages.hs-opentelemetry-sdk
+      pkgs.haskellPackages.http-conduit
+      pkgs.haskellPackages.http-types
+      pkgs.haskellPackages.ihp-hsx
+      pkgs.haskellPackages.monad-logger
+      pkgs.haskellPackages.selective
+      pkgs.haskellPackages.unliftio
+      pkgs.haskellPackages.wai-extra
+      pkgs.haskellPackages.warp
+      pkgs.haskellPackages.tagsoup
+      pkgs.haskellPackages.time
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+  bins = depot.nix.getBins openlab-tools [ "openlab-tools" ];
+
+in
+
+depot.nix.writeExecline "openlab-tools-wrapped" { } [
+  "importas"
+  "-i"
+  "PATH"
+  "PATH"
+  "export"
+  "PATH"
+  "${pkgs.postgresql}/bin:$${PATH}"
+  "export"
+  "OPENLAB_TOOLS_TOOLS"
+  (pkgs.linkFarm "openlab-tools-tools" [
+    {
+      name = "pg_format";
+      path = "${pkgs.pgformatter}/bin/pg_format";
+    }
+  ])
+  bins.openlab-tools
+]
+
diff --git a/users/Profpatsch/openlab-tools/openlab-tools.cabal b/users/Profpatsch/openlab-tools/openlab-tools.cabal
new file mode 100644
index 0000000000..b7d217e051
--- /dev/null
+++ b/users/Profpatsch/openlab-tools/openlab-tools.cabal
@@ -0,0 +1,111 @@
+cabal-version:      3.0
+name:               openlab-tools
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+  default-language: GHC2021
+
+
+library
+    import: common-options
+
+    hs-source-dirs: src
+
+    exposed-modules:
+       OpenlabTools
+
+    build-depends:
+        base >=4.15 && <5,
+        text,
+        my-prelude,
+        my-webstuff,
+        pa-prelude,
+        pa-error-tree,
+        pa-label,
+        pa-json,
+        pa-field-parser,
+        pa-run-command,
+        aeson-better-errors,
+        aeson,
+        blaze-html,
+        bytestring,
+        containers,
+        deepseq,
+        unordered-containers,
+        exceptions,
+        filepath,
+        hs-opentelemetry-sdk,
+        hs-opentelemetry-api,
+        http-conduit,
+        http-types,
+        ihp-hsx,
+        monad-logger,
+        mtl,
+        network-uri,
+        scientific,
+        selective,
+        unliftio,
+        wai-extra,
+        wai,
+        warp,
+        tagsoup,
+        time,
+        stm,
+        case-insensitive
+
+executable openlab-tools
+    import: common-options
+
+    main-is: Main.hs
+
+    ghc-options:
+      -threaded
+
+    build-depends:
+        base >=4.15 && <5,
+        openlab-tools
diff --git a/users/Profpatsch/openlab-tools/src/OpenlabTools.hs b/users/Profpatsch/openlab-tools/src/OpenlabTools.hs
new file mode 100644
index 0000000000..9fe51aba18
--- /dev/null
+++ b/users/Profpatsch/openlab-tools/src/OpenlabTools.hs
@@ -0,0 +1,551 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module OpenlabTools where
+
+import Control.Concurrent.STM hiding (atomically, readTVarIO)
+import Control.DeepSeq (NFData, deepseq)
+import Control.Monad.Logger qualified as Logger
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Aeson.BetterErrors qualified as Json
+import Data.CaseInsensitive qualified as CaseInsensitive
+import Data.Error.Tree
+import Data.HashMap.Strict qualified as HashMap
+import Data.List qualified as List
+import Data.Maybe (listToMaybe)
+import Data.Text qualified as Text
+import Data.Time (NominalDiffTime, UTCTime (utctDayTime), diffUTCTime, getCurrentTime)
+import Data.Time qualified as Time
+import Data.Time.Clock (addUTCTime)
+import Data.Time.Format qualified as Time.Format
+import Debug.Trace
+import FieldParser (FieldParser' (..))
+import FieldParser qualified as Field
+import GHC.Records (HasField (..))
+import GHC.Stack qualified
+import IHP.HSX.QQ (hsx)
+import Json qualified
+import Label
+import Network.HTTP.Client.Conduit qualified as Http
+import Network.HTTP.Simple qualified as Http
+import Network.HTTP.Types
+import Network.HTTP.Types qualified as Http
+import Network.Wai qualified as Wai
+import Network.Wai.Handler.Warp qualified as Warp
+import Network.Wai.Parse qualified as Wai
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import OpenTelemetry.Trace.Core qualified as Otel hiding (inSpan, inSpan')
+import OpenTelemetry.Trace.Monad qualified as Otel
+import Parse (Parse)
+import Parse qualified
+import PossehlAnalyticsPrelude
+import Pretty
+import System.Environment qualified as Env
+import System.IO qualified as IO
+import Text.Blaze.Html.Renderer.Pretty qualified as Html.Pretty
+import Text.Blaze.Html.Renderer.Utf8 qualified as Html
+import Text.Blaze.Html5 qualified as Html
+import Text.HTML.TagSoup qualified as Soup
+import UnliftIO hiding (Handler, newTVarIO)
+import Prelude hiding (span, until)
+
+mapallSpaceOla :: Text
+mapallSpaceOla = "https://mapall.space/heatmap/show.php?id=OpenLab+Augsburg"
+
+mainPage :: Html.Html
+mainPage =
+  Html.docTypeHtml
+    [hsx|
+          <head>
+            <title>Openlab Augsburg Tools</title>
+            <meta charset="utf-8">
+            <meta name="viewport" content="width=device-width, initial-scale=1">
+          </head>
+
+          <body>
+            <p>Welcome to the OpenLab Augsburg tools thingy. The idea is to provide some services that can be embedded into our other pages.</p>
+
+            <h2>Whatโ€™s there</h2>
+            <ul>
+              <li>
+                A <a href="snips/table-opening-hours-last-week">table displaying the opening hours last week</a>, courtesy of <a href={mapallSpaceOla}>mapall.space</a>.
+              </li>
+            </ul>
+
+
+            <h2>Show me the code/how to contribute</h2>
+
+            <p>The source code can be found <a href="https://code.tvl.fyi/tree/users/Profpatsch/openlab-tools">in my user dir in the tvl repo</a>.</p>
+
+            <p>To build the server, clone the repository from <a href="https://code.tvl.fyi/depot.git">https://code.tvl.fyi/depot.git</a>.
+            Then <code>cd</code> into <code>users/Profpatsch</code>, run <code>nix-shell</code>.
+            </p>
+
+            <p>You can now run the server with <code>cabal repl openlab-tools/`</code> by executing the <code>main</code> function inside the GHC repl. It starts on port <code>9099</code>.
+            <br>
+            To try out changes to the code, stop the server with <kbd><kbd>Ctrl</kbd>+<kbd>z</kbd></kbd> and type <code>:reload</code>, then <code>main</code> again.
+            <br>
+            Finally, from within <code>users/Profpatsch</code> you can start a working development environment by installing <var>vscode</var> or <var>vscodium</var> and the <var>Haskell</var> extension. Then run <code>code .</code> from within the directory.
+            </p>
+
+            <p>Once you have a patch, <a href="https://matrix.to/#/@profpatsch:augsburg.one">contact me on Matrix</a> or DM me at <code>irc/libera</code>, nick <code>Profpatsch</code>.
+            </p>
+          </body>
+        |]
+
+debug :: Bool
+debug = False
+
+runApp :: IO ()
+runApp = withTracer $ \tracer -> do
+  let renderHtml =
+        if debug
+          then Html.Pretty.renderHtml >>> stringToText >>> textToBytesUtf8 >>> toLazyBytes
+          else Html.renderHtml
+
+  let runApplication ::
+        (MonadUnliftIO m, MonadLogger m) =>
+        ( Wai.Request ->
+          (Wai.Response -> m Wai.ResponseReceived) ->
+          m Wai.ResponseReceived
+        ) ->
+        m ()
+      runApplication app = do
+        withRunInIO $ \runInIO -> Warp.run 9099 $ \req respond -> do
+          let catchAppException act =
+                try act >>= \case
+                  Right a -> pure a
+                  Left (AppException err) -> do
+                    runInIO (logError err)
+                    respond (Wai.responseLBS Http.status500 [] "")
+          liftIO $ catchAppException (runInIO $ app req (\resp -> liftIO $ respond resp))
+
+  let appT :: AppT IO () = do
+        let h extra res = Wai.responseLBS Http.ok200 (("Content-Type", "text/html") : extra) res
+        runHandlers
+          runApplication
+          [ Handler
+              { path = "",
+                body =
+                  Body
+                    (pure ())
+                    (\((), _) -> pure $ h [] (renderHtml mainPage))
+              },
+            Handler
+              { path = "snips/table-opening-hours-last-week",
+                body =
+                  Body
+                    ((label @"ifModifiedSince" <$> parseIfModifiedSince))
+                    ( \(req', cache) -> do
+                        now <- liftIO getCurrentTime <&> mkSecondTime
+                        new <- updateCacheIfNewer now cache heatmap
+                        let cacheToHeaders =
+                              [ ("Last-Modified", new.lastModified & formatHeaderTime),
+                                ("Expires", new.until & formatHeaderTime),
+                                ( "Cache-Control",
+                                  let maxAge = new.until `diffSecondTime` now
+                                   in [fmt|max-age={maxAge & floor @NominalDiffTime @Int  & show}, immutable|]
+                                )
+                              ]
+                        if
+                            -- If the last cache update is newer or equal to the requested version, we can tell the browser itโ€™s fine
+                            | Just modifiedSince <- req'.ifModifiedSince,
+                              modifiedSince >= new.lastModified ->
+                                pure $ Wai.responseLBS Http.status304 cacheToHeaders ""
+                            | otherwise ->
+                                pure $ h cacheToHeaders (new.result & toLazyBytes)
+                    )
+              }
+          ]
+
+  runReaderT (appT :: AppT IO ()).unAppT Context {..}
+  where
+    -- "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified#syntax"
+    headerFormat = "%a, %d %b %0Y %T GMT"
+    formatHeaderTime (SecondTime t) =
+      t
+        & Time.Format.formatTime
+          @UTCTime
+          Time.Format.defaultTimeLocale
+          headerFormat
+        & stringToText
+        & textToBytesUtf8
+    parseHeaderTime =
+      Field.utf8
+        >>> ( FieldParser $ \t ->
+                t
+                  & textToString
+                  & Time.Format.parseTimeM
+                    @Maybe
+                    @UTCTime
+                    {-no leading whitespace -} False
+                    Time.Format.defaultTimeLocale
+                    headerFormat
+                  & annotate [fmt|Cannot parse header timestamp "{t}"|]
+            )
+    parseIfModifiedSince :: Parse Wai.Request (Maybe SecondTime)
+    parseIfModifiedSince =
+      lmap
+        ( (.requestHeaders)
+            >>> findMaybe
+              ( \(h, v) ->
+                  if "If-Modified-Since" == CaseInsensitive.mk h then Just v else Nothing
+              )
+        )
+        (Parse.maybe $ Parse.fieldParser parseHeaderTime)
+        & rmap (fmap mkSecondTime)
+
+parseRequest :: (MonadThrow f, MonadIO f) => Otel.Span -> Parse from a -> from -> f a
+parseRequest span parser req =
+  Parse.runParse "Unable to parse the HTTP request" parser req
+    & assertM span id
+
+heatmap :: AppT IO ByteString
+heatmap = do
+  Http.httpBS [fmt|GET {mapallSpaceOla}|]
+    <&> (.responseBody)
+    <&> Soup.parseTags
+    <&> Soup.canonicalizeTags
+    <&> findHeatmap
+    <&> fromMaybe (htmlToTags [hsx|<p>Uh oh! could not fetch the table from <a href={mapallSpaceOla}>{mapallSpaceOla}</a></p>|])
+    <&> Soup.renderTags
+  where
+    firstSection f t = t & Soup.sections f & listToMaybe
+    match :: Soup.Tag ByteString -> Soup.Tag ByteString -> Bool
+    match x (t :: Soup.Tag ByteString) = (Soup.~==) @ByteString t x
+    findHeatmap t =
+      t
+        & firstSection (match (Soup.TagOpen ("") [("class", "heatmap")]))
+        >>= firstSection (match (Soup.TagOpen "table" []))
+        <&> getTable
+        <&> (<> htmlToTags [hsx|<figcaption>source: <a href={mapallSpaceOla} target="_blank">mapall.space</a></figcaption>|])
+        <&> wrapTagStream (T2 (label @"el" "figure") (label @"attrs" []))
+
+    -- get the table from opening tag to closing tag (allowing nested tables)
+    getTable = go 0
+      where
+        go _ [] = []
+        go d (el : els)
+          | match (Soup.TagOpen "table" []) el = el : go (d + 1) els
+          | match (Soup.TagClose "table") el = if d <= 1 then [el] else el : go (traceShowId $ d - 1) els
+          | otherwise = el : go d els
+
+    htmlToTags :: Html.Html -> [Soup.Tag ByteString]
+    htmlToTags h = h & Html.renderHtml & toStrictBytes & Soup.parseTags
+
+    -- TODO: this is dog-slow because of the whole list recreation!
+    wrapTagStream ::
+      T2 "el" ByteString "attrs" [Soup.Attribute ByteString] ->
+      [Soup.Tag ByteString] ->
+      [Soup.Tag ByteString]
+    wrapTagStream tag inner = (Soup.TagOpen (tag.el) tag.attrs : inner) <> [Soup.TagClose tag.el]
+
+main :: IO ()
+main =
+  runApp
+
+-- ( do
+--     -- todo: trace that to the init functions as well
+--     Otel.inSpan "whatcd-resolver main function" Otel.defaultSpanArguments $ do
+--       _ <- runTransaction migrate
+--       htmlUi
+-- )
+
+data Handler m = Handler
+  { path :: Text,
+    body :: Body m
+  }
+
+data Body m
+  = forall a.
+    Body
+      (Parse Wai.Request a)
+      ((a, TVar (Cache ByteString)) -> m Wai.Response)
+
+runHandlers ::
+  (Otel.MonadTracer m, MonadUnliftIO m, MonadThrow m) =>
+  -- ( (Wai.Request -> (Wai.Response -> m Wai.ResponseReceived) -> m Wai.ResponseReceived) ->
+  --   m ()
+  -- ) ->
+  ( (Wai.Request -> (Wai.Response -> m a) -> m a) ->
+    m ()
+  ) ->
+  [Handler m] ->
+  m ()
+runHandlers runApplication handlers = do
+  withCaches ::
+    [ T2
+        "handler"
+        (Handler m)
+        "cache"
+        (TVar (Cache ByteString))
+    ] <-
+    handlers
+      & traverse
+        ( \h -> do
+            cache <- liftIO $ newCache h.path "nothing yet"
+            pure $ T2 (label @"handler" h) (label @"cache" cache)
+        )
+  runApplication $ \req respond -> do
+    let mHandler =
+          withCaches
+            & List.find
+              ( \h ->
+                  (h.handler.path)
+                    == (req & Wai.pathInfo & Text.intercalate "/")
+              )
+    case mHandler of
+      Nothing -> respond $ Wai.responseLBS Http.status404 [] "nothing here (yet)"
+      Just handler -> do
+        inSpan' "TODO" $ \span -> do
+          case handler.handler.body of
+            Body parse runHandler -> do
+              req' <- req & parseRequest span parse
+              resp <- runHandler (req', handler.cache)
+              respond resp
+
+inSpan :: (MonadUnliftIO m, Otel.MonadTracer m) => Text -> m a -> m a
+inSpan name = Otel.inSpan name Otel.defaultSpanArguments
+
+inSpan' :: (MonadUnliftIO m, Otel.MonadTracer m) => Text -> (Otel.Span -> m a) -> m a
+-- inSpan' name =  Otel.inSpan' name Otel.defaultSpanArguments
+inSpan' _name act = act (error "todo telemetry disabled")
+
+zipT2 ::
+  forall l1 l2 t1 t2.
+  ( HasField l1 (T2 l1 [t1] l2 [t2]) [t1],
+    HasField l2 (T2 l1 [t1] l2 [t2]) [t2]
+  ) =>
+  T2 l1 [t1] l2 [t2] ->
+  [T2 l1 t1 l2 t2]
+zipT2 xs =
+  zipWith
+    (\t1 t2 -> T2 (label @l1 t1) (label @l2 t2))
+    (getField @l1 xs)
+    (getField @l2 xs)
+
+unzipT2 :: forall l1 t1 l2 t2. [T2 l1 t1 l2 t2] -> T2 l1 [t1] l2 [t2]
+unzipT2 xs = xs <&> toTup & unzip & fromTup
+  where
+    toTup :: forall a b. T2 a t1 b t2 -> (t1, t2)
+    toTup (T2 a b) = (getField @a a, getField @b b)
+    fromTup :: (a, b) -> T2 l1 a l2 b
+    fromTup (t1, t2) = T2 (label @l1 t1) (label @l2 t2)
+
+unzipT3 :: forall l1 t1 l2 t2 l3 t3. [T3 l1 t1 l2 t2 l3 t3] -> T3 l1 [t1] l2 [t2] l3 [t3]
+unzipT3 xs = xs <&> toTup & unzip3 & fromTup
+  where
+    toTup :: forall a b c. T3 a t1 b t2 c t3 -> (t1, t2, t3)
+    toTup (T3 a b c) = (getField @a a, getField @b b, getField @c c)
+    fromTup :: (a, b, c) -> T3 l1 a l2 b l3 c
+    fromTup (t1, t2, t3) = T3 (label @l1 t1) (label @l2 t2) (label @l3 t3)
+
+newtype Optional a = OptionalInternal (Maybe a)
+
+mkOptional :: a -> Optional a
+mkOptional defaultValue = OptionalInternal $ Just defaultValue
+
+defaults :: Optional a
+defaults = OptionalInternal Nothing
+
+instance HasField "withDefault" (Optional a) (a -> a) where
+  getField (OptionalInternal m) defaultValue = case m of
+    Nothing -> defaultValue
+    Just a -> a
+
+httpJson ::
+  ( MonadIO m,
+    MonadThrow m
+  ) =>
+  (Optional (Label "contentType" ByteString)) ->
+  Otel.Span ->
+  Json.Parse ErrorTree b ->
+  Http.Request ->
+  m b
+httpJson opts span parser req = do
+  let opts' = opts.withDefault (label @"contentType" "application/json")
+  Http.httpBS req
+    >>= assertM
+      span
+      ( \resp -> do
+          let statusCode = resp & Http.responseStatus & (.statusCode)
+              contentType =
+                resp
+                  & Http.responseHeaders
+                  & List.lookup "content-type"
+                  <&> Wai.parseContentType
+                  <&> (\(ct, _mimeAttributes) -> ct)
+          if
+              | statusCode == 200,
+                Just ct <- contentType,
+                ct == opts'.contentType ->
+                  Right $ (resp & Http.responseBody)
+              | statusCode == 200,
+                Just otherType <- contentType ->
+                  Left [fmt|Server returned a non-json body, with content-type "{otherType}"|]
+              | statusCode == 200,
+                Nothing <- contentType ->
+                  Left [fmt|Server returned a body with unspecified content type|]
+              | code <- statusCode -> Left [fmt|Server returned an non-200 error code, code {code}: {resp & showPretty}|]
+      )
+    >>= assertM
+      span
+      ( \body ->
+          Json.parseStrict parser body
+            & first (Json.parseErrorTree "could not parse redacted response")
+      )
+
+assertM :: (MonadThrow f, MonadIO f) => Otel.Span -> (t -> Either ErrorTree a) -> t -> f a
+assertM span f v = case f v of
+  Right a -> pure a
+  Left err -> appThrowTree span err
+
+-- | UTC time that is only specific to the second
+newtype SecondTime = SecondTime {unSecondTime :: UTCTime}
+  deriving newtype (Show, Eq, Ord)
+
+mkSecondTime :: UTCTime -> SecondTime
+mkSecondTime utcTime = SecondTime utcTime {utctDayTime = Time.secondsToDiffTime $ floor utcTime.utctDayTime}
+
+diffSecondTime :: SecondTime -> SecondTime -> NominalDiffTime
+diffSecondTime (SecondTime a) (SecondTime b) = diffUTCTime a b
+
+data Cache a = Cache
+  { name :: !Text,
+    until :: !SecondTime,
+    lastModified :: !SecondTime,
+    result :: !a
+  }
+  deriving (Show)
+
+newCache :: Text -> a -> IO (TVar (Cache a))
+newCache name result = do
+  let until = mkSecondTime $ Time.UTCTime {utctDay = Time.ModifiedJulianDay 1, utctDayTime = 1}
+  let lastModified = until
+  newTVarIO $ Cache {..}
+
+updateCache :: (NFData a, Eq a) => SecondTime -> TVar (Cache a) -> a -> STM (Cache a)
+updateCache now cache result' = do
+  -- make sure we donโ€™t hold onto the world by deepseq-ing and evaluating to WHNF
+  let !result = deepseq result' result'
+  let until = mkSecondTime $ (5 * 60) `addUTCTime` now.unSecondTime
+  !toWrite <- do
+    old <- readTVar cache
+    let name = old.name
+    -- only update the lastModified time iff the content changed (this is helpful for HTTP caching with If-Modified-Since)
+    if old.result == result
+      then do
+        let lastModified = old.lastModified
+        pure $ Cache {..}
+      else do
+        let lastModified = now
+        pure $ Cache {..}
+  _ <- writeTVar cache $! toWrite
+  pure toWrite
+
+-- | Run the given action iff the cache is stale, otherwise just return the item from the cache.
+updateCacheIfNewer :: (MonadUnliftIO m, NFData b, Eq b) => SecondTime -> TVar (Cache b) -> m b -> m (Cache b)
+updateCacheIfNewer now cache act = withRunInIO $ \runInIO -> do
+  old <- readTVarIO cache
+  if old.until < now
+    then do
+      res <- runInIO act
+      atomically $ updateCache now cache res
+    else pure old
+
+-- pgFormat <- readTools (label @"toolsEnvVar" "OPENLAB_TOOLS_TOOLS") (readTool "pg_format")
+-- let config = label @"logDatabaseQueries" LogDatabaseQueries
+-- pgConnPool <-
+--   Pool.newPool $
+--     Pool.defaultPoolConfig
+--       {- resource init action -} (Postgres.connectPostgreSQL (db & TmpPg.toConnectionString))
+--       {- resource destruction -} Postgres.close
+--       {- unusedResourceOpenTime -} 10
+--       {- max resources across all stripes -} 20
+-- transmissionSessionId <- newEmptyMVar
+-- let newAppT = do
+--       logInfo [fmt|Running with config: {showPretty config}|]
+--       logInfo [fmt|Connected to database at {db & TmpPg.toDataDirectory} on socket {db & TmpPg.toConnectionString}|]
+--       appT
+-- runReaderT newAppT.unAppT Context {..}
+
+withTracer :: (Otel.Tracer -> IO c) -> IO c
+withTracer f = do
+  setDefaultEnv "OTEL_SERVICE_NAME" "whatcd-resolver"
+  bracket
+    -- Install the SDK, pulling configuration from the environment
+    Otel.initializeGlobalTracerProvider
+    -- Ensure that any spans that haven't been exported yet are flushed
+    Otel.shutdownTracerProvider
+    -- Get a tracer so you can create spans
+    (\tracerProvider -> f $ Otel.makeTracer tracerProvider "whatcd-resolver" Otel.tracerOptions)
+
+setDefaultEnv :: String -> String -> IO ()
+setDefaultEnv envName defaultValue = do
+  Env.lookupEnv envName >>= \case
+    Just _env -> pure ()
+    Nothing -> Env.setEnv envName defaultValue
+
+data Context = Context
+  { tracer :: Otel.Tracer
+  }
+
+newtype AppT m a = AppT {unAppT :: ReaderT Context m a}
+  deriving newtype (Functor, Applicative, Monad, MonadIO, MonadUnliftIO, MonadThrow)
+
+data AppException = AppException Text
+  deriving stock (Show)
+  deriving anyclass (Exception)
+
+-- | A specialized variant of @addEvent@ that records attributes conforming to
+-- the OpenTelemetry specification's
+-- <https://github.com/open-telemetry/opentelemetry-specification/blob/49c2f56f3c0468ceb2b69518bcadadd96e0a5a8b/specification/trace/semantic_conventions/exceptions.md semantic conventions>
+--
+-- @since 0.0.1.0
+recordException ::
+  ( MonadIO m,
+    HasField "message" r Text,
+    HasField "type_" r Text
+  ) =>
+  Otel.Span ->
+  r ->
+  m ()
+recordException span dat = liftIO $ do
+  callStack <- GHC.Stack.whoCreated dat.message
+  newEventTimestamp <- Just <$> Otel.getTimestamp
+  Otel.addEvent span $
+    Otel.NewEvent
+      { newEventName = "exception",
+        newEventAttributes =
+          HashMap.fromList
+            [ ("exception.type", Otel.toAttribute @Text dat.type_),
+              ("exception.message", Otel.toAttribute @Text dat.message),
+              ("exception.stacktrace", Otel.toAttribute @Text $ Text.unlines $ map stringToText callStack)
+            ],
+        ..
+      }
+
+appThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> ErrorTree -> m a
+appThrowTree span exc = do
+  let msg = prettyErrorTree exc
+  -- recordException
+  --   span
+  --   ( T2
+  --       (label @"type_" "AppException")
+  --       (label @"message" msg)
+  --   )
+  throwM $ AppException msg
+
+orAppThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> Either ErrorTree a -> m a
+orAppThrowTree span = \case
+  Left err -> appThrowTree span err
+  Right a -> pure a
+
+instance (MonadIO m) => MonadLogger (AppT m) where
+  monadLoggerLog loc src lvl msg = liftIO $ Logger.defaultOutput IO.stderr loc src lvl (Logger.toLogStr msg)
+
+instance (Monad m) => Otel.MonadTracer (AppT m) where
+  getTracer = AppT $ asks (.tracer)
diff --git a/users/Profpatsch/read-http.nix b/users/Profpatsch/read-http.nix
index 854a11b7d0..d9ad6fc30d 100644
--- a/users/Profpatsch/read-http.nix
+++ b/users/Profpatsch/read-http.nix
@@ -2,15 +2,18 @@
 
 let
 
-  read-http = depot.nix.writers.rustSimple {
-    name = "read-http";
-    dependencies = [
-      depot.third_party.rust-crates.ascii
-      depot.third_party.rust-crates.httparse
-      depot.users.Profpatsch.netencode.netencode-rs
-      depot.users.Profpatsch.arglib.netencode.rust
-      depot.users.Profpatsch.execline.exec-helpers
-    ];
-  } (builtins.readFile ./read-http.rs);
+  read-http = depot.nix.writers.rustSimple
+    {
+      name = "read-http";
+      dependencies = [
+        depot.third_party.rust-crates.ascii
+        depot.third_party.rust-crates.httparse
+        depot.users.Profpatsch.netencode.netencode-rs
+        depot.users.Profpatsch.arglib.netencode.rust
+        depot.users.Profpatsch.execline.exec-helpers
+      ];
+    }
+    (builtins.readFile ./read-http.rs);
 
-in read-http
+in
+read-http
diff --git a/users/Profpatsch/read-http.rs b/users/Profpatsch/read-http.rs
index 50ff663b99..2b24e6beb1 100644
--- a/users/Profpatsch/read-http.rs
+++ b/users/Profpatsch/read-http.rs
@@ -1,37 +1,35 @@
-extern crate httparse;
-extern crate netencode;
 extern crate arglib_netencode;
 extern crate ascii;
 extern crate exec_helpers;
+extern crate httparse;
+extern crate netencode;
 
-use std::os::unix::io::FromRawFd;
-use std::io::Read;
-use std::io::Write;
+use exec_helpers::{die_expected_error, die_temporary, die_user_error};
 use std::collections::HashMap;
-use exec_helpers::{die_user_error, die_expected_error, die_temporary};
+use std::io::{Read, Write};
+use std::os::unix::io::FromRawFd;
 
-use netencode::{U, T, dec};
 use netencode::dec::Decoder;
+use netencode::{dec, T, U};
 
 enum What {
     Request,
-    Response
+    Response,
 }
 
 // reads a http request (stdin), and writes all headers to stdout, as netencoded record.
 // The keys are text, but can be lists of text iff headers appear multiple times, so beware.
 fn main() -> std::io::Result<()> {
-
     exec_helpers::no_args("read-http");
 
     let args = dec::RecordDot {
         field: "what",
         inner: dec::OneOf {
             list: vec!["request", "response"],
-            inner: dec::Text
-        }
+            inner: dec::Text,
+        },
     };
-    let what : What = match args.dec(arglib_netencode::arglib_netencode("read-http", None).to_u()) {
+    let what: What = match args.dec(arglib_netencode::arglib_netencode("read-http", None).to_u()) {
         Ok("request") => What::Request,
         Ok("response") => What::Response,
         Ok(v) => panic!("shouldnโ€™t happen!, value was: {}", v),
@@ -39,7 +37,8 @@ fn main() -> std::io::Result<()> {
     };
 
     fn read_stdin_to_complete<F>(mut parse: F) -> ()
-        where F: FnMut(&[u8]) -> httparse::Result<usize>
+    where
+        F: FnMut(&[u8]) -> httparse::Result<usize>,
     {
         let mut res = httparse::Status::Partial;
         loop {
@@ -48,16 +47,22 @@ fn main() -> std::io::Result<()> {
             }
             let mut buf = [0; 2048];
             match std::io::stdin().read(&mut buf[..]) {
-                Ok(size) => if size == 0 {
-                    break;
-                },
-                Err(err) => die_temporary("read-http", format!("could not read from stdin, {:?}", err))
+                Ok(size) => {
+                    if size == 0 {
+                        break;
+                    }
+                }
+                Err(err) => {
+                    die_temporary("read-http", format!("could not read from stdin, {:?}", err))
+                }
             }
             match parse(&buf) {
                 Ok(status) => {
                     res = status;
                 }
-                Err(err) => die_temporary("read-http", format!("httparse parsing failed: {:#?}", err))
+                Err(err) => {
+                    die_temporary("read-http", format!("httparse parsing failed: {:#?}", err))
+                }
             }
         }
     }
@@ -66,7 +71,10 @@ fn main() -> std::io::Result<()> {
         let mut res = HashMap::new();
         for httparse::Header { name, value } in headers {
             let val = ascii::AsciiStr::from_ascii(*value)
-                .expect(&format!("read-http: we require header values to be ASCII, but the header {} was {:?}", name, value))
+                .expect(&format!(
+                    "read-http: we require header values to be ASCII, but the header {} was {:?}",
+                    name, value
+                ))
                 .as_str();
             // lowercase the header names, since the standard doesnโ€™t care
             // and we want unique strings to match against
@@ -77,13 +85,13 @@ fn main() -> std::io::Result<()> {
                     let name_lower = name.to_lowercase();
                     let _ = res.insert(name_lower, U::List(vec![U::Text(t), U::Text(val)]));
                     ()
-                },
+                }
                 Some(U::List(mut l)) => {
                     let name_lower = name.to_lowercase();
                     l.push(U::Text(val));
                     let _ = res.insert(name_lower, U::List(l));
                     ()
-                },
+                }
                 Some(o) => panic!("read-http: header not text nor list: {:?}", o),
             }
         }
@@ -98,12 +106,14 @@ fn main() -> std::io::Result<()> {
             match chonker.next() {
                 Some(Ok(chunk)) => {
                     buf.extend_from_slice(&chunk);
-                    if chunk.windows(4).any(|c| c == b"\r\n\r\n" ) {
+                    if chunk.windows(4).any(|c| c == b"\r\n\r\n") {
                         return Some(());
                     }
-                },
-                Some(Err(err)) => die_temporary("read-http", format!("error reading from stdin: {:?}", err)),
-                None => return None
+                }
+                Some(Err(err)) => {
+                    die_temporary("read-http", format!("error reading from stdin: {:?}", err))
+                }
+                None => return None,
             }
         }
     }
@@ -118,68 +128,99 @@ fn main() -> std::io::Result<()> {
             let mut buf: Vec<u8> = vec![];
             match read_till_end_of_header(&mut buf, stdin.lock()) {
                 Some(()) => match req.parse(&buf) {
-                    Ok(httparse::Status::Complete(_body_start)) => {},
-                    Ok(httparse::Status::Partial) => die_expected_error("read-http", "httparse should have gotten a full header"),
-                    Err(err) => die_expected_error("read-http", format!("httparse response parsing failed: {:#?}", err))
+                    Ok(httparse::Status::Complete(_body_start)) => {}
+                    Ok(httparse::Status::Partial) => {
+                        die_expected_error("read-http", "httparse should have gotten a full header")
+                    }
+                    Err(err) => die_expected_error(
+                        "read-http",
+                        format!("httparse response parsing failed: {:#?}", err),
+                    ),
                 },
-                None => die_expected_error("read-http", format!("httparse end of stdin reached before able to parse request headers"))
+                None => die_expected_error(
+                    "read-http",
+                    format!("httparse end of stdin reached before able to parse request headers"),
+                ),
             }
             let method = req.method.expect("method must be filled on complete parse");
             let path = req.path.expect("path must be filled on complete parse");
             write_dict_req(method, path, &normalize_headers(req.headers))
-        },
+        }
         Response => {
             let mut resp = httparse::Response::new(&mut headers);
             let mut buf: Vec<u8> = vec![];
             match read_till_end_of_header(&mut buf, stdin.lock()) {
                 Some(()) => match resp.parse(&buf) {
-                    Ok(httparse::Status::Complete(_body_start)) => {},
-                    Ok(httparse::Status::Partial) => die_expected_error("read-http", "httparse should have gotten a full header"),
-                    Err(err) => die_expected_error("read-http", format!("httparse response parsing failed: {:#?}", err))
+                    Ok(httparse::Status::Complete(_body_start)) => {}
+                    Ok(httparse::Status::Partial) => {
+                        die_expected_error("read-http", "httparse should have gotten a full header")
+                    }
+                    Err(err) => die_expected_error(
+                        "read-http",
+                        format!("httparse response parsing failed: {:#?}", err),
+                    ),
                 },
-                None => die_expected_error("read-http", format!("httparse end of stdin reached before able to parse response headers"))
+                None => die_expected_error(
+                    "read-http",
+                    format!("httparse end of stdin reached before able to parse response headers"),
+                ),
             }
             let code = resp.code.expect("code must be filled on complete parse");
-            let reason = resp.reason.expect("reason must be filled on complete parse");
+            let reason = resp
+                .reason
+                .expect("reason must be filled on complete parse");
             write_dict_resp(code, reason, &normalize_headers(resp.headers))
         }
     }
 }
 
-fn write_dict_req<'a, 'buf>(method: &'buf str, path: &'buf str, headers: &'a HashMap<String, U<'a>>) -> std::io::Result<()> {
-    let mut http = vec![
-        ("method", U::Text(method)),
-        ("path", U::Text(path)),
-    ].into_iter().collect();
+fn write_dict_req<'a, 'buf>(
+    method: &'buf str,
+    path: &'buf str,
+    headers: &'a HashMap<String, U<'a>>,
+) -> std::io::Result<()> {
+    let mut http = vec![("method", U::Text(method)), ("path", U::Text(path))]
+        .into_iter()
+        .collect();
     write_dict(http, headers)
 }
 
-fn write_dict_resp<'a, 'buf>(code: u16, reason: &'buf str, headers: &'a HashMap<String, U<'a>>) -> std::io::Result<()> {
+fn write_dict_resp<'a, 'buf>(
+    code: u16,
+    reason: &'buf str,
+    headers: &'a HashMap<String, U<'a>>,
+) -> std::io::Result<()> {
     let mut http = vec![
         ("status", U::N6(code as u64)),
         ("status-text", U::Text(reason)),
-    ].into_iter().collect();
+    ]
+    .into_iter()
+    .collect();
     write_dict(http, headers)
 }
 
-
-fn write_dict<'buf, 'a>(mut http: HashMap<&str, U<'a>>, headers: &'a HashMap<String, U<'a>>) -> std::io::Result<()> {
-    match http.insert("headers", U::Record(
-        headers.iter().map(|(k,v)| (k.as_str(), v.clone())).collect()
-    )) {
+fn write_dict<'buf, 'a>(
+    mut http: HashMap<&str, U<'a>>,
+    headers: &'a HashMap<String, U<'a>>,
+) -> std::io::Result<()> {
+    match http.insert(
+        "headers",
+        U::Record(
+            headers
+                .iter()
+                .map(|(k, v)| (k.as_str(), v.clone()))
+                .collect(),
+        ),
+    ) {
         None => (),
         Some(_) => panic!("read-http: headers already in dict"),
     };
-    netencode::encode(
-        &mut std::io::stdout(),
-        &U::Record(http)
-    )?;
+    netencode::encode(&mut std::io::stdout(), &U::Record(http))?;
     Ok(())
 }
 
-
 // iter helper
-
+// TODO: put into its own module
 struct Chunkyboi<T> {
     inner: T,
     buf: Vec<u8>,
@@ -188,10 +229,7 @@ struct Chunkyboi<T> {
 impl<R: Read> Chunkyboi<R> {
     fn new(inner: R, chunksize: usize) -> Self {
         let buf = vec![0; chunksize];
-        Chunkyboi {
-            inner,
-            buf
-        }
+        Chunkyboi { inner, buf }
     }
 }
 
@@ -205,7 +243,7 @@ impl<R: Read> Iterator for Chunkyboi<R> {
                 // clone a new buffer so we can reuse the internal one
                 Some(Ok(self.buf[..read].to_owned()))
             }
-            Err(err) => Some(Err(err))
+            Err(err) => Some(Err(err)),
         }
     }
 }
diff --git a/users/Profpatsch/reverse-haskell-deps.nix b/users/Profpatsch/reverse-haskell-deps.nix
deleted file mode 100644
index b47347ea9f..0000000000
--- a/users/Profpatsch/reverse-haskell-deps.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-{ depot, pkgs, ... }:
-
-# Parses https://packdeps.haskellers.com/reverse
-# and outputs the amount of reverse dependencies of each hackage package.
-
-let
-
-  rev = depot.nix.writeExecline "reverse-haskell-deps" {} [
-    "pipeline" [
-      "${pkgs.curl}/bin/curl" "-L" "https://packdeps.haskellers.com/reverse"
-    ]
-    rev-hs
-
-  ];
-
-  rev-hs = pkgs.writers.writeHaskell "revers-haskell-deps-hs" {
-    libraries =  [
-      pkgs.haskellPackages.nicify-lib
-      pkgs.haskellPackages.tagsoup
-    ];
-
-  }
-    ./reverse-haskell-deps.hs;
-
-
-in rev
diff --git a/users/Profpatsch/reverse-haskell-deps/README.md b/users/Profpatsch/reverse-haskell-deps/README.md
new file mode 100644
index 0000000000..efc288cae4
--- /dev/null
+++ b/users/Profpatsch/reverse-haskell-deps/README.md
@@ -0,0 +1,3 @@
+# reverse-haskell-deps
+
+Parse the HTML at `https://packdeps.haskellers.com/reverse` to get the data about Haskell package reverse dependencies in a structured way (they should just expose that as a json tbh).
diff --git a/users/Profpatsch/reverse-haskell-deps.hs b/users/Profpatsch/reverse-haskell-deps/ReverseHaskellDeps.hs
index 6b644df9ec..0e18ce8a6b 100644
--- a/users/Profpatsch/reverse-haskell-deps.hs
+++ b/users/Profpatsch/reverse-haskell-deps/ReverseHaskellDeps.hs
@@ -1,72 +1,76 @@
 {-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-import qualified Text.HTML.TagSoup as Tag
-import qualified Data.Text as Text
-import Data.Text (Text)
-import qualified Data.List as List
+
+module Main where
+
+import Data.ByteString qualified as ByteString
+import Data.Either
+import Data.List qualified as List
 import Data.Maybe
-import Text.Nicify
-import qualified Text.Read as Read
+import Data.Text (Text)
+import Data.Text qualified as Text
+import Data.Text.Encoding qualified
+import MyPrelude
 import Numeric.Natural
-import Data.Either
-import qualified Data.ByteString as ByteString
-import qualified Data.Text.Encoding
+import Text.HTML.TagSoup qualified as Tag
+import Text.Nicify
+import Text.Read qualified as Read
 
-parseNat :: Text.Text -> Maybe Natural
-parseNat = Read.readMaybe . Text.unpack
+parseNat :: Text -> Maybe Natural
+parseNat = Read.readMaybe . textToString
 
 printNice :: Show a => a -> IO ()
 printNice = putStrLn . nicify . show
 
-type Tag = Tag.Tag Text.Text
+type Tag = Tag.Tag Text
 
 main = do
   reverseHtml <- readStdinUtf8
   printNice $ List.sortOn snd $ packagesAndReverseDeps reverseHtml
-
   where
-    readStdinUtf8 = Data.Text.Encoding.decodeUtf8 <$> ByteString.getContents
+    readStdinUtf8 = bytesToTextUtf8Lenient <$> ByteString.getContents
 
 -- | reads the table provided by https://packdeps.haskellers.com/reverse
 -- figuring out all sections (starting with the link to the package name),
 -- then figuring out the name of the package and the first column,
 -- which is the number of reverse dependencies of the package
+packagesAndReverseDeps :: Text -> [(Text, Natural)]
 packagesAndReverseDeps reverseHtml = do
   let tags = Tag.parseTags reverseHtml
-  let sections =  Tag.partitions (isJust . reverseLink) tags
-  let sectionNames = map (fromJust . reverseLink . head) sections
+  let sections = Tag.partitions (isJust . reverseLink) tags
+  let sectionName [] = "<unknown section>"
+      sectionName (sect : _) = sect & reverseLink & fromMaybe "<unknown section>"
+  let sectionNames = map sectionName sections
   mapMaybe
-    (\(name :: Text.Text, sect) -> do
+    ( \(name :: Text, sect) -> do
         reverseDeps <- firstNaturalNumber sect
-        pure (sectionPackageName name sect, reverseDeps) :: Maybe (Text.Text, Natural))
+        pure (sectionPackageName name sect, reverseDeps) :: Maybe (Text, Natural)
+    )
     $ zip sectionNames sections
-
-
   where
     reverseLink = \case
-      Tag.TagOpen "a" attrs -> mapFind attrReverseLink attrs
+      Tag.TagOpen "a" attrs -> findMaybe attrReverseLink attrs
       _ -> Nothing
 
     attrReverseLink = \case
-      ("href", lnk) -> if
-          | "packdeps.haskellers.com/reverse/" `Text.isInfixOf` lnk -> Just lnk
-          | otherwise -> Nothing
+      ("href", lnk) ->
+        if
+            | "packdeps.haskellers.com/reverse/" `Text.isInfixOf` lnk -> Just lnk
+            | otherwise -> Nothing
       _ -> Nothing
 
     sectionPackageName :: Text -> [Tag] -> Text
     sectionPackageName sectionName = \case
-      (_: Tag.TagText name : _) -> name
-      (_: el : _) -> sectionName
+      (_ : Tag.TagText name : _) -> name
+      (_ : el : _) -> sectionName
       xs -> sectionName
 
-
     firstNaturalNumber :: [Tag] -> Maybe Natural
     firstNaturalNumber =
-      mapFind (\case
-        Tag.TagText t -> parseNat t
-        _ -> Nothing)
-
-    mapFind :: (a -> Maybe b) -> [a] -> Maybe b
-    mapFind f xs = fromJust . f <$> List.find (isJust . f) xs
+      findMaybe
+        ( \case
+            Tag.TagText t -> parseNat t
+            _ -> Nothing
+        )
diff --git a/users/Profpatsch/reverse-haskell-deps/default.nix b/users/Profpatsch/reverse-haskell-deps/default.nix
new file mode 100644
index 0000000000..b0a44420d7
--- /dev/null
+++ b/users/Profpatsch/reverse-haskell-deps/default.nix
@@ -0,0 +1,32 @@
+{ depot, pkgs, ... }:
+
+# Parses https://packdeps.haskellers.com/reverse
+# and outputs the amount of reverse dependencies of each hackage package.
+
+let
+
+  rev = depot.nix.writeExecline "reverse-haskell-deps" { } [
+    "pipeline"
+    [
+      "${pkgs.curl}/bin/curl"
+      "-L"
+      "https://packdeps.haskellers.com/reverse"
+    ]
+    rev-hs
+
+  ];
+
+  rev-hs = pkgs.writers.writeHaskell "revers-haskell-deps-hs"
+    {
+      libraries = [
+        depot.users.Profpatsch.my-prelude
+        pkgs.haskellPackages.nicify-lib
+        pkgs.haskellPackages.tagsoup
+      ];
+      ghcArgs = [ "-threaded" ];
+    }
+    ./ReverseHaskellDeps.hs;
+
+
+in
+rev
diff --git a/users/Profpatsch/reverse-haskell-deps/reverse-haskell-deps.cabal b/users/Profpatsch/reverse-haskell-deps/reverse-haskell-deps.cabal
new file mode 100644
index 0000000000..4792f52adf
--- /dev/null
+++ b/users/Profpatsch/reverse-haskell-deps/reverse-haskell-deps.cabal
@@ -0,0 +1,16 @@
+cabal-version:      3.0
+name:               reverse-haskell-deps
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+library
+    exposed-modules:          ReverseHaskellDeps.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        my-prelude,
+        tagsoup,
+        nicify-lib
+
+    default-language: Haskell2010
diff --git a/users/Profpatsch/shell.nix b/users/Profpatsch/shell.nix
new file mode 100644
index 0000000000..b5095d476f
--- /dev/null
+++ b/users/Profpatsch/shell.nix
@@ -0,0 +1,110 @@
+# generic shell.nix that can be used for most of my projects here,
+# until I figure out a way to have composable shells.
+let root = (import ../../. { }); in
+{ pkgs ? root.third_party.nixpkgs, depot ? root, ... }:
+
+pkgs.mkShell {
+
+  buildInputs = [
+    pkgs.sqlite-interactive
+    pkgs.sqlite-utils
+    pkgs.haskell-language-server
+    pkgs.cabal-install
+    (pkgs.haskellPackages.ghcWithHoogle (h: [
+      h.async
+      h.aeson-better-errors
+      h.blaze-html
+      h.conduit-extra
+      h.error
+      h.monad-logger
+      h.pa-field-parser
+      h.pa-label
+      h.pa-json
+      h.pa-pretty
+      h.pa-run-command
+      h.ihp-hsx
+      h.PyF
+      h.foldl
+      h.unliftio
+      h.xml-conduit
+      h.wai
+      h.wai-extra
+      h.warp
+      h.profunctors
+      h.semigroupoids
+      h.validation-selective
+      h.free
+      h.cryptonite-conduit
+      h.sqlite-simple
+      h.hedgehog
+      h.http-conduit
+      h.http-conduit
+      h.wai-conduit
+      h.nonempty-containers
+      h.deriving-compat
+      h.unix
+      h.tagsoup
+      h.attoparsec
+      h.iCalendar
+      h.case-insensitive
+      h.hscolour
+      h.nicify-lib
+      h.hspec
+      h.hspec-expectations-pretty-diff
+      h.tmp-postgres
+      h.postgresql-simple
+      h.resource-pool
+      h.xmonad-contrib
+      h.hs-opentelemetry-sdk
+      h.punycode
+    ]))
+
+    pkgs.rustup
+    pkgs.pkg-config
+    pkgs.fuse
+    pkgs.postgresql_14
+    pkgs.nodejs
+    pkgs.ninja
+    pkgs.s6
+    pkgs.caddy
+
+    (depot.nix.binify {
+      name = "nix-run";
+      exe = depot.users.Profpatsch.nix-tools.nix-run;
+    })
+  ];
+
+  DEPOT_ROOT = toString ./../..;
+  PROFPATSCH_ROOT = toString ./.;
+
+  WHATCD_RESOLVER_TOOLS = pkgs.linkFarm "whatcd-resolver-tools" [
+    {
+      name = "pg_format";
+      path = "${pkgs.pgformatter}/bin/pg_format";
+    }
+  ];
+
+  # DECLIB_MASTODON_ACCESS_TOKEN read from `pass` in .envrc.
+
+  RUSTC_WRAPPER =
+    let
+      wrapperArgFile = libs: pkgs.writeText "rustc-wrapper-args"
+        (pkgs.lib.concatStringsSep
+          "\n"
+          (pkgs.lib.concatLists
+            (map
+              (lib: [
+                "-L"
+                "${pkgs.lib.getLib lib}/lib"
+              ])
+              libs)));
+    in
+    depot.nix.writeExecline "rustc-wrapper" { readNArgs = 1; } [
+      "$1"
+      "$@"
+      "@${wrapperArgFile [
+      depot.third_party.rust-crates.nom
+    ]}"
+    ];
+
+}
diff --git a/users/Profpatsch/shortcuttable/default.nix b/users/Profpatsch/shortcuttable/default.nix
new file mode 100644
index 0000000000..13ba220400
--- /dev/null
+++ b/users/Profpatsch/shortcuttable/default.nix
@@ -0,0 +1,172 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  # run prog... and restart whenever SIGHUP is received
+  #
+  # this is useful for binding to a shortcut.
+  #
+  # Unfortunately, this requires a bunch of workarounds around the semantics of `trap`,
+  # but the general idea of bundling subprocesses with `setsid` is somewhat sound.
+  runShortcuttable =
+    depot.nix.writeExecline "run-shortcuttable" { } [
+      "importas"
+      "-i"
+      "run"
+      "XDG_RUNTIME_DIR"
+      "if"
+      [ "mkdir" "-p" "\${run}/shortcuttable/test" ]
+      "getpid"
+      "-E"
+      "controlpid"
+      savePid
+      "\${run}/shortcuttable/test/control"
+      "$controlpid"
+
+      # start the program
+      "background"
+      [
+        startSaveSID
+        "\${run}/shortcuttable/test/running-sid"
+        "$@"
+      ]
+
+      "trap"
+      [
+        "SIGHUP"
+        [
+          "if"
+          [ "echo" "got hup" ]
+          "if"
+          [
+            "if"
+            [ "echo" "killing our child processes" ]
+            "envfile"
+            "\${run}/shortcuttable/test/running-sid"
+            "importas"
+            "-ui"
+            "child_sid"
+            "pid"
+            "foreground"
+            [ "ps" "-f" "--sid" "$child_sid" ]
+            ctrlCCtrlDSid
+            "$child_sid"
+          ]
+          "if"
+          [ "echo" "restarting into" "$@" ]
+          "background"
+          [
+            startSaveSID
+            "\${run}/shortcuttable/test/running-sid"
+            "$@"
+          ]
+        ]
+        "SIGTERM"
+        [
+          (killShortcuttable { signal = "TERM"; })
+          "\${run}/shortcuttable/test/running-sid"
+          "\${run}/shortcuttable/test/exit"
+        ]
+        "SIGINT"
+        [
+          (killShortcuttable { signal = "INT"; })
+          "\${run}/shortcuttable/test/running-sid"
+          "\${run}/shortcuttable/test/exit"
+        ]
+      ]
+      depot.users.Profpatsch.execline.setsid
+      "child_sid"
+      "getpid"
+      "-E"
+      "exitpid"
+      savePid
+      "\${run}/shortcuttable/test/exit"
+      "$exitpid"
+      "sleep"
+      "infinity"
+    ];
+
+  killShortcuttable = { signal }: depot.nix.writeExecline "kill-shortcuttable" { readNArgs = 2; } [
+    "if"
+    [ "echo" "got SIG${signal}, quitting" ]
+    "if"
+    [
+      "envfile"
+      "$1"
+      "importas"
+      "-ui"
+      "child_sid"
+      "pid"
+      "foreground"
+      [ "ps" "-f" "--sid" "$child_sid" ]
+      ctrlCCtrlDSid
+      "$child_sid"
+    ]
+    "if"
+    [ "echo" "killing shortcuttable loop" ]
+    "envfile"
+    "$2"
+    "importas"
+    "-ui"
+    "trap_pid"
+    "pid"
+    "foreground"
+    [ "ps" "-fp" "$trap_pid" ]
+    "kill"
+    "--signal"
+    signal
+    "$trap_pid"
+  ];
+
+  savePid = depot.nix.writeExecline "save-pid" { readNArgs = 2; } [
+    "if"
+    [ "echo" "saving process:" ]
+    "if"
+    [ "ps" "-fp" "$2" ]
+    "if"
+    [
+      "redirfd"
+      "-w"
+      "1"
+      "$1"
+      "printf"
+      "pid = %s\n"
+      "$2"
+    ]
+    "$@"
+  ];
+
+  # try to kill process, first with SIGTERM then SIGQUIT (in case itโ€™s a repl)
+  ctrlCCtrlDSid = depot.nix.writeExecline "ctrl-c-ctrl-d" { readNArgs = 1; } [
+    "ifelse"
+    "-n"
+    [ "kill" "--signal" "TERM" "--" "-\${1}" ]
+    [
+      "if"
+      [ "echo" "could not kill via SIGTERM, trying SIGQUIT โ€ฆ" ]
+      "ifelse"
+      "-n"
+      [ "kill" "--signal" "QUIT" "--" "-\${1}" ]
+      [ "echo" "SIGQUIT failed as well, keeping it running" ]
+      "$@"
+    ]
+    "$@"
+  ];
+
+  startSaveSID = depot.nix.writeExecline "start-save-sid" { readNArgs = 1; } [
+    depot.users.Profpatsch.execline.setsid
+    "child_sid"
+    "importas"
+    "-ui"
+    "child_sid"
+    "child_sid"
+    "if"
+    [ "echo" "children sid:" "$child_sid" ]
+    savePid
+    "$1"
+    "$child_sid"
+    "$@"
+  ];
+
+
+in
+runShortcuttable
diff --git a/users/Profpatsch/struct-edit/default.nix b/users/Profpatsch/struct-edit/default.nix
deleted file mode 100644
index 970cdd4d02..0000000000
--- a/users/Profpatsch/struct-edit/default.nix
+++ /dev/null
@@ -1,13 +0,0 @@
-{ depot, ... }:
-depot.nix.buildGo.program {
-    name = "struct-edit";
-    srcs = [
-      ./main.go
-    ];
-    deps = [
-      depot.third_party.gopkgs."github.com".charmbracelet.bubbletea
-      depot.third_party.gopkgs."github.com".charmbracelet.lipgloss
-      depot.third_party.gopkgs."github.com".muesli.termenv
-      depot.third_party.gopkgs."github.com".mattn.go-isatty
-    ];
-}
diff --git a/users/Profpatsch/struct-edit/main.go b/users/Profpatsch/struct-edit/main.go
deleted file mode 100644
index 7e43074266..0000000000
--- a/users/Profpatsch/struct-edit/main.go
+++ /dev/null
@@ -1,431 +0,0 @@
-package main
-
-import (
-	json "encoding/json"
-	"fmt"
-	"log"
-	"os"
-	"strings"
-	"sort"
-
-	tea "github.com/charmbracelet/bubbletea"
-	lipgloss "github.com/charmbracelet/lipgloss"
-	// termenv "github.com/muesli/termenv"
-	// isatty "github.com/mattn/go-isatty"
-)
-
-// Keeps the full data structure and a path that indexes our current position into it.
-type model struct {
-	path          []index
-	data          val
-}
-
-// an index into a value, uint for lists and string for maps.
-// nil for any scalar value.
-// TODO: use an actual interface for these
-type index interface{}
-
-/// recursive value that we can represent.
-type val struct {
-	// the โ€œtypeโ€ of value; see tag const belove
-	tag tag
-	// last known position of our cursor
-	last_index index
-	// documentation (TODO)
-	doc string
-	// the actual value;
-	// the actual structure is behind a pointer so we can replace the struct.
-	// determined by the tag
-	// tagString -> *string
-	// tagFloat -> *float64
-	// tagList -> *[]val
-	// tagMap -> *map[string]val
-	val interface{}
-}
-
-type tag string
-
-const (
-	tagString tag = "string"
-	tagFloat  tag = "float"
-	tagList   tag = "list"
-	tagMap    tag = "map"
-)
-
-// print a value, flat
-func (v val) Render() string {
-	s := ""
-	switch v.tag {
-	case tagString:
-		s += *v.val.(*string)
-	case tagFloat:
-		s += fmt.Sprint(*v.val.(*float64))
-	case tagList:
-		s += "[ "
-		vs := []string{}
-		for _, enum := range v.enumerate() {
-			vs = append(vs, enum.v.Render())
-		}
-		s += strings.Join(vs, ", ")
-		s += " ]"
-	case tagMap:
-		s += "{ "
-		vs := []string{}
-		for _, enum := range v.enumerate() {
-			vs = append(vs, fmt.Sprintf("%s: %s", enum.i.(string), enum.v.Render()))
-		}
-		s += strings.Join(vs, ", ")
-		s += " }"
-	default:
-		s += fmt.Sprintf("<unknown: %v>", v)
-	}
-	return s
-}
-
-// render an index, depending on the type
-func renderIndex(i index) (s string) {
-	switch i := i.(type) {
-	case nil:
-		s = ""
-	// list index
-	case uint:
-		s = "*"
-	// map index
-	case string:
-		s = i + ":"
-	}
-	return
-}
-
-// take an arbitrary (within restrictions) go value and construct a val from it
-func makeVal(i interface{}) val {
-	var v val
-	switch i := i.(type) {
-	case string:
-		v = val{
-			tag: tagString,
-			last_index: index(nil),
-			doc: "",
-			val: &i,
-		}
-	case float64:
-		v = val{
-			tag: tagFloat,
-			last_index: index(nil),
-			doc: "",
-			val: &i,
-		}
-	case []interface{}:
-		ls := []val{}
-		for _, i := range i {
-			ls = append(ls, makeVal(i))
-		}
-		v = val{
-			tag: tagList,
-			last_index: pos1Inner(tagList, &ls),
-			doc: "",
-			val: &ls,
-		}
-	case map[string]interface{}:
-		ls := map[string]val{}
-		for k, i := range i {
-			ls[k] = makeVal(i)
-		}
-		v = val{
-			tag: tagMap,
-			last_index: pos1Inner(tagMap, &ls),
-			doc: "",
-			val: &ls,
-		}
-	default:
-		log.Fatalf("makeVal: cannot read json of type %T", i)
-	}
-	return v
-}
-
-// return an index that points at the first entry in val
-func (v val) pos1() index {
-	return v.enumerate()[0].i
-}
-
-func pos1Inner(tag tag, v interface{}) index {
-	return enumerateInner(tag, v)[0].i
-}
-
-type enumerate struct {
-	i index
-	v val
-}
-
-// enumerate gives us a stable ordering of elements in this val.
-// for scalars itโ€™s just a nil index & the val itself.
-// Guaranteed to always return at least one element.
-func (v val) enumerate() (e []enumerate) {
-	e = enumerateInner(v.tag, v.val)
-	if e == nil {
-		e = append(e, enumerate{
-			i: nil,
-			v: v,
-		})
-	}
-	return
-}
-
-// like enumerate, but returns an empty slice for scalars without inner vals.
-func enumerateInner(tag tag, v interface{}) (e []enumerate) {
-	switch tag {
-	case tagString:
-		fallthrough
-	case tagFloat:
-		e = nil
-	case tagList:
-		for i, v := range *v.(*[]val) {
-			e = append(e, enumerate{i: index(uint(i)), v: v})
-		}
-	case tagMap:
-		// map sorting order is not stable (actually randomized thank jabber)
-		// so letโ€™s sort them
-		keys := []string{}
-		m := *v.(*map[string]val)
-		for k, _ := range m {
-			keys = append(keys, k)
-		}
-		sort.Strings(keys)
-		for _, k := range keys {
-			e = append(e, enumerate{i: index(k), v: m[k]})
-		}
-	default:
-		log.Fatalf("unknown val tag %s, %v", tag, v)
-	}
-	return
-}
-
-func (m model) PathString() string {
-	s := "/ "
-	var is []string
-	for _, v := range m.path {
-		is = append(is, fmt.Sprintf("%v", v))
-	}
-	s += strings.Join(is, " / ")
-	return s
-}
-
-// walk the given path down in data, to get the value at that point.
-// Assumes that all path indexes are valid indexes into data.
-// Returns a pointer to the value at point, in order to be able to change it.
-func walk(data *val, path []index) (*val, bool, error) {
-	res := data
-	atPath := func(index int) string {
-		return fmt.Sprintf("at path %v", path[:index+1])
-	}
-	errf := func(ty string, val interface{}, index int) error {
-		return fmt.Errorf("walk: canโ€™t walk into %s %v %s", ty, val, atPath(index))
-	}
-	for i, p := range path {
-		switch res.tag {
-		case tagString:
-			return nil, true, nil
-		case tagFloat:
-			return nil, true, nil
-		case tagList:
-			switch p := p.(type) {
-			case uint:
-				list := *res.val.(*[]val)
-				if int(p) >= len(list) || p < 0 {
-					return nil, false, fmt.Errorf("index out of bounds %s", atPath(i))
-				}
-				res = &list[p]
-			default:
-				return nil, false, fmt.Errorf("not a list index %s", atPath(i))
-			}
-		case tagMap:
-			switch p := p.(type) {
-			case string:
-				m := *res.val.(*map[string]val)
-				if a, ok := m[p]; ok {
-					res = &a
-				} else {
-				  return nil, false, fmt.Errorf("index %s not in map %s", p, atPath(i))
-				}
-			default:
-				return nil, false, fmt.Errorf("not a map index %v %s", p, atPath(i))
-			}
-
-		default:
-			return nil, false, errf(string(res.tag), res.val, i)
-		}
-	}
-	return res, false, nil
-}
-
-// descend into the selected index. Assumes that the index is valid.
-// Will not descend into scalars.
-func (m model) descend() (model, error) {
-	// TODO: two walks?!
-	this, _, err := walk(&m.data, m.path)
-	if err != nil {
-		return m, err
-	}
-	newPath := append(m.path, this.last_index)
-	_, bounce, err := walk(&m.data, newPath)
-	if err != nil {
-		return m, err
-	}
-	// only descend if we *can*
-	if !bounce {
-		m.path = newPath
-	}
-	return m, nil
-}
-
-// ascend to one level up. stops at the root.
-func (m model) ascend() (model, error) {
-	if len(m.path) > 0 {
-		m.path = m.path[:len(m.path)-1]
-		_, _, err := walk(&m.data, m.path)
-		return m, err
-	}
-	return m, nil
-}
-
-/// go to the next item, or wraparound
-func (min model) next() (m model, err error) {
-	m = min
-	this, _, err := walk(&m.data, m.path)
-	if err != nil {
-		return
-	}
-	enumL := this.enumerate()
-	setNext := false
-	for _, enum := range enumL {
-		if setNext {
-			this.last_index = enum.i
-			setNext = false
-			break
-		}
-		if enum.i == this.last_index {
-			setNext = true
-		}
-	}
-	// wraparound
-	if setNext {
-		this.last_index = enumL[0].i
-	}
-	return
-}
-
-/// go to the previous item, or wraparound
-func (min model) prev() (m model, err error) {
-	m = min
-	this, _, err := walk(&m.data, m.path)
-	if err != nil {
-		return
-	}
-	enumL := this.enumerate()
-	// last element, wraparound
-	prevIndex := enumL[len(enumL)-1].i
-	for _, enum := range enumL {
-		if enum.i == this.last_index {
-			this.last_index = prevIndex
-			break
-		}
-		prevIndex = enum.i
-	}
-	return
-}
-
-/// bubbletea implementations
-
-func (m model) Init() tea.Cmd {
-	return nil
-}
-
-func initialModel(v interface{}) model {
-	val := makeVal(v)
-	return model{
-		path:          []index{},
-		data:          val,
-	}
-}
-
-func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
-	var err error
-	switch msg := msg.(type) {
-	case tea.KeyMsg:
-		switch msg.String() {
-		case "ctrl+c", "q":
-			return m, tea.Quit
-
-		case "up":
-			m, err = m.prev()
-
-		case "down":
-			m, err = m.next()
-
-		case "right":
-			m, err = m.descend()
-
-		case "left":
-			m, err = m.ascend()
-
-			// 	case "enter":
-			// 		_, ok := m.selected[m.cursor]
-			// 		if ok {
-			// 			delete(m.selected, m.cursor)
-			// 		} else {
-			// 			m.selected[m.cursor] = struct{}{}
-			// 		}
-		}
-
-	}
-	if err != nil {
-		log.Fatal(err)
-	}
-	return m, nil
-}
-
-var pathColor = lipgloss.NewStyle().
-	// light blue
-	Foreground(lipgloss.Color("12"))
-
-var selectedColor = lipgloss.NewStyle().
-	Bold(true)
-
-func (m model) View() string {
-	s := pathColor.Render(m.PathString())
-	cur, _, err := walk(&m.data, m.path)
-	if err != nil {
-		log.Fatal(err)
-	}
-	s += cur.doc + "\n"
-	s += "\n"
-	for _, enum := range cur.enumerate() {
-		is := renderIndex(enum.i)
-		if is != "" {
-			s += is + " "
-		}
-		if enum.i == cur.last_index {
-			s += selectedColor.Render(enum.v.Render())
-		} else {
-			s += enum.v.Render()
-		}
-		s += "\n"
-	}
-
-	// s += fmt.Sprintf("%v\n", m)
-	// s += fmt.Sprintf("%v\n", cur)
-
-	return s
-}
-
-func main() {
-	var input interface{}
-	err := json.NewDecoder(os.Stdin).Decode(&input)
-	if err != nil {
-		log.Fatal("json from stdin: ", err)
-	}
-	p := tea.NewProgram(initialModel(input))
-	if err := p.Start(); err != nil {
-		log.Fatal("bubbletea TUI error: ", err)
-	}
-}
diff --git a/users/Profpatsch/sync-abfall-ics-aichach-friedberg/README.md b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/README.md
new file mode 100644
index 0000000000..e0a6aa2fb8
--- /dev/null
+++ b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/README.md
@@ -0,0 +1,3 @@
+# sync-abfall-ics-aichach-friedberg
+
+A small tool to sync the ICS files for the local trash collection times at https://abfallwirtschaft.lra-aic-fdb.de/
diff --git a/users/Profpatsch/sync-abfall-ics-aichach-friedberg/default.nix b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/default.nix
new file mode 100644
index 0000000000..739274cb6f
--- /dev/null
+++ b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/default.nix
@@ -0,0 +1,31 @@
+{ depot, pkgs, ... }:
+
+let
+  sync-to-dir = depot.users.Profpatsch.writers.python3
+    {
+      name = "sync-ics-to-dir";
+      libraries = (py: [
+        py.httpx
+        py.icalendar
+      ]);
+    } ./sync-ics-to-dir.py;
+
+  config =
+    depot.users.Profpatsch.importDhall.importDhall
+      {
+        root = ./..;
+        files = [
+          "sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall"
+          "dhall/lib.dhall"
+          "ini/ini.dhall"
+        ];
+        main = "sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall";
+        deps = [
+        ];
+      }
+      depot.users.Profpatsch.ini.externs;
+
+
+
+in
+{ inherit config; }
diff --git a/users/Profpatsch/sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall
new file mode 100644
index 0000000000..2a7ac84979
--- /dev/null
+++ b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/ics-to-caldav.dhall
@@ -0,0 +1,139 @@
+let Ini = ../ini/ini.dhall
+
+let Lib = ../dhall/lib.dhall
+
+in  \(Ini/externs : Ini.Externs) ->
+      let Vdirsyncer =
+            let StorageType =
+                  < FileSystem : { path : Text, fileext : < ICS > }
+                  | Http : { url : Text }
+                  >
+
+            let Collection = < FromA | FromB | Collection : Text >
+
+            let Collections =
+                  < Unspecified | TheseCollections : List Collection >
+
+            let Storage = { storageName : Text, storage : StorageType }
+
+            in  { Storage
+                , StorageType
+                , Collection
+                , Collections
+                , Pair =
+                    { pairName : Text
+                    , a : Storage
+                    , b : Storage
+                    , collections : Collections
+                    }
+                }
+
+      let toIniSections
+          : Vdirsyncer.Pair -> Ini.Sections
+          = \(pair : Vdirsyncer.Pair) ->
+              let
+                  -- we assume the names are [a-zA-Z_]
+                  renderList =
+                    \(l : List Text) ->
+                          "["
+                      ++  Lib.Text/concatMapSep
+                            ", "
+                            Text
+                            (\(t : Text) -> "\"${t}\"")
+                            l
+                      ++  "]"
+
+              in  let nv = \(name : Text) -> \(value : Text) -> { name, value }
+
+                  let mkStorage =
+                        \(storage : Vdirsyncer.Storage) ->
+                          { name = "storage ${storage.storageName}"
+                          , value =
+                              merge
+                                { FileSystem =
+                                    \ ( fs
+                                      : { path : Text, fileext : < ICS > }
+                                      ) ->
+                                      [ nv "type" "filesystem"
+                                      , nv
+                                          "fileext"
+                                          (merge { ICS = ".ics" } fs.fileext)
+                                      , nv "path" fs.path
+                                      ]
+                                , Http =
+                                    \(http : { url : Text }) ->
+                                      [ nv "type" "http", nv "url" http.url ]
+                                }
+                                storage.storage
+                          }
+
+                  in  [ { name = "pair ${pair.pairName}"
+                        , value =
+                          [ nv "a" pair.a.storageName
+                          , nv "b" pair.b.storageName
+                          , nv
+                              "collections"
+                              ( merge
+                                  { Unspecified = "none"
+                                  , TheseCollections =
+                                      \(colls : List Vdirsyncer.Collection) ->
+                                        renderList
+                                          ( Lib.List/map
+                                              Vdirsyncer.Collection
+                                              Text
+                                              ( \ ( coll
+                                                  : Vdirsyncer.Collection
+                                                  ) ->
+                                                  merge
+                                                    { FromA = "from a"
+                                                    , FromB = "from b"
+                                                    , Collection =
+                                                        \(t : Text) -> t
+                                                    }
+                                                    coll
+                                              )
+                                              colls
+                                          )
+                                  }
+                                  pair.collections
+                              )
+                          ]
+                        }
+                      , mkStorage pair.a
+                      , mkStorage pair.b
+                      ]
+
+      in  { example =
+              Ini/externs.renderIni
+                ( Ini.appendInis
+                    ( Lib.List/map
+                        Vdirsyncer.Pair
+                        Ini.Ini
+                        ( \(pair : Vdirsyncer.Pair) ->
+                            { globalSection = [] : Ini.Section
+                            , sections = toIniSections pair
+                            }
+                        )
+                        (   [ { pairName = "testPair"
+                              , a =
+                                { storageName = "mystor"
+                                , storage =
+                                    Vdirsyncer.StorageType.FileSystem
+                                      { path = "./test-ics"
+                                      , fileext = < ICS >.ICS
+                                      }
+                                }
+                              , b =
+                                { storageName = "mystor"
+                                , storage =
+                                    Vdirsyncer.StorageType.Http
+                                      { url = "https://profpatsch.de" }
+                                }
+                              , collections = Vdirsyncer.Collections.Unspecified
+                              }
+                            ]
+                          : List Vdirsyncer.Pair
+                        )
+                    )
+                )
+          }
diff --git a/users/Profpatsch/sync-abfall-ics-aichach-friedberg/sync-ics-to-dir.py b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/sync-ics-to-dir.py
new file mode 100644
index 0000000000..4af3b9fb85
--- /dev/null
+++ b/users/Profpatsch/sync-abfall-ics-aichach-friedberg/sync-ics-to-dir.py
@@ -0,0 +1,133 @@
+# horrible little module that fetches ICS files for the local trash public service.
+#
+# It tries its best to not overwrite existing ICS files in case the upstream goes down
+# or returns empty ICS files.
+import sys
+import httpx
+import asyncio
+import icalendar
+from datetime import datetime
+import syslog
+import os.path
+
+# Internal id for the street (extracted from the ics download url)
+ortsteil_id = "e9c32ab3-df25-4660-b88e-abda91897d7a"
+
+# They are using a numeric encoding to refer to different kinds of trash
+fraktionen = {
+    "restmรผll": "1",
+    "bio": "5",
+    "papier": "7",
+    "gelbe_tonne": "13",
+    "problemmรผllsammlung": "20"
+}
+
+def ics_url(year):
+  frakt = ','.join(fraktionen.values())
+  return f'https://awido.cubefour.de/Customer/aic-fdb/KalenderICS.aspx?oid={ortsteil_id}&jahr={year}&fraktionen={frakt}&reminder=1.12:00'
+
+def fetchers_for_years(start_year, no_of_years_in_future):
+    """given a starting year, and a number of years in the future,
+    return the years for which to fetch ics files"""
+    current_year = datetime.now().year
+    max_year = current_year + no_of_years_in_future
+    return {
+        "passed_years": range(start_year, current_year),
+        "this_and_future_years": range(current_year, 1 + max_year)
+    }
+
+async def fetch_ics(c, url):
+    """fetch an ICS file from an URL"""
+    try:
+        resp = await c.get(url)
+    except Exception as e:
+        return { "ics_does_not_exist_exc": e }
+
+    if resp.is_error:
+        return { "ics_does_not_exist": resp }
+    else:
+        try:
+            ics = icalendar.Calendar.from_ical(resp.content)
+            return { "ics": { "ics_parsed": ics, "ics_bytes": resp.content } }
+        except ValueError as e:
+            return { "ics_cannot_be_parsed": e }
+
+def ics_has_events(ics):
+    """Determine if there is any event in the ICS, otherwise we can assume itโ€™s an empty file"""
+    for item in ics.walk():
+      if isinstance(item, icalendar.Event):
+        return True
+    return False
+
+async def write_nonempty_ics(directory, year, ics):
+    # only overwrite if the new ics has any events
+    if ics_has_events(ics['ics_parsed']):
+        path = os.path.join(directory, f"{year}.ics")
+        with open(path, "wb") as f:
+            f.write(ics['ics_bytes'])
+            info(f"wrote ics for year {year} to file {path}")
+    else:
+        info(f"ics for year {year} was empty, skipping")
+
+
+def main():
+    ics_directory = os.getenv("ICS_DIRECTORY", None)
+    if not ics_directory:
+        critical("please set ICS_DIRECTORY")
+    start_year = int(os.getenv("ICS_START_YEAR", 2022))
+    future_years = int(os.getenv("ICS_FUTURE_YEARS", 2))
+
+    years = fetchers_for_years(start_year, no_of_years_in_future=future_years)
+
+
+    async def go():
+        async with httpx.AsyncClient(follow_redirects=True) as c:
+            info(f"fetching ics for passed years: {years['passed_years']}")
+            for year in years["passed_years"]:
+                match await fetch_ics(c, ics_url(year)):
+                    case { "ics_does_not_exist_exc": error }:
+                       warn(f"The ics for the year {year} is gone, error when requesting: {error} for url {ics_url(year)}")
+                    case { "ics_does_not_exist": resp }:
+                       warn(f"The ics for the year {year} is gone, server returned status {resp.status} for url {ics_url(year)}")
+                    case { "ics_cannot_be_parsed": error }:
+                       warn(f"The returned ICS could not be parsed: {error} for url {ics_url(year)}")
+                    case { "ics": ics }:
+                       info(f"fetched ics from {ics_url(year)}")
+                       await write_nonempty_ics(ics_directory, year, ics)
+                    case _:
+                       critical("unknown case for ics result")
+
+
+            info(f"fetching ics for current and upcoming years: {years['this_and_future_years']}")
+            for year in years["this_and_future_years"]:
+                match await fetch_ics(c, ics_url(year)):
+                    case { "ics_does_not_exist_exc": error }:
+                       critical(f"The ics for the year {year} is not available, error when requesting: {error} for url {ics_url(year)}")
+                    case { "ics_does_not_exist": resp }:
+                       critical(f"The ics for the year {year} is not available, server returned status {resp.status} for url {ics_url(year)}")
+                    case { "ics_cannot_be_parsed": error }:
+                       critical(f"The returned ICS could not be parsed: {error} for url {ics_url(year)}")
+                    case { "ics": ics }:
+                       info(f"fetched ics from {ics_url(year)}")
+                       await write_nonempty_ics(ics_directory, year, ics)
+                    case _:
+                       critical("unknown case for ics result")
+
+    asyncio.run(go())
+
+def info(msg):
+    syslog.syslog(syslog.LOG_INFO, msg)
+
+def critical(msg):
+    syslog.syslog(syslog.LOG_CRIT, msg)
+    sys.exit(1)
+
+def warn(msg):
+    syslog.syslog(syslog.LOG_WARNING, msg)
+
+def debug(msg):
+    syslog.syslog(syslog.LOG_DEBUG, msg)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/users/Profpatsch/tagtime/README.md b/users/Profpatsch/tagtime/README.md
new file mode 100644
index 0000000000..ab2c7d14e5
--- /dev/null
+++ b/users/Profpatsch/tagtime/README.md
@@ -0,0 +1,18 @@
+# tagtime reimplementation
+
+Whatโ€™s great about original perl tagtime?
+
+* timestamps are deterministic from the beginning (keep)
+* the tagging system should just work (tm)
+
+Whatโ€™s the problem with the original perl tagtime?
+
+* it uses a bad, arbitrary file format -> sqlite3
+* the query window does not time out, so itโ€™s easy to miss that itโ€™s open (often hidden behind another window), and then the following pings might never appear)
+* Thereโ€™s a bug with tags containing a `.` -> sqlite3
+
+What would be cool to have?
+
+* multi-entry mode (ping on phone and laptop and merge the replies eventually since they will apply to single timestamps)
+* simplifying reporting based on fuzzy matching & history
+* auto-generate nice time reports with hours for work items
diff --git a/users/Profpatsch/toINI.nix b/users/Profpatsch/toINI.nix
new file mode 100644
index 0000000000..537505d30b
--- /dev/null
+++ b/users/Profpatsch/toINI.nix
@@ -0,0 +1,79 @@
+{ lib, ... }:
+let
+  /* Generate an INI-style config file from an attrset
+   * specifying the global section (no header), and a
+   * list of sections which contain name/value pairs.
+   *
+   * generators.toINI {} {
+   *   globalSection = [
+   *     { name = "someGlobalKey"; value = "hi"; }
+   *   ];
+   *   sections = [
+   *     { name = "foo"; value = [
+   *         { name = "hi"; value = "${pkgs.hello}"; }
+   *         { name = "ciao"; value = "bar"; }
+   *       ];
+   *     }
+   *     { name = "baz";
+   *       value = [ { name = "also, integers"; value = 42; } ];
+   *     }
+   *   ];
+   * }
+   *
+   *> someGlobalKey=hi
+   *>
+   *> [foo]
+   *> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
+   *> ciao=bar
+   *>
+   *> [baz]
+   *> also, integers=42
+   *>
+   *
+   * The mk* configuration attributes can generically change
+   * the way sections and key-value strings are generated.
+   *
+   * Order of the sections and of keys is preserved,
+   * duplicate keys are allowed.
+   */
+  toINI =
+    {
+      # apply transformations (e.g. escapes) to section names
+      mkSectionName ? (name: lib.strings.escape [ "[" "]" ] name)
+    , # format a setting line from key and value
+      mkKeyValue ? lib.generators.mkKeyValueDefault { } "="
+    ,
+    }: { globalSection, sections }:
+    let
+      mkSection = sectName: sectValues: ''
+        [${mkSectionName sectName}]
+      '' + toKeyValue { inherit mkKeyValue; } sectValues;
+      # map input to ini sections
+      mkSections = lib.strings.concatMapStringsSep "\n"
+        ({ name, value }: mkSection name value)
+        sections;
+      mkGlobalSection =
+        if globalSection == [ ]
+        then ""
+        else toKeyValue { inherit mkKeyValue; } globalSection
+          + "\n";
+    in
+    mkGlobalSection
+    + mkSections;
+
+  /* Generate a name-value-style config file from a list.
+   *
+   * mkKeyValue is the same as in toINI.
+   */
+  toKeyValue =
+    { mkKeyValue ? lib.generators.mkKeyValueDefault { } "="
+    ,
+    }:
+    let
+      mkLine = k: v: mkKeyValue k v + "\n";
+      mkLines = k: v: [ (mkLine k v) ];
+    in
+    nameValues: lib.strings.concatStrings (lib.concatLists (map ({ name, value }: mkLines name value) nameValues));
+
+in
+toINI
diff --git a/users/Profpatsch/tree-sitter.nix b/users/Profpatsch/tree-sitter.nix
index 1e3f378019..2224da2a3b 100644
--- a/users/Profpatsch/tree-sitter.nix
+++ b/users/Profpatsch/tree-sitter.nix
@@ -2,17 +2,18 @@
 
 let
   bins = depot.nix.getBins pkgs.coreutils [ "head" "printf" "cat" ]
-      // depot.nix.getBins pkgs.ncurses [ "tput" ]
-      // depot.nix.getBins pkgs.bc [ "bc" ]
-      // depot.nix.getBins pkgs.ocamlPackages.sexp [ "sexp" ];
-
-  print-ast = depot.nix.writers.rustSimple {
-    name = "print-ast";
-    dependencies = with depot.third_party.rust-crates; [
-      libloading
-      tree-sitter
-    ];
-  } ''
+    // depot.nix.getBins pkgs.ncurses [ "tput" ]
+    // depot.nix.getBins pkgs.bc [ "bc" ]
+    // depot.nix.getBins pkgs.ocamlPackages.sexp [ "sexp" ];
+
+  print-ast = depot.nix.writers.rustSimple
+    {
+      name = "print-ast";
+      dependencies = with depot.third_party.rust-crates; [
+        libloading
+        tree-sitter
+      ];
+    } ''
     extern crate libloading;
     extern crate tree_sitter;
     use std::mem;
@@ -58,13 +59,14 @@ let
     };
   };
 
-  watch-file-modified = depot.nix.writers.rustSimple {
-    name = "watch-file-modified";
-    dependencies = [
-      depot.third_party.rust-crates.inotify
-      depot.users.Profpatsch.netstring.rust-netstring
-    ];
-  } ''
+  watch-file-modified = depot.nix.writers.rustSimple
+    {
+      name = "watch-file-modified";
+      dependencies = [
+        depot.third_party.rust-crates.inotify
+        depot.users.Profpatsch.netstring.rust-netstring
+      ];
+    } ''
     extern crate inotify;
     extern crate netstring;
     use inotify::{EventMask, WatchMask, Inotify};
@@ -101,75 +103,103 @@ let
   '';
 
   # clear screen and set LINES and COLUMNS to terminal height & width
-  clear-screen = depot.nix.writeExecline "clear-screen" {} [
-    "if" [ bins.tput "clear" ]
-    "backtick" "-in" "LINES" [ bins.tput "lines" ]
-    "backtick" "-in" "COLUMNS" [ bins.tput "cols" ]
+  clear-screen = depot.nix.writeExecline "clear-screen" { } [
+    "if"
+    [ bins.tput "clear" ]
+    "backtick"
+    "-in"
+    "LINES"
+    [ bins.tput "lines" ]
+    "backtick"
+    "-in"
+    "COLUMNS"
+    [ bins.tput "cols" ]
     "$@"
   ];
 
   print-nix-file = depot.nix.writeExecline "print-nix-file" { readNArgs = 1; } [
-    "pipeline" [ print-ast "${tree-sitter-nix}/parser" "tree_sitter_nix" "$1" ]
-    "pipeline" [ bins.sexp "print" ]
+    "pipeline"
+    [ print-ast "${tree-sitter-nix}/parser" "tree_sitter_nix" "$1" ]
+    "pipeline"
+    [ bins.sexp "print" ]
     clear-screen
-    "importas" "-ui" "lines" "LINES"
-    "backtick" "-in" "ls" [
+    "importas"
+    "-ui"
+    "lines"
+    "LINES"
+    "backtick"
+    "-in"
+    "ls"
+    [
       "pipeline"
-        # when you pull out bc to decrement an integer itโ€™s time to switch to python lol
-        [ bins.printf "x=%s; --x\n" "$lines" ]
-        bins.bc
+      # when you pull out bc to decrement an integer itโ€™s time to switch to python lol
+      [ bins.printf "x=%s; --x\n" "$lines" ]
+      bins.bc
     ]
-    "importas" "-ui" "l" "ls"
-    bins.head "-n\${l}"
+    "importas"
+    "-ui"
+    "l"
+    "ls"
+    bins.head
+    "-n\${l}"
   ];
 
   print-nix-file-on-update = depot.nix.writeExecline "print-nix-file-on-update" { readNArgs = 1; } [
-    "if" [ print-nix-file "$1" ]
-    "pipeline" [ watch-file-modified "$1" ]
-    "forstdin" "-d" "" "file"
-    "importas" "file" "file"
-    print-nix-file "$file"
+    "if"
+    [ print-nix-file "$1" ]
+    "pipeline"
+    [ watch-file-modified "$1" ]
+    "forstdin"
+    "-d"
+    ""
+    "file"
+    "importas"
+    "file"
+    "file"
+    print-nix-file
+    "$file"
   ];
 
   # copied from nixpkgs
   buildTreeSitterGrammar =
-      {
-        # language name
-        language
-        # source for the language grammar
-      , source
-      }:
-
-      pkgs.stdenv.mkDerivation {
-
-        pname = "${language}-grammar";
-        inherit (pkgs.tree-sitter) version;
-
-        src = source;
-
-        buildInputs = [ pkgs.tree-sitter ];
-
-        dontUnpack = true;
-        configurePhase= ":";
-        buildPhase = ''
-          runHook preBuild
-          scanner_cc="$src/src/scanner.cc"
-          if [ ! -f "$scanner_cc" ]; then
-            scanner_cc=""
-          fi
-          $CXX -I$src/src/ -c $scanner_cc
-          $CC -I$src/src/ -shared -o parser -Os  scanner.o $src/src/parser.c -lstdc++
-          runHook postBuild
-        '';
-        installPhase = ''
-          runHook preInstall
-          mkdir $out
-          mv parser $out/
-          runHook postInstall
-        '';
-      };
-
-in depot.nix.utils.drvTargets {
+    {
+      # language name
+      language
+      # source for the language grammar
+    , source
+    }:
+
+    pkgs.stdenv.mkDerivation {
+
+      pname = "${language}-grammar";
+      inherit (pkgs.tree-sitter) version;
+
+      src = source;
+
+      buildInputs = [ pkgs.tree-sitter ];
+
+      dontUnpack = true;
+      configurePhase = ":";
+      buildPhase = ''
+        runHook preBuild
+        scanner_cc="$src/src/scanner.cc"
+        if [ ! -f "$scanner_cc" ]; then
+          scanner_cc=""
+        fi
+        $CXX -I$src/src/ -c $scanner_cc
+        $CC -I$src/src/ -shared -o parser -Os  scanner.o $src/src/parser.c -lstdc++
+        runHook postBuild
+      '';
+      installPhase = ''
+        runHook preInstall
+        mkdir $out
+        mv parser $out/
+        runHook postInstall
+      '';
+    };
+
+in
+depot.nix.readTree.drvTargets {
   inherit
     print-ast
     tree-sitter-nix
diff --git a/users/Profpatsch/whatcd-resolver/Main.hs b/users/Profpatsch/whatcd-resolver/Main.hs
new file mode 100644
index 0000000000..21cd80cbf0
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/Main.hs
@@ -0,0 +1,6 @@
+module Main where
+
+import WhatcdResolver qualified
+
+main :: IO ()
+main = WhatcdResolver.main
diff --git a/users/Profpatsch/whatcd-resolver/README.md b/users/Profpatsch/whatcd-resolver/README.md
new file mode 100644
index 0000000000..d1902e546a
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/README.md
@@ -0,0 +1,21 @@
+# whatcd-resolver
+
+To run:
+
+```
+ninja run-services
+```
+
+in one terminal (starts the background tasks)
+
+```
+ninja run
+```
+
+to start the server. It runs on `9092`.
+
+You need to be in the `nix-shell` in `./..`.
+
+You need to set the `pass` key `internet/redacted/api-keys/whatcd-resolver` to an API key for RED.
+
+You need to have a transmission-rpc-daemon listening on port `9091` (no auth, try ssh port forwarding lol).
diff --git a/users/Profpatsch/whatcd-resolver/build.ninja b/users/Profpatsch/whatcd-resolver/build.ninja
new file mode 100644
index 0000000000..ff6ba8df04
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/build.ninja
@@ -0,0 +1,20 @@
+
+builddir = .ninja
+
+outdir = ./output
+
+rule run-services
+  command = s6-svscan ./services
+
+rule run
+  command = execlineb -c '$
+    importas -i DEPOT_ROOT DEPOT_ROOT $
+    importas -i PROFPATSCH_ROOT PROFPATSCH_ROOT cd $$PROFPATSCH_ROOT $
+    nix-run { $$DEPOT_ROOT -A users.Profpatsch.shortcuttable } cabal repl whatcd-resolver/ --repl-options "-e main" $
+    '
+
+build run-services: run-services
+  pool = console
+
+build run: run
+  pool = console
diff --git a/users/Profpatsch/whatcd-resolver/default.nix b/users/Profpatsch/whatcd-resolver/default.nix
new file mode 100644
index 0000000000..27468507ac
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/default.nix
@@ -0,0 +1,76 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  #   bins = depot.nix.getBins pkgs.sqlite ["sqlite3"];
+
+  whatcd-resolver = pkgs.haskellPackages.mkDerivation {
+    pname = "whatcd-resolver";
+    version = "0.1.0";
+
+    src = depot.users.Profpatsch.exactSource ./. [
+      ./whatcd-resolver.cabal
+      ./Main.hs
+      ./src/WhatcdResolver.hs
+      ./src/AppT.hs
+      ./src/JsonLd.hs
+      ./src/Optional.hs
+      ./src/Html.hs
+      ./src/Http.hs
+      ./src/Transmission.hs
+      ./src/Redacted.hs
+    ];
+
+    libraryHaskellDepends = [
+      depot.users.Profpatsch.my-prelude
+      depot.users.Profpatsch.my-webstuff
+      pkgs.haskellPackages.pa-prelude
+      pkgs.haskellPackages.pa-label
+      pkgs.haskellPackages.pa-json
+      pkgs.haskellPackages.pa-error-tree
+      pkgs.haskellPackages.pa-field-parser
+      pkgs.haskellPackages.pa-run-command
+      pkgs.haskellPackages.aeson-better-errors
+      pkgs.haskellPackages.blaze-html
+      pkgs.haskellPackages.hs-opentelemetry-sdk
+      pkgs.haskellPackages.http-conduit
+      pkgs.haskellPackages.http-types
+      pkgs.haskellPackages.ihp-hsx
+      pkgs.haskellPackages.monad-logger
+      pkgs.haskellPackages.resource-pool
+      pkgs.haskellPackages.postgresql-simple
+      pkgs.haskellPackages.tmp-postgres
+      pkgs.haskellPackages.unliftio
+      pkgs.haskellPackages.wai-extra
+      pkgs.haskellPackages.warp
+      pkgs.haskellPackages.punycode
+    ];
+
+    isExecutable = true;
+    isLibrary = false;
+    license = lib.licenses.mit;
+  };
+
+  bins = depot.nix.getBins whatcd-resolver [ "whatcd-resolver" ];
+
+in
+
+depot.nix.writeExecline "whatcd-resolver-wrapped" { } [
+  "importas"
+  "-i"
+  "PATH"
+  "PATH"
+  "export"
+  "PATH"
+  # TODO: figure out how to automatically migrate to a new postgres version with tmp_postgres (dump?)
+  "${pkgs.postgresql_14}/bin:$${PATH}"
+  "export"
+  "WHATCD_RESOLVER_TOOLS"
+  (pkgs.linkFarm "whatcd-resolver-tools" [
+    {
+      name = "pg_format";
+      path = "${pkgs.pgformatter}/bin/pg_format";
+    }
+  ])
+  bins.whatcd-resolver
+]
+
diff --git a/users/Profpatsch/whatcd-resolver/notes.org b/users/Profpatsch/whatcd-resolver/notes.org
new file mode 100644
index 0000000000..24662c0f32
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/notes.org
@@ -0,0 +1,48 @@
+* The Glorious what.cdยน Resolver
+
+  ยน: At the time of writing, what.cd didnโ€™t even exist anymore
+
+** Idea
+   
+   Stream your music (or media) from a private tracker transparently.
+   โ€œSpotify for torrentsโ€
+
+** Technical
+
+   You need to have a seedbox, which runs a server program.
+   The server manages queries, downloads torrents and requested files, and
+   provides http streams to the downloaded files (while caching them for
+   seeding).
+
+   Clients then use the API to search for music (e.g. query for artists or
+   tracks) and get back the promise of a stream to the resolved file (a bit how
+   resolvers in the Tomahawk Player work)
+
+*** The Server
+
+**** Resolving queries
+
+     ~resolve :: Query -> IO Identifiers~
+
+     A query is a search input for content (could be an artist or a movie name
+     or something)
+
+     There have to be multiple providers, depending on the site used
+     (e.g. one for Gazelle trackers, one for Piratebay) and some intermediate
+     structure (e.g. for going through Musicbrainz first).
+
+     Output is a unique identifier for a fetchable resource; this could be a
+     link to a torrent combined with a file/directory in said torrent.
+
+**** Fetching Identifiers
+
+     ~fetch :: Identifier -> IO (Promise Stream)~
+
+     Takes an Identifier (which should provide all information on how to grab
+     the media file and returns a stream to the media file once itโ€™s ready.
+     
+     For torrents, this probably consists of telling the torrent
+     library/application to fetch a certain torrent and start downloading the
+     required files in it. The torrent fetcher would also need to do seeding and
+     space management, since one usually has to keep a ratio and hard drive
+     space is not unlimited.
diff --git a/users/Profpatsch/whatcd-resolver/server-notes.org b/users/Profpatsch/whatcd-resolver/server-notes.org
new file mode 100644
index 0000000000..cb990aba3d
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/server-notes.org
@@ -0,0 +1,2 @@
+* whatcd-resolver-server
+
diff --git a/users/Profpatsch/whatcd-resolver/services/.gitignore b/users/Profpatsch/whatcd-resolver/services/.gitignore
new file mode 100644
index 0000000000..5cdb254e8c
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/services/.gitignore
@@ -0,0 +1,3 @@
+/.s6-svscan/
+/**/event/
+/**/supervise/
diff --git a/users/Profpatsch/whatcd-resolver/services/jaeger/run b/users/Profpatsch/whatcd-resolver/services/jaeger/run
new file mode 100755
index 0000000000..41332f8bb6
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/services/jaeger/run
@@ -0,0 +1,3 @@
+#!/usr/bin/env execlineb
+importas -i DEPOT_ROOT DEPOT_ROOT
+nix-run { $DEPOT_ROOT -A users.Profpatsch.jaeger -kK --builders '' }
diff --git a/users/Profpatsch/whatcd-resolver/services/reverse-proxy/run b/users/Profpatsch/whatcd-resolver/services/reverse-proxy/run
new file mode 100755
index 0000000000..7081b35f5a
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/services/reverse-proxy/run
@@ -0,0 +1,2 @@
+#!/usr/bin/env execlineb
+caddy reverse-proxy --from :9092 --to :9093
diff --git a/users/Profpatsch/whatcd-resolver/src/AppT.hs b/users/Profpatsch/whatcd-resolver/src/AppT.hs
new file mode 100644
index 0000000000..7afd430745
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/AppT.hs
@@ -0,0 +1,151 @@
+{-# LANGUAGE DeriveAnyClass #-}
+
+module AppT where
+
+import Control.Monad.Logger qualified as Logger
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Error.Tree
+import Data.HashMap.Strict (HashMap)
+import Data.HashMap.Strict qualified as HashMap
+import Data.Pool (Pool)
+import Data.Text qualified as Text
+import Database.PostgreSQL.Simple qualified as Postgres
+import GHC.Stack qualified
+import Label
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import OpenTelemetry.Trace.Core qualified as Otel hiding (inSpan, inSpan')
+import OpenTelemetry.Trace.Monad qualified as Otel
+import PossehlAnalyticsPrelude
+import Postgres.MonadPostgres
+import System.IO qualified as IO
+import Tool (Tool)
+import UnliftIO
+import Prelude hiding (span)
+
+data Context = Context
+  { config :: Label "logDatabaseQueries" DebugLogDatabaseQueries,
+    tracer :: Otel.Tracer,
+    pgFormat :: Tool,
+    pgConnPool :: Pool Postgres.Connection,
+    transmissionSessionId :: MVar ByteString
+  }
+
+newtype AppT m a = AppT {unAppT :: ReaderT Context m a}
+  deriving newtype (Functor, Applicative, Monad, MonadIO, MonadUnliftIO, MonadThrow)
+
+data AppException = AppException Text
+  deriving stock (Show)
+  deriving anyclass (Exception)
+
+-- *  Logging & Opentelemetry
+
+instance (MonadIO m) => MonadLogger (AppT m) where
+  monadLoggerLog loc src lvl msg = liftIO $ Logger.defaultOutput IO.stderr loc src lvl (Logger.toLogStr msg)
+
+instance (Monad m) => Otel.MonadTracer (AppT m) where
+  getTracer = AppT $ asks (.tracer)
+
+class (MonadUnliftIO m, Otel.MonadTracer m) => MonadOtel m
+
+instance (MonadUnliftIO m) => MonadOtel (AppT m)
+
+instance (MonadOtel m) => MonadOtel (Transaction m)
+
+inSpan :: (MonadOtel m) => Text -> m a -> m a
+inSpan name = Otel.inSpan name Otel.defaultSpanArguments
+
+inSpan' :: (MonadOtel m) => Text -> (Otel.Span -> m a) -> m a
+inSpan' name = Otel.inSpan' name Otel.defaultSpanArguments
+
+-- | Add the attribute to the span, prefixing it with the `_` namespace (to easier distinguish our applicationโ€™s tags from standard tags)
+addAttribute :: (MonadIO m, Otel.ToAttribute a) => Otel.Span -> Text -> a -> m ()
+addAttribute span key a = Otel.addAttribute span ("_." <> key) a
+
+-- | Add the attributes to the span, prefixing each key with the `_` namespace (to easier distinguish our applicationโ€™s tags from standard tags)
+addAttributes :: (MonadIO m) => Otel.Span -> HashMap Text Otel.Attribute -> m ()
+addAttributes span attrs = Otel.addAttributes span $ attrs & HashMap.mapKeys ("_." <>)
+
+appThrowTreeNewSpan :: (MonadThrow m, MonadOtel m) => Text -> ErrorTree -> m a
+appThrowTreeNewSpan spanName exc = inSpan' spanName $ \span -> do
+  let msg = prettyErrorTree exc
+  recordException
+    span
+    ( T2
+        (label @"type_" "AppException")
+        (label @"message" msg)
+    )
+  throwM $ AppException msg
+
+appThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> ErrorTree -> m a
+appThrowTree span exc = do
+  let msg = prettyErrorTree exc
+  recordException
+    span
+    ( T2
+        (label @"type_" "AppException")
+        (label @"message" msg)
+    )
+  throwM $ AppException msg
+
+orAppThrowTree :: (MonadThrow m, MonadIO m) => Otel.Span -> Either ErrorTree a -> m a
+orAppThrowTree span = \case
+  Left err -> appThrowTree span err
+  Right a -> pure a
+
+assertM :: (MonadThrow f, MonadIO f) => Otel.Span -> (t -> Either ErrorTree a) -> t -> f a
+assertM span f v = case f v of
+  Right a -> pure a
+  Left err -> appThrowTree span err
+
+assertMNewSpan :: (MonadThrow f, MonadOtel f) => Text -> (t -> Either ErrorTree a) -> t -> f a
+assertMNewSpan spanName f v = case f v of
+  Right a -> pure a
+  Left err -> appThrowTreeNewSpan spanName err
+
+-- | A specialized variant of @addEvent@ that records attributes conforming to
+-- the OpenTelemetry specification's
+-- <https://github.com/open-telemetry/opentelemetry-specification/blob/49c2f56f3c0468ceb2b69518bcadadd96e0a5a8b/specification/trace/semantic_conventions/exceptions.md semantic conventions>
+--
+-- @since 0.0.1.0
+recordException ::
+  ( MonadIO m,
+    HasField "message" r Text,
+    HasField "type_" r Text
+  ) =>
+  Otel.Span ->
+  r ->
+  m ()
+recordException span dat = liftIO $ do
+  callStack <- GHC.Stack.whoCreated dat.message
+  newEventTimestamp <- Just <$> Otel.getTimestamp
+  Otel.addEvent span $
+    Otel.NewEvent
+      { newEventName = "exception",
+        newEventAttributes =
+          HashMap.fromList
+            [ ("exception.type", Otel.toAttribute @Text dat.type_),
+              ("exception.message", Otel.toAttribute @Text dat.message),
+              ("exception.stacktrace", Otel.toAttribute @Text $ Text.unlines $ map stringToText callStack)
+            ],
+        ..
+      }
+
+-- * Postgres
+
+instance (MonadThrow m, MonadUnliftIO m) => MonadPostgres (AppT m) where
+  execute = executeImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  executeMany = executeManyImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  executeManyReturningWith = executeManyReturningWithImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  queryWith = queryWithImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  queryWith_ = queryWithImpl_ (AppT ask)
+
+  foldRowsWithAcc = foldRowsWithAccImpl (AppT ask) (AppT $ asks (.config.logDatabaseQueries))
+  runTransaction = runPGTransaction
+
+runPGTransaction :: (MonadUnliftIO m) => Transaction (AppT m) a -> AppT m a
+runPGTransaction (Transaction transaction) = do
+  pool <- AppT ask <&> (.pgConnPool)
+  withRunInIO $ \unliftIO ->
+    withPGTransaction pool $ \conn -> do
+      unliftIO $ runReaderT transaction conn
diff --git a/users/Profpatsch/whatcd-resolver/src/Html.hs b/users/Profpatsch/whatcd-resolver/src/Html.hs
new file mode 100644
index 0000000000..49b87b23dc
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Html.hs
@@ -0,0 +1,69 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Html where
+
+import Data.Aeson qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Map.Strict qualified as Map
+import IHP.HSX.QQ (hsx)
+import PossehlAnalyticsPrelude
+import Text.Blaze.Html (Html)
+import Text.Blaze.Html5 qualified as Html
+import Prelude hiding (span)
+
+-- | Render an arbitrary json value to HTML in a more-or-less reasonable fashion.
+mkVal :: Json.Value -> Html
+mkVal = \case
+  Json.Number n -> Html.toHtml @Text $ showToText n
+  Json.String s -> Html.toHtml @Text s
+  Json.Bool True -> [hsx|<em>true</em>|]
+  Json.Bool False -> [hsx|<em>false</em>|]
+  Json.Null -> [hsx|<em>null</em>|]
+  Json.Array arr -> toOrderedList mkVal arr
+  Json.Object obj ->
+    obj
+      & KeyMap.toMapText
+      & toDefinitionList (Html.toHtml @Text) mkVal
+
+toOrderedList :: (Foldable t1) => (t2 -> Html) -> t1 t2 -> Html
+toOrderedList mkValFn arr =
+  arr
+    & foldMap (\el -> Html.li $ mkValFn el)
+    & Html.ol
+
+toUnorderedList :: (Foldable t1) => (t2 -> Html) -> t1 t2 -> Html
+toUnorderedList mkValFn arr =
+  arr
+    & foldMap (\el -> Html.li $ mkValFn el)
+    & Html.ul
+
+-- | Render a definition list from a Map
+toDefinitionList :: (Text -> Html) -> (t -> Html) -> Map Text t -> Html
+toDefinitionList mkKeyFn mkValFn obj =
+  obj
+    & Map.toList
+    & foldMap (\(k, v) -> Html.dt (mkKeyFn k) <> Html.dd (mkValFn v))
+    & Html.dl
+
+-- | Render a table-like structure of json values as an HTML table.
+toTable :: [[(Text, Json.Value)]] -> Html
+toTable xs =
+  case xs & nonEmpty of
+    Nothing ->
+      [hsx|<p>No results.</p>|]
+    Just xs' -> do
+      let headers = xs' & NonEmpty.head <&> fst <&> (\h -> [hsx|<th>{h}</th>|]) & mconcat
+      let vals = xs' & foldMap (Html.tr . foldMap (Html.td . mkVal . snd))
+      [hsx|
+              <table class="table">
+                <thead>
+                  <tr>
+                  {headers}
+                  </tr>
+                </thead>
+                <tbody>
+                  {vals}
+                </tbody>
+              </table>
+          |]
diff --git a/users/Profpatsch/whatcd-resolver/src/Http.hs b/users/Profpatsch/whatcd-resolver/src/Http.hs
new file mode 100644
index 0000000000..4fdbb306ad
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Http.hs
@@ -0,0 +1,129 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Http
+  ( doRequestJson,
+    RequestOptions (..),
+    mkRequestOptions,
+    setRequestMethod,
+    setRequestBodyLBS,
+    setRequestHeader,
+    getResponseStatus,
+    getResponseHeader,
+    getResponseBody,
+  )
+where
+
+import AppT
+import Data.CaseInsensitive (CI (original))
+import Data.Char qualified as Char
+import Data.Int (Int64)
+import Data.List qualified as List
+import Data.Text qualified as Text
+import Data.Text.Lazy qualified as Lazy.Text
+import Data.Text.Punycode qualified as Punycode
+import Json.Enc qualified as Enc
+import MyPrelude
+import Network.HTTP.Client
+import Network.HTTP.Simple
+import OpenTelemetry.Attributes qualified as Otel
+import Optional
+import Prelude hiding (span)
+
+data RequestOptions = RequestOptions
+  { method :: ByteString,
+    host :: Text,
+    port :: Optional Int,
+    path :: Optional [Text],
+    headers :: Optional [Header],
+    usePlainHttp :: Optional Bool
+  }
+
+mkRequestOptions :: (HasField "method" r ByteString, HasField "host" r Text) => r -> RequestOptions
+mkRequestOptions opts =
+  RequestOptions
+    { method = opts.method,
+      port = defaults,
+      host = opts.host,
+      path = defaults,
+      headers = defaults,
+      usePlainHttp = defaults
+    }
+
+doRequestJson ::
+  (MonadOtel m) =>
+  RequestOptions ->
+  Enc.Enc ->
+  m (Response ByteString)
+doRequestJson opts val = inSpan' "HTTP Request (JSON)" $ \span -> do
+  let x = requestToXhCommandLine opts val
+  let attrs = [100, 200 .. fromIntegral @Int @Int64 (x & Text.length)]
+  for_ attrs $ \n -> do
+    addAttribute span [fmt|request.xh.{n}|] (Lazy.Text.repeat 'x' & Lazy.Text.take n & toStrict & Otel.TextAttribute)
+  addAttribute span "request.xh" (requestToXhCommandLine opts val)
+  defaultRequest {secure = not (opts & optsUsePlainHttp)}
+    & setRequestHost (opts & optsHost)
+    & setRequestPort (opts & optsPort)
+    -- TODO: is this automatically escaped by the library?
+    & setRequestPath (opts & optsPath)
+    & setRequestHeaders (opts & optsHeaders)
+    & setRequestMethod opts.method
+    & setRequestBodyLBS (Enc.encToBytesUtf8Lazy val)
+    & httpBS
+
+optsHost :: RequestOptions -> ByteString
+optsHost opts =
+  if opts.host & Text.isAscii
+    then opts.host & textToBytesUtf8
+    else opts.host & Punycode.encode
+
+optsUsePlainHttp :: RequestOptions -> Bool
+optsUsePlainHttp opts = opts.usePlainHttp.withDefault False
+
+optsPort :: RequestOptions -> Int
+optsPort opts = opts.port.withDefault (if opts & optsUsePlainHttp then 80 else 443)
+
+optsPath :: RequestOptions -> ByteString
+optsPath opts = opts.path.withDefault [] & Text.intercalate "/" & ("/" <>) & textToBytesUtf8
+
+optsHeaders :: RequestOptions -> [Header]
+optsHeaders opts = opts.headers.withDefault []
+
+-- | Create a string that can be pasted on the command line to invoke the same HTTP request via the `xh` tool (curl but nicer syntax)
+requestToXhCommandLine :: RequestOptions -> Enc.Enc -> Text
+requestToXhCommandLine opts val = do
+  let protocol = if opts & optsUsePlainHttp then "http" :: Text else "https"
+  let url = [fmt|{protocol}://{opts & optsHost}:{opts & optsPort}{opts & optsPath}|]
+  let headers = opts & optsHeaders <&> \(hdr, v) -> hdr.original <> ":" <> v
+
+  prettyArgsForBash $
+    mconcat
+      [ ["xh", url],
+        headers <&> bytesToTextUtf8Lenient,
+        ["--raw"],
+        [val & Enc.encToBytesUtf8 & bytesToTextUtf8Lenient]
+      ]
+
+-- | Pretty print a command line in a way that can be copied to bash.
+prettyArgsForBash :: [Text] -> Text
+prettyArgsForBash = Text.intercalate " " . map simpleBashEscape
+
+-- | Simple escaping for bash words. If they contain anything thatโ€™s not ascii chars
+-- and a bunch of often-used special characters, put the word in single quotes.
+simpleBashEscape :: Text -> Text
+simpleBashEscape t = do
+  case Text.find (not . isSimple) t of
+    Just _ -> escapeSingleQuote t
+    Nothing -> t
+  where
+    -- any word that is just ascii characters is simple (no spaces or control characters)
+    -- or contains a few often-used characters like - or .
+    isSimple c =
+      Char.isAsciiLower c
+        || Char.isAsciiUpper c
+        || Char.isDigit c
+        -- These are benign, bash will not interpret them as special characters.
+        || List.elem c ['-', '.', ':', '/']
+    -- Put the word in single quotes
+    -- If there is a single quote in the word,
+    -- close the single quoted word, add a single quote, open the word again
+    escapeSingleQuote t' = "'" <> Text.replace "'" "'\\''" t' <> "'"
diff --git a/users/Profpatsch/whatcd-resolver/src/JsonLd.hs b/users/Profpatsch/whatcd-resolver/src/JsonLd.hs
new file mode 100644
index 0000000000..16b1ab991b
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/JsonLd.hs
@@ -0,0 +1,138 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module JsonLd where
+
+import AppT
+import Control.Monad.Reader
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.ByteString.Builder qualified as Builder
+import Data.List qualified as List
+import Data.Map.Strict qualified as Map
+import Data.Set (Set)
+import Data.Set qualified as Set
+import Html qualified
+import IHP.HSX.QQ (hsx)
+import Json qualified
+import Label
+import MyPrelude
+import Network.HTTP.Client.Conduit qualified as Http
+import Network.HTTP.Simple qualified as Http
+import Network.HTTP.Types.URI qualified as Url
+import Network.URI (URI)
+import Optional
+import Redacted
+import Text.Blaze.Html (Html)
+import Prelude hiding (span)
+
+-- | A recursive `json+ld` structure.
+data Jsonld
+  = JsonldObject JsonldObject
+  | JsonldAnonymousObject JsonldAnonymousObject
+  | JsonldArray [Jsonld]
+  | JsonldField Json.Value
+  deriving stock (Show, Eq)
+
+-- | A json+ld object, that is something which can be further expanded by following the URL in its `id_` field.
+data JsonldObject = JsonldObject'
+  { -- | `@type` field; currently just the plain value without taking into account the json+ld context
+    type_ :: Set Text,
+    -- | `@id` field, usually a link to follow for expanding the object to its full glory
+    id_ :: Text,
+    -- | any fields of this object that remote deemed important enough to already pre-emptively include in the object; to get all fields resolve the URL in `id_`.
+    previewFields :: Map Text Jsonld
+  }
+  deriving stock (Show, Eq)
+
+-- | A json+ld object that cannot be inspected further by resolving its ID
+data JsonldAnonymousObject = JsonldAnonymousObject'
+  { -- | `@type` field; currently just the plain value without taking into account the json+ld context
+    type_ :: Set Text,
+    -- | fields of this anonymous object
+    fields :: Map Text Jsonld
+  }
+  deriving stock (Show, Eq)
+
+jsonldParser :: (Monad m) => Json.ParseT err m Jsonld
+jsonldParser =
+  Json.asValue >>= \cur -> do
+    if
+      | Json.Object _ <- cur -> do
+          type_ <-
+            Json.keyMay "@type" (Json.asArraySet Json.asText Json.<|> (Set.singleton <$> Json.asText))
+              <&> fromMaybe Set.empty
+          idMay <- Json.keyMay "@id" $ Json.asText
+          fields <-
+            Json.asObjectMap jsonldParser
+              <&> Map.delete "@type"
+              <&> Map.delete "@id"
+
+          if
+            | Just id_ <- idMay -> do
+                pure $ JsonldObject $ JsonldObject' {previewFields = fields, ..}
+            | otherwise -> pure $ JsonldAnonymousObject $ JsonldAnonymousObject' {..}
+      | Json.Array _ <- cur -> do
+          JsonldArray <$> Json.eachInArray jsonldParser
+      | otherwise -> pure $ JsonldField cur
+
+renderJsonld :: Jsonld -> Html
+renderJsonld = \case
+  JsonldObject obj -> renderObject obj (Just obj.id_) obj.previewFields
+  JsonldAnonymousObject obj -> renderObject obj Nothing obj.fields
+  JsonldArray arr ->
+    Html.toOrderedList renderJsonld arr
+  JsonldField f -> Html.mkVal f
+  where
+    renderObject obj mId_ fields = do
+      let id_ =
+            mId_ <&> \i ->
+              [hsx|
+                  <dt>Url</dt>
+                  <dd><a href={i}>{i}</a></dd>
+                  |]
+          getMoreButton =
+            mId_ <&> \i ->
+              [hsx|
+              <div>
+                <button
+                  hx-get={snippetHref i}
+                  hx-target="closest dl"
+                  hx-swap="outerHTML"
+                >more fields โ€ฆ</button>
+              </div>
+            |]
+      [hsx|
+      <dl>
+        <dt>Type</dt>
+        <dd>{obj.type_ & toList & schemaTypes}</dd>
+        {id_}
+        <dt>Fields</dt>
+        <dd>
+          {fields & Html.toDefinitionList schemaType renderJsonld}
+          {getMoreButton}
+        </dd>
+      </dl>
+    |]
+    snippetHref target =
+      Builder.toLazyByteString $
+        "/snips/jsonld/render"
+          <> Url.renderQueryBuilder True [("target", Just (textToBytesUtf8 target))]
+
+    schemaTypes xs =
+      xs
+        <&> schemaType
+        & List.intersperse ", "
+        & mconcat
+    schemaType t =
+      let href :: Text = [fmt|https://schema.org/{t}|] in [hsx|<a href={href} target="_blank">{t}</a>|]
+
+httpGetJsonLd :: (MonadThrow m, MonadOtel m) => (URI, Http.Request) -> m Jsonld
+httpGetJsonLd (uri, req) = inSpan' "Fetch json+ld" $ \span -> do
+  addAttribute span "json+ld.targetUrl" (uri & showToText)
+  httpJson
+    (mkOptional (label @"contentType" "application/ld+json"))
+    jsonldParser
+    ( req
+        & Http.setRequestMethod "GET"
+        & Http.setRequestHeader "Accept" ["application/ld+json"]
+    )
diff --git a/users/Profpatsch/whatcd-resolver/src/Optional.hs b/users/Profpatsch/whatcd-resolver/src/Optional.hs
new file mode 100644
index 0000000000..9791c84970
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Optional.hs
@@ -0,0 +1,18 @@
+module Optional where
+
+import GHC.Records (getField)
+import MyPrelude
+
+newtype Optional a = OptionalInternal (Maybe a)
+  deriving newtype (Functor)
+
+mkOptional :: a -> Optional a
+mkOptional defaultValue = OptionalInternal $ Just defaultValue
+
+defaults :: Optional a
+defaults = OptionalInternal Nothing
+
+instance HasField "withDefault" (Optional a) (a -> a) where
+  getField (OptionalInternal m) defaultValue = case m of
+    Nothing -> defaultValue
+    Just a -> a
diff --git a/users/Profpatsch/whatcd-resolver/src/Redacted.hs b/users/Profpatsch/whatcd-resolver/src/Redacted.hs
new file mode 100644
index 0000000000..4369c18408
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Redacted.hs
@@ -0,0 +1,537 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Redacted where
+
+import AppT
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.Error.Tree
+import Data.List qualified as List
+import Database.PostgreSQL.Simple (Binary (Binary), Only (..))
+import Database.PostgreSQL.Simple.SqlQQ (sql)
+import Database.PostgreSQL.Simple.Types (PGArray (PGArray))
+import FieldParser qualified as Field
+import Json qualified
+import Label
+import MyPrelude
+import Network.HTTP.Client.Conduit qualified as Http
+import Network.HTTP.Simple qualified as Http
+import Network.HTTP.Types
+import Network.Wai.Parse qualified as Wai
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import Optional
+import Postgres.Decoder qualified as Dec
+import Postgres.MonadPostgres
+import Pretty
+import RunCommand (runCommandExpect0)
+import Prelude hiding (span)
+
+redactedSearch ::
+  (MonadLogger m, MonadThrow m, MonadOtel m) =>
+  [(ByteString, ByteString)] ->
+  Json.Parse ErrorTree a ->
+  m a
+redactedSearch advanced parser =
+  inSpan "Redacted API Search" $
+    redactedApiRequestJson
+      ( T2
+          (label @"action" "browse")
+          (label @"actionArgs" ((advanced <&> second Just)))
+      )
+      parser
+
+redactedGetTorrentFile ::
+  ( MonadLogger m,
+    MonadThrow m,
+    HasField "torrentId" dat Int,
+    MonadOtel m
+  ) =>
+  dat ->
+  m ByteString
+redactedGetTorrentFile dat = inSpan' "Redacted Get Torrent File" $ \span -> do
+  req <-
+    mkRedactedApiRequest
+      ( T2
+          (label @"action" "download")
+          ( label @"actionArgs"
+              [ ("id", Just (dat.torrentId & showToText @Int & textToBytesUtf8))
+              -- try using tokens as long as we have them (TODO: what if thereโ€™s no tokens left?
+              -- ANSWER: it breaks:
+              -- responseBody = "{\"status\":\"failure\",\"error\":\"You do not have any freeleech tokens left. Please use the regular DL link.\"}",
+              -- ("usetoken", Just "1")
+              ]
+          )
+      )
+  httpTorrent span req
+
+-- fix
+--   ( \io -> do
+--       logInfo "delay"
+--       liftIO $ threadDelay 10_000_000
+--       io
+--   )
+
+exampleSearch :: (MonadThrow m, MonadLogger m, MonadPostgres m, MonadOtel m) => m (Transaction m ())
+exampleSearch = do
+  t1 <-
+    redactedSearchAndInsert
+      [ ("searchstr", "cherish"),
+        ("artistname", "kirinji"),
+        -- ("year", "1982"),
+        -- ("format", "MP3"),
+        -- ("releasetype", "album"),
+        ("order_by", "year")
+      ]
+  t3 <-
+    redactedSearchAndInsert
+      [ ("searchstr", "mouss et hakim"),
+        ("artistname", "mouss et hakim"),
+        -- ("year", "1982"),
+        -- ("format", "MP3"),
+        -- ("releasetype", "album"),
+        ("order_by", "year")
+      ]
+  t2 <-
+    redactedSearchAndInsert
+      [ ("searchstr", "thriller"),
+        ("artistname", "michael jackson"),
+        -- ("year", "1982"),
+        -- ("format", "MP3"),
+        -- ("releasetype", "album"),
+        ("order_by", "year")
+      ]
+  pure (t1 >> t2 >> t3)
+
+-- | Do the search, return a transaction that inserts all results from all pages of the search.
+redactedSearchAndInsert ::
+  forall m.
+  ( MonadLogger m,
+    MonadPostgres m,
+    MonadThrow m,
+    MonadOtel m
+  ) =>
+  [(ByteString, ByteString)] ->
+  m (Transaction m ())
+redactedSearchAndInsert extraArguments = do
+  logInfo [fmt|Doing redacted search with arguments: {showPretty extraArguments}|]
+  -- The first search returns the amount of pages, so we use that to query all results piece by piece.
+  firstPage <- go Nothing
+  let remainingPages = firstPage.pages - 1
+  logInfo [fmt|Got the first page, found {remainingPages} more pages|]
+  let otherPagesNum = [(2 :: Natural) .. remainingPages]
+  otherPages <- traverse go (Just <$> otherPagesNum)
+  pure $
+    (firstPage : otherPages)
+      & concatMap (.tourGroups)
+      & \case
+        IsNonEmpty tgs -> tgs & insertTourGroupsAndTorrents
+        IsEmpty -> pure ()
+  where
+    go mpage =
+      redactedSearch
+        ( extraArguments
+            -- pass the page (for every search but the first one)
+            <> (mpage & ifExists (\page -> ("page", (page :: Natural) & showToText & textToBytesUtf8)))
+        )
+        ( do
+            status <- Json.key "status" Json.asText
+            when (status /= "success") $ do
+              Json.throwCustomError [fmt|Status was not "success", but {status}|]
+            Json.key "response" $ do
+              pages <-
+                Json.keyMay "pages" (Field.toJsonParser (Field.mapError singleError $ Field.jsonNumber >>> Field.boundedScientificIntegral @Int "not an Integer" >>> Field.integralToNatural))
+                  -- in case the field is missing, letโ€™s assume there is only one page
+                  <&> fromMaybe 1
+              Json.key "results" $ do
+                tourGroups <-
+                  label @"tourGroups"
+                    <$> ( Json.eachInArray $ do
+                            groupId <- Json.keyLabel @"groupId" "groupId" (Json.asIntegral @_ @Int)
+                            groupName <- Json.keyLabel @"groupName" "groupName" Json.asText
+                            fullJsonResult <-
+                              label @"fullJsonResult"
+                                <$> ( Json.asObject
+                                        -- remove torrents cause they are inserted separately below
+                                        <&> KeyMap.filterWithKey (\k _ -> k /= "torrents")
+                                        <&> Json.Object
+                                    )
+                            let tourGroup = T3 groupId groupName fullJsonResult
+                            torrents <- Json.keyLabel @"torrents" "torrents" $
+                              Json.eachInArray $ do
+                                torrentId <- Json.keyLabel @"torrentId" "torrentId" (Json.asIntegral @_ @Int)
+                                fullJsonResultT <- label @"fullJsonResult" <$> Json.asValue
+                                pure $ T2 torrentId fullJsonResultT
+                            pure (T2 (label @"tourGroup" tourGroup) torrents)
+                        )
+                pure
+                  ( T2
+                      (label @"pages" pages)
+                      tourGroups
+                  )
+        )
+    insertTourGroupsAndTorrents ::
+      NonEmpty
+        ( T2
+            "tourGroup"
+            (T3 "groupId" Int "groupName" Text "fullJsonResult" Json.Value)
+            "torrents"
+            [T2 "torrentId" Int "fullJsonResult" Json.Value]
+        ) ->
+      Transaction m ()
+    insertTourGroupsAndTorrents dat = do
+      let tourGroups = dat <&> (.tourGroup)
+      let torrents = dat <&> (.torrents)
+      insertTourGroups tourGroups
+        >>= ( \res ->
+                insertTorrents $
+                  zipT2 $
+                    T2
+                      (label @"torrentGroupIdPg" $ res <&> (.tourGroupIdPg))
+                      (label @"torrents" (torrents & toList))
+            )
+    insertTourGroups ::
+      NonEmpty
+        ( T3
+            "groupId"
+            Int
+            "groupName"
+            Text
+            "fullJsonResult"
+            Json.Value
+        ) ->
+      Transaction m [Label "tourGroupIdPg" Int]
+    insertTourGroups dats = do
+      let groupNames =
+            dats <&> \dat -> [fmt|{dat.groupId}: {dat.groupName}|]
+      logInfo [fmt|Inserting tour groups for {showPretty groupNames}|]
+      _ <-
+        execute
+          [fmt|
+                  DELETE FROM redacted.torrent_groups
+                  WHERE group_id = ANY (?::integer[])
+              |]
+          (Only $ (dats <&> (.groupId) & toList & PGArray :: PGArray Int))
+      executeManyReturningWith
+        [fmt|
+              INSERT INTO redacted.torrent_groups (
+                group_id, group_name, full_json_result
+              ) VALUES
+              ( ?, ? , ? )
+              ON CONFLICT (group_id) DO UPDATE SET
+                group_id = excluded.group_id,
+                group_name = excluded.group_name,
+                full_json_result = excluded.full_json_result
+              RETURNING (id)
+            |]
+        ( dats <&> \dat ->
+            ( dat.groupId,
+              dat.groupName,
+              dat.fullJsonResult
+            )
+        )
+        (label @"tourGroupIdPg" <$> Dec.fromField @Int)
+
+    insertTorrents ::
+      [ T2
+          "torrentGroupIdPg"
+          Int
+          "torrents"
+          [T2 "torrentId" Int "fullJsonResult" Json.Value]
+      ] ->
+      Transaction m ()
+    insertTorrents dats = do
+      _ <-
+        execute
+          [sql|
+            DELETE FROM redacted.torrents_json
+            WHERE torrent_id = ANY (?::integer[])
+          |]
+          ( Only $
+              PGArray
+                [ torrent.torrentId
+                  | dat <- dats,
+                    torrent <- dat.torrents
+                ]
+          )
+
+      execute
+        [sql|
+          INSERT INTO redacted.torrents_json
+            ( torrent_group
+            , torrent_id
+            , full_json_result)
+          SELECT *
+          FROM UNNEST(
+              ?::integer[]
+            , ?::integer[]
+            , ?::jsonb[]
+          ) AS inputs(
+              torrent_group
+            , torrent_id
+            , full_json_result)
+          |]
+        ( [ ( dat.torrentGroupIdPg :: Int,
+              group.torrentId :: Int,
+              group.fullJsonResult :: Json.Value
+            )
+            | dat <- dats,
+              group <- dat.torrents
+          ]
+            & unzip3PGArray
+        )
+      pure ()
+
+unzip3PGArray :: [(a1, a2, a3)] -> (PGArray a1, PGArray a2, PGArray a3)
+unzip3PGArray xs = xs & unzip3 & \(a, b, c) -> (PGArray a, PGArray b, PGArray c)
+
+redactedGetTorrentFileAndInsert ::
+  ( HasField "torrentId" r Int,
+    MonadPostgres m,
+    MonadThrow m,
+    MonadLogger m,
+    MonadOtel m
+  ) =>
+  r ->
+  Transaction m (Label "torrentFile" ByteString)
+redactedGetTorrentFileAndInsert dat = inSpan' "Redacted Get Torrent File and Insert" $ \span -> do
+  bytes <- redactedGetTorrentFile dat
+  execute
+    [sql|
+    UPDATE redacted.torrents_json
+    SET torrent_file = ?::bytea
+    WHERE torrent_id = ?::integer
+  |]
+    ( (Binary bytes :: Binary ByteString),
+      dat.torrentId
+    )
+    >>= assertOneUpdated span "redactedGetTorrentFileAndInsert"
+    >>= \() -> pure (label @"torrentFile" bytes)
+
+getTorrentFileById ::
+  ( MonadPostgres m,
+    HasField "torrentId" r Int,
+    MonadThrow m
+  ) =>
+  r ->
+  Transaction m (Maybe (Label "torrentFile" ByteString))
+getTorrentFileById dat = do
+  queryWith
+    [sql|
+    SELECT torrent_file
+    FROM redacted.torrents
+    WHERE torrent_id = ?::integer
+  |]
+    (Only $ (dat.torrentId :: Int))
+    (fmap @Maybe (label @"torrentFile") <$> Dec.byteaMay)
+    >>= ensureSingleRow
+
+updateTransmissionTorrentHashById ::
+  ( MonadPostgres m,
+    HasField "torrentId" r Int,
+    HasField "torrentHash" r Text
+  ) =>
+  r ->
+  Transaction m (Label "numberOfRowsAffected" Natural)
+updateTransmissionTorrentHashById dat = do
+  execute
+    [sql|
+    UPDATE redacted.torrents_json
+    SET transmission_torrent_hash = ?::text
+    WHERE torrent_id = ?::integer
+    |]
+    ( dat.torrentHash :: Text,
+      dat.torrentId :: Int
+    )
+
+assertOneUpdated ::
+  (HasField "numberOfRowsAffected" r Natural, MonadThrow m, MonadIO m) =>
+  Otel.Span ->
+  Text ->
+  r ->
+  m ()
+assertOneUpdated span name x = case x.numberOfRowsAffected of
+  1 -> pure ()
+  n -> appThrowTree span ([fmt|{name :: Text}: Expected to update exactly one row, but updated {n :: Natural} row(s)|])
+
+data TorrentData transmissionInfo = TorrentData
+  { groupId :: Int,
+    torrentId :: Int,
+    seedingWeight :: Int,
+    torrentJson :: Json.Value,
+    torrentGroupJson :: T3 "artist" Text "groupName" Text "groupYear" Int,
+    torrentStatus :: TorrentStatus transmissionInfo
+  }
+
+data TorrentStatus transmissionInfo
+  = NoTorrentFileYet
+  | NotInTransmissionYet
+  | InTransmission (T2 "torrentHash" Text "transmissionInfo" transmissionInfo)
+
+getTorrentById :: (MonadPostgres m, HasField "torrentId" r Int, MonadThrow m) => r -> Transaction m Json.Value
+getTorrentById dat = do
+  queryWith
+    [sql|
+    SELECT full_json_result FROM redacted.torrents
+    WHERE torrent_id = ?::integer
+  |]
+    (getLabel @"torrentId" dat)
+    (Dec.json Json.asValue)
+    >>= ensureSingleRow
+
+-- | Find the best torrent for each torrent group (based on the seeding_weight)
+getBestTorrents :: (MonadPostgres m) => Transaction m [TorrentData ()]
+getBestTorrents = do
+  queryWith
+    [sql|
+      SELECT * FROM (
+        SELECT DISTINCT ON (group_id)
+          tg.group_id,
+          t.torrent_id,
+          seeding_weight,
+          t.full_json_result AS torrent_json,
+          tg.full_json_result AS torrent_group_json,
+          t.torrent_file IS NOT NULL,
+          t.transmission_torrent_hash
+        FROM redacted.torrents t
+        JOIN redacted.torrent_groups tg ON tg.id = t.torrent_group
+        ORDER BY group_id, seeding_weight DESC
+      ) as _
+      ORDER BY seeding_weight DESC
+    |]
+    ()
+    ( do
+        groupId <- Dec.fromField @Int
+        torrentId <- Dec.fromField @Int
+        seedingWeight <- Dec.fromField @Int
+        torrentJson <- Dec.json Json.asValue
+        torrentGroupJson <-
+          ( Dec.json $ do
+              artist <- Json.keyLabel @"artist" "artist" Json.asText
+              groupName <- Json.keyLabel @"groupName" "groupName" Json.asText
+              groupYear <- Json.keyLabel @"groupYear" "groupYear" (Json.asIntegral @_ @Int)
+              pure $ T3 artist groupName groupYear
+            )
+        hasTorrentFile <- Dec.fromField @Bool
+        transmissionTorrentHash <-
+          Dec.fromField @(Maybe Text)
+        pure $
+          TorrentData
+            { torrentStatus =
+                if
+                  | not hasTorrentFile -> NoTorrentFileYet
+                  | Nothing <- transmissionTorrentHash -> NotInTransmissionYet
+                  | Just hash <- transmissionTorrentHash ->
+                      InTransmission $
+                        T2 (label @"torrentHash" hash) (label @"transmissionInfo" ()),
+              ..
+            }
+    )
+
+-- | Do a request to the redacted API. If you know what that is, you know how to find the API docs.
+mkRedactedApiRequest ::
+  ( MonadThrow m,
+    MonadIO m,
+    MonadLogger m,
+    HasField "action" p ByteString,
+    HasField "actionArgs" p [(ByteString, Maybe ByteString)]
+  ) =>
+  p ->
+  m Http.Request
+mkRedactedApiRequest dat = do
+  authKey <- runCommandExpect0 "pass" ["internet/redacted/api-keys/whatcd-resolver"]
+  pure $
+    [fmt|https://redacted.ch/ajax.php|]
+      & Http.setRequestMethod "GET"
+      & Http.setQueryString (("action", Just dat.action) : dat.actionArgs)
+      & Http.setRequestHeader "Authorization" [authKey]
+
+httpTorrent ::
+  ( MonadIO m,
+    MonadThrow m
+  ) =>
+  Otel.Span ->
+  Http.Request ->
+  m ByteString
+httpTorrent span req =
+  Http.httpBS req
+    >>= assertM
+      span
+      ( \resp -> do
+          let statusCode = resp & Http.responseStatus & (.statusCode)
+              contentType =
+                resp
+                  & Http.responseHeaders
+                  & List.lookup "content-type"
+                  <&> Wai.parseContentType
+                  <&> (\(ct, _mimeAttributes) -> ct)
+          if
+            | statusCode == 200,
+              Just "application/x-bittorrent" <- contentType ->
+                Right $ (resp & Http.responseBody)
+            | statusCode == 200,
+              Just otherType <- contentType ->
+                Left [fmt|Redacted returned a non-torrent body, with content-type "{otherType}"|]
+            | statusCode == 200,
+              Nothing <- contentType ->
+                Left [fmt|Redacted returned a body with unspecified content type|]
+            | code <- statusCode -> Left [fmt|Redacted returned an non-200 error code, code {code}: {resp & showPretty}|]
+      )
+
+httpJson ::
+  ( MonadThrow m,
+    MonadOtel m
+  ) =>
+  (Optional (Label "contentType" ByteString)) ->
+  Json.Parse ErrorTree b ->
+  Http.Request ->
+  m b
+httpJson opts parser req = inSpan' "HTTP Request (JSON)" $ \span -> do
+  let opts' = opts.withDefault (label @"contentType" "application/json")
+  Http.httpBS req
+    >>= assertM
+      span
+      ( \resp -> do
+          let statusCode = resp & Http.responseStatus & (.statusCode)
+              contentType =
+                resp
+                  & Http.responseHeaders
+                  & List.lookup "content-type"
+                  <&> Wai.parseContentType
+                  <&> (\(ct, _mimeAttributes) -> ct)
+          if
+            | statusCode == 200,
+              Just ct <- contentType,
+              ct == opts'.contentType ->
+                Right $ (resp & Http.responseBody)
+            | statusCode == 200,
+              Just otherType <- contentType ->
+                Left [fmt|Server returned a non-json body, with content-type "{otherType}"|]
+            | statusCode == 200,
+              Nothing <- contentType ->
+                Left [fmt|Server returned a body with unspecified content type|]
+            | code <- statusCode -> Left [fmt|Server returned an non-200 error code, code {code}: {resp & showPretty}|]
+      )
+    >>= assertM
+      span
+      ( \body ->
+          Json.parseStrict parser body
+            & first (Json.parseErrorTree "could not parse redacted response")
+      )
+
+redactedApiRequestJson ::
+  ( MonadThrow m,
+    MonadLogger m,
+    HasField "action" p ByteString,
+    HasField "actionArgs" p [(ByteString, Maybe ByteString)],
+    MonadOtel m
+  ) =>
+  p ->
+  Json.Parse ErrorTree a ->
+  m a
+redactedApiRequestJson dat parser =
+  do
+    mkRedactedApiRequest dat
+    >>= httpJson defaults parser
diff --git a/users/Profpatsch/whatcd-resolver/src/Transmission.hs b/users/Profpatsch/whatcd-resolver/src/Transmission.hs
new file mode 100644
index 0000000000..66dbeb9ce7
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/Transmission.hs
@@ -0,0 +1,306 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module Transmission where
+
+import AppT
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.Error.Tree
+import Data.HashMap.Strict qualified as HashMap
+import Data.List qualified as List
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Map.Strict qualified as Map
+import Database.PostgreSQL.Simple (Only (..))
+import Database.PostgreSQL.Simple.Types (PGArray (PGArray))
+import FieldParser (FieldParser' (..))
+import FieldParser qualified as Field
+import Html qualified
+import Http qualified
+import Json qualified
+import Json.Enc (Enc)
+import Json.Enc qualified as Enc
+import Label
+import MyPrelude
+import Network.HTTP.Types
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import Optional
+import Postgres.MonadPostgres
+import Pretty
+import Text.Blaze.Html (Html)
+import UnliftIO
+import Prelude hiding (span)
+
+-- | A value between (inclusive) 0% and (inclusive) 100%. Precise to 1% steps.
+newtype Percentage = Percentage {unPercentage :: Int}
+  deriving stock (Show)
+
+-- | Parse a scientific into a Percentage
+scientificPercentage :: FieldParser' Error Scientific Percentage
+scientificPercentage =
+  Field.boundedScientificRealFloat @Float
+    >>> ( FieldParser $ \f ->
+            if
+              | f < 0 -> Left "percentage cannot be negative"
+              | f > 1 -> Left "percentage cannot be over 100%"
+              | otherwise -> Right $ Percentage $ ceiling (f * 100)
+        )
+
+-- | Fetch the current status from transmission, and remove the tranmission hash from our database
+-- iff it does not exist in transmission anymore
+getAndUpdateTransmissionTorrentsStatus ::
+  ( MonadTransmission m,
+    MonadThrow m,
+    MonadLogger m,
+    MonadPostgres m,
+    MonadOtel m
+  ) =>
+  Map (Label "torrentHash" Text) () ->
+  (Transaction m (Map (Label "torrentHash" Text) (Label "percentDone" Percentage)))
+getAndUpdateTransmissionTorrentsStatus knownTorrents = do
+  let fields = ["hashString", "percentDone"]
+  actualTorrents <-
+    lift @Transaction $
+      doTransmissionRequest'
+        ( transmissionRequestListOnlyTorrents
+            ( T2
+                (label @"fields" fields)
+                (label @"ids" (Map.keys knownTorrents))
+            )
+            $ do
+              torrentHash <- Json.keyLabel @"torrentHash" "hashString" Json.asText
+              percentDone <- Json.keyLabel @"percentDone" "percentDone" (Field.toJsonParser $ Field.jsonNumber >>> scientificPercentage)
+              pure (torrentHash, percentDone)
+        )
+        <&> Map.fromList
+  let toDelete = Map.difference knownTorrents actualTorrents
+  execute
+    [fmt|
+    UPDATE redacted.torrents_json
+    SET transmission_torrent_hash = NULL
+    WHERE transmission_torrent_hash = ANY (?::text[])
+  |]
+    $ Only (toDelete & Map.keys <&> (.torrentHash) & PGArray :: PGArray Text)
+  pure actualTorrents
+
+getTransmissionTorrentsTable ::
+  (MonadTransmission m, MonadThrow m, MonadLogger m, MonadOtel m) => m Html
+getTransmissionTorrentsTable = do
+  let fields =
+        [ "hashString",
+          "name",
+          "percentDone",
+          "percentComplete",
+          "downloadDir",
+          "files"
+        ]
+  doTransmissionRequest'
+    ( transmissionRequestListAllTorrents fields $ do
+        Json.asObject <&> KeyMap.toMapText
+    )
+    <&> \resp ->
+      Html.toTable
+        ( resp
+            & List.sortOn (\m -> m & Map.lookup "percentDone" & fromMaybe (Json.Number 0))
+            <&> Map.toList
+            -- TODO
+            & List.take 100
+        )
+
+data TransmissionRequest = TransmissionRequest
+  { method :: Text,
+    arguments :: Map Text Enc,
+    tag :: Maybe Int
+  }
+  deriving stock (Show)
+
+transmissionConnectionConfig :: T3 "host" Text "port" Int "usePlainHttp" Bool
+transmissionConnectionConfig = (T3 (label @"host" "localhost") (label @"port" 9091) (label @"usePlainHttp" True))
+
+transmissionRequestListAllTorrents :: (Monad m) => [Text] -> Json.ParseT e m out -> (TransmissionRequest, Json.ParseT e m [out])
+transmissionRequestListAllTorrents fields parseTorrent =
+  ( TransmissionRequest
+      { method = "torrent-get",
+        arguments =
+          Map.fromList
+            [ ("fields", Enc.list Enc.text fields)
+            ],
+        tag = Nothing
+      },
+    Json.key "torrents" $ Json.eachInArray parseTorrent
+  )
+
+transmissionRequestListOnlyTorrents ::
+  ( HasField "ids" r1 [(Label "torrentHash" Text)],
+    HasField "fields" r1 [Text],
+    Monad m
+  ) =>
+  r1 ->
+  Json.ParseT e m out ->
+  (TransmissionRequest, Json.ParseT e m [out])
+transmissionRequestListOnlyTorrents dat parseTorrent =
+  ( TransmissionRequest
+      { method = "torrent-get",
+        arguments =
+          Map.fromList
+            [ ("ids", Enc.list (\i -> Enc.text i.torrentHash) dat.ids),
+              ("fields", Enc.list Enc.text dat.fields)
+            ],
+        tag = Nothing
+      },
+    Json.key "torrents" $ Json.eachInArray parseTorrent
+  )
+
+transmissionRequestAddTorrent ::
+  (HasField "torrentFile" r ByteString, Monad m) =>
+  r ->
+  ( TransmissionRequest,
+    Json.ParseT err m (T2 "torrentHash" Text "torrentName" Text)
+  )
+transmissionRequestAddTorrent dat =
+  ( TransmissionRequest
+      { method = "torrent-add",
+        arguments =
+          Map.fromList
+            [ ("metainfo", Enc.base64Bytes dat.torrentFile),
+              ("paused", Enc.bool False)
+            ],
+        tag = Nothing
+      },
+    do
+      let p method = Json.key method $ do
+            hash <- Json.keyLabel @"torrentHash" "hashString" Json.asText
+            name <- Json.keyLabel @"torrentName" "name" Json.asText
+            pure $ T2 hash name
+      p "torrent-duplicate" Json.<|> p "torrent-added"
+  )
+
+data TransmissionResponse output = TransmissionResponse
+  { result :: TransmissionResponseStatus,
+    arguments :: Maybe output,
+    tag :: Maybe Int
+  }
+  deriving stock (Show)
+
+data TransmissionResponseStatus
+  = TransmissionResponseSuccess
+  | TransmissionResponseFailure Text
+  deriving stock (Show)
+
+doTransmissionRequest' ::
+  ( MonadTransmission m,
+    MonadThrow m,
+    MonadLogger m,
+    MonadOtel m
+  ) =>
+  (TransmissionRequest, Json.Parse Error output) ->
+  m output
+doTransmissionRequest' req = inSpan' "Transmission Request" $ \span -> do
+  resp <-
+    doTransmissionRequest
+      span
+      transmissionConnectionConfig
+      req
+  case resp.result of
+    TransmissionResponseFailure err -> appThrowTree span (nestedError "Transmission RPC error" $ singleError $ newError err)
+    TransmissionResponseSuccess -> case resp.arguments of
+      Nothing -> appThrowTree span "Transmission RPC error: No `arguments` field in response"
+      Just out -> pure out
+
+-- | Contact the transmission RPC, and do the CSRF protection dance.
+--
+-- Spec: https://github.com/transmission/transmission/blob/main/docs/rpc-spec.md
+doTransmissionRequest ::
+  ( MonadTransmission m,
+    HasField "host" t1 Text,
+    HasField "port" t1 Int,
+    HasField "usePlainHttp" t1 Bool,
+    MonadThrow m,
+    MonadLogger m,
+    MonadOtel m
+  ) =>
+  Otel.Span ->
+  t1 ->
+  (TransmissionRequest, Json.Parse Error output) ->
+  m (TransmissionResponse output)
+doTransmissionRequest span dat (req, parser) = do
+  sessionId <- getTransmissionId
+  let textArg t = (Enc.text t, Otel.toAttribute @Text t)
+  let encArg enc = (enc, Otel.toAttribute @Text $ enc & Enc.encToTextPretty)
+  let intArg i = (Enc.int i, Otel.toAttribute @Int i)
+
+  let body :: [(Text, (Enc, Otel.Attribute))] =
+        ( [ ("method", req.method & textArg),
+            ("arguments", encArg $ Enc.map id req.arguments)
+          ]
+            <> (req.tag & foldMap (\t -> [("tag", t & intArg)]))
+        )
+  addAttributes
+    span
+    ( HashMap.fromList $
+        body
+          <&> bimap
+            (\k -> [fmt|transmission.{k}|])
+            (\(_, attr) -> attr)
+    )
+  resp <-
+    Http.doRequestJson
+      ( (Http.mkRequestOptions (T2 (label @"method" "POST") (label @"host" dat.host)))
+          { Http.path = mkOptional ["transmission", "rpc"],
+            Http.port = mkOptional dat.port,
+            Http.headers = mkOptional $ (sessionId & ifExists ("X-Transmission-Session-Id",)),
+            Http.usePlainHttp = mkOptional dat.usePlainHttp
+          }
+      )
+      (body <&> second fst & Enc.object)
+  -- Implement the CSRF protection thingy
+  case resp & Http.getResponseStatus & (.statusCode) of
+    409 -> do
+      tid <-
+        resp
+          & Http.getResponseHeader "X-Transmission-Session-Id"
+          & nonEmpty
+          & annotate [fmt|Missing "X-Transmission-Session-Id" header in 409 response: {showPretty resp}|]
+          & unwrapIOError
+          & liftIO
+          <&> NonEmpty.head
+      setTransmissionId tid
+      doTransmissionRequest span dat (req, parser)
+    200 ->
+      resp
+        & Http.getResponseBody
+        & Json.parseStrict
+          ( Json.mapError singleError $ do
+              result <-
+                Json.key "result" Json.asText <&> \case
+                  "success" -> TransmissionResponseSuccess
+                  err -> TransmissionResponseFailure err
+              arguments <-
+                Json.keyMay "arguments" parser
+              tag <-
+                Json.keyMay
+                  "tag"
+                  (Field.toJsonParser (Field.jsonNumber >>> Field.boundedScientificIntegral "tag too long"))
+              pure TransmissionResponse {..}
+          )
+        & first (Json.parseErrorTree "Cannot parse transmission RPC response")
+        & \case
+          Right a -> pure a
+          Left err -> do
+            case Json.eitherDecodeStrict' @Json.Value (resp & Http.getResponseBody) of
+              Left _err -> pure ()
+              Right val -> logInfo [fmt|failing transmission response: {showPrettyJson val}|]
+            appThrowTree span err
+    _ -> liftIO $ unwrapIOError $ Left [fmt|Non-200 response: {showPretty resp}|]
+
+class MonadTransmission m where
+  getTransmissionId :: m (Maybe ByteString)
+  setTransmissionId :: ByteString -> m ()
+
+instance (MonadIO m) => MonadTransmission (AppT m) where
+  getTransmissionId = AppT (asks (.transmissionSessionId)) >>= tryTakeMVar
+  setTransmissionId t = do
+    var <- AppT $ asks (.transmissionSessionId)
+    putMVar var t
diff --git a/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs b/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs
new file mode 100644
index 0000000000..f1902bac8c
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/src/WhatcdResolver.hs
@@ -0,0 +1,698 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module WhatcdResolver where
+
+import AppT
+import Control.Category qualified as Cat
+import Control.Monad.Catch.Pure (runCatch)
+import Control.Monad.Logger.CallStack
+import Control.Monad.Reader
+import Data.Aeson qualified as Json
+import Data.Aeson.BetterErrors qualified as Json
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.HashMap.Strict qualified as HashMap
+import Data.List qualified as List
+import Data.Map.Strict qualified as Map
+import Data.Pool qualified as Pool
+import Data.Text qualified as Text
+import Database.PostgreSQL.Simple qualified as Postgres
+import Database.PostgreSQL.Simple.SqlQQ (sql)
+import Database.PostgreSQL.Simple.Types (PGArray (PGArray))
+import Database.Postgres.Temp qualified as TmpPg
+import FieldParser (FieldParser, FieldParser' (..))
+import FieldParser qualified as Field
+import Html qualified
+import IHP.HSX.QQ (hsx)
+import Json qualified
+import Json.Enc (Enc)
+import Json.Enc qualified as Enc
+import JsonLd
+import Label
+import Multipart2 qualified as Multipart
+import MyPrelude
+import Network.HTTP.Client.Conduit qualified as Http
+import Network.HTTP.Simple qualified as Http
+import Network.HTTP.Types
+import Network.HTTP.Types qualified as Http
+import Network.URI (URI)
+import Network.URI qualified
+import Network.URI qualified as URI
+import Network.Wai (ResponseReceived)
+import Network.Wai qualified as Wai
+import Network.Wai.Handler.Warp qualified as Warp
+import Network.Wai.Parse qualified as Wai
+import OpenTelemetry.Attributes qualified as Otel
+import OpenTelemetry.Trace qualified as Otel hiding (getTracer, inSpan, inSpan')
+import OpenTelemetry.Trace.Monad qualified as Otel
+import Parse (Parse)
+import Parse qualified
+import Postgres.Decoder qualified as Dec
+import Postgres.MonadPostgres
+import Pretty
+import Redacted
+import System.Directory qualified as Dir
+import System.Directory qualified as Xdg
+import System.Environment qualified as Env
+import System.FilePath ((</>))
+import Text.Blaze.Html (Html)
+import Text.Blaze.Html.Renderer.Pretty qualified as Html.Pretty
+import Text.Blaze.Html.Renderer.Utf8 qualified as Html
+import Text.Blaze.Html5 qualified as Html
+import Tool (readTool, readTools)
+import Transmission
+import UnliftIO hiding (Handler)
+import Prelude hiding (span)
+
+main :: IO ()
+main =
+  runAppWith
+    ( do
+        -- todo: trace that to the init functions as well
+        Otel.inSpan "whatcd-resolver main function" Otel.defaultSpanArguments $ do
+          _ <- runTransaction migrate
+          htmlUi
+    )
+    <&> first showToError
+    >>= expectIOError "could not start whatcd-resolver"
+
+htmlUi :: AppT IO ()
+htmlUi = do
+  let debug = True
+  uniqueRunId <-
+    runTransaction $
+      querySingleRowWith
+        [sql|
+            SELECT gen_random_uuid()::text
+        |]
+        ()
+        (Dec.fromField @Text)
+
+  withRunInIO $ \runInIO -> Warp.run 9093 $ \req respond -> do
+    let catchAppException act =
+          try act >>= \case
+            Right a -> pure a
+            Left (AppException err) -> do
+              runInIO (logError err)
+              respond (Wai.responseLBS Http.status500 [] "")
+
+    catchAppException $ do
+      let mp span parser =
+            Multipart.parseMultipartOrThrow
+              (appThrowTree span)
+              parser
+              req
+
+      let torrentIdMp span =
+            mp
+              span
+              ( do
+                  label @"torrentId" <$> Multipart.field "torrent-id" ((Field.utf8 >>> Field.signedDecimal >>> Field.bounded @Int "int"))
+              )
+      let parseQueryArgs span parser =
+            Parse.runParse "Unable to find the right request query arguments" (lmap Wai.queryString parser) req
+              & assertM span id
+
+      let parseQueryArgsNewSpan spanName parser =
+            Parse.runParse "Unable to find the right request query arguments" (lmap Wai.queryString parser) req
+              & assertMNewSpan spanName id
+
+      let handlers :: Handlers (AppT IO)
+          handlers respond =
+            Map.fromList
+              [ ("", respond.h (mainHtml uniqueRunId)),
+                ( "snips/redacted/search",
+                  respond.h $
+                    \span -> do
+                      dat <-
+                        mp
+                          span
+                          ( do
+                              label @"searchstr" <$> Multipart.field "redacted-search" Cat.id
+                          )
+                      snipsRedactedSearch dat
+                ),
+                ( "snips/redacted/torrentDataJson",
+                  respond.h $ \span -> do
+                    dat <- torrentIdMp span
+                    Html.mkVal <$> (runTransaction $ getTorrentById dat)
+                ),
+                ( "snips/redacted/getTorrentFile",
+                  respond.h $ \span -> do
+                    dat <- torrentIdMp span
+                    runTransaction $ do
+                      inserted <- redactedGetTorrentFileAndInsert dat
+                      running <-
+                        lift @Transaction $
+                          doTransmissionRequest' (transmissionRequestAddTorrent inserted)
+                      updateTransmissionTorrentHashById
+                        ( T2
+                            (getLabel @"torrentHash" running)
+                            (getLabel @"torrentId" dat)
+                        )
+                      pure $
+                        everySecond
+                          "snips/transmission/getTorrentState"
+                          (Enc.object [("torrent-hash", Enc.text running.torrentHash)])
+                          "Starting"
+                ),
+                -- TODO: this is bad duplication??
+                ( "snips/redacted/startTorrentFile",
+                  respond.h $ \span -> do
+                    dat <- torrentIdMp span
+                    runTransaction $ do
+                      file <-
+                        getTorrentFileById dat
+                          <&> annotate [fmt|No torrent file for torrentId "{dat.torrentId}"|]
+                          >>= orAppThrowTree span
+
+                      running <-
+                        lift @Transaction $
+                          doTransmissionRequest' (transmissionRequestAddTorrent file)
+                      updateTransmissionTorrentHashById
+                        ( T2
+                            (getLabel @"torrentHash" running)
+                            (getLabel @"torrentId" dat)
+                        )
+                      pure $
+                        everySecond
+                          "snips/transmission/getTorrentState"
+                          (Enc.object [("torrent-hash", Enc.text running.torrentHash)])
+                          "Starting"
+                ),
+                ( "snips/transmission/getTorrentState",
+                  respond.h $ \span -> do
+                    dat <- mp span $ label @"torrentHash" <$> Multipart.field "torrent-hash" Field.utf8
+                    status <-
+                      doTransmissionRequest'
+                        ( transmissionRequestListOnlyTorrents
+                            ( T2
+                                (label @"ids" [label @"torrentHash" dat.torrentHash])
+                                (label @"fields" ["hashString"])
+                            )
+                            (Json.keyLabel @"torrentHash" "hashString" Json.asText)
+                        )
+                        <&> List.find (\torrent -> torrent.torrentHash == dat.torrentHash)
+
+                    pure $
+                      case status of
+                        Nothing -> [hsx|ERROR unknown|]
+                        Just _torrent -> [hsx|Running|]
+                ),
+                ( "snips/jsonld/render",
+                  respond.h $ \span -> do
+                    qry <-
+                      parseQueryArgs
+                        span
+                        ( label @"target"
+                            <$> ( (singleQueryArgument "target" Field.utf8 >>> textToURI)
+                                    & Parse.andParse uriToHttpClientRequest
+                                )
+                        )
+                    jsonld <- httpGetJsonLd (qry.target)
+                    pure $ renderJsonld jsonld
+                ),
+                ( "autorefresh",
+                  respond.plain $ do
+                    qry <-
+                      parseQueryArgsNewSpan
+                        "Autorefresh Query Parse"
+                        ( label @"hasItBeenRestarted"
+                            <$> singleQueryArgument "hasItBeenRestarted" Field.utf8
+                        )
+                    pure $
+                      Wai.responseLBS
+                        Http.ok200
+                        ( [("Content-Type", "text/html")]
+                            <> if uniqueRunId /= qry.hasItBeenRestarted
+                              then -- cause the client side to refresh
+                                [("HX-Refresh", "true")]
+                              else []
+                        )
+                        ""
+                )
+              ]
+      runInIO $
+        runHandlers
+          debug
+          (\respond -> respond.h $ (mainHtml uniqueRunId))
+          handlers
+          req
+          respond
+  where
+    everySecond :: Text -> Enc -> Html -> Html
+    everySecond call extraData innerHtml = [hsx|<div hx-trigger="every 1s" hx-swap="outerHTML" hx-post={call} hx-vals={Enc.encToBytesUtf8 extraData}>{innerHtml}</div>|]
+
+    mainHtml :: Text -> Otel.Span -> AppT IO Html
+    mainHtml uniqueRunId _span = runTransaction $ do
+      jsonld <-
+        httpGetJsonLd
+          ( URI.parseURI "https://musicbrainz.org/work/92000fd4-d304-406d-aeb4-6bdbeed318ec" & annotate "not an URI" & unwrapError,
+            "https://musicbrainz.org/work/92000fd4-d304-406d-aeb4-6bdbeed318ec"
+          )
+          <&> renderJsonld
+      bestTorrentsTable <- getBestTorrentsTable
+      -- transmissionTorrentsTable <- lift @Transaction getTransmissionTorrentsTable
+      pure $
+        Html.docTypeHtml
+          [hsx|
+      <head>
+        <title>whatcd-resolver</title>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>
+        <script src="https://unpkg.com/htmx.org@1.9.2" integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h" crossorigin="anonymous"></script>
+        <style>
+          dl {
+            margin: 1em;
+            padding: 0.5em 1em;
+            border: thin solid;
+          }
+        </style>
+      </head>
+      <body>
+        {jsonld}
+        <form
+          hx-post="/snips/redacted/search"
+          hx-target="#redacted-search-results">
+          <label for="redacted-search">Redacted Search</label>
+          <input
+            id="redacted-search"
+            type="text"
+            name="redacted-search" />
+          <button type="submit" hx-disabled-elt="this">Search</button>
+          <div class="htmx-indicator">Search running!</div>
+        </form>
+        <div id="redacted-search-results">
+          {bestTorrentsTable}
+        </div>
+        <!-- refresh the page if the uniqueRunId is different -->
+        <input
+             hidden
+             type="text"
+             id="autorefresh"
+             name="hasItBeenRestarted"
+             value={uniqueRunId}
+             hx-get="/autorefresh"
+             hx-trigger="every 5s"
+             hx-swap="none"
+        />
+      </body>
+    |]
+
+type Handlers m = HandlerResponses m -> Map Text (m ResponseReceived)
+
+type HandlerResponses m = T2 "h" ((Otel.Span -> m Html) -> m ResponseReceived) "plain" (m Wai.Response -> m ResponseReceived)
+
+runHandlers ::
+  (MonadOtel m) =>
+  Bool ->
+  (HandlerResponses m -> m ResponseReceived) ->
+  (HandlerResponses m -> Map Text (m ResponseReceived)) ->
+  Wai.Request ->
+  (Wai.Response -> IO ResponseReceived) ->
+  m ResponseReceived
+runHandlers debug defaultHandler handlers req respond = withRunInIO $ \runInIO -> do
+  let renderHtml =
+        if debug
+          then Html.Pretty.renderHtml >>> stringToText >>> textToBytesUtf8 >>> toLazyBytes
+          else Html.renderHtml
+  let hh route act =
+        Otel.inSpan'
+          [fmt|Route {route }|]
+          ( Otel.defaultSpanArguments
+              { Otel.attributes =
+                  HashMap.fromList
+                    [ ("server.path", Otel.toAttribute @Text route)
+                    ]
+              }
+          )
+          ( \span -> do
+              res <- act span
+              liftIO $ respond . Wai.responseLBS Http.ok200 ([("Content-Type", "text/html")] <> res.extraHeaders) . renderHtml $ res.html
+          )
+  let h route act = hh route (\span -> act span <&> (\html -> T2 (label @"html" html) (label @"extraHeaders" [])))
+
+  let path = (req & Wai.pathInfo & Text.intercalate "/")
+  let handlerResponses =
+        ( T2
+            (label @"h" (h path))
+            (label @"plain" (\m -> liftIO $ runInIO m >>= respond))
+        )
+  let handler =
+        (handlers handlerResponses)
+          & Map.lookup path
+          & fromMaybe (defaultHandler handlerResponses)
+  runInIO handler
+
+singleQueryArgument :: Text -> FieldParser ByteString to -> Parse Http.Query to
+singleQueryArgument field inner =
+  Parse.mkParsePushContext
+    field
+    ( \(ctx, qry) -> case qry
+        & mapMaybe
+          ( \(k, v) ->
+              if k == (field & textToBytesUtf8)
+                then Just v
+                else Nothing
+          ) of
+        [] -> Left [fmt|No such query argument "{field}", at {ctx & Parse.showContext}|]
+        [Nothing] -> Left [fmt|Expected one query argument with a value, but "{field}" was a query flag|]
+        [Just one] -> Right one
+        more -> Left [fmt|More than one value for query argument "{field}": {show more}, at {ctx & Parse.showContext}|]
+    )
+    >>> Parse.fieldParser inner
+
+-- | Make sure we can parse the given Text into an URI.
+textToURI :: Parse Text URI
+textToURI =
+  Parse.fieldParser
+    ( FieldParser $ \text ->
+        text
+          & textToString
+          & Network.URI.parseURI
+          & annotate [fmt|Cannot parse this as a URL: "{text}"|]
+    )
+
+-- | Make sure we can parse the given URI into a Request.
+--
+-- This tries to work around the horrible, horrible interface in Http.Client.
+uriToHttpClientRequest :: Parse URI Http.Request
+uriToHttpClientRequest =
+  Parse.mkParseNoContext
+    ( \url ->
+        (url & Http.requestFromURI)
+          & runCatch
+          & first (checkException @Http.HttpException)
+          & \case
+            Left (Right (Http.InvalidUrlException urlText reason)) ->
+              Left [fmt|Unable to set the url "{urlText}" as request URL, reason: {reason}|]
+            Left (Right exc@(Http.HttpExceptionRequest _ _)) ->
+              Left [fmt|Weird! Should not get a HttpExceptionRequest when parsing an URL (bad library design), was {exc & displayException}|]
+            Left (Left someExc) ->
+              Left [fmt|Weird! Should not get anyhting but a HttpException when parsing an URL (bad library design), was {someExc & displayException}|]
+            Right req -> pure req
+    )
+
+checkException :: (Exception b) => SomeException -> Either SomeException b
+checkException some = case fromException some of
+  Nothing -> Left some
+  Just e -> Right e
+
+snipsRedactedSearch ::
+  ( MonadLogger m,
+    MonadPostgres m,
+    HasField "searchstr" r ByteString,
+    MonadThrow m,
+    MonadTransmission m,
+    MonadOtel m
+  ) =>
+  r ->
+  m Html
+snipsRedactedSearch dat = do
+  t <-
+    redactedSearchAndInsert
+      [ ("searchstr", dat.searchstr),
+        ("releasetype", "album")
+      ]
+  runTransaction $ do
+    t
+    getBestTorrentsTable
+
+getBestTorrentsTable ::
+  ( MonadTransmission m,
+    MonadThrow m,
+    MonadLogger m,
+    MonadPostgres m,
+    MonadOtel m
+  ) =>
+  Transaction m Html
+getBestTorrentsTable = do
+  bestStale :: [TorrentData ()] <- getBestTorrents
+  actual <-
+    getAndUpdateTransmissionTorrentsStatus
+      ( bestStale
+          & mapMaybe
+            ( \td -> case td.torrentStatus of
+                InTransmission h -> Just h
+                _ -> Nothing
+            )
+          <&> (\t -> (getLabel @"torrentHash" t, t.transmissionInfo))
+          & Map.fromList
+      )
+  let fresh =
+        bestStale
+          --  we have to update the status of every torrent thatโ€™s not in tranmission anymore
+          -- TODO I feel like itโ€™s easier (& more correct?) to just do the database request again โ€ฆ
+          <&> ( \td -> case td.torrentStatus of
+                  InTransmission info ->
+                    case actual & Map.lookup (getLabel @"torrentHash" info) of
+                      -- TODO this is also pretty dumb, cause it assumes that we have the torrent file if it was in transmission before,
+                      -- which is an internal factum that is established in getBestTorrents (and might change later)
+                      Nothing -> td {torrentStatus = NotInTransmissionYet}
+                      Just transmissionInfo -> td {torrentStatus = InTransmission (T2 (getLabel @"torrentHash" info) (label @"transmissionInfo" transmissionInfo))}
+                  NotInTransmissionYet -> td {torrentStatus = NotInTransmissionYet}
+                  NoTorrentFileYet -> td {torrentStatus = NoTorrentFileYet}
+              )
+  let localTorrent b = case b.torrentStatus of
+        NoTorrentFileYet -> [hsx|<button hx-post="snips/redacted/getTorrentFile" hx-swap="outerHTML" hx-vals={Enc.encToBytesUtf8 $ Enc.object [("torrent-id", Enc.int b.torrentId)]}>Upload Torrent</button>|]
+        InTransmission info -> [hsx|{info.transmissionInfo.percentDone.unPercentage}% done|]
+        NotInTransmissionYet -> [hsx|<button hx-post="snips/redacted/startTorrentFile" hx-swap="outerHTML" hx-vals={Enc.encToBytesUtf8 $ Enc.object [("torrent-id", Enc.int b.torrentId)]}>Start Torrent</button>|]
+  let bestRows =
+        fresh
+          & foldMap
+            ( \b -> do
+                [hsx|
+                  <tr>
+                  <td>{localTorrent b}</td>
+                  <td>{Html.toHtml @Int b.groupId}</td>
+                  <td>{Html.toHtml @Text b.torrentGroupJson.artist}</td>
+                  <td>{Html.toHtml @Text b.torrentGroupJson.groupName}</td>
+                  <td>{Html.toHtml @Int b.seedingWeight}</td>
+                  <td><details hx-trigger="toggle once" hx-post="snips/redacted/torrentDataJson" hx-vals={Enc.encToBytesUtf8 $ Enc.object [("torrent-id", Enc.int b.torrentId)]}></details></td>
+                  </tr>
+                |]
+            )
+  pure $
+    [hsx|
+        <table class="table">
+          <thead>
+            <tr>
+              <th>Local</th>
+              <th>Group ID</th>
+              <th>Artist</th>
+              <th>Name</th>
+              <th>Weight</th>
+              <th>Torrent</th>
+              <th>Torrent Group</th>
+            </tr>
+          </thead>
+          <tbody>
+            {bestRows}
+          </tbody>
+        </table>
+      |]
+
+getTransmissionTorrentsTable ::
+  (MonadTransmission m, MonadThrow m, MonadLogger m, MonadOtel m) => m Html
+getTransmissionTorrentsTable = do
+  let fields =
+        [ "hashString",
+          "name",
+          "percentDone",
+          "percentComplete",
+          "downloadDir",
+          "files"
+        ]
+  doTransmissionRequest'
+    ( transmissionRequestListAllTorrents fields $ do
+        Json.asObject <&> KeyMap.toMapText
+    )
+    <&> \resp ->
+      Html.toTable
+        ( resp
+            & List.sortOn (\m -> m & Map.lookup "percentDone" & fromMaybe (Json.Number 0))
+            <&> Map.toList
+            -- TODO
+            & List.take 100
+        )
+
+unzip3PGArray :: [(a1, a2, a3)] -> (PGArray a1, PGArray a2, PGArray a3)
+unzip3PGArray xs = xs & unzip3 & \(a, b, c) -> (PGArray a, PGArray b, PGArray c)
+
+assertOneUpdated ::
+  (HasField "numberOfRowsAffected" r Natural, MonadThrow m, MonadIO m) =>
+  Otel.Span ->
+  Text ->
+  r ->
+  m ()
+assertOneUpdated span name x = case x.numberOfRowsAffected of
+  1 -> pure ()
+  n -> appThrowTree span ([fmt|{name :: Text}: Expected to update exactly one row, but updated {n :: Natural} row(s)|])
+
+migrate ::
+  ( MonadPostgres m,
+    MonadOtel m
+  ) =>
+  Transaction m (Label "numberOfRowsAffected" Natural)
+migrate = inSpan "Database Migration" $ do
+  execute
+    [sql|
+    CREATE SCHEMA IF NOT EXISTS redacted;
+
+    CREATE TABLE IF NOT EXISTS redacted.torrent_groups (
+      id SERIAL PRIMARY KEY,
+      group_id INTEGER,
+      group_name TEXT,
+      full_json_result JSONB,
+      UNIQUE(group_id)
+    );
+
+    CREATE TABLE IF NOT EXISTS redacted.torrents_json (
+      id SERIAL PRIMARY KEY,
+      torrent_id INTEGER,
+      torrent_group SERIAL NOT NULL REFERENCES redacted.torrent_groups(id) ON DELETE CASCADE,
+      full_json_result JSONB,
+      UNIQUE(torrent_id)
+    );
+
+    ALTER TABLE redacted.torrents_json
+    ADD COLUMN IF NOT EXISTS torrent_file bytea NULL;
+    ALTER TABLE redacted.torrents_json
+    ADD COLUMN IF NOT EXISTS transmission_torrent_hash text NULL;
+
+    -- inflect out values of the full json
+
+    CREATE OR REPLACE VIEW redacted.torrents AS
+    SELECT
+      t.id,
+      t.torrent_id,
+      t.torrent_group,
+      -- the seeding weight is used to find the best torrent in a group.
+      ( ((full_json_result->'seeders')::integer*3
+        + (full_json_result->'snatches')::integer
+        )
+      -- prefer remasters by multiplying them with 3
+      * (CASE
+          WHEN full_json_result->>'remasterTitle' ILIKE '%remaster%'
+          THEN 3
+          ELSE 1
+         END)
+      )
+      AS seeding_weight,
+      t.full_json_result,
+      t.torrent_file,
+      t.transmission_torrent_hash
+    FROM redacted.torrents_json t;
+
+    CREATE INDEX IF NOT EXISTS torrents_json_seeding ON redacted.torrents_json(((full_json_result->'seeding')::integer));
+    CREATE INDEX IF NOT EXISTS torrents_json_snatches ON redacted.torrents_json(((full_json_result->'snatches')::integer));
+  |]
+    ()
+
+httpTorrent ::
+  ( MonadIO m,
+    MonadThrow m
+  ) =>
+  Otel.Span ->
+  Http.Request ->
+  m ByteString
+httpTorrent span req =
+  Http.httpBS req
+    >>= assertM
+      span
+      ( \resp -> do
+          let statusCode = resp & Http.responseStatus & (.statusCode)
+              contentType =
+                resp
+                  & Http.responseHeaders
+                  & List.lookup "content-type"
+                  <&> Wai.parseContentType
+                  <&> (\(ct, _mimeAttributes) -> ct)
+          if
+            | statusCode == 200,
+              Just "application/x-bittorrent" <- contentType ->
+                Right $ (resp & Http.responseBody)
+            | statusCode == 200,
+              Just otherType <- contentType ->
+                Left [fmt|Redacted returned a non-torrent body, with content-type "{otherType}"|]
+            | statusCode == 200,
+              Nothing <- contentType ->
+                Left [fmt|Redacted returned a body with unspecified content type|]
+            | code <- statusCode -> Left [fmt|Redacted returned an non-200 error code, code {code}: {resp & showPretty}|]
+      )
+
+runAppWith :: AppT IO a -> IO (Either TmpPg.StartError a)
+runAppWith appT = withTracer $ \tracer -> withDb $ \db -> do
+  pgFormat <- readTools (label @"toolsEnvVar" "WHATCD_RESOLVER_TOOLS") (readTool "pg_format")
+  let config = label @"logDatabaseQueries" LogDatabaseQueries
+  pgConnPool <-
+    Pool.newPool $
+      Pool.defaultPoolConfig
+        {- resource init action -} (Postgres.connectPostgreSQL (db & TmpPg.toConnectionString))
+        {- resource destruction -} Postgres.close
+        {- unusedResourceOpenTime -} 10
+        {- max resources across all stripes -} 20
+  transmissionSessionId <- newEmptyMVar
+  let newAppT = do
+        logInfo [fmt|Running with config: {showPretty config}|]
+        logInfo [fmt|Connected to database at {db & TmpPg.toDataDirectory} on socket {db & TmpPg.toConnectionString}|]
+        appT
+  runReaderT newAppT.unAppT Context {..}
+
+withTracer :: (Otel.Tracer -> IO c) -> IO c
+withTracer f = do
+  setDefaultEnv "OTEL_SERVICE_NAME" "whatcd-resolver"
+  bracket
+    -- Install the SDK, pulling configuration from the environment
+    ( do
+        (processors, opts) <- Otel.getTracerProviderInitializationOptions
+        tp <-
+          Otel.createTracerProvider
+            processors
+            -- workaround the attribute length bug https://github.com/iand675/hs-opentelemetry/issues/113
+            ( opts
+                { Otel.tracerProviderOptionsAttributeLimits =
+                    opts.tracerProviderOptionsAttributeLimits
+                      { Otel.attributeCountLimit = Just 65_000
+                      }
+                }
+            )
+        Otel.setGlobalTracerProvider tp
+        pure tp
+    )
+    -- Ensure that any spans that haven't been exported yet are flushed
+    Otel.shutdownTracerProvider
+    -- Get a tracer so you can create spans
+    (\tracerProvider -> f $ Otel.makeTracer tracerProvider "whatcd-resolver" Otel.tracerOptions)
+
+setDefaultEnv :: String -> String -> IO ()
+setDefaultEnv envName defaultValue = do
+  Env.lookupEnv envName >>= \case
+    Just _env -> pure ()
+    Nothing -> Env.setEnv envName defaultValue
+
+withDb :: (TmpPg.DB -> IO a) -> IO (Either TmpPg.StartError a)
+withDb act = do
+  dataDir <- Xdg.getXdgDirectory Xdg.XdgData "whatcd-resolver"
+  let databaseDir = dataDir </> "database"
+  let socketDir = dataDir </> "database-socket"
+  Dir.createDirectoryIfMissing True socketDir
+  initDbConfig <-
+    Dir.doesDirectoryExist databaseDir >>= \case
+      True -> pure TmpPg.Zlich
+      False -> do
+        putStderrLn [fmt|Database does not exist yet, creating in "{databaseDir}"|]
+        Dir.createDirectoryIfMissing True databaseDir
+        pure TmpPg.DontCare
+  let cfg =
+        mempty
+          { TmpPg.dataDirectory = TmpPg.Permanent (databaseDir),
+            TmpPg.socketDirectory = TmpPg.Permanent socketDir,
+            TmpPg.port = pure $ Just 5431,
+            TmpPg.initDbConfig
+          }
+  TmpPg.withConfig cfg $ \db -> do
+    -- print [fmt|data dir: {db & TmpPg.toDataDirectory}|]
+    -- print [fmt|conn string: {db & TmpPg.toConnectionString}|]
+    act db
diff --git a/users/Profpatsch/whatcd-resolver/whatcd-resolver.cabal b/users/Profpatsch/whatcd-resolver/whatcd-resolver.cabal
new file mode 100644
index 0000000000..a9bd04827b
--- /dev/null
+++ b/users/Profpatsch/whatcd-resolver/whatcd-resolver.cabal
@@ -0,0 +1,121 @@
+cabal-version:      3.0
+name:               whatcd-resolver
+version:            0.1.0.0
+author:             Profpatsch
+maintainer:         mail@profpatsch.de
+
+common common-options
+  ghc-options:
+      -Wall
+      -Wno-type-defaults
+      -Wunused-packages
+      -Wredundant-constraints
+      -fwarn-missing-deriving-strategies
+
+  -- See https://downloads.haskell.org/ghc/latest/docs/users_guide/exts.html
+  -- for a description of all these extensions
+  default-extensions:
+      -- Infer Applicative instead of Monad where possible
+    ApplicativeDo
+
+    -- Allow literal strings to be Text
+    OverloadedStrings
+
+    -- Syntactic sugar improvements
+    LambdaCase
+    MultiWayIf
+
+    -- Makes the (deprecated) usage of * instead of Data.Kind.Type an error
+    NoStarIsType
+
+    -- Convenient and crucial to deal with ambiguous field names, commonly
+    -- known as RecordDotSyntax
+    OverloadedRecordDot
+
+    -- does not export record fields as functions, use OverloadedRecordDot to access instead
+    NoFieldSelectors
+
+    -- Allow the same record field name to be declared twice per module.
+    -- This works, because we use `OverloadedRecordDot` everywhere (enforced by `NoFieldSelectors`).
+    DuplicateRecordFields
+
+    -- Record punning
+    RecordWildCards
+
+    -- Improved Deriving
+    DerivingStrategies
+    DerivingVia
+
+    -- Type-level strings
+    DataKinds
+
+    -- to enable the `type` keyword in import lists (ormolu uses this automatically)
+    ExplicitNamespaces
+
+    -- allows defining pattern synonyms, but also the `import Foo (pattern FooPattern)` import syntax
+    PatternSynonyms
+
+  default-language: GHC2021
+
+library
+    import: common-options
+
+    hs-source-dirs: src
+
+    exposed-modules:
+       WhatcdResolver
+       AppT
+       JsonLd
+       Optional
+       Http
+       Html
+       Transmission
+       Redacted
+
+    build-depends:
+        base >=4.15 && <5,
+        text,
+        my-prelude,
+        my-webstuff,
+        pa-prelude,
+        pa-error-tree,
+        pa-label,
+        pa-json,
+        pa-field-parser,
+        pa-run-command,
+        aeson-better-errors,
+        aeson,
+        blaze-html,
+        bytestring,
+        case-insensitive,
+        containers,
+        unordered-containers,
+        directory,
+        exceptions,
+        filepath,
+        hs-opentelemetry-sdk,
+        hs-opentelemetry-api,
+        http-conduit,
+        http-types,
+        http-client,
+        ihp-hsx,
+        monad-logger,
+        mtl,
+        network-uri,
+        resource-pool,
+        postgresql-simple,
+        punycode,
+        tmp-postgres,
+        unliftio,
+        wai-extra,
+        wai,
+        warp,
+
+executable whatcd-resolver
+    import: common-options
+
+    main-is: Main.hs
+
+    build-depends:
+        base >=4.15 && <5,
+        whatcd-resolver
diff --git a/users/Profpatsch/writers/default.nix b/users/Profpatsch/writers/default.nix
index 3151a9d3bd..9fb69231a1 100644
--- a/users/Profpatsch/writers/default.nix
+++ b/users/Profpatsch/writers/default.nix
@@ -1,7 +1,7 @@
 { depot, pkgs, lib, ... }:
 let
-  bins = depot.nix.getBins pkgs.s6-portable-utils ["s6-mkdir" "s6-cat" "s6-ln" "s6-ls" "s6-touch" ]
-      // depot.nix.getBins pkgs.coreutils ["printf" ];
+  bins = depot.nix.getBins pkgs.s6-portable-utils [ "s6-mkdir" "s6-cat" "s6-ln" "s6-ls" "s6-touch" ]
+    // depot.nix.getBins pkgs.coreutils [ "printf" ];
 
   inherit (depot.nix.yants) defun struct restrict attrs list string drv any;
 
@@ -11,66 +11,110 @@ let
     restrict
       "flake error"
       (s: lib.any (prefix: (builtins.substring 0 1 s) == prefix)
-          [ "E" "W" ])
+        [ "E" "W" ])
       string;
   Libraries = defun [ (attrs any) (list drv) ];
 
-  python3 = {
-    name,
-    libraries ? (_: []),
-    flakeIgnore ? []
-  }: pkgs.writers.writePython3 name {
-    libraries = Libraries libraries pkgs.python3Packages;
-    flakeIgnore =
-      let ignoreTheseErrors = [
-        # whitespace after {
-        "E201"
-        # whitespace before }
-        "E202"
-        # fuck 4-space indentation
-        "E121" "E111"
-        # who cares about blank lines โ€ฆ
-        # โ€ฆ at end of files
-        "W391"
-        # โ€ฆ between functions
-        "E302" "E305"
-      ];
-      in list FlakeError (ignoreTheseErrors ++ flakeIgnore);
-  };
+  pythonPackages = pkgs.python310Packages;
+  buildPythonPackages = pkgs.buildPackages.python310Packages;
+  python = pythonPackages.python;
+
+  python3 =
+    { name
+    , libraries ? (_: [ ])
+    , flakeIgnore ? [ ]
+    }:
+    let
+    in
+    pkgs.writers.makePythonWriter python pythonPackages buildPythonPackages name {
+      libraries = Libraries libraries pythonPackages;
+      flakeIgnore =
+        let
+          ignoreTheseErrors = [
+            # whitespace after {
+            "E201"
+            # whitespace before }
+            "E202"
+            # fuck 4-space indentation
+            "E121"
+            "E111"
+            # who cares about blank lines โ€ฆ
+            # โ€ฆ at end of files
+            "W391"
+            # โ€ฆ between functions
+            "E302"
+            "E305"
+            # โ€ฆ if thereโ€™s too many of them
+            "E303"
+            # or lines that are too long
+            "E501"
+          ];
+        in
+        list FlakeError (ignoreTheseErrors ++ flakeIgnore);
+    };
 
   # TODO: add the same flake check as the pyhon3 writer
-  python3Lib = { name, libraries ? (_: []) }: moduleString:
-    let srcTree = depot.nix.runExecline.local name { stdin = moduleString; } [
-      "importas" "out" "out"
-      "if" [ bins.s6-mkdir "-p" "\${out}/${name}" ]
-      "if" [
-        "redirfd" "-w" "1" "\${out}/setup.py"
-        bins.printf ''
-          from distutils.core import setup
+  python3Lib = { name, libraries ? (_: [ ]) }: moduleString:
+    let
+      srcTree = depot.nix.runExecline.local name { stdin = moduleString; } [
+        "importas"
+        "out"
+        "out"
+        "if"
+        [ bins.s6-mkdir "-p" "\${out}/${name}" ]
+        "if"
+        [
+          "redirfd"
+          "-w"
+          "1"
+          "\${out}/setup.py"
+          bins.printf
+          ''
+            from distutils.core import setup
 
-          setup(
-            name='%s',
-            packages=['%s']
-          )
-        '' name name
-      ]
-      "if" [
-        # redirect stdin to the init py
-        "redirfd" "-w" "1" "\${out}/${name}/__init__.py"
-        bins.s6-cat
-      ]
-    ];
-    in pkgs.python3Packages.buildPythonPackage {
+            setup(
+              name='%s',
+              packages=['%s']
+            )
+          ''
+          name
+          name
+        ]
+        "if"
+        [
+          # redirect stdin to the init py
+          "redirfd"
+          "-w"
+          "1"
+          "\${out}/${name}/__init__.py"
+          bins.s6-cat
+        ]
+      ];
+    in
+    pythonPackages.buildPythonPackage {
       inherit name;
       src = srcTree;
-      propagatedBuildInputs = libraries pkgs.python3Packages;
+      propagatedBuildInputs = libraries pythonPackages;
       doCheck = false;
     };
 
 
-in {
+  ghcBins = libraries: depot.nix.getBins (pkgs.ghc.withPackages (_: libraries)) [ "runghc" ];
+
+  writeHaskellInteractive = name: { libraries, ghcArgs ? [ ] }: path:
+    depot.nix.writeExecline name { } ([
+      (ghcBins libraries).runghc
+      "--"
+    ] ++ ghcArgs ++ [
+      "--"
+      path
+    ]);
+
+in
+{
   inherit
     python3
     python3Lib
+    writeHaskellInteractive
     ;
 }
diff --git a/users/Profpatsch/writers/tests/default.nix b/users/Profpatsch/writers/tests/default.nix
index c4769a28c6..879aae82f7 100644
--- a/users/Profpatsch/writers/tests/default.nix
+++ b/users/Profpatsch/writers/tests/default.nix
@@ -10,38 +10,46 @@ let
     coreutils
     ;
 
-  run = drv: depot.nix.runExecline.local "run-${drv.name}" {} [
-    "if" [ drv ]
-    "importas" "out" "out"
-    "${coreutils}/bin/touch" "$out"
+  run = drv: depot.nix.runExecline.local "run-${drv.name}" { } [
+    "if"
+    [ drv ]
+    "importas"
+    "out"
+    "out"
+    "${coreutils}/bin/touch"
+    "$out"
   ];
 
-  pythonTransitiveLib = python3Lib {
-    name = "transitive";
-  } ''
+  pythonTransitiveLib = python3Lib
+    {
+      name = "transitive";
+    } ''
     def transitive(s):
       return s + " 1 2 3"
   '';
 
-  pythonTestLib = python3Lib {
-    name = "test_lib";
-    libraries = _: [ pythonTransitiveLib ];
-  } ''
+  pythonTestLib = python3Lib
+    {
+      name = "test_lib";
+      libraries = _: [ pythonTransitiveLib ];
+    } ''
     import transitive
     def test():
       return transitive.transitive("test")
   '';
 
-  pythonWithLib = run (python3 {
-    name = "python-with-lib";
-    libraries = _: [ pythonTestLib ];
-  } ''
+  pythonWithLib = run (python3
+    {
+      name = "python-with-lib";
+      libraries = _: [ pythonTestLib ];
+    } ''
     import test_lib
 
-    assert(test_lib.test() == "test 1 2 3")
+    assert test_lib.test() == "test 1 2 3"
   '');
 
-in depot.nix.utils.drvTargets {
+in
+depot.nix.readTree.drvTargets {
   inherit
     pythonWithLib
     ;
diff --git a/users/Profpatsch/ytextr/README.md b/users/Profpatsch/ytextr/README.md
new file mode 100644
index 0000000000..f1e40d8e68
--- /dev/null
+++ b/users/Profpatsch/ytextr/README.md
@@ -0,0 +1,5 @@
+# ytextr
+
+Wrapper around `yt-dlp` for downloading videos in good default quality with good default settings.
+
+Will always download the most up-to-date `yt-dlp` first, because the software usually stops working after a few weeks and needs to be updated, so just using `<nixpkgs>` often fails.
diff --git a/users/Profpatsch/ytextr/create-symlink-farm.nix b/users/Profpatsch/ytextr/create-symlink-farm.nix
new file mode 100644
index 0000000000..7b3a45b916
--- /dev/null
+++ b/users/Profpatsch/ytextr/create-symlink-farm.nix
@@ -0,0 +1,19 @@
+{
+  # list of package attribute names to get at run time
+  packageNamesAtRuntimeJsonPath
+,
+}:
+let
+  pkgs = import <nixpkgs> { };
+
+  getPkg = pkgName: pkgs.${pkgName};
+
+  packageNamesAtRuntime = builtins.fromJSON (builtins.readFile packageNamesAtRuntimeJsonPath);
+
+  runtime = map getPkg packageNamesAtRuntime;
+
+in
+pkgs.symlinkJoin {
+  name = "symlink-farm";
+  paths = runtime;
+}
diff --git a/users/Profpatsch/ytextr/default.nix b/users/Profpatsch/ytextr/default.nix
new file mode 100644
index 0000000000..3f3f073113
--- /dev/null
+++ b/users/Profpatsch/ytextr/default.nix
@@ -0,0 +1,82 @@
+{ depot, pkgs, lib, ... }:
+
+# ytextr is a wrapper arount yt-dlp (previously youtube-dl)
+# that extracts a single video according to my preferred settings.
+#
+# It will be sandboxed to the current directory, since I donโ€™t particularly
+# trust the massive codebase of that tool (with hundreds of contributors).
+#
+# Since the rules for downloading videos is usually against the wishes
+# of proprietary vendors, and a video is many megabytes anyway,
+# it will be fetched from the most recent nixpkgs unstable channel before running.
+
+let
+  bins = depot.nix.getBins pkgs.nix [ "nix-build" ]
+    // depot.nix.getBins pkgs.bubblewrap [ "bwrap" ];
+
+  # Run a command, with the given packages in scope, and `packageNamesAtRuntime` being fetched at the start in the given nix `channel`.
+  nix-run-with-channel =
+    {
+      # The channel to get `packageNamesAtRuntime` from
+      channel
+    , # executable to run with `packageNamesAtRuntime` in PATH
+      # and the argv
+      executable
+    , # A list of nixpkgs package attribute names that should be put into PATH when running `command`.
+      packageNamesAtRuntime
+    ,
+    }: depot.nix.writeExecline "nix-run-with-channel-${channel}" { } [
+      # TODO: prevent race condition by writing a temporary gc root
+      "backtick"
+      "-iE"
+      "storepath"
+      [
+        bins.nix-build
+        "-I"
+        "nixpkgs=channel:${channel}"
+        "--arg"
+        "packageNamesAtRuntimeJsonPath"
+        (pkgs.writeText "packageNamesAtRuntime.json" (builtins.toJSON packageNamesAtRuntime))
+        ./create-symlink-farm.nix
+      ]
+      "importas"
+      "-ui"
+      "PATH"
+      "PATH"
+      "export"
+      "PATH"
+      "\${storepath}/bin:\${PATH}"
+      executable
+      "$@"
+    ];
+
+in
+nix-run-with-channel {
+  channel = "nixos-unstable";
+  packageNamesAtRuntime = [ "yt-dlp" ];
+  executable = depot.nix.writeExecline "ytextr" { } [
+    "getcwd"
+    "-E"
+    "cwd"
+    bins.bwrap
+    "--ro-bind"
+    "/nix/store"
+    "/nix/store"
+    "--ro-bind"
+    "/etc"
+    "/etc"
+    "--bind"
+    "$cwd"
+    "$cwd"
+    "yt-dlp"
+    "--no-playlist"
+    "--write-sub"
+    "--all-subs"
+    "--embed-subs"
+    "--merge-output-format"
+    "mkv"
+    "-f"
+    "bestvideo[height<=?1080]+bestaudio/best"
+    "$@"
+  ];
+}
diff --git a/users/aaqaishtyaq/OWNERS b/users/aaqaishtyaq/OWNERS
new file mode 100644
index 0000000000..99c4a74244
--- /dev/null
+++ b/users/aaqaishtyaq/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+aaqaishtyaq
diff --git a/users/aspen/OWNERS b/users/aspen/OWNERS
new file mode 100644
index 0000000000..3dff20d574
--- /dev/null
+++ b/users/aspen/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+aspen
diff --git a/users/aspen/achilles/.envrc b/users/aspen/achilles/.envrc
new file mode 100644
index 0000000000..b80e28b4b8
--- /dev/null
+++ b/users/aspen/achilles/.envrc
@@ -0,0 +1,2 @@
+source_up
+eval "$(lorri direnv)"
diff --git a/users/grfn/achilles/.gitignore b/users/aspen/achilles/.gitignore
index ea8c4bf7f3..ea8c4bf7f3 100644
--- a/users/grfn/achilles/.gitignore
+++ b/users/aspen/achilles/.gitignore
diff --git a/users/grfn/achilles/Cargo.lock b/users/aspen/achilles/Cargo.lock
index 30e5e021f1..3c767db1e4 100644
--- a/users/grfn/achilles/Cargo.lock
+++ b/users/aspen/achilles/Cargo.lock
@@ -1,5 +1,7 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
+version = 3
+
 [[package]]
 name = "achilles"
 version = "0.1.0"
@@ -43,9 +45,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.38"
+version = "1.0.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
+checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
 
 [[package]]
 name = "arrayvec"
@@ -66,15 +68,15 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "bimap"
-version = "0.6.0"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f92b72b8f03128773278bf74418b9205f3d2a12c39a61f92395f47af390c32bf"
+checksum = "bc0455254eb5c6964c4545d8bac815e1a1be4f3afe0ae695ea539c12d728d44b"
 
 [[package]]
 name = "bit-set"
@@ -93,15 +95,15 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
 
 [[package]]
 name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitvec"
-version = "0.19.5"
+version = "0.19.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
+checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
 dependencies = [
  "funty",
  "radium",
@@ -111,15 +113,15 @@ dependencies = [
 
 [[package]]
 name = "byteorder"
-version = "1.4.2"
+version = "1.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "cc"
-version = "1.0.67"
+version = "1.0.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
 
 [[package]]
 name = "cfg-if"
@@ -129,37 +131,35 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "clap"
-version = "3.0.0-beta.2"
+version = "3.1.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142"
+checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"
 dependencies = [
  "atty",
  "bitflags",
- "clap_derive",
+ "clap_lex",
  "indexmap",
- "lazy_static",
- "os_str_bytes",
  "strsim",
  "termcolor",
  "textwrap",
- "unicode-width",
- "vec_map",
 ]
 
 [[package]]
-name = "clap_derive"
-version = "3.0.0-beta.2"
+name = "clap_lex"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1"
+checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"
 dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
+ "os_str_bytes",
 ]
 
 [[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
 name = "crate-root"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -167,9 +167,9 @@ checksum = "59c6fe4622b269032d2c5140a592d67a9c409031d286174fcde172fbed86f0d3"
 
 [[package]]
 name = "ctor"
-version = "0.1.19"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
+checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
 dependencies = [
  "quote",
  "syn",
@@ -177,12 +177,14 @@ dependencies = [
 
 [[package]]
 name = "derive_more"
-version = "0.99.11"
+version = "0.99.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
 dependencies = [
+ "convert_case",
  "proc-macro2",
  "quote",
+ "rustc_version",
  "syn",
 ]
 
@@ -199,6 +201,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 
 [[package]]
+name = "fastrand"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
+dependencies = [
+ "instant",
+]
+
+[[package]]
 name = "fnv"
 version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -212,9 +223,9 @@ checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
 
 [[package]]
 name = "getrandom"
-version = "0.2.2"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
+checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
 dependencies = [
  "cfg-if",
  "libc",
@@ -223,33 +234,24 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.9.1"
+version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
-
-[[package]]
-name = "heck"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
-dependencies = [
- "unicode-segmentation",
-]
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
 
 [[package]]
 name = "hermit-abi"
-version = "0.1.18"
+version = "0.1.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "indexmap"
-version = "1.6.2"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
+checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
 dependencies = [
  "autocfg",
  "hashbrown",
@@ -258,7 +260,7 @@ dependencies = [
 [[package]]
 name = "inkwell"
 version = "0.1.0"
-source = "git+https://github.com/TheDan64/inkwell?branch=master#a2db15b0bd1c06d71763585ae10d9ea4e775da0c"
+source = "git+https://github.com/TheDan64/inkwell?branch=master#6ab2b19e1b90be55fa4f9f056f29bd1ed557b990"
 dependencies = [
  "either",
  "inkwell_internals",
@@ -266,13 +268,12 @@ dependencies = [
  "llvm-sys",
  "once_cell",
  "parking_lot",
- "regex",
 ]
 
 [[package]]
 name = "inkwell_internals"
-version = "0.3.0"
-source = "git+https://github.com/TheDan64/inkwell?branch=master#a2db15b0bd1c06d71763585ae10d9ea4e775da0c"
+version = "0.5.0"
+source = "git+https://github.com/TheDan64/inkwell?branch=master#6ab2b19e1b90be55fa4f9f056f29bd1ed557b990"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -281,18 +282,18 @@ dependencies = [
 
 [[package]]
 name = "instant"
-version = "0.1.9"
+version = "0.1.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "itertools"
-version = "0.10.0"
+version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319"
+checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
 dependencies = [
  "either",
 ]
@@ -305,9 +306,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "lexical-core"
-version = "0.7.5"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374"
+checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
 dependencies = [
  "arrayvec",
  "bitflags",
@@ -318,29 +319,30 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.88"
+version = "0.2.125"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a"
+checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
 
 [[package]]
 name = "llvm-sys"
-version = "110.0.1"
+version = "110.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21ede189444b8c78907e5d36da5dabcf153170fcff9c1dba48afc4b33c7e19f0"
+checksum = "6b918288a585ac36703abefcbc5d4c43137b604ec0c2d39abefb55e25c7501dc"
 dependencies = [
  "cc",
  "lazy_static",
  "libc",
  "regex",
- "semver",
+ "semver 0.11.0",
 ]
 
 [[package]]
 name = "lock_api"
-version = "0.4.2"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
+checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
 dependencies = [
+ "autocfg",
  "scopeguard",
 ]
 
@@ -352,9 +354,9 @@ checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
 
 [[package]]
 name = "nom"
-version = "6.1.2"
+version = "6.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2"
+checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6"
 dependencies = [
  "bitvec",
  "funty",
@@ -373,57 +375,55 @@ dependencies = [
 
 [[package]]
 name = "num-traits"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.7.2"
+version = "1.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
 
 [[package]]
 name = "os_str_bytes"
-version = "2.4.0"
+version = "6.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85"
+checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
 
 [[package]]
 name = "output_vt100"
-version = "0.1.2"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
+checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
 dependencies = [
  "winapi",
 ]
 
 [[package]]
 name = "parking_lot"
-version = "0.11.1"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
+checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
 dependencies = [
- "instant",
  "lock_api",
  "parking_lot_core",
 ]
 
 [[package]]
 name = "parking_lot_core"
-version = "0.8.3"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
+checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
 dependencies = [
  "cfg-if",
- "instant",
  "libc",
  "redox_syscall",
  "smallvec",
- "winapi",
+ "windows-sys",
 ]
 
 [[package]]
@@ -437,9 +437,9 @@ dependencies = [
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.10"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
 
 [[package]]
 name = "pratt"
@@ -449,9 +449,9 @@ checksum = "e31bbc12f7936a7b195790dd6d9b982b66c54f45ff6766decf25c44cac302dce"
 
 [[package]]
 name = "pretty_assertions"
-version = "0.7.1"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f297542c27a7df8d45de2b0e620308ab883ad232d06c14b76ac3e144bda50184"
+checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b"
 dependencies = [
  "ansi_term",
  "ctor",
@@ -460,34 +460,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "proc-macro-error"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
-dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn",
- "version_check",
-]
-
-[[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
-
-[[package]]
 name = "proc-macro2"
-version = "1.0.24"
+version = "1.0.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
+checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
 dependencies = [
  "unicode-xid",
 ]
@@ -503,7 +479,7 @@ dependencies = [
  "byteorder",
  "lazy_static",
  "num-traits",
- "quick-error 2.0.0",
+ "quick-error 2.0.1",
  "rand",
  "rand_chacha",
  "rand_xorshift",
@@ -520,15 +496,15 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 
 [[package]]
 name = "quick-error"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
 
 [[package]]
 name = "quote"
-version = "1.0.9"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
 dependencies = [
  "proc-macro2",
 ]
@@ -541,21 +517,20 @@ checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
 
 [[package]]
 name = "rand"
-version = "0.8.3"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
  "rand_chacha",
  "rand_core",
- "rand_hc",
 ]
 
 [[package]]
 name = "rand_chacha"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
  "rand_core",
@@ -563,23 +538,14 @@ dependencies = [
 
 [[package]]
 name = "rand_core"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
 dependencies = [
  "getrandom",
 ]
 
 [[package]]
-name = "rand_hc"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
 name = "rand_xorshift"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -590,30 +556,29 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.5"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
+checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.4.3"
+version = "1.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
+checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
 dependencies = [
  "aho-corasick",
  "memchr",
  "regex-syntax",
- "thread_local",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.22"
+version = "0.6.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
 
 [[package]]
 name = "remove_dir_all"
@@ -625,6 +590,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver 1.0.9",
+]
+
+[[package]]
 name = "rusty-fork"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -638,9 +612,9 @@ dependencies = [
 
 [[package]]
 name = "ryu"
-version = "1.0.5"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
 
 [[package]]
 name = "scopeguard"
@@ -658,6 +632,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "semver"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd"
+
+[[package]]
 name = "semver-parser"
 version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -668,9 +648,9 @@ dependencies = [
 
 [[package]]
 name = "smallvec"
-version = "1.6.1"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
+checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
 
 [[package]]
 name = "static_assertions"
@@ -685,10 +665,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
+name = "structmeta"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bd9c2155aa89fb2c2cb87d99a610c689e7c47099b3e9f1c8a8f53faf4e3d2e3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "structmeta-derive",
+ "syn",
+]
+
+[[package]]
+name = "structmeta-derive"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bafede0d0a2f21910f36d47b1558caae3076ed80f6f3ad0fc85a91e6ba7e5938"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "syn"
-version = "1.0.61"
+version = "1.0.94"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed22b90a0e734a23a7610f4283ac9e5acfb96cbb30dfefa540d66f866f1c09c5"
+checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -703,13 +706,13 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
 
 [[package]]
 name = "tempfile"
-version = "3.2.0"
+version = "3.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
 dependencies = [
  "cfg-if",
+ "fastrand",
  "libc",
- "rand",
  "redox_syscall",
  "remove_dir_all",
  "winapi",
@@ -717,47 +720,45 @@ dependencies = [
 
 [[package]]
 name = "termcolor"
-version = "1.1.2"
+version = "1.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
 dependencies = [
  "winapi-util",
 ]
 
 [[package]]
 name = "test-strategy"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2328963c69243416e811c88066d18f670792b2e36e17fa57f4b1a124f85d18a8"
+checksum = "22c726321a7c108ca1de4ed2e6a362ead7193ecfbe0b326c5dff602b65a09e6a"
 dependencies = [
  "proc-macro2",
  "quote",
+ "structmeta",
  "syn",
 ]
 
 [[package]]
 name = "textwrap"
-version = "0.12.1"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
-dependencies = [
- "unicode-width",
-]
+checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
 
 [[package]]
 name = "thiserror"
-version = "1.0.24"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.24"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -765,49 +766,22 @@ dependencies = [
 ]
 
 [[package]]
-name = "thread_local"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
-dependencies = [
- "once_cell",
-]
-
-[[package]]
 name = "ucd-trie"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
 
 [[package]]
-name = "unicode-segmentation"
-version = "1.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
-
-[[package]]
 name = "unicode-xid"
-version = "0.2.1"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
-
-[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
 
 [[package]]
 name = "version_check"
-version = "0.9.2"
+version = "0.9.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "void"
@@ -862,6 +836,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
 name = "wyz"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/users/grfn/achilles/Cargo.toml b/users/aspen/achilles/Cargo.toml
index f091399a0d..f091399a0d 100644
--- a/users/grfn/achilles/Cargo.toml
+++ b/users/aspen/achilles/Cargo.toml
diff --git a/users/grfn/achilles/ach/.gitignore b/users/aspen/achilles/ach/.gitignore
index ac5296ebbd..ac5296ebbd 100644
--- a/users/grfn/achilles/ach/.gitignore
+++ b/users/aspen/achilles/ach/.gitignore
diff --git a/users/grfn/achilles/ach/Makefile b/users/aspen/achilles/ach/Makefile
index 3a8cd2865e..3a8cd2865e 100644
--- a/users/grfn/achilles/ach/Makefile
+++ b/users/aspen/achilles/ach/Makefile
diff --git a/users/grfn/achilles/ach/externs.ach b/users/aspen/achilles/ach/externs.ach
index faf8ce90e3..faf8ce90e3 100644
--- a/users/grfn/achilles/ach/externs.ach
+++ b/users/aspen/achilles/ach/externs.ach
diff --git a/users/grfn/achilles/ach/functions.ach b/users/aspen/achilles/ach/functions.ach
index dc6e7a1f3e..dc6e7a1f3e 100644
--- a/users/grfn/achilles/ach/functions.ach
+++ b/users/aspen/achilles/ach/functions.ach
diff --git a/users/grfn/achilles/ach/simple.ach b/users/aspen/achilles/ach/simple.ach
index 20f1677235..20f1677235 100644
--- a/users/grfn/achilles/ach/simple.ach
+++ b/users/aspen/achilles/ach/simple.ach
diff --git a/users/grfn/achilles/ach/units.ach b/users/aspen/achilles/ach/units.ach
index 70635d978c..70635d978c 100644
--- a/users/grfn/achilles/ach/units.ach
+++ b/users/aspen/achilles/ach/units.ach
diff --git a/users/grfn/achilles/default.nix b/users/aspen/achilles/default.nix
index 5245049d4a..714be60728 100644
--- a/users/grfn/achilles/default.nix
+++ b/users/aspen/achilles/default.nix
@@ -17,8 +17,11 @@ depot.third_party.naersk.buildPackage {
     ncurses
     libxml2
     libffi
-    pkgconfig
+    pkg-config
   ]);
 
   doCheck = true;
+
+  # Trouble linking against LLVM, maybe since rustc's llvmPackages got bumped?
+  meta.ci.skip = true;
 }
diff --git a/users/grfn/achilles/shell.nix b/users/aspen/achilles/shell.nix
index f32dce3ba3..1434cf8a32 100644
--- a/users/grfn/achilles/shell.nix
+++ b/users/aspen/achilles/shell.nix
@@ -1,4 +1,4 @@
-with (import ../../.. {}).third_party.nixpkgs;
+with (import ../../.. { }).third_party.nixpkgs;
 
 mkShell {
   buildInputs = [
diff --git a/users/grfn/achilles/src/ast/hir.rs b/users/aspen/achilles/src/ast/hir.rs
index cdfaef567d..cdfaef567d 100644
--- a/users/grfn/achilles/src/ast/hir.rs
+++ b/users/aspen/achilles/src/ast/hir.rs
diff --git a/users/grfn/achilles/src/ast/mod.rs b/users/aspen/achilles/src/ast/mod.rs
index 5438d29d2c..5438d29d2c 100644
--- a/users/grfn/achilles/src/ast/mod.rs
+++ b/users/aspen/achilles/src/ast/mod.rs
diff --git a/users/grfn/achilles/src/codegen/llvm.rs b/users/aspen/achilles/src/codegen/llvm.rs
index 9a71ac954e..9a71ac954e 100644
--- a/users/grfn/achilles/src/codegen/llvm.rs
+++ b/users/aspen/achilles/src/codegen/llvm.rs
diff --git a/users/grfn/achilles/src/codegen/mod.rs b/users/aspen/achilles/src/codegen/mod.rs
index 8ef057dba0..8ef057dba0 100644
--- a/users/grfn/achilles/src/codegen/mod.rs
+++ b/users/aspen/achilles/src/codegen/mod.rs
diff --git a/users/grfn/achilles/src/commands/check.rs b/users/aspen/achilles/src/commands/check.rs
index 0bea482c14..0bea482c14 100644
--- a/users/grfn/achilles/src/commands/check.rs
+++ b/users/aspen/achilles/src/commands/check.rs
diff --git a/users/grfn/achilles/src/commands/compile.rs b/users/aspen/achilles/src/commands/compile.rs
index be8767575a..be8767575a 100644
--- a/users/grfn/achilles/src/commands/compile.rs
+++ b/users/aspen/achilles/src/commands/compile.rs
diff --git a/users/grfn/achilles/src/commands/eval.rs b/users/aspen/achilles/src/commands/eval.rs
index 61a712c08a..efd7399ed1 100644
--- a/users/grfn/achilles/src/commands/eval.rs
+++ b/users/aspen/achilles/src/commands/eval.rs
@@ -1,10 +1,6 @@
 use clap::Clap;
 
-use crate::codegen;
-use crate::interpreter;
-use crate::parser;
-use crate::tc;
-use crate::Result;
+use crate::{codegen, interpreter, parser, tc, Result};
 
 /// Evaluate an expression and print its result
 #[derive(Clap)]
diff --git a/users/grfn/achilles/src/commands/mod.rs b/users/aspen/achilles/src/commands/mod.rs
index fd0a822708..fd0a822708 100644
--- a/users/grfn/achilles/src/commands/mod.rs
+++ b/users/aspen/achilles/src/commands/mod.rs
diff --git a/users/grfn/achilles/src/common/env.rs b/users/aspen/achilles/src/common/env.rs
index 59a5e46c46..59a5e46c46 100644
--- a/users/grfn/achilles/src/common/env.rs
+++ b/users/aspen/achilles/src/common/env.rs
diff --git a/users/grfn/achilles/src/common/error.rs b/users/aspen/achilles/src/common/error.rs
index 51575a895e..51575a895e 100644
--- a/users/grfn/achilles/src/common/error.rs
+++ b/users/aspen/achilles/src/common/error.rs
diff --git a/users/grfn/achilles/src/common/mod.rs b/users/aspen/achilles/src/common/mod.rs
index 8368a6dd18..8368a6dd18 100644
--- a/users/grfn/achilles/src/common/mod.rs
+++ b/users/aspen/achilles/src/common/mod.rs
diff --git a/users/grfn/achilles/src/common/namer.rs b/users/aspen/achilles/src/common/namer.rs
index 016e9f6ed9..016e9f6ed9 100644
--- a/users/grfn/achilles/src/common/namer.rs
+++ b/users/aspen/achilles/src/common/namer.rs
diff --git a/users/grfn/achilles/src/compiler.rs b/users/aspen/achilles/src/compiler.rs
index 45b215473d..45b215473d 100644
--- a/users/grfn/achilles/src/compiler.rs
+++ b/users/aspen/achilles/src/compiler.rs
diff --git a/users/grfn/achilles/src/interpreter/error.rs b/users/aspen/achilles/src/interpreter/error.rs
index 268d6f479a..268d6f479a 100644
--- a/users/grfn/achilles/src/interpreter/error.rs
+++ b/users/aspen/achilles/src/interpreter/error.rs
diff --git a/users/grfn/achilles/src/interpreter/mod.rs b/users/aspen/achilles/src/interpreter/mod.rs
index 70df7a0724..70df7a0724 100644
--- a/users/grfn/achilles/src/interpreter/mod.rs
+++ b/users/aspen/achilles/src/interpreter/mod.rs
diff --git a/users/grfn/achilles/src/interpreter/value.rs b/users/aspen/achilles/src/interpreter/value.rs
index 272d1167a3..272d1167a3 100644
--- a/users/grfn/achilles/src/interpreter/value.rs
+++ b/users/aspen/achilles/src/interpreter/value.rs
diff --git a/users/grfn/achilles/src/main.rs b/users/aspen/achilles/src/main.rs
index 5ae1b59b3a..5ae1b59b3a 100644
--- a/users/grfn/achilles/src/main.rs
+++ b/users/aspen/achilles/src/main.rs
diff --git a/users/grfn/achilles/src/parser/expr.rs b/users/aspen/achilles/src/parser/expr.rs
index f596b18970..b18ce4a0dc 100644
--- a/users/grfn/achilles/src/parser/expr.rs
+++ b/users/aspen/achilles/src/parser/expr.rs
@@ -1,9 +1,8 @@
 use std::borrow::Cow;
 
-use nom::alt;
 use nom::character::complete::{digit1, multispace0, multispace1};
 use nom::{
-    call, char, complete, delimited, do_parse, flat_map, many0, map, named, opt, parse_to,
+    alt, call, char, complete, delimited, do_parse, flat_map, many0, map, named, opt, parse_to,
     preceded, separated_list0, separated_list1, tag, tuple,
 };
 use pratt::{Affix, Associativity, PrattParser, Precedence};
diff --git a/users/grfn/achilles/src/parser/macros.rs b/users/aspen/achilles/src/parser/macros.rs
index 406e5c0e69..406e5c0e69 100644
--- a/users/grfn/achilles/src/parser/macros.rs
+++ b/users/aspen/achilles/src/parser/macros.rs
diff --git a/users/grfn/achilles/src/parser/mod.rs b/users/aspen/achilles/src/parser/mod.rs
index e088cbca10..e088cbca10 100644
--- a/users/grfn/achilles/src/parser/mod.rs
+++ b/users/aspen/achilles/src/parser/mod.rs
diff --git a/users/grfn/achilles/src/parser/type_.rs b/users/aspen/achilles/src/parser/type_.rs
index b80f0e0860..b80f0e0860 100644
--- a/users/grfn/achilles/src/parser/type_.rs
+++ b/users/aspen/achilles/src/parser/type_.rs
diff --git a/users/grfn/achilles/src/parser/util.rs b/users/aspen/achilles/src/parser/util.rs
index bb53fb7fff..bb53fb7fff 100644
--- a/users/grfn/achilles/src/parser/util.rs
+++ b/users/aspen/achilles/src/parser/util.rs
diff --git a/users/grfn/achilles/src/passes/hir/mod.rs b/users/aspen/achilles/src/passes/hir/mod.rs
index 872c449eb0..872c449eb0 100644
--- a/users/grfn/achilles/src/passes/hir/mod.rs
+++ b/users/aspen/achilles/src/passes/hir/mod.rs
diff --git a/users/grfn/achilles/src/passes/hir/monomorphize.rs b/users/aspen/achilles/src/passes/hir/monomorphize.rs
index 251a988f4f..251a988f4f 100644
--- a/users/grfn/achilles/src/passes/hir/monomorphize.rs
+++ b/users/aspen/achilles/src/passes/hir/monomorphize.rs
diff --git a/users/grfn/achilles/src/passes/hir/strip_positive_units.rs b/users/aspen/achilles/src/passes/hir/strip_positive_units.rs
index 85ee1cce48..85ee1cce48 100644
--- a/users/grfn/achilles/src/passes/hir/strip_positive_units.rs
+++ b/users/aspen/achilles/src/passes/hir/strip_positive_units.rs
diff --git a/users/grfn/achilles/src/passes/mod.rs b/users/aspen/achilles/src/passes/mod.rs
index 306869bef1..306869bef1 100644
--- a/users/grfn/achilles/src/passes/mod.rs
+++ b/users/aspen/achilles/src/passes/mod.rs
diff --git a/users/grfn/achilles/src/tc/mod.rs b/users/aspen/achilles/src/tc/mod.rs
index 5825bab1fb..5825bab1fb 100644
--- a/users/grfn/achilles/src/tc/mod.rs
+++ b/users/aspen/achilles/src/tc/mod.rs
diff --git a/users/grfn/achilles/tests/compile.rs b/users/aspen/achilles/tests/compile.rs
index 0f1086bfd8..0f1086bfd8 100644
--- a/users/grfn/achilles/tests/compile.rs
+++ b/users/aspen/achilles/tests/compile.rs
diff --git a/users/aspen/bbbg/.clj-kondo/config.edn b/users/aspen/bbbg/.clj-kondo/config.edn
new file mode 100644
index 0000000000..8faddb77ec
--- /dev/null
+++ b/users/aspen/bbbg/.clj-kondo/config.edn
@@ -0,0 +1 @@
+{:lint-as {garden.def/defstyles clojure.core/def}}
diff --git a/users/grfn/achilles/.envrc b/users/aspen/bbbg/.envrc
index 051d09d292..051d09d292 100644
--- a/users/grfn/achilles/.envrc
+++ b/users/aspen/bbbg/.envrc
diff --git a/users/aspen/bbbg/.gitignore b/users/aspen/bbbg/.gitignore
new file mode 100644
index 0000000000..99dbfc4436
--- /dev/null
+++ b/users/aspen/bbbg/.gitignore
@@ -0,0 +1,9 @@
+/target
+/classes
+*.jar
+*.class
+/.nrepl-port
+/.cpcache
+/.clojure
+/result
+/.clj-kondo/.cache
diff --git a/users/aspen/bbbg/Makefile b/users/aspen/bbbg/Makefile
new file mode 100644
index 0000000000..fc45477984
--- /dev/null
+++ b/users/aspen/bbbg/Makefile
@@ -0,0 +1,2 @@
+deps.nix: deps.edn
+	clj2nix ./deps.edn ./deps.nix '-A:uberjar' '-A:clj-test'
diff --git a/users/aspen/bbbg/README.md b/users/aspen/bbbg/README.md
new file mode 100644
index 0000000000..41f59319cb
--- /dev/null
+++ b/users/aspen/bbbg/README.md
@@ -0,0 +1,129 @@
+# Brooklyn-Based Board Gaming signup sheet
+
+This directory contains a small web application that acts as a signup
+sheet and attendee tracking system for [my local board gaming
+meetup](https://www.meetup.com/brooklyn-based-board-gaming/).
+
+## Development
+
+### Installing dependencies
+
+#### With Nix + Docker ("blessed way")
+
+Prerequisites:
+
+-   [Nix](https://nixos.org/)
+-   [lorri](https://github.com/nix-community/lorri)
+-   [Docker](https://www.docker.com/)
+
+From this directory in a full checkout of depot, run the following
+commands to install all development dependencies:
+
+``` shell-session
+$ pwd
+/path/to/depot/users/aspen/bbbg
+$ direnv allow
+$ lorri watch --once # Wait for a single nix shell build
+```
+
+Then, to run a docker container with the development database:
+
+``` shell-session
+$ pwd
+/path/to/depot/users/aspen/bbbg
+$ arion up -d
+```
+
+#### Choose-your-own-adventure
+
+Note that the **authoritative** source for dev dependencies is the `shell.nix`
+file in this directory - those may diverge from what's written here; if so
+follow those versions rather than these.
+
+-   Install the [clojure command-line
+    tools](https://clojure.org/guides/getting_started), with openjdk 11
+-   Install and run a postgresql 12 database, with:
+    -   A user with superuser priveleges, the username `bbbg` and the
+        password `password`
+    -   A database called `bbbg` owned by that user.
+-   Export the following environment variables in a context visible by
+    whatever method you use to run the application:
+    -   `PGHOST=localhost`
+    -   `PGUSER=bbbg`
+    -   `PGDATABASE=bbbg`
+    -   `PGPASSWORD=bbbg`
+
+### Running the application
+
+Before running the app, you'll need an oauth2 client-id and client secret for a
+Discord app. The application can either load those from a
+[pass](https://www.passwordstore.org/) password store, or read them from
+plaintext files in a directory. In either case, they should be accessible at the
+paths `bbbg/discord-client-id` and `bbbg/discord-client-secret` respectively.
+
+#### From the command line
+
+``` shell-session
+$ clj -A:dev
+Clojure 1.11.0-alpha3
+user=> (require 'bbbg.core)
+nil
+user=> ;; Optionally, if you're using a directory with plaintext files for the discord client ID and client secret:
+user=> (bbbg.util.dev-secrets/set-backend! [:dir "/path/to/that/directory"])
+user=> (bbbg.core/run-dev)
+##<SystemMap>
+user=> (bbbg.db/migrate! (:db bbbg.core/system))
+11:57:26.536 [main] INFO  migratus.core - Starting migrations {  }
+11:57:26.538 [main] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... {  }
+11:57:26.883 [main] INFO  com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection com.impossibl.postgres.jdbc.PGDirectConnection@3cae770e {  }
+11:57:26.884 [main] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. {  }
+11:57:26.923 [main] INFO  migratus.core - Ending migrations {  }
+nil
+```
+
+This will run a web server for the application listening at
+<http://localhost:8888>
+
+#### In Emacs, with [CIDER](https://docs.cider.mx/cider/index.html) + [direnv](https://github.com/wbolster/emacs-direnv)
+
+Open `//users/aspen/bbbg/src/bbbg/core.clj` in a buffer, then follow the
+instructions at the end of the file
+
+## Deployment
+
+### With nix+terraform
+
+Deployment configuration is located in the `tf.nix` file, which is
+currently tightly coupled to my own infrastructure and AWS account but
+could hypothetically be adjusted to be general-purpose.
+
+To deploy a new version of the application, after following "installing
+dependencies" above, run the following command in a context with ec2
+credentials available:
+
+``` shell-session
+$ terraform apply
+```
+
+The current deploy configuration includes:
+
+-   An ec2 instance running nixos, with a postgresql database and the
+    bbbg application running as a service, behind nginx with an
+    auto-renewing letsencrypt cert
+-   The DNS A record for `bbbg.gws.fyi` pointing at that ec2 instance,
+    in the cloudflare zone for `gws.fyi`
+
+### Otherwise
+
+ยฏ\\\_(ใƒ„)_/ยฏ
+
+You'll need:
+
+-   An uberjar for bbbg; the canonical way of building that is `nix-build
+    /path/to/depot -A users.aspen.bbbg.server-jar` but I\'m not sure how that
+    works outside of nix
+-   A postgresql database
+-   Environment variables telling the app how to connect to that
+    database. See `config.systemd.services.bbbg-server.environment` in
+    `module.nix` for which env vars are currently being exported by the
+    NixOS module that runs the production version of the app
diff --git a/users/aspen/bbbg/arion-compose.nix b/users/aspen/bbbg/arion-compose.nix
new file mode 100644
index 0000000000..c8a6dd156d
--- /dev/null
+++ b/users/aspen/bbbg/arion-compose.nix
@@ -0,0 +1,15 @@
+{ ... }:
+
+{
+  services = {
+    postgres.service = {
+      image = "postgres:12";
+      environment = {
+        POSTGRES_DB = "bbbg";
+        POSTGRES_USER = "bbbg";
+        POSTGRES_PASSWORD = "password";
+      };
+      ports = [ "5432:5432" ];
+    };
+  };
+}
diff --git a/users/aspen/bbbg/arion-pkgs.nix b/users/aspen/bbbg/arion-pkgs.nix
new file mode 100644
index 0000000000..c6d603be2a
--- /dev/null
+++ b/users/aspen/bbbg/arion-pkgs.nix
@@ -0,0 +1,2 @@
+let depot = import ../../.. { };
+in depot.third_party.nixpkgs
diff --git a/users/aspen/bbbg/default.nix b/users/aspen/bbbg/default.nix
new file mode 100644
index 0000000000..6afb68353c
--- /dev/null
+++ b/users/aspen/bbbg/default.nix
@@ -0,0 +1,82 @@
+args@{ depot, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  inherit (depot.third_party) gitignoreSource;
+
+  deps = import ./deps.nix {
+    inherit (pkgs) fetchMavenArtifact fetchgit lib;
+  };
+in
+rec {
+  meta.ci.targets = [
+    "db-util"
+    "server"
+    "tf"
+  ];
+
+  depsPaths = deps.makePaths { };
+
+  resources = builtins.filterSource (_: type: type != "symlink") ./resources;
+
+  classpath.dev = concatStringsSep ":" (
+    (map gitignoreSource [ ./src ./test ./env/dev ]) ++ [ resources ] ++ depsPaths
+  );
+
+  classpath.test = concatStringsSep ":" (
+    (map gitignoreSource [ ./src ./test ./env/test ]) ++ [ resources ] ++ depsPaths
+  );
+
+  classpath.prod = concatStringsSep ":" (
+    (map gitignoreSource [ ./src ./env/prod ]) ++ [ resources ] ++ depsPaths
+  );
+
+  testClojure = pkgs.writeShellScript "test-clojure" ''
+    export HOME=$(pwd)
+    ${pkgs.clojure}/bin/clojure -Scp ${depsPaths}
+  '';
+
+  mkJar = name: opts:
+    with pkgs;
+    assert (hasSuffix ".jar" name);
+    stdenv.mkDerivation rec {
+      inherit name;
+      dontUnpack = true;
+      buildPhase = ''
+        export HOME=$(pwd)
+        cp ${./pom.xml} pom.xml
+        cp ${./deps.edn} deps.edn
+        ${clojure}/bin/clojure \
+          -Scp ${classpath.prod} \
+          -A:uberjar \
+          ${name} \
+          -C ${opts}
+      '';
+
+      doCheck = true;
+
+      checkPhase = ''
+        echo "checking for existence of ${name}"
+        [ -f ${name} ]
+      '';
+
+      installPhase = ''
+        cp ${name} $out
+      '';
+    };
+
+  db-util-jar = mkJar "bbbg-db-util.jar" "-m bbbg.db";
+
+  db-util = pkgs.writeShellScriptBin "bbbg-db-util" ''
+    exec ${pkgs.openjdk17_headless}/bin/java -jar ${db-util-jar} "$@"
+  '';
+
+  server-jar = mkJar "bbbg-server.jar" "-m bbbg.core";
+
+  server = pkgs.writeShellScriptBin "bbbg-server" ''
+    exec ${pkgs.openjdk17_headless}/bin/java -jar ${server-jar} "$@"
+  '';
+
+  tf = import ./tf.nix args;
+}
diff --git a/users/aspen/bbbg/deps.edn b/users/aspen/bbbg/deps.edn
new file mode 100644
index 0000000000..39ce843c22
--- /dev/null
+++ b/users/aspen/bbbg/deps.edn
@@ -0,0 +1,70 @@
+{:deps
+ {org.clojure/clojure {:mvn/version "1.11.0-alpha3"}
+
+  ;; DB
+  com.github.seancorfield/next.jdbc {:mvn/version "1.2.761"}
+  com.impossibl.pgjdbc-ng/pgjdbc-ng {:mvn/version "0.8.9"}
+  com.zaxxer/HikariCP {:mvn/version "5.0.0"}
+  migratus/migratus {:mvn/version "1.3.5"}
+  com.github.seancorfield/honeysql {:mvn/version "2.2.840"}
+  nilenso/honeysql-postgres {:mvn/version "0.4.112"}
+
+  ;; HTTP
+  http-kit/http-kit {:mvn/version "2.5.3"}
+  ring/ring {:mvn/version "1.9.4"}
+  compojure/compojure {:mvn/version "1.6.2"}
+  javax.servlet/servlet-api {:mvn/version "2.5"}
+  ring-oauth2/ring-oauth2 {:mvn/version "0.2.0"}
+  clj-http/clj-http {:mvn/version "3.12.3"}
+  ring-logger/ring-logger {:mvn/version "1.0.1"}
+
+  ;; Web
+  hiccup/hiccup {:mvn/version "1.0.5"}
+  garden/garden {:mvn/version "1.3.10"}
+
+
+  ;; Logging + Observability
+  ch.qos.logback/logback-classic {:mvn/version "1.3.0-alpha12"}
+  org.slf4j/jul-to-slf4j {:mvn/version "2.0.0-alpha4"}
+  org.slf4j/jcl-over-slf4j {:mvn/version "2.0.0-alpha4"}
+  org.slf4j/log4j-over-slf4j {:mvn/version "2.0.0-alpha4"}
+  cambium/cambium.core {:mvn/version "1.1.1"}
+  cambium/cambium.codec-cheshire {:mvn/version "1.0.0"}
+  cambium/cambium.logback.core {:mvn/version "0.4.5"}
+  cambium/cambium.logback.json {:mvn/version "0.4.5"}
+  clj-commons/iapetos {:mvn/version "0.1.12"}
+
+  ;; Utilities
+  com.stuartsierra/component {:mvn/version "1.0.0"}
+  yogthos/config {:mvn/version "1.1.9"}
+  clojure.java-time/clojure.java-time {:mvn/version "0.3.3"}
+  cheshire/cheshire {:mvn/version "5.10.1"}
+  org.apache.commons/commons-lang3 {:mvn/version "3.12.0"}
+  org.clojure/data.csv {:mvn/version "1.0.0"}
+
+  ;; Spec
+  org.clojure/spec.alpha {:mvn/version "0.3.218"}
+  org.clojure/core.specs.alpha {:mvn/version "0.2.62"}
+  expound/expound {:mvn/version "0.8.10"}
+  org.clojure/test.check {:mvn/version "1.1.1"}}
+
+ :paths
+ ["src"
+  "test"
+  "resources"
+  "target/classes"]
+ :aliases
+ {:dev {:extra-paths ["env/dev"]
+        :jvm-opts ["-XX:-OmitStackTraceInFastThrow"]}
+  :clj-test {:extra-paths ["test" "env/test"]
+             :extra-deps {io.github.cognitect-labs/test-runner
+                          {:git/url "https://github.com/cognitect-labs/test-runner"
+                           :sha "cc75980b43011773162b485f46f939dc5fba91e4"}}
+             :main-opts ["-m" "cognitect.test-runner"
+                         "-d" "test"]}
+  :uberjar {:extra-deps {seancorfield/depstar {:mvn/version "1.0.94"}}
+            :extra-paths ["env/prod"]
+            :main-opts ["-m" "hf.depstar.uberjar"]}
+
+  :outdated {:extra-deps {com.github.liquidz/antq {:mvn/version "1.3.1"}}
+             :main-opts ["-m" "antq.core"]}}}
diff --git a/users/aspen/bbbg/deps.nix b/users/aspen/bbbg/deps.nix
new file mode 100644
index 0000000000..02f5ecb468
--- /dev/null
+++ b/users/aspen/bbbg/deps.nix
@@ -0,0 +1,1494 @@
+# generated by clj2nix-1.1.0-rc
+{ fetchMavenArtifact, fetchgit, lib }:
+
+let
+  repos = [
+    "https://repo1.maven.org/maven2/"
+    "https://repo.clojars.org/"
+  ];
+
+in
+rec {
+  makePaths = { extraClasspaths ? [ ] }:
+    if (builtins.typeOf extraClasspaths != "list")
+    then builtins.throw "extraClasspaths must be of type 'list'!"
+    else (lib.concatMap
+      (dep:
+        builtins.map
+          (path:
+            if builtins.isString path then
+              path
+            else if builtins.hasAttr "jar" path then
+              path.jar
+            else if builtins.hasAttr "outPath" path then
+              path.outPath
+            else
+              path
+          )
+          dep.paths)
+      packages) ++ extraClasspaths;
+  makeClasspaths = { extraClasspaths ? [ ] }:
+    if (builtins.typeOf extraClasspaths != "list")
+    then builtins.throw "extraClasspaths must be of type 'list'!"
+    else builtins.concatStringsSep ":" (makePaths { inherit extraClasspaths; });
+  packageSources = builtins.map (dep: dep.src) packages;
+  packages = [
+    rec {
+      name = "cambium.logback.json/cambium";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cambium.logback.json";
+        groupId = "cambium";
+        sha512 = "8e3f32bc1e11071ddc8700204333ba653585de7985c03d14c351950a7896975092e9deffd658bfec7b0b8b9cc72dc025d8e5179a185bd25da26e500218ec37a5";
+        version = "0.4.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clojure/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clojure";
+        groupId = "org.clojure";
+        sha512 = "a242514f623a17601b360886563c4a4fe09335e4e16522ac42bbcacda073ae77651cfed446daae7fe74061bb7dff5adc454769c0edc0ded350136c3c707e75b9";
+        version = "1.11.0-alpha3";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "joda-time/joda-time";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "joda-time";
+        groupId = "joda-time";
+        sha512 = "012fb9aa9b00b456f72a92374855a7f062f8617c026c436eee2cda67dffa2f8622201909c0f4f454bb346ff5a3ed6f60c236fafb19fa66f612d9861f27b38d3a";
+        version = "2.10";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-codec/commons-codec";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-codec";
+        groupId = "commons-codec";
+        sha512 = "da30a716770795fce390e4dd340a8b728f220c6572383ffef55bd5839655d5611fcc06128b2144f6cdcb36f53072a12ec80b04afee787665e7ad0b6e888a6787";
+        version = "1.15";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "HikariCP/com.zaxxer";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "HikariCP";
+        groupId = "com.zaxxer";
+        sha512 = "a41b6d8b1c4656e633459824f10320965976eeead01bd5cb24911040073181730e61feb797aef89d9e01c922e89cb58654f364df0a6b1bf62ab3e6f9cc367d77";
+        version = "5.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-devel/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-devel";
+        groupId = "ring";
+        sha512 = "79a1ec9f9d03aa4fa0426353970b13468ee65ce314b51ab7a2682212a196a9b5c985eacdee5dbc6ff2f1b536a4e06d0e85e9dd7cc9a49958735c9c4e6d427fd5";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient";
+        groupId = "io.prometheus";
+        sha512 = "60af1cefff04e7036467eae54f5930d5677e4ab066f8ed38a391b54df17733acfefac45e19ee53cef289347bddce5fc69a2766f4e580d21a22cfd9e2348e2723";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-lang3/org.apache.commons";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-lang3";
+        groupId = "org.apache.commons";
+        sha512 = "fbdbc0943cb3498b0148e86a39b773f97c8e6013740f72dbc727faeabea402073e2cc8c4d68198e5fc6b08a13b7700236292e99d4785f2c9989f2e5fac11fd81";
+        version = "3.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.logging/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.logging";
+        groupId = "org.clojure";
+        sha512 = "b7a9680f1156fc7c1574a4364ca550d47668ba727fc80110fdd00c159bedb45c5be82f09cdfb8e8e988e3381e2cf8881ea70651e38001e3eaa4ece31ad0bf0c5";
+        version = "1.2.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "core.specs.alpha/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "core.specs.alpha";
+        groupId = "org.clojure";
+        sha512 = "f521f95b362a47bb35f7c85528c34537f905fb3dd24f2284201e445635a0df701b35d8419d53c6507cc78d3717c1f83cda35ea4c82abd8943cd2ab3de3fcad70";
+        version = "0.2.62";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-common/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-common";
+        groupId = "io.netty";
+        sha512 = "7efc2f6774a3dbe8408fe182e19830b5b7a994a0d1b0eb50699df691c2450befa05ac205bbf341ad57bef3a04281ce435031e97e725c5c4edfc705a418828ce8";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-databind/com.fasterxml.jackson.core";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-databind";
+        groupId = "com.fasterxml.jackson.core";
+        sha512 = "9f771e78af669b1e1683d6c5903bbf4790aaa88b6b420c2018437da318c3fa4220cd7fa726f3e42a1b8075def1fdbd3744937c15f3bcedfca3050199247363e8";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "expound/expound";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "expound";
+        groupId = "expound";
+        sha512 = "ca0a57cfd215cff6be36d1f83461ec2d0559c0eae172c8a8bd6e1676d49933d3c30a71192889bd75d813581707d5eda0ec05de03326396bc0cedebf2d71811e5";
+        version = "0.8.10";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "spec.alpha/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "spec.alpha";
+        groupId = "org.clojure";
+        sha512 = "ddfe4fa84622abd8ac56e2aa565a56e6bdc0bf330f377ff3e269ddc241bb9dbcac332c13502dfd4c09c2c08fe24d8d2e8cf3d04a1bc819ca5657b4e41feaa7c2";
+        version = "0.3.218";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.cli/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.cli";
+        groupId = "org.clojure";
+        sha512 = "1d88aa03eb6a664bf2c0ce22c45e7296d54d716e29b11904115be80ea1661623cf3e81fc222d164047058239010eb678af92ffedc7c3006475cceb59f3b21265";
+        version = "1.0.206";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "compojure/compojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "compojure";
+        groupId = "compojure";
+        sha512 = "1f4ba1354bd95772963a4ef0e129dde59d16f4f9fac0f89f2505a1d5de3b4527e45073219c0478e0b3285da46793e7c145ec5a55a9dae2fca6b77dc8d67b4db6";
+        version = "1.6.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-fileupload/commons-fileupload";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-fileupload";
+        groupId = "commons-fileupload";
+        sha512 = "a8780b7dd7ab68f9e1df38e77a5207c45ff50ec53d8b1476570d069edc8f59e52fb1d0fc534d7e513ac5a01b385ba73c320794c82369a72bd6d817a3b3b21f39";
+        version = "1.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jetty-http/org.eclipse.jetty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jetty-http";
+        groupId = "org.eclipse.jetty";
+        sha512 = "60422ff3ef311f1d9d7340c2accdf611d40e738a39e9128967175ede4990439f4725995988849957742d488f749dd2e0740f74dc5bd9b3364e32fbaa66689308";
+        version = "9.4.42.v20210604";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jetty-util/org.eclipse.jetty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jetty-util";
+        groupId = "org.eclipse.jetty";
+        sha512 = "d69084e2cfe0c3af1dc7ee2745d563549a4068b6e8aed5cd2b9f31167168fb64d418c4134a6dfb811b627ec0051d7ff71e0a02e4e775d18a53543d0871c44730";
+        version = "9.4.42.v20210604";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "janino/org.codehaus.janino";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "janino";
+        groupId = "org.codehaus.janino";
+        sha512 = "6853d7d53d3629df43a3a17ff5c989f59ec14e9030be5f67426deb9d0797fa3996b0609d582c65f22a4f7680c941b39ab6d466c480b2fea4bf92218a9b89651d";
+        version = "3.1.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jcl-over-slf4j/org.slf4j";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jcl-over-slf4j";
+        groupId = "org.slf4j";
+        sha512 = "23662fe407fcdbcba8865a8cd3f8bb09d4eb178a2a6511a32e35b995722b345e73f5dc1dd85d2d0a5c707db05aa57e0b3d0b96b59e55403fc486343d5ca4c0d6";
+        version = "2.0.0-alpha4";
+
+      };
+      paths = [ src ];
+    }
+
+    (rec {
+      name = "io.github.cognitect-labs/test-runner";
+      src = fetchgit {
+        name = "test-runner";
+        url = "https://github.com/cognitect-labs/test-runner";
+        rev = "cc75980b43011773162b485f46f939dc5fba91e4";
+        sha256 = "1661ddmmqva1yiz9p09i5l32lfpi0a99h56022zgvz03nca2ksbg";
+      };
+      paths = map (path: src + path) [
+        "/src"
+      ];
+    })
+
+    rec {
+      name = "cambium.logback.core/cambium";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cambium.logback.core";
+        groupId = "cambium";
+        sha512 = "83ee9a583dd8a7b2e82e0981b4e51b005095a27257eb1b07165d9701645609060c466ae67fb9431f524a544d52b71fa00009b8acf05aadbeb549043515f9b382";
+        version = "0.4.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpasyncclient/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpasyncclient";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "0a80db5dbf772f02d02ba6c7c163e8da9517dd7195714b495acb845c429580c1fc926d3e71c115e75be8c145651dce2fdfa0dc380132f7809c14b3ad95492aee";
+        version = "4.1.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-jackson/ch.qos.logback.contrib";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-jackson";
+        groupId = "ch.qos.logback.contrib";
+        sha512 = "d9a3d4cb6cf4eda6fc18e2d374007d27c6ddba98e989a8d8a01b49859b280450113f685df6e16c5fbe0472bc9e26308bc7e8b7e0aedab9404cf0b492d7511685";
+        version = "0.1.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_tracer_otel/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_tracer_otel";
+        groupId = "io.prometheus";
+        sha512 = "bce192e6162cb3ada7dd6c2d10456e78bce71c170faa09bad2896272fa1bd4a036288d707f3d47747991d8946c74fe21c565713fb15c7052305eb753c94dd939";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-codec/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-codec";
+        groupId = "io.netty";
+        sha512 = "f6d9c4a5b508ca0d5f0e213473088f5d7b2e184e447dc092e69227109e28da9b8e68b2238ca6ab4e9915bacacf59cc0dce6ebcbbb05dad34a03b7976d9670c51";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-oauth2/ring-oauth2";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-oauth2";
+        groupId = "ring-oauth2";
+        sha512 = "3ed765b4bbb5749fcdcdb501b93ab656a413ade5af24c7aa34639718ed1fd0a5f325b05bd135540d56e55cbb456a2cb7852ba0e45bc5233e28229986eef75bb9";
+        version = "0.2.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.macro/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.macro";
+        groupId = "org.clojure";
+        sha512 = "65ce5e29379620ac458274c53cd9926e4b764fcaebb1a2b3bc8aef86bbe10c79e654b028bc4328905d2495a680fa90f5002cf5c47885f6449fad43a04a594b26";
+        version = "0.1.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-dataformat-cbor/com.fasterxml.jackson.dataformat";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-dataformat-cbor";
+        groupId = "com.fasterxml.jackson.dataformat";
+        sha512 = "ea5d049eac1b94666479c5e36de14d8fa4b7f24cb92f0f310d2ec2b4de66ef9023161060e67228ef2d7420a002ef861db12a29cad0864638c21612da49686f4f";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "depstar/seancorfield";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "depstar";
+        groupId = "seancorfield";
+        sha512 = "0f4458b39b8b1949755bc2fe64b239673a9efa3a0140998464bbbcab216ec847344c1b8920611f7c9ca07261850f3a08144ae221cc2c41813a080189e32f9c10";
+        version = "1.0.94";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-core/ch.qos.logback";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-core";
+        groupId = "ch.qos.logback";
+        sha512 = "fc554548f499e284007eeecf76bf4e1995effb6ac8a6262aa96118f623bf9085a9d5bec3741833dd3cae6a76b2ff78c6d0a1fe68bc01213207c93d8e2da345ca";
+        version = "1.3.0-alpha12";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "honeysql/honeysql";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "honeysql";
+        groupId = "honeysql";
+        sha512 = "74d1d93c968b33686848e3bf8934f3b5f002c2b69b1b55a3a3b172c952e9991324e6e95e3a0ce2fecf1de0d3a036f4dff7286df689f0733f253909464e0269f6";
+        version = "1.0.461";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-buffer/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-buffer";
+        groupId = "io.netty";
+        sha512 = "181b55d99d8d46bbf5f67f05bdccb0381af23a9fca3e6d935e6cde727b132c67133de1c3d81ed19b04c1a5b232be0de16ec1de7e81b532878bc69564237c15dc";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "slingshot/slingshot";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "slingshot";
+        groupId = "slingshot";
+        sha512 = "ff2b2a27b441d230261c7f3ec8c38aa551865e05ab6438a74bd12bfcbc5f6bdc88199d42aaf5932b47df84f3d2700c8f514b9f4e9b5da28d29da7ff6b09a7fb5";
+        version = "0.12.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpcore-nio/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpcore-nio";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "002af5f72b68a4ff1b1ff46b788013283d195e1d62ee1d7b102aa930b30f77f7e215a6d18edbea0fccd18fb1fa3a66cc4aef6070d72d6d1886f0044dfe0e16c7";
+        version = "4.4.10";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-jetty-adapter/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-jetty-adapter";
+        groupId = "ring";
+        sha512 = "93075903ad73a8b73cb77ee9f53ed33594f40a5dafe8129089adb4cfa333e37468764203c00244568f02abf0c0eee9f5d9a9f96c420919027cf2746a41ec38e3";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_tracer_common/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_tracer_common";
+        groupId = "io.prometheus";
+        sha512 = "6f717af63340efd84c5467ae752be7e66f586f0e8b57adb5b7a8ef99b223203ed829aad6797f6ef1811d6d861b00a621a1288c9271ec2ba77018d6d9eb9e7987";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "component/com.stuartsierra";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "component";
+        groupId = "com.stuartsierra";
+        sha512 = "108b02f51165ad07c2cf5232fbd954d052880c2456e6fb6db3342bda6851c76b73bf9145f03fb0df2b5782fe39f368b2868780c1e8e2dfa2ab2c68dd97f34ab7";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-handler/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-handler";
+        groupId = "io.netty";
+        sha512 = "48874727553dd7084f5c48d90de123704ae334837c3a103f598887bb21405dd62c57603b59300ac2fcdd936f0af99ed0730487fb9fb8917d236b8fe3f78f3c02";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "yuicompressor/com.yahoo.platform.yui";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "yuicompressor";
+        groupId = "com.yahoo.platform.yui";
+        sha512 = "ba2588bd50eaa3005b1919daad9f9c86a33351ceb9b7b5f0a9a498a548cc523e99f9345917a64303f8e23925feea226386d3eac01f640f788d1be4c7cf0315e0";
+        version = "2.4.8";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-io/commons-io";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-io";
+        groupId = "commons-io";
+        sha512 = "6af22dffaaecd1553147e788b5cf50368582318f396e456fe9ff33f5175836713a5d700e51720465c932c2b1987daa83027358005812d6a95d5755432de3a79d";
+        version = "2.10.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.namespace/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.namespace";
+        groupId = "org.clojure";
+        sha512 = "2cdb9c5d9bc4fd01dae182e9ad4b91eeaa2487003a977e7d8d5e66f562a9544b59f558710eccf421ea63cbbfa953ac8944fe9b9a76049fb82a47eb2bdcb3a4d7";
+        version = "1.1.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "honeysql/com.github.seancorfield";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "honeysql";
+        groupId = "com.github.seancorfield";
+        sha512 = "a0e5ebbf922aaf170c2d74ec0efc0df7e3bda92d0b8cc5f40ee4c8ddcb8c7e0e46556fac381513e0ac76b10f681c14c2d2569010c2f8eab4ff04f6373c2bf229";
+        version = "2.2.840";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-core/com.fasterxml.jackson.core";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-core";
+        groupId = "com.fasterxml.jackson.core";
+        sha512 = "428e0ebb16dd4c74ab0adf712058fd0dc0cd788f6e6f90c60c627da6577b345fac60a30694e111f1cd4e3e8bf79a1f1b820d30ada114984b26c28e299e326eaa";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clj-time/clj-time";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clj-time";
+        groupId = "clj-time";
+        sha512 = "cfeb46af59fd4112aa5a5d0087a39355f0fc19514b4c02bc6c3d9f81c9bda40491686207836e9a7943aebeb82a3b36f4e8b7407a8908c5ef151122644b278d75";
+        version = "0.15.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clj-http/clj-http";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clj-http";
+        groupId = "clj-http";
+        sha512 = "9884557d4f38068cb3234aec80acc0de8f9716645529693ffd9bd6db8221f5d1cf9e2d1b8bf7c7df4215d71372b02d83043ebf8fc27dc422552b32c9bdba1602";
+        version = "3.12.3";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jul-to-slf4j/org.slf4j";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jul-to-slf4j";
+        groupId = "org.slf4j";
+        sha512 = "350cfb889248d724b27dce697f635f12d9db463f107830b9518ce184dc4cc1ab3933eb5bdab08515e69766c3d5be24547dac289d6406c44eca90717230714b91";
+        version = "2.0.0-alpha4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "migratus/migratus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "migratus";
+        groupId = "migratus";
+        sha512 = "ee5ce8601930d063e0d9d90fc8e165b78fc1587bfd7e0fc9922735bc2f9fc27f8cf8bf10d49d6fd57b899ac4b250145bd653915ed770424416e026ba37d1b604";
+        version = "1.3.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpcore/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpcore";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "f16a652f4a7b87dbf7cb16f8590d54a3f719c4c7b2f8883ce59db2d73be4701b64f2ca8a2c45aca6a5dbeaddeedff0c280a03722f70c076e239b645faa54eff9";
+        version = "4.4.14";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpclient-cache/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpclient-cache";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "e150e8dc49c8c9972d8b324b56bb292b15e2f0e686f1292c4edac975615dfb16e5edb8ab325e614732a7d43a03061ca4fe93fe1e1f7487851a4d4d3af50a61f9";
+        version = "4.5.13";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "instaparse/instaparse";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "instaparse";
+        groupId = "instaparse";
+        sha512 = "ec2fcf4a09319a8fa9489b08fd9c9a5fe6e63155dde74d096f947fabc4f68d3d1bf68faf21e175e80eaee785f563a1903d30c550f93fb13a16a240609e3dfa2e";
+        version = "1.4.8";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "honeysql-postgres/nilenso";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "honeysql-postgres";
+        groupId = "nilenso";
+        sha512 = "d4accd3b8819cf715ecdb29496cf5a6a5ad3871fd579e55c7148d4e05774cb896c681b0c6f84df88aa9cd8e6ef9bfd65788ede9a49ba365ad0e32ee350091879";
+        version = "0.4.112";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clj-tuple/clj-tuple";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clj-tuple";
+        groupId = "clj-tuple";
+        sha512 = "dd626944d0aba679a21b164ed0c77ea84449359361496cba810f83b9fdeab751e5889963888098ce4bf8afa112dbda0a46ed60348a9c01ad36a2e255deb7ab6d";
+        version = "0.2.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-annotations/com.fasterxml.jackson.core";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-annotations";
+        groupId = "com.fasterxml.jackson.core";
+        sha512 = "6fdad6c5bb71a97331a662fe26265aacab6869f3307a710697d5c2f256fd48935764bfb0b3505a2cbb1605daf0b7350abdf84a1b1cf2bb1e91d9184565243c8e";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "hiccup/hiccup";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "hiccup";
+        groupId = "hiccup";
+        sha512 = "034f15be46c35029f41869c912f82cb2929fbbb0524ea64bd98dcdb9cf09875b28c75e926fa5fff53942b0f9e543e85a73a2d03c3f2112eecae30fcef8b148f4";
+        version = "1.0.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "riddley/riddley";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "riddley";
+        groupId = "riddley";
+        sha512 = "b478ecba9d1ab9d38c84a42354586fcece763000907b40c97bc43c0f16dc560b0860144efe410193cb3b7cb0149fbc1724fdd737cc3ba53de23618f5b30e6f9f";
+        version = "0.1.12";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "java.classpath/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "java.classpath";
+        groupId = "org.clojure";
+        sha512 = "90cd8edeaea02bd908d8cfb0cf5b1cf901aeb38ea3f4971c4b813d33210438aae6fff8e724a8272d2ea9441d373e7d936fa5870e309c1e9721299f662dbbdb9a";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_pushgateway/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_pushgateway";
+        groupId = "io.prometheus";
+        sha512 = "31c8878929f516ba7030cc9ec4ac4cbcb09955a9fdae23c6904bc481e40e70e1b3e05619c49b646119077ef6f57c430cc7944f6bafdbca24c9efa8145474fcf7";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ns-tracker/ns-tracker";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ns-tracker";
+        groupId = "ns-tracker";
+        sha512 = "cfb6c2c9f899b43d1284acdc572b34b977936c4df734b38137dfea045421b74d529509cde23695f1dc5ee06d046c2f6b61a2cd98058da1c7220c21dd0361964f";
+        version = "0.4.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clout/clout";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clout";
+        groupId = "clout";
+        sha512 = "99d6e1a8c5726ca4e5d12b280a39e6d1182d734922600f27d588d3d65fbc830c5e03f9e0421ff25c819deee4d1f389fd3906222716ace1eb17ce70ef9c5e8f4b";
+        version = "2.2.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-logging/commons-logging";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-logging";
+        groupId = "commons-logging";
+        sha512 = "ed00dbfabd9ae00efa26dd400983601d076fe36408b7d6520084b447e5d1fa527ce65bd6afdcb58506c3a808323d28e88f26cb99c6f5db9ff64f6525ecdfa557";
+        version = "1.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clojure.java-time/clojure.java-time";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clojure.java-time";
+        groupId = "clojure.java-time";
+        sha512 = "62d8a286ec3393594e7f84eba22dbb02c1305a80a18b2574058ae963d3f3e829ff960c8b66e89069e6c071a11f869203134c6c4cdec6f8e516c9b314796c8108";
+        version = "0.3.3";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "data.csv/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "data.csv";
+        groupId = "org.clojure";
+        sha512 = "b039775a859ed27eca8f8ae74ccb6afde3ad1fe2b3cbe542240c324d60fe1237e495eb1300ee9eb4ff4ef59f01faf7aec6ef1dd6a025ee4fe556c1d91acfcf1b";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_tracer_otel_agent/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_tracer_otel_agent";
+        groupId = "io.prometheus";
+        sha512 = "97694210d9a5b48a7cb9dda2a187432c4813edb3051edfa5832a0a471e0b2d5988dab92b70c292e78f59b169345deb5c1c706361fd726f3dc2480766dedfdcec";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "next.jdbc/com.github.seancorfield";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "next.jdbc";
+        groupId = "com.github.seancorfield";
+        sha512 = "0b4b01ba126bb8b1e2c14262db9fca75456b274d09535d9a7bb386699bf20dc9ac11590d210769e7429ca59ebfdfbb06916b3ff275cc817d74eac5bbabdab8f2";
+        version = "1.2.761";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "java.jdbc/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "java.jdbc";
+        groupId = "org.clojure";
+        sha512 = "6162b7774dca58b62a94bc5a04ba845e4c7065c9c589cc3bb802becfec0baf0989a338c1bf9a5db7c3128873702840d5f2451628f3aac977245975d65a683b7d";
+        version = "0.7.11";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-transport/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-transport";
+        groupId = "io.netty";
+        sha512 = "c11d690ffeaf3267b2166f73a43108fb89d588fcef3f6d3053bf4b6f6669483baa618fd97438010692a6fa28334372d5a31b7c0996961d4eabb60cbdc358a536";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "crypto-random/crypto-random";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "crypto-random";
+        groupId = "crypto-random";
+        sha512 = "3520df744f250dbe061d1a5d7a05b7143f3a67a4c3f9ad87b8044ee68a36a702a0bcb3a203e35d380899dd01c28e01988b0a7af914b942ccbe0c35c9bdb22e11";
+        version = "1.2.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-transport-native-unix-common/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-transport-native-unix-common";
+        groupId = "io.netty";
+        sha512 = "b63e5f8a44b7f37f3dba378bd06af64dd1d7be3f0b1a7d47ad139ff06e0212b4c7081275b1b5b12183aeb72eb5f9bf9ef03ed8c78bc302aeb4817dca7bd89f3a";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-codec/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-codec";
+        groupId = "ring";
+        sha512 = "38b9775a794831b8afd8d66991a75aa5910cd50952c9035866bf9cc01353810aedafbc3f35d8f9e56981ebf9e5c37c00b968759ed087d2855348b3f46d8d0487";
+        version = "1.1.3";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "spy/com.impossibl.pgjdbc-ng";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "spy";
+        groupId = "com.impossibl.pgjdbc-ng";
+        sha512 = "173615c39aa6015a732e329217b40e3ea1c304c9c168d2764d6ef23ab8775e2f4432339bc22d049662561f09d3fd890b5415738620d64dcedb762d5da26b4ebb";
+        version = "0.8.9";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-json-core/ch.qos.logback.contrib";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-json-core";
+        groupId = "ch.qos.logback.contrib";
+        sha512 = "2a826036f21997e2979fda83ae3e33cf62f3b2b2df15a7b11d1fd8a52163b09f0f2f8d72f5fdcea0ec1289b3d27727ed5e6b0bcdf4c5d741f4bac07b7b6139e8";
+        version = "0.1.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpclient/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpclient";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "3567739186e551f84cad3e4b6b270c5b8b19aba297675a96bcdff3663ff7d20d188611d21f675fe5ff1bfd7d8ca31362070910d7b92ab1b699872a120aa6f089";
+        version = "4.5.13";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "crypto-equality/crypto-equality";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "crypto-equality";
+        groupId = "crypto-equality";
+        sha512 = "54cf3bd28f633665962bf6b41f5ccbf2634d0db210a739e10a7b12f635e13c7ef532efe1d5d8c0120bb46478bbd08000b179f4c2dd52123242dab79fea97d6a6";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "cheshire/cheshire";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cheshire";
+        groupId = "cheshire";
+        sha512 = "855e9c42a8d1c64f4db5cda45e31e914eb5ed99a715e8d7a5759a9c4ab6c69a82353635ca7b0837880c6cf9b41b11184ae11e09cbf2c07aa13db32c539e5dfd4";
+        version = "5.10.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tigris/tigris";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tigris";
+        groupId = "tigris";
+        sha512 = "fdff4ef5e7175a973aaef98de4f37dee8e125fc711c495382e280aaf3e11341fe8925d52567ca60f3f1795511ade11bc23461c88959632dfae3cf50374d02bf6";
+        version = "0.1.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "config/yogthos";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "config";
+        groupId = "yogthos";
+        sha512 = "3437992d192465edc74aec5259d5e0c0ad7e631dff860b2ee14cef27f13cee7c60487202cf00fc160a95fb0b85ce1ddf56cbdd0c008b47ac598061bf115f6a23";
+        version = "1.1.9";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jetty-io/org.eclipse.jetty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jetty-io";
+        groupId = "org.eclipse.jetty";
+        sha512 = "a8c5f73089daa0c8b27f836acddf40bcbf07bbb2571a4d73653be8aac3fb339022f546326722f216bad78a68886934d24db9bec54235124592dd29dbeab69051";
+        version = "9.4.42.v20210604";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-json-classic/ch.qos.logback.contrib";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-json-classic";
+        groupId = "ch.qos.logback.contrib";
+        sha512 = "d30bf70217d316914d83d46cc15783f656354084087d59cbc0620a746f10b4a42e56d33b3e50a8b3596a64ec8314730bf5ff9a3f7dc3417bdd0582665be009ec";
+        version = "0.1.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "tools.reader/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "tools.reader";
+        groupId = "org.clojure";
+        sha512 = "3481259c7a1eac719db2921e60173686726a0c2b65879d51a64d516a37f6120db8ffbb74b8bd273404285d7b25143ab5c7ced37e7c0eaf4ab1e44586ccd3c651";
+        version = "1.3.6";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "simpleclient_common/io.prometheus";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "simpleclient_common";
+        groupId = "io.prometheus";
+        sha512 = "dedd003638eb3651c112e2d697ac94eb4e3b3e32c94fa41bb1efe2c889a347cdc7bd13256e05423f3370592d4fd65faf8db57f0387ab75814d7fa77b14cbbadf";
+        version = "0.12.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "commons-compiler/org.codehaus.janino";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "commons-compiler";
+        groupId = "org.codehaus.janino";
+        sha512 = "f0778b891ef14d8ee6776747eab0b25da716cdc530752a81aedec2a77570e2f66402179b9408a6efde8125c808eb060a720d2f4977c1f1d022bdaae7eac8d011";
+        version = "3.1.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "servlet-api/javax.servlet";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "servlet-api";
+        groupId = "javax.servlet";
+        sha512 = "363ba5590436ab82067b7a2e14b481aeb2b12ca4048d7a1519a2e549b2d3c09ddf718ac64dc2be6c2fc24c51fdc9c8160261329403113369588ce27d87771db6";
+        version = "2.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "iapetos/clj-commons";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "iapetos";
+        groupId = "clj-commons";
+        sha512 = "d17f36c0cf0ec78db5e893e5c033f8562b31650bda6f5ee582e68f84a07a3631d04d6f69e4e18b1ca64e732c180fa669dfb69a78849e13f601cd563a7a8aab94";
+        version = "0.1.12";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "javax.servlet-api/javax.servlet";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "javax.servlet-api";
+        groupId = "javax.servlet";
+        sha512 = "32f7e3565c6cdf3d9a562f8fd597fe5059af0cf6b05b772a144a74bbc95927ac275eb38374538ec1c72adcce4c8e1e2c9f774a7b545db56b8085af0065e4a1e5";
+        version = "3.1.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "potemkin/potemkin";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "potemkin";
+        groupId = "potemkin";
+        sha512 = "5abc050bf7ff0b27d8c45aaa5e378201980815b711b2db99735db73304576c17e285026ea48a714bf0b0df7ad7a008de38b7d182cdc0e8989f4be1e6b3afa8aa";
+        version = "0.4.5";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-resolver/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-resolver";
+        groupId = "io.netty";
+        sha512 = "fabf893de74264caa1799c15d184ed8f20b7bf9b1c41abb29f29adf728a934951f97892a4924634f9efbda17c8cf74ea3ff97bafca616711e3c5f79b8ed9ef3e";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-transport-native-epoll/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-transport-native-epoll";
+        groupId = "io.netty";
+        sha512 = "6fbc2dd2622699f3fc1f329acbd94baf7f1d8923c5cfcae262e6f2d64b4fd71b606561bce5e2b511dff8e052cdade930091fab683fd98713f6b62a622a2c6254";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "clj-stacktrace/clj-stacktrace";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "clj-stacktrace";
+        groupId = "clj-stacktrace";
+        sha512 = "993f8a544203801fc074eefacee8e553e426422b3492d47b857d87ac73cde72c91e29f629382b9eae8cf9600bc2c4c29d2e7169e509c46302ab973c86e73af0c";
+        version = "0.2.8";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "cambium.codec-cheshire/cambium";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cambium.codec-cheshire";
+        groupId = "cambium";
+        sha512 = "614491cf752a597f29ae29885db6c1ed191341303d89183bee52e4e2c76eb8eb14693562ad09484f379a074b36d97085e848ec3845e069440e6422506c1636f1";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "slf4j-api/org.slf4j";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "slf4j-api";
+        groupId = "org.slf4j";
+        sha512 = "ad705ab6fd5cd904ef6861c0adf08af19593cf6a486b18de548fe3d68e57b1baa7e02947584fd4dcc350ddcddcf906c01e8d9ba7943a202690d0d788627696b5";
+        version = "2.0.0-alpha4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "test.check/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "test.check";
+        groupId = "org.clojure";
+        sha512 = "b8d7a330b0b5514cd6a00c4382052fab51c3c9d3bc53133f8506791fa670e7c5ecd65094977ea5ced91f59623b0abd1ab8feeec96d63c5c6e459b265a655c577";
+        version = "1.1.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-logger/ring-logger";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-logger";
+        groupId = "ring-logger";
+        sha512 = "b675a61c173289fc610d84920ba40178bf62b3bc680923cb66866d78ee2a508296b27a1ab14b66bfbe0304a64166a7e3c3ddee36564dd4a2f988861bce455a3a";
+        version = "1.0.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-servlet/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-servlet";
+        groupId = "ring";
+        sha512 = "3d8e6ec224e13d54810a945c0b6c0d2d863736a48d8c4bfc8fadb96b6b0fa9baa638644d0d92d8a53650b188e6e75d391731b08b26eb0f551e90a7504e7f4267";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "logback-classic/ch.qos.logback";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "logback-classic";
+        groupId = "ch.qos.logback";
+        sha512 = "f9fe0f126061f4abe3973b631b8d8244ba9e9d77783479a6500d629d772050dee508a001fc14d2131407fbdd0d33dd6b8aeb9b1ea9125b471bb8412e8de659e6";
+        version = "1.3.0-alpha12";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "dependency/com.stuartsierra";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "dependency";
+        groupId = "com.stuartsierra";
+        sha512 = "d32fbc4813bd16f2ed8c82e2915e1fb564e88422159bd3580a85c8cd969d1bbbe315bdc13d29c2f0eaceeeafcf649ee712c8df4532464d560aaeae4ae5953866";
+        version = "1.0.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "camel-snake-kebab/camel-snake-kebab";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "camel-snake-kebab";
+        groupId = "camel-snake-kebab";
+        sha512 = "589d34b500560b7113760a16bfb6f0ccd8f162a1ce8c9bc829495432159ba9c95aebf6bc43aa126237a0525806a205a05f9910122074902b659e7fd151d176b1";
+        version = "0.4.2";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring";
+        groupId = "ring";
+        sha512 = "93c48fb670736b91fb41d8076e1e9c4f53c67693d15e75290da319e7d7881b829a24180029b3a0fa051473c6c77ac3c97b519254ebf2b2c9538b185e79b69162";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "netty-transport-native-kqueue/io.netty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "netty-transport-native-kqueue";
+        groupId = "io.netty";
+        sha512 = "87e10c06e394a1698d65381d3be8336f753c55e3e899e297510161d0c72540023f30f9032322957e035ead793204a084b988bc21a2bc312fcf7567a22d02a3c4";
+        version = "4.1.63.Final";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "java.data/org.clojure";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "java.data";
+        groupId = "org.clojure";
+        sha512 = "225e1eafd1a659278212d831f7cd8609359f8c880ef3d69b4ade6301ce3c511307ce31d94cb82d5407314b990bd04714ec26273bb3036b248116a7a75fa75e1f";
+        version = "1.0.95";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jetty-server/org.eclipse.jetty";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jetty-server";
+        groupId = "org.eclipse.jetty";
+        sha512 = "b347f8a6e5b84e0f460037027e238a61edec710ade768c95e7be13dcea498abe43d5e622ee69ac7494138d1a8fcf92e07b7deab569c554831c57baad71c53b9b";
+        version = "9.4.42.v20210604";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "httpmime/org.apache.httpcomponents";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "httpmime";
+        groupId = "org.apache.httpcomponents";
+        sha512 = "e1b0ee84bce78576074dc1b6836a69d8f5518eade38562e6890e3ddaa72b7f54bf735c8e2286142c58cddf45f745da31261e5d73b7d8092eb6ecfb20946eb36c";
+        version = "4.5.13";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "log4j-over-slf4j/org.slf4j";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "log4j-over-slf4j";
+        groupId = "org.slf4j";
+        sha512 = "48fa023c57294b73b9bd2f53e3dd3169e03426e5b3aa9d80e1bb1a9abf927fc26ef9f64d02b9769d5577d83094d0f41f044d35bb3b4f6037d66d6b2f19b484a1";
+        version = "2.0.0-alpha4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "ring-core/ring";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "ring-core";
+        groupId = "ring";
+        sha512 = "38d7214a3fc1b80ab55999036638dd1971272e01bec4cb8e0ee0a4aa83f51b8c41ba8a5850b0660227f067d2f9c6d75c0c0737725ea02762bbf8d192dc72febe";
+        version = "1.9.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "cambium.core/cambium";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "cambium.core";
+        groupId = "cambium";
+        sha512 = "0e1fe626c6d0b31aad84ea2e4466273065925548ee5915f442b7997ebfe795faea36dbeac50a0f8c16bbd20d877511e3f8c4ff4f2b916a4538513aaa5cc20112";
+        version = "1.1.1";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "medley/medley";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "medley";
+        groupId = "medley";
+        sha512 = "749ef43b5ea2cae7dc96db871cdd15c7b3c9cfbd96828c20ab08e67d39a5e938357d15994d8d413bc68678285d6c666f2a7296fbf305706d03b3007254e3c55c";
+        version = "1.3.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "garden/garden";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "garden";
+        groupId = "garden";
+        sha512 = "2cc29f071b68bf451835f76de351ac2efb930b5df9ca7237fdca439d3c4d797d7fa207a147886efe1738ab1c50b76c1e366bf9ffcd6f286b0b211260aedd0b25";
+        version = "1.3.10";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jackson-dataformat-smile/com.fasterxml.jackson.dataformat";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jackson-dataformat-smile";
+        groupId = "com.fasterxml.jackson.dataformat";
+        sha512 = "69676964a2b09516b8ffd0d847b6f9a9b843424185453731b548c25e7e9ce30e808c56d66923f9183e2b5c1ba007421b146a6806e768b8e6b07470d60227f1dd";
+        version = "2.12.4";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "jaxb-api/javax.xml.bind";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "jaxb-api";
+        groupId = "javax.xml.bind";
+        sha512 = "0c5bfc2c9f655bf5e6d596e0c196dcb9344d6dc78bf774207c8f8b6be59f69addf2b3121e81491983eff648dfbd55002b9878132de190825dad3ef3a1265b367";
+        version = "2.3.0";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "pgjdbc-ng/com.impossibl.pgjdbc-ng";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "pgjdbc-ng";
+        groupId = "com.impossibl.pgjdbc-ng";
+        sha512 = "a34ac9146257329f6e9b354f13f564c65dbea6463addae383e3918d3a64c90c67f5f7fda6b5c3866de991a568d6690edb3fb09f2507593390a6e30ec0c79e02c";
+        version = "0.8.9";
+
+      };
+      paths = [ src ];
+    }
+
+    rec {
+      name = "http-kit/http-kit";
+      src = fetchMavenArtifact {
+        inherit repos;
+        artifactId = "http-kit";
+        groupId = "http-kit";
+        sha512 = "4186a2429984745e18730aa8fd545f1fc1812083819ebf77aecfc04e0d31585358a5e25a308c7f21d81359418bbc72390c281f5ed91ae116cf1af79860ba22c3";
+        version = "2.5.3";
+
+      };
+      paths = [ src ];
+    }
+
+  ];
+}
+  
\ No newline at end of file
diff --git a/users/aspen/bbbg/env/dev/bbbg-signup/env.clj b/users/aspen/bbbg/env/dev/bbbg-signup/env.clj
new file mode 100644
index 0000000000..c30e328ffa
--- /dev/null
+++ b/users/aspen/bbbg/env/dev/bbbg-signup/env.clj
@@ -0,0 +1,3 @@
+(ns bbbg.env)
+
+(def environment :env/dev)
diff --git a/users/aspen/bbbg/env/dev/logback.xml b/users/aspen/bbbg/env/dev/logback.xml
new file mode 100644
index 0000000000..7aa21978bb
--- /dev/null
+++ b/users/aspen/bbbg/env/dev/logback.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg { %mdc }%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="INFO">
+    <appender-ref ref="STDOUT" />
+  </root>
+
+  <logger name="user" level="ALL" />
+  <logger name="ci.windtunnel" level="ALL" />
+</configuration>
diff --git a/users/aspen/bbbg/env/prod/bbbg-signup/env.clj b/users/aspen/bbbg/env/prod/bbbg-signup/env.clj
new file mode 100644
index 0000000000..46e8cd67e3
--- /dev/null
+++ b/users/aspen/bbbg/env/prod/bbbg-signup/env.clj
@@ -0,0 +1,3 @@
+(ns bbbg.env)
+
+(def environment :env/prod)
diff --git a/users/aspen/bbbg/env/prod/logback.xml b/users/aspen/bbbg/env/prod/logback.xml
new file mode 100644
index 0000000000..b81118ed6b
--- /dev/null
+++ b/users/aspen/bbbg/env/prod/logback.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <!-- Silence Logback's own status messages about config parsing -->
+  <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
+
+  <!-- Console output -->
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <!-- Only log level INFO and above -->
+    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+      <level>INFO</level>
+    </filter>
+    <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
+      <layout class="cambium.logback.json.FlatJsonLayout">
+        <jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter">
+          <prettyPrint>false</prettyPrint>
+        </jsonFormatter>
+        <!-- <context>api</context> -->
+        <timestampFormat>yyyy-MM-dd'T'HH:mm:ss.SSS'Z'</timestampFormat>
+        <timestampFormatTimezoneId>UTC</timestampFormatTimezoneId>
+        <appendLineSeparator>true</appendLineSeparator>
+      </layout>
+    </encoder>
+  </appender>
+
+
+  <root level="INFO">
+    <appender-ref ref="STDOUT" />
+  </root>
+
+  <logger name="user" level="ALL" />
+</configuration>
diff --git a/users/aspen/bbbg/env/test/bbbg-signup/env.clj b/users/aspen/bbbg/env/test/bbbg-signup/env.clj
new file mode 100644
index 0000000000..352147a6d0
--- /dev/null
+++ b/users/aspen/bbbg/env/test/bbbg-signup/env.clj
@@ -0,0 +1,3 @@
+(ns bbbg.env)
+
+(def environment :env/test)
diff --git a/users/aspen/bbbg/env/test/logback.xml b/users/aspen/bbbg/env/test/logback.xml
new file mode 100644
index 0000000000..8554f3d0ed
--- /dev/null
+++ b/users/aspen/bbbg/env/test/logback.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+      <pattern>%msg%n</pattern>
+    </encoder>
+  </appender>
+  <root level="OFF">
+    <appender-ref ref="CONSOLE"/>
+  </root>
+</configuration>
diff --git a/users/aspen/bbbg/module.nix b/users/aspen/bbbg/module.nix
new file mode 100644
index 0000000000..c5bacdf4d7
--- /dev/null
+++ b/users/aspen/bbbg/module.nix
@@ -0,0 +1,137 @@
+{ config, lib, pkgs, depot, ... }:
+
+let
+  bbbg = depot.users.aspen.bbbg;
+  cfg = config.services.bbbg;
+in
+{
+  options = with lib; {
+    services.bbbg = {
+      enable = mkEnableOption "BBBG Server";
+
+      port = mkOption {
+        type = types.int;
+        default = 7222;
+        description = "Port to listen to for the HTTP server";
+      };
+
+      domain = mkOption {
+        type = types.str;
+        default = "bbbg.gws.fyi";
+        description = "Domain to host under";
+      };
+
+      proxy = {
+        enable = mkEnableOption "NGINX reverse proxy";
+      };
+
+      database = {
+        enable = mkEnableOption "BBBG Database Server";
+
+        user = mkOption {
+          type = types.str;
+          default = "bbbg";
+          description = "Database username";
+        };
+
+        host = mkOption {
+          type = types.str;
+          default = "localhost";
+          description = "Database host";
+        };
+
+        name = mkOption {
+          type = types.str;
+          default = "bbbg";
+          description = "Database name";
+        };
+
+        port = mkOption {
+          type = types.int;
+          default = 5432;
+          description = "Database host";
+        };
+      };
+    };
+  };
+
+  config = lib.mkMerge [
+    (lib.mkIf cfg.enable {
+      systemd.services.bbbg-server = {
+        wantedBy = [ "multi-user.target" ];
+        after = [ "network.target" ];
+
+        serviceConfig = {
+          DynamicUser = true;
+          Restart = "always";
+          EnvironmentFile = config.age.secretsDir + "/bbbg";
+        };
+
+        environment = {
+          PGHOST = cfg.database.host;
+          PGUSER = cfg.database.user;
+          PGDATABASE = cfg.database.name;
+          PORT = toString cfg.port;
+          BASE_URL = "https://${cfg.domain}";
+        };
+
+        script = "${bbbg.server}/bin/bbbg-server";
+      };
+
+      systemd.services.migrate-bbbg = {
+        description = "Run database migrations for BBBG";
+        wantedBy = [ "bbbg-server.service" ];
+        after = ([ "network.target" ]
+          ++ (if cfg.database.enable
+        then [ "postgresql.service" ]
+        else [ ]));
+
+        serviceConfig = {
+          Type = "oneshot";
+          EnvironmentFile = config.age.secretsDir + "/bbbg";
+        };
+
+        environment = {
+          PGHOST = cfg.database.host;
+          PGUSER = cfg.database.user;
+          PGDATABASE = cfg.database.name;
+        };
+
+        script = "${bbbg.db-util}/bin/bbbg-db-util migrate";
+      };
+    })
+    (lib.mkIf cfg.database.enable {
+      services.postgresql = {
+        enable = true;
+        authentication = lib.mkForce ''
+          local all all trust
+          host all all 127.0.0.1/32 password
+          host all all ::1/128 password
+          hostnossl all all 127.0.0.1/32 password
+          hostnossl all all ::1/128  password
+        '';
+
+        ensureDatabases = [
+          cfg.database.name
+        ];
+
+        ensureUsers = [{
+          name = cfg.database.user;
+          ensurePermissions = {
+            "DATABASE ${cfg.database.name}" = "ALL PRIVILEGES";
+          };
+        }];
+      };
+    })
+    (lib.mkIf cfg.proxy.enable {
+      services.nginx = {
+        enable = true;
+        virtualHosts."${cfg.domain}" = {
+          enableACME = true;
+          forceSSL = true;
+          locations."/".proxyPass = "http://localhost:${toString cfg.port}";
+        };
+      };
+    })
+  ];
+}
diff --git a/users/aspen/bbbg/pom.xml b/users/aspen/bbbg/pom.xml
new file mode 100644
index 0000000000..012c0985f1
--- /dev/null
+++ b/users/aspen/bbbg/pom.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>fyi.gws</groupId>
+  <artifactId>bbbg</artifactId>
+  <version>0.1.0-SNAPSHOT</version>
+  <name>fyi.gws/bbbg</name>
+  <description>webhook listener for per-branch deploys</description>
+  <url>https://bbbg.gws.fyi</url>
+  <developers>
+    <developer>
+      <name>Griffin Smith</name>
+    </developer>
+  </developers>
+  <dependencies>
+    <dependency>
+      <groupId>org.clojure</groupId>
+      <artifactId>clojure</artifactId>
+      <version>1.11.0-alpha3</version>
+    </dependency>
+  </dependencies>
+  <build>
+    <sourceDirectory>src</sourceDirectory>
+  </build>
+  <repositories>
+    <repository>
+      <id>clojars</id>
+      <url>https://repo.clojars.org/</url>
+    </repository>
+    <repository>
+      <id>sonatype</id>
+      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
+    </repository>
+  </repositories>
+  <distributionManagement>
+    <repository>
+      <id>clojars</id>
+      <name>Clojars repository</name>
+      <url>https://clojars.org/repo</url>
+    </repository>
+  </distributionManagement>
+</project>
diff --git a/users/aspen/bbbg/resources/base.css b/users/aspen/bbbg/resources/base.css
new file mode 100644
index 0000000000..c86c3f24f0
--- /dev/null
+++ b/users/aspen/bbbg/resources/base.css
@@ -0,0 +1,152 @@
+/* montserrat-italic - latin */
+@font-face {
+  font-family: "Montserrat";
+  font-style: italic;
+  font-weight: 400;
+  src: local("Montserrat Italic"), local("Montserrat-Italic"),
+    url("/fonts/montserrat-v15-latin-italic.woff2") format("woff2"),
+    /* Chrome 26+, Opera 23+, Firefox 39+ */
+      url("/fonts/montserrat-v15-latin-italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* montserrat-regular - latin */
+@font-face {
+  font-family: "Montserrat";
+  font-style: normal;
+  font-weight: 400;
+  src: local("Montserrat Regular"), local("Montserrat-Regular"),
+    url("/fonts/montserrat-v15-latin-regular.woff2") format("woff2"),
+    /* Chrome 26+, Opera 23+, Firefox 39+ */
+      url("/fonts/montserrat-v15-latin-regular.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* montserrat-500 - latin */
+@font-face {
+  font-family: "Montserrat";
+  font-style: normal;
+  font-weight: 500;
+  src: local("Montserrat Medium"), local("Montserrat-Medium"),
+    url("/fonts/montserrat-v15-latin-500.woff2") format("woff2"),
+    /* Chrome 26+, Opera 23+, Firefox 39+ */
+      url("/fonts/montserrat-v15-latin-500.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* montserrat-500italic - latin */
+@font-face {
+  font-family: "Montserrat";
+  font-style: italic;
+  font-weight: 500;
+  src: local("Montserrat Medium Italic"), local("Montserrat-MediumItalic"),
+    url("/fonts/montserrat-v15-latin-500italic.woff2") format("woff2"),
+    /* Chrome 26+, Opera 23+, Firefox 39+ */
+      url("/fonts/montserrat-v15-latin-500italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* montserrat-600 - latin */
+@font-face {
+  font-family: "Montserrat";
+  font-style: normal;
+  font-weight: 600;
+  src: local("Montserrat SemiBold"), local("Montserrat-SemiBold"),
+    url("/fonts/montserrat-v15-latin-600.woff2") format("woff2"),
+    /* Chrome 26+, Opera 23+, Firefox 39+ */
+      url("/fonts/montserrat-v15-latin-600.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* montserrat-800 - latin */
+@font-face {
+  font-family: "Montserrat";
+  font-style: normal;
+  font-weight: 800;
+  src: local("Montserrat ExtraBold"), local("Montserrat-ExtraBold"),
+    url("/fonts/montserrat-v15-latin-800.woff2") format("woff2"),
+    /* Chrome 26+, Opera 23+, Firefox 39+ */
+      url("/fonts/montserrat-v15-latin-800.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+/* montserrat-800italic - latin */
+@font-face {
+  font-family: "Montserrat";
+  font-style: italic;
+  font-weight: 800;
+  src: local("Montserrat ExtraBold Italic"), local("Montserrat-ExtraBoldItalic"),
+    url("/fonts/montserrat-v15-latin-800italic.woff2") format("woff2"),
+    /* Chrome 26+, Opera 23+, Firefox 39+ */
+      url("/fonts/montserrat-v15-latin-800italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
+}
+
+body {
+  width: 100%;
+  font-family: "Montserrat", Helvetica, sans-serif;
+  margin: 0;
+  box-sizing: border-box;
+}
+
+*,
+::before,
+::after {
+  box-sizing: border-box;
+}
+
+ul,
+ol {
+  padding: 0;
+}
+
+body,
+h1,
+h2,
+h3,
+h4,
+p,
+ul,
+ol,
+li,
+figure,
+figcaption,
+blockquote,
+dl,
+dd {
+  margin: 0;
+}
+
+body {
+  min-height: 100vh;
+  scroll-behavior: smooth;
+  text-rendering: optimizeSpeed;
+  line-height: 1.5;
+}
+
+ul[class],
+ol[class] {
+  list-style: none;
+}
+
+a:not([class]) {
+  text-decoration-skip-ink: auto;
+}
+
+img {
+  max-width: 100%;
+  display: block;
+}
+
+article > * + * {
+  margin-top: 1em;
+}
+
+input,
+button,
+textarea,
+select {
+  font: inherit;
+}
+
+@media (prefers-reduced-motion: reduce) {
+  * {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+    scroll-behavior: auto !important;
+  }
+}
diff --git a/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.down.sql b/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.down.sql
new file mode 100644
index 0000000000..69b818a4f4
--- /dev/null
+++ b/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.down.sql
@@ -0,0 +1,14 @@
+drop table "public"."user";
+
+-- ;;
+
+drop table "public"."event_attendee";
+
+
+-- ;;
+
+drop table "public"."event";
+
+-- ;;
+
+drop table "public"."attendee";
diff --git a/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.up.sql b/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.up.sql
new file mode 100644
index 0000000000..9718d84748
--- /dev/null
+++ b/users/aspen/bbbg/resources/migrations/20211212165646-init-schema.up.sql
@@ -0,0 +1,32 @@
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
+-- ;;
+CREATE TABLE "attendee" (
+    "id" UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
+    "meetup_name" TEXT NOT NULL,
+    "discord_name" TEXT,
+    "meetup_user_id" TEXT,
+    "organizer_notes" TEXT NOT NULL DEFAULT '',
+    "created_at" TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
+);
+-- ;;
+CREATE TABLE "event" (
+    "id" UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
+    "date" DATE NOT NULL,
+    "created_at" TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
+);
+-- ;;
+CREATE TABLE "event_attendee" (
+    "event_id" UUID NOT NULL REFERENCES "event" ("id"),
+    "attendee_id" UUID NOT NULL REFERENCES "attendee" ("id"),
+    "rsvpd_attending" BOOL,
+    "attended" BOOL,
+    "created_at" TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
+    PRIMARY KEY ("event_id", "attendee_id")
+);
+-- ;;
+CREATE TABLE "user" (
+    "id" UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
+    "username" TEXT NOT NULL,
+    "discord_user_id" TEXT NOT NULL,
+    "created_at" TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
+);
diff --git a/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql b/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql
new file mode 100644
index 0000000000..936abf6c7d
--- /dev/null
+++ b/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.down.sql
@@ -0,0 +1 @@
+DROP TABLE "attendee_check";
diff --git a/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql b/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql
new file mode 100644
index 0000000000..5e82dcb171
--- /dev/null
+++ b/users/aspen/bbbg/resources/migrations/20211220002229-add-attendee-checks.up.sql
@@ -0,0 +1,7 @@
+CREATE TABLE attendee_check (
+    "id" UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4(),
+    "attendee_id" UUID NOT NULL REFERENCES attendee ("id"),
+    "user_id" UUID NOT NULL REFERENCES "public"."user" ("id"),
+    "last_dose_at" DATE,
+    "checked_at" TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
+);
diff --git a/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql b/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql
new file mode 100644
index 0000000000..cbee0c00ac
--- /dev/null
+++ b/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.down.sql
@@ -0,0 +1 @@
+drop index attendee_uniq_meetup_user_id;
diff --git a/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql b/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql
new file mode 100644
index 0000000000..5895cad56b
--- /dev/null
+++ b/users/aspen/bbbg/resources/migrations/20211224161028-add-attendee-unique-meetup-id.up.sql
@@ -0,0 +1,2 @@
+create unique index "attendee_uniq_meetup_user_id" on attendee (meetup_user_id);
+-- ;;
diff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff
new file mode 100644
index 0000000000..1c83d8518d
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2
new file mode 100644
index 0000000000..9dc5c7f158
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500.woff2
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff
new file mode 100644
index 0000000000..71476d858f
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2
new file mode 100644
index 0000000000..0fb9838c9d
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-500italic.woff2
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff
new file mode 100644
index 0000000000..e7f8a31ba3
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2
new file mode 100644
index 0000000000..29cc1a9734
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-600.woff2
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff
new file mode 100644
index 0000000000..79203dd780
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2
new file mode 100644
index 0000000000..0abb707aed
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800.woff2
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff
new file mode 100644
index 0000000000..65415571a7
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2
new file mode 100644
index 0000000000..674e6eabe7
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-800italic.woff2
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff
new file mode 100644
index 0000000000..67f1e85379
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2
new file mode 100644
index 0000000000..469aede09c
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-italic.woff2
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff
new file mode 100644
index 0000000000..676a065e24
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff
Binary files differdiff --git a/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2 b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2
new file mode 100644
index 0000000000..70788c2732
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/fonts/montserrat-v15-latin-regular.woff2
Binary files differdiff --git a/users/aspen/bbbg/resources/public/main.js b/users/aspen/bbbg/resources/public/main.js
new file mode 100644
index 0000000000..87c0b64d0a
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/main.js
@@ -0,0 +1,73 @@
+window.onload = () => {
+  const input = document.getElementById("name-autocomplete");
+  if (input != null) {
+    const attendeeList = document.getElementById("attendees-list");
+    const filterAttendees = (filter) => {
+      if (filter == "") {
+        for (let elt of attendeeList.querySelectorAll("li")) {
+          elt.classList.remove("hidden");
+        }
+
+        return;
+      }
+
+      let re = "";
+      for (let c of filter) {
+        re += `${c}.*`;
+      }
+      let filterRe = new RegExp(re, "i");
+
+      for (let elt of attendeeList.querySelectorAll("li")) {
+        const attendee = JSON.parse(elt.dataset.attendee);
+        if (attendee["bbbg.attendee/meetup-name"].match(filterRe) == null) {
+          elt.classList.add("hidden");
+        } else {
+          elt.classList.remove("hidden");
+        }
+      }
+    };
+
+    const attendeeIDInput = document.getElementById("attendee-id");
+    const submit = document.querySelector("#submit-button");
+    const signupForm = document.getElementById("signup-form");
+
+    input.oninput = (e) => {
+      filterAttendees(e.target.value);
+      attendeeIDInput.value = null;
+      submit.classList.add("hidden");
+      submit.setAttribute("disabled", "disabled");
+      signupForm.setAttribute("disabled", "disabled");
+    };
+
+    attendeeList.addEventListener("click", (e) => {
+      if (!(e.target instanceof HTMLLIElement)) {
+        return;
+      }
+      if (e.target.dataset.attendee == null) {
+        return;
+      }
+
+      const attendee = JSON.parse(e.target.dataset.attendee);
+      input.value = attendee["bbbg.attendee/meetup-name"];
+      attendeeIDInput.value = attendee["bbbg.attendee/id"];
+
+      submit.classList.remove("hidden");
+      submit.removeAttribute("disabled");
+      signupForm.removeAttribute("disabled");
+    });
+  }
+
+  document.querySelectorAll("form").forEach((form) => {
+    form.addEventListener("submit", (e) => {
+      if (e.target.attributes.disabled) {
+        e.preventDefault();
+      }
+
+      const confirmMessage = e.target.dataset.confirm;
+      if (confirmMessage != null && !confirm(confirmMessage)) {
+        e.stopImmediatePropagation();
+        e.preventDefault();
+      }
+    });
+  });
+};
diff --git a/users/aspen/bbbg/resources/public/robots.txt b/users/aspen/bbbg/resources/public/robots.txt
new file mode 100644
index 0000000000..1f53798bb4
--- /dev/null
+++ b/users/aspen/bbbg/resources/public/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: /
diff --git a/users/aspen/bbbg/shell.nix b/users/aspen/bbbg/shell.nix
new file mode 100644
index 0000000000..c253a2b9ba
--- /dev/null
+++ b/users/aspen/bbbg/shell.nix
@@ -0,0 +1,29 @@
+let
+  depot = import ../../.. { };
+in
+with depot.third_party.nixpkgs;
+
+mkShell {
+  buildInputs = [
+    arion
+    depot.third_party.clj2nix
+    clojure
+    openjdk11_headless
+    postgresql_12
+    nix-prefetch-git
+    (writeShellScriptBin "terraform" ''
+      set -e
+      module=$(nix-build ~/code/depot -A users.grfn.bbbg.tf.module)
+      rm -f ~/tfstate/bbbg/*.json
+      cp ''${module}/*.json ~/tfstate/bbbg
+      exec ${depot.users.aspen.bbbg.tf.terraform}/bin/terraform \
+        -chdir=/home/grfn/tfstate/bbbg \
+        "$@"
+    '')
+  ];
+
+  PGHOST = "localhost";
+  PGUSER = "bbbg";
+  PGDATABASE = "bbbg";
+  PGPASSWORD = "password";
+}
diff --git a/users/aspen/bbbg/src/bbbg/attendee.clj b/users/aspen/bbbg/src/bbbg/attendee.clj
new file mode 100644
index 0000000000..49a6d621de
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/attendee.clj
@@ -0,0 +1,10 @@
+(ns bbbg.attendee
+  (:require [clojure.spec.alpha :as s]))
+
+(s/def ::id uuid?)
+
+(s/def ::meetup-name (s/and string? seq))
+
+(s/def ::discord-name (s/nilable string?))
+
+(s/def ::organizer-notes string?)
diff --git a/users/aspen/bbbg/src/bbbg/attendee_check.clj b/users/aspen/bbbg/src/bbbg/attendee_check.clj
new file mode 100644
index 0000000000..f34c41198e
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/attendee_check.clj
@@ -0,0 +1,4 @@
+(ns bbbg.attendee-check
+  (:require [clojure.spec.alpha :as s]))
+
+(s/def ::id uuid?)
diff --git a/users/aspen/bbbg/src/bbbg/core.clj b/users/aspen/bbbg/src/bbbg/core.clj
new file mode 100644
index 0000000000..632774d5cd
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/core.clj
@@ -0,0 +1,69 @@
+(ns bbbg.core
+  (:gen-class)
+  (:require
+   [bbbg.db :as db]
+   [bbbg.web :as web]
+   [clojure.spec.alpha :as s]
+   [clojure.spec.test.alpha :as stest]
+   [com.stuartsierra.component :as component]
+   [expound.alpha :as exp]))
+
+(s/def ::config
+  (s/merge
+   ::db/config
+   ::web/config))
+
+(defn make-system [config]
+  (component/system-map
+   :db (db/make-database config)
+   :web (web/make-server config)))
+
+(defn env->config []
+  (s/assert
+   ::config
+   (merge
+    (db/env->config)
+    (web/env->config))))
+
+(defn dev-config []
+  (s/assert
+   ::config
+   (merge
+    (db/dev-config)
+    (web/dev-config))))
+
+(defonce system nil)
+
+(defn init-dev []
+  (s/check-asserts true)
+  (set! s/*explain-out* exp/printer)
+  (stest/instrument))
+
+(defn run-dev []
+  (init-dev)
+  (alter-var-root
+   #'system
+   (fn [sys]
+     (when sys
+       (component/start sys))
+     (component/start (make-system (dev-config))))))
+
+(defn -main [& _args]
+  (alter-var-root
+   #'system
+   (constantly (component/start (make-system (env->config))))))
+
+(comment
+  ;; To run the application:
+  ;; 1. `M-x cider-jack-in`
+  ;; 2. `M-x cider-load-buffer` in this buffer
+  ;; 3. (optionally) configure the secrets backend in `bbbg.util.dev-secrets`
+  ;; 4. Put your cursor after the following form and run `M-x cider-eval-last-sexp`
+  ;;
+  ;; A web server will be listening on http://localhost:8888
+
+  (do
+    (run-dev)
+    (bbbg.db/migrate! (:db system)))
+
+  )
diff --git a/users/aspen/bbbg/src/bbbg/db.clj b/users/aspen/bbbg/src/bbbg/db.clj
new file mode 100644
index 0000000000..5bbf88925a
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/db.clj
@@ -0,0 +1,366 @@
+(ns bbbg.db
+  (:gen-class)
+  (:refer-clojure :exclude [get list count])
+  (:require [camel-snake-kebab.core :as csk :refer [->kebab-case ->snake_case]]
+            [bbbg.util.core :as u]
+            [clojure.set :as set]
+            [clojure.spec.alpha :as s]
+            [clojure.string :as str]
+            [com.stuartsierra.component :as component]
+            [config.core :refer [env]]
+            [honeysql.format :as hformat]
+            [migratus.core :as migratus]
+            [next.jdbc :as jdbc]
+            [next.jdbc.connection :as jdbc.conn]
+            next.jdbc.date-time
+            [next.jdbc.optional :as jdbc.opt]
+            [next.jdbc.result-set :as rs]
+            [next.jdbc.sql :as sql])
+  (:import [com.impossibl.postgres.jdbc PGSQLSimpleException]
+           com.zaxxer.hikari.HikariDataSource
+           [java.sql Connection ResultSet Types]
+           javax.sql.DataSource))
+
+(s/def ::host string?)
+(s/def ::database string?)
+(s/def ::user string?)
+(s/def ::password string?)
+
+(s/def ::config
+  (s/keys :opt [::host
+                ::database
+                ::user
+                ::password]))
+
+(s/fdef make-database
+  :args
+  (s/cat :config (s/keys :opt [::config])))
+
+(s/fdef env->config :ret ::config)
+
+(s/def ::db any?)
+
+;;;
+
+(def default-config
+  (s/assert
+   ::config
+   {::host "localhost"
+    ::database "bbbg"
+    ::user "bbbg"
+    ::password "password"}))
+
+(defn dev-config [] default-config)
+
+(defn env->config []
+  (->>
+   {::host (:pghost env)
+    ::database (:pgdatabase env)
+    ::user (:pguser env)
+    ::password (:pgpassword env)}
+   u/remove-nils
+   (s/assert ::config)))
+
+(defn ->db-spec [config]
+  (-> default-config
+      (merge config)
+      (set/rename-keys
+       {::host :host
+        ::database :dbname
+        ::user :username
+        ::password :password})
+      (assoc :dbtype "pgsql")))
+
+(defn connection
+  "Make a one-off connection from the given `::config` map, or the environment
+  if not provided"
+  ([] (connection (env->config)))
+  ([config]
+   (-> config
+       ->db-spec
+       (set/rename-keys {:username :user})
+       jdbc/get-datasource
+       jdbc/get-connection)))
+
+(defrecord Database [config]
+  component/Lifecycle
+  (start [this]
+    (assoc this :pool (jdbc.conn/->pool HikariDataSource (->db-spec config))))
+  (stop [this]
+    (some-> this :pool .close)
+    (dissoc this :pool))
+
+  clojure.lang.IFn
+  (invoke [this] (:pool this)))
+
+(defn make-database [config]
+  (map->Database {:config config}))
+
+(defn database? [x]
+  (or
+   (instance? Database x)
+   (and (map? x) (contains? x :pool))))
+
+;;;
+;;; Migrations
+;;;
+
+(defn migratus-config
+  [db]
+  {:store :database
+   :migration-dir "migrations/"
+   :migration-table-name "__migrations__"
+   :db
+   (let [db (if (ifn? db) (db) db)]
+     (cond
+       (.isInstance Connection db)
+       {:connection db}
+       (.isInstance DataSource db)
+       {:datasource db}
+       :else (throw
+              (ex-info "migratus-config called with value of unrecognized type"
+                       {:value db}))))})
+
+(defn generate-migration
+  ([db name] (generate-migration db name :sql))
+  ([db name type] (migratus/create (migratus-config db) name type)))
+
+(defn migrate!
+  [db] (migratus/migrate (migratus-config db)))
+
+(defn rollback!
+  [db] (migratus/rollback (migratus-config db)))
+
+;;;
+;;; Database interaction
+;;;
+
+(defn ->key-ns [tn]
+  (let [tn (name tn)
+        tn (if (str/starts-with? tn "public.")
+             (second (str/split tn #"\." 2))
+             tn)]
+    (str "bbbg." (->kebab-case tn))))
+
+(defn ->table-name [kns]
+  (let [kns (name kns)]
+    (->snake_case
+     (if (str/starts-with? kns "public.")
+       kns
+       (str "public." (last (str/split kns #"\.")))))))
+
+(defn ->column
+  ([col] (->column nil col))
+  ([table col]
+   (let [col-table (some-> col namespace ->table-name)
+         snake-col (-> col name ->snake_case (str/replace #"\?$" ""))]
+     (if (or (not (namespace col))
+             (not table)
+             (= (->table-name table) col-table))
+       snake-col
+       ;; different table, assume fk
+       (str
+        (str/replace-first col-table "public." "")
+        "_"
+        snake-col)))))
+
+(defn ->value [v]
+  (if (keyword? v)
+    (-> v name csk/->snake_case_string)
+    v))
+
+(defn process-key-map [table key-map]
+  (into {}
+        (map (fn [[k v]] [(->column table k)
+                          (->value v)]))
+        key-map))
+
+(defn fkize [col]
+  (if (str/ends-with? col "-id")
+    (let [table (str/join "-" (butlast (str/split (name col) #"-")))]
+      (keyword (->key-ns table) "id"))
+    col))
+
+(def ^:private enum-members-cache (atom {}))
+(defn- enum-members
+  "Returns a set of enum members as strings for the enum with the given name"
+  [db name]
+  (if-let [e (find @enum-members-cache name)]
+    (val e)
+    (let [r (try
+              (-> (jdbc/execute-one!
+                   (db)
+                   [(format "select enum_range(null::%s) as members" name)])
+                  :members
+                  .getArray
+                  set)
+              (catch PGSQLSimpleException _
+                nil))]
+      (swap! enum-members-cache assoc name r)
+      r)))
+
+(def ^{:private true
+       :dynamic true}
+  *meta-db*
+  "Database connection to use to query metadata"
+  nil)
+
+(extend-protocol rs/ReadableColumn
+  String
+  (read-column-by-label [x _] x)
+  (read-column-by-index [x rsmeta idx]
+    (if-not *meta-db*
+      x
+      (let [typ (.getColumnTypeName rsmeta idx)]
+        ;; TODO: Is there a better way to figure out if a type is an enum?
+        (if (enum-members *meta-db* typ)
+          (keyword (csk/->kebab-case-string typ)
+                   (csk/->kebab-case-string x))
+          x)))))
+
+(comment
+  (->key-ns :public.user)
+  (->key-ns :public.api-token)
+  (->key-ns :api-token)
+  (->table-name :api-token)
+  (->table-name :public.user)
+  (->table-name :bbbg.user)
+  )
+
+(defn as-fq-maps [^ResultSet rs _opts]
+  (let [qualify #(when (seq %) (str "bbbg." (->kebab-case %)))
+        rsmeta (.getMetaData rs)
+        cols (mapv
+              (fn [^Integer i]
+                (let [ty (.getColumnType rsmeta i)
+                      lab (.getColumnLabel rsmeta i)
+                      n (str (->kebab-case lab)
+                             (when (= ty Types/BOOLEAN) "?"))]
+                  (fkize
+                   (if-let [q (some-> rsmeta (.getTableName i) qualify not-empty)]
+                     (keyword q n)
+                     (keyword n)))))
+              (range 1 (inc (.getColumnCount rsmeta))))]
+    (jdbc.opt/->MapResultSetOptionalBuilder rs rsmeta cols)))
+
+(def jdbc-opts
+  {:builder-fn as-fq-maps
+   :column-fn ->snake_case
+   :table-fn ->snake_case})
+
+(defmethod hformat/fn-handler "count-distinct" [_ field]
+  (str "count(distinct " (hformat/to-sql field) ")"))
+
+(defn fetch
+  "Fetch a single row from the db matching the given `sql-map` or query"
+  [db sql-map & [opts]]
+  (s/assert
+   (s/nilable (s/keys))
+   (binding [*meta-db* db]
+     (jdbc/execute-one!
+      (db)
+      (if (map? sql-map)
+        (hformat/format sql-map)
+        sql-map)
+      (merge jdbc-opts opts)))))
+
+(defn get
+  "Retrieve a single record from the given table by ID"
+  [db table id & [opts]]
+  (when id
+    (fetch
+     db
+     {:select [:*]
+      :from [table]
+      :where [:= :id id]}
+     opts)))
+
+(defn list
+  "Returns a list of rows from the db matching the given sql-map, table or
+  query"
+  [db sql-map-or-table & [opts]]
+  (s/assert
+   (s/coll-of (s/keys))
+   (binding [*meta-db* db]
+     (jdbc/execute!
+      (db)
+      (cond
+        (map? sql-map-or-table)
+        (hformat/format sql-map-or-table)
+        (keyword? sql-map-or-table)
+        (hformat/format {:select [:*] :from [sql-map-or-table]})
+        :else
+        sql-map-or-table)
+      (merge jdbc-opts opts)))))
+
+(defn count
+  [db sql-map]
+  (binding [*meta-db* db]
+    (:count
+     (fetch db {:select [[:%count.* :count]], :from [[sql-map :sq]]}))))
+
+(defn exists?
+  "Returns true if the given sql query-map would return any results"
+  [db sql-map]
+  (binding [*meta-db* db]
+    (pos?
+     (count db sql-map))))
+
+(defn execute!
+  "Given a database and a honeysql query map, perform an operation on the
+  database and discard the results"
+  [db sql-map & [opts]]
+  (jdbc/execute!
+   (db)
+   (hformat/format sql-map)
+   (merge jdbc-opts opts)))
+
+(defn insert!
+  "Given a database, a table name, and a data hash map, inserts the
+  data as a single row in the database and attempts to return a map of generated
+  keys."
+  [db table key-map & [opts]]
+  (binding [*meta-db* db]
+    (sql/insert!
+     (db)
+     table
+     (process-key-map table key-map)
+     (merge jdbc-opts opts))))
+
+(defn update!
+  "Given a database, a table name, a hash map of columns and values
+  to set, and a honeysql predicate, perform an update on the table.
+  Will "
+  [db table key-map where-params & [opts]]
+  (binding [*meta-db* db]
+    (execute! db
+              {:update table
+               :set (u/map-keys keyword (process-key-map table key-map))
+               :where where-params
+               :returning [:id]}
+              opts)))
+
+(defn delete!
+  "Delete all rows from the given table matching the given where clause"
+  [db table where-clause]
+  (binding [*meta-db* db]
+    (sql/delete! (db) table (hformat/format-predicate where-clause))))
+
+(defmacro with-transaction [[sym db opts] & body]
+  `(jdbc/with-transaction
+     [tx# (~db) ~opts]
+     (let [~sym (constantly tx#)]
+       ~@body)))
+
+(defn -main [& args]
+  (let [db (component/start (make-database (env->config)))]
+    (case (first args)
+      "migrate" (migrate! db)
+      "rollback" (rollback! db))))
+
+(comment
+  (def db (:db bbbg.core/system))
+  (generate-migration db "add-attendee-unique-meetup-id")
+  (migrate! db)
+
+  )
diff --git a/users/aspen/bbbg/src/bbbg/db/attendee.clj b/users/aspen/bbbg/src/bbbg/db/attendee.clj
new file mode 100644
index 0000000000..da5ee29321
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/db/attendee.clj
@@ -0,0 +1,85 @@
+(ns bbbg.db.attendee
+  (:require
+   [bbbg.attendee :as attendee]
+   [bbbg.db :as db]
+   [bbbg.util.sql :refer [count-where]]
+   honeysql-postgres.helpers
+   [honeysql.helpers
+    :refer
+    [merge-group-by merge-join merge-left-join merge-select merge-where]]
+   [bbbg.util.core :as u]))
+
+(defn search
+  ([q] (search {:select [:attendee.*] :from [:attendee]} q))
+  ([db-or-query q]
+   (if (db/database? db-or-query)
+     (db/list db-or-query (search q))
+     (cond-> db-or-query
+       q (merge-where
+          [:or
+           [:ilike :meetup_name (str "%" q "%")]
+           [:ilike :discord_name (str "%" q "%")]]))))
+  ([db query q]
+   (db/list db (search query q))))
+
+(defn for-event
+  ([event-id]
+   (for-event {:select [:attendee.*]
+               :from [:attendee]}
+              event-id))
+  ([db-or-query event-id]
+   (if (db/database? db-or-query)
+     (db/list db-or-query (for-event event-id))
+     (-> db-or-query
+         (merge-select :event-attendee.*)
+         (merge-join :event_attendee [:= :attendee.id :event_attendee.attendee_id])
+         (merge-where [:= :event_attendee.event_id event-id]))))
+  ([db query event-id]
+   (db/list db (for-event query event-id))))
+
+(defn with-stats
+  ([] (with-stats {:select [:attendee.*]
+                   :from [:attendee]}))
+  ([query]
+   (-> query
+       (merge-left-join :event_attendee [:= :attendee.id :event_attendee.attendee_id])
+       (merge-group-by :attendee.id)
+       (merge-select
+        [(count-where :event_attendee.rsvpd_attending) :events-rsvpd]
+        [(count-where :event_attendee.attended) :events-attended]
+        [(count-where [:and
+                       :event_attendee.rsvpd_attending
+                       [:not :event_attendee.attended]])
+         :no-shows]))))
+
+(defn upsert-all!
+  [db attendees]
+  (when (seq attendees)
+    (db/list
+     db
+     {:insert-into :attendee
+      :values (map #(->> %
+                         (db/process-key-map :attendee)
+                         (u/map-keys keyword))
+                   attendees)
+      :upsert {:on-conflict [:meetup-user-id]
+               :do-update-set [:meetup-name]}
+      :returning [:id :meetup-user-id]})))
+
+(comment
+  (def db (:db bbbg.core/system))
+  (db/database? db)
+  (search db "gri")
+  (db/insert! db :attendee {::attendee/meetup-name "Griffin Smith"
+                            ::attendee/discord-name "grfn"
+                            })
+
+  (search db (with-stats) "gri")
+
+  (search (with-stats) "gri")
+
+  (db/list db (with-stats))
+
+  (db/insert! db :attendee {::attendee/meetup-name "Rando Guy"
+                            ::attendee/discord-name "rando"})
+  )
diff --git a/users/aspen/bbbg/src/bbbg/db/attendee_check.clj b/users/aspen/bbbg/src/bbbg/db/attendee_check.clj
new file mode 100644
index 0000000000..492f786bd6
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/db/attendee_check.clj
@@ -0,0 +1,55 @@
+(ns bbbg.db.attendee-check
+  (:require
+   [bbbg.attendee :as attendee]
+   [bbbg.attendee-check :as attendee-check]
+   [bbbg.db :as db]
+   [bbbg.user :as user]
+   [bbbg.util.core :as u]))
+
+(defn create! [db params]
+  (db/insert! db :attendee-check
+              (select-keys params [::attendee/id
+                                   ::user/id
+                                   ::attendee-check/last-dose-at])))
+
+(defn attendees-with-last-checks
+  [db attendees]
+  (when (seq attendees)
+    (let [ids (map ::attendee/id attendees)
+          checks
+          (db/list db {:select [:attendee-check.*]
+                       :from [:attendee-check]
+                       :join [[{:select [:%max.attendee-check.checked-at
+                                         :attendee-check.attendee-id]
+                                :from [:attendee-check]
+                                :group-by [:attendee-check.attendee-id]
+                                :where [:in :attendee-check.attendee-id ids]}
+                               :last-check]
+                              [:=
+                               :attendee-check.attendee-id
+                               :last-check.attendee-id]]})
+          users (if (seq checks)
+                  (u/key-by
+                   ::user/id
+                   (db/list db {:select [:public.user.*]
+                                :from [:public.user]
+                                :where [:in :id (map ::user/id checks)]}))
+                  {})
+          checks (map #(assoc % :user (users (::user/id %))) checks)
+          attendee-id->check (u/key-by ::attendee/id checks)]
+      (map #(assoc % :last-check (attendee-id->check (::attendee/id %)))
+           attendees))))
+
+(comment
+  (def db (:db bbbg.core/system))
+
+  (attendees-with-last-checks
+   db
+   (db/list db :attendee)
+   )
+
+  (db/insert! db :attendee-check
+              {::attendee/id #uuid "58bcd372-ff6e-49df-b280-23d24c5ba0f0"
+               ::user/id #uuid "303fb606-5ef0-4682-ad7d-6429c670cd78"
+               ::attendee-check/last-dose-at "2021-12-19"})
+  )
diff --git a/users/aspen/bbbg/src/bbbg/db/event.clj b/users/aspen/bbbg/src/bbbg/db/event.clj
new file mode 100644
index 0000000000..1b5a4e11ec
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/db/event.clj
@@ -0,0 +1,94 @@
+(ns bbbg.db.event
+  (:require
+   [bbbg.attendee :as attendee]
+   [bbbg.db :as db]
+   [bbbg.event :as event]
+   [bbbg.util.sql :refer [count-where]]
+   [honeysql.helpers
+    :refer [merge-group-by merge-left-join merge-select merge-where]]
+   [java-time :refer [local-date local-date-time local-time]]))
+
+(defn create! [db event]
+  (db/insert! db :event (select-keys event [::event/date])))
+
+(defn attended!
+  [db params]
+  (db/execute!
+   db
+   {:insert-into :event-attendee
+    :values [{:event_id (::event/id params)
+              :attendee_id (::attendee/id params)
+              :attended true}]
+    :upsert {:on-conflict [:event-id :attendee-id]
+             :do-update-set! {:attended true}}}))
+
+(defn on-day
+  ([day] {:select [:event.*]
+          :from [:event]
+          :where [:= :date (str day)]})
+  ([db day]
+   (db/list db (on-day day))))
+
+
+(def end-of-day-hour
+  ;; 7am utc = 3am nyc
+  7)
+
+(defn current-day
+  ([] (current-day (local-date-time)))
+  ([dt]
+   (if (<= 0
+           (.getHour (local-time dt))
+           end-of-day-hour)
+     (java-time/minus
+      (local-date dt)
+      (java-time/days 1))
+     (local-date dt))))
+
+(comment
+  (current-day
+   (local-date-time
+    2022 5 1
+    1 13 0))
+  )
+
+(defn today
+  ([] (on-day (current-day)))
+  ([db] (db/list db (today))))
+
+(defn upcoming
+  ([] (upcoming {:select [:event.*] :from [:event]}))
+  ([query]
+   (merge-where query [:>= :date (local-date)])))
+
+(defn past
+  ([] (past {:select [:event.*] :from [:event]}))
+  ([query]
+   (merge-where query [:< :date (local-date)])))
+
+(defn with-attendee-counts
+  [query]
+  (-> query
+      (merge-left-join :event_attendee [:= :event.id :event_attendee.event-id])
+      (merge-select :%count.event_attendee.attendee_id)
+      (merge-group-by :event.id :event_attendee.event-id)))
+
+(defn with-stats
+  [query]
+  (-> query
+      (merge-left-join :event_attendee [:= :event.id :event_attendee.event-id])
+      (merge-select
+       [(count-where :event-attendee.rsvpd_attending) :num-rsvps]
+       [(count-where :event-attendee.attended) :num-attendees])
+      (merge-group-by :event.id)))
+
+(comment
+  (def db (:db bbbg.core/system))
+  (db/list db (-> (today) (with-attendee-counts)))
+
+  (honeysql.format/format
+   (honeysql-postgres.helpers/upsert {:insert-into :foo
+                                      :values {:bar 1}}
+                                     (-> (honeysql-postgres.helpers/on-conflict :did)
+                                         (honeysql-postgres.helpers/do-update-set! [:did true]))))
+  )
diff --git a/users/aspen/bbbg/src/bbbg/db/event_attendee.clj b/users/aspen/bbbg/src/bbbg/db/event_attendee.clj
new file mode 100644
index 0000000000..31411e5d45
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/db/event_attendee.clj
@@ -0,0 +1,17 @@
+(ns bbbg.db.event-attendee
+  (:require honeysql-postgres.format
+            [bbbg.db :as db]
+            [bbbg.util.core :as u]))
+
+(defn upsert-all!
+  [db attendees]
+  (when (seq attendees)
+    (db/execute!
+     db
+     {:insert-into :event-attendee
+      :values (map #(->> %
+                         (db/process-key-map :event-attendee)
+                         (u/map-keys keyword))
+                   attendees)
+      :upsert {:on-conflict [:event-id :attendee-id]
+               :do-update-set [:rsvpd-attending]}})))
diff --git a/users/aspen/bbbg/src/bbbg/db/user.clj b/users/aspen/bbbg/src/bbbg/db/user.clj
new file mode 100644
index 0000000000..700105ef63
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/db/user.clj
@@ -0,0 +1,19 @@
+(ns bbbg.db.user
+  (:require [bbbg.db :as db]
+            [bbbg.user :as user]))
+
+(defn create! [db attrs]
+  (db/insert! db
+              :public.user
+              (select-keys attrs [::user/id
+                                  ::user/username
+                                  ::user/discord-user-id])))
+
+(defn find-or-create! [db attrs]
+  (or
+   (db/fetch db {:select [:*]
+                 :from [:public.user]
+                 :where [:=
+                         :discord-user-id
+                         (::user/discord-user-id attrs)]})
+   (create! db attrs)))
diff --git a/users/aspen/bbbg/src/bbbg/discord.clj b/users/aspen/bbbg/src/bbbg/discord.clj
new file mode 100644
index 0000000000..e854ec1d14
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/discord.clj
@@ -0,0 +1,44 @@
+(ns bbbg.discord
+  (:refer-clojure :exclude [get])
+  (:require
+   [bbbg.util.dev-secrets :refer [secret]]
+   [clj-http.client :as http]
+   [clojure.string :as str]))
+
+(def base-uri "https://discord.com/api")
+
+(defn api-uri [path]
+  (str base-uri
+       (when-not (str/starts-with? path "/") "/")
+       path))
+
+(defn get
+  ([token path]
+   (get token path {}))
+  ([token path params]
+   (:body
+    (http/get (api-uri path)
+              (-> params
+                  (assoc :accept :json
+                         :as :json)
+                  (assoc-in [:headers "authorization"]
+                            (str "Bearer " (:token token))))))))
+
+(defn me [token]
+  (get token "/users/@me"))
+
+(defn guilds [token]
+  (get token "/users/@me/guilds"))
+
+(defn guild-member [token guild-id]
+  (get token (str "/users/@me/guilds/" guild-id "/member")))
+
+(comment
+  (def token {:token (secret "bbbg/test-token")})
+  (me token)
+  (guilds token)
+  (guild-member token "841295283564052510")
+
+  (get token "/guilds/841295283564052510/roles")
+
+  )
diff --git a/users/aspen/bbbg/src/bbbg/discord/auth.clj b/users/aspen/bbbg/src/bbbg/discord/auth.clj
new file mode 100644
index 0000000000..35bc580e39
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/discord/auth.clj
@@ -0,0 +1,90 @@
+(ns bbbg.discord.auth
+  (:require
+   [bbbg.discord :as discord]
+   [bbbg.util.core :as u]
+   [bbbg.util.dev-secrets :refer [secret]]
+   clj-time.coerce
+   [clojure.spec.alpha :as s]
+   [config.core :refer [env]]
+   [ring.middleware.oauth2 :refer [wrap-oauth2]]))
+
+(s/def ::client-id string?)
+(s/def ::client-secret string?)
+(s/def ::bbbg-guild-id string?)
+(s/def ::bbbg-organizer-role string?)
+
+(s/def ::config (s/keys :req [::client-id
+                              ::client-secret
+                              ::bbbg-guild-id
+                              ::bbbg-organizer-role]))
+
+;;;
+
+(defn env->config []
+  (s/assert
+   ::config
+   {::client-id (:discord-client-id env)
+    ::client-secret (:discord-client-secret env)
+    ::bbbg-guild-id (:bbbg-guild-id env "841295283564052510")
+    ::bbbg-organizer-role (:bbbg-organizer-role
+                           env
+                           ;; TODO this might not be the right id
+                           "908428000817725470")}))
+
+(defn dev-config []
+  (s/assert
+   ::config
+   {::client-id (secret "bbbg/discord-client-id")
+    ::client-secret (secret "bbbg/discord-client-secret")
+    ::bbbg-guild-id "841295283564052510"
+    ::bbbg-organizer-role "908428000817725470"}))
+
+;;;
+
+(def access-token-url
+  "https://discord.com/api/oauth2/token")
+
+(def authorization-url
+  "https://discord.com/api/oauth2/authorize")
+
+(def revoke-url
+  "https://discord.com/api/oauth2/token/revoke")
+
+(def scopes ["guilds"
+             "guilds.members.read"
+             "identify"])
+
+(defn discord-oauth-profile [{:keys [base-url] :as env}]
+  {:authorize-uri authorization-url
+   :access-token-uri access-token-url
+   :client-id (::client-id env)
+   :client-secret (::client-secret env)
+   :scopes scopes
+   :launch-uri "/auth/discord"
+   :redirect-uri (str base-url "/auth/discord/redirect")
+   :landing-uri (str base-url "/auth/success")})
+
+(comment
+  (-> "https://bbbg-staging.gws.fyi/auth/login"
+      (java.net.URI/create)
+      (.resolve "https://bbbg.gws.fyi/auth/discord/redirect")
+      str)
+  )
+
+(defn wrap-discord-auth [handler env]
+  (wrap-oauth2 handler {:discord (discord-oauth-profile env)}))
+
+(defn check-discord-auth
+  "Check that the user with the given token has the correct level of discord
+  auth"
+  [{::keys [bbbg-guild-id bbbg-organizer-role]} token]
+  (and (some (comp #{bbbg-guild-id} :id)
+             (discord/guilds token))
+       (some #{bbbg-organizer-role}
+             (:roles (discord/guild-member token bbbg-guild-id)))))
+
+(comment
+  (#'ring.middleware.oauth2/valid-profile?
+   (discord-oauth-profile
+    (dev-config)))
+  )
diff --git a/users/aspen/bbbg/src/bbbg/event.clj b/users/aspen/bbbg/src/bbbg/event.clj
new file mode 100644
index 0000000000..aa0578f354
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/event.clj
@@ -0,0 +1,4 @@
+(ns bbbg.event
+  (:require [clojure.spec.alpha :as s]))
+
+(s/def ::id uuid?)
diff --git a/users/aspen/bbbg/src/bbbg/event_attendee.clj b/users/aspen/bbbg/src/bbbg/event_attendee.clj
new file mode 100644
index 0000000000..7b6b4c2764
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/event_attendee.clj
@@ -0,0 +1,6 @@
+(ns bbbg.event-attendee
+  (:require [clojure.spec.alpha :as s]))
+
+(s/def ::attended? boolean?)
+
+(s/def ::rsvpd-attending? boolean?)
diff --git a/users/aspen/bbbg/src/bbbg/handlers/attendee_checks.clj b/users/aspen/bbbg/src/bbbg/handlers/attendee_checks.clj
new file mode 100644
index 0000000000..d7307c4067
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/handlers/attendee_checks.clj
@@ -0,0 +1,68 @@
+(ns bbbg.handlers.attendee-checks
+  (:require
+   [bbbg.attendee :as attendee]
+   [bbbg.attendee-check :as attendee-check]
+   [bbbg.db :as db]
+   [bbbg.db.attendee-check :as db.attendee-check]
+   [bbbg.handlers.core :refer [page-response wrap-auth-required]]
+   [bbbg.user :as user]
+   [bbbg.util.display :refer [format-date]]
+   [compojure.coercions :refer [as-uuid]]
+   [compojure.core :refer [context GET POST]]
+   [ring.util.response :refer [not-found redirect]]
+   [bbbg.views.flash :as flash]))
+
+(defn- edit-attendee-checks-page [{:keys [existing-check]
+                                   attendee-id ::attendee/id}]
+  [:div.page
+   (when existing-check
+     [:p
+      "Already checked on "
+      (-> existing-check ::attendee-check/checked-at format-date)
+      " by "
+      (::user/username existing-check)])
+   [:form.attendee-checks-form
+    {:method :post
+     :action (str "/attendees/" attendee-id "/checks")}
+    [:div.form-group
+     [:label
+      "Last Dose"
+      [:input {:type :date
+               :name :last-dose-at}]]]
+    [:div.form-group
+     [:input {:type :submit
+              :value "Mark Checked"}]]]])
+
+(defn attendee-checks-routes [{:keys [db]}]
+  (wrap-auth-required
+   (context "/attendees/:attendee-id/checks" [attendee-id :<< as-uuid]
+     (GET "/edit" []
+       (if (db/exists? db {:select [1]
+                           :from [:attendee]
+                           :where [:= :id attendee-id]})
+         (let [existing-check (db/fetch
+                               db
+                               {:select [:attendee-check.*
+                                         :public.user.*]
+                                :from [:attendee-check]
+                                :join [:public.user
+                                       [:=
+                                        :attendee-check.user-id
+                                        :public.user.id]]
+                                :where [:= :attendee-id attendee-id]})]
+           (page-response
+            (edit-attendee-checks-page
+             {:existing-check existing-check
+              ::attendee/id attendee-id})))
+         (not-found "Attendee not found")))
+     (POST "/" {{:keys [last-dose-at]} :params
+                {user-id ::user/id} :session}
+       (db.attendee-check/create!
+        db
+        {::attendee/id attendee-id
+         ::user/id user-id
+         ::attendee-check/last-dose-at last-dose-at})
+       (-> (redirect "/attendees")
+           (flash/add-flash
+            #:flash{:type :success
+                    :message "Successfully updated vaccination status"}))))))
diff --git a/users/aspen/bbbg/src/bbbg/handlers/attendees.clj b/users/aspen/bbbg/src/bbbg/handlers/attendees.clj
new file mode 100644
index 0000000000..ce84b88e97
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/handlers/attendees.clj
@@ -0,0 +1,162 @@
+(ns bbbg.handlers.attendees
+  (:require
+   [bbbg.attendee :as attendee]
+   [bbbg.attendee-check :as attendee-check]
+   [bbbg.db :as db]
+   [bbbg.db.attendee :as db.attendee]
+   [bbbg.db.attendee-check :as db.attendee-check]
+   [bbbg.db.event :as db.event]
+   [bbbg.event :as event]
+   [bbbg.handlers.core :refer [page-response wrap-auth-required]]
+   [bbbg.user :as user]
+   [bbbg.util.display :refer [format-date]]
+   [bbbg.views.flash :as flash]
+   [cheshire.core :as json]
+   [compojure.coercions :refer [as-uuid]]
+   [compojure.core :refer [GET POST routes]]
+   [honeysql.helpers :refer [merge-where]]
+   [ring.util.response :refer [content-type not-found redirect response]])
+  (:import
+   java.util.UUID))
+
+(defn- attendees-page [{:keys [attendees q edit-notes]}]
+  [:div.page
+   [:form.search-form {:method :get :action "/attendees"}
+    [:input.search-input
+     {:type "search"
+      :name "q"
+      :value q
+      :title "Search Attendees"}]
+    [:input {:type "submit"
+             :value "Search Attendees"}]]
+   [:table.attendees
+    [:thead
+     [:tr
+      [:th "Meetup Name"]
+      [:th "Discord Name"]
+      [:th "Events RSVPd"]
+      [:th "Events Attended"]
+      [:th "No-Shows"]
+      [:th "Last Vaccination Check"]
+      [:th "Notes"]]]
+    [:tbody
+     (for [attendee (sort-by
+                     (comp #{edit-notes} ::attendee/id)
+                     (comp - compare)
+                     attendees)
+           :let [id (::attendee/id attendee)]]
+       [:tr
+        [:td.attendee-name (::attendee/meetup-name attendee)]
+        [:td
+         [:label.mobile-label "Discord Name: "]
+         (or (not-empty (::attendee/discord-name attendee))
+             "โ€”")]
+        [:td
+         [:label.mobile-label "Events RSVPd: "]
+         (:events-rsvpd attendee)]
+        [:td
+         [:label.mobile-label "Events Attended: "]
+         (:events-attended attendee)]
+        [:td
+         [:label.mobile-label "No-shows: "]
+         (:no-shows attendee)]
+        [:td
+         [:label.mobile-label "Last Vaccination Check: "]
+         (if-let [last-check (:last-check attendee)]
+           (str "โœ”๏ธ "(-> last-check
+                        ::attendee-check/checked-at
+                        format-date)
+                ", by "
+                (get-in last-check [:user ::user/username]))
+           (list
+            [:span {:title "Not Checked"}
+             "โŒ"]
+            " "
+            [:a {:href (str "/attendees/" id "/checks/edit")}
+             "Edit"] ))]
+        (if (= edit-notes id)
+          [:td
+           [:form.organizer-notes {:method :post
+                                   :action (str "/attendees/" id "/notes")}
+            [:div.form-group
+             [:input {:type :text :name "notes"
+                      :value (::attendee/organizer-notes attendee)
+                      :autofocus true}]]
+            [:div.form-group
+             [:input {:type "Submit" :value "Save Notes"}]]]]
+          [:td
+           [:p
+            (::attendee/organizer-notes attendee)]
+           [:p
+            [:a {:href (str "/attendees?edit-notes=" id)}
+             "Edit Notes"]]])])]]])
+
+(defn attendees-routes [{:keys [db]}]
+  (routes
+   (wrap-auth-required
+    (routes
+     (GET "/attendees" [q edit-notes]
+       (let [attendees (db/list db (cond-> (db.attendee/with-stats)
+                                     q (db.attendee/search q)))
+             attendees (db.attendee-check/attendees-with-last-checks
+                        db
+                        attendees)
+             edit-notes (some-> edit-notes UUID/fromString)]
+         (page-response (attendees-page {:attendees attendees
+                                         :q q
+                                         :edit-notes edit-notes}))))
+
+     (POST "/attendees/:id/notes" [id :<< as-uuid notes]
+       (if (seq (db/update! db
+                            :attendee
+                            {::attendee/organizer-notes notes}
+                            [:= :id id]))
+         (-> (redirect "/attendees")
+             (flash/add-flash
+              #:flash{:type :success
+                      :message "Notes updated successfully"}))
+         (not-found "Attendee not found")))))
+
+   (GET "/attendees.json" [q event_id attended]
+     (let [results
+           (db/list
+            db
+            (cond->
+                (if q
+                  (db.attendee/search q)
+                  {:select [:attendee.*] :from [:attendee]})
+                event_id (db.attendee/for-event event_id)
+                (some? attended)
+                (merge-where
+                 (case attended
+                   "true" :attended
+                   "false" [:or [:= :attended nil] [:not :attended]]))))]
+       (-> {:results results}
+           json/generate-string
+           response
+           (content-type "application/json"))))
+
+   (POST "/event_attendees" [event_id attendee_id]
+     (if (and (db/exists? db {:select [:id] :from [:event] :where [:= :id event_id]})
+              (db/exists? db {:select [:id] :from [:attendee] :where [:= :id attendee_id]}))
+       (do
+         (db.event/attended! db {::event/id event_id
+                                 ::attendee/id attendee_id})
+         (-> (redirect (str "/signup-forms/" event_id))
+             (flash/add-flash
+              #:flash{:type :success
+                      :message "Thank you for signing in! Enjoy the event."})))
+       (response "Something went wrong")))))
+
+(comment
+  (def db (:db bbbg.core/system))
+  (db/list db :attendee)
+  (db/list db
+           (->
+            (db.attendee/search "gr")
+            (db.attendee/for-event #uuid "9f4f3eae-3317-41a7-843c-81bcae52aebf")))
+  (honeysql.format/format
+   (->
+    (db.attendee/search "gr")
+    (db.attendee/for-event #uuid "9f4f3eae-3317-41a7-843c-81bcae52aebf")))
+  )
diff --git a/users/aspen/bbbg/src/bbbg/handlers/core.clj b/users/aspen/bbbg/src/bbbg/handlers/core.clj
new file mode 100644
index 0000000000..caa679ee87
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/handlers/core.clj
@@ -0,0 +1,91 @@
+(ns bbbg.handlers.core
+  (:require
+   [bbbg.user :as user]
+   [bbbg.views.flash :as flash]
+   [hiccup.core :refer [html]]
+   [ring.util.response :refer [content-type response]]
+   [clojure.string :as str]))
+
+(def ^:dynamic *authenticated?* false)
+
+(defn authenticated? [request]
+  (some? (get-in request [:session ::user/id])))
+
+(defn wrap-auth-required [handler]
+  (fn [req]
+    (when (authenticated? req)
+      (handler req))))
+
+(defn wrap-dynamic-auth [handler]
+  (fn [req]
+    (binding [*authenticated?* (authenticated? req)]
+      (handler req))))
+
+(def ^:dynamic *current-uri*)
+
+(defn wrap-current-uri [handler]
+  (fn [req]
+    (binding [*current-uri* (:uri req)]
+      (handler req))))
+
+(defn nav-item [href label]
+  (let [active?
+        (when *current-uri*
+          (str/starts-with?
+           *current-uri*
+           href))]
+    [:li {:class (when active? "active")}
+     [:a {:href href}
+      label]]))
+
+(defn global-nav []
+  [:nav.global-nav
+   [:ul
+    (nav-item "/events" "Events")
+    (when *authenticated?*
+      (nav-item "/attendees" "Attendees"))
+    [:li.spacer]
+    [:li
+     (if *authenticated?*
+       [:form.link-form
+        {:method :post
+         :action "/auth/sign-out"}
+        [:input {:type "submit"
+                 :value "Sign Out"}]]
+       [:a {:href "/auth/discord"}
+        "Sign In"])]]])
+
+(defn render-page [opts & body]
+  (let [[{:keys [title]} body]
+        (if (map? opts)
+          [opts body]
+          [{} (concat [opts] body)])]
+    (html
+     [:html {:lang "en"}
+      [:head
+       [:meta {:charset "UTF-8"}]
+       [:meta {:name "viewport"
+               :content "width=device-width,initial-scale=1"}]
+       [:title (if title
+                 (str title " - BBBG")
+                 "BBBG")]
+       [:link {:rel "stylesheet"
+               :type "text/css"
+               :href "/main.css"}]]
+      [:body
+       [:div.content
+        (global-nav)
+        #_(flash/render-flash flash/test-flash)
+        (flash/render-flash)
+        body]
+       [:script {:src "/main.js"}]]])))
+
+(defn page-response [& render-page-args]
+  (-> (apply render-page render-page-args)
+      response
+      (content-type "text/html")))
+
+(comment
+  (render-page
+   [:h1 "hi"])
+  )
diff --git a/users/aspen/bbbg/src/bbbg/handlers/events.clj b/users/aspen/bbbg/src/bbbg/handlers/events.clj
new file mode 100644
index 0000000000..6f6d6f3585
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/handlers/events.clj
@@ -0,0 +1,259 @@
+(ns bbbg.handlers.events
+  (:require
+   [bbbg.db :as db]
+   [bbbg.db.attendee :as db.attendee]
+   [bbbg.db.event :as db.event]
+   [bbbg.event :as event]
+   [bbbg.handlers.core :refer [*authenticated?* page-response]]
+   [bbbg.meetup.import :refer [import-attendees!]]
+   [bbbg.util.display :refer [format-date pluralize]]
+   [bbbg.util.time :as t]
+   [bbbg.views.flash :as flash]
+   [compojure.coercions :refer [as-uuid]]
+   [compojure.core :refer [context GET POST]]
+   [java-time :refer [local-date]]
+   [ring.util.response :refer [not-found redirect]]
+   [bbbg.attendee :as attendee]
+   [bbbg.event-attendee :as event-attendee]
+   [bbbg.db.attendee-check :as db.attendee-check]
+   [bbbg.attendee-check :as attendee-check]
+   [bbbg.user :as user])
+  (:import
+   java.time.format.FormatStyle))
+
+(defn- num-attendees [event]
+  (str
+   (:num-attendees event)
+   (if (= (t/->LocalDate (::event/date event))
+          (local-date))
+     " Signed In"
+     (str " Attendee" (when-not (= 1 (:num-attendees event)) "s")))))
+
+(def index-type->label
+  {:upcoming "Upcoming"
+   :past "Past"})
+(def other-index-type
+  {:upcoming :past
+   :past :upcoming})
+
+(defn events-index
+  [{:keys [events num-events type]}]
+  [:div.page
+   [:div.page-header
+    [:h1
+     (pluralize
+      num-events
+      (str (index-type->label type) " Event"))]
+    [:a {:href (str "/events"
+                    (when (= :upcoming type)
+                      "/past"))}
+     "View "
+     (index-type->label (other-index-type type))
+     " Events"]]
+   (when *authenticated?*
+     [:a.button {:href "/events/new"}
+      "Create New Event"])
+   [:ul.events-list
+    (for [event (sort-by
+                 ::event/date
+                 (comp - compare)
+                 events)]
+      [:li
+       [:p
+        [:a {:href (str "/events/" (::event/id event))}
+         (format-date (::event/date event)
+                      FormatStyle/FULL)]]
+       [:p
+        (pluralize (:num-rsvps event) "RSVP")
+        ", "
+        (num-attendees event)]])]])
+
+(defn- import-attendee-list-form-group []
+  [:div.form-group
+   [:label "Import Attendee List"
+    [:br]
+    [:input {:type :file
+             :name :attendees}]]])
+
+(defn import-attendees-form [event]
+  [:form {:method :post
+          :action (str "/events/" (::event/id event) "/attendees")
+          :enctype "multipart/form-data"}
+   (import-attendee-list-form-group)
+   [:div.form-group
+    [:input {:type :submit
+             :value "Import"}]]])
+
+(defn event-page [{:keys [event attendees]}]
+  [:div.page
+   [:div.page-header
+    [:h1 (format-date (::event/date event)
+                      FormatStyle/FULL)]
+    [:div.spacer]
+    [:a.button {:href (str "/signup-forms/" (::event/id event) )}
+     "Go to Signup Form"]
+    [:form#delete-event
+     {:method :post
+      :action (str "/events/" (::event/id event) "/delete")
+      :data-confirm "Are you sure you want to delete this event?"}
+     [:input.error {:type "submit"
+                    :value "Delete Event"}]]]
+   [:div.stats
+    [:p (pluralize (:num-rsvps event) "RSVP")]
+    [:p (num-attendees event)]]
+   [:div
+    (import-attendees-form event)]
+   [:div
+    [:table.attendees
+     [:thead
+      [:th "Meetup Name"]
+      [:th "Discord Name"]
+      [:th "RSVP"]
+      [:th "Signed In"]
+      [:th "Last Vaccination Check"]]
+     [:tbody
+      (for [attendee (sort-by (juxt (comp not ::event-attendee/rsvpd-attending?)
+                                    (comp not ::event-attendee/attended?)
+                                    (comp some? :last-check)
+                                    ::attendee/meetup-name)
+                              attendees)]
+        [:tr
+         [:td.attendee-name (::attendee/meetup-name attendee)]
+         [:td
+          [:label.mobile-label "Discord Name: "]
+          (or (not-empty (::attendee/discord-name attendee))
+              "โ€”")]
+         [:td
+          [:label.mobile-label "RSVP: "]
+          (if (::event-attendee/rsvpd-attending? attendee)
+            [:span {:title "Yes"} "โœ”๏ธ"]
+            [:span {:title "No"} "โŒ"])]
+         [:td
+          [:label.mobile-label "Signed In: "]
+          (if (::event-attendee/attended? attendee)
+            [:span {:title "Yes"} "โœ”๏ธ"]
+            [:span {:title "No"} "โŒ"])]
+         [:td
+          [:label.mobile-label "Last Vaccination Check: "]
+          (if-let [last-check (:last-check attendee)]
+            (str "โœ”๏ธ "(-> last-check
+                         ::attendee-check/checked-at
+                         format-date)
+                 ", by "
+                 (get-in last-check [:user ::user/username]))
+            (list
+             [:span {:title "Not Checked"}
+              "โŒ"]
+             " "
+             [:a {:href (str "/attendees/"
+                             (::attendee/id attendee)
+                             "/checks/edit")}
+              "Edit"]))]])]]]])
+
+(defn import-attendees-page [{:keys [event]}]
+  [:div.page
+   [:h1 "Import Attendees for " (format-date (::event/date event))]
+   (import-attendees-form event)])
+
+(defn event-form
+  ([] (event-form {}))
+  ([event]
+   [:div.page
+    [:div.page-header
+     [:h1 "Create New Event"]]
+    [:form {:method "POST"
+            :action "/events"
+            :enctype "multipart/form-data"}
+     [:div.form-group
+      [:label "Date"
+       [:input {:type "date"
+                :id "date"
+                :name "date"
+                :value (str (::event/date event))}]]]
+     (import-attendee-list-form-group)
+     [:div.form-group
+      [:input {:type "submit"
+               :value "Create Event"}]]]]))
+
+(defn- events-list-handler [db query type]
+  (let [events (db/list db (db.event/with-stats query))
+        num-events (db/count db query)]
+    (page-response
+     (events-index {:events events
+                    :num-events num-events
+                    :type type}))))
+
+(defn events-routes [{:keys [db]}]
+  (context "/events" []
+    (GET "/" []
+      (events-list-handler db (db.event/upcoming) :upcoming))
+
+    (GET "/past" []
+      (events-list-handler db (db.event/past) :past))
+
+    (GET "/new" [date]
+      (page-response
+       {:title "New Event"}
+       (event-form {::event/date date})))
+
+    (POST "/" [date attendees]
+      (let [event (db.event/create! db {::event/date date})
+            message
+            (if attendees
+              (let [num-attendees
+                    (import-attendees! db
+                                       (::event/id event)
+                                       (:tempfile attendees))]
+                (format "Event created with %d attendees"
+                        num-attendees))
+              "Event created")]
+        (-> (str "/signup-forms/" (::event/id event))
+            redirect
+            (flash/add-flash {:flash/type :success
+                              :flash/message message}))))
+
+    (context "/:id" [id :<< as-uuid]
+      (GET "/" []
+        (if-let [event (db/fetch db
+                                 (-> {:select [:event.*]
+                                      :from [:event]
+                                      :where [:= :event.id id]}
+                                     (db.event/with-stats)))]
+          (let [attendees (db.attendee-check/attendees-with-last-checks
+                           db
+                           (db/list db (db.attendee/for-event id)))]
+            (page-response
+             (event-page {:event event
+                          :attendees attendees})))
+          (not-found "Event Not Found")))
+
+      (POST "/delete" []
+        (db/delete! db :event_attendee [:= :event-id id])
+        (db/delete! db :event [:= :id id])
+        (-> (redirect "/events")
+            (flash/add-flash
+             #:flash {:type :success
+                      :message "Successfully deleted event"})))
+
+      (GET "/attendees/import" []
+        (if-let [event (db/get db :event id)]
+          (page-response
+           (import-attendees-page {:event event}))
+          (not-found "Event Not Found")))
+
+      (POST "/attendees" [attendees]
+        (let [num-imported (import-attendees! db id (:tempfile attendees))]
+          (-> (redirect (str "/events/" id))
+              (flash/add-flash
+               #:flash{:type :success
+                       :message (format "Successfully imported %d attendees"
+                                        num-imported)})))))))
+
+(comment
+  (def db (:db bbbg.core/system))
+
+  (-> (db/list db :event)
+      first
+      ::event/date
+      format-date)
+  )
diff --git a/users/aspen/bbbg/src/bbbg/handlers/home.clj b/users/aspen/bbbg/src/bbbg/handlers/home.clj
new file mode 100644
index 0000000000..17d4875536
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/handlers/home.clj
@@ -0,0 +1,52 @@
+(ns bbbg.handlers.home
+  (:require
+   [bbbg.db.user :as db.user]
+   [bbbg.discord.auth :as discord.auth]
+   [bbbg.handlers.core :refer [page-response authenticated?]]
+   [bbbg.user :as user]
+   [bbbg.views.flash :as flash]
+   [compojure.core :refer [GET POST routes]]
+   [ring.util.response :refer [redirect]]
+   [bbbg.discord :as discord]))
+
+(defn- home-page []
+  [:div.home-page
+   [:a.signup-form-link {:href "/signup-forms"}
+    "Event Signup Form"]])
+
+(defn auth-failure []
+  [:div.auth-failure
+   [:p
+    "Sorry, only users with the Organizers role in discord can sign in"]
+   [:p
+    [:a {:href "/"} "Go Back"]]])
+
+(defn home-routes [{:keys [db] :as env}]
+  (routes
+   (GET "/" [] (page-response (home-page)))
+
+   (POST "/auth/sign-out" request
+     (if (authenticated? request)
+       (-> (redirect "/")
+           (update :session dissoc ::user/id)
+           (flash/add-flash
+            {:flash/message "Successfully Signed Out"
+             :flash/type :success}))
+       (redirect "/")))
+
+   (GET "/auth/success" request
+     (let [token (get-in request [:oauth2/access-tokens :discord])]
+       (if (discord.auth/check-discord-auth env token)
+         (let [discord-user (discord/me token)
+               user (db.user/find-or-create!
+                     db
+                     #::user{:username (:username discord-user)
+                             :discord-user-id (:id discord-user)})]
+           (-> (redirect "/")
+               (assoc-in [:session ::user/id] (::user/id user))
+               (flash/add-flash
+                {:flash/message "Successfully Signed In"
+                 :flash/type :success})))
+         (->
+          (page-response (auth-failure))
+          (assoc :status 401)))))))
diff --git a/users/aspen/bbbg/src/bbbg/handlers/signup_form.clj b/users/aspen/bbbg/src/bbbg/handlers/signup_form.clj
new file mode 100644
index 0000000000..ed1d7644f5
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/handlers/signup_form.clj
@@ -0,0 +1,93 @@
+(ns bbbg.handlers.signup-form
+  (:require
+   [bbbg.attendee :as attendee]
+   [bbbg.db :as db]
+   [bbbg.db.attendee :as db.attendee]
+   [bbbg.db.event :as db.event]
+   [bbbg.event :as event]
+   [bbbg.handlers.core
+    :refer [*authenticated?* authenticated? page-response]]
+   [cheshire.core :as json]
+   [compojure.core :refer [context GET]]
+   [honeysql.helpers :refer [merge-where]]
+   [java-time :refer [local-date]]
+   [ring.util.response :refer [redirect]]))
+
+(defn no-events-page [{:keys [authenticated?]}]
+  [:div.page
+   [:p
+    "There are no events for today"]
+   (when authenticated?
+     [:p
+      [:a.button {:href (str "/events/new?date=" (str (local-date)))}
+       "Create New Event"]])])
+
+(defn signup-page [{:keys [event attendees]}]
+  [:div.signup-page
+   [:form#signup-form
+    {:method "POST"
+     :action "/event_attendees"
+     :disabled "disabled"}
+    [:input#name-autocomplete
+     {:type "search"
+      :title "Name"
+      :name "name"
+      :spellcheck "false"
+      :autocorrect "off"
+      :autocomplete "off"
+      :autocapitalize "off"
+      :maxlength "2048"}]
+    [:input#attendee-id {:type "hidden" :name "attendee_id"}]
+    [:input#event-id {:type "hidden" :name "event_id" :value (::event/id event)}]
+    [:input#submit-button.hidden
+     {:type "submit"
+      :value "Sign In"
+      :disabled "disabled"}]]
+   [:ul#attendees-list
+    (if (seq attendees)
+      (for [attendee attendees]
+        [:li {:data-attendee (json/generate-string attendee)
+              :role "button"}
+         (::attendee/meetup-name attendee)])
+      [:li.no-attendees
+       [:p
+        "Nobody has RSVPed to this event yet, or no attendee list has been
+         imported"]
+       (when *authenticated?*
+         [:p
+          [:a.button
+           {:href (str "/events/"
+                       (::event/id event)
+                       "/attendees/import")}
+           "Import Attendee List"]])])]])
+
+(defn event-not-found []
+  [:div.event-not-found
+   [:p "Event not found"]
+   [:p [:a {:href (str "/events/new")} "Create a new event"]]])
+
+;;;
+
+(defn signup-form-routes [{:keys [db]}]
+  (context "/signup-forms" []
+    (GET "/" request
+      (if-let [event (db/fetch db (db.event/today))]
+        (redirect (str "/signup-forms/" (::event/id event)))
+        (page-response (no-events-page
+                        {:authenticated? (authenticated? request)}))))
+
+    (GET "/:event-id" [event-id]
+      (if-let [event (db/get db :event event-id)]
+        (let [attendees (db/list db
+                                 (->
+                                  (db.attendee/for-event event-id)
+                                  (merge-where
+                                   [:and
+                                    [:or
+                                     [:= :attended nil]
+                                     [:not :attended]]
+                                    :rsvpd_attending])))]
+          (page-response
+           (signup-page {:event event
+                         :attendees attendees})))
+        (event-not-found)))))
diff --git a/users/aspen/bbbg/src/bbbg/meetup/import.clj b/users/aspen/bbbg/src/bbbg/meetup/import.clj
new file mode 100644
index 0000000000..bbf8678976
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/meetup/import.clj
@@ -0,0 +1,125 @@
+(ns bbbg.meetup.import
+  (:require
+   [bbbg.attendee :as attendee]
+   [bbbg.db.attendee :as db.attendee]
+   [bbbg.db.event-attendee :as db.event-attendee]
+   [bbbg.event :as event]
+   [bbbg.event-attendee :as event-attendee]
+   [bbbg.meetup-user :as meetup-user]
+   [bbbg.util.core :as u]
+   [bbbg.util.spec :as u.s]
+   [clojure.data.csv :as csv]
+   [clojure.java.io :as io]
+   [clojure.spec.alpha :as s]
+   [clojure.string :as str]
+   [expound.alpha :as exp]))
+
+(def spreadsheet-column->key
+  {"Name" :name
+   "User ID" :user-id
+   "Title" :title
+   "Event Host" :event-host
+   "RSVP" :rsvp
+   "Guests" :guests
+   "RSVPed on" :rsvped-on
+   "Joined Group on" :joined-group-on
+   "URL of Member Profile" :member-profile-url})
+
+(defn read-attendees [f]
+  (with-open [reader (io/reader f)]
+    (let [[headers & rows] (-> reader (csv/read-csv :separator \tab))
+          keys (map spreadsheet-column->key headers)]
+      (doall
+       (->> rows
+            (map (partial zipmap keys))
+            (map (partial u/filter-kv (fn [k _] (some? k))))
+            (filter (partial some (comp seq val))))))))
+
+;;;
+
+(s/def ::imported-attendee
+  (s/keys :req [::attendee/meetup-name
+                ::meetup-user/id]))
+
+(def key->attendee-col
+  {:name ::attendee/meetup-name
+   :user-id ::meetup-user/id})
+
+(defn row-user-id->user-id [row-id]
+  (str/replace-first row-id "user " ""))
+
+(defn check-attendee [attendee]
+  ()
+  (if (s/valid? ::imported-attendee attendee)
+    attendee
+    (throw (ex-info
+            (str "Invalid imported attendee\n"
+                 (exp/expound-str ::imported-attendee attendee))
+            (assoc (s/explain-data ::imported-attendee attendee)
+                   ::s/failure
+                   ::s/assertion-failed)))))
+
+(defn row->attendee [r]
+  (u.s/assert!
+   ::imported-attendee
+   (update (u/keep-keys key->attendee-col r)
+           ::meetup-user/id row-user-id->user-id)))
+
+;;;
+
+(s/def ::imported-event-attendee
+  (s/keys :req [::event-attendee/rsvpd-attending?
+                ::attendee/id
+                ::event/id]))
+
+(def key->event-attendee-col
+  {:rsvp ::event-attendee/rsvpd-attending?})
+
+(defn row->event-attendee
+  [{event-id ::event/id :keys [meetup-id->attendee-id]} r]
+  (let [attendee-id (-> r :user-id row-user-id->user-id meetup-id->attendee-id)]
+    (u.s/assert!
+     ::imported-event-attendee
+     (-> (u/keep-keys key->event-attendee-col r)
+         (update ::event-attendee/rsvpd-attending?
+                 (partial = "Yes"))
+         (assoc ::event/id event-id
+                ::attendee/id attendee-id)))))
+
+;;;
+
+(defn import-attendees! [db event-id f]
+  (let [rows (read-attendees f)
+        attendees (db.attendee/upsert-all! db (map row->attendee rows))
+        meetup-id->attendee-id (into {}
+                                     (map (juxt ::meetup-user/id ::attendee/id))
+                                     attendees)]
+    (db.event-attendee/upsert-all!
+     db
+     (map (partial row->event-attendee
+                   {::event/id event-id
+                    :meetup-id->attendee-id meetup-id->attendee-id})
+          rows))
+    (count rows)))
+
+;;; Spreadsheet columns:
+;;;
+;;; Name
+;;; User ID
+;;; Title
+;;; Event Host
+;;; RSVP
+;;; Guests
+;;; RSVPed on
+;;; Joined Group on
+;;; URL of Member Profile
+;;; Have you been to one of our events before? Note, attendance at all events will require proof of vaccination until further notice.
+
+(comment
+  (def -filename- "/home/aspen/code/depot/users/aspen/bbbg/sample-data.tsv")
+  (def event-id #uuid "09f8fed6-7480-451b-89a2-bb4edaeae657")
+
+  (read-attendees -filename-)
+  (import-attendees! (:db bbbg.core/system) event-id -filename-)
+
+  )
diff --git a/users/aspen/bbbg/src/bbbg/meetup_user.clj b/users/aspen/bbbg/src/bbbg/meetup_user.clj
new file mode 100644
index 0000000000..945d681c6f
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/meetup_user.clj
@@ -0,0 +1,6 @@
+(ns bbbg.meetup-user
+  (:require [clojure.spec.alpha :as s]))
+
+(s/def ::id
+  (s/nilable
+   (s/and string? seq)))
diff --git a/users/aspen/bbbg/src/bbbg/styles.clj b/users/aspen/bbbg/src/bbbg/styles.clj
new file mode 100644
index 0000000000..a860ae6076
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/styles.clj
@@ -0,0 +1,407 @@
+;; -*- eval: (rainbow-mode) -*-
+(ns bbbg.styles
+  (:require
+   [garden.color :as color]
+   [garden.compiler :refer [compile-css]]
+   [garden.def :refer [defstyles]]
+   [garden.selectors
+    :refer [& active attr= descendant focus hover nth-child]]
+   [garden.stylesheet :refer [at-media]]
+   [garden.units :refer [px]]))
+
+(def black "#342e37")
+
+(def silver "#f9fafb")
+
+(def gray "#aaa")
+
+(def gray-light "#ddd")
+
+(def purple "#837aff")
+
+(def red "#c42348")
+
+(def orange "#fa824c")
+
+(def yellow "#FACB0F")
+
+(def blue "#026fb1")
+
+(def green "#87E24B")
+
+(def contextual-colors
+  {:success green
+   :info blue
+   :warning yellow
+   :error red})
+
+;;;
+
+(def content-width (px 1200))
+(def mobile-width (px 480))
+
+(defn desktop [& rules]
+  (at-media
+   {:screen true
+    :min-width content-width}
+   [:& rules]))
+
+(defn mobile [& rules]
+  (at-media
+   {:screen true
+    :max-width mobile-width}
+   [:& rules]))
+
+(defn not-mobile [& rules]
+  (at-media
+   {:screen true
+    :min-width mobile-width}
+   [:& rules]))
+
+
+;;;
+
+(defstyles global-nav
+  [:.global-nav
+   {:background-color silver}
+
+   [:>ul
+    {:display :flex
+     :flex-direction :row
+     :list-style :none}
+
+    (desktop
+     {:width content-width
+      :margin "0 auto"})]
+
+   [:a (descendant :.link-form (attr= "type" "submit"))
+    {:padding "1rem 1.5rem"
+     :display :block
+     :color black
+     :text-decoration :none}
+
+    [(& hover)
+     {:color blue}]]
+
+   [:li.active
+    {:font-weight "bold"
+     :border-bottom [["1px" "solid" black]]}]]
+
+  [:.spacer
+   {:flex 1}])
+
+(def link-conditional-styles
+  (list
+   [(& hover) (& active)
+    {:text-decoration :underline}]
+   [(& active)
+    {:color purple}]))
+
+(defstyles link-form
+  [:form.link-form
+   {:margin 0}
+   [(attr= "type" "submit")
+    {:background "none"
+     :border "none"
+     :padding 0
+     :color blue
+     :text-decoration :none
+     :cursor :pointer}
+    link-conditional-styles]])
+
+(defstyles search-form
+  [:.search-form
+   {:display :flex
+    :flex-direction :row
+    :width "100%"}
+
+   [:>*+*
+    {:margin-left "0.75rem"}]
+
+   [:input
+    {:flex 1}]
+
+   [(attr= "type" "submit")
+    {:flex 0}]])
+
+(defstyles forms
+  (let [text-input-types
+        #{"date"
+          "datetime-local"
+          "email"
+          "month"
+          "number"
+          "password"
+          "search"
+          "tel"
+          "text"
+          "time"
+          "url"
+          "week"}
+        each-text-type (fn [& rules]
+                         (into
+                          []
+                          (concat
+                           (map (comp & (partial attr= "type"))
+                                text-input-types)
+                           rules)))]
+    (each-text-type
+     {:width "100%"
+      :display "block"
+      :padding "0.6rem 0.75rem"
+      :border [["1px" "solid" gray-light]]
+      :border-radius "3px"
+      :box-shadow [["inset" 0 "1px" "5px" "rgba(0,0,0,0.075)"]]
+      :transition "border-color 150ms"
+      :background "none"}
+     [(& focus)
+      {:outline "none"
+       :border-color purple}]))
+
+  [(attr= "type" "submit") :button :.button
+   {:background-color (color/lighten blue 30)
+    :padding "0.6rem 0.75rem"
+    :border-radius "3px"
+    :border [[(px 1) "solid" (color/lighten blue 30)]]
+    :cursor :pointer
+    :display :inline-block}
+
+   [(& hover)
+    {:border-color blue
+     :text-decoration :none
+     :box-shadow [[0 "1px" "5px" "rgba(0,0,0,0.075)"]]}
+    [(:a &)
+     {:text-decoration :none}]]
+
+   [(& active)
+    {:background-color blue
+     :color :white
+     :box-shadow :none}
+    [(& :a)
+     {:text-decoration :none}]]
+
+   (for [[context color] contextual-colors]
+     [(& (keyword (str "." (name context))))
+      {:background-color (color/lighten color 30)
+       :border-color (color/lighten color 30)
+       :color black}
+
+      [(& hover)
+       {:border-color color}]])]
+
+  [:label
+   {:font-weight 600
+    :width "100%"}
+
+   [:input
+    {:font-weight "initial"
+     :margin-top "0.3rem"}]]
+
+  [:.form-group
+   {:display :flex
+    :margin-bottom "0.8rem"
+    :flex-direction :column}
+
+   [(attr= "type" "submit")
+    {:text-align :right
+     :align-self :flex-end}]])
+
+(defstyles tables
+  [:table
+   {:width "100%"
+    :border-collapse "collapse"}]
+
+  [:th
+   {:text-align "left"}]
+
+  [:td :th
+   {:padding "0.75rem 1rem"
+    :border-spacing 0
+    :border "none"}]
+
+  [:tr
+   {:border-spacing 0
+    :border "none"}
+   [(& (nth-child :even))
+    {:background-color silver}]])
+
+(defstyles flash
+  [:.flash-messages
+   {:max-width "800px"
+    :margin "1rem auto"}
+
+   (at-media
+    {:screen true
+     :max-width "800px"}
+    [:&
+     {:margin-left "1rem"
+      :margin-right "1rem"}])]
+
+  [:.flash-message
+   {:padding "1rem 1.5rem"
+    :border "1px solid"
+    :margin-bottom "1rem"}]
+
+  (for [[context color] contextual-colors]
+    [(& (keyword (str ".flash-" (name context))))
+     {:border-color color
+      :background-color (color/lighten color 30)
+      :border-radius "3px"}]))
+
+(defstyles home-page
+  [:.home-page
+   {:display :flex
+    :flex 1
+    :justify-content :center
+    :align-items :center}
+   [:.signup-form-link
+    {:display :block
+     :border [["1px" :solid blue]]
+     :border-radius "3px"
+     :color black
+     :font-size "2rem"
+     :background-color (color/lighten blue 50)
+     :margin-left "auto"
+     :margin-right "auto"
+     :padding "2rem"}
+    (desktop
+     {:padding "5rem"
+      :margin-left 0
+      :margin-right 0})
+    [(& hover) (& active)
+     {:text-decoration :none}]
+    [(& active)
+     {:background-color (color/lighten blue 30)}]]])
+
+(defstyles signup-page
+  [:.signup-page
+   {:margin "1rem"}
+   (desktop
+    {:width content-width
+     :margin "1rem auto"})]
+
+  [:#signup-form
+   {:display :flex
+    :flex-direction :row
+    :width "100%"}
+
+   [:*
+    {:flex 1}]
+
+   [:*+*
+    {:margin-left "1rem"}]
+
+   [(attr= "type" "submit")
+    {:flex 0}]]
+
+  [:#attendees-list
+   {:list-style "none"
+    :overflow-y "auto"
+    :height "calc(100vh - 8.32425rem)"}
+
+   [:li
+    {:padding "0.75rem 1rem"
+     :margin "0.35rem 0"
+     :border-radius "3px"
+     :background-color silver}]]
+
+  [:.no-attendees
+   {:text-align "center"
+    :margin-top "6rem"}
+
+   [:.button
+    {:margin-top "0.5rem"}]]
+
+  [:.hidden
+   {:display :none}])
+
+(defstyles attendees
+  [:.attendee-checks-form
+   {:max-width "340px"
+    :margin-left "auto"
+    :margin-right "auto"}]
+
+  [:.attendees
+   (mobile
+    {:display :block}
+
+    [:thead {:display :none}]
+    [:tbody :tr :td
+     {:display :block}]
+
+    [:tr
+     {:background-color silver
+      :padding "0.5rem 0.8rem"
+      :margin-bottom "1rem"
+      :border-radius "3px"}]
+    [:td {:padding "0.2rem 0"}]
+
+    [:.attendee-name
+     {:font-weight "bold"
+      :margin-bottom "0.9rem"}])
+
+   (not-mobile
+    [:.mobile-label
+     {:display :none}])])
+
+(defstyles events
+  [:.events-list
+   {:margin-top "1rem"}
+
+   [:li
+    {:margin-bottom "1rem"}]])
+
+(defstyles styles
+  forms
+  tables
+  global-nav
+  link-form
+  search-form
+  flash
+  home-page
+  signup-page
+  attendees
+  events
+
+  [:body
+   {:color black}]
+
+  [:.content
+   {:display :flex
+    :flex-direction :column
+    :height "100%"
+    :width "100%"}]
+
+  [:.page
+   {:margin-top "1rem"
+    :margin-left "1rem"
+    :margin-right "1rem"}
+
+   (desktop
+    {:width content-width
+     :margin-left "auto"
+     :margin-right "auto"})]
+
+  [:.page-header
+   {:display :flex
+    :flex-wrap :wrap
+    :padding-bottom "0.7rem"
+    :margin-bottom "1rem"
+    :border-bottom [["1px" "solid" silver]]
+    :align-items :center}
+
+   [:*+*
+    {:margin-left "0.5rem"}]
+
+   [:form
+    {:margin-block-end 0}]]
+
+  [(attr= "role" "button")
+   {:cursor :pointer}]
+
+  [:a {:color blue
+       :text-decoration :none}
+   link-conditional-styles])
+
+(def stylesheet
+  (compile-css styles))
diff --git a/users/aspen/bbbg/src/bbbg/user.clj b/users/aspen/bbbg/src/bbbg/user.clj
new file mode 100644
index 0000000000..f48c8d7338
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/user.clj
@@ -0,0 +1,8 @@
+(ns bbbg.user
+  (:require [clojure.spec.alpha :as s]))
+
+(s/def ::id uuid?)
+
+(s/def ::discord-id string?)
+
+(s/def ::username string?)
diff --git a/users/aspen/bbbg/src/bbbg/util/core.clj b/users/aspen/bbbg/src/bbbg/util/core.clj
new file mode 100644
index 0000000000..d458aa5592
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/util/core.clj
@@ -0,0 +1,138 @@
+(ns bbbg.util.core
+  (:require
+   [clojure.java.shell :refer [sh]]
+   [clojure.string :as str])
+  (:import
+   java.util.UUID))
+
+(defn remove-nils
+  "Remove all keys with nil values from m"
+  [m]
+  (let [!m (transient m)]
+    (doseq [[k v] m]
+      (when (nil? v)
+        (dissoc! !m k)))
+    (persistent! !m)))
+
+
+(defn alongside
+  "Apply a pair of functions to the first and second element of a two element
+  vector, respectively. The two argument form partially applies, such that:
+
+  ((alongside f g) xy) โ‰ก (alongside f g xy)
+
+  This is equivalent to (***) in haskell's Control.Arrow"
+  ([f g] (partial alongside f g))
+  ([f g [x y]] [(f x) (g y)]))
+
+(defn map-kv
+  "Map a pair of functions over the keys and values of a map, respectively.
+  Preserves metadata on the incoming map.
+  The two argument form returns a transducer that yields map-entries.
+
+  (partial map-kv identity identity) โ‰ก identity"
+  ([kf vf]
+   (map (fn [[k v]]
+          ;; important to return a map-entry here so that callers down the road
+          ;; can use `key` or `val`
+          (first {(kf k) (vf v)}))))
+  ([kf vf m]
+   (into (empty m) (map-kv kf vf) m)))
+
+(defn filter-kv
+  "Returns a map containing the elements of m for which (f k v) returns logical
+  true. The one-argument form returns a transducer that yields map entries"
+  ([f] (filter (partial apply f)))
+  ([f m]
+   (into (empty m) (filter-kv f) m)))
+
+(defn map-keys
+  "Map f over the keys of m. Preserves metadata on the incoming map. The
+  one-argument form returns a transducer that yields map-entries."
+  ([f] (map-kv f identity))
+  ([f m] (map-kv f identity m)))
+
+(defn keep-keys
+  "Map f over the keys of m, keeping only those entries for which f does not
+  return nil. Preserves metadata on the incoming map. The one-argument form
+  returns a transducer that yields map-entries."
+  ([f] (keep (fn [[k v]] (when-let [k' (f k)]
+                          (first {k' v})))))
+  ([f m] (into (empty m) (keep-keys f) m)))
+
+(defn map-vals
+  "Map f over the values of m. Preserves metadata on the incoming map. The
+  one-argument form returns a transducer that yields map-entries."
+  ([f] (map-kv identity f))
+  ([f m] (map-kv identity f m)))
+
+(defn map-keys-recursive [f x]
+  (cond
+    (map? x) (map-kv f (partial map-keys-recursive f) x)
+    (sequential? x) (map (partial map-keys-recursive f) x)
+    :else x))
+
+(defn denamespace [x]
+  (if (keyword? x)
+    (keyword (name x))
+    (map-keys-recursive denamespace x)))
+
+(defn reverse-merge
+  "Like `clojure.core/merge`, except duplicate keys from maps earlier in the
+  argument list take precedence
+
+    => (merge {:x 1} {:x 2})
+    {:x 2}
+
+    => (sut/reverse-merge {:x 1} {:x 2})
+    {:x 1}"
+  [& ms]
+  (apply merge (reverse ms)))
+
+(defn invert-map
+  "Invert the keys and vals of m. Behavior with duplicate vals is undefined.
+
+  => (sut/invert-map {:x 1 :y 2})
+  {1 :x 2 :y}"
+  [m]
+  (into {} (map (comp vec reverse)) m))
+
+(defn ->uuid
+  "Converts x to uuid, returning nil if x is nil or empty"
+  [x]
+  (cond
+    (not x) nil
+    (uuid? x) x
+    (and (string? x) (seq x))
+    (UUID/fromString x)))
+
+(defn key-by
+  "Create a map from a seq obtaining keys via f
+
+    => (sut/key-by :x [{:x 1} {:x 2 :y 3}])
+    {1 {:x 1}, 2 {:x 2 :y 3}}"
+  [f l]
+  (into {} (map (juxt f identity)) l))
+
+(defn distinct-by
+  "Like clojure.core/distinct, but can take a function f by which
+  distinctiveness is calculated"
+  [distinction-fn coll]
+  (let [step (fn step [xs seen]
+               (lazy-seq
+                ((fn [[f :as xs] seen]
+                   (when-let [s (seq xs)]
+                     (if (contains? seen (distinction-fn f))
+                       (recur (rest s) seen)
+                       (cons f (step (rest s) (conj seen (distinction-fn f)))))))
+                 xs seen)))]
+    (step coll #{})))
+
+(defn pass [n]
+  (let [{:keys [exit out err]} (sh "pass" n)]
+    (if (= 0 exit)
+      (str/trim out)
+      (throw (Exception.
+              (format "`pass` command failed\nStandard output:%s\nStandard Error:%s"
+                      out
+                      err))))))
diff --git a/users/aspen/bbbg/src/bbbg/util/dev_secrets.clj b/users/aspen/bbbg/src/bbbg/util/dev_secrets.clj
new file mode 100644
index 0000000000..88f1b50caa
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/util/dev_secrets.clj
@@ -0,0 +1,59 @@
+(ns bbbg.util.dev-secrets
+  "Utility library for loading secrets during development from multiple
+  backends.
+
+  # Supported backends
+
+  - [Pass][0] (the default)
+
+        (bbbg.util.dev-secrets/set-backend! :pass)
+
+    Loads all secrets by shelling out to `pass <secret-name>`
+
+    [0]: https://www.passwordstore.org/
+
+  - Directory
+
+        (bbbg.util.dev-secrets/set-backend! [:dir \"/path/to/secret/directory\"])
+
+     Loads all secrets by reading the secret name as a (plaintext!) file rooted
+     at the given directory"
+  (:require [bbbg.util.core :as u]
+            [clojure.string :as str]
+            [clojure.java.io :as io]))
+
+(def ^:dynamic *secret-backend* :pass)
+
+(defn set-backend!
+  "Change the default secret-backend"
+  [backend]
+  (alter-var-root #'*secret-backend* (constantly backend)))
+
+(defmulti ^:private load-secret
+  (fn [backend _secret]
+    (if (coll? backend) (first backend) backend)))
+
+(defmethod load-secret :pass [_ secret]
+  (u/pass secret))
+
+(defmethod load-secret :dir [[_ dir] secret]
+  (str/trim (slurp (io/file dir secret))))
+
+(defn secret
+  "Load the value for the given `secret-name' from the currently selected
+  backend"
+  [secret-name]
+  (load-secret *secret-backend* secret-name))
+
+(comment
+  (secret "bbbg/discord-client-id")
+
+  (binding [*secret-backend* [:dir "/tmp/bbbg-secrets"]]
+    (secret "bbbg/discord-client-id"))
+
+  (set-backend! [:dir "/tmp/bbbg-secrets"])
+  (secret "bbbg/discord-client-id")
+
+  (set-backend! :pass)
+  (secret "bbbg/discord-client-id")
+  )
diff --git a/users/aspen/bbbg/src/bbbg/util/display.clj b/users/aspen/bbbg/src/bbbg/util/display.clj
new file mode 100644
index 0000000000..40716632a3
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/util/display.clj
@@ -0,0 +1,23 @@
+(ns bbbg.util.display
+  (:require
+   [bbbg.util.time :as t])
+  (:import
+   [java.time.format DateTimeFormatter FormatStyle]))
+
+(defn format-date
+  ([d] (format-date d FormatStyle/MEDIUM))
+  ([d ^FormatStyle format-style]
+   (let [formatter (DateTimeFormatter/ofLocalizedDate format-style)]
+     (.format (t/->LocalDate d) formatter))))
+
+(defn pluralize
+  ([n sing plur]
+   (str (or n 0) " " (if (= 1 n) sing plur)))
+  ([n sing]
+   (pluralize n sing (str sing "s"))))
+
+(comment
+  (format-date #inst "2021-12-19T05:00:00.000-00:00")
+  (format-date #inst "2021-12-19T05:00:00.000-00:00"
+               FormatStyle/FULL)
+  )
diff --git a/users/aspen/bbbg/src/bbbg/util/spec.clj b/users/aspen/bbbg/src/bbbg/util/spec.clj
new file mode 100644
index 0000000000..89ac926699
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/util/spec.clj
@@ -0,0 +1,16 @@
+(ns bbbg.util.spec
+  (:require [expound.alpha :as exp]
+            [clojure.spec.alpha :as s]))
+
+(defn assert!
+  ([spec s] (assert! "Spec assertion failed" spec s))
+  ([message spec x]
+   (if (s/valid? spec x)
+     x
+     (throw (ex-info
+             (str message
+                  "\n"
+                  (exp/expound-str spec x))
+             (assoc (s/explain-data spec x)
+                    ::s/failure
+                    ::s/assertion-failed))))))
diff --git a/users/aspen/bbbg/src/bbbg/util/sql.clj b/users/aspen/bbbg/src/bbbg/util/sql.clj
new file mode 100644
index 0000000000..988959fd06
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/util/sql.clj
@@ -0,0 +1,5 @@
+(ns bbbg.util.sql
+  (:require [honeysql.core :as hsql]))
+
+(defn count-where [cond]
+  (hsql/call :count (hsql/call :case cond #sql/raw "1" :else nil)))
diff --git a/users/aspen/bbbg/src/bbbg/util/time.clj b/users/aspen/bbbg/src/bbbg/util/time.clj
new file mode 100644
index 0000000000..0278f89f5e
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/util/time.clj
@@ -0,0 +1,152 @@
+(ns bbbg.util.time
+  "Utilities for dealing with date/time"
+  (:require [clojure.spec.alpha :as s]
+            [clojure.test.check.generators :as gen]
+            [java-time :as jt])
+  (:import [java.time
+            LocalDateTime LocalTime OffsetDateTime ZoneId ZoneOffset
+            LocalDate Year]
+           [java.time.format DateTimeFormatter DateTimeParseException]
+           java.util.Calendar
+           org.apache.commons.lang3.time.DurationFormatUtils))
+
+(set! *warn-on-reflection* true)
+
+(defprotocol ToOffsetDateTime
+  (->OffsetDateTime [this]
+    "Coerces its argument to a `java.time.OffsetDateTime`"))
+
+(extend-protocol ToOffsetDateTime
+  OffsetDateTime
+  (->OffsetDateTime [odt] odt)
+
+  java.util.Date
+  (->OffsetDateTime [d]
+    (-> d
+        .toInstant
+        (OffsetDateTime/ofInstant (ZoneId/of "UTC")))))
+
+(defprotocol ToLocalTime (->LocalTime [this]))
+(extend-protocol ToLocalTime
+  LocalTime
+  (->LocalTime [lt] lt)
+
+  java.sql.Time
+  (->LocalTime [t]
+    (let [^Calendar cal (doto (Calendar/getInstance)
+                          (.setTime t))]
+      (LocalTime/of
+       (.get cal Calendar/HOUR_OF_DAY)
+       (.get cal Calendar/MINUTE)
+       (.get cal Calendar/SECOND))))
+
+  java.util.Date
+  (->LocalTime [d]
+    (-> d .toInstant (LocalTime/ofInstant (ZoneId/of "UTC")))))
+
+(defn local-time? [x] (satisfies? ToLocalTime x))
+(s/def ::local-time
+  (s/with-gen local-time?
+    #(gen/let [hour (gen/choose 0 23)
+               minute (gen/choose 0 59)
+               second (gen/choose 0 59)
+               nanos gen/nat]
+       (LocalTime/of hour minute second nanos))))
+
+(defprotocol ToLocalDate (->LocalDate [this]))
+(extend-protocol ToLocalDate
+  LocalDate
+  (->LocalDate [ld] ld)
+
+  java.sql.Date
+  (->LocalDate [sd] (.toLocalDate sd))
+
+  java.util.Date
+  (->LocalDate [d]
+    (-> d .toInstant (LocalDate/ofInstant (ZoneId/of "UTC")))))
+
+(defn local-date? [x] (satisfies? ToLocalDate x))
+(s/def ::local-date
+  (s/with-gen local-date?
+    #(gen/let [year (gen/choose Year/MIN_VALUE Year/MAX_VALUE)
+               day (gen/choose 1 (if (.isLeap (Year/of year))
+                                   366
+                                   365))]
+       (LocalDate/ofYearDay year day))))
+
+(extend-protocol Inst
+  OffsetDateTime
+  (inst-ms* [zdt]
+    (inst-ms* (.toInstant zdt)))
+
+  LocalDateTime
+  (inst-ms* [^LocalDateTime ldt]
+    (inst-ms* (.toInstant ldt ZoneOffset/UTC))))
+
+(let [formatter DateTimeFormatter/ISO_OFFSET_DATE_TIME]
+  (defn ^OffsetDateTime parse-iso-8601
+    "Parse s as an iso-8601 datetime, returning nil if invalid"
+    [^String s]
+    (try
+      (OffsetDateTime/parse s formatter)
+      (catch DateTimeParseException _ nil)))
+
+  (defn format-iso-8601
+    "Format dt, which can be an OffsetDateTime or java.util.Date, as iso-8601"
+    [dt]
+    (some->> dt ->OffsetDateTime (.format formatter))))
+
+(let [formatter DateTimeFormatter/ISO_TIME]
+  (defn parse-iso-8601-time
+    "Parse s as an iso-8601 timestamp, returning nil if invalid"
+    [^String s]
+    (try
+      (LocalTime/parse s formatter)
+      (catch DateTimeParseException _ nil)))
+
+  (defn format-iso-8601-time
+    "Format lt, which can be a LocalTime or java.sql.Time, as an iso-8601
+    formatted timestamp without a date."
+    [lt]
+    (some->> lt ->LocalTime (.format formatter))))
+
+(defmethod print-dup LocalTime [t w]
+  (binding [*out* w]
+    (print "#local-time ")
+    (print (str "\"" (format-iso-8601-time t) "\""))))
+
+(defmethod print-method LocalTime [t w]
+  (print-dup t w))
+
+(let [formatter DateTimeFormatter/ISO_LOCAL_DATE]
+  (defn parse-iso-8601-date
+    "Parse s as an iso-8601 date, returning nil if invalid"
+    [^String s]
+    (try
+      (LocalDate/parse s formatter)
+      (catch DateTimeParseException _ nil)))
+
+  (defn format-iso-8601-date
+    "Format lt, which can be a LocalDate, as an iso-8601 formatted date without
+    a timestamp."
+    [lt]
+    (some->> lt ->LocalDate (.format formatter))))
+
+(defmethod print-dup LocalDate [t w]
+  (binding [*out* w]
+    (print "#local-date ")
+    (print (str "\"" (format-iso-8601-date t) "\""))))
+
+(defmethod print-method LocalDate [t w]
+  (print-dup t w))
+
+
+(defn ^String human-format-duration
+  "Human-format the given duration"
+  [^java.time.Duration dur]
+  (DurationFormatUtils/formatDurationWords (Math/abs (.toMillis dur)) true true))
+
+(comment
+  (human-format-duration (jt/hours 5))
+  (human-format-duration (jt/plus (jt/hours 5) (jt/minutes 7)))
+  )
diff --git a/users/aspen/bbbg/src/bbbg/views/flash.clj b/users/aspen/bbbg/src/bbbg/views/flash.clj
new file mode 100644
index 0000000000..a44b21d4cb
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/views/flash.clj
@@ -0,0 +1,39 @@
+(ns bbbg.views.flash
+  (:require [clojure.spec.alpha :as s]))
+
+(s/def :flash/type #{:success :error :warning :info})
+(s/def :flash/message string?)
+(s/def ::flash (s/keys :req [:flash/type :flash/message]))
+(s/fdef add-flash :args (s/cat :resp map? :flash ::flash) :ret map?)
+
+;;;
+
+(def ^:dynamic *flash* nil)
+
+(defn wrap-page-flash [handler]
+  (fn
+    ([request]
+     (binding [*flash* (:flash request)]
+       (handler request)))
+    ([request respond raise]
+     (binding [*flash* (:flash request)]
+       (handler request respond raise)))))
+
+(defn add-flash [resp flash]
+  (update-in resp [:flash :flash/messages] conj flash))
+
+(defn render-flash
+  ([] (render-flash *flash*))
+  ([flash]
+   (when-some [messages (not-empty (:flash/messages flash))]
+     [:ul.flash-messages
+      (for [message messages]
+        [:li.flash-message
+         {:class (str "flash-" (-> message :flash/type name))}
+         (:flash/message message)])])))
+
+(def test-flash
+  {:flash/messages
+   (for [type [:success :error :warning :info]]
+     {:flash/type type
+      :flash/message (str "Sample " type " message")})})
diff --git a/users/aspen/bbbg/src/bbbg/web.clj b/users/aspen/bbbg/src/bbbg/web.clj
new file mode 100644
index 0000000000..f9755577a5
--- /dev/null
+++ b/users/aspen/bbbg/src/bbbg/web.clj
@@ -0,0 +1,140 @@
+(ns bbbg.web
+  (:require
+   [bbbg.discord.auth :as discord.auth :refer [wrap-discord-auth]]
+   [bbbg.handlers.attendee-checks :as attendee-checks]
+   [bbbg.handlers.attendees :as attendees]
+   [bbbg.handlers.core :refer [wrap-current-uri wrap-dynamic-auth]]
+   [bbbg.handlers.events :as events]
+   [bbbg.handlers.home :as home]
+   [bbbg.handlers.signup-form :as signup-form]
+   [bbbg.styles :refer [stylesheet]]
+   [bbbg.util.core :as u]
+   [bbbg.views.flash :refer [wrap-page-flash]]
+   [cambium.core :as log]
+   clj-time.coerce
+   [clojure.java.io :as io]
+   [clojure.spec.alpha :as s]
+   [com.stuartsierra.component :as component]
+   [compojure.core :refer [GET routes]]
+   [config.core :refer [env]]
+   [org.httpkit.server :as http-kit]
+   [ring.logger :refer [wrap-with-logger]]
+   [ring.middleware.flash :refer [wrap-flash]]
+   [ring.middleware.keyword-params :refer [wrap-keyword-params]]
+   [ring.middleware.multipart-params :refer [wrap-multipart-params]]
+   [ring.middleware.params :refer [wrap-params]]
+   [ring.middleware.resource :refer [wrap-resource]]
+   [ring.middleware.session :refer [wrap-session]]
+   [ring.middleware.session.cookie :refer [cookie-store]]
+   [ring.util.response :refer [content-type response]])
+  (:import
+   java.util.Base64))
+
+(s/def ::port pos-int?)
+
+(s/def ::cookie-secret
+  (s/and bytes? #(= 16 (count %))))
+
+(s/def ::config
+  (s/merge
+   (s/keys :req [::port]
+           :opt [::cookie-secret
+                 ::base-url])
+   ::discord.auth/config))
+
+(s/fdef make-server
+  :args (s/cat :config ::config))
+
+
+(defn- string->cookie-secret [raw]
+  (s/assert
+   ::cookie-secret
+   (when raw
+     (.decode (Base64/getDecoder)
+              (.getBytes raw "UTF-8")))))
+
+(defn env->config []
+  (s/assert
+   ::config
+   (u/remove-nils
+    (merge
+     {::port (:port env 8888)
+      ::cookie-secret (some-> env :cookie-secret string->cookie-secret)
+      ::base-url (:base-url env)}
+     (discord.auth/env->config)))))
+
+(defn dev-config []
+  (s/assert
+   ::config
+   (merge
+    {::port 8888
+     ::cookie-secret (into-array Byte/TYPE (repeat 16 0))}
+    (discord.auth/dev-config))))
+
+;;;
+
+(defn app-routes [env]
+  (routes
+   (GET "/main.css" []
+     (-> (response
+          (str
+           "\n/* begin base.css */\n"
+           (slurp (io/resource "base.css"))
+           "\n/* end base.css */\n"
+           stylesheet))
+         (content-type "text/css")))
+
+   (attendees/attendees-routes env)
+   (attendee-checks/attendee-checks-routes env)
+   (signup-form/signup-form-routes env)
+   (events/events-routes env)
+   (home/home-routes env)))
+
+(defn middleware [app env]
+  (-> app
+      (wrap-resource "public")
+      (wrap-with-logger
+       {:log-fn
+        (fn [{:keys [level throwable message]}]
+          (log/log level {} throwable message))})
+      wrap-current-uri
+      wrap-dynamic-auth
+      (wrap-discord-auth env)
+      wrap-keyword-params
+      wrap-multipart-params
+      wrap-params
+      wrap-page-flash
+      wrap-flash
+      (wrap-session {:store (cookie-store
+                             {:key (:cookie-secret env)
+                              :readers {'clj-time/date-time
+                                        clj-time.coerce/from-string}})
+                     :cookie-attrs {:same-site :lax}})))
+
+(defn handler [env]
+  (-> (app-routes env)
+      (middleware env)))
+
+(defrecord WebServer [port cookie-secret db]
+  component/Lifecycle
+  (start [this]
+    (assoc this
+           ::shutdown-fn
+           (http-kit/run-server
+            (fn [r] ((handler this) r))
+            {:port port})))
+  (stop [this]
+    (if-let [shutdown-fn (::shutdown-fn this)]
+      (do (shutdown-fn :timeout 100)
+          (dissoc this ::shutdown-fn))
+      this)))
+
+(defn make-server [{::keys [port cookie-secret]
+                    :as env}]
+  (component/using
+   (map->WebServer
+    (merge
+     {:port port
+      :cookie-secret cookie-secret}
+     env))
+   [:db]))
diff --git a/users/aspen/bbbg/test/bbbg/meetup/import_test.clj b/users/aspen/bbbg/test/bbbg/meetup/import_test.clj
new file mode 100644
index 0000000000..d7d698a58c
--- /dev/null
+++ b/users/aspen/bbbg/test/bbbg/meetup/import_test.clj
@@ -0,0 +1,7 @@
+(ns bbbg.meetup.import-test
+  (:require [bbbg.meetup.import :as sut]
+            [clojure.test :refer :all]))
+
+(deftest test-row-user-id->user-id
+  (is (= "246364067" (sut/row-user-id->user-id "user 246364067")))
+  (is (= "246364067" (sut/row-user-id->user-id "246364067"))))
diff --git a/users/aspen/bbbg/tf.nix b/users/aspen/bbbg/tf.nix
new file mode 100644
index 0000000000..e6ea69dfd0
--- /dev/null
+++ b/users/aspen/bbbg/tf.nix
@@ -0,0 +1,96 @@
+{ depot, ... }:
+
+let
+  inherit (depot.users.aspen)
+    terraform
+    ;
+
+in
+terraform.workspace "bbbg"
+{
+  plugins = (p: with p; [
+    aws
+    cloudflare
+  ]);
+}
+{
+  machine = terraform.nixosMachine {
+    name = "bbbg";
+    instanceType = "t3a.small";
+    rootVolumeSizeGb = 250;
+    extraIngressPorts = [ 80 443 ];
+    configuration = { pkgs, lib, config, depot, ... }: {
+      imports = [
+        ./module.nix
+        "${depot.third_party.agenix.src}/modules/age.nix"
+      ];
+
+      services.openssh.enable = true;
+
+      services.nginx = {
+        enable = true;
+        recommendedTlsSettings = true;
+        recommendedOptimisation = true;
+        recommendedGzipSettings = true;
+        recommendedProxySettings = true;
+      };
+
+      networking.firewall.enable = false;
+
+      programs.zsh.enable = true;
+
+      users.users.grfn = {
+        isNormalUser = true;
+        initialPassword = "password";
+        extraGroups = [
+          "wheel"
+          "networkmanager"
+          "audio"
+          "docker"
+        ];
+        shell = pkgs.zsh;
+        openssh.authorizedKeys.keys = [
+          depot.users.aspen.keys.main
+        ];
+      };
+
+      security.sudo.extraRules = [{
+        groups = [ "wheel" ];
+        commands = [{ command = "ALL"; options = [ "NOPASSWD" ]; }];
+      }];
+
+      nix.gc = {
+        automatic = true;
+        dates = "weekly";
+        options = "--delete-older-than 30d";
+      };
+
+      age.secrets = {
+        bbbg.file =
+          depot.users.aspen.secrets."bbbg.age";
+      };
+
+      services.bbbg.enable = true;
+      services.bbbg.database.enable = true;
+      services.bbbg.proxy.enable = true;
+      services.bbbg.domain = "bbbg.gws.fyi";
+
+      security.acme.defaults.email = "root@gws.fyi";
+      security.acme.acceptTerms = true;
+    };
+  };
+
+  dns = {
+    data.cloudflare_zone.gws-fyi = {
+      name = "gws.fyi";
+    };
+
+    resource.cloudflare_record.bbbg = {
+      zone_id = "\${data.cloudflare_zone.gws-fyi.id}";
+      name = "bbbg";
+      type = "A";
+      value = "\${aws_instance.bbbg_machine.public_ip}";
+      proxied = false;
+    };
+  };
+}
diff --git a/users/grfn/emacs.d/+bindings.el b/users/aspen/emacs.d/+bindings.el
index 05c83435a5..0bcc922635 100644
--- a/users/grfn/emacs.d/+bindings.el
+++ b/users/aspen/emacs.d/+bindings.el
@@ -119,6 +119,9 @@ private/hlissner/snippets."
 
 (evil-set-command-property 'flycheck-next-error :repeat nil)
 (evil-set-command-property 'flycheck-prev-error :repeat nil)
+(evil-set-command-property 'flycheck-previous-error :repeat nil)
+(evil-set-command-property 'smerge-next :repeat nil)
+(evil-set-command-property 'smerge-prev :repeat nil)
 
 ;;;
 
@@ -376,7 +379,7 @@ private/hlissner/snippets."
      :desc "Slack Unreads"         :n  "u" #'slack-select-unread-rooms
      :desc "Slack Threads"         :n  "r" #'slack-all-threads
 
-     :desc "Email"                 :n "m" #'notmuch-tree-jump-search
+     :desc "Email"                 :n "m" #'notmuch-jump-search
 
      (:desc "ERC" :prefix "e"
        :desc "Channel" :n "c" #'erc-switch-to-buffer)
@@ -809,7 +812,6 @@ private/hlissner/snippets."
  ;; Rust
  (:after rust
    (:map rust-mode-map
-     "K"     #'racer-describe
      "g RET" #'cargo-process-test))
 
  ;; Elixir
@@ -1378,11 +1380,12 @@ If invoked with a prefix ARG eval the expression after inserting it"
      :n "g SPC" 'cider-eval-buffer
      :n "g \\"  'cider-switch-to-repl-buffer
      :n "K"     'cider-doc
-     :n "g K"   'cider-grimoire
+     :n "g K"   'cider-apropos
      :n "g d"   'cider-find-dwim
      :n "C-w ]" 'cider-find-dwim-other-window
      ;; :n "g RET" 'cider-test-run-ns-tests
      :n "g RET" 'grfn/run-clj-or-cljs-test
+     :n "g r" #'cljr-rename-symbol
 
      "C-c C-r r" 'cljr-add-require-to-ns
      "C-c C-r i" 'cljr-add-import-to-ns
@@ -1424,4 +1427,13 @@ If invoked with a prefix ARG eval the expression after inserting it"
    (:localleader
     :n "a" #'lsp-execute-code-action))
 
- )
+ (:map prolog-mode-map
+  :n "g SPC" #'prolog-compile-buffer
+  :n "g \\" #'run-prolog)
+
+ (:map tuareg-mode-map
+  :n "g RET" (ฮป! () (compile "dune build @@runtest"))
+  :n "g SPC" #'dune-promote
+  :n "g \\" #'utop
+  :n "g y" #'merlin-locate-type
+  "C-c C-f" (ฮป! () (compile "dune fmt"))))
diff --git a/users/grfn/emacs.d/+commands.el b/users/aspen/emacs.d/+commands.el
index 518f185cb9..518f185cb9 100644
--- a/users/grfn/emacs.d/+commands.el
+++ b/users/aspen/emacs.d/+commands.el
diff --git a/users/grfn/emacs.d/+private.el.gpg b/users/aspen/emacs.d/+private.el.gpg
index 6273c67d6e..6273c67d6e 100644
--- a/users/grfn/emacs.d/+private.el.gpg
+++ b/users/aspen/emacs.d/+private.el.gpg
Binary files differdiff --git a/users/grfn/emacs.d/.gitignore b/users/aspen/emacs.d/.gitignore
index 1fd0e39887..1fd0e39887 100644
--- a/users/grfn/emacs.d/.gitignore
+++ b/users/aspen/emacs.d/.gitignore
diff --git a/users/grfn/emacs.d/autoload/evil.el b/users/aspen/emacs.d/autoload/evil.el
index 319c93c05e..319c93c05e 100644
--- a/users/grfn/emacs.d/autoload/evil.el
+++ b/users/aspen/emacs.d/autoload/evil.el
diff --git a/users/grfn/emacs.d/autoload/hlissner.el b/users/aspen/emacs.d/autoload/hlissner.el
index 87b2236d12..87b2236d12 100644
--- a/users/grfn/emacs.d/autoload/hlissner.el
+++ b/users/aspen/emacs.d/autoload/hlissner.el
diff --git a/users/grfn/emacs.d/clocked-in-elt.el b/users/aspen/emacs.d/clocked-in-elt.el
index be4161441d..be4161441d 100644
--- a/users/grfn/emacs.d/clocked-in-elt.el
+++ b/users/aspen/emacs.d/clocked-in-elt.el
diff --git a/users/grfn/emacs.d/clojure.el b/users/aspen/emacs.d/clojure.el
index 9dc1a68323..f001a3e12b 100644
--- a/users/grfn/emacs.d/clojure.el
+++ b/users/aspen/emacs.d/clojure.el
@@ -10,6 +10,7 @@
 
 (defun +grfn/clojure-setup ()
   ;; (flycheck-select-checker 'clj-kondo)
+  (require 'flycheck)
   (push 'clojure-cider-kibit flycheck-disabled-checkers)
   (push 'clojure-cider-eastwood flycheck-disabled-checkers)
   (push 'clojure-cider-typed flycheck-disabled-checkers)
diff --git a/users/grfn/emacs.d/company-sql.el b/users/aspen/emacs.d/company-sql.el
index e623aa2de1..e623aa2de1 100644
--- a/users/grfn/emacs.d/company-sql.el
+++ b/users/aspen/emacs.d/company-sql.el
diff --git a/users/grfn/emacs.d/config.el b/users/aspen/emacs.d/config.el
index d19e6c3b94..6398feace8 100644
--- a/users/grfn/emacs.d/config.el
+++ b/users/aspen/emacs.d/config.el
@@ -13,7 +13,7 @@
   (setq doom-font (font-spec :family font-family :size 14)
         doom-big-font (font-spec :family font-family :size 24)
         doom-big-font-increment 5
-        doom-variable-pitch-font (font-spec :family "DejaVu Sans")
+        doom-variable-pitch-font (font-spec :family font-family)
         doom-unicode-font (font-spec :family font-family)))
 
 (require 's)
@@ -228,12 +228,13 @@
 (use-package! org-tracker
   :hook (org-mode . org-tracker-mode)
   :config
-  (setq org-tracker-state-alist '(("BACKLOG" . "Backlog")
-                                  ("PLANNED" . "Planned")
-                                  ("TODO" . "Selected for Development")
+  (setq org-tracker-state-alist '(("INBOX" . "Triage")
+                                  ("BACKLOG" . "Backlog")
+                                  ("TODO" . "Todo")
                                   ("ACTIVE" . "In Progress")
                                   ("PR" . "Code Review")
-                                  ("DONE" . "Done"))
+                                  ("DONE" . "Done")
+                                  ("CANCELLED" . "Canceled"))
         org-tracker-username "griffin@readyset.io"
         org-tracker-claim-ticket-on-status-update '("ACTIVE" "PR" "DONE")
         org-tracker-create-stories-with-labels 'existing)
@@ -346,7 +347,9 @@
   (set-face-foreground 'slack-message-output-header +solarized-s-base01)
   (set-face-attribute 'slack-message-output-header nil :underline nil)
   (set-face-attribute 'slack-message-output-text nil :height 1.0)
-  )
+  (set-face-attribute 'caml-types-expr-face
+                      nil
+                      :background +solarized-s-base2))
 
 (after! solarized-theme
   (set-face-foreground 'font-lock-doc-face +solarized-s-base1)
@@ -368,8 +371,9 @@
   (setq git-commit-summary-max-length 50))
 
 (after! ivy
-  (setq ivy-re-builders-alist
-        '((t . ivy--regex-fuzzy))))
+  ;; (setq ivy-re-builders-alist
+  ;;       '((t . ivy--regex-fuzzy)))
+  )
 
 (add-hook 'before-save-hook 'delete-trailing-whitespace)
 
@@ -466,13 +470,53 @@
 
 (setq projectile-create-missing-test-files 't)
 
+(setq grfn/tracker-refs-re
+      (rx line-start
+          (or "Refs" "Fixes")
+          ": "
+          (one-or-more graph)
+          line-end))
+
+(defun grfn/add-tracker-reference-to-commit-message ()
+  (interactive)
+  (when-let* ((ticket-id (grfn/org-clocked-in-ticket-id)))
+    (save-excursion
+      (save-match-data
+        (goto-char (point-min))
+        ;; Don't add one if we've already got one
+        (unless (search-forward-regexp grfn/tracker-refs-re nil t)
+          (or
+           (and
+            (search-forward-regexp (rx line-start "Change-Id:") nil t)
+            (forward-line -1))
+           (and
+            (search-forward-regexp (rx line-start "# Please enter") nil t)
+            (forward-line -2)))
+          (insert (format "\nRefs: %s" ticket-id)))))))
+
+(defun grfn/switch-tracker-refs-fixes ()
+  (interactive)
+  (save-excursion
+    (save-match-data
+      (if (not (search-forward-regexp grfn/tracker-refs-re nil t))
+          (message "Could not find reference to ticket")
+        (goto-char (point-at-bol))
+        (save-restriction
+          (narrow-to-region (point-at-bol)
+                            (point-at-eol))
+          (or
+           (and (search-forward "Refs" nil t)
+                (replace-match "Fixes"))
+           (and (search-forward "Fixes" nil t)
+                (replace-match "Refs"))))))))
+
 (after! magit
   (map! :map magit-mode-map
         ;; :n "] ]" #'magit-section-forward
         ;; :n "[ [" #'magit-section-backward
         )
 
-  (define-suffix-command magit-commit-wip ()
+  (transient-define-suffix magit-commit-wip ()
     (interactive)
     (magit-commit-create '("-m" "wip")))
 
@@ -481,11 +525,11 @@
     ["c"]
     (list "W" "Commit WIP" #'magit-commit-wip))
 
-  (define-suffix-command magit-reset-head-back ()
+  (transient-define-suffix magit-reset-head-back ()
     (interactive)
     (magit-reset-mixed "HEAD~"))
 
-  (define-suffix-command magit-reset-head-previous ()
+  (transient-define-suffix magit-reset-head-previous ()
     (interactive)
     (magit-reset-mixed "HEAD@{1}"))
 
@@ -536,7 +580,13 @@
   (transient-append-suffix
     #'magit-branch
     ["c"]
-    (list "M" "Rename branch to Tracker ticket" #'magit-rename-org-tracker-branch)))
+    (list "M" "Rename branch to Tracker ticket" #'magit-rename-org-tracker-branch))
+
+  )
+
+(add-hook 'git-commit-setup-hook #'grfn/add-tracker-reference-to-commit-message)
+(map! (:map git-commit-mode-map
+       "C-c C-f" #'grfn/switch-tracker-refs-fixes))
 
 ;; (defun grfn/split-window-more-sensibly (&optional window)
 ;;   (let ((window (or window (selected-window))))
@@ -739,15 +789,8 @@
         cider-save-file-on-load 't)
   )
 
-(defun +org-clocked-in-element ()
-  (when-let ((item (car org-clock-history)))
-    (save-mark-and-excursion
-    (with-current-buffer (marker-buffer item)
-      (goto-char (marker-position item))
-      (org-element-at-point)))))
-
 (comment
- (setq elt (+org-clocked-in-item))
+ (setq elt (+org-clocked-in-element))
 
  (eq 'headline (car elt))
  (plist-get (cadr elt) :raw-value)
@@ -757,45 +800,45 @@
   (when (eq 'headline (car elt))
     (plist-get (cadr elt) :raw-value)))
 
-(setq +ligatures-extra-symbols
-      (append +ligatures-extra-symbols
-              '(:equal     "โ‰ก"
-                :not-equal "โ‰ "
-                :is        "โ‰ฃ"
-                :isnt      "โ‰ข"
-                :lte       "โ‰ค"
-                :gte       "โ‰ฅ"
-                :subseteq  "โŠ†"
-                )))
-
-(after! python
-  (set-pretty-symbols! 'python-mode :merge t
-    :equal      "=="
-    :not-equal "!="
-    :lte "<="
-    :gte ">="
-    :is  "is"
-    :isnt "is not"
-    :subseteq "issubset"
-
-    ;; doom builtins
-
-    ;; Functional
-    :def "def"
-    :lambda "lambda"
-    ;; Types
-    :null "None"
-    :true "True" :false "False"
-    :int "int" :str "str"
-    :float "float"
-    :bool "bool"
-    :tuple "tuple"
-    ;; Flow
-    :not "not"
-    :in "in" :not-in "not in"
-    :and "and" :or "or"
-    :for "for"
-    :return "return" :yield "yield"))
+;; (setq +ligatures-extra-symbols
+;;       (append +ligatures-extra-symbols
+;;               '(:equal     "โ‰ก"
+;;                 :not-equal "โ‰ "
+;;                 :is        "โ‰ฃ"
+;;                 :isnt      "โ‰ข"
+;;                 :lte       "โ‰ค"
+;;                 :gte       "โ‰ฅ"
+;;                 :subseteq  "โŠ†"
+;;                 )))
+
+;; (after! python
+;;   (set-pretty-symbols! 'python-mode :merge t
+;;     :equal      "=="
+;;     :not-equal "!="
+;;     :lte "<="
+;;     :gte ">="
+;;     :is  "is"
+;;     :isnt "is not"
+;;     :subseteq "issubset"
+
+;;     ;; doom builtins
+
+;;     ;; Functional
+;;     :def "def"
+;;     :lambda "lambda"
+;;     ;; Types
+;;     :null "None"
+;;     :true "True" :false "False"
+;;     :int "int" :str "str"
+;;     :float "float"
+;;     :bool "bool"
+;;     :tuple "tuple"
+;;     ;; Flow
+;;     :not "not"
+;;     :in "in" :not-in "not in"
+;;     :and "and" :or "or"
+;;     :for "for"
+;;     :return "return" :yield "yield"))
 
 (use-package! sqlup-mode
   :hook
@@ -853,8 +896,7 @@
   (map! [remap counsel-org-capture] #'org-capture
         [remap org-capture] #'org-capture))
 
-(use-package! evil-snipe :disabled t)
-(evil-snipe-mode -1)
+(remove-hook 'doom-first-input-hook #'evil-snipe-mode)
 
 (use-package! rainbow-mode)
 
@@ -1080,3 +1122,18 @@
 (set-popup-rule!
   "^\\*gud-"
   :quit nil)
+
+(setq elcord-editor-icon "emacs_icon")
+
+;;; ocaml
+
+(after! merlin-mode
+  (set-face-attribute
+   'caml-types-expr-face
+   nil
+   :background +solarized-s-base2)
+  (add-hook! merlin-mode
+     (setq whitespace-line-column 90)))
+
+(use-package! dune-format
+  :hook (dune-mode . dune-format-on-save-mode))
diff --git a/users/grfn/emacs.d/cpp.el b/users/aspen/emacs.d/cpp.el
index 5b5dc8ead6..6068736eca 100644
--- a/users/grfn/emacs.d/cpp.el
+++ b/users/aspen/emacs.d/cpp.el
@@ -7,18 +7,18 @@
   (add-to-list 'flycheck-disabled-checkers 'c/c++-gcc)
   (add-to-list 'flycheck-disabled-checkers 'c/c++-clang))
 
-(defun +grfn/cpp-setup ()
+(defun +aspen/cpp-setup ()
   (when (s-starts-with?
-         "/home/grfn/code/depot/third_party/nix"
+         "/home/aspen/code/depot/third_party/nix"
          (buffer-file-name))
-    (setq lsp-clients-clangd-executable "/home/grfn/code/depot/users/grfn/emacs.d/nix-clangd.sh"
+    (setq lsp-clients-clangd-executable "/home/aspen/code/depot/users/aspen/emacs.d/nix-clangd.sh"
           lsp-clients-clangd-args nil)
     (google-set-c-style)
     (lsp)
     (add-to-list 'flycheck-disabled-checkers 'c/c++-gcc)
     (add-to-list 'flycheck-disabled-checkers 'c/c++-clang)))
 
-(add-hook 'c++-mode-hook #'+grfn/cpp-setup)
+(add-hook 'c++-mode-hook #'+aspen/cpp-setup)
 
 (use-package! protobuf-mode)
 
@@ -34,6 +34,6 @@
 (comment
  (setq
   lsp-clients-clangd-executable
-  "/home/grfn/code/depot/third_party/nix/clangd.sh"
+  "/home/aspen/code/depot/third_party/nix/clangd.sh"
   lsp-clients-clangd-args nil)
  )
diff --git a/users/grfn/emacs.d/email.el b/users/aspen/emacs.d/email.el
index 83076898b4..70360d0072 100644
--- a/users/grfn/emacs.d/email.el
+++ b/users/aspen/emacs.d/email.el
@@ -40,3 +40,14 @@
                    (eq (car action)
                        'display-buffer-same-window))
   :ignore t)
+
+(defun apply-thread-patchset (repo branch)
+  (interactive "Dgit repo: \nsnew branch name: ")
+  (let ((tid notmuch-show-thread-id)
+        (tmp "/tmp/notmuch-patchset"))
+    (shell-command (format "notmuch-extract-patch %s > %s && ( cd %s && git checkout -b %s && git am %s )"
+                           (shell-quote-argument tid)
+                           (shell-quote-argument tmp)
+                           (shell-quote-argument (expand-file-name repo))
+                           (shell-quote-argument branch)
+                           (shell-quote-argument tmp)))))
diff --git a/users/grfn/emacs.d/github-org.el b/users/aspen/emacs.d/github-org.el
index f4f9d2e370..f4f9d2e370 100644
--- a/users/grfn/emacs.d/github-org.el
+++ b/users/aspen/emacs.d/github-org.el
diff --git a/users/grfn/emacs.d/google-c-style.el b/users/aspen/emacs.d/google-c-style.el
index 9bb12c61aa..9bb12c61aa 100644
--- a/users/grfn/emacs.d/google-c-style.el
+++ b/users/aspen/emacs.d/google-c-style.el
diff --git a/users/grfn/emacs.d/grid.el b/users/aspen/emacs.d/grid.el
index 75776a38cd..75776a38cd 100644
--- a/users/grfn/emacs.d/grid.el
+++ b/users/aspen/emacs.d/grid.el
diff --git a/users/grfn/emacs.d/init.el b/users/aspen/emacs.d/init.el
index 6719e93381..46530ab950 100644
--- a/users/grfn/emacs.d/init.el
+++ b/users/aspen/emacs.d/init.el
@@ -1,10 +1,11 @@
 ;;; -*- lexical-binding: t; -*-
 
+(defvar native-comp-deferred-compilation-deny-list nil)
+
 (doom! :completion
        company           ; the ultimate code completion backend
-       ;;helm              ; the *other* search engine for love and life
-       ;;ido               ; the other *other* search engine...
-       ivy               ; a search engine for love and life
+       (ivy +fuzzy
+            +prescient)               ; a search engine for love and life
 
        :ui
        ;;deft              ; notational velocity for Emacs
@@ -21,13 +22,13 @@
        (popup            ; tame sudden yet inevitable temporary windows
         +all             ; catch all popups that start with an asterix
         +defaults)       ; default popup rules
-       ligatures         ; replace bits of code with pretty symbols
-       ;;tabbar            ; FIXME an (incomplete) tab bar for Emacs
-       ;;treemacs          ; a project drawer, like neotree but cooler
+       ;; ligatures         ; replace bits of code with pretty symbols
+       ;; tabbar            ; FIXME an (incomplete) tab bar for Emacs
+       ;; treemacs          ; a project drawer, like neotree but cooler
        unicode           ; extended unicode support for various languages
        vc-gutter         ; vcs diff in the fringe
        vi-tilde-fringe   ; fringe tildes to mark beyond EOB
-       window-select     ; visually switch windows
+       ;;window-select     ; visually switch windows
        workspaces        ; tab emulation, persistence & separate workspaces
 
        :editor
@@ -35,23 +36,22 @@
        file-templates    ; auto-snippets for empty files
        fold              ; (nigh) universal code folding
        ;;(format +onsave)  ; automated prettiness
+       ;;god               ; run Emacs commands without modifier keys
        ;;lispy             ; vim for lisp, for people who dont like vim
-       multiple-cursors  ; editing in many places at once
+       ;;multiple-cursors  ; editing in many places at once
+       ;;objed             ; text object editing for the innocent
        ;;parinfer          ; turn lisp into python, sort of
-       rotate-text       ; cycle region at point between text candidates
+       ;;rotate-text       ; cycle region at point between text candidates
        snippets          ; my elves. They type so I don't have to
        word-wrap
 
        :emacs
-       (dired            ; making dired pretty [functional]
-       ;;+ranger         ; bringing the goodness of ranger to dired
-       ;;+icons          ; colorful icons for dired-mode
-        )
+       dired             ; making dired pretty [functional]
        electric          ; smarter, keyword-based electric-indent
        ;;eshell            ; a consistent, cross-platform shell (WIP)
        ;;term              ; terminals in Emacs
-       vc                ; version-control and Emacs, sitting in a tree
        (undo +tree)
+       vc                ; version-control and Emacs, sitting in a tree
 
        :tools
        ;;ansible
@@ -74,6 +74,7 @@
        ;;rgb               ; creating color strings
        ;;terraform         ; infrastructure as code
        ;;tmux              ; an API for interacting with tmux
+       tree-sitter       ; syntax and parsing, sitting in a tree...
        ;;upload            ; map local to remote projects via ssh/ftp
        ;;wakatime
        ;;vterm             ; another terminals in Emacs
@@ -112,7 +113,7 @@
        markdown          ; writing docs for people to ignore
        ;;nim               ; python + lisp at the speed of c
        nix               ; I hereby declare "nix geht mehr!"
-       ;;ocaml             ; an objective camel
+       ocaml             ; an objective camel
        (org              ; organize your plain life in plain text
         +dragndrop       ; drag & drop files/images into org buffers
         +attach          ; custom attachment system
@@ -133,7 +134,7 @@
        racket            ; a DSL for DSLs
        rest              ; Emacs as a REST client
        ;;ruby              ; 1.step do {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
-       rust              ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
+       (rust +tree-sitter)              ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
        ;;scala             ; java, but good
        (sh +fish)        ; she sells (ba|z|fi)sh shells on the C xor
        ;;solidity          ; do you need a blockchain? No.
@@ -141,6 +142,7 @@
        ;;terra             ; Earth and Moon in alignment for performance.
        ;;web               ; the tubes
        ;;vala              ; GObjective-C
+       zig
 
        ;; Applications are complex and opinionated modules that transform Emacs
        ;; toward a specific purpose. They may have additional dependencies and
@@ -171,64 +173,3 @@
        ;; provides a Spacemacs-inspired keybinding scheme and a smartparens
        ;; config. Use it as a reference for your own modules.
        (default +bindings +smartparens))
-(custom-set-variables
- ;; custom-set-variables was added by Custom.
- ;; If you edit it by hand, you could mess it up, so be careful.
- ;; Your init file should contain only one such instance.
- ;; If there is more than one, they won't work right.
- '(doom-big-font-mode nil)
- '(flycheck-javascript-flow-args nil)
- '(org-agenda-files
-   '("/home/griffin/notes/personal.org" "/home/griffin/notes/2020-01-27-data-pipeline-deploy-mismatch.org" "/home/griffin/notes/architecture.org" "/home/griffin/notes/cooking.org" "/home/griffin/notes/culture-survey.org" "/home/griffin/notes/dir-structure.org" "/home/griffin/notes/dnd.org" "/home/griffin/notes/inbox.org" "/home/griffin/notes/misc-todo.org" "/home/griffin/notes/nix-talk.org" "/home/griffin/notes/notes.org" "/home/griffin/notes/one-on-one.org" "/home/griffin/notes/work.org" "/home/griffin/notes/xanthous.org" "/home/griffin/notes/xgboost.org"))
- '(safe-local-variable-values
-   '((intero-stack-yaml . "/home/griffin/code/mlem/stack.yaml")
-     (elisp-lint-indent-specs
-      (if-let* . 2)
-      (when-let* . 1)
-      (let* . defun)
-      (nrepl-dbind-response . 2)
-      (cider-save-marker . 1)
-      (cider-propertize-region . 1)
-      (cider-map-repls . 1)
-      (cider--jack-in . 1)
-      (cider--make-result-overlay . 1)
-      (insert-label . defun)
-      (insert-align-label . defun)
-      (insert-rect . defun)
-      (cl-defun . 2)
-      (with-parsed-tramp-file-name . 2)
-      (thread-first . 1)
-      (thread-last . 1))
-     (checkdoc-package-keywords-flag)
-     (cider-jack-in-default . "shadow-cljs")
-     (projectile-project-root . "/home/griffin/code/urb/grid/backend/src")
-     (python-pytest-executable . "/home/griffin/code/urb/grid/backend/src/.venv/bin/pytest"))))
-(custom-set-faces
- ;; custom-set-faces was added by Custom.
- ;; If you edit it by hand, you could mess it up, so be careful.
- ;; Your init file should contain only one such instance.
- ;; If there is more than one, they won't work right.
- '(default ((((class color) (min-colors 89)) (:foreground "#657b83" :background "#fdf6e3"))))
- '(agda2-highlight-bound-variable-face ((t nil)))
- '(agda2-highlight-coinductive-constructor-face ((t (:foreground "#b58900"))))
- '(agda2-highlight-datatype-face ((t (:foreground "#268bd2"))))
- '(agda2-highlight-dotted-face ((t nil)))
- '(agda2-highlight-error-face ((t (:foreground "#dc322f" :underline t))))
- '(agda2-highlight-field-face ((t (:foreground "#dc322f"))))
- '(agda2-highlight-function-face ((t (:foreground "#268bd2"))))
- '(agda2-highlight-incomplete-pattern-face ((t (:background "#cb4b16" :foreground "#002b36"))))
- '(agda2-highlight-inductive-constructor-face ((t (:foreground "#859900"))))
- '(agda2-highlight-keyword-face ((t (:foreground "#859900"))))
- '(agda2-highlight-module-face ((t (:foreground "#b58900"))))
- '(agda2-highlight-number-face ((t (:foreground "#6c71c4"))))
- '(agda2-highlight-operator-face ((t nil)))
- '(agda2-highlight-postulate-face ((t (:foreground "#268bd2"))))
- '(agda2-highlight-primitive-face ((t (:foreground "#268bd2"))))
- '(agda2-highlight-primitive-type-face ((t (:foreground "#268bd2"))))
- '(agda2-highlight-record-face ((t (:foreground "#268bd2"))))
- '(agda2-highlight-string-face ((t (:foreground "#2aa198"))))
- '(agda2-highlight-symbol-face ((((background "#fdf6e3")) (:foreground "#586e75"))))
- '(agda2-highlight-termination-problem-face ((t (:background "#cb4b16" :foreground "#002b36"))))
- '(agda2-highlight-typechecks-face ((t (:background "#2aa198" :foreground "#002b36"))))
- '(agda2-highlight-unsolved-constraint-face ((t (:background "#eee8d5"))))
- '(agda2-highlight-unsolved-meta-face ((t (:background "#eee8d5")))))
diff --git a/users/grfn/emacs.d/irc.el b/users/aspen/emacs.d/irc.el
index 117869599d..117869599d 100644
--- a/users/grfn/emacs.d/irc.el
+++ b/users/aspen/emacs.d/irc.el
diff --git a/users/grfn/emacs.d/lisp.el b/users/aspen/emacs.d/lisp.el
index c45cc7e6e3..c45cc7e6e3 100644
--- a/users/grfn/emacs.d/lisp.el
+++ b/users/aspen/emacs.d/lisp.el
diff --git a/users/grfn/emacs.d/nix-clangd.sh b/users/aspen/emacs.d/nix-clangd.sh
index 16f6252d8b..16f6252d8b 100755
--- a/users/grfn/emacs.d/nix-clangd.sh
+++ b/users/aspen/emacs.d/nix-clangd.sh
diff --git a/users/grfn/emacs.d/nix.el b/users/aspen/emacs.d/nix.el
index ec5b474af2..ec5b474af2 100644
--- a/users/grfn/emacs.d/nix.el
+++ b/users/aspen/emacs.d/nix.el
diff --git a/users/grfn/emacs.d/org-alerts.el b/users/aspen/emacs.d/org-alerts.el
index 8e6c3e0417..8e6c3e0417 100644
--- a/users/grfn/emacs.d/org-alerts.el
+++ b/users/aspen/emacs.d/org-alerts.el
diff --git a/users/grfn/emacs.d/org-config.el b/users/aspen/emacs.d/org-config.el
index d81d073dab..b8d88d3195 100644
--- a/users/grfn/emacs.d/org-config.el
+++ b/users/aspen/emacs.d/org-config.el
@@ -1,28 +1,35 @@
 ;;; -*- lexical-binding: t; -*-
 
+(defun +aspen/org-setup ()
+  (setq-local truncate-lines -1)
+  (display-line-numbers-mode -1)
+  (line-number-mode -1))
+
+(add-hook 'org-mode-hook #'+aspen/org-setup)
+
 (defun notes-file (f)
   (concat org-directory (if (string-prefix-p "/" f) "" "/") f))
 
-(defun grfn/org-project-tag->key (tag)
+(defun aspen/org-project-tag->key (tag)
   (s-replace-regexp "^project__" "" tag))
 
-(defun grfn/org-project-tag->name (tag)
+(defun aspen/org-project-tag->name (tag)
   (s-titleized-words
-   (s-join " " (s-split "_" (grfn/org-project-tag->key tag)))))
+   (s-join " " (s-split "_" (aspen/org-project-tag->key tag)))))
 
-(defun grfn/org-project-tag->keys (tag)
+(defun aspen/org-project-tag->keys (tag)
   (s-join "" (cons "p"
                    (-map (lambda (s) (substring-no-properties s 0 1))
-                         (s-split "_" (grfn/org-project-tag->key tag))))))
+                         (s-split "_" (aspen/org-project-tag->key tag))))))
 
-(defun grfn/org-projects->agenda-commands (project-tags)
+(defun aspen/org-projects->agenda-commands (project-tags)
   (loop for tag in project-tags
-        collect `(,(grfn/org-project-tag->keys tag)
-                  ,(grfn/org-project-tag->name tag)
+        collect `(,(aspen/org-project-tag->keys tag)
+                  ,(aspen/org-project-tag->name tag)
                   tags-todo
                   ,tag)))
 
-(defun grfn/org-projects ()
+(defun aspen/org-projects ()
   (loop for (tag) in
         (org-global-tags-completion-table
          (directory-files-recursively "~/notes" "\\.org$"))
@@ -30,7 +37,7 @@
         collect tag))
 
 (comment
- (grfn/org-projects->agenda-commands (grfn/org-projects))
+ (aspen/org-projects->agenda-commands (aspen/org-projects))
  )
 
 (setq
@@ -71,7 +78,7 @@
 
    ("m" "Email" entry
     (file +org-default-todo-file)
-    "* TODO [%l[%:subject]] :email:\n%i")
+    "* TODO [[%L][%:subject]] :email:\n%i")
 
    ("n" "Notes" entry
     (file +org-default-todo-file)
@@ -93,37 +100,35 @@
     (file+headline ,(notes-file "tvix.org") "Tvix TODO")
     "* TODO %?\nContext %a\nIn task: %K")
    ("pw" "Windtunnel" entry
-    (file+headline ,(notes-file "windtunnel.org") "Tasks")
+    (file+headline ,(notes-file "windtunnel.org") "Inbox")
     "* TODO %i%?\nContext: %a\nIn task: %K")
-
-   ("d" "Data recording")
    )
 
  org-capture-templates-contexts
- `(("px" ((in-file . "/home/griffin/code/xanthous/.*"))))
+ `(("px" ((in-file . "/home/aspen/code/depot/users/aspen/xanthous/.*")))
+   ("e" ((in-mode . "notmuch-show-mode"))))
 
  org-deadline-warning-days 1
  org-agenda-skip-scheduled-if-deadline-is-shown 'todo
  org-todo-keywords '((sequence "TODO(t)" "ACTIVE(a)" "|" "DONE(d)" "RUNNING(r)")
                      (sequence "NEXT(n)" "WAITING(w)" "LATER(l)" "|" "CANCELLED(c)"))
  org-agenda-custom-commands
- `(("S" "Sprint Tasks" tags-todo "sprint")
-   ("i" "Inbox" tags "inbox")
+ `(("i" "Inbox" tags "inbox")
    ("r" "Running jobs" todo "RUNNING")
    ("w" "@Work" tags-todo "@work")
    ("n" . "Next...")
-   ("np" "Next Sprint" tags-todo "next_sprint|sprint_planning")
+   ("nw" "Next @Work" tags-todo "@work&next")
+   ("nt" "Next tooling" tags-todo "tooling")
 
    ("p" . "Project...")
-   ,@(grfn/org-projects->agenda-commands (grfn/org-projects)))
+   ,@(aspen/org-projects->agenda-commands (aspen/org-projects)))
 
  org-agenda-dim-blocked-tasks nil
  org-enforce-todo-dependencies nil
 
  org-babel-clojure-backend 'cider)
 
-
-(defun +grfn/insert-work-template ()
+(defun +aspen/insert-work-template ()
   (interactive)
   (goto-char (point-min))
   (forward-line)
@@ -137,20 +142,20 @@
 #+PROPERTY: NOBLOCKING t
 #+COLUMNS: %TODO %40ITEM(Task) %17EFFORT(Estimated){:} %CLOCKSUM(Time Spent) %17STORY-TYPE(Type) %TAGS"))
 
-(defun +grfn/insert-org-template ()
+(defun +aspen/insert-org-template ()
   (interactive)
   (pcase (buffer-file-name)
-    ((s-contains "/work/") (+grfn/insert-work-template))))
+    ((s-contains "/work/") (+aspen/insert-work-template))))
 
 ;;; TODO: this doesn't work?
-(define-auto-insert "\\.org?$" #'grfn/insert-org-template t)
+(define-auto-insert "\\.org?$" #'aspen/insert-org-template t)
 
 (defun forge--post-submit-around---link-pr-to-org-item
     (orig)
   (let ((cb (funcall orig)))
     (lambda (value headers status req)
       (prog1 (funcall cb value headers status req)
-        (grfn/at-org-clocked-in-item
+        (aspen/at-org-clocked-in-item
          (let ((url (alist-get 'html_url value))
                (number (alist-get 'number value)))
            (org-set-property
@@ -173,13 +178,6 @@
  #'forge--post-submit-callback
  :around #'forge--post-submit-around---link-pr-to-org-item)
 
-(defun +grfn/org-setup ()
-  (setq-local truncate-lines -1)
-  (display-line-numbers-mode -1)
-  (line-number-mode -1))
-
-(add-hook 'org-mode-hook #'+grfn/org-setup)
-
 (set-face-foreground 'org-block +solarized-s-base00)
 (setq whitespace-global-modes '(not org-mode magit-mode vterm-mode))
 (setf (alist-get 'file org-link-frame-setup) 'find-file-other-window)
diff --git a/users/grfn/emacs.d/org-gcal.el b/users/aspen/emacs.d/org-gcal.el
index 3e315c5e60..3e315c5e60 100644
--- a/users/grfn/emacs.d/org-gcal.el
+++ b/users/aspen/emacs.d/org-gcal.el
diff --git a/users/grfn/emacs.d/org-query.el b/users/aspen/emacs.d/org-query.el
index e659e02c8b..9d3b3358a9 100644
--- a/users/grfn/emacs.d/org-query.el
+++ b/users/aspen/emacs.d/org-query.el
@@ -4,6 +4,23 @@
 (require 'org-agenda)
 (require 'inflections)
 
+(defun grfn/org-text-element->string (elt)
+  (cond
+   ((stringp elt) elt)
+   ((and (consp elt)
+         (symbolp (car elt)))
+    (-> elt (caddr) (grfn/org-text-element->string) (s-trim) (concat " ")))))
+
+(defun grfn/org-element-title (elt)
+  (let ((title (org-element-property :title elt)))
+    (cond
+     ((stringp title) title)
+     ((listp title)
+      (->> title
+           (mapcar #'grfn/org-text-element->string)
+           (s-join "")
+           (s-trim))))))
+
 (defun grfn/org-agenda-entry->element (agenda-entry)
   ;; ???
   ())
@@ -49,7 +66,7 @@
 
 (defun grfn/num-inbox-items-message ()
   (let ((n (grfn/num-inbox-items)))
-    (unless (zerop n)
+    (if (zerop n) ""
       (format "%d %s"
               n
               (if (= 1 n) "item" "items")))))
@@ -86,7 +103,10 @@
 (defun grfn/org-current-clocked-in-task-message ()
   (if (org-clocking-p)
       (format "(%s) [%s]"
-              (org-element-property :title (grfn/org-element-clocked-in-task))
+              (->> (grfn/org-element-clocked-in-task)
+                   (grfn/org-element-title)
+                   (substring-no-properties)
+                   (s-trim))
               (grfn/minutes->hours:minutes
                (org-clock-get-clocked-time)))
     ""))
@@ -94,3 +114,30 @@
 (comment
  (grfn/org-current-clocked-in-task-message)
  )
+
+(cl-defgeneric grfn/org-tracker-ticket-id-label (backend elt)
+  (org-tracker-backend/extract-issue-id backend elt))
+(cl-defmethod grfn/org-tracker-ticket-id-label
+  ((backend org-tracker-linear-backend) elt)
+  (when-let* ((link (plist-get elt :LINEAR-KEY)))
+    (string-match
+     (rx "[[" (one-or-more anything) "]"
+         "[" (group (one-or-more anything)) "]]")
+     link)
+    (match-string 1 link)))
+
+(defun grfn/org-clocked-in-ticket-id ()
+  (grfn/at-org-clocked-in-item
+   (when-let* ((backend (org-tracker-current-backend t)))
+     (grfn/org-tracker-ticket-id-label
+      backend
+      (cadr (org-element-at-point))))))
+
+(comment
+ (grfn/at-org-clocked-in-item
+  (org-tracker-backend/extract-issue-id
+   (org-tracker-current-backend)
+   (cadr (org-element-at-point))))
+
+ (grfn/org-clocked-in-ticket-id)
+ )
diff --git a/users/grfn/emacs.d/packages.el b/users/aspen/emacs.d/packages.el
index 5a580cad17..15a3843f4d 100644
--- a/users/grfn/emacs.d/packages.el
+++ b/users/aspen/emacs.d/packages.el
@@ -20,6 +20,7 @@
 (package! org-tracker
   :recipe (:host file
            :local-repo "~/code/org-tracker"))
+(package! jiralib2)
 (package! org-alert)
 (package! ob-http)
 (package! ob-ipython)
@@ -28,6 +29,7 @@
 (package! org-sticky-header)
 (package! gnuplot)
 (package! gnuplot-mode)
+(package! org-d20)
 
 ;; Presentation
 (package! epresent)
@@ -69,7 +71,7 @@
 (package! lsp-treemacs)
 
 ;; Rust
-(package! rustic :disable t)
+;; (package! rustic :disable t)
 ;; (package! racer :disable t)
 (package! cargo)
 
diff --git a/users/grfn/emacs.d/rust.el b/users/aspen/emacs.d/rust.el
index 689d843e78..9988d16a53 100644
--- a/users/grfn/emacs.d/rust.el
+++ b/users/aspen/emacs.d/rust.el
@@ -5,18 +5,20 @@
 (defun grfn/rust-setup ()
   (interactive)
 
-  (push '(?> . ("<" . ">")) evil-surround-pairs-alist)
-  (push '(?< . ("< " . " >")) evil-surround-pairs-alist)
+  (direnv--maybe-update-environment)
 
-  (setq lsp-rust-server 'rust-analyzer)
+  (+evil-embrace-angle-bracket-modes-hook-h)
+
+  ;; (setq lsp-rust-server 'rust-analyzer)
   (setq-local whitespace-line-column 100
               fill-column 100)
-  (setq rust-format-show-buffer nil)
+  ;; (setq rust-format-show-buffer nil)
   (setq lsp-rust-analyzer-import-merge-behaviour "last"
         lsp-rust-analyzer-cargo-watch-command "clippy"
         lsp-rust-analyzer-cargo-watch-args ["--target-dir" "/home/grfn/code/readyset/readyset/target/rust-analyzer"]
+        rustic-format-trigger 'on-save
         lsp-ui-doc-enable t)
-  (rust-enable-format-on-save)
+  ;; (rust-enable-format-on-save)
   (lsp))
 
 (add-hook 'rust-mode-hook #'grfn/rust-setup)
@@ -26,6 +28,7 @@
   :n "g RET" #'lsp-rust-analyzer-run
   :n "g R" #'lsp-find-references
   :n "g d" #'lsp-find-definition
+  :n "g Y" #'lsp-goto-type-definition
   (:localleader
    "m" #'lsp-rust-analyzer-expand-macro)))
 
diff --git a/users/grfn/emacs.d/show-matching-paren.el b/users/aspen/emacs.d/show-matching-paren.el
index ab65a912a8..ab65a912a8 100644
--- a/users/grfn/emacs.d/show-matching-paren.el
+++ b/users/aspen/emacs.d/show-matching-paren.el
diff --git a/users/grfn/emacs.d/slack-snippets.el b/users/aspen/emacs.d/slack-snippets.el
index b5bd4db748..b5bd4db748 100644
--- a/users/grfn/emacs.d/slack-snippets.el
+++ b/users/aspen/emacs.d/slack-snippets.el
diff --git a/users/grfn/emacs.d/slack.el b/users/aspen/emacs.d/slack.el
index 54d3b40b09..54d3b40b09 100644
--- a/users/grfn/emacs.d/slack.el
+++ b/users/aspen/emacs.d/slack.el
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/annotation b/users/aspen/emacs.d/snippets/haskell-mode/annotation
index 8a2854d759..8a2854d759 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/annotation
+++ b/users/aspen/emacs.d/snippets/haskell-mode/annotation
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/benchmark-module b/users/aspen/emacs.d/snippets/haskell-mode/benchmark-module
index cbb1646e41..cbb1646e41 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/benchmark-module
+++ b/users/aspen/emacs.d/snippets/haskell-mode/benchmark-module
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/header b/users/aspen/emacs.d/snippets/haskell-mode/header
index fdd8250d86..fdd8250d86 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/header
+++ b/users/aspen/emacs.d/snippets/haskell-mode/header
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/hedgehog-generator b/users/aspen/emacs.d/snippets/haskell-mode/hedgehog-generator
index 68863f7054..68863f7054 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/hedgehog-generator
+++ b/users/aspen/emacs.d/snippets/haskell-mode/hedgehog-generator
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/hedgehog-property b/users/aspen/emacs.d/snippets/haskell-mode/hedgehog-property
index bf39a2a3ee..bf39a2a3ee 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/hedgehog-property
+++ b/users/aspen/emacs.d/snippets/haskell-mode/hedgehog-property
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/hlint b/users/aspen/emacs.d/snippets/haskell-mode/hlint
index 74b63dc672..f25a9b8d40 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/hlint
+++ b/users/aspen/emacs.d/snippets/haskell-mode/hlint
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: hlint
-# uuid:
+# uuid: hlint
 # expand-env: ((yas-indent-line 'fixed))
 # key: hlint
 # condition: t
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/import-i b/users/aspen/emacs.d/snippets/haskell-mode/import-i
index 4a7fca2c2f..4a7fca2c2f 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/import-i
+++ b/users/aspen/emacs.d/snippets/haskell-mode/import-i
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/inl b/users/aspen/emacs.d/snippets/haskell-mode/inl
index 6e17b83d71..6e17b83d71 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/inl
+++ b/users/aspen/emacs.d/snippets/haskell-mode/inl
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/inline b/users/aspen/emacs.d/snippets/haskell-mode/inline
index 1beafbe50b..1beafbe50b 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/inline
+++ b/users/aspen/emacs.d/snippets/haskell-mode/inline
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/language pragma b/users/aspen/emacs.d/snippets/haskell-mode/language pragma
index 6f84720f45..6f84720f45 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/language pragma
+++ b/users/aspen/emacs.d/snippets/haskell-mode/language pragma
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/lens.field b/users/aspen/emacs.d/snippets/haskell-mode/lens.field
index b22ea3d2e8..b22ea3d2e8 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/lens.field
+++ b/users/aspen/emacs.d/snippets/haskell-mode/lens.field
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/module b/users/aspen/emacs.d/snippets/haskell-mode/module
index 4554d33f9b..4554d33f9b 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/module
+++ b/users/aspen/emacs.d/snippets/haskell-mode/module
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/shut up, hlint b/users/aspen/emacs.d/snippets/haskell-mode/shut up, hlint
index fccff1d66f..fccff1d66f 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/shut up, hlint
+++ b/users/aspen/emacs.d/snippets/haskell-mode/shut up, hlint
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/test-group b/users/aspen/emacs.d/snippets/haskell-mode/test-group
index 948e90d9e0..bf6a66f8a3 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/test-group
+++ b/users/aspen/emacs.d/snippets/haskell-mode/test-group
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: test-group
-# uuid:
+# uuid: test-group
 # key: testGroup
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/test-module b/users/aspen/emacs.d/snippets/haskell-mode/test-module
index 036b0ae998..036b0ae998 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/test-module
+++ b/users/aspen/emacs.d/snippets/haskell-mode/test-module
diff --git a/users/grfn/emacs.d/snippets/haskell-mode/undefined b/users/aspen/emacs.d/snippets/haskell-mode/undefined
index 7bcd99b571..7bcd99b571 100644
--- a/users/grfn/emacs.d/snippets/haskell-mode/undefined
+++ b/users/aspen/emacs.d/snippets/haskell-mode/undefined
diff --git a/users/grfn/emacs.d/snippets/js2-mode/action-type b/users/aspen/emacs.d/snippets/js2-mode/action-type
index ef8d1a3863..ef8d1a3863 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/action-type
+++ b/users/aspen/emacs.d/snippets/js2-mode/action-type
diff --git a/users/grfn/emacs.d/snippets/js2-mode/before b/users/aspen/emacs.d/snippets/js2-mode/before
index 4569b65831..4569b65831 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/before
+++ b/users/aspen/emacs.d/snippets/js2-mode/before
diff --git a/users/grfn/emacs.d/snippets/js2-mode/context b/users/aspen/emacs.d/snippets/js2-mode/context
index d83809f3c3..d83809f3c3 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/context
+++ b/users/aspen/emacs.d/snippets/js2-mode/context
diff --git a/users/grfn/emacs.d/snippets/js2-mode/describe b/users/aspen/emacs.d/snippets/js2-mode/describe
index bd0198181d..bd0198181d 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/describe
+++ b/users/aspen/emacs.d/snippets/js2-mode/describe
diff --git a/users/grfn/emacs.d/snippets/js2-mode/expect b/users/aspen/emacs.d/snippets/js2-mode/expect
index eba41ef330..eba41ef330 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/expect
+++ b/users/aspen/emacs.d/snippets/js2-mode/expect
diff --git a/users/grfn/emacs.d/snippets/js2-mode/function b/users/aspen/emacs.d/snippets/js2-mode/function
index b423044b44..b423044b44 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/function
+++ b/users/aspen/emacs.d/snippets/js2-mode/function
diff --git a/users/grfn/emacs.d/snippets/js2-mode/header b/users/aspen/emacs.d/snippets/js2-mode/header
index 3e303764cb..3e303764cb 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/header
+++ b/users/aspen/emacs.d/snippets/js2-mode/header
diff --git a/users/grfn/emacs.d/snippets/js2-mode/it b/users/aspen/emacs.d/snippets/js2-mode/it
index a451cfc08a..a451cfc08a 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/it
+++ b/users/aspen/emacs.d/snippets/js2-mode/it
diff --git a/users/grfn/emacs.d/snippets/js2-mode/it-pending b/users/aspen/emacs.d/snippets/js2-mode/it-pending
index 00da312e10..00da312e10 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/it-pending
+++ b/users/aspen/emacs.d/snippets/js2-mode/it-pending
diff --git a/users/grfn/emacs.d/snippets/js2-mode/module b/users/aspen/emacs.d/snippets/js2-mode/module
index dc79819d89..dc79819d89 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/module
+++ b/users/aspen/emacs.d/snippets/js2-mode/module
diff --git a/users/grfn/emacs.d/snippets/js2-mode/record b/users/aspen/emacs.d/snippets/js2-mode/record
index 0bb0f02436..0bb0f02436 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/record
+++ b/users/aspen/emacs.d/snippets/js2-mode/record
diff --git a/users/grfn/emacs.d/snippets/js2-mode/test b/users/aspen/emacs.d/snippets/js2-mode/test
index 938d490a74..938d490a74 100644
--- a/users/grfn/emacs.d/snippets/js2-mode/test
+++ b/users/aspen/emacs.d/snippets/js2-mode/test
diff --git a/users/grfn/emacs.d/snippets/nix-mode/fetchFromGitHub b/users/aspen/emacs.d/snippets/nix-mode/fetchFromGitHub
index 9b93735730..d2447e4b5a 100644
--- a/users/grfn/emacs.d/snippets/nix-mode/fetchFromGitHub
+++ b/users/aspen/emacs.d/snippets/nix-mode/fetchFromGitHub
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: fetchFromGitHub
-# uuid:
+# uuid: fetchFromGitHub
 # key: fetchFromGitHub
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/nix-mode/pythonPackage b/users/aspen/emacs.d/snippets/nix-mode/pythonPackage
index 0a74c21e18..0a74c21e18 100644
--- a/users/grfn/emacs.d/snippets/nix-mode/pythonPackage
+++ b/users/aspen/emacs.d/snippets/nix-mode/pythonPackage
diff --git a/users/grfn/emacs.d/snippets/nix-mode/sha256 b/users/aspen/emacs.d/snippets/nix-mode/sha256
index e3d52e1c02..bc640e5ab0 100644
--- a/users/grfn/emacs.d/snippets/nix-mode/sha256
+++ b/users/aspen/emacs.d/snippets/nix-mode/sha256
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: sha256
-# uuid:
+# uuid: sha256
 # key: sha256
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/org-mode/SQL source block b/users/aspen/emacs.d/snippets/org-mode/SQL source block
index b5d43fd6bc..b5d43fd6bc 100644
--- a/users/grfn/emacs.d/snippets/org-mode/SQL source block
+++ b/users/aspen/emacs.d/snippets/org-mode/SQL source block
diff --git a/users/grfn/emacs.d/snippets/org-mode/combat b/users/aspen/emacs.d/snippets/org-mode/combat
index ef46062d09..b4db0f433a 100644
--- a/users/grfn/emacs.d/snippets/org-mode/combat
+++ b/users/aspen/emacs.d/snippets/org-mode/combat
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: combat
-# uuid:
+# uuid: combat
 # key: combat
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/org-mode/date b/users/aspen/emacs.d/snippets/org-mode/date
index 297529cdac..297529cdac 100644
--- a/users/grfn/emacs.d/snippets/org-mode/date
+++ b/users/aspen/emacs.d/snippets/org-mode/date
diff --git a/users/grfn/emacs.d/snippets/org-mode/date-time b/users/aspen/emacs.d/snippets/org-mode/date-time
index fde469276c..fde469276c 100644
--- a/users/grfn/emacs.d/snippets/org-mode/date-time
+++ b/users/aspen/emacs.d/snippets/org-mode/date-time
diff --git a/users/grfn/emacs.d/snippets/org-mode/description b/users/aspen/emacs.d/snippets/org-mode/description
index a43bc95cc3..a43bc95cc3 100644
--- a/users/grfn/emacs.d/snippets/org-mode/description
+++ b/users/aspen/emacs.d/snippets/org-mode/description
diff --git a/users/grfn/emacs.d/snippets/org-mode/nologdone b/users/aspen/emacs.d/snippets/org-mode/nologdone
index e5be85d6b3..e5be85d6b3 100644
--- a/users/grfn/emacs.d/snippets/org-mode/nologdone
+++ b/users/aspen/emacs.d/snippets/org-mode/nologdone
diff --git a/users/grfn/emacs.d/snippets/org-mode/python source block b/users/aspen/emacs.d/snippets/org-mode/python source block
index 247ae51b0b..247ae51b0b 100644
--- a/users/grfn/emacs.d/snippets/org-mode/python source block
+++ b/users/aspen/emacs.d/snippets/org-mode/python source block
diff --git a/users/grfn/emacs.d/snippets/org-mode/reveal b/users/aspen/emacs.d/snippets/org-mode/reveal
index 1bdbdfa5dc..1bdbdfa5dc 100644
--- a/users/grfn/emacs.d/snippets/org-mode/reveal
+++ b/users/aspen/emacs.d/snippets/org-mode/reveal
diff --git a/users/grfn/emacs.d/snippets/org-mode/transaction b/users/aspen/emacs.d/snippets/org-mode/transaction
index 37f2dd31ca..37f2dd31ca 100644
--- a/users/grfn/emacs.d/snippets/org-mode/transaction
+++ b/users/aspen/emacs.d/snippets/org-mode/transaction
diff --git a/users/aspen/emacs.d/snippets/prolog-mode/use-module b/users/aspen/emacs.d/snippets/prolog-mode/use-module
new file mode 100644
index 0000000000..75fd19b641
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/prolog-mode/use-module
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: use-module
+# uuid: use-module
+# key: use
+# condition: t
+# --
+:- use_module(${1:library($2)}${3:, [$4]}).
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/python-mode/add_column b/users/aspen/emacs.d/snippets/python-mode/add_column
index 47e83850d5..47e83850d5 100644
--- a/users/grfn/emacs.d/snippets/python-mode/add_column
+++ b/users/aspen/emacs.d/snippets/python-mode/add_column
diff --git a/users/grfn/emacs.d/snippets/python-mode/decorate b/users/aspen/emacs.d/snippets/python-mode/decorate
index 9448b45c96..4f96748572 100644
--- a/users/grfn/emacs.d/snippets/python-mode/decorate
+++ b/users/aspen/emacs.d/snippets/python-mode/decorate
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: decorate
-# uuid:
+# uuid: decorate
 # key: decorate
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/python-mode/dunder b/users/aspen/emacs.d/snippets/python-mode/dunder
index c49ec40a15..71d99dddc6 100644
--- a/users/grfn/emacs.d/snippets/python-mode/dunder
+++ b/users/aspen/emacs.d/snippets/python-mode/dunder
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: dunder
-# uuid:
+# uuid: dunder
 # key: du
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/python-mode/name b/users/aspen/emacs.d/snippets/python-mode/name
index eca6d60b48..1495cc91d9 100644
--- a/users/grfn/emacs.d/snippets/python-mode/name
+++ b/users/aspen/emacs.d/snippets/python-mode/name
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: name
-# uuid:
+# uuid: name
 # key: name
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/python-mode/op.get_bind.execute b/users/aspen/emacs.d/snippets/python-mode/op.get_bind.execute
index aba801c6ba..aba801c6ba 100644
--- a/users/grfn/emacs.d/snippets/python-mode/op.get_bind.execute
+++ b/users/aspen/emacs.d/snippets/python-mode/op.get_bind.execute
diff --git a/users/grfn/emacs.d/snippets/python-mode/pdb b/users/aspen/emacs.d/snippets/python-mode/pdb
index 6b5c0bbc0a..41c6f87cbf 100644
--- a/users/grfn/emacs.d/snippets/python-mode/pdb
+++ b/users/aspen/emacs.d/snippets/python-mode/pdb
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: pdb
-# uuid:
+# uuid: pdb
 # key: pdb
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/rust-mode/#[macro_use] b/users/aspen/emacs.d/snippets/rust-mode/#[macro_use]
index fea942a337..fea942a337 100644
--- a/users/grfn/emacs.d/snippets/rust-mode/#[macro_use]
+++ b/users/aspen/emacs.d/snippets/rust-mode/#[macro_use]
diff --git a/users/aspen/emacs.d/snippets/rust-mode/async test b/users/aspen/emacs.d/snippets/rust-mode/async test
new file mode 100644
index 0000000000..2352d7b56b
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/rust-mode/async test
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: async test
+# uuid: atest
+# key: atest
+# condition: t
+# --
+#[tokio::test${1:(flavor = "multi_thread")}]
+async fn ${2:test_name}() {
+   `%`$0
+}
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/rust-mode/benchmark b/users/aspen/emacs.d/snippets/rust-mode/benchmark
index f1446923a0..9ec4307538 100644
--- a/users/grfn/emacs.d/snippets/rust-mode/benchmark
+++ b/users/aspen/emacs.d/snippets/rust-mode/benchmark
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: benchmark
-# uuid:
+# uuid: benchmark
 # key: bench
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/rust-mode/proptest b/users/aspen/emacs.d/snippets/rust-mode/proptest
index 377b3cfcf6..be12af4911 100644
--- a/users/grfn/emacs.d/snippets/rust-mode/proptest
+++ b/users/aspen/emacs.d/snippets/rust-mode/proptest
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: proptest
-# uuid:
+# uuid: proptest
 # key: proptest
 # condition: t
 # --
diff --git a/users/aspen/emacs.d/snippets/rust-mode/test-module b/users/aspen/emacs.d/snippets/rust-mode/test-module
new file mode 100644
index 0000000000..bfa2ca2d18
--- /dev/null
+++ b/users/aspen/emacs.d/snippets/rust-mode/test-module
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: test-module
+# uuid: test-module
+# key: tmod
+# condition: t
+# --
+mod $1 {
+    use super::*;
+
+    $0
+}
\ No newline at end of file
diff --git a/users/grfn/emacs.d/snippets/rust-mode/tests b/users/aspen/emacs.d/snippets/rust-mode/tests
index 0a476ab586..0a476ab586 100644
--- a/users/grfn/emacs.d/snippets/rust-mode/tests
+++ b/users/aspen/emacs.d/snippets/rust-mode/tests
diff --git a/users/grfn/emacs.d/snippets/snippet-mode/indent b/users/aspen/emacs.d/snippets/snippet-mode/indent
index d38ffceafb..d38ffceafb 100644
--- a/users/grfn/emacs.d/snippets/snippet-mode/indent
+++ b/users/aspen/emacs.d/snippets/snippet-mode/indent
diff --git a/users/grfn/emacs.d/snippets/sql-mode/count(*) group by b/users/aspen/emacs.d/snippets/sql-mode/count(*) group by
index 6acc46ff39..6acc46ff39 100644
--- a/users/grfn/emacs.d/snippets/sql-mode/count(*) group by
+++ b/users/aspen/emacs.d/snippets/sql-mode/count(*) group by
diff --git a/users/grfn/emacs.d/snippets/terraform-mode/variable b/users/aspen/emacs.d/snippets/terraform-mode/variable
index e64175200f..14822f1a05 100644
--- a/users/grfn/emacs.d/snippets/terraform-mode/variable
+++ b/users/aspen/emacs.d/snippets/terraform-mode/variable
@@ -1,6 +1,6 @@
 # -*- mode: snippet -*-
 # name: variable
-# uuid:
+# uuid: variable
 # key: var
 # condition: t
 # --
diff --git a/users/grfn/emacs.d/snippets/text-mode/date b/users/aspen/emacs.d/snippets/text-mode/date
index 7b94311470..7b94311470 100644
--- a/users/grfn/emacs.d/snippets/text-mode/date
+++ b/users/aspen/emacs.d/snippets/text-mode/date
diff --git a/users/grfn/emacs.d/splitjoin.el b/users/aspen/emacs.d/splitjoin.el
index dbc9704d79..dbc9704d79 100644
--- a/users/grfn/emacs.d/splitjoin.el
+++ b/users/aspen/emacs.d/splitjoin.el
diff --git a/users/grfn/emacs.d/sql-strings.el b/users/aspen/emacs.d/sql-strings.el
index eef397a24e..eef397a24e 100644
--- a/users/grfn/emacs.d/sql-strings.el
+++ b/users/aspen/emacs.d/sql-strings.el
diff --git a/users/grfn/emacs.d/terraform.el b/users/aspen/emacs.d/terraform.el
index 2d69c9bad9..2d69c9bad9 100644
--- a/users/grfn/emacs.d/terraform.el
+++ b/users/aspen/emacs.d/terraform.el
diff --git a/users/grfn/emacs.d/tests/splitjoin_test.el b/users/aspen/emacs.d/tests/splitjoin_test.el
index 6495a1a595..6495a1a595 100644
--- a/users/grfn/emacs.d/tests/splitjoin_test.el
+++ b/users/aspen/emacs.d/tests/splitjoin_test.el
diff --git a/users/grfn/emacs.d/themes/grfn-solarized-light-theme.el b/users/aspen/emacs.d/themes/grfn-solarized-light-theme.el
index ae00b6b5fc..ae00b6b5fc 100644
--- a/users/grfn/emacs.d/themes/grfn-solarized-light-theme.el
+++ b/users/aspen/emacs.d/themes/grfn-solarized-light-theme.el
diff --git a/users/grfn/emacs.d/utils.el b/users/aspen/emacs.d/utils.el
index 21192753a2..21192753a2 100644
--- a/users/grfn/emacs.d/utils.el
+++ b/users/aspen/emacs.d/utils.el
diff --git a/users/grfn/emacs.d/vterm.el b/users/aspen/emacs.d/vterm.el
index a7fdea46da..a7fdea46da 100644
--- a/users/grfn/emacs.d/vterm.el
+++ b/users/aspen/emacs.d/vterm.el
diff --git a/users/aspen/emacs/.gitignore b/users/aspen/emacs/.gitignore
new file mode 100644
index 0000000000..f5236c1235
--- /dev/null
+++ b/users/aspen/emacs/.gitignore
@@ -0,0 +1,2 @@
+custom.el
+config.el
diff --git a/users/aspen/emacs/config.org b/users/aspen/emacs/config.org
new file mode 100644
index 0000000000..b3762affe4
--- /dev/null
+++ b/users/aspen/emacs/config.org
@@ -0,0 +1,1393 @@
+# Local variables:
+# lexical-binding: t
+# eval: (paxedit-mode 1)
+# eval: (display-line-numbers-mode 1)
+# eval: (flyspell-mode -1)
+# eval: (org-config-mode 1)
+# End:
+
+#+title: Emacs Config
+#+PROPERTY: header-args:emacs-lisp :results silent
+#+PROPERTY: header-args:elisp :results silent
+
+#+begin_src emacs-lisp :tangle yes
+;; -*- lexical-binding: t; -*-
+#+end_src
+
+* Utils
+#+begin_src elisp :tangle yes
+(use-package! dash)
+#+end_src
+
+** Elisp extras
+
+#+begin_src elisp :tangle yes
+(defmacro comment (&rest _body)
+  "Comment out one or more s-expressions"
+  nil)
+
+(defun inc (x) "Returns x + 1" (+ 1 x))
+(defun dec (x) "Returns x - 1" (- x 1))
+
+(defun average (ns)
+  "Arithmetic mean of xs"
+  (if (null ns) nil
+    (/ (apply #'+ ns)
+       (length ns))))
+
+(defun alist-set (alist-symbol key value)
+  "Set VALUE of a KEY in ALIST-SYMBOL."
+  (set alist-symbol (cons (list key value) (assq-delete-all key (eval alist-symbol)))))
+
+(defun rx-words (&rest words)
+  (rx-to-string
+   `(and symbol-start (group (or ,@words)) symbol-end)))
+#+end_src
+
+#+begin_src elisp :tangle no :results example
+(average (list 1 2 3 4))
+#+end_src
+
+** Text editing utils
+*** Reading strings
+#+begin_src elisp :tangle yes
+(defun get-char (&optional point)
+  "Get the character at the given `point' (defaulting to the current point),
+without properties"
+  (let ((point (or point (point))))
+    (buffer-substring-no-properties point (+ 1 point))))
+
+(defun get-line (&optional lineno)
+  "Read the line number `lineno', or the current line if `lineno' is nil, and
+return it as a string stripped of all text properties"
+  (let ((current-line (line-number-at-pos)))
+    (if (or (not lineno)
+            (= current-line lineno))
+        (thing-at-point 'line t)
+      (save-mark-and-excursion
+       (line-move (- lineno (line-number-at-pos)))
+       (thing-at-point 'line t)))))
+
+(defun get-line-point ()
+  "Get the position in the current line of the point"
+  (- (point) (line-beginning-position)))
+
+;; Moving in the file
+
+(defun goto-line-char (pt)
+  "Moves the point to the given position expressed as an offset from the start
+of the line"
+  (goto-char (+ (line-beginning-position) pt)))
+
+(defun goto-eol ()
+  "Moves to the end of the current line"
+  (goto-char (line-end-position)))
+
+(defun goto-regex-on-line (regex)
+  "Moves the point to the first occurrence of `regex' on the current line.
+Returns nil if the regex did not match, non-nil otherwise"
+  (when-let ((current-line (get-line))
+             (line-char (string-match regex current-line)))
+    (goto-line-char line-char)))
+
+(defun goto-regex-on-line-r (regex)
+  "Moves the point to the *last* occurrence of `regex' on the current line.
+Returns nil if the regex did not match, non-nil otherwise"
+  (when-let ((current-line (get-line))
+             (modified-regex (concat ".*\\(" regex "\\)"))
+             (_ (string-match modified-regex current-line))
+             (match-start (match-beginning 1)))
+    (goto-line-char match-start)))
+#+end_src
+
+#+begin_src elisp :tangle no
+(progn
+  (string-match (rx (and (zero-or-more anything)
+                         (group "foo" "foo")))
+                "foofoofoo")
+  (match-beginning 1))
+#+end_src
+
+*** Changing file contents
+#+begin_src elisp :tangle yes
+(defmacro saving-excursion (&rest body)
+  `(ฮป! () (save-excursion ,@body)))
+
+(defun delete-line ()
+  "Remove the line at the current point"
+  (delete-region (line-beginning-position)
+                 (inc (line-end-position))))
+
+(defmacro modify-then-indent (&rest body)
+  "Modify text in the buffer according to body, then re-indent from where the
+  cursor started to where the cursor ended up, then return the cursor to where
+  it started."
+  `(let ((beg (line-beginning-position))
+         (orig-line-char (- (point) (line-beginning-position))))
+     (atomic-change-group
+       (save-mark-and-excursion
+        ,@body
+        (evil-indent beg (+ (line-end-position) 1))))
+     (goto-line-char orig-line-char)))
+
+(pcase-defmacro s-starts-with (prefix)
+  `(pred (s-starts-with-p ,prefix)))
+
+(pcase-defmacro s-contains (needle &optional ignore-case)
+  `(pred (s-contains-p ,needle
+                       ,@(when ignore-case (list ignore-case)))))
+#+end_src
+
+#+begin_src elisp :tangle no
+(pcase "foo"
+  ((s-contains "bar") 1)
+  ((s-contains "o") 2))
+#+end_src
+
+** Evil utils
+#+begin_src elisp :tangle yes
+(defmacro define-move-and-insert
+    (name &rest body)
+  `(defun ,name (count &optional vcount skip-empty-lines)
+     ;; Following interactive form taken from the source for `evil-insert'
+     (interactive
+      (list (prefix-numeric-value current-prefix-arg)
+            (and (evil-visual-state-p)
+                 (memq (evil-visual-type) '(line block))
+                 (save-excursion
+                   (let ((m (mark)))
+                     ;; go to upper-left corner temporarily so
+                     ;; `count-lines' yields accurate results
+                     (evil-visual-rotate 'upper-left)
+                     (prog1 (count-lines evil-visual-beginning evil-visual-end)
+                       (set-mark m)))))
+            (evil-visual-state-p)))
+     (atomic-change-group
+       ,@body
+       (evil-insert count vcount skip-empty-lines))))
+#+end_src
+
+* Name and email
+#+begin_src emacs-lisp
+(setq user-full-name "Aspen Smith"
+      user-mail-address "root@gws.fyi")
+#+end_src
+
+* Visual style
+#+begin_src elisp :tangle yes
+(let ((font-family (pcase system-type
+                     ('darwin "MesloLGSDZ NF")
+                     ('gnu/linux "Meslo LGSDZ Nerd Font"))))
+  (setq doom-font (font-spec :family font-family :height 113)
+        doom-big-font (font-spec :family font-family :size 24)
+        doom-big-font-increment 5
+        doom-variable-pitch-font (font-spec :family font-family)
+        doom-theme 'doom-solarized-light))
+
+(setq display-line-numbers-type t)
+
+(setq doom-modeline-buffer-file-name-style 'relative-to-project
+      doom-modeline-modal-icon nil
+      doom-modeline-github t
+      doom-modeline-height 12)
+#+end_src
+
+#+begin_src elisp :tangle yes
+(setq whitespace-style '(face lines-tail))
+(global-whitespace-mode t)
+(add-hook 'org-mode-hook (lambda () (whitespace-mode -1)) t)
+#+end_src
+
+** Theme
+[[https://davidjohnstone.net/lch-lab-colour-gradient-picker][LAB colour gradient picker]] is a good tool for trying to find "halfway points" between two colours
+
+*** Variables
+#+begin_src elisp :tangle no
+(rainbow-mode)
+#+end_src
+
+#+name: solarized-vars
+#+begin_src elisp :tangle yes
+(setq +solarized-s-base03    "#002b36"
+      +solarized-s-base02    "#073642"
+      ;; emphasized content
+      +solarized-s-base01    "#586e75"
+      ;; primary content
+      +solarized-s-base00    "#657b83"
+      +solarized-s-base0     "#839496"
+      ;; comments
+      +solarized-s-base1     "#93a1a1"
+      ;; background highlight light
+      +solarized-s-base2     "#eee8d5"
+      ;; background light
+      +solarized-s-base3     "#fdf6e3"
+
+      +solarized-halfway-highlight "#f5efdc"
+
+      ;; Solarized accented colors
+      +solarized-yellow    "#b58900"
+      +solarized-orange    "#cb4b16"
+      +solarized-red       "#dc322f"
+      +solarized-magenta   "#d33682"
+      +solarized-violet    "#6c71c4"
+      +solarized-blue      "#268bd2"
+      +solarized-cyan      "#2aa198"
+      +solarized-green     "#859900"
+
+      ;; Darker and lighter accented colors
+      ;; Only use these in exceptional circumstances!
+      +solarized-yellow-d  "#7B6000"
+      +solarized-yellow-l  "#DEB542"
+      +solarized-orange-d  "#8B2C02"
+      +solarized-orange-l  "#F2804F"
+      +solarized-red-d     "#990A1B"
+      +solarized-red-l     "#FF6E64"
+      +solarized-magenta-d "#93115C"
+      +solarized-magenta-l "#F771AC"
+      +solarized-violet-d  "#3F4D91"
+      +solarized-violet-l  "#9EA0E5"
+      +solarized-blue-d    "#00629D"
+      +solarized-blue-l    "#69B7F0"
+      +solarized-cyan-d    "#00736F"
+      +solarized-cyan-l    "#69CABF"
+      +solarized-green-d   "#546E00"
+      +solarized-green-l   "#B4C342")
+#+end_src
+
+*** Overrides
+
+#+name: overrides-for-solarized-light
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(cursor :background ,+solarized-s-base00)
+  `(font-lock-doc-face :foreground ,+solarized-s-base1)
+  `(font-lock-preprocessor-face :foreground ,+solarized-red :bold nil)
+  `(font-lock-keyword-face :foreground ,+solarized-green :bold nil)
+  `(font-lock-builtin-face :foreground ,+solarized-s-base01 :bold t)
+  `(font-lock-function-name-face :foreground ,+solarized-blue)
+  `(font-lock-constant-face :foreground ,+solarized-blue)
+  `(font-lock-type-face :italic nil)
+  `(highlight-numbers-number :bold nil)
+  `(highlight :background ,+solarized-s-base2)
+  `(solaire-hl-line-face :background ,+solarized-halfway-highlight)
+  `(hl-line :background ,+solarized-s-base2)
+
+  `(linum :background ,+solarized-s-base2 :foreground ,+solarized-s-base1)
+  `(line-number :background ,+solarized-s-base2 :foreground ,+solarized-s-base1)
+  `(line-number-current-line :background ,+solarized-s-base2 :foreground ,+solarized-s-base1)
+  `(fringe :background ,+solarized-s-base2)
+
+  `(whitespace-line :foreground ,+solarized-red :underline t)
+
+  `(haskell-operator-face :foreground ,+solarized-green)
+  `(haskell-keyword-face :foreground ,+solarized-cyan)
+
+  `(magit-branch-local :foreground ,+solarized-blue :bold t)
+  `(magit-branch-remote :foreground ,+solarized-green :bold t)
+  `(magit-branch-remote-head :foreground ,+solarized-green :bold t :box t)
+  `(magit-branch-current :box t :bold t)
+  `(magit-header-line :background nil :foreground ,+solarized-yellow :bold t :box nil)
+  `(diff-refine-added :foreground "#dbdb9c" :background "#5b6e35" :bold nil)
+  `(magit-diff-added-highlight :foreground "#657827" :background "#efeac7" :bold nil)
+  `(diff-refine-removed :background "#8e433d" :foreground "#ffb9a1" :bold nil)
+  `(magit-diff-removed-highlight :foreground "#a33c35" :background "#ffdec8" :bold nil)
+  `(magit-diff-hunk-heading :background "#f8e8c6" :foreground "#876d26" :bold nil)
+  `(magit-diff-hunk-heading-highlight :background "#f1d49b" :foreground "#766634" :bold nil)
+  `(magit-section-heading :foreground "#b58900")
+  `(magit-filename :foreground ,+solarized-s-base00)
+  `(magit-diff-context-highlight :background ,+solarized-halfway-highlight)
+
+  `(transient-delimiter :foreground ,+solarized-s-base1)
+  `(transient-inapt-suffix :foreground ,+solarized-s-base1)
+  `(transient-inactive-value :foreground ,+solarized-s-base1)
+  `(transient-inactive-argument :foreground ,+solarized-s-base1)
+  `(transient-key-exit :foreground ,+solarized-green :bold t)
+  `(transient-key-stay :foreground ,+solarized-blue :bold t)
+  )
+  #+end_src
+
+* Keybindings and navigation
+Get the hell out of here, snipe!
+#+begin_src elisp :tangle yes
+(remove-hook 'doom-first-input-hook #'evil-snipe-mode)
+#+end_src
+
+#+begin_src emacs-lisp :tangle yes
+(map!
+ (:leader
+  "b" #'consult-buffer
+  "r" #'consult-recent-file))
+#+end_src
+
+** Flycheck
+#+begin_src elisp :tangle yes
+(evil-set-command-property 'flycheck-next-error :repeat nil)
+(evil-set-command-property 'flycheck-prev-error :repeat nil)
+(evil-set-command-property 'flycheck-previous-error :repeat nil)
+
+(map!
+ (:map flycheck-mode-map
+  :m  "]e" #'flycheck-next-error
+  :m  "[e" #'flycheck-previous-error))
+#+end_src
+
+** Smerge
+#+begin_src elisp :tangle yes
+(evil-set-command-property 'smerge-next :repeat nil)
+(evil-set-command-property 'smerge-prev :repeat nil)
+
+(map!
+ :n "] n" #'smerge-next
+ :n "[ n" #'smerge-prev
+ (:leader
+  (:desc "smerge" :prefix "g m"
+   :desc "Keep Current" :n "SPC" #'smerge-keep-current
+   :desc "Keep All"     :n "a" #'smerge-keep-all
+   :desc "Keep Upper"   :n "u" #'smerge-keep-upper
+   :desc "Keep Lower"   :n "l" #'smerge-keep-lower)))
+t
+ #+end_src
+
+** Vinegar-style dired
+#+begin_src elisp :tangle yes
+(defun dired-mode-p () (eq 'dired-mode major-mode))
+
+(defun aspen/dired-minus ()
+  (interactive)
+  (if (dired-mode-p)
+      (dired-up-directory)
+    (when buffer-file-name
+      (-> (buffer-file-name)
+          (f-dirname)
+          (dired)))))
+
+(map!
+ :n "-" #'aspen/dired-minus
+ (:map dired-mode-map
+       "-" #'aspen/dired-minus))
+#+end_src
+
+** Lisp mappings
+*** Use paxedit
+#+begin_src elisp :tangle yes
+(use-package! paxedit
+  :hook ((emacs-lisp-mode . paxedit-mode)
+         (clojure-mode . paxedit-mode)
+         (common-lisp-mode . paxedit-mode)))
+#+end_src
+
+*** Paxedit functions
+
+#+begin_src elisp :tangle yes
+(define-move-and-insert aspen/insert-at-sexp-end
+  (when (not (equal (get-char) "("))
+    (backward-up-list))
+  (forward-sexp)
+  (backward-char))
+
+(define-move-and-insert aspen/insert-at-sexp-start
+  (backward-up-list)
+  (forward-char))
+
+(define-move-and-insert aspen/insert-at-form-start
+  (backward-sexp)
+  (backward-char)
+  (insert " "))
+
+(define-move-and-insert aspen/insert-at-form-end
+  (forward-sexp)
+  (insert " "))
+
+(defun aspen/paxedit-kill (&optional n)
+  (interactive "p")
+  (or (paxedit-comment-kill)
+      (when (paxedit-symbol-cursor-within?)
+        (paxedit-symbol-kill))
+      (paxedit-implicit-sexp-kill n)
+      (paxedit-sexp-kill n)
+      (message paxedit-message-kill)))
+#+end_src
+
+*** Paxedit mappings
+#+begin_src elisp :tangle yes
+(map!
+ (:after paxedit
+         (:map paxedit-mode-map
+          :i ";"                          #'paxedit-insert-semicolon
+          :i "("                          #'paxedit-open-round
+          :i "["                          #'paxedit-open-bracket
+          :i "{"                          #'paxedit-open-curly
+          :n [remap evil-yank-line]       #'paxedit-copy
+          :n [remap evil-delete-line]     #'aspen/paxedit-kill
+          :n "g o"                        #'paxedit-sexp-raise
+          :n [remap evil-join-whitespace] #'paxedit-compress
+          :n "g S"                        #'paxedit-format-1
+          :n "g k"                        #'paxedit-backward-up
+          :n "g j"                        #'paxedit-backward-end)))
+
+(require 'general)
+(general-evil-setup t)
+
+(nmap
+  ">" (general-key-dispatch 'evil-shift-right
+        "e" 'paxedit-transpose-forward
+        ")" 'sp-forward-slurp-sexp
+        "(" 'sp-backward-barf-sexp
+        "I" 'aspen/insert-at-sexp-end
+        ;; "a" 'grfn/insert-at-form-end
+        ))
+
+(nmap
+  "<" (general-key-dispatch 'evil-shift-left
+        "e" 'paxedit-transpose-backward
+        ")" 'sp-forward-barf-sexp
+        "(" 'sp-backward-slurp-sexp
+        "I" 'aspen/insert-at-sexp-start
+        ;; "a" 'grfn/insert-at-form-start
+        ))
+#+end_src
+
+*** Eval functions
+#+begin_src elisp :tangle yes
+(use-package! predd)
+
+(predd-defmulti eval-sexp (lambda (form) major-mode))
+
+(predd-defmethod eval-sexp 'clojure-mode (form)
+  (cider-interactive-eval form))
+
+(predd-defmethod eval-sexp 'emacs-lisp-mode (form)
+  (pp-eval-expression form))
+
+(predd-defmulti eval-sexp-region (lambda (_beg _end) major-mode))
+
+(predd-defmethod eval-sexp-region 'clojure-mode (beg end)
+  (cider-interactive-eval nil nil (list beg end)))
+
+(predd-defmethod eval-sexp-region 'emacs-lisp-mode (beg end)
+  (pp-eval-expression (read (buffer-substring beg end))))
+
+(predd-defmulti eval-sexp-region-context (lambda (_beg _end _context) major-mode))
+
+(predd-defmethod eval-sexp-region-context 'clojure-mode (beg end context)
+  (cider--eval-in-context (buffer-substring beg end)))
+
+(defun pp-eval-context-region (beg end context)
+  (interactive "r\nxContext: ")
+  (let* ((inner-expr (read (buffer-substring beg end)))
+         (full-expr (list 'let* context inner-expr)))
+    (pp-eval-expression full-expr)))
+
+(predd-defmethod eval-sexp-region-context 'emacs-lisp-mode (beg end context)
+  (pp-eval-context-region beg end context))
+
+(predd-defmulti preceding-sexp (lambda () major-mode))
+
+(predd-defmethod preceding-sexp 'clojure-mode ()
+  (cider-last-sexp))
+
+(predd-defmethod preceding-sexp 'emacs-lisp-mode ()
+  (elisp--preceding-sexp))
+
+(defun eval-sexp-at-point ()
+  (interactive)
+  (let ((bounds (bounds-of-thing-at-point 'sexp)))
+    (eval-sexp-region (car bounds)
+                      (cdr bounds))))
+
+(defun eval-last-sexp (_)
+  (interactive)
+  (eval-sexp (preceding-sexp)))
+
+;;;
+
+(defun cider-insert-current-sexp-in-repl (&optional arg)
+  "Insert the expression at point in the REPL buffer.
+If invoked with a prefix ARG eval the expression after inserting it"
+  (interactive "P")
+  (cider-insert-in-repl (cider-sexp-at-point) arg))
+
+(evil-define-operator fireplace-send (beg end)
+  (cider-insert-current-sexp-in-repl nil nil (list beg end)))
+
+(defun +clojure-pprint-expr (form)
+  (format "(with-out-str (clojure.pprint/pprint %s))"
+          form))
+
+(defun cider-eval-read-and-print-handler (&optional buffer)
+  "Make a handler for evaluating and reading then printing result in BUFFER."
+  (nrepl-make-response-handler
+   (or buffer (current-buffer))
+   (lambda (buffer value)
+     (let ((value* (read value)))
+       (with-current-buffer buffer
+         (insert
+          (if (derived-mode-p 'cider-clojure-interaction-mode)
+              (format "\n%s\n" value*)
+            value*)))))
+   (lambda (_buffer out) (cider-emit-interactive-eval-output out))
+   (lambda (_buffer err) (cider-emit-interactive-eval-err-output err))
+   '()))
+
+(defun cider-eval-and-replace (beg end)
+  "Evaluate the expression in region and replace it with its result"
+  (interactive "r")
+  (let ((form (buffer-substring beg end)))
+    (cider-nrepl-sync-request:eval form)
+    (kill-region beg end)
+    (cider-interactive-eval
+     (+clojure-pprint-expr form)
+     (cider-eval-read-and-print-handler))))
+
+(defun cider-eval-current-sexp-and-replace ()
+  "Evaluate the expression at point and replace it with its result"
+  (interactive)
+  (apply #'cider-eval-and-replace (cider-sexp-at-point 'bounds)))
+
+;;;
+#+end_src
+
+*** Eval bindings
+fireplace-esque eval binding
+
+#+begin_src elisp :tangle yes
+(evil-define-operator fireplace-eval (beg end)
+  (eval-sexp-region beg end))
+
+(evil-define-operator fireplace-replace (beg end)
+  (cider-eval-and-replace beg end))
+
+(evil-define-operator fireplace-eval-context (beg end)
+  (eval-sexp-region-context beg end))
+
+(nmap :keymaps 'cider-mode-map
+  "c" (general-key-dispatch 'evil-change
+        "p" (general-key-dispatch 'fireplace-eval
+              "p" 'cider-eval-sexp-at-point
+              "c" 'cider-eval-last-sexp
+              "d" 'cider-eval-defun-at-point
+              "r" 'cider-test-run-test)
+        "q" (general-key-dispatch 'fireplace-send
+              "q" 'cider-insert-current-sexp-in-repl
+              "c" 'cider-insert-last-sexp-in-repl)
+        "x" (general-key-dispatch 'fireplace-eval-context
+              "x" 'cider-eval-sexp-at-point-in-context
+              "c" 'cider-eval-last-sexp-in-context)
+        "!" (general-key-dispatch 'fireplace-replace
+              "!" 'cider-eval-current-sexp-and-replace
+              "c" 'cider-eval-last-sexp-and-replace)
+        "y" 'cider-copy-last-result))
+
+;;;
+
+(nmap :keymaps 'emacs-lisp-mode-map
+  "c" (general-key-dispatch 'evil-change
+        "p" (general-key-dispatch 'fireplace-eval
+              "p" 'eval-sexp-at-point
+              "c" 'eval-last-sexp
+              "d" 'eval-defun
+              "r" 'cider-test-run-test)
+        "x" (general-key-dispatch 'fireplace-eval-context
+              "x" 'cider-eval-sexp-at-point-in-context
+              "c" 'cider-eval-last-sexp-in-context)
+        "!" (general-key-dispatch 'fireplace-replace
+              "!" 'cider-eval-current-sexp-and-replace
+              "c" 'cider-eval-last-sexp-and-replace)
+        "y" 'cider-copy-last-result))
+
+(nmap :keymaps 'sly-mode-map
+  "c" (general-key-dispatch 'evil-change
+        "p" (general-key-dispatch 'sly-eval
+              ;; "p" 'eval-sexp-at-point
+              "c" 'sly-eval-last-expression
+              "d" 'sly-eval-defun
+              ;; "r" 'cider-test-run-test
+              )
+        ;; "x" (general-key-dispatch 'fireplace-eval-context
+        ;;       "x" 'cider-eval-sexp-at-point-in-context
+        ;;       "c" 'cider-eval-last-sexp-in-context
+        ;;       )
+        ;; "!" (general-key-dispatch 'fireplace-replace
+        ;;       "!" 'cider-eval-current-sexp-and-replace
+        ;;       "c" 'cider-eval-last-sexp-and-replace)
+        ;; "y" 'cider-copy-last-result
+        ))
+
+#+end_src
+
+** Coerce
+
+#+begin_src elisp :tangle yes
+(use-package! string-inflection
+  :config
+  (nmap "c" (general-key-dispatch 'evil-change
+              "r c" (saving-excursion (string-inflection-lower-camelcase))
+              "r C" (saving-excursion (string-inflection-camelcase))
+              "r m" (saving-excursion (string-inflection-camelcase))
+              "r s" (saving-excursion (string-inflection-underscore))
+              "r u" (saving-excursion (string-inflection-upcase))
+              "r -" (saving-excursion (string-inflection-kebab-case))
+              "r k" (saving-excursion (string-inflection-kebab-case))
+              ;; "r ." (saving-excursion (string-inflection-dot-case))
+              ;; "r ." (saving-excursion (string-inflection-space-case))
+              ;; "r ." (saving-excursion (string-inflection-title-case))
+              )))
+#+end_src
+
+* Mode-specific config
+** org-mode
+#+begin_src elisp :tangle yes
+(after! org
+  (load! "org-config")
+  (load! "org-query"))
+#+end_src
+
+*** Theme overrides
+
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(org-drawer :foreground ,+solarized-s-base1 :bold t)
+  `(org-block :foreground ,+solarized-s-base00)
+  `(org-meta-line :foreground ,+solarized-s-base1 :italic t)
+  `(org-document-title :foreground ,+solarized-s-base01 :height 1.3)
+  `(org-done :foreground ,+solarized-green)
+  `(org-headline-done :foreground ,+solarized-green)
+  `(org-special-keyword :foreground ,+solarized-s-base1 :bold t)
+  `(org-date :foreground ,+solarized-blue :underline t)
+  `(org-table
+    :foreground ,+solarized-s-base0  ; used to be green, I think I like this better?
+    :italic t)
+  `(org-link :foreground ,+solarized-yellow)
+  `(org-todo :foreground ,+solarized-cyan)
+  `(org-code :foreground ,+solarized-s-base1)
+  `(org-block-begin-line :foreground ,+solarized-s-base1 :italic t)
+  `(org-block-end-line :foreground ,+solarized-s-base1 :italic t)
+  `(org-document-info-keyword :foreground ,+solarized-s-base1 :italic t)
+
+  `(org-level-1 :foreground ,+solarized-red)
+  `(org-level-2 :foreground ,+solarized-green)
+  `(org-level-3 :foreground ,+solarized-blue)
+  `(org-level-4 :foreground ,+solarized-yellow)
+  `(org-level-5 :foreground ,+solarized-cyan)
+  `(org-level-6 :foreground ,+solarized-violet)
+  `(org-level-7 :foreground ,+solarized-magenta)
+  `(org-level-8 :foreground ,+solarized-blue))
+#+end_src
+
+*** Commands
+#+begin_src elisp :tangle yes
+(defun grfn/insert-new-src-block ()
+  (interactive)
+  (let* ((current-src-block (org-element-at-point))
+         (src-block-head (save-excursion
+                           (goto-char (org-element-property
+                                       :begin current-src-block))
+                           (let ((line (thing-at-point 'line t)))
+                             (if (not (s-starts-with? "#+NAME:" (s-trim line)))
+                                 line
+                               (forward-line)
+                               (thing-at-point 'line t)))))
+         (point-to-insert
+          (if-let (results-loc (org-babel-where-is-src-block-result))
+              (save-excursion
+                (goto-char results-loc)
+                (org-element-property
+                 :end
+                 (org-element-at-point)))
+            (org-element-property :end (org-element-at-point)))))
+    (goto-char point-to-insert)
+    (insert "\n")
+    (insert src-block-head)
+    (let ((contents (point-marker)))
+      (insert "\n#+END_SRC\n")
+      (goto-char contents))))
+
+(defun grfn/+org-insert-item (orig direction)
+  (interactive)
+  (if (and (org-in-src-block-p)
+           (equal direction 'below))
+      (grfn/insert-new-src-block)
+    (funcall orig direction)))
+
+(advice-add #'+org--insert-item :around #'grfn/+org-insert-item)
+#+end_src
+*** Bindings
+#+begin_src elisp :tangle yes
+(map!
+ (:after org
+  :n "C-c C-x C-o" #'org-clock-out
+  (:leader
+   "n k" #'org-archive-subtree-default)
+
+  (:map org-capture-mode-map
+   :n "g RET" #'org-capture-finalize
+   :n "g \\"  #'org-captue-refile)))
+#+end_src
+
+** magit
+#+begin_src elisp :tangle yes
+(after! magit
+  (map! :map magit-mode-map
+        ;; :n "] ]" #'magit-section-forward
+        ;; :n "[ [" #'magit-section-backward
+        )
+
+  (transient-define-suffix magit-commit-wip ()
+    (interactive)
+    (magit-commit-create '("-m" "wip")))
+
+  (transient-append-suffix
+    #'magit-commit
+    ["c"]
+    (list "W" "Commit WIP" #'magit-commit-wip))
+
+  (transient-define-suffix magit-reset-head-back ()
+    (interactive)
+    (magit-reset-mixed "HEAD~"))
+
+  (transient-define-suffix magit-reset-head-previous ()
+    (interactive)
+    (magit-reset-mixed "HEAD@{1}"))
+
+  (transient-append-suffix
+    #'magit-reset
+    ["f"]
+    (list "b" "Reset HEAD~"    #'magit-reset-head-back))
+  (transient-append-suffix
+    #'magit-reset
+    ["f"]
+    (list "o" "Reset HEAD@{1}" #'magit-reset-head-previous)))
+#+end_src
+
+** elisp
+*** Org config mode
+The minor-mode for *this file*!
+
+#+begin_src elisp :tangle yes
+(after! smartparens
+  (sp-local-pair 'org-config-mode "'" "'" :actions nil)
+  (sp-local-pair 'org-config-mode "`" "`" :actions nil))
+
+(define-minor-mode org-config-mode
+  "Minor-mode for tangled org .el config"
+  :group 'org
+  :lighter "Org-config"
+  :keymap '()
+  (sp-update-local-pairs 'org-config-mode))
+#+end_src
+
+*** Bindings
+#+begin_src elisp :tangle yes
+(map!
+ (:map emacs-lisp-mode-map
+  :n "g SPC" #'eval-buffer
+  :n "g RET" (ฮป! () (ert t)) ))
+#+end_src
+
+** tuareg
+*** Config
+
+#+begin_src elisp :tangle yes
+
+(defun aspen/tuareg-setup ()
+  (setq-local sp-max-pair-length (->> '("begin" "sig" "struct")
+                                      (--map (length it))
+                                      (-max))
+              whitespace-line-column 80))
+
+(add-hook 'tuareg-mode-hook #'aspen/tuareg-setup)
+
+(defun sp-tuareg-post-handler (id action context)
+  (when (equal action 'insert)
+    (save-excursion
+      (insert "x")
+      (newline)
+      (indent-according-to-mode))
+    (delete-char 1)))
+
+(after! smartparens-ml
+  (sp-local-pair 'tuareg-mode "module" "end" :actions nil)
+
+  (dolist (pair-start '("begin" "sig" "struct"))
+    (sp-local-pair 'tuareg-mode
+                   pair-start "end"
+                   :when '(("SPC" "RET" "<evil-ret>"))
+                   :unless '(sp-in-string-p)
+                   :actions '(insert navigate)
+                   :post-handlers '(sp-tuareg-post-handler))))
+nil
+    #+end_src
+
+#+begin_src elisp :tangle yes
+(after! dune-mode
+  (add-hook 'dune-mode-hook 'paxedit-mode))
+#+end_src
+
+*** Bindings
+#+begin_src elisp :tangle yes
+(map!
+ (:map tuareg-mode-map
+  :n "g RET" (ฮป! () (compile "dune build @@runtest"))
+  :n "g SPC" #'dune-promote
+  :n "g \\" #'utop
+  :n "g y" #'merlin-locate-type
+  "C-c C-f" (ฮป! () (compile "dune fmt"))))
+#+end_src
+
+*** Theme overrides
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(tuareg-font-lock-governing-face :foreground ,+solarized-s-base01 :bold t)
+  `(tuareg-font-lock-label-face :foreground ,+solarized-blue)
+  `(tuareg-font-lock-constructor-face :foreground ,+solarized-yellow)
+  `(tuareg-font-lock-operator-face :foreground ,+solarized-red)
+  `(tuareg-font-lock-attribute-face :foreground ,+solarized-red :bold nil)
+  `(tuareg-font-lock-extension-node-face :background nil :inherit 'font-lock-preprocessor-face)
+  `(merlin-eldoc-occurrences-face :background ,+solarized-s-base2)
+  `(merlin-type-face :background ,+solarized-s-base2)
+  `(utop-prompt :foreground ,+solarized-blue)
+  `(utop-frozen :foreground ,+solarized-s-base1 :italic t)
+  `(vertico-group-title :foreground ,+solarized-s-base1)
+  `(vertico-group-header :foreground ,+solarized-s-base1))
+#+end_src
+
+** clojure
+
+*** Setup
+
+#+begin_src elisp :tangle yes
+(defun clojure-thing-at-point-setup ()
+  (interactive)
+  ;; Used by cider-find-dwim to parse the symbol at point
+  (setq-local
+   thing-at-point-file-name-chars
+   (concat thing-at-point-file-name-chars
+           "><!?")))
+
+(defun +grfn/clojure-setup ()
+  ;; (flycheck-select-checker 'clj-kondo)
+  (require 'flycheck)
+  (push 'clojure-cider-kibit flycheck-disabled-checkers)
+  (push 'clojure-cider-eastwood flycheck-disabled-checkers)
+  (push 'clojure-cider-typed flycheck-disabled-checkers)
+  )
+
+(after! clojure-mode
+  (define-clojure-indent
+    (PUT 2)
+    (POST 2)
+    (GET 2)
+    (PATCH 2)
+    (DELETE 2)
+    (context 2)
+    (checking 3)
+    (match 1)
+    (domonad 0)
+    (describe 1)
+    (before 1)
+    (it 2))
+
+  (add-hook 'clojure-mode-hook #'clojure-thing-at-point-setup)
+  (add-hook 'clojure-mode-hook #'+grfn/clojure-setup))
+
+(use-package! flycheck-clojure
+  ;; :disabled t
+  :after (flycheck cider)
+  :config
+  (flycheck-clojure-setup))
+
+(after! clj-refactor
+  (setq cljr-magic-requires :prompt
+        cljr-clojure-test-declaration "[clojure.test :refer :all]"
+        cljr-cljc-clojure-test-declaration"#?(:clj [clojure.test :refer :all]
+:cljs [cljs.test :refer-macros [deftest is testing]])"
+        )
+  (add-to-list
+   'cljr-magic-require-namespaces
+   '("s" . "clojure.spec.alpha")))
+
+(set-popup-rule! "^\\*cider-test-report" :size 0.4)
+nil
+#+end_src
+
+*** Commands
+
+#+begin_src elisp :tangle yes
+(defun grfn/run-clj-or-cljs-test ()
+  (interactive)
+  (message "Running tests...")
+  (cl-case (cider-repl-type-for-buffer)
+    (cljs
+     (cider-interactive-eval
+      "(with-out-str (cljs.test/run-tests))"
+      (nrepl-make-response-handler
+       (current-buffer)
+       (lambda (_ value)
+         (with-output-to-temp-buffer "*cljs-test-results*"
+           (print
+            (->> value
+                 (s-replace "\"" "")
+                 (s-replace "\\n" "\n")))))
+       nil nil nil)))
+    (('clj 'multi)
+     (funcall-interactively
+      #'cider-test-run-ns-tests
+      nil))))
+
+(defun cider-copy-last-result ()
+  (interactive)
+  (cider-interactive-eval
+   "*1"
+   (nrepl-make-response-handler
+    (current-buffer)
+    (lambda (_ value)
+      (kill-new value)
+      (message "Copied last result (%s) to clipboard"
+               (if (= (length value) 1) "1 char"
+                 (format "%d chars" (length value)))))
+    nil nil nil)))
+
+#+end_src
+
+*** Bindings
+
+
+#+begin_src elisp :tangle yes
+(map!
+ (:after
+  clojure-mode
+  (:map clojure-mode-map
+   :n "] f" 'forward-sexp
+   :n "[ f" 'backward-sexp))
+
+ (:after
+  cider-mode
+  (:map cider-mode-map
+   :n "g SPC" 'cider-eval-buffer
+   :n "g \\"  'cider-switch-to-repl-buffer
+   :n "K"     'cider-doc
+   :n "g K"   'cider-apropos
+   :n "g d"   'cider-find-dwim
+   :n "C-w ]" 'cider-find-dwim-other-window
+   ;; :n "g RET" 'cider-test-run-ns-tests
+   :n "g RET" 'grfn/run-clj-or-cljs-test
+   :n "g r" #'cljr-rename-symbol
+
+   "C-c C-r r" 'cljr-add-require-to-ns
+   "C-c C-r i" 'cljr-add-import-to-ns
+
+   (:localleader
+    ;; :desc "Inspect last result" :n "i" 'cider-inspect-last-result
+    ;; :desc "Search for documentation" :n "h s" 'cider-apropos-doc
+    :desc "Add require to ns" :n "n r" 'cljr-add-require-to-ns
+    :desc "Add import to ns" :n "n i" 'cljr-add-import-to-ns))
+  (:map cider-repl-mode-map
+   :n "g \\" 'cider-switch-to-last-clojure-buffer)))
+ #+end_src
+
+** rust
+#+begin_src elisp :tangle yes
+(defun aspen/rust-setup ()
+  (interactive)
+  (+evil-embrace-angle-bracket-modes-hook-h)
+  (setq-local whitespace-line-column 100
+              fill-column 100))
+
+(add-hook 'rust-mode-hook #'aspen/rust-setup)
+#+end_src
+
+*** Bindings
+
+#+begin_src elisp :tangle yes
+(map!
+ (:map rust-mode-map
+  :n "g RET" #'lsp-rust-analyzer-run
+  :n "g R" #'lsp-find-references
+  :n "g d" #'lsp-find-definition
+  :n "g Y" #'lsp-goto-type-definition
+  (:localleader
+   "m" #'lsp-rust-analyzer-expand-macro)))
+#+end_src
+
+*** Theme overrides
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(rust-unsafe :foreground ,+solarized-red))
+#+end_src
+
+** common-lisp
+*** Commands
+#+begin_src emacs-lisp :tangle yes
+(defun aspen/sly-panettone ()
+  (interactive)
+  (sly
+   (concat
+    (s-trim
+     (shell-command-to-string
+      "nix-build -o sbcl -E 'with import ~/code/depot {}; nix.buildLisp.sbclWith [web.panettone]'"))
+    "/bin/sbcl")))
+
+(defun aspen/setup-lisp ()
+  (interactive)
+  (rainbow-delimiters-mode)
+  (paxedit-mode 1)
+  (flycheck-mode -1))
+
+(add-hook 'common-lisp-mode-hook #'aspen/setup-lisp)
+
+(defun sly-run-tests ()
+  (interactive)
+  ;; TODO: handle other test frameworks
+  (let ((orig-window (get-buffer-window)))
+    (sly-eval '(fiveam:run!))
+    (funcall-interactively #'sly-mrepl-sync)
+    (select-window orig-window)))
+#+end_src
+
+*** Bindings
+
+#+begin_src emacs-lisp :tangle yes
+(map!
+ (:map sly-mode-map
+  :n "g \\" #'sly-mrepl-sync
+  :n "g d" #'sly-edit-definition
+  :n "K" #'sly-documentation
+  :n "g SPC" #'sly-compile-and-load-file
+  :n "g RET" #'sly-run-tests)
+
+ (:map sly-mrepl-mode-map
+  "C-k" #'sly-mrepl-previous-prompt
+  "C-r" #'isearch-backward))
+#+end_src
+
+* Completion
+** Corfu
+#+begin_src emacs-lisp :tangle yes
+(setopt +corfu-want-ret-to-confirm nil)
+
+(use-package! corfu
+  :demand t
+  :bind (:map corfu-map
+              ("TAB" . corfu-next)
+              ([tab] . corfu-next)
+              ("S-TAB" . corfu-previous)
+              ([backtab] . corfu-previous))
+  :init (setopt corfu-on-exact-match 'insert
+                corfu-preselect 'prompt
+                completion-cycle-threshold 1
+                corfu-quit-no-match t
+                corfu-quit-at-boundary t)
+  :config
+  (map! :map corfu-map
+        :i "TAB" #'corfu-next
+        :i [tab] #'corfu-next
+        :i "S-TAB" #'corfu-previous
+        :i [backtab] #'corfu-previous))
+#+end_src
+
+** Fuzzy search
+#+begin_src emacs-lisp :tangle yes
+(use-package! hotfuzz
+  :after (orderless corfu)
+  :config
+  (setopt completion-styles '(hotfuzz basic)
+          completion-ignore-case t))
+#+end_src
+
+* Email
+#+begin_src elisp :tangle yes
+(after! notmuch
+  (setq notmuch-saved-searches
+        '((:name "inbox" :query "tag:inbox tag:important not tag:trash" :key "i")
+          (:name "flagged" :query "tag:flagged" :key "f")
+          (:name "sent" :query "tag:sent" :key "s")
+          (:name "drafts" :query "tag:draft" :key "d")
+
+          (:name "work" :query "tag:inbox and tag:important and path:work/**"
+                 :key "w")
+          (:name "personal" :query "tag:inbox and tag:important and path:personal/**"
+                 :key "p"))
+        message-send-mail-function 'message-send-mail-with-sendmail
+        message-sendmail-f-is-evil 't
+        message-sendmail-envelope-from 'header
+        message-sendmail-extra-arguments '("--read-envelope-from")))
+
+(defun aspen/notmuch-sync ()
+  (interactive)
+  (let* ((search-buffer (current-buffer))
+         (proc (start-process-shell-command
+                "notmuch-sync"
+                "*notmuch-sync*"
+                "cd ~/mail/personal/ && gmi sync"))
+         (buf (process-buffer proc)))
+
+    (set-process-sentinel
+     proc
+     (lambda (proc msg)
+       (internal-default-process-sentinel proc msg)
+       (when (and (string= msg "finished\n"))
+         (kill-buffer buf)
+         (with-current-buffer search-buffer
+           (when (eq major-mode 'notmuch-search-mode)
+             (notmuch-refresh-this-buffer))))))
+
+    (with-current-buffer buf
+      (+popup-buffer-mode))
+    (display-buffer buf '(display-buffer-at-bottom . ()))))
+
+(set-popup-rule!
+  "^\\*notmuch-sync\\*$"
+  :select nil
+  :quit 'other)
+
+(map! :map notmuch-search-mode-map
+      :n "g SPC" #'aspen/notmuch-sync)
+#+end_src
+
+** Bindings
+#+begin_src emacs-lisp :tangle yes
+(map!
+ (:leader
+  :desc "Email" :n "o m" #'notmuch-jump-search
+  :desc "Search email" "s M" #'consult-notmuch))
+#+end_src
+
+** Theme
+
+#+begin_src emacs-lisp :tangle yes
+(custom-set-faces!
+  `(notmuch-message-summary-face
+    :background ,+solarized-halfway-highlight))
+#+end_src
+
+* Misc
+** TVL
+#+begin_src emacs-lisp :tangle yes
+(require 'tvl)
+#+end_src
+
+** Matchit
+#+begin_src elisp :tangle yes
+(use-package! evil-matchit)
+#+end_src
+** Direnv
+#+begin_src elisp :tangle yes
+(use-package! direnv
+  :config (direnv-mode))
+#+end_src
+** IRC
+*** Connecting to IRC
+
+#+begin_src elisp :tangle yes
+(defvar irc-servers
+  '("hackint"
+    "libera"))
+
+(defun irc-connect (server)
+  (interactive
+   (list (completing-read "Server: " irc-servers)))
+  (let ((pw (-> (shell-command-to-string
+                 (format "pass irccloud/%s" server))
+                (s-trim)
+                (s-lines)
+                (-last-item)))
+        (gnutls-verify-error nil))
+    (erc-tls :server "bnc.irccloud.com"
+             :port 6697
+             :nick "aspen"
+             :password (concat "bnc@"
+                               (s-trim (shell-command-to-string "hostname"))
+                               ":"
+                               pw))))
+
+(defun aspen/switch-to-erc-buffer-or-connect ()
+  (interactive)
+  (if (functionp 'erc-switch-to-buffer)
+      (call-interactively #'erc-switch-to-buffer)
+    (call-interactively #'irc-connect)))
+#+end_src
+
+#+begin_src elisp :tangle yes
+(map! :leader "o I" #'irc-connect
+      :leader "o i" #'aspen/switch-to-erc-buffer-or-connect)
+#+end_src
+
+*** IRC alerts
+#+begin_src elisp :tangle yes
+(use-package! alert)
+
+(defgroup erc-alert nil
+  "Alert me using alert.el for important ERC messages"
+  :group 'erc)
+
+(defcustom erc-noise-regexp
+  "\\(Logging in:\\|Signing off\\|You're now away\\|Welcome back\\)"
+  "This regexp matches unwanted noise."
+  :type 'regexp
+  :group 'erc)
+
+(setq tvl-enabled? t)
+
+(defun disable-tvl-notifications ()
+  (interactive)
+  (setq tvl-enabled? nil))
+
+(defun enable-tvl-notifications ()
+  (interactive)
+  (setq tvl-enabled? t))
+
+(defun erc-alert-important-p (info)
+  (let ((message (plist-get info :message))
+        (erc-message (-> info (plist-get :data) (plist-get :message)))
+        (erc-channel (-> info (plist-get :data) (plist-get :channel))))
+    (and erc-message
+         (not (or (string-match "^\\** *Users on #" message)
+                  (string-match erc-noise-regexp
+                                message)))
+         (or (and tvl-enabled?
+                  (string-equal erc-channel "#tvl"))
+             (string-match "grfn" message)))))
+
+(comment
+ last-info
+ erc-noise-regexp
+ (setq tvl-enabled? nil)
+ )
+
+(defun my-erc-hook (&optional match-type nick message)
+  "Shows a notification, when user's nick was mentioned.
+If the buffer is currently not visible, makes it sticky."
+  (setq last-message message)
+  (if (or (null match-type) (not (eq match-type 'fool)))
+      (let (alert-log-messages)
+        (alert (or message (buffer-string))
+               :severity (if (string-match "grfn" (or message ""))
+                             'high 'low)
+               :title (or nick (buffer-name))
+               :data `(:message ,(or message (buffer-string))
+                                :channel ,(or nick (buffer-name)))))))
+
+(add-hook 'erc-text-matched-hook 'my-erc-hook)
+(add-hook 'erc-insert-modify-hook 'my-erc-hook)
+
+(defun my-erc-define-alerts (&rest ignore)
+  ;; Unless the user has recently typed in the ERC buffer, highlight the fringe
+  (alert-add-rule
+   :status   '(buried visible idle)
+   :severity '(moderate high urgent)
+   :mode     'erc-mode
+   :predicate
+   #'(lambda (info)
+       (and (not (eq (current-buffer) (plist-get info :buffer)))
+            (string-match "grfn:" (plist-get info :message))))
+   :persistent
+   #'(lambda (info)
+       ;; If the buffer is buried, or the user has been idle for
+       ;; `alert-reveal-idle-time' seconds, make this alert
+       ;; persistent.  Normally, alerts become persistent after
+       ;; `alert-persist-idle-time' seconds.
+       (memq (plist-get info :status) '(buried idle)))
+   :style 'message
+   :continue t)
+
+  (alert-add-rule
+   :status 'buried
+   :mode   'erc-mode
+   :predicate #'erc-alert-important-p
+   :style 'libnotify
+   :append t)
+
+  (alert-add-rule
+   :status 'buried
+   :mode   'erc-mode
+   :predicate #'erc-alert-important-p
+   :style 'message
+   :append t)
+
+  (alert-add-rule
+   :mode 'erc-mode
+   :predicate #'erc-alert-important-p
+   :style 'log
+   :append t)
+
+  (alert-add-rule :mode 'erc-mode :style 'ignore :append t))
+
+(add-hook 'erc-connect-pre-hook 'my-erc-define-alerts)
+#+end_src
+
+*** Don't send ~:q~, etc, to the server
+#+begin_src elisp :tangle yes
+(defun fix-irc-message (msg)
+  (let ((msg (s-trim msg)))
+    (if (string-equal msg ":q") "" msg)))
+(advice-add #'erc-user-input :filter-return #'fix-irc-message)
+#+end_src
+
+*** Theme overrides
+#+begin_src elisp :tangle yes
+(custom-set-faces!
+  `(erc-button :foreground ,+solarized-blue))
+#+end_src
+
+*** TODO Nick rainbow colors
+Stole this from https://github.com/jtdaugherty/emacs-config/blob/master/common/erc-nick-colors.el.
+
+IT doesn't work though :(
+
+#+begin_src elisp :tangle yes
+(setq nick-face-list '())
+
+;; Define the list of colors to use when coloring IRC nicks.
+(setq-default erc-colors-list (list +solarized-yellow
+                                    +solarized-orange
+                                    +solarized-red
+                                    +solarized-magenta
+                                    +solarized-violet
+                                    +solarized-blue
+                                    +solarized-cyan
+                                    +solarized-green))
+
+(defun build-nick-face-list ()
+  "build-nick-face-list builds a list of new faces using the
+foreground colors specified in erc-colors-list.  The nick faces
+created here will be used to format IRC nicks."
+  (let ((i -1))
+    (setq nick-face-list
+          (mapcar
+           (lambda (COLOR)
+             (setq i (1+ i))
+             (list (custom-declare-face
+                    (make-symbol (format "erc-nick-face-%d" i))
+                    (list (list t (list :foreground COLOR)))
+                    (format "Nick face %d" i))))
+           erc-colors-list))))
+
+(defun erc-insert-nick-colors ()
+  "This insert-modify hook looks for nicks in new messages and
+computes md5(nick) and uses substring(md5_value, 0, 4) mod (length
+nick-face-list) to index the face list and produce the same face for a
+given nick each time it is seen.  We get a lot of collisions this way,
+unfortunately, but it's better than some other methods I tried.
+Additionally, if you change the order or size of the erc-colors-list,
+you'll change the colors used for nicks."
+  (if (null nick-face-list) (build-nick-face-list))
+  (save-excursion
+    (goto-char (point-min))
+    (if (looking-at "<\\([^>]*\\)>")
+        (let ((nick (match-string 1)))
+          (put-text-property (match-beginning 1) (match-end 1)
+                             'face (nth
+                                    (mod (string-to-number
+                                          (substring (md5 nick) 0 4) 16)
+                                         (length nick-face-list))
+                                    nick-face-list))))))
+
+;; This adds the ERC message insert hook.
+(add-hook 'erc-insert-modify-hook 'erc-insert-nick-colors)
+#+end_src
+
+* Hacks
+Not having this breaks elisp documentation :(
+#+begin_src elisp :tangle yes
+(defvar elisp-demos-user-files nil)
+#+end_src
diff --git a/users/aspen/emacs/init.el b/users/aspen/emacs/init.el
new file mode 100644
index 0000000000..7674d088b5
--- /dev/null
+++ b/users/aspen/emacs/init.el
@@ -0,0 +1,199 @@
+;;; init.el -*- lexical-binding: t; -*-
+
+;; This file controls what Doom modules are enabled and what order they load
+;; in. Remember to run 'doom sync' after modifying it!
+
+;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's
+;;      documentation. There you'll find a link to Doom's Module Index where all
+;;      of our modules are listed, including what flags they support.
+
+;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or
+;;      'C-c c k' for non-vim users) to view its documentation. This works on
+;;      flags as well (those symbols that start with a plus).
+;;
+;;      Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
+;;      directory (for easy access to its source code).
+
+(doom! :input
+       ;;bidi              ; (tfel ot) thgir etirw uoy gnipleh
+       ;;chinese
+       ;;japanese
+       ;;layout            ; auie,ctsrnm is the superior home row
+
+       :completion
+       ;; company             ; the ultimate code completion backend
+       corfu
+       ;;helm              ; the *other* search engine for love and life
+       ;;ido               ; the other *other* search engine...
+       ;;ivy               ; a search engine for love and life
+       vertico           ; the search engine of the future
+
+       :ui
+       ;;deft              ; notational velocity for Emacs
+       doom              ; what makes DOOM look the way it does
+       doom-dashboard    ; a nifty splash screen for Emacs
+       ;;doom-quit         ; DOOM quit-message prompts when you quit Emacs
+       (emoji +unicode)  ; ๐Ÿ™‚
+       hl-todo           ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
+       ;;hydra
+       ;;indent-guides     ; highlighted indent columns
+       ;;ligatures         ; ligatures and symbols to make your code pretty again
+       ;;minimap           ; show a map of the code on the side
+       modeline          ; snazzy, Atom-inspired modeline, plus API
+       ;;nav-flash         ; blink cursor line after big motions
+       ;;neotree           ; a project drawer, like NERDTree for vim
+       ophints           ; highlight the region an operation acts on
+       (popup +defaults)   ; tame sudden yet inevitable temporary windows
+       ;;tabs              ; a tab bar for Emacs
+       ;;treemacs          ; a project drawer, like neotree but cooler
+       ;;unicode           ; extended unicode support for various languages
+       (vc-gutter +pretty) ; vcs diff in the fringe
+       vi-tilde-fringe   ; fringe tildes to mark beyond EOB
+       ;;window-select     ; visually switch windows
+       workspaces        ; tab emulation, persistence & separate workspaces
+       ;;zen               ; distraction-free coding or writing
+
+       :editor
+       (evil +everywhere); come to the dark side, we have cookies
+       file-templates    ; auto-snippets for empty files
+       fold              ; (nigh) universal code folding
+       (format +onsave)  ; automated prettiness
+       ;;god               ; run Emacs commands without modifier keys
+       ;;lispy             ; vim for lisp, for people who don't like vim
+       ;;multiple-cursors  ; editing in many places at once
+       ;;objed             ; text object editing for the innocent
+       ;;parinfer          ; turn lisp into python, sort of
+       ;;rotate-text       ; cycle region at point between text candidates
+       snippets          ; my elves. They type so I don't have to
+       word-wrap         ; soft wrapping with language-aware indent
+
+       :emacs
+       dired             ; making dired pretty [functional]
+       electric          ; smarter, keyword-based electric-indent
+       ;;ibuffer         ; interactive buffer management
+       undo              ; persistent, smarter undo for your inevitable mistakes
+       vc                ; version-control and Emacs, sitting in a tree
+
+       :term
+       ;;eshell            ; the elisp shell that works everywhere
+       ;;shell             ; simple shell REPL for Emacs
+       ;;term              ; basic terminal emulator for Emacs
+       vterm             ; the best terminal emulation in Emacs
+
+       :checkers
+       syntax              ; tasing you for every semicolon you forget
+       (spell +flyspell) ; tasing you for misspelling mispelling
+       ;;grammar           ; tasing grammar mistake every you make
+
+       :tools
+       ;;ansible
+       ;;biblio            ; Writes a PhD for you (citation needed)
+       ;;debugger          ; FIXME stepping through code, to help you add bugs
+       direnv
+       docker
+       ;;editorconfig      ; let someone else argue about tabs vs spaces
+       ;;ein               ; tame Jupyter notebooks with emacs
+       (eval +overlay)     ; run code, run (also, repls)
+       ;;gist              ; interacting with github gists
+       lookup              ; navigate your code and its documentation
+       lsp               ; M-x vscode
+       magit             ; a git porcelain for Emacs
+       ;;make              ; run make tasks from Emacs
+       pass              ; password manager for nerds
+       ;;pdf               ; pdf enhancements
+       ;;prodigy           ; FIXME managing external services & code builders
+       ;;rgb               ; creating color strings
+       ;;taskrunner        ; taskrunner for all your projects
+       terraform         ; infrastructure as code
+       ;;tmux              ; an API for interacting with tmux
+       ;;tree-sitter       ; syntax and parsing, sitting in a tree...
+       ;;upload            ; map local to remote projects via ssh/ftp
+
+       :os
+       (:if IS-MAC macos)  ; improve compatibility with macOS
+       ;;tty               ; improve the terminal Emacs experience
+
+       :lang
+       agda              ; types of types of types of types...
+       ;;beancount         ; mind the GAAP
+       ;;(cc +lsp)         ; C > C++ == 1
+       clojure           ; java with a lisp
+       common-lisp       ; if you've seen one lisp, you've seen them all
+       ;;coq               ; proofs-as-programs
+       ;;crystal           ; ruby at the speed of c
+       ;;csharp            ; unity, .NET, and mono shenanigans
+       data              ; config/data formats
+       ;;(dart +flutter)   ; paint ui and not much else
+       ;;dhall
+       ;;elixir            ; erlang done right
+       ;;elm               ; care for a cup of TEA?
+       emacs-lisp        ; drown in parentheses
+       ;;erlang            ; an elegant language for a more civilized age
+       ;;ess               ; emacs speaks statistics
+       ;;factor
+       ;;faust             ; dsp, but you get to keep your soul
+       ;;fortran           ; in FORTRAN, GOD is REAL (unless declared INTEGER)
+       ;;fsharp            ; ML stands for Microsoft's Language
+       ;;fstar             ; (dependent) types and (monadic) effects and Z3
+       ;;gdscript          ; the language you waited for
+       ;;(go +lsp)         ; the hipster dialect
+       ;;(graphql +lsp)    ; Give queries a REST
+       (haskell +lsp)    ; a language that's lazier than I am
+       ;;hy                ; readability of scheme w/ speed of python
+       ;;idris             ; a language you can depend on
+       json              ; At least it ain't XML
+       ;;(java +lsp)       ; the poster child for carpal tunnel syndrome
+       (javascript +lsp)        ; all(hope(abandon(ye(who(enter(here))))))
+       ;;julia             ; a better, faster MATLAB
+       ;;kotlin            ; a better, slicker Java(Script)
+       ;;latex             ; writing papers in Emacs has never been so fun
+       ;;lean              ; for folks with too much to prove
+       ;;ledger            ; be audit you can be
+       ;;lua               ; one-based indices? one-based indices
+       markdown          ; writing docs for people to ignore
+       ;;nim               ; python + lisp at the speed of c
+       nix               ; I hereby declare "nix geht mehr!"
+       ocaml             ; an objective camel
+       (org               ; organize your plain life in plain text
+        +gnuplot
+        +present
+        +pretty
+        )
+       ;;php               ; perl's insecure younger brother
+       ;;plantuml          ; diagrams for confusing people more
+       ;;purescript        ; javascript, but functional
+       python            ; beautiful is better than ugly
+       ;;qt                ; the 'cutest' gui framework ever
+       ;;racket            ; a DSL for DSLs
+       ;;raku              ; the artist formerly known as perl6
+       ;;rest              ; Emacs as a REST client
+       ;;rst               ; ReST in peace
+       ;;(ruby +rails)     ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
+       (rust +lsp)       ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
+       ;;scala             ; java, but good
+       ;;(scheme +guile)   ; a fully conniving family of lisps
+       sh                ; she sells {ba,z,fi}sh shells on the C xor
+       ;;sml
+       ;;solidity          ; do you need a blockchain? No.
+       ;;swift             ; who asked for emoji variables?
+       ;;terra             ; Earth and Moon in alignment for performance.
+       web               ; the tubes
+       yaml              ; JSON, but readable
+       ;;zig               ; C, but simpler
+
+       :email
+       ;;(mu4e +org +gmail)
+       notmuch
+       ;;(wanderlust +gmail)
+
+       :app
+       ;;calendar
+       ;;emms
+       ;;everywhere        ; *leave* Emacs!? You must be joking
+       irc               ; how neckbeards socialize
+       ;;(rss +org)        ; emacs as an RSS reader
+       ;;twitter           ; twitter client https://twitter.com/vnought
+
+       :config
+       literate
+       (default +bindings +smartparens))
diff --git a/users/aspen/emacs/org-config.el b/users/aspen/emacs/org-config.el
new file mode 100644
index 0000000000..89cf7486fb
--- /dev/null
+++ b/users/aspen/emacs/org-config.el
@@ -0,0 +1,141 @@
+;;; org-config.el -*- lexical-binding: t; -*-
+
+(defun +aspen/org-setup ()
+  (setq-local truncate-lines -1)
+  (display-line-numbers-mode -1)
+  (line-number-mode -1)
+  (when-let*
+      ((path (buffer-file-name))
+       (fn (file-name-nondirectory path))
+       (equal (string-equal fn "config.org")))
+    (paxedit-mode 1)
+    (display-line-numbers-mode 1)
+    (flyspell-mode -1)
+    (org-config-mode 1)))
+
+(add-hook 'org-mode-hook #'+aspen/org-setup 50)
+
+(defun notes-file (f)
+  (concat org-directory (if (string-prefix-p "/" f) "" "/") f))
+
+(defun aspen/org-project-tag->key (tag)
+  (s-replace-regexp "^project__" "" tag))
+
+(defun aspen/org-project-tag->name (tag)
+  (s-titleized-words
+   (s-join " " (s-split "_" (aspen/org-project-tag->key tag)))))
+
+(defun aspen/org-project-tag->keys (tag)
+  (s-join "" (cons "p"
+                   (-map (lambda (s) (substring-no-properties s 0 1))
+                         (s-split "_" (aspen/org-project-tag->key tag))))))
+
+(defun aspen/org-projects->agenda-commands (project-tags)
+  (cl-loop for tag in project-tags
+           collect `(,(aspen/org-project-tag->keys tag)
+                     ,(aspen/org-project-tag->name tag)
+                     tags-todo
+                     ,tag)))
+
+(defun aspen/org-projects ()
+  (cl-loop for (tag) in
+           (org-global-tags-completion-table
+            (directory-files-recursively "~/notes" "\\.org$"))
+           when (s-starts-with-p "project__" tag)
+           collect tag))
+
+(comment
+ (aspen/org-projects->agenda-commands (aspen/org-projects))
+ )
+
+(setq
+ org-directory (expand-file-name "~/notes")
+ +org-dir (expand-file-name "~/notes")
+ org-default-notes-file (concat org-directory "/inbox.org")
+ +org-default-todo-file (concat org-directory "/inbox.org")
+ org-agenda-files (directory-files-recursively
+                   "~/notes" "\\.org$")
+ org-refile-targets '((org-agenda-files :maxlevel . 3))
+ org-outline-path-complete-in-steps nil
+ org-refile-use-outline-path t
+ org-file-apps `((auto-mode . emacs)
+                 (,(rx (or (and "." (optional "x") (optional "htm") (optional "l") buffer-end)
+                           (and buffer-start "http" (optional "s") "://")))
+                  . "firefox %s")
+                 (,(rx ".pdf" buffer-end) . "apvlv %s")
+                 (,(rx "." (or "png"
+                               "jpg"
+                               "jpeg"
+                               "gif"
+                               "tif"
+                               "tiff")
+                       buffer-end)
+                  . "feh %s"))
+ org-log-done 'time
+ org-archive-location "~/notes/trash::* From %s"
+ org-cycle-separator-lines 2
+ org-hidden-keywords '(title)
+ org-tags-column -130
+ org-ellipsis "โ€ฆ"
+ org-imenu-depth 9
+ org-capture-templates
+ `(("t" "Todo" entry
+    (file +org-default-todo-file)
+    "* TODO %?\n%i"
+    :kill-buffer t)
+
+   ("m" "Email" entry
+    (file +org-default-todo-file)
+    "* TODO [[%L][%:subject]] :email:\n%i")
+
+   ("n" "Notes" entry
+    (file +org-default-todo-file)
+    "* %U %?\n%i"
+    :prepend t
+    :kill-buffer t)
+
+   ("c" "Task note" entry
+    (clock)
+    "* %U %?\n%i[%l[Context]]\n"
+    :kill-buffer t
+    :unnarrowed t)
+
+   ("p" "Projects")
+   ("px" "Xanthous" entry
+    (file+headline ,(notes-file "xanthous.org") "Backlog")
+    "* TODO %?\nContext %a\nIn task: %K")
+   ("pt" "Tvix" entry
+    (file+headline ,(notes-file "tvix.org") "Tvix TODO")
+    "* TODO %?\nContext %a\nIn task: %K")
+   ("pw" "Windtunnel" entry
+    (file+headline ,(notes-file "windtunnel.org") "Inbox")
+    "* TODO %i%?\nContext: %a\nIn task: %K")
+   )
+
+ org-capture-templates-contexts
+ `(("px" ((in-file . "/home/aspen/code/depot/users/aspen/xanthous/.*")))
+   ("e" ((in-mode . "notmuch-show-mode"))))
+
+ org-deadline-warning-days 1
+ org-agenda-skip-scheduled-if-deadline-is-shown 'todo
+ org-todo-keywords '((sequence "TODO(t)" "ACTIVE(a)" "|" "DONE(d)" "RUNNING(r)")
+                     (sequence "NEXT(n)" "WAITING(w)" "LATER(l)" "|" "CANCELLED(c)"))
+ org-agenda-custom-commands
+ `(("i" "Inbox" tags "inbox")
+   ("r" "Running jobs" todo "RUNNING")
+   ("w" "@Work" tags-todo "@work")
+   ("n" . "Next...")
+   ("nw" "Next @Work" tags-todo "@work&next")
+   ("nt" "Next tooling" tags-todo "tooling")
+
+   ;; ("p" . "Project...")
+   ;; ,@(aspen/org-projects->agenda-commands (aspen/org-projects))
+   )
+
+ org-agenda-dim-blocked-tasks nil
+ org-enforce-todo-dependencies nil
+
+ org-babel-clojure-backend 'cider)
+
+(setq whitespace-global-modes '(not org-mode magit-mode vterm-mode))
+(setf (alist-get 'file org-link-frame-setup) 'find-file-other-window)
diff --git a/users/aspen/emacs/packages.el b/users/aspen/emacs/packages.el
new file mode 100644
index 0000000000..0bcc345d88
--- /dev/null
+++ b/users/aspen/emacs/packages.el
@@ -0,0 +1,14 @@
+;; -*- no-byte-compile: t; -*-
+;;; $DOOMDIR/packages.el
+
+(package! dash)
+(package! paxedit)
+(package! predd
+  :recipe (:host github :repo "skeeto/predd"))
+(package! direnv)
+(package! alert)
+(package! flycheck-clojure)
+(package! evil-matchit)
+(package! string-inflection)
+(package! protobuf-mode)
+(package! hotfuzz)
diff --git a/users/aspen/emacs/snippets/haskell-mode/annotation b/users/aspen/emacs/snippets/haskell-mode/annotation
new file mode 100644
index 0000000000..8a2854d759
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/annotation
@@ -0,0 +1,5 @@
+# key: ann
+# name: annotation
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# ANN ${1:module} ("${2:HLint: ignore ${3:Reduce duplication}}" :: String) #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/benchmark-module b/users/aspen/emacs/snippets/haskell-mode/benchmark-module
new file mode 100644
index 0000000000..cbb1646e41
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/benchmark-module
@@ -0,0 +1,26 @@
+# key: bench
+# name: benchmark-module
+# expand-env: ((yas-indent-line (quote fixed)))
+# --
+--------------------------------------------------------------------------------
+module ${1:`(if (not buffer-file-name) "Module"
+                (let ((name (file-name-sans-extension (buffer-file-name)))
+                      (case-fold-search nil))
+                     (if (cl-search "bench/" name)
+                         (replace-regexp-in-string "/" "."
+                           (replace-regexp-in-string "^\/[^A-Z]*" ""
+                             (car (last (split-string name "src")))))
+                         (file-name-nondirectory name))))`} ( benchmark, main ) where
+--------------------------------------------------------------------------------
+import Bench.Prelude
+--------------------------------------------------------------------------------
+import ${1:$(s-chop-suffix "Bench" yas-text)}
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain [benchmark]
+
+--------------------------------------------------------------------------------
+
+benchmark :: Benchmark
+benchmark = bgroup "${1:$(->> yas-text (s-chop-suffix "Bench") (s-split ".") -last-item)}" [bench "something dumb" $ nf (1 +) (1 :: Int)]
diff --git a/users/aspen/emacs/snippets/haskell-mode/header b/users/aspen/emacs/snippets/haskell-mode/header
new file mode 100644
index 0000000000..fdd8250d86
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/header
@@ -0,0 +1,5 @@
+# key: hh
+# name: header
+# expand-env: ((yas-indent-line 'fixed))
+# --
+--------------------------------------------------------------------------------$2
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/hedgehog-generator b/users/aspen/emacs/snippets/haskell-mode/hedgehog-generator
new file mode 100644
index 0000000000..68863f7054
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/hedgehog-generator
@@ -0,0 +1,8 @@
+# key: gen
+# name: Hedgehog Generator
+# expand-env: ((yas-indent-line (quote fixed)))
+# --
+gen${1:Foo} :: Gen $1
+gen$1 = do
+  $2
+  pure $1{..}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/hedgehog-property b/users/aspen/emacs/snippets/haskell-mode/hedgehog-property
new file mode 100644
index 0000000000..bf39a2a3ee
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/hedgehog-property
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: Hedgehog Property
+# key: hprop
+# expand-env: ((yas-indent-line 'fixed))
+# --
+hprop_${1:somethingIsAlwaysTrue} :: Property
+hprop_$1 = property $ do
+  ${2:x} <- forAll ${3:Gen.int $ Range.linear 1 100}
+  ${4:x === x}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/hlint b/users/aspen/emacs/snippets/haskell-mode/hlint
new file mode 100644
index 0000000000..f25a9b8d40
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/hlint
@@ -0,0 +1,8 @@
+# -*- mode: snippet -*-
+# name: hlint
+# uuid: hlint
+# expand-env: ((yas-indent-line 'fixed))
+# key: hlint
+# condition: t
+# --
+{-# ANN module ("Hlint: ignore $1" :: String) #- }
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/import-i b/users/aspen/emacs/snippets/haskell-mode/import-i
new file mode 100644
index 0000000000..4a7fca2c2f
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/import-i
@@ -0,0 +1,4 @@
+# key: i
+# name: import-i
+# --
+import           ${1:Prelude}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/inl b/users/aspen/emacs/snippets/haskell-mode/inl
new file mode 100644
index 0000000000..6e17b83d71
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/inl
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: inl
+# key: inl
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# INLINE $1 #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/inline b/users/aspen/emacs/snippets/haskell-mode/inline
new file mode 100644
index 0000000000..1beafbe50b
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/inline
@@ -0,0 +1,5 @@
+# key: inline
+# name: inline
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# INLINE $1 #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/language pragma b/users/aspen/emacs/snippets/haskell-mode/language pragma
new file mode 100644
index 0000000000..6f84720f45
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/language pragma
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: language pragma
+# key: lang
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# LANGUAGE $1 #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/lens.field b/users/aspen/emacs/snippets/haskell-mode/lens.field
new file mode 100644
index 0000000000..b22ea3d2e8
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/lens.field
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: lens.field
+# key: lens
+# expand-env: ((yas-indent-line 'fixed))
+# --
+${1:field} :: Lens' ${2:Source} ${3:Target}
+$1 = lens _${4:sourceField} $ \\${2:$(-> yas-text s-word-initials s-downcase)} ${4:$(-> yas-text s-word-initials s-downcase)} -> ${2:$(-> yas-text s-word-initials s-downcase)} { _$4 = ${4:$(-> yas-text s-word-initials s-downcase)} }
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/module b/users/aspen/emacs/snippets/haskell-mode/module
new file mode 100644
index 0000000000..4554d33f9b
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/module
@@ -0,0 +1,32 @@
+# -*- mode: snippet -*-
+# key: module
+# name: module
+# condition: (= (length "module") (current-column))
+# expand-env: ((yas-indent-line 'fixed))
+# contributor: Luke Hoersten <luke@hoersten.org>
+# --
+--------------------------------------------------------------------------------
+-- |
+-- Module      : $1
+-- Description : $2
+-- Maintainer  : Griffin Smith <grfn@urbint.com>
+-- Maturity    : ${3:Draft, Usable, Maintained, OR MatureAF}
+--
+-- $4
+--------------------------------------------------------------------------------
+module ${1:`(if (not buffer-file-name) "Module"
+                (let ((name (file-name-sans-extension (buffer-file-name)))
+                      (case-fold-search nil))
+                     (if (or (cl-search "src/" name)
+                             (cl-search "test/" name))
+                         (replace-regexp-in-string "/" "."
+                           (replace-regexp-in-string "^\/[^A-Z]*" ""
+                             (car (last (split-string name "src")))))
+                         (file-name-nondirectory name))))`}
+  (
+  ) where
+--------------------------------------------------------------------------------
+import Prelude
+--------------------------------------------------------------------------------
+
+$0
diff --git a/users/aspen/emacs/snippets/haskell-mode/shut up, hlint b/users/aspen/emacs/snippets/haskell-mode/shut up, hlint
new file mode 100644
index 0000000000..fccff1d66f
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/shut up, hlint
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: shut up, hlint
+# key: dupl
+# expand-env: ((yas-indent-line 'fixed))
+# --
+{-# ANN module ("HLint: ignore Reduce duplication" :: String) #-}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/test-group b/users/aspen/emacs/snippets/haskell-mode/test-group
new file mode 100644
index 0000000000..bf6a66f8a3
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/test-group
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: test-group
+# uuid: test-group
+# key: testGroup
+# condition: t
+# --
+testGroup "${1:name}"
+[ $0
+]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/test-module b/users/aspen/emacs/snippets/haskell-mode/test-module
new file mode 100644
index 0000000000..036b0ae998
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/test-module
@@ -0,0 +1,27 @@
+# -*- mode: snippet -*-
+# name: test-module
+# key: test
+# expand-env: ((yas-indent-line 'fixed))
+# --
+--------------------------------------------------------------------------------
+module ${1:`(if (not buffer-file-name) "Module"
+                (let ((name (file-name-sans-extension (buffer-file-name)))
+                      (case-fold-search nil))
+                     (if (cl-search "test/" name)
+                         (replace-regexp-in-string "/" "."
+                           (replace-regexp-in-string "^\/[^A-Z]*" ""
+                             (car (last (split-string name "src")))))
+                         (file-name-nondirectory name))))`} (main, test) where
+--------------------------------------------------------------------------------
+import           Test.Prelude
+--------------------------------------------------------------------------------
+import           ${1:$(s-chop-suffix "Spec" yas-text)}
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain test
+
+test :: TestTree
+test = testGroup "$1"
+  [ $0
+  ]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/haskell-mode/undefined b/users/aspen/emacs/snippets/haskell-mode/undefined
new file mode 100644
index 0000000000..7bcd99b571
--- /dev/null
+++ b/users/aspen/emacs/snippets/haskell-mode/undefined
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: undefined
+# key: u
+# expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil))
+# --
+undefined$1
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/action-type b/users/aspen/emacs/snippets/js2-mode/action-type
new file mode 100644
index 0000000000..ef8d1a3863
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/action-type
@@ -0,0 +1,4 @@
+# key: at
+# name: action-type
+# --
+export const ${1:FOO_BAR$(->> yas-text s-upcase (s-replace-all '(("-" . "_") (" " . "_"))))}: '${3:ns}/${1:$(-> yas-text s-dashed-words)}' = '$3/${1:$(-> yas-text s-dashed-words)}'$5
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/before b/users/aspen/emacs/snippets/js2-mode/before
new file mode 100644
index 0000000000..4569b65831
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/before
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: before
+# key: bef
+# --
+before(function() {
+                  $1
+})
diff --git a/users/aspen/emacs/snippets/js2-mode/context b/users/aspen/emacs/snippets/js2-mode/context
new file mode 100644
index 0000000000..d83809f3c3
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/context
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: context
+# key: context
+# --
+context('$1', function() {
+              $2
+})
diff --git a/users/aspen/emacs/snippets/js2-mode/describe b/users/aspen/emacs/snippets/js2-mode/describe
new file mode 100644
index 0000000000..bd0198181d
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/describe
@@ -0,0 +1,6 @@
+# key: desc
+# name: describe
+# --
+describe('$1', () => {
+  $2
+})
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/expect b/users/aspen/emacs/snippets/js2-mode/expect
new file mode 100644
index 0000000000..eba41ef330
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/expect
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: expect
+# key: ex
+# --
+expect($1).$2
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/function b/users/aspen/emacs/snippets/js2-mode/function
new file mode 100644
index 0000000000..b423044b44
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/function
@@ -0,0 +1,6 @@
+# key: f
+# name: function
+# --
+function $1($2) {
+         $3
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/header b/users/aspen/emacs/snippets/js2-mode/header
new file mode 100644
index 0000000000..3e303764cb
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/header
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: header
+# key: hh
+# expand-env: ((yas-indent-line 'fixed))
+# --
+////////////////////////////////////////////////////////////////////////////////
diff --git a/users/aspen/emacs/snippets/js2-mode/it b/users/aspen/emacs/snippets/js2-mode/it
new file mode 100644
index 0000000000..a451cfc08a
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/it
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: it
+# key: it
+# --
+it('$1', () => {
+  $2
+})
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/it-pending b/users/aspen/emacs/snippets/js2-mode/it-pending
new file mode 100644
index 0000000000..00da312e10
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/it-pending
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: it-pending
+# key: xi
+# --
+it('$1')$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/module b/users/aspen/emacs/snippets/js2-mode/module
new file mode 100644
index 0000000000..dc79819d89
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/module
@@ -0,0 +1,12 @@
+# key: module
+# name: module
+# expand-env: ((yas-indent-line (quote fixed)))
+# condition: (= (length "module") (current-column))
+# --
+/**
+ * @fileOverview $1
+ * @name ${2:`(file-name-nondirectory (buffer-file-name))`}
+ * @author Griffin Smith
+ * @license Proprietary
+ */
+$3
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/record b/users/aspen/emacs/snippets/js2-mode/record
new file mode 100644
index 0000000000..0bb0f02436
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/record
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: record
+# key: rec
+# --
+export default class $1 extends Record({
+  $2
+}) {}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/js2-mode/test b/users/aspen/emacs/snippets/js2-mode/test
new file mode 100644
index 0000000000..938d490a74
--- /dev/null
+++ b/users/aspen/emacs/snippets/js2-mode/test
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: test
+# key: test
+# --
+test('$1', () => {
+  $2
+})
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/nix-mode/fetchFromGitHub b/users/aspen/emacs/snippets/nix-mode/fetchFromGitHub
new file mode 100644
index 0000000000..d2447e4b5a
--- /dev/null
+++ b/users/aspen/emacs/snippets/nix-mode/fetchFromGitHub
@@ -0,0 +1,12 @@
+# -*- mode: snippet -*-
+# name: fetchFromGitHub
+# uuid: fetchFromGitHub
+# key: fetchFromGitHub
+# condition: t
+# --
+fetchFromGitHub {
+                owner = "$1";
+                repo = "$2";
+                rev = "$3";
+                sha256 = "0000000000000000000000000000000000000000000000000000";
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/nix-mode/pythonPackage b/users/aspen/emacs/snippets/nix-mode/pythonPackage
new file mode 100644
index 0000000000..0a74c21e18
--- /dev/null
+++ b/users/aspen/emacs/snippets/nix-mode/pythonPackage
@@ -0,0 +1,16 @@
+# key: pypkg
+# name: pythonPackage
+# condition: t
+# --
+${1:pname} = buildPythonPackage rec {
+           name = "\${pname}-\${version}";
+           pname = "$1";
+           version = "${2:1.0.0}";
+           src = fetchPypi {
+               inherit pname version;
+               sha256 = "0000000000000000000000000000000000000000000000000000";
+           };
+           propagatedBuildInputs = with pythonSelf; [
+               $3
+           ];
+};
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/nix-mode/sha256 b/users/aspen/emacs/snippets/nix-mode/sha256
new file mode 100644
index 0000000000..bc640e5ab0
--- /dev/null
+++ b/users/aspen/emacs/snippets/nix-mode/sha256
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: sha256
+# uuid: sha256
+# key: sha256
+# condition: t
+# --
+sha256 = "0000000000000000000000000000000000000000000000000000";
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/SQL source block b/users/aspen/emacs/snippets/org-mode/SQL source block
new file mode 100644
index 0000000000..b5d43fd6bc
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/SQL source block
@@ -0,0 +1,6 @@
+# key: sql
+# name: SQL source block
+# --
+#+BEGIN_SRC sql ${1::async}
+$2
+#+END_SRC
diff --git a/users/aspen/emacs/snippets/org-mode/combat b/users/aspen/emacs/snippets/org-mode/combat
new file mode 100644
index 0000000000..b4db0f433a
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/combat
@@ -0,0 +1,13 @@
+# -*- mode: snippet -*-
+# name: combat
+# uuid: combat
+# key: combat
+# condition: t
+# --
+|             | initiative | max hp | current hp | status |      |
+|-------------+------------+--------+------------+--------+------|
+| Barty Barty |            |        |            |        | <--- |
+| Hectoroth   |            |        |            |        |      |
+| Xanadu      |            |        |            |        |      |
+| Aurora      |            |        |            |        |      |
+| EFB         |            |        |            |        |      |
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/date b/users/aspen/emacs/snippets/org-mode/date
new file mode 100644
index 0000000000..297529cdac
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/date
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# key: date
+# name: date.org
+# --
+[`(format-time-string "%Y-%m-%d")`]$0
diff --git a/users/aspen/emacs/snippets/org-mode/date-time b/users/aspen/emacs/snippets/org-mode/date-time
new file mode 100644
index 0000000000..fde469276c
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/date-time
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: date-time
+# key: dt
+# --
+[`(format-time-string "%Y-%m-%d %H:%m:%S")`]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/description b/users/aspen/emacs/snippets/org-mode/description
new file mode 100644
index 0000000000..a43bc95cc3
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/description
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: description
+# key: desc
+# --
+:DESCRIPTION:
+$1
+:END:
diff --git a/users/aspen/emacs/snippets/org-mode/nologdone b/users/aspen/emacs/snippets/org-mode/nologdone
new file mode 100644
index 0000000000..e5be85d6b3
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/nologdone
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: nologdone
+# key: nologdone
+# --
+#+STARTUP: nologdone$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/python source block b/users/aspen/emacs/snippets/org-mode/python source block
new file mode 100644
index 0000000000..247ae51b0b
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/python source block
@@ -0,0 +1,6 @@
+# key: py
+# name: Python source block
+# --
+#+BEGIN_SRC python
+$0
+#+END_SRC
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/reveal b/users/aspen/emacs/snippets/org-mode/reveal
new file mode 100644
index 0000000000..1bdbdfa5dc
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/reveal
@@ -0,0 +1,6 @@
+# key: reveal
+# name: reveal
+# condition: t
+# --
+#+ATTR_REVEAL: :frag ${1:roll-in}
+$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/org-mode/transaction b/users/aspen/emacs/snippets/org-mode/transaction
new file mode 100644
index 0000000000..37f2dd31ca
--- /dev/null
+++ b/users/aspen/emacs/snippets/org-mode/transaction
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: transaction
+# key: begin
+# --
+BEGIN;
+$0
+ROLLBACK;
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/prolog-mode/tests b/users/aspen/emacs/snippets/prolog-mode/tests
new file mode 100644
index 0000000000..a9d92a0d5b
--- /dev/null
+++ b/users/aspen/emacs/snippets/prolog-mode/tests
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: tests
+# uuid: tests
+# key: tests
+# condition: t
+# --
+:- begin_tests(${1:name}).
+
+$0
+
+:- end_tests($1).
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/prolog-mode/use-module b/users/aspen/emacs/snippets/prolog-mode/use-module
new file mode 100644
index 0000000000..75fd19b641
--- /dev/null
+++ b/users/aspen/emacs/snippets/prolog-mode/use-module
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: use-module
+# uuid: use-module
+# key: use
+# condition: t
+# --
+:- use_module(${1:library($2)}${3:, [$4]}).
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/add_column b/users/aspen/emacs/snippets/python-mode/add_column
new file mode 100644
index 0000000000..47e83850d5
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/add_column
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: add_column
+# key: op.add_column
+# --
+op.add_column('${1:table}', sa.Column('${2:name}', sa.${3:String()}))$0
diff --git a/users/aspen/emacs/snippets/python-mode/decorate b/users/aspen/emacs/snippets/python-mode/decorate
new file mode 100644
index 0000000000..4f96748572
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/decorate
@@ -0,0 +1,15 @@
+# -*- mode: snippet -*-
+# name: decorate
+# uuid: decorate
+# key: decorate
+# condition: t
+# --
+def wrap(inner):
+    @wraps(inner)
+    def wrapped(*args, **kwargs):
+        ret = inner(*args, **kwargs)
+        return ret
+
+    return wrapped
+
+return wrap
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/dunder b/users/aspen/emacs/snippets/python-mode/dunder
new file mode 100644
index 0000000000..71d99dddc6
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/dunder
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: dunder
+# uuid: dunder
+# key: du
+# condition: t
+# --
+__$1__$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/name b/users/aspen/emacs/snippets/python-mode/name
new file mode 100644
index 0000000000..1495cc91d9
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/name
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: name
+# uuid: name
+# key: name
+# condition: t
+# --
+__name__
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/python-mode/op.get_bind.execute b/users/aspen/emacs/snippets/python-mode/op.get_bind.execute
new file mode 100644
index 0000000000..aba801c6ba
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/op.get_bind.execute
@@ -0,0 +1,7 @@
+# key: exec
+# name: op.get_bind.execute
+# --
+op.get_bind().execute(
+    """
+    `(progn (sqlup-mode) "")`$1
+    """)
diff --git a/users/aspen/emacs/snippets/python-mode/pdb b/users/aspen/emacs/snippets/python-mode/pdb
new file mode 100644
index 0000000000..41c6f87cbf
--- /dev/null
+++ b/users/aspen/emacs/snippets/python-mode/pdb
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: pdb
+# uuid: pdb
+# key: pdb
+# condition: t
+# --
+import pdb; pdb.set_trace()
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/#[macro_use] b/users/aspen/emacs/snippets/rust-mode/#[macro_use]
new file mode 100644
index 0000000000..fea942a337
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/#[macro_use]
@@ -0,0 +1,5 @@
+# key: macro_use
+# name: #[macro_use]
+# --
+#[macro_use]
+${1:extern crate} ${2:something};$0
diff --git a/users/aspen/emacs/snippets/rust-mode/async test b/users/aspen/emacs/snippets/rust-mode/async test
new file mode 100644
index 0000000000..2352d7b56b
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/async test
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: async test
+# uuid: atest
+# key: atest
+# condition: t
+# --
+#[tokio::test${1:(flavor = "multi_thread")}]
+async fn ${2:test_name}() {
+   `%`$0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/benchmark b/users/aspen/emacs/snippets/rust-mode/benchmark
new file mode 100644
index 0000000000..9ec4307538
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/benchmark
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: benchmark
+# uuid: benchmark
+# key: bench
+# condition: t
+# --
+#[bench]
+fn ${1:benchmark_name}(b: &mut Bencher) {
+   `%`b.iter(|| $0);
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/proptest b/users/aspen/emacs/snippets/rust-mode/proptest
new file mode 100644
index 0000000000..be12af4911
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/proptest
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: proptest
+# uuid: proptest
+# key: proptest
+# condition: t
+# --
+#[proptest]
+fn ${1:test_name}($2) {
+   `%`$0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/test-module b/users/aspen/emacs/snippets/rust-mode/test-module
new file mode 100644
index 0000000000..bfa2ca2d18
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/test-module
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: test-module
+# uuid: test-module
+# key: tmod
+# condition: t
+# --
+mod $1 {
+    use super::*;
+
+    $0
+}
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/rust-mode/tests b/users/aspen/emacs/snippets/rust-mode/tests
new file mode 100644
index 0000000000..0a476ab586
--- /dev/null
+++ b/users/aspen/emacs/snippets/rust-mode/tests
@@ -0,0 +1,9 @@
+# key: tests
+# name: test module
+# --
+#[cfg(test)]
+mod ${1:tests} {
+    use super::*;
+
+    $0
+}
diff --git a/users/aspen/emacs/snippets/snippet-mode/indent b/users/aspen/emacs/snippets/snippet-mode/indent
new file mode 100644
index 0000000000..d38ffceafb
--- /dev/null
+++ b/users/aspen/emacs/snippets/snippet-mode/indent
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: indent
+# key: indent
+# --
+# expand-env: ((yas-indent-line 'fixed))
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/sql-mode/count(*) group by b/users/aspen/emacs/snippets/sql-mode/count(*) group by
new file mode 100644
index 0000000000..6acc46ff39
--- /dev/null
+++ b/users/aspen/emacs/snippets/sql-mode/count(*) group by
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: count(*) group by
+# key: countby
+# --
+SELECT count(*), ${1:column} FROM ${2:table} GROUP BY $1;
diff --git a/users/aspen/emacs/snippets/terraform-mode/variable b/users/aspen/emacs/snippets/terraform-mode/variable
new file mode 100644
index 0000000000..14822f1a05
--- /dev/null
+++ b/users/aspen/emacs/snippets/terraform-mode/variable
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: variable
+# uuid: variable
+# key: var
+# condition: t
+# --
+variable "${1:name}" {
+  type = ${2:string}
+  ${3:default = ${4:default}}
+}
+$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/text-mode/date b/users/aspen/emacs/snippets/text-mode/date
new file mode 100644
index 0000000000..7b94311470
--- /dev/null
+++ b/users/aspen/emacs/snippets/text-mode/date
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# name: date
+# key: date
+# --
+`(format-time-string "%Y-%m-%d")`$0
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/tuareg-mode/expect-test b/users/aspen/emacs/snippets/tuareg-mode/expect-test
new file mode 100644
index 0000000000..e0b541fce4
--- /dev/null
+++ b/users/aspen/emacs/snippets/tuareg-mode/expect-test
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: expect-test
+# uuid: expect-test
+# key: exp
+# condition: t
+# --
+let%expect_test "${1:name}" =
+        ${2:<body>};
+        [%expect {| $3 |}]
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/tuareg-mode/module b/users/aspen/emacs/snippets/tuareg-mode/module
new file mode 100644
index 0000000000..9b1701e3a2
--- /dev/null
+++ b/users/aspen/emacs/snippets/tuareg-mode/module
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: module
+# uuid: module
+# key: mod
+# condition: t
+# --
+module ${1:Name} = struct
+       $0
+end
\ No newline at end of file
diff --git a/users/aspen/emacs/snippets/tuareg-mode/test-module b/users/aspen/emacs/snippets/tuareg-mode/test-module
new file mode 100644
index 0000000000..b16176e5f3
--- /dev/null
+++ b/users/aspen/emacs/snippets/tuareg-mode/test-module
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: test-module
+# uuid: test-module
+# key: tmod
+# condition: t
+# --
+let%test_module ${1:_} =
+  (module struct
+    $0
+  end)
\ No newline at end of file
diff --git a/users/aspen/goodcry-band/flower-icon.svg b/users/aspen/goodcry-band/flower-icon.svg
new file mode 100644
index 0000000000..b6be590293
--- /dev/null
+++ b/users/aspen/goodcry-band/flower-icon.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1771.11 1435.15"><defs><style>.cls-1{stroke-width:0px;}</style></defs><g id="Vector_layer"><path class="cls-1" d="M1771.11,875.48c-.33-35.4-10.09-70.1-27.29-101.01-11.08-19.24-20.82-39.78-36.96-55.42-10.4-9.11-17.83-20.72-26.21-31.56-9.33-10.95-21.45-19-30.38-30.36-27.35-22.24-12.76-34.32-1.94-60.9,8.79-20.05,13.71-41.66,19.19-62.79,3.57-18.58.18-37.72-1.37-56.39-1.58-22.27-1.44-45.19-10.32-66.32-14.2-47.8-36.49-95.61-75.58-128.05-27.22-18.93-57.09-39.56-91.51-39.59-9.48.98-18.42,4.67-27.92,5.76-42.77,16.76-26.99-42.54-37.06-64.98-7.1-26.54-27.89-32.48-52.51-35.43-35.43-4.89-71.98,9.13-106.54-3.49-27.14-13.8-43.12-42.06-67.02-60.24-55.12-45.05-129.47-62.61-199.5-64.59-34.05-.15-68.54-.65-102.33,3.82-19.99,5.97-41.12,6.98-60.85,13.73-11.73,4.12-21.82,11.56-32.67,17.44-14.45,6.32-26.44,17.64-31.74,32.68-41.27-86.06-119.21-114.31-200.79-60.09-37.38,32.01-56.19,79.49-66.8,126.38-7.07,39.24-12.98,79.64-1.49,118.7,4.49,14.6,11.02,28.51,17.25,42.44,5.46,12.25,22.04,36.07,11.56,47.99-12.29-15.01-16.12-37.44-24.48-55.11-12.75-41.95-18.76-86.69-38.95-126.03-8.5-12.98-18.32-25.96-32.06-33.73-14.3-6.37-31.26-12.33-46.87-7.92-31.92,11.75-45.64,44.4-55.98,74.29-5.87-22.48-17.95-43.18-38.16-55.48-15.69-13.73-36.01-16.25-56.09-15.83-50.91-2.01-98.86,27.31-134.89,61.03C39.6,275.57,4.76,342.32.4,439.12c-1.8,28,2.8,56.49,8.82,83.77,16.71,43,43.38,81.31,75.86,113.96-2.74,32.46.58,65.14,11.69,95.83-31.32-24.65-81.15-18.06-83.79,27.85-3.23,22.9-4.91,46.38-2.93,69.45,5.2,20.4,8.5,41.54,16.9,60.96,18.83,41.77,41.21,82.18,71.7,116.58,20.37,30.63,47.6,55.15,73.07,81.38,10.37,11.22,21.87,21.21,33.82,30.69,17.27,17.57,36.39,32.9,55.96,47.8,10.67,8.76,23.16,14.67,34.97,21.63,15.61,11.02,32.8,19.22,50.78,25.58,17.47,7.91,35.94,13,54.66,16.96,19.23,5.22,38.06,12.06,57.93,14.63,24.68,3.06,48.25,12.66,73.43,11.62,7.18-1.6,15.25-2.2,22.17-5.03,22.35,16.36,45.55,31.57,69.54,45.57,83.91,37.44,178.97,48.34,270.03,38.15-15.17,30.55,14.79,56.84,37.84,72.5,15.94,10.07,33.68,18.07,52.24,21.71,21.14,2.19,42.5,4.71,63.78,4.44,45.11-2.87,90.41-7.14,134.22-18.8,46.89-12.93,93.05-32.46,132.57-61.02,37.78-30.46,74.07-64.12,96.9-107.61,11.75,27.84,44.29,37.48,72.01,30.87,31.21-4.75,72.45-43.66,100.77-74.45,22.14,27.29,73.15-50.99,85.41-69.68,16.52-28.25,36.67-54.5,50.46-84.23,25.92-55.19,45.48-113.63,49.89-174.72ZM1460.9,276.15c8.88-1.45,16.57-7.19,25.88-5.6,38.05.11,69.23,26.2,95.06,51.54,22.91,26.92,38.54,59.74,47.92,93.66,2.39,12.99,4.44,26,8.06,38.73,5.51,21.17,4.93,43.11,2.43,64.68-.22,16.89-3.18,33.46-8.58,49.26-2.69-15.06-7.16-30.8-13.57-47.56-18.23-48.57-51.88-88.69-77.18-133.44-13.07-22.62-30.2-42.54-45-64.02-15.04-17.3-32.62-32.22-51.4-45.35,4.85.02,10.27-.58,16.36-1.91ZM794.3,100.59c7.64-9.84,14.83-21.6,26.28-27.31,17.39-5.79,34.62-11.96,51.74-18.52,97.43-13.85,208.66-16.11,294.28,39.46,34.23,21.67,53.11,63.65,92.83,77.16,16.61,5.49,34.67,5.99,52.03,5.23,21.49-2.47,43.3-4.12,64.79-.68,15.89,1.73,23.66,12.75,24.22,27.96,2.01,18.94,3.67,38.68,10.47,53.03-39.82-19.54-81.21-14.12-118.72,16.16-26.28,21.78-52.37,43.89-75.28,69.27-26.39,25.53-52.02,51.64-75.32,80.03-23.57,30.02-46.59,61.01-74.53,87.2-26.54,27.89-59.1,48.33-89.41,71.53-17.91-25.07-48.34-25.96-77.12-19.25.58-3.03.88-6.3.85-9.78,2.27-22.58-5-44.45-8.94-66.45-3.19-18.91-7.79-37.5-13.77-55.73-4.32-10.56-4.98-21.17-3.54-32.39.32-23.57,2.43-47.29-.8-70.75-3.2-19.68-4.45-39.95-11.75-58.7-5.45-13.08-13.18-25.05-19.89-37.51-6.18-11.07-14.93-20.28-22.35-30.46-6.96-11.13-12.44-23.08-18.79-34.54-12.95-18.54-20.28-44.56-7.27-64.97ZM976.33,625.44c10.76,17.07,28.85,26.47,45.26,37.11-20.03,6.69-40.98,19.19-62.62,13.63-.43-.16-.86-.34-1.25-.57-5.97-7.79-.57-15.63,3.67-22.74,5.38-8.92,10.95-17.77,14.94-27.43ZM869.12,501.61c3.31,17.37,7.21,35.47,6.43,53.1-7.77-11.15-4.84-38.49-6.43-53.1ZM848,333.84c-5.35-15.16-10.23-30.46-15.18-45.75-6.65-17.66-13.04-35.39-19.71-53.04,8.41,12.5,16.73,25.1,21.3,39.6,7.94,18.88,14.81,38.4,13.58,59.18ZM88.3,601.18c-28.81-34.4-49.07-66-58.54-110.42-9.6-82.28,10.7-159.42,58.88-226.72,24.07-32.65,54.03-62.77,90.98-80.41,19.48-10.85,42.08-15.61,64.29-14.33,10.47,2.03,20.17,6.87,30.57,9.31,37.38,15.8,33.97,66.88,33.95,101.01-1.88,33.09-4.35,66.13-6.87,99.18-3.62,24.48-.95,48.94-.18,73.5,1.66,32.76,4.12,65.55,11.27,97.64-26.21-22.48-58.46-37.58-92.41-43.82-40.78-11.29-81.13-1.9-98.56,39.96-18.74,12.39-30.71,32.91-33.38,55.1ZM334.87,610.31c23.05,26.91,41.89,57.88,54.95,90.79-36.04-38.71-63.88-83.9-98.64-123.66-8.51-10.24-20.26-16.73-31.31-23.82-13.1-9.08-27.21-16.58-42.48-21.31,46.73,11.66,88.67,39.39,117.47,78ZM332.1,503.73c1.95,7.45,2.99,15.05,2.79,22.76-1.06-7.57-1.98-15.16-2.79-22.76ZM114.44,615.85c.74-5.82,1.52-11.63,2.45-17.42,2.77,14.91,7,29.54,13.47,43.28-5.93-4.84-11.73-8.96-17.29-14.7.43-3.72.9-7.44,1.38-11.16ZM111.3,661.38c19.46,16.13,39.09,31.95,55.06,51.72,16.66,16.85,27.56,38.57,43.05,56.42,21.41,20.13,42.37,40.75,63.56,61.14,11.57,9.66,21.98,22.93,36.89,27.21,22.22.92,17.47-26.05,2.2-33.47-14.76-14.88-30.85-28.45-46.46-42.48-24.9-25.97-49.23-52.56-72.26-80.21-9.81-10.94-15.8-24.3-22.42-37.24-15.16-26.47-26.4-56.31-29.53-86.36.57-5.28,1.67-10.5,2.86-15.68,31.15-19.97,68.4-6.45,97.75,10.47,16.32,11.99,31.14,26.3,45.44,40.62,24.27,28.88,43.09,61.94,68.43,89.89,10.43,10.79,19.72,24.83,34.28,30.13,12.67,4.33,27.75-3.32,28.77-17.34,2.63-37.24-44.91-97.48-66.53-128.42,8.67-10.03,13.28-23.77,10.48-36.93.33-12.55-1.32-24.83-3.8-37.11-.62-23.3-4.68-46.26-6.93-69.43-4.54-36.88-9.37-73.89-8.4-111.11-2.57-24.7-.77-48.96,4.12-73.39,6.75-30.4,13.32-63.87,36.28-86.56,57.97-32.22,74.96,66.65,85.91,104.54,12.17,28.63,23.88,117.38,64.24,112.62,9.97-1.04,18.73-7.05,22.74-16.3,11.58-25.22-6.04-50.26-14.89-73.35-10.28-21.68-18.74-44.83-20.05-68.97-2.58-55.92,14.63-112.1,42.91-160.03,22.76-31.39,66.7-50.84,105.14-43.38,40.58,10.02,68.66,46.59,81.02,84.92,8.35,28.47,16.77,56.84,21.36,86.18,7.76,24.01,16.84,47.67,25.73,71.31,15.69,49.6,33.95,98.35,49.09,148.13.34,10.29-.95,20.47-2.61,30.6-2.23,17.46-1.06,35.15-2.09,52.7-2.07,22.02.43,43.7,8.76,64.27,1.98,4,4.18,7.35,6.53,10.09-18.84,7.48-38.8,4.69-58.34,1.31-32.1.35-85.85-28.12-110.1-.58-20.21,23.8-.45,57.49,23.61,69.73,29.65,18.66,79.95,12.81,95.05,49.68,3.48,8.33-.05,16.2-.3,24.66.64,16.51,3.06,34.82,14.9,47.29,34.84,34.8,75.25,42.75,117.08,14.53,33.39,20.64,15.06,64.05,66.8,83.16,27.3,8.23,49.21-15.85,74.01-21.83,23.39-3.97,32.88,12.87,12.12,27.95-20.67,15.81-51.96,31.06-46.48,62.25,3.57,26.4,61.82,71.49,11.47,84.41-15.38,3.61-38.86,3.06-38.55-17.85,1.06-8.45,3.48-16.98,1.76-25.61-2.7-14.51-11.62-30.87-27.55-33.2-14.16-.6-29.2,5.7-42.72-1.42-43.25-22.24-17.84-27.6-35.17-61.5-14.95-35.59-63.38-30.7-91.49-14.67-12.83,5.54-23.46,14.47-34.1,23.31-8.03,5.06-13.63,17.73-24.32,15.81-6-15.53,20.34-37.57,22.97-58.44,2.92-11.99-4.24-24.97-15.79-29.16-9.23-3.06-19.67-2.12-27.99-7.88-12.86-16.46-24.25-34.06-33.58-52.76-8.92-20.17-12.26-45.08-30.8-59.31-12.79-9.14-30.2-12.68-44.87-6.1-8.82,3.95-15.59,11.31-22,18.36-7.33,9.47-11.79,21.28-23.7,26.19-12.31,5.44-26.04,7.16-39.28,9.02-10.99.57-22.5-.91-32.96,3.28-16.75,6.65-29.32,26.66-21.28,44.2,10.53,28.33,41.16,39.15,68.7,43.31,15.69,1.54,31.74.01,47.18,3.74,25.94,7.59,58.99,25.78,17.03,43.03-10.84,3.4-22.51,3.51-33.79,4.33-8.53-.17-17.03-4.38-25.5-1.91-48.6,19.81-18.72,78.15,18,93.1,25.89,16.55,46.83,12.4,75.34,8.38,19.34-2.71,21.17-5.6,35.71,10.49,10.41,14.93,8,36.65,24.78,47.28,17.97,9.46,41.78,11.17,60.58,3,16.49-7.83,28.72-17.65,32.6-36.22,14.22-11.78,32.75-6.88,48.67-1.63,17.38,6.74,37.39,11.35,50.3,25.62,28.26,29.07,8.32,28.99-6.95,40.69-44.87-19.19-99.3-.94-125.47,39.81-8.38,11.74-16.92,23.5-23.33,36.46-7.26,15.64-13.14,33.12-24.63,46.14-15.36,11.6-11.72-51.75-17.11-60.92-5.91-19.44-13.17-39.02-26.85-54.42-10.75-11.19-19.73-24.05-31.09-34.61-9.29-7.86-19.61-14.35-29.38-21.6-35.21-23.33-73.94-44.36-115.64-52.62-34.02-1.47-72.59-2.5-99.67,21.57-4.02,4.69-7.47,9.83-10.35,15.27-7.8-7-18.54-11.32-26.42-18.18-60.52-39.09-117.75-82.22-171.52-130.32-35.89-36.56-65.16-79.39-83.83-127.19-6.23-11.64-6.67-24.85-8.74-37.6-2.15-11.6-3.97-23.2-3.66-35.03ZM385.59,1028.94c-2.28-.29-4.55-.69-6.74-1.39-40.37-17.97-74.89-47.36-105.97-78.35,16.13,11.57,30.06,25.98,47.52,35.69,20.99,15.74,44.7,27.8,65.19,44.04ZM520.9,1229.92c-9.86-1.53-20.79-4.42-31.1-4.71-12.99-1.41-25.25-6.52-38.06-8.95-23.99-4.06-47.48-9.48-70.02-18.78-15.03-4.58-29.96-9.44-44.03-16.54-36.74-14.55-66.63-41.01-97.15-65.43-13.76-9.49-25.33-21.6-36.89-33.68-17.66-16.96-36.28-33.17-51.25-52.65-8.88-11.4-19.87-20.89-29.32-31.79-10.3-11.81-18-25.58-26.55-38.64-23.31-30.48-41.14-64.78-53.36-101.17-6.71-16.45-3.69-34.34-5.41-51.62-.18-16.75-1.77-33.77,1.77-50.29,1.54-3.78,5.71-7.97,9.37-10.15-.47.32-.9.61-1.37.94,22.48-7.46,51.81,20.27,62.48,38.51,22.98,25.35,35,57.88,57.34,83.61,37.58,50.02,77.76,98.82,128.54,135.93,24.87,20.02,49.5,45.84,82.83,49.46,5.38.51,10.9.03,15.96-1.35-.19,12.2,1.87,24.43,6.25,35.7,17.1,45.19,54.22,78.97,88.98,111.01,11.65,10.55,23.59,20.73,35.79,30.57-1.6-.02-3.21-.01-4.8.03ZM926.61,1303.58c-45.82,12.53-95.58,10.91-142.64,6.74-22.9-1.06-45.29-5.96-68.05-8.42-13.96-2.89-27.06-8.9-40.85-12.54-20.02-7.77-38.98-18.24-58.58-27.05-36.44-23.15-70.85-49.29-103.16-77.89-16.42-14.82-32.86-29.65-46.82-46.88-16.63-17.08-32.49-35.56-40.57-58.36-8.2-25.11-4.74-55.22,17.2-72.34,9.7-5.07,21.18-5.52,31.77-7.79,18.13-3.74,35.55,3.76,53.29,6.37,37.41,6.83,69.98,29.9,100.42,51.62,11.9,12.38,27.28,21.17,37.81,34.81,22.03,29.65,29.08,54.8,31.35,91.12,1.02,23.18,16.61,49.86,43.16,41.69,14.51-6.21,21.8-21.67,29.68-34.42,13.46-20.81,21.28-45.08,36.75-64.5,41.18-48.81,96.1-38.48,135.1,6.92,56.28,67.28,27.62,109.15-15.89,170.91ZM1356.98,1266.48c-24.13,25.97-55.25,43.16-86.52,59-31.55,18.74-65.82,31.58-99.21,46.46-34.07,11.31-69.21,22.03-105.43,21.89-22.92,1.44-45.79,2.66-68.58-1.13-12.23-.96-24.61-1.27-36.44-4.81-31.08-12.81-33-30.58-18.42-58.44,11.46-8.92,25.56-11.34,39.51-13.63,22.06-3.86,44.25-7.82,66.72-7.68,38.69-2.74,77.27-8.08,115.31-15.85,53-13.52,103.66-35.31,151.44-61.78,12.96-6.95,25.89-14,38.38-21.77,13.57-6.82,26.27-20.48,42.02-20.53,5.44,27.57-21.91,58.41-38.78,78.28ZM1485.27,1251.85c-18.36,5.92-40.47,2.7-48.58-16.83-3.94-17.02-2.26-34.98-.67-52.25,1.04-8.73-2.22-17.25-.28-25.88,1.92-11.2-.72-22.38-1.7-33.54-5.26-64.63,25.85-72.52,45.89-120.67,16.2-43.4-16.24-83.42-17.14-126.48,14.78-25.89,30.23-53.13,30.52-83.78,1.11-18.02,2.21-44.2-16.71-53.35-15.4-15.05-36.41-24.78-57.93-26.54-18.96-2.2-35.23,9.28-53.28,12.29-13.66,3.06-26.04,9.91-39.2,14.48-17.35,5.23-33.45,13.36-49.16,22.27-30.19,16.08-60.98,32.56-88.28,52.92-6.76,8.02-3.27,20.93,6.48,24.61,7.04,2.08,14.86,1.04,22.1.83,9.77-1.45,18.51-6.43,27.18-10.91,13.84-6.84,28.97-11.49,41.11-21.34,28.34-20.15,57.55-39.65,88.02-56.35,20.62-4.64,49.5-9.85,69.18-1.13-20.6,32.99-55.54,101.96-47.8,140.64-16.65,17.3-37.63,29.42-56.98,43.35-16.9,7.01-34.2,13.25-51.21,19.69-8.82,5.05-9.24,19.19.01,24.07,16.48,6.09,35.83-4.02,52.77-5.88,18.2-3.15,35.85-8.25,53.42-13.84,8.78-1.65,17.85-1.31,26.62-2.97,10.04-2.15,20.19-5.62,30.35-2.17,5.23,28.45-7.19,55.96-20.01,80.69-13.51,12.47-27.52,24.52-42.35,35.38-12.24,9.15-27.57,11.45-41.74,16.21-27.06,10.47-54.95,17.51-83.37,23.17-33.03,7.99-67.65,8.69-99.99,19.19-9.2,1.97-22.03,2.86-24.57,13.91-2.67,9.51,6.56,18.36,15.89,17.32,7.98-2.26,16.12-2.36,24.46-3.56,25.86-4.07,51.3-10.3,77.33-13.39,35.89-5.73,147.2-30.71,152.85,23.02-22.81-15.12-80.81,28.91-103.67,40.53-29.59,16.38-60.59,29.89-91.58,43.35-31.87,10.4-64.31,20.71-97.6,25.18-19.87,4.21-40.04,3.1-60.15,4.46-29.64,4.99-59.95,5.93-89.21,13.05,57.44-76.41,39.21-148.41-34.4-204.64,10.4-7.64,19.72-19.15,22.94-31.9.81-12.81-5.4-26.2-14.04-35.48-26.34-21.25-57.1-39.89-91.82-41.07-18.34-1.88-39.59-2.77-52.39,13.03-6.04,8.01-6.55,19.81-13.82,26.73-15.35,5.72-40.8,12.31-49.94-5.87-3.71-10.32-2.82-22.32-11.06-30.92-14.52-20.29-42.39-31.96-66.22-22.04-32.44,9.22-81.61-6.54-85.12-44.82,11.75-6.85,27.33-1.64,40.41-3.13,9.76-.64,19.42-2.54,28.55-6.11,48.23-15.79,47.9-61.86,5-84.59-19.44-8.52-41.22-9.23-61.89-12.92-22.77-2.68-55.99-2.03-62.25-30.06.46-.36.93-.71,1.41-1.04,49.06-22.1,61.53,16.68,115.67-54.54,34.01-30.38,41.58,30.65,51.81,51.81,10,18.48,22.11,36.57,36.7,51.75,10.11,8.36,23.08,12.28,35.64,15.28.15,2.46-.46,4.89-1.54,7.09-7.68,14.49-17.23,28.34-21.46,44.37-9.01,23.18,18.04,48.47,39.92,35.23,36.75-16.55,43.49-43.44,90.42-47.9,18.89-2.81,29.36,13.05,25.72,30.43,1.36,14.48,8.7,29.15,19.67,38.74,13.36,10.4,28.94,20.84,46.37,21.47,32.23-.52,38.48-6.15,32.53,31.24-2.1,14.36,6.17,29.35,18.14,37.05,20.51,14.75,56.22,10.84,76.06-3.43,31.34-22.47,14.55-61.63-5.02-86.1-4.65-8.21-13.35-20.77-6.86-29.71,22.69-19.57,61.62-35.38,55.02-71.68-8.69-30.28-46.31-37.54-71.97-24.7-27.44,16.56-51.29,30.05-67.99-7.85-4.99-17.6-15.54-33.17-26.58-47.56-10.12-11.53-28.91-14.12-42.41-7.45-6.05,2.82-9.8,8.63-15.53,11.85-16.61,3.53-34.6.19-48.68-9.43-29.23-22.23-21.02-34.26-21.7-65.53-5.39-32.49-36.53-51.7-66.29-59.2-15.41-6.34-32.33-8.94-47.23-16.33-7.71-5.81-14.04-14.89-13.74-24.87,4.41-10.47,26.2-1.23,35.68-1.2,18.37,2.46,36.13,7.31,54.19,11.14,32.96,5.06,66.61-2.33,96.92-15.36,17.97-7.67,73.04-14.45,58.13,19.6-11.12,23.14-33.59,51.26-20.83,77.54,11.08,15.38,32.97,18.39,50.81,14.41,22.08-3.4,40.8-17.23,63.44-17.95,1.71,1.64,13.81-3.91,15.44-2.18,1.94,2.35-6.58,11.94-4.63,14.27-43.96,68.69,42.61,97.83,95.15,70.78,33.38-16.03,47.18-54.04,77.36-73.6,15.17-8.27,31.06-15.13,46.36-23.16,21.76-10.04,44.26-18.32,62.62-34.27,28.12-18,53.17-41.63,72.66-68.73,16.03-21.5,11.47-59.55-22.42-45.47-23.4,14.02-45.14,31.2-64.2,50.74-11.15,8.9-22.25,17.87-32.55,27.76-15.19,14.19-29.82,28.96-45.84,42.23-14.65,14.1-30.04,27.07-47.86,37.01-30.09,23.32-80.12,48.72-110.63,13.07,8.98-22.1-13.61-39.84-34.34-38.79-11.27-8.01-22.64-15.88-33.58-24.34-9.39-6.71-20.12-12.84-23.81-24.54,0,0-.09.31-.09.31-.19-.77-.36-1.54-.51-2.32,16.63-18.58,39.83-30.08,58.92-45.96,19.26-15.5,36.96-32.57,53.44-50.99,30.64-36.95,58.96-75.75,93.41-109.38,18.38-21.55,39.01-40.97,59.87-60.08,19.79-20.35,41.19-39.04,64.6-55.14,56.77-33,114.64,22.85,151.63,61.96,19,18.02,27.95,43.17,43.96,63.54,29.05,45.32,61.11,89.6,77.96,141.29,10.87,34.96,11.61,74.44-1.56,108.83-21.47,58.49-63.54,106.12-95.98,158.43-8.89,14.45.39,31.84,17.94,29.22,13.99-3.42,24.64-12.79,35.17-22.41,25.76-26.23,36.57-82.91,83.13-76.76,16.2,10.84,26.9,29.12,31.09,48.17,9.5,22.02,12.71,34.68,18.73,68.48,8.7,42.72,1.67,86.8.42,104.78-6.85,20.25-15.5,39.92-21.71,60.47-35.09,71.66-77.19,141.48-138.3,194.14-13.4,11.11-26.47,23.69-43.19,29.52ZM1413.82,910.32c.37-.36.73-.73,1.09-1.09,8.35,2.33,17.23-.16,23.9-5.49,2.37,7.1,5.75,13.85,7.68,21.09-11.8.85-23.83-.49-35.43,2.17-8.65,1.85-16.57,6.16-25.19,8.05,9.55-7.97,19.07-15.99,27.94-24.72ZM1457.36,776.71c1.79,30.73-16.39,57.24-32.95,81.56,7.56-28.42,18.47-55.95,32.95-81.56ZM1408.49,1119.24c-1.89-1.33-3.84-2.57-5.91-3.63-9.28-4.76-19.49-7.22-29.46-10.11,11.35-6.05,22.44-12.54,34.05-18.12-.65,10.65,1.41,21.21,1.31,31.86ZM1073.16,737.59c.12.34.24.68.37,1.01-2.99-6.12-.1-12.56,2.85-18.07,21.48,18.79,53.78,21.24,79.96,11.77-22.32,16.1-39.73,23.92-67.41,16.02-5.99-2.27-12.83-5.51-15.98-11.35.19.56.21.58.22.61ZM1307.12,624.34c14.17-11.75,26.48-25.63,40.94-37,14.47-9.19,25.81-22.18,39.56-32.26-10.14,19.49-26.88,33.41-44.58,45.77-11.32,8.85-22.66,17.74-35.92,23.5ZM1708.32,1014.94c-9.26,21.81-23.1,41.29-33.46,62.59-7.81,13.01-15.95,26-24.46,38.81,14.11-22.98,26.54-46.93,36.06-72.15,11.03-20.71,22.62-41.45,24.85-65.28,2.55-12.85,7.95-25.15,8.62-38.34.64-11.57-.87-23.09-.66-34.66-2.58-22.74-2.47-34.02-5.04-50.9-1.28-16.91-10.25-31.51-15.04-47.4-3.81-25.83-11.73-54.59-31.58-72.71-23.68-19.29-54.09-22.19-77.81-1.4,14.25-23.19,25.48-44.85,33.16-66.27,5.92,7.5,13.11,14.71,20.71,21.8,9.09,13.28,21.54,23.31,33.33,34.01,10.88,12.93,19.23,27.85,29.68,41.17,30.81,49.68,42.79,84.43,34.43,143.36-9.36,36.26-18.84,72.52-32.8,107.36Z"/></g></svg>
\ No newline at end of file
diff --git a/users/aspen/goodcry-band/flower-leaves.svg b/users/aspen/goodcry-band/flower-leaves.svg
new file mode 100644
index 0000000000..5a3b6771a1
--- /dev/null
+++ b/users/aspen/goodcry-band/flower-leaves.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2090.36 1497.03"><defs><style>.cls-1{stroke-width:0px;fill:#b4ada5;}</style></defs><g id="Vector_layer"><path class="cls-1" d="M2086.65,1083.82c6.75-34.75,4.12-70.71-6.56-104.43-7.01-21.07-12.45-43.14-25.14-61.68-8.37-11.01-13.33-23.87-19.38-36.17-6.95-12.59-17.22-22.9-23.7-35.82-22.36-27.25-5.64-36.18,10.27-60.06,12.62-17.89,21.75-38.08,31.35-57.69,7.21-17.5,7.71-36.93,9.93-55.53,2.9-22.14,7.62-44.57,3.14-67.05-4.37-49.67-16.65-100.97-48.47-140.57-22.89-23.98-48.03-50.17-81.75-57.08-9.49-.94-18.99.9-28.51.07-45.26,7.88-17.95-47.07-23.33-71.08-1.65-27.43-20.83-37.4-44.38-45.2-33.74-11.87-72.35-5.44-103.69-24.71-23.83-18.95-33.85-49.82-53.63-72.42-45.01-55.16-114.35-87.21-182.57-103.15-33.33-6.95-67.03-14.33-101.03-16.7-20.78,1.85-41.69-1.38-62.36,1.3-12.32,1.69-23.69,6.97-35.49,10.56-15.42,3.31-29.43,12.01-37.63,25.68-23.24-92.57-93.97-135.83-184.74-98.99-43.02,23.9-70.94,66.66-90.71,110.49-14.77,37.04-28.63,75.44-25.17,116.01,1.48,15.21,5.1,30.14,8.43,45.03,2.9,13.1,14.39,39.74,1.74,49.33-9.04-17.16-8.31-39.9-12.97-58.89-4.11-43.65-1.06-88.69-12.98-131.27-5.74-14.42-12.76-29.1-24.67-39.45-12.74-9.1-28.17-18.33-44.34-17.12-33.62,5.14-53.59,34.38-69.69,61.6-1.26-23.2-8.96-45.9-26.31-61.98-12.63-16.59-32.03-23.12-51.79-26.72-49.48-12.15-102.32,7.01-144.36,32.85-76.23,57.07-123.7,115.51-147.32,209.49-7.36,27.08-8.55,55.91-8.1,83.84,7.78,45.48,26.26,88.34,51.56,126.82-9.17,31.26-12.44,63.94-7.69,96.23-25.76-30.41-75.91-33.91-87.66,10.55-7.74,21.79-14.08,44.46-16.74,67.46,1.02,21.03.03,42.4,4.38,63.1,2.39,10.56,5,21.09,7.88,31.54-16.25,1.4-32.32,4.74-47.47,11.14-26.58,5.04-50.9,16.37-75.64,26.77-46.43,16.02-87.08,44.1-128.39,69.99-45.04,33.44-86.36,72.96-122.33,115.93-2.25,2.43-3.79,5.42-4.64,8.65-5.06,5-5.47,13.88-.14,19.08,1.89,2.01,4.02,3.13,6.26,3.6.17.16.34.32.51.47,0,0-.22-.25-.39-.45,0,0,0,0,.01,0,7.55,8.69,19.12,11.94,29.01,17.21,13.28,5.16,27.84,4.83,41.74,7.19,19.77,4.56,39.7,5.31,59.89,4.32,20.16.8,40.31-.01,60.46-.72,11.86-.36,23.56-2.44,35.38-3.36,21.08-.85,42.24-.99,62.88-5.92,19.77-4.19,39.6-8.13,59.37-12.27,17.73-4.78,35.28-10.31,52.59-16.52-12.32,11.74-24.53,23.57-34.8,37.23-11.17,15.17-25.19,27.98-35.97,43.43-8.63,14.81-20.87,27.83-27.88,43.4-19.79,31.32-35.32,65.95-53.04,98.71-13.39,27.24-33.3,55.57,1.63,75.87,17.05,10.89,38.22,11.96,57.68,15.43,15.61,4.26,31.71,3.54,47.7,2.68,16.49-.74,33,.79,49.48.12,16.08-1.42,31.88-5,48.05-5.87,38.09-4.28,75.86-10.82,113.78-16.02,29.61-9.02,60.24-14.2,89.52-24.29,46.43-13.97,92.09-30.67,135.75-51.83,10.71-5.52,21.5-10.9,32.24-16.37.5-.26.97-.56,1.42-.89,13.5,13.96,27.51,27.46,42.03,40.43,74.73,53.45,165.71,83.12,256.96,91.33-20.97,26.91,3.14,58.65,22.59,78.6,13.61,13.05,29.39,24.44,46.85,31.71,20.27,6.37,40.7,13.11,61.61,17.1,44.78,6.2,90.02,11.06,135.27,8.39,48.53-3.31,97.66-13.22,142.09-33.3,43.1-22.3,85.39-48.03,116.45-86.08,5.95,29.63,35.91,45.57,64.39,44.63,31.53,1.58,79.71-28.3,113.62-52.82,16.24,31.17,81.86-35.35,97.61-51.22,21.83-24.38,46.82-46.08,66.27-72.45,36.43-48.89,67.26-102.25,83.79-161.23ZM1902.44,434.59c8.99.36,17.67-3.74,26.48-.31,37.26,7.71,62.59,39.5,82.85,69.49,17.07,30.96,25.83,66.23,28.24,101.35-.25,13.2-.85,26.37.16,39.56,1.17,21.84-3.79,43.23-10.54,63.86-3.59,16.51-9.8,32.15-18.25,46.55.38-15.29-.86-31.61-3.79-49.31-8.16-51.24-33.12-97.27-48.96-146.17-8.29-24.77-21.09-47.72-31.3-71.72-11.28-19.96-25.53-38.08-41.3-54.71,4.75.99,10.18,1.48,16.41,1.4ZM1284.36,129.39c9.46-8.11,18.85-18.21,31.2-21.51,18.19-2.2,36.31-4.8,54.4-7.81,98.23,5.9,207.68,25.9,280.46,97.46,29.21,28.07,39.32,72.98,75.55,94.15,15.18,8.7,32.78,12.79,49.93,15.52,21.55,1.87,43.25,4.61,63.62,12.28,15.23,4.87,20.64,17.22,18.15,32.24-1.82,18.95-4.13,38.63-.33,54.05-35.11-27.1-76.76-30.06-119.55-7.89-30.1,16.09-60.08,32.54-87.6,52.83-30.96,19.74-61.29,40.2-89.79,63.37-29.09,24.7-57.84,50.47-90.45,70.55-31.58,22.02-67.56,35.55-101.9,52.23-12.54-28.14-42.18-35.1-71.72-34.27,1.17-2.85,2.12-5.99,2.79-9.41,6.74-21.67,3.98-44.55,4.51-66.9.65-19.16-.14-38.3-2.36-57.36-2.12-11.21-.65-21.74,3-32.44,5.02-23.03,11.83-45.85,13.35-69.48.8-19.93,3.62-40.03.22-59.87-2.72-13.91-7.91-27.18-11.99-40.73-3.84-12.08-10.57-22.85-15.82-34.31-4.59-12.29-7.57-25.1-11.51-37.6-8.98-20.75-10.96-47.72,5.86-65.11ZM1357.85,680.02c7.13,18.87,22.98,31.7,36.93,45.4-20.97,2.55-43.99,10.61-64.08.84-.39-.24-.77-.5-1.12-.81-4.29-8.82,2.56-15.43,8.14-21.55,7.05-7.66,14.28-15.23,20.12-23.89ZM1277.54,537.27c-.22,17.68-.02,36.2-4.31,53.31-5.39-12.48,2.95-38.68,4.31-53.31ZM1290.37,368.66c-2.21-15.92-3.94-31.89-5.73-47.86-2.99-18.63-5.71-37.28-8.71-55.9,5.74,13.93,11.37,27.94,12.96,43.06,4,20.09,6.84,40.58,1.48,60.7ZM492.57,478.83c-21.35-39.46-34.89-74.48-35.29-119.89,7.03-82.54,42.34-154.06,102.99-210.38,30.11-27.18,65.48-50.71,105.21-60.61,21.25-6.74,44.35-6.89,65.85-1.19,9.85,4.08,18.39,10.77,28.09,15.23,33.47,22.95,19.92,72.32,13.09,105.76-8.45,32.05-17.47,63.93-26.55,95.81-8.43,23.26-10.71,47.77-14.86,71.98-4.92,32.43-9.06,65.06-8.47,97.92-21.19-27.27-49.78-48.5-81.79-61.4-37.7-19.21-79.12-18.07-104.55,19.46-20.83,8.4-36.67,26.11-43.71,47.32ZM732.35,537.03c17.21,30.97,29.48,65.08,35.71,99.94-27.58-45.13-45.82-94.97-71.94-140.88-6.29-11.73-16.51-20.44-25.92-29.6-11.02-11.51-23.35-21.68-37.37-29.36,43.46,20.77,79.01,56.31,99.52,99.9ZM750.93,432.05c.42,7.69-.08,15.34-1.81,22.85.47-7.63,1.09-15.25,1.81-22.85ZM515.26,498.42c1.89-5.56,3.82-11.09,5.88-16.58-.26,15.16.96,30.34,4.55,45.1-4.85-5.92-9.7-11.12-14-17.86,1.17-3.56,2.37-7.11,3.58-10.66ZM503.08,542.41c15.84,19.69,31.91,39.12,43.62,61.67,12.96,19.84,19.3,43.29,30.91,63.88,16.96,24.01,33.38,48.4,50.06,72.6,9.41,11.78,16.96,26.86,30.71,34.04,21.59,5.34,22.32-22.03,8.84-32.36-11.49-17.53-24.55-34.04-37.04-50.91-19.2-30.42-37.73-61.34-54.78-93.03-7.43-12.68-10.62-26.96-14.53-40.97-9.56-28.96-14.61-60.45-11.68-90.51,1.62-5.06,3.74-9.96,5.93-14.79,34.51-13.34,68.31,7.34,93.69,29.79,13.59,15.01,25.26,31.99,36.4,48.88,18.01,33.14,29.84,69.3,49.1,101.75,8.06,12.65,14.36,28.27,27.57,36.37,11.55,6.78,27.86,2.29,31.65-11.24,10.02-35.96-24.53-104.49-39.53-139.12,10.5-8.1,17.77-20.64,17.65-34.09,2.83-12.24,3.67-24.59,3.69-37.12,4.05-22.95,4.66-46.26,7.09-69.42,2.92-37.05,5.58-74.27,13.97-110.55,2.42-24.71,9.03-48.12,18.7-71.09,12.69-28.44,25.82-59.92,52.84-77.56,63.24-19.99,60.13,80.28,63.29,119.6,6.2,30.48-.05,119.78,40.44,123.18,9.97.98,19.76-3.17,25.54-11.42,16.39-22.4,4.12-50.45.07-74.85-5.74-23.29-9.41-47.67-5.87-71.58,8.65-55.31,36.73-106.92,74.02-148.23,28.57-26.21,75.51-36.49,111.68-21.5,37.76,17.92,57.97,59.37,62.42,99.4,2.5,29.56,5.08,59.05,3.71,88.71,2.81,25.08,6.98,50.08,10.97,75.01,5.47,51.74,13.61,103.15,18.5,154.95-1.73,10.15-5.02,19.87-8.67,29.46-5.67,16.67-8.06,34.23-12.58,51.22-6.43,21.16-8.31,42.9-4.26,64.72,1.14,4.32,2.63,8.04,4.38,11.2-19.95,3.56-38.95-3.16-57.43-10.37-31.52-6.07-78.51-44.71-107.77-22.57-24.56,19.28-11.93,56.24,9.2,73.04,25.32,24.2,75.77,28.52,83.21,67.67,1.74,8.85-3.29,15.86-5.22,24.1-2.67,16.31-3.96,34.73,5.15,49.32,27.19,41.06,65.19,56.92,111.82,37.63,28.59,26.89,1.95,65.77,48.84,94.83,25.1,13.52,51.38-5.7,76.87-6.61,23.71.78,29.65,19.18,6.29,29.81-23.41,11.36-57.12,20.05-57.98,51.71-1.78,26.58,46.29,82.4-5.63,85-15.79.46-38.69-4.77-34.21-25.19,2.73-8.07,6.81-15.94,6.84-24.74.25-14.75-5.22-32.57-20.36-38.04-13.76-3.41-29.75-.25-41.58-9.92-37.94-30.44-11.96-30.61-22.18-67.29-7.53-37.86-55.96-42.75-86.71-32.65-13.68,2.86-25.87,9.49-38.07,16.03-8.88,3.35-16.9,14.65-26.98,10.63-2.77-16.41,27.44-32.75,34.18-52.68,5.26-11.16.83-25.31-9.64-31.72-8.44-4.84-18.85-6.01-25.86-13.32-9.32-18.69-16.95-38.22-22.36-58.41-4.71-21.55-3-46.62-18.33-64.26-10.7-11.52-27.05-18.46-42.75-14.94-9.43,2.11-17.54,7.96-25.23,13.59-9.08,7.82-15.81,18.5-28.45,20.93-13.15,2.87-26.95,1.81-40.29.99-10.88-1.63-21.86-5.38-32.95-3.37-17.74,3.17-34.06,20.26-29.68,39.05,4.66,29.86,32.51,46.58,58.66,56.16,15.07,4.64,31.1,6.35,45.49,13.09,23.9,12.62,52.65,37.05,8.09,45.57-11.3,1.16-22.76-1.06-33.97-2.51-8.32-1.87-15.81-7.7-24.6-6.97-51.58,9.7-33.96,72.83-.97,94.82,22.06,21.39,43.41,21.51,72.15,23.26,19.49,1.21,21.86-1.26,32.89,17.41,7.22,16.71.52,37.51,14.84,51.28,15.72,12.86,38.7,19.29,58.76,15.05,17.72-4.38,31.66-11.56,39.18-28.98,16.29-8.7,33.46-.2,48.01,8.13,15.68,10.07,34.37,18.59,44.17,35.16,21.88,34.13-8.5,23.11-14.94,38.48-40.13-27.77-97.11-20.76-130.89,13.94-10.55,9.83-21.27,19.65-30.15,31.06-10.24,13.87-19.49,29.83-33.35,40.29-17.36,8.3-1.14-53.05-4.59-63.11-1.91-20.22-5.1-40.87-15.44-58.69-8.3-13.11-14.52-27.51-23.55-40.12-7.54-9.55-16.35-17.98-24.47-27.04-29.84-29.9-63.58-58.23-102.8-74.66-33.04-8.23-70.63-16.96-101.97,1.22-4.87,3.8-9.28,8.14-13.19,12.89-6.25-8.42-15.9-14.8-22.25-23.1-51.49-50.39-98.95-104.09-142.02-161.96-27.86-42.99-47.99-90.81-56.73-141.38-3.78-12.65-1.57-25.68-1.05-38.59.21-11.8.74-23.52,3.42-35.06ZM698.4,957.36c-2.18-.74-4.32-1.58-6.33-2.71-35.96-25.68-63.92-61.37-88.18-97.94,13.49,14.56,24.27,31.46,39.43,44.47,17.42,19.62,38.24,36.17,55.08,56.18ZM402.14,669.37c3.17-16.45,5.01-33.44,11.78-48.92,2.27-3.4,7.19-6.66,11.21-8.08-.52.22-1,.42-1.53.65,23.52-2.82,46.72,30.21,53.52,50.21,17.45,29.43,22.73,63.71,39.48,93.38,26.83,56.52,56.45,112.37,98.79,158.88,20.37,24.59,39.34,54.8,71.28,65.01,5.17,1.57,10.67,2.21,15.91,1.87-2.63,11.92-3.05,24.31-1.01,36.23,7.72,47.7,37.35,88.21,65,126.55,9.3,12.66,18.97,25.02,28.96,37.1-1.57-.34-3.14-.65-4.71-.93-9.36-3.47-19.49-8.48-29.54-10.83-12.45-3.98-23.43-11.43-35.5-16.38-22.7-8.77-44.63-18.78-64.86-32.39-13.81-7.49-27.47-15.23-39.83-25.01-33.09-21.6-57.09-53.49-82.11-83.52-11.59-12.04-20.5-26.23-29.42-40.38-13.91-20.15-28.92-39.75-39.69-61.83-6.42-12.94-15.3-24.44-22.38-37-7.74-13.63-12.53-28.66-18.3-43.16-4.89-10.08-9.24-20.38-13.09-30.87,2.65-6.29-.31-13.03-5.6-16.5-6.31-20.32-10.77-41.23-13.38-62.42-3.29-17.46,3.25-34.38,5.02-51.66ZM485.49,1170.78c-10.62,6.91-22.35,11.62-33.91,16.68-2.81-4.53-8.35-7.47-16.36-5.57-9.55,1.34-18.96-1.56-28.4-2.95-16.68-2.15-33.52-2.48-50.19-4.72-7.97-.67-15.91-1.77-23.86-2.76,1.11-2.17,2.21-4.34,3.29-6.52,12.24-17.32,23.9-35.21,36.88-52.11.75-1.23,1.52-2.45,2.31-3.65,7.83,5.39,21.74,6.1,29.88,9.3,20.03,4.07,39.24,10.85,58.6,17.3,17.93,6.21,36.45,11.87,55.07,16.39-11.31,5.81-22.57,11.74-33.32,18.6ZM354.03,1236.28c-24.32,10.51-48.77,20.73-73.14,31.13,11.41-23.7,24.88-46.54,37.71-69.56,12.62,3.62,32.18,2.41,42.37,4.29,16.75,1.24,33.43,3.67,50.16,5.13-19.14,9.45-38.27,18.92-57.09,29.01ZM553.55,1132.27c-3.43-1.19-7.23-1.18-10.85-1.65-6.23-2.02-12.19-4.95-18.7-6.11-27.04-7.53-53.72-16.44-81.21-22.48-17.01-4.25-33.63-9.9-50.56-14.5,6.83-7.59,14.09-14.84,21.25-22.18,8.81-9.25,16.65-19.37,25.85-28.24,15.87-12.32,30.75-25.63,46.8-37.67,1.96-1.46,4.37-2.12,6.66-2.97,3.58,4.66,7.28,9.23,11.07,13.74,13.41,20.67,29.08,39.51,45.29,58.02,8.71,10.71,19.76,19,29.95,28.18,4.1,4.35,8.4,8.46,12.88,12.36-12.82,7.78-25.89,15.23-38.43,23.51ZM342.19,818.11c14.19-1.66,27.63-6.7,41.8-8.04,2.21-.16,4.39-.69,6.56-1.25,7.03,21.13,15.52,41.72,26.07,61.38-7.29,1.15-14.57,2.41-21.83,3.74-.25-.62-.54-1.24-.9-1.84-2.86-5.12-8.81-7.28-13.84-9.71-18.69-13.39-38.81-24.55-58.32-36.69,6.8-2.59,13.58-5.22,20.46-7.59ZM289.22,837.21c5.83,5.81,16,7.92,22.23,13.4,16.33,9.83,31.17,22.14,48.5,30.24-9.4,1.97-18.79,4-28.18,6.05-18.83,5.06-37.65,10.21-56.38,15.64-7.33-3.72-16.42-4.69-23.3-9.57-14.69-6.52-29.95-11.77-45.31-16.59,26.78-14.55,54.09-28.7,82.43-39.17ZM174.88,893.79c1.77-.96,3.54-1.92,5.31-2.89.23.53.49,1.06.8,1.57,4.67,7.61,14.88,7.86,22.27,11.69,10.04,4.03,20.29,7.5,30.49,11.06-17.23,5.54-34.34,11.45-51.26,17.89-27.41,13.65-57.34,21.66-84.3,36.18-10.38,6.06-21.57,10.24-32.66,14.7,34.25-32.72,69.61-64.34,109.35-90.21ZM51.37,1027.44c-2.46-.64-5.17-1.08-7.86-1.65,17.63-8.33,35.09-16.86,52.2-26.29,23.52-8.95,46.38-19.38,69.05-30.24,2.74-.85,5.46-1.73,8.18-2.63-.96,1.17-1.93,2.34-2.89,3.5-11.71,11.84-22.59,24.43-32.94,37.48-7.9,9.75-16.13,19.22-24.19,28.83-.07,0-.13-.01-.2-.02-21.02.87-41.43-2.03-61.34-8.98ZM145.92,1038.74c.09-.08.17-.17.26-.25,11.03-12.59,22.97-24.35,33.68-37.22,13.18-16.29,27.52-31.54,41.37-47.21,2.2-1.84,3.56-4.09,4.23-6.47,8-2.94,16.04-5.79,24.15-8.43,13.11-5.37,26.56-9.65,40.17-13.41-10.85,12.52-19.26,27.04-30.31,39.46-11.22,15.96-22.77,32.17-34.48,48.58-4.76,6.71-12.94,15.51-10.04,23.37-2.58.05-5.17.05-7.76-.02-20.39.92-40.85,2.04-61.26,1.62ZM240.53,1035.08c.52-.76,1.02-1.53,1.49-2.29,6.71-8.33,13.28-16.75,19.88-25.17,15.91-17.43,27.84-37.79,43.09-55.71,5.36-6.27,9.09-13.66,14.15-20.12,4.28-4.51,9.39-9.04,10.53-15.44.03-.15.04-.3.06-.45,13.6-3.13,27.21-6.2,40.68-9.72,8.08-1.47,16.16-2.97,24.24-4.44-2.63,2.8-5.18,5.68-7.42,8.81-16.79,25.69-34.98,50.4-50.85,76.74-4.15,6.02-8.41,12.02-12.91,17.78-5.99,5.44-11.72,13.32-9.11,20.48-12.79,2.01-25.67,3.42-38.63,4.14-11.75,1.78-23.44,3.87-35.2,5.4ZM344.1,1019.77c3.9-5.99,7.43-12.44,11.2-16.21,24.25-29.73,44.11-62.48,66.79-93.31,3.2-3.27,7-6.15,9.16-10.24,12.64,24.47,28.59,47.03,43.58,70.22-8.04,3.71-16.66,9.24-23.71,11.59-34.21,16.18-70.1,29.43-107.02,37.95ZM290.88,1308.2c-4.92-2.62-14.6-3.11-19.2-6.92,4.32-1.86,8.51-3.98,12.09-5.31,16.44-7.16,32.99-14.02,49.33-21.41,11.19-5.6,22.49-10.92,33.89-16.04-10.94,16.54-19.59,34.53-27.35,52.75-.49,1.24-.79,2.43-.96,3.59-16.19-.25-32.15-2.09-47.79-6.67ZM408.16,1314.82c-13.87,1.97-27.78,2.88-41.67,2.4,4.92-11.49,11.56-22.12,17.6-33.06,6.64-10.28,10.62-22.72,19.92-30.97,3.91-3.93,4.67-8.58,3.5-12.67,12.59-6.18,24.97-12.8,37.9-18.3,14.04-7.02,27.74-14.66,41.61-22-2.34,11.28-2.86,22.95-2.92,34.55-1.82,16.59-4.97,32.99-6.99,49.55-.29,8.29-4.59,17-4.41,25.29-21.44,2.67-42.91,4.36-64.53,5.21ZM569.91,1293.19c-22.57,7.18-45.83,9.82-69.16,12.72.52-3.25.95-6.51,1.82-9.4,4.72-19.63,7.7-39.62,8.47-59.8,1.33-12.55,2.49-25.12,3.23-37.71,2.04-4.81,5.54-9.6,5.18-14.84,17.87-9.78,34.03-22.24,52.97-30.08,9.37-5.26,17.14-11.82,26.95-16.92,5.55-3.23,10.05-7.79,15.38-11.22,2.95,1.94,5.95,3.83,8.98,5.65,3.91,2.83,7.92,5.5,12.01,8.02.56,9.76-1.7,19.21-2.61,28.91-.3,20.14-3.57,39.98-5.24,60.03.03,10.89-5.93,42.95-2.29,53.61-18.35,4.72-36.91,8.53-55.71,11.02ZM782.2,1226.39c-31.37,13.54-63.05,26.5-95.59,36.96-12.3,4.33-24.7,8.43-37.21,12.18,1.93-14.68,5.34-29.16,6.66-43.95,1.64-12.47,2.43-25.02,2.92-37.58,1.29-12.7,3.64-25.41,2.45-38.22-.05-.78-.04-1.54,0-2.31,4.13,1.96,8.29,3.85,12.49,5.67,17.8,8.95,34.88,19.43,53.84,25.91,23.57,7.93,44.74,22.04,69.63,26.06,5.92-.11,12.27.53,18.35.07-.64.35-1.27.71-1.88,1.05-10.46,4.94-21.09,9.48-31.66,14.17ZM1173.64,1334.56c-47.4,3.12-95.83-8.4-141.11-21.9-22.23-5.61-43.19-14.89-65-21.84-13.1-5.62-24.73-14.13-37.52-20.44-18.07-11.61-34.55-25.66-51.99-38.21-31.08-29.96-59.57-62.45-85.52-96.93-13.13-17.8-26.27-35.62-36.51-55.29-12.88-20.06-24.73-41.33-28.09-65.29-3.02-26.24,6.39-55.05,31.31-67.44,10.52-3.03,21.86-1.18,32.69-1.29,18.51-.04,34.09,10.79,50.94,16.89,35.29,14.17,62.59,43.28,88.09,70.64,9.19,14.51,22.5,26.19,30.09,41.67,15.67,33.45,17.54,59.51,12.51,95.55-3.63,22.92,6.31,52.17,33.96,49.48,15.46-3.18,25.69-16.88,35.96-27.8,17.35-17.7,29.86-39.92,48.9-55.86,50.11-39.6,101.85-18.5,130.99,33.78,41.7,77.16,5.25,112.47-49.72,164.29ZM1602.74,1384.19c-28.84,20.62-62.76,31.26-96.56,40.52-34.66,12.06-70.81,17.79-106.49,25.7-35.65,4.27-72.21,7.76-107.68.38-22.75-3.17-45.4-6.54-66.97-14.81-11.79-3.38-23.86-6.16-34.74-11.99-27.89-18.76-26.23-36.56-6.37-60.94,13.01-6.45,27.31-6,41.43-5.47,22.39.63,44.92,1.18,66.91,5.8,38.46,5.05,77.33,7.53,116.15,7.51,54.63-2.66,108.62-13.89,160.73-30.28,14.08-4.22,28.17-8.54,41.96-13.66,14.66-3.97,29.83-14.82,45.27-11.72-.18,28.1-33.14,52.85-53.64,68.95ZM1731.37,1395.49c-19.17,2.14-40.19-5.44-44.24-26.19-.46-17.46,4.78-34.72,9.78-51.33,2.76-8.35,1.27-17.35,4.89-25.41,4.12-10.59,3.77-22.07,5.03-33.2,7.76-64.38,39.81-65.9,69.08-109.07,24.54-39.29.76-84.98,8.47-127.36,19.65-22.42,40.23-46.02,46.64-75.99,4.68-17.43,11-42.87-5.71-55.61-12.08-17.82-30.73-31.56-51.46-37.58-18.14-5.95-36.38,2.05-54.66,1.4-13.99.27-27.49,4.51-41.3,6.36-18.05,1.66-35.44,6.41-52.61,12-32.79,9.72-66.25,19.72-97.07,34.22-8.22,6.51-7.39,19.85,1.44,25.41,6.48,3.44,14.35,3.98,21.49,5.23,9.86.53,19.43-2.6,28.81-5.26,14.92-3.94,30.69-5.47,44.54-12.7,31.8-14.08,64.31-27.35,97.5-37.63,21.14-.43,50.47.23,68.01,12.72-26.78,28.21-74.79,88.8-74.94,128.25-19.77,13.62-42.75,21.31-64.49,31.09-17.96,3.49-36.16,6.15-54.11,9.06-9.65,3.19-12.88,16.96-4.8,23.59,14.93,9.26,35.92,3.22,52.88,4.78,18.46.55,36.78-.92,55.11-2.89,8.94.13,17.75,2.28,26.68,2.41,10.26-.1,20.9-1.47,30.18,3.94-.56,28.92-18.22,53.4-35.73,75.06-15.73,9.51-31.86,18.53-48.57,26.21-13.82,6.52-29.3,5.71-44.14,7.54-28.61,4.85-57.34,6.17-86.32,6.05-33.96,1.23-68.02-5.01-101.81-1.17-9.4.09-22.16-1.6-26.86,8.72-4.52,8.78,2.76,19.3,12.11,20.15,8.28-.62,16.27.91,24.68,1.39,26.15,1.18,52.32.16,78.44,2.33,36.31,1.56,150.37-.68,145.16,53.09-19.32-19.38-84.96,12.18-109.68,19-32.26,10.14-65.34,17.18-98.39,24.17-33.31,3.82-67.15,7.45-100.66,5.17-20.31.15-39.85-4.96-59.83-7.65-30.04-1.04-59.92-6.17-90.02-5.04,71.55-63.39,68.07-137.59,7.18-207.38,11.72-5.41,23.15-14.82,28.85-26.67,3.36-12.39-.06-26.75-6.67-37.57-21.56-26.09-47.97-50.5-81.77-58.59-17.59-5.5-38.24-10.63-53.94,2.3-7.52,6.64-10.37,18.11-18.89,23.43-16.19,2.54-42.44,3.91-47.77-15.73-1.57-10.85,1.7-22.43-4.66-32.5-10.17-22.78-35.15-39.78-60.48-34.83-33.63,2.55-78.66-22.71-74.45-60.92,12.88-4.37,27.1,3.85,40.22,5.01,9.69,1.32,19.53,1.39,29.19-.29,50.41-5.84,59.29-51.04,21.8-81.89-17.34-12.24-38.54-17.28-58.06-25.03-21.77-7.17-54.46-13.18-54.99-41.89.52-.26,1.05-.51,1.59-.74,52.49-11.85,56.96,28.63,124.23-30.33,39.39-22.97,34.62,38.34,40.41,61.11,6.11,20.11,14.36,40.25,25.62,58.04,8.23,10.21,20.16,16.64,31.87,22.09-.34,2.44-1.43,4.7-2.92,6.64-10.42,12.67-22.54,24.33-29.89,39.19-13.46,20.91,7.99,51.1,32.08,42.5,39.32-8.87,51.29-33.87,98.17-28.87,19.07,1.02,26.17,18.65,19.12,34.96-1.56,14.46,2.7,30.3,11.53,41.89,11.02,12.86,24.19,26.2,41.15,30.3,31.68,5.93,38.94,1.66,25.63,37.11-4.92,13.66.18,29.99,10.37,39.93,17.15,18.55,52.92,21.85,75.21,11.83,35.2-15.76,26.57-57.48,12.28-85.37-2.92-8.98-8.93-23.01-.79-30.48,26.14-14.64,67.45-22.36,68.23-59.24-2.47-31.4-37.88-46.04-65.58-38.58-30.2,10.74-56.26,19.2-65.05-21.28-1.37-18.25-8.6-35.61-16.54-51.91-7.61-13.32-25.5-19.62-40.07-15.78-6.49,1.55-11.33,6.5-17.59,8.51-16.98.14-33.94-6.72-45.82-18.97-24.2-27.63-13.75-37.77-8.17-68.54,1.21-32.91-25.46-57.95-53.12-71.25-13.83-9.29-29.89-15.22-43.01-25.44-6.4-7.23-10.79-17.39-8.49-27.11,6.42-9.38,25.91,4.03,35.2,5.95,17.5,6.08,33.94,14.38,50.87,21.74,31.28,11.54,65.73,11.02,98.04,4.31,19.14-3.92,74.45.43,53.04,30.82-15.52,20.45-43.15,43.52-35.9,71.81,7.78,17.29,28.63,24.61,46.9,24.27,22.31,1.08,43.42-8.73,65.75-4.91,1.35,1.95,2.66,3.92,3.91,5.95,1.43,2.69,2.82,5.39,4.27,8.06-56.79,58.52,22.21,104.37,79.09,88.36,35.91-9.04,57.02-43.53,90.5-56.66,16.52-5.07,33.46-8.61,50.05-13.43,23.33-5.49,47.03-9.1,68.21-21.07,31.15-12.02,60.42-30.17,84.92-52.83,20-17.86,23.14-56.06-12.88-49.04-25.73,9.06-50.46,21.55-73.04,36.89-12.7,6.49-25.37,13.06-37.44,20.7-17.72,10.87-35,22.42-53.36,32.22-17.17,10.89-34.84,20.52-54.29,26.7-34.14,16.84-88.24,31.73-111.01-9.3,13.21-19.86-5.38-41.76-25.9-44.87-9.44-10.1-19.01-20.08-28.04-30.56-7.86-8.46-17.15-16.6-18.43-28.8,0,0-.15.29-.15.29-.04-.79-.05-1.58-.03-2.37,20-14.89,45.04-21.52,66.91-33.27,21.97-11.34,42.72-24.53,62.55-39.28,37.4-30.08,72.9-62.45,113.38-88.51,22.32-17.44,46.41-32.35,70.67-46.9,23.46-15.98,48.16-30.02,74.32-41.12,62.22-20.99,107.76,45.29,136.2,91.01,15.02,21.45,18.76,47.89,30.38,71.04,19.41,50.21,41.98,100,48.15,154.02,3.66,36.42-3.5,75.26-23.27,106.32-32.73,53.02-83.46,91.28-125.69,136.06-11.6,12.38-5.98,31.28,11.74,32.22,14.39-.56,26.7-7.61,38.94-14.93,30.48-20.56,52.4-73.93,96.79-58.6,13.7,13.86,20.54,33.9,20.84,53.41,4.91,23.47.39,47.35,4.67,70.84.95,17.1-1.47,34.56-3.92,51.46-6.38,16.86-11.79,33.92-16.6,51.29-10.75,18.47-23.17,36.02-33.35,54.91-48.7,63.2-103.9,123.2-174.3,162.6-15.35,8.21-30.67,17.92-48.21,20.3ZM1729.6,1046.57c.43-.28.86-.56,1.29-.85,7.71,3.95,16.92,3.29,24.52-.61.9,7.43,2.87,14.72,3.32,22.2-11.73-1.53-23.25-5.24-35.15-4.95-8.84.09-17.47,2.72-26.29,2.85,10.95-5.9,21.88-11.86,32.32-18.64ZM1798.95,924.36c-4.38,30.47-27.49,52.81-48.58,73.33,13.09-26.33,29.28-51.13,48.58-73.33ZM1682.63,1250.21c-1.58-1.68-3.25-3.28-5.06-4.74-8.14-6.52-17.66-10.97-26.84-15.79,12.33-3.66,24.49-7.81,36.98-10.96-2.77,10.3-2.86,21.07-5.08,31.48ZM1430.32,809.26c.05.36.1.71.16,1.07-1.71-6.59,2.41-12.32,6.4-17.13,17.29,22.7,48.45,31.56,76,27.51-25.09,11.31-43.71,15.5-69.25,2.23-5.41-3.42-11.47-7.97-13.4-14.31.08.58.09.61.09.64ZM1682.19,745.04c16.23-8.69,31.07-19.83,47.51-28.08,16.02-6.11,29.72-16.57,45.21-23.71-13.83,17.08-33.01,27.37-52.83,35.94-12.86,6.41-25.74,12.85-39.89,15.84ZM1997.26,1207.92c-13.43,19.52-30.88,35.85-45.29,54.65-10.26,11.19-20.82,22.28-31.72,33.14,18.42-19.7,35.38-40.68,49.74-63.49,14.94-18.09,30.45-36.1,37.39-59,5.06-12.08,12.81-23.05,16.11-35.84,2.94-11.21,3.76-22.8,6.28-34.09,5.28-16.48,4.38-33.83,5.23-50.88,2.12-16.82-3.75-32.92-5.26-49.45,1.43-26.07-.58-55.84-16.41-77.55-19.35-23.63-48.57-32.55-75.96-16.92,18.6-19.88,33.93-38.86,45.73-58.31,4.31,8.53,9.9,17.03,15.93,25.5,6.25,14.83,16.44,27.14,25.87,39.98,8.07,14.85,13.28,31.13,20.85,46.27,20.26,54.83,25.06,91.27,5.09,147.35-16.41,33.66-32.94,67.29-53.59,98.64Z"/></g></svg>
diff --git a/users/aspen/goodcry-band/flower.png b/users/aspen/goodcry-band/flower.png
new file mode 100644
index 0000000000..b5ad24bac6
--- /dev/null
+++ b/users/aspen/goodcry-band/flower.png
Binary files differdiff --git a/users/aspen/goodcry-band/index.css b/users/aspen/goodcry-band/index.css
new file mode 100644
index 0000000000..ef2aaee882
--- /dev/null
+++ b/users/aspen/goodcry-band/index.css
@@ -0,0 +1,170 @@
+@import "./reset.css";
+
+:root {
+    --gray: #b4ada5;
+    --black: #2f2715;
+    --background: #fff9f4;
+    --blue: #195f9a;
+    --lavender: #5f6eb2;
+}
+
+html {
+    width: 100%;
+}
+
+body {
+    background-color: var(--background);
+    color: var(--black);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    width: 100%;
+    overflow-x: hidden;
+    font-family: "Helvetica", sans-serif;
+}
+
+h1,
+h2,
+subtitle {
+    font-family: "Playfair Display", serif;
+}
+
+header {
+    display: flex !important;
+    flex-direction: column;
+    align-items: center;
+    font-size: 30px;
+    display: inline-block;
+    text-align: center;
+    max-width: 100%;
+    overflow-x: hidden;
+}
+
+@media (min-width: 800px) {
+    header {
+        overflow-x: initial;
+    }
+}
+
+subtitle {
+    display: block;
+    padding-top: 0.5rem;
+    padding-bottom: 2.7rem;
+}
+
+hr {
+    border-top-style: none;
+    border-left-style: none;
+    border-right-style: none;
+    border-bottom: 1px solid var(--gray);
+    width: 35%;
+    margin: 1.4rem 0;
+}
+
+header hr {
+    margin-top: 0;
+    margin-bottom: 2.7rem;
+}
+
+.header-image {
+    width: 75%;
+    max-width: 75%;
+    transform: translateX(4%);
+    margin-top: 1rem;
+}
+
+@media (min-width: 600px) {
+    .header-image {
+        max-width: 600px;
+        margin-bottom: 1.2rem;
+    }
+}
+
+h2 {
+    margin-bottom: 1.4rem;
+    border-bottom: 1px solid var(--gray);
+    padding-bottom: 0.5rem;
+}
+
+.content {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+
+a {
+    color: var(--blue);
+    text-decoration: none;
+}
+
+p {
+    text-align: center;
+    max-width: 400px;
+    padding: 0 1rem;
+}
+
+a:active {
+    text-decoration: underline;
+    color: var(--lavender);
+}
+
+.link-list {
+    padding: 0 1rem;
+    list-style: none;
+    font-size: 1.2em;
+    text-align: center;
+}
+
+@media (min-width: 800px) {
+    .link-list {
+        padding: 0;
+    }
+}
+
+.link-list li > * {
+    display: block;
+    padding: 0.4rem 0;
+}
+
+.social-links {
+    margin-top: 3rem;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+}
+
+.social-links > * + * {
+    margin-left: 0.5rem;
+}
+
+.social-links svg {
+    fill: var(--gray);
+}
+
+.social-links a {
+    color: var(--gray);
+    font-size: 24px;
+    vertical-align: middle;
+}
+
+.social-links a:hover {
+    color: var(--blue);
+}
+
+.social-links a:active {
+    color: var(--lavender);
+    text-decoration: none;
+}
+
+.social-links a:hover svg {
+    fill: var(--blue);
+}
+
+.social-links a:active svg {
+    fill: var(--lavender);
+}
+
+footer img {
+    width: 80px;
+    margin: 3rem 0;
+}
diff --git a/users/aspen/goodcry-band/index.html b/users/aspen/goodcry-band/index.html
new file mode 100644
index 0000000000..ea9ec2f654
--- /dev/null
+++ b/users/aspen/goodcry-band/index.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta http-equiv="x-ua-compatible" content="ie=edge" />
+    <title>Good Cry</title>
+    <meta name="description" content="" />
+    <meta name="viewport" content="width=device-width, initial-scale=1" />
+    <link rel="stylesheet" href="index.css" type="text/css" media="screen" />
+    <link rel="preconnect" href="https://fonts.googleapis.com" />
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
+    <link
+      href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400..900;1,400..900&display=swap"
+      rel="stylesheet"
+    />
+  </head>
+  <body>
+    <header class="header">
+      <img class="header-image" src="./flower.png" alt="Flower" />
+      <h1 class="title">Good Cry</h1>
+      <subtitle> just let it all out, baby </subtitle>
+      <hr />
+    </header>
+    <h2>Shows</h2>
+    <ul class="link-list">
+      <li>
+        <a
+          href="https://www.eventbrite.com/e/skylar-pocket-w-leche-malo-good-cry-tickets-836634866407"
+        >
+          February 24th, 2024 <br />
+          @ The Broadway in Brooklyn NY
+        </a>
+      </li>
+    </ul>
+    <div class="content">
+      <hr />
+      <p>
+        Good Cry is rock n roll by way of grief for an unreachable future. Good
+        Cry is songs about the trials and tribulations of NYC trans love and
+        community. Good Cry is the joy in the heartache
+      </p>
+      <hr />
+      <p>
+        DM us
+        <a href="https://www.instagram.com/goodcryband_bk/">on Instagram</a> for
+        booking inquiries
+      </p>
+    </div>
+    <nav class="social-links">
+      <a href="https://www.instagram.com/goodcryband_bk/" title="Instagram">
+        <svg
+          xmlns="http://www.w3.org/2000/svg"
+          width="24"
+          height="24"
+          viewBox="0 0 24 24"
+        >
+          <path
+            d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"
+          />
+        </svg>
+      </a>
+      <a href="mailto:hello@goodcry.band" title="Email"> @ </a>
+    </nav>
+    <footer>
+      <img src="./flower-leaves.svg" alt="Flower" />
+    </footer>
+  </body>
+</html>
diff --git a/users/aspen/goodcry-band/reset.css b/users/aspen/goodcry-band/reset.css
new file mode 100644
index 0000000000..23dcf53d29
--- /dev/null
+++ b/users/aspen/goodcry-band/reset.css
@@ -0,0 +1,45 @@
+*,
+*::before,
+*::after {
+    box-sizing: border-box;
+}
+
+* {
+    margin: 0;
+}
+
+body {
+    line-height: 1.5;
+    -webkit-font-smoothing: antialiased;
+}
+
+img,
+picture,
+video,
+canvas,
+svg {
+    display: block;
+    max-width: 100%;
+}
+
+input,
+button,
+textarea,
+select {
+    font: inherit;
+}
+
+p,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+    overflow-wrap: break-word;
+}
+
+#root,
+#__next {
+    isolation: isolate;
+}
diff --git a/users/grfn/keyboard/.gitignore b/users/aspen/keyboard/.gitignore
index b2be92b7db..b2be92b7db 100644
--- a/users/grfn/keyboard/.gitignore
+++ b/users/aspen/keyboard/.gitignore
diff --git a/users/grfn/keyboard/README.org b/users/aspen/keyboard/README.org
index b085883a10..b085883a10 100644
--- a/users/grfn/keyboard/README.org
+++ b/users/aspen/keyboard/README.org
diff --git a/users/grfn/keyboard/default.nix b/users/aspen/keyboard/default.nix
index dda1d9682c..929ec7d628 100644
--- a/users/grfn/keyboard/default.nix
+++ b/users/aspen/keyboard/default.nix
@@ -32,6 +32,16 @@ rec {
     AVR_CFLAGS = [
       "-isystem ${avrlibc}/avr/include"
       "-L${avrlibc}/avr/lib/avr5"
+      # GCC 12 has improved array-bounds warnings, failing the build of QMK.
+      # Newer versions of the firmware would work probably, but they heavily
+      # altered the build system, so it is non-trivial. Backporting the patch
+      # that fixes it seems difficult โ€“ the next change to the offending matrix.c
+      # after the pinned qmkSource commit is
+      # https://github.com/qmk/qmk_firmware/commit/11c308d436180974b7719ce78cdffdd83a1302c0
+      # which heavily changes the way the code works.
+      #
+      # TODO(grfn): address this properly
+      "-Wno-error=array-bounds"
     ];
 
     AVR_ASFLAGS = AVR_CFLAGS;
@@ -59,5 +69,5 @@ rec {
       -w ${layout}
   '';
 
-  meta.targets = [ "layout" ];
+  meta.ci.targets = [ "layout" ];
 }
diff --git a/users/aspen/keyboard/flash b/users/aspen/keyboard/flash
new file mode 100755
index 0000000000..ab6557a44c
--- /dev/null
+++ b/users/aspen/keyboard/flash
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+exec "$(nix-build --no-out-link ../../.. -A users.aspen.keyboard.flash)"
diff --git a/users/grfn/keyboard/increase-tapping-delay.patch b/users/aspen/keyboard/increase-tapping-delay.patch
index 316c435fed..316c435fed 100644
--- a/users/grfn/keyboard/increase-tapping-delay.patch
+++ b/users/aspen/keyboard/increase-tapping-delay.patch
diff --git a/users/grfn/keyboard/keymap.c b/users/aspen/keyboard/keymap.c
index fbb28c9aac..741b7b2cfd 100644
--- a/users/grfn/keyboard/keymap.c
+++ b/users/aspen/keyboard/keymap.c
@@ -31,11 +31,11 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
       KC_EQUAL,       KC_1,           KC_2,   KC_3,   KC_4,   KC_5,   KC_LEFT,
       KC_TAB,         KC_Q,           KC_W,   KC_E,   KC_R,   KC_T,   KC_LALT,
       KC_ESCAPE,      KC_A,           KC_S,   KC_D,   KC_F,   KC_G,
-      KC_RSFT,        CTL_T(KC_Z),    KC_X,   KC_C,   KC_V,   KC_B,   KC_TAB,
+      KC_LSFT,        CTL_T(KC_Z),    KC_X,   KC_C,   KC_V,   KC_B,   KC_TAB,
       LT(1,KC_GRAVE), KC_QUOTE,       LALT(KC_LSHIFT),KC_LEFT,KC_RIGHT,
-                                        ALT_T(KC_APPLICATION),      LAMBDA,
+                                        ALT_T(KC_APPLICATION),      KC_SPACE,
                                                                     KC_LBRACKET,
-                                        GUI_T(KC_NO), LSFT_T(KC_BSPACE),    KC_COLN,
+                                        KC_LGUI, LSFT_T(KC_BSPACE),    KC_COLN,
 
       KC_MY_COMPUTER, KC_6,   KC_7,   KC_8,       KC_9,       KC_0,               KC_MINUS,
       KC_RALT,      KC_Y,   KC_U,   KC_I,       KC_O,       KC_P,               KC_BSLASH,
@@ -69,7 +69,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
   ),
 
   [2] = LAYOUT_ergodox(
-      KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
+      KC_SCROLLLOCK,  KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
       KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_UP,       KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
       KC_TRANSPARENT, KC_TRANSPARENT, KC_MS_LEFT,     KC_MS_DOWN,     KC_MS_RIGHT,    KC_TRANSPARENT,
       KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT, KC_TRANSPARENT,
diff --git a/users/grfn/keys.nix b/users/aspen/keys.nix
index c52229b3a8..29d5a3fa63 100644
--- a/users/grfn/keys.nix
+++ b/users/aspen/keys.nix
@@ -1,5 +1,6 @@
 { ... }:
 {
   whitby = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDIwl+xQYRCk6Ijz/Ll8eXKZrcTH9/7xwlvIowiuqDSFtGkf+73QJkwVJ0YiKHWAPwIUWMzCEO/Ab2g6j4PcR+XYu8kXbrwT5aW65L/AK1oaav2RfV1bnQEVUP9FRPL52BN42J0ibI2QJZKJVws9JF7vxTWPPG0V0eoxcaRMk1ZEqq+/k3GuN8D69VSV8xo9lB8yZEvTxs0YQRiiF7Q6t/3jhYtz6lCdazQviRcSEOj5AVsDjcf1XIAPOcLK4Q4OEXL49T3UaitSYMyKIO8hzNLiyGAUlSbshAnutPXdyNBypkCs6FrSPSRdBfFjzUVE/a+JWCPmx0q0xAVd497Efxby+Vsa2/TPMp7tSisPaqk3MpPmjBS7eI/y4Pl2GpAB4OVANEBNd1Q6K2/37Pk+PrZtIUBiRG8sM0Od36BjwLCxvG0G5P/UYZ93aC8GzqkRf4evOBMiJCvR2o9CDEDycNyTm1y5dyJzQewOTWX9nsiF1rllc92W0ZALvpO03+W2+k= grfn@chupacabra";
-  main = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHPiNpPB6Uqs/VSW/C8tR/Z5wCQxKppNL2iETb1ucsYsFf1B2apG5txj06NMT6IWXwWpZXq7ld+/sA+a2I03lO2INP7S1Dto5nAwpNhhKN/UBXk76qYTdY5tEvb9J89S2ZzfQWR30aZ0CEDDrcbc+YktU1eSLdluu6QH+M/uPBweSiVn5wNHkc5sRdbyiVsZSQJ41MO7PQrzGpe7Pxola/ghOHdEFlESJMKA5uoRpCGboxtDE9tMJwG5MxNwHERpfI9FjvvLsJRrp9dRf6A/RQjlV/nb1GmpX0I8pvrXEPxm/l0rOAgE81VSsM+BxJ7ZvCe8u/YqMYJ8xVfskzlVsf griffin@MacBook-Pro";
+  main = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMcBGBoWd5pPIIQQP52rcFOQN3wAY0J/+K2fuU6SffjA";
+  old = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHPiNpPB6Uqs/VSW/C8tR/Z5wCQxKppNL2iETb1ucsYsFf1B2apG5txj06NMT6IWXwWpZXq7ld+/sA+a2I03lO2INP7S1Dto5nAwpNhhKN/UBXk76qYTdY5tEvb9J89S2ZzfQWR30aZ0CEDDrcbc+YktU1eSLdluu6QH+M/uPBweSiVn5wNHkc5sRdbyiVsZSQJ41MO7PQrzGpe7Pxola/ghOHdEFlESJMKA5uoRpCGboxtDE9tMJwG5MxNwHERpfI9FjvvLsJRrp9dRf6A/RQjlV/nb1GmpX0I8pvrXEPxm/l0rOAgE81VSsM+BxJ7ZvCe8u/YqMYJ8xVfskzlVsf griffin@MacBook-Pro";
 }
diff --git a/users/grfn/org-clubhouse/.gitignore b/users/aspen/org-clubhouse/.gitignore
index 2a7dd97deb..2a7dd97deb 100644
--- a/users/grfn/org-clubhouse/.gitignore
+++ b/users/aspen/org-clubhouse/.gitignore
diff --git a/users/grfn/org-clubhouse/CODE_OF_CONDUCT.org b/users/aspen/org-clubhouse/CODE_OF_CONDUCT.org
index f15e387d54..f15e387d54 100644
--- a/users/grfn/org-clubhouse/CODE_OF_CONDUCT.org
+++ b/users/aspen/org-clubhouse/CODE_OF_CONDUCT.org
diff --git a/users/grfn/org-clubhouse/LICENSE b/users/aspen/org-clubhouse/LICENSE
index 1777f0fac3..1777f0fac3 100644
--- a/users/grfn/org-clubhouse/LICENSE
+++ b/users/aspen/org-clubhouse/LICENSE
diff --git a/users/grfn/org-clubhouse/README.org b/users/aspen/org-clubhouse/README.org
index 9cd8fbe892..9cd8fbe892 100644
--- a/users/grfn/org-clubhouse/README.org
+++ b/users/aspen/org-clubhouse/README.org
diff --git a/users/grfn/org-clubhouse/org-clubhouse.el b/users/aspen/org-clubhouse/org-clubhouse.el
index e6e29b5751..e6e29b5751 100644
--- a/users/grfn/org-clubhouse/org-clubhouse.el
+++ b/users/aspen/org-clubhouse/org-clubhouse.el
diff --git a/users/aspen/pkgs/cargo-hakari.nix b/users/aspen/pkgs/cargo-hakari.nix
new file mode 100644
index 0000000000..b6f4e7e400
--- /dev/null
+++ b/users/aspen/pkgs/cargo-hakari.nix
@@ -0,0 +1,27 @@
+{ pkgs, ... }:
+
+with pkgs;
+
+rustPlatform.buildRustPackage rec {
+  pname = "cargo-hakari";
+  version = "0.9.13";
+
+  src = fetchFromGitHub {
+    owner = "facebookincubator";
+    repo = "cargo-guppy";
+    rev = "cargo-hakari-${version}";
+    sha256 = "11ds2zryxdd6rvszkpphb0xnfg7rqisg6kixrwyiydjrm5rdjg9d";
+  };
+
+  cargoSha256 = "0b2hjyak5v4m3g5zjk2q8bdb4iv3015qw1rmhpclv4cv48lcmdbb";
+
+  buildAndTestSubdir = "tools/cargo-hakari";
+
+  nativeBuildInputs = [
+    pkg-config
+  ];
+
+  buildInputs = [
+    openssl
+  ];
+}
diff --git a/users/aspen/pkgs/cargo-nextest.nix b/users/aspen/pkgs/cargo-nextest.nix
new file mode 100644
index 0000000000..dbf3bd7eef
--- /dev/null
+++ b/users/aspen/pkgs/cargo-nextest.nix
@@ -0,0 +1,27 @@
+{ pkgs, ... }:
+
+with pkgs;
+
+rustPlatform.buildRustPackage rec {
+  pname = "cargo-nextest";
+  version = "0.9.36";
+
+  src = fetchFromGitHub {
+    owner = "nextest-rs";
+    repo = "nextest";
+    rev = "cargo-nextest-${version}";
+    sha256 = "1g40r38bqmdhc0dy07pj27vkc64d3fw6v5z2vwn82xld2h9dg7w2";
+  };
+
+  cargoSha256 = "1g862azgkn3xk3v3chs8hv1b1prj1pq2vfzbhcx6ir9l00kv6gcv";
+
+  cargoTestFlags = [
+    "--"
+    "--skip"
+    "tests_integration::test_relocated_run"
+    "--skip"
+    "tests_integration::test_run"
+    "--skip"
+    "tests_integration::test_run_after_build"
+  ];
+}
diff --git a/users/aspen/pkgs/notmuch-extract-patch.nix b/users/aspen/pkgs/notmuch-extract-patch.nix
new file mode 100644
index 0000000000..7f00f925ec
--- /dev/null
+++ b/users/aspen/pkgs/notmuch-extract-patch.nix
@@ -0,0 +1,18 @@
+{ pkgs, ... }:
+
+let
+  inherit (pkgs) lib;
+  src = pkgs.fetchurl {
+    url = "https://raw.githubusercontent.com/aaptel/notmuch-extract-patch/f732a53e12a7c91a06755ebfab2007adc9b3063b/notmuch-extract-patch";
+    sha256 = "0nawkl04sj7psw6ikzay7kydj3dhd0fkwghcsf5rzaw4bmp4kbax";
+  };
+
+in
+pkgs.runCommand "notmuch-extract-patch"
+{
+  buildInputs = [ pkgs.python3 ];
+} ''
+  mkdir -p $out/bin
+  install -m 755 ${src} $out/bin/notmuch-extract-patch
+  patchShebangs $out/bin
+''
diff --git a/users/aspen/pkgs/py3status.nix b/users/aspen/pkgs/py3status.nix
new file mode 100644
index 0000000000..89f52d9674
--- /dev/null
+++ b/users/aspen/pkgs/py3status.nix
@@ -0,0 +1,12 @@
+{ pkgs, ... }:
+
+pkgs.python3Packages.py3status.overridePythonAttrs (old: rec {
+  name = "${pname}-${old.version}";
+  pname = "py3status-glittershark";
+  src = pkgs.fetchFromGitHub {
+    owner = "glittershark";
+    repo = "py3status";
+    rev = "f243be1458cdabd5a7524adb76b5db99006c810c";
+    sha256 = "0ffmv91562yk0wigriw4d5nfg2b32wqx8x78qvdqkawzvgbwrwvl";
+  };
+})
diff --git a/users/grfn/resume/chimera.png b/users/aspen/resume/chimera.png
index 6dde989c53..6dde989c53 100644
--- a/users/grfn/resume/chimera.png
+++ b/users/aspen/resume/chimera.png
Binary files differdiff --git a/users/grfn/resume/collection.sty b/users/aspen/resume/collection.sty
index 4f1540a9d2..4f1540a9d2 100644
--- a/users/grfn/resume/collection.sty
+++ b/users/aspen/resume/collection.sty
diff --git a/users/aspen/resume/default.nix b/users/aspen/resume/default.nix
new file mode 100644
index 0000000000..4454e74c82
--- /dev/null
+++ b/users/aspen/resume/default.nix
@@ -0,0 +1,40 @@
+{ pkgs, ... }:
+
+with pkgs.lib;
+
+pkgs.runCommand "resume.pdf"
+{
+  buildInputs = [
+    (pkgs.texlive.combine {
+      inherit (pkgs.texlive)
+        capt-of
+        collection-fontsrecommended
+        enumitem
+        etoolbox
+        fancyvrb
+        float
+        fncychap
+        framed
+        l3packages
+        microtype
+        needspace
+        parskip
+        scheme-basic
+        tabulary
+        titlesec
+        ulem
+        upquote
+        varwidth
+        wrapfig
+        xcolor
+        ;
+    })
+  ];
+} ''
+  cp ${builtins.filterSource (path: type:
+    type == "regular" &&
+    any (ext: hasSuffix ext path) [".sty" ".cls" ".tex" ".png"]
+  ) ./.}/* .
+  pdflatex ./resume.tex
+  cp resume.pdf $out
+''
diff --git a/users/grfn/resume/helvetica.sty b/users/aspen/resume/helvetica.sty
index dacc129a10..dacc129a10 100644
--- a/users/grfn/resume/helvetica.sty
+++ b/users/aspen/resume/helvetica.sty
diff --git a/users/grfn/resume/moderncv.cls b/users/aspen/resume/moderncv.cls
index a40f807337..3248907133 100644
--- a/users/grfn/resume/moderncv.cls
+++ b/users/aspen/resume/moderncv.cls
@@ -219,6 +219,7 @@
 % defines one's name

 % usage: \name{<firstname>}{<lastname>}

 \newcommand*{\name}[2]{\def\@firstname{#1}\def\@lastname{#2}}

+\newcommand*{\pronouns}[1]{\def\@pronouns{#1}}

 % defines one's title (optional)

 % usage: \title{<title>}

 \renewcommand*{\title}[1]{\def\@title{#1}}

@@ -284,7 +285,7 @@
 % usage: \moderncvstyle{<style variant name>}

 \newcommand*{\moderncvstyle}[1]{

   \RequirePackage{moderncvstyle#1}}

-  

+

 % loads a color scheme

 % usage: \moderncvcolor{<color scheme name>}

 \newcommand*{\moderncvcolor}[1]{

diff --git a/users/grfn/resume/moderncvcolorblack.sty b/users/aspen/resume/moderncvcolorblack.sty
index 3a6e1477f3..3a6e1477f3 100644
--- a/users/grfn/resume/moderncvcolorblack.sty
+++ b/users/aspen/resume/moderncvcolorblack.sty
diff --git a/users/grfn/resume/moderncvcolorblue.sty b/users/aspen/resume/moderncvcolorblue.sty
index 7b949c704a..7b949c704a 100644
--- a/users/grfn/resume/moderncvcolorblue.sty
+++ b/users/aspen/resume/moderncvcolorblue.sty
diff --git a/users/grfn/resume/moderncvcolorgreen.sty b/users/aspen/resume/moderncvcolorgreen.sty
index 4de7f848a0..4de7f848a0 100644
--- a/users/grfn/resume/moderncvcolorgreen.sty
+++ b/users/aspen/resume/moderncvcolorgreen.sty
diff --git a/users/grfn/resume/moderncvcolorgrey.sty b/users/aspen/resume/moderncvcolorgrey.sty
index 9018726a23..9018726a23 100644
--- a/users/grfn/resume/moderncvcolorgrey.sty
+++ b/users/aspen/resume/moderncvcolorgrey.sty
diff --git a/users/grfn/resume/moderncvcolororange.sty b/users/aspen/resume/moderncvcolororange.sty
index 134ae24011..134ae24011 100644
--- a/users/grfn/resume/moderncvcolororange.sty
+++ b/users/aspen/resume/moderncvcolororange.sty
diff --git a/users/grfn/resume/moderncvcolorpurple.sty b/users/aspen/resume/moderncvcolorpurple.sty
index d3dc5345b0..d3dc5345b0 100644
--- a/users/grfn/resume/moderncvcolorpurple.sty
+++ b/users/aspen/resume/moderncvcolorpurple.sty
diff --git a/users/grfn/resume/moderncvcolorred.sty b/users/aspen/resume/moderncvcolorred.sty
index 681181997d..681181997d 100644
--- a/users/grfn/resume/moderncvcolorred.sty
+++ b/users/aspen/resume/moderncvcolorred.sty
diff --git a/users/grfn/resume/moderncvcompatibility.sty b/users/aspen/resume/moderncvcompatibility.sty
index 1fc53f2180..1fc53f2180 100644
--- a/users/grfn/resume/moderncvcompatibility.sty
+++ b/users/aspen/resume/moderncvcompatibility.sty
diff --git a/users/grfn/resume/moderncviconsletters.sty b/users/aspen/resume/moderncviconsletters.sty
index 0a4e2864be..0a4e2864be 100644
--- a/users/grfn/resume/moderncviconsletters.sty
+++ b/users/aspen/resume/moderncviconsletters.sty
diff --git a/users/grfn/resume/moderncviconsmarvosym.sty b/users/aspen/resume/moderncviconsmarvosym.sty
index eb1b1ec727..eb1b1ec727 100644
--- a/users/grfn/resume/moderncviconsmarvosym.sty
+++ b/users/aspen/resume/moderncviconsmarvosym.sty
diff --git a/users/grfn/resume/moderncvstylebanking.sty b/users/aspen/resume/moderncvstylebanking.sty
index fb0b70fdcd..fb0b70fdcd 100644
--- a/users/grfn/resume/moderncvstylebanking.sty
+++ b/users/aspen/resume/moderncvstylebanking.sty
diff --git a/users/grfn/resume/moderncvstylecasual.sty b/users/aspen/resume/moderncvstylecasual.sty
index e375e7612a..f8cf856d1a 100644
--- a/users/grfn/resume/moderncvstylecasual.sty
+++ b/users/aspen/resume/moderncvstylecasual.sty
@@ -64,6 +64,7 @@
 % fonts

 \renewcommand*{\namefont}{\fontsize{38}{40}\mdseries\upshape}

 \renewcommand*{\addressfont}{\normalsize\mdseries\slshape}

+\newcommand*{\pronounsfont}{\fontsize{18}{40}\mdseries\upshape}

 

 % commands

 \renewcommand*{\makecvtitle}{%

@@ -86,7 +87,7 @@
   \@initializelength{\makecvtitlepicturewidth}%

   \settowidth{\makecvtitlepicturewidth}{\usebox{\makecvtitlepicturebox}}%

   \parbox[b]{\textwidth-\makecvtitlepicturewidth}{%

-    \raggedleft\namefont{\color{color2!50}\@firstname} {\color{color2}\@lastname}}\\[-.35em]% alternate design: \MakeLowercase and no space

+    \raggedleft\namefont{\color{color2!50}\@firstname} {\color{color2}\@lastname} {\color{color2!50}\pronounsfont{\@pronouns}}}\\[-.35em]% alternate design: \MakeLowercase and no space

   {\color{color2!50}\rule{\textwidth}{.25ex}}%

   % optional title

   \ifthenelse{\equal{\@title}{}}{}{\\[1.25em]\null\hfill\titlestyle{\@title}}\\[2.5em]% \null is required as there is no box on the line after \\, so glue (and leaders) disappears; this is in contrast to after \par, where the next line starts with an indent box (even after \noindent).

diff --git a/users/grfn/resume/moderncvstyleclassic.sty b/users/aspen/resume/moderncvstyleclassic.sty
index 63cf97aa3b..63cf97aa3b 100644
--- a/users/grfn/resume/moderncvstyleclassic.sty
+++ b/users/aspen/resume/moderncvstyleclassic.sty
diff --git a/users/grfn/resume/moderncvstyleempty.sty b/users/aspen/resume/moderncvstyleempty.sty
index 85932464d1..85932464d1 100644
--- a/users/grfn/resume/moderncvstyleempty.sty
+++ b/users/aspen/resume/moderncvstyleempty.sty
diff --git a/users/grfn/resume/moderncvstyleoldstyle.sty b/users/aspen/resume/moderncvstyleoldstyle.sty
index ff732f4e2a..ff732f4e2a 100644
--- a/users/grfn/resume/moderncvstyleoldstyle.sty
+++ b/users/aspen/resume/moderncvstyleoldstyle.sty
diff --git a/users/grfn/resume/picture.png b/users/aspen/resume/picture.png
index 63b21b5320..63b21b5320 100644
--- a/users/grfn/resume/picture.png
+++ b/users/aspen/resume/picture.png
Binary files differdiff --git a/users/grfn/resume/resume.tex b/users/aspen/resume/resume.tex
index 933558d570..fb226c4ddf 100644
--- a/users/grfn/resume/resume.tex
+++ b/users/aspen/resume/resume.tex
@@ -1,6 +1,6 @@
 %% start of file `template.tex'.
 %% Copyright 2006-2013 Xavier Danaux (xdanaux@gmail.com).
-%% Copyright 2014-2020 Griffin Smith (wildgriffin45@gmail.com).
+%% Copyright 2014-2023 Griffin Smith (root@gws.fyi).
 %
 % This work may be distributed and/or modified under the
 % conditions of the LaTeX Project Public License version 1.3c,
@@ -27,19 +27,25 @@
 \usepackage[scale=0.8, margin=0.65in]{geometry}
 \setlength{\hintscolumnwidth}{2.6cm}
 
-\name{Griffin}{Smith}
+\name{Aspen}{Smith}
+\pronouns{she/her}
 \title{Software Engineer}
 \phone[mobile]{(720) 206-7218}
-\email{grfn@gws.fyi}
-\homepage{https://www.gws.fyi}
-\extrainfo{References available upon request}
+\email{aspen@gws.fyi}
+\homepage{gws.fyi}
+\extrainfo{she/her}
+
 
 \begin{document}
 \makecvtitle{}
 \section{Skills}
+\cvitem{Rust}{Expertise in high-performance, low latency, low-level systems
+development with Rust, including everything from fundamental data structure
+implementation to asynchronous distributed systems development}
 \cvitem{Clojure}{Extensive experience architecting, deploying, and building
 complex web applications in Clojure and Clojurescript, with a focus on
-Re-Frame and Reagent.}
+Re-Frame and Reagent. Experience testing distributed systems in Clojure using
+Jepsen.}
 \cvitem{Haskell}{Passionate love for pure functional programming as a hobbyist
 pursuit, but also practical experience building production systems in Haskell at
 scale, and using Haskell's advanced type system extensions where appropriate to
@@ -48,10 +54,6 @@ deliver increased ergonomics and safety.}
 stack both for local development dependencies and for configuring and building
 production software. Core contributer to a fork of the nix implementation itself
 (tvix) aimed at providing increased safety, performance, and flexibility.}
-\cvitem{Scala}{Understanding of Scala from the perspective of a functional
-programmer rather than a Java programmer. Experience building production
-big-data processing systems using Akka, and deep programming with Scala's type
-system using Shapeless.}
 \cvitem{Unix/Linux}{Experience with administrating highly available distributed
 systems. Passion for the Unix philosophy of discrete, composable units of
 functionality.}
@@ -61,36 +63,35 @@ understanding of the internals of the Ruby interpreter and object system.}
 \cvitem{Javascript}{Experience developing real-time responsive single-page web
 applications using React, in addition to significant contributions to the React
 open-source community.}
-\cvitem{SQL}{Deep understanding of relational databases, including experience
-designing the database schema in Postgres for an application with over a decade
-of usage, hundreds of gigabytes of data, complex, multi-tiered hierarchical data
-structures, as well as experience writing and optimizing large, complex queries
-against that database.}
+\cvitem{SQL}{Deep understanding of relational databases as an
+implementer, in the context of an innovative new database implementing a query
+planner and incremental materialization for the PostgreSQL and MySQL dialects of
+SQL from the ground up -- and of course also a user}
 
 \subsection{Additional Tools}
 \cvitem{}{\footnotesize
     \begin{itemize*}
         \item Vim
+        \item Emacs (yes, also)
         \item Kubernetes
         \item Git
-        \item Puppet
+        \item Terraform
         \item AWS
-        \item Reagent
+        \item GCP
         \item Datomic
         \item Elasticsearch
         \item Redis
-        \item DynamoDB
         \item Docker
-        \item JIRA
         \item Java
+        \item Scala
         \item QuickCheck (and similar tools)
+        \item Jepsen
         \item Python
         \item Elixir
     \end{itemize*}
     \newline
     \textbf{Novice Level:}
     \begin{itemize*}
-        \item Rust
         \item C++
         \item Erlang
         \item Prolog
@@ -100,8 +101,41 @@ against that database.}
     \end{itemize*}}
 
 \section{Experience}
-\subsection{Employment}
-\cventry{2019-present}{Engineering Manager}{Urbint}{New York, NY}{}
+\cventry{2020--2023}{Staff Software Engineer}{ReadySet}{Remote}{}
+{Founding engineer at a startup bringing a high performance
+  partially-stateful, incrementally-maintained SQL database based on the Noria
+  thesis to market
+  \begin{itemize}
+    \item Served as the main technical leadership for the project throughout its
+          maturation from a research codebase to a production-grade system
+    \item Extended the Noria PhD thesis by implementing methods from multiple
+          research papers, masters theses, and other papers from database
+          research, in addition to original database research and development.
+    \item Invented or helped develop multiple novel database techniques in
+          partially materialized dataflow, including index planning and
+          selection, pagination, post-lookup aggregate processing, partial
+          ``straddled'' joins, weak indexes for correct execution of partial
+          joins, and more.
+    \item Invented novel ways to test SQL databases, including a new deterministic
+          generator for SQL queries.
+    \item Developed the clustered high availability distributed runtime mode from
+          a buggy research feature into a production ready distributed system
+          that passed a suite of Jepsen tests.
+    \item Implemented a significant fraction of the SQL query planner, which
+          required both implementing algorithms specified in database research
+          papers and inventing new techniques to work around the limitations of
+          partially materialized dataflow
+    \item Optimized critical components of the code base, including algorithmic
+          optimizations, CPU cache analysis, low-level data structures, and
+          broad system runtime analysis
+    \item Implemented a type inference engine and expression evaluator that
+          supported multiple dialects of SQL configured at compile-time, with
+          maximum code reuse while preserving maintainability
+    \item Mentored multiple junior and senior engineers
+    \item Open-Source contributions visible at
+          \url{https://github.com/readysettech/readyset/commits?author=glittershark}
+  \end{itemize}}
+\cventry{2019--2020}{Engineering Manager}{Urbint}{New York, NY}{}
 {\begin{itemize}
    \item Lead of the platform team with two direct reports - a senior SRE and
      a senior software engineer.
@@ -190,23 +224,29 @@ against that database.}
 
 \section{Project Highlights}
 \newcommand{\project}[3]{\item \textbf{#1} -- \textit{#2}\newline{}#3}
-\cvitem{}{\begin{itemize}
-  \project{Github Bug Bounty}{https://bounty.github.com/researchers/glittershark.html}{
-    Discovered and responsibly disclosed a persistent XSS on Github's main
-    website}
-  \project{Tvix}{https://cs.tvl.fyi/depot/-/blob/third\_party/nix/README.md}{
-    Fork of the Nix build tool delivering increased reliability, code
-    quality, and pluggability}
-  \project{Panettone}{https://cs.tvl.fyi/depot/-/tree/web/panettone}{
-    Aggressively simple bug-tracker developed in Common Lisp for the community
-    involved in the development of Tvix. Hosted at https://b.tvl.fyi}
-  \project{Org-Clubhouse}{https://github.com/glittershark/org-clubhouse}{
-    Emacs library for integration between org-mode and the Clubhouse issue
-    tracker}
-  \project{core-async-storage}{https://github.com/glittershark/core-async-storage}{
-    Simple Clojurescript wrapper around React Native's AsyncStorage using
-    core.async}
-\end{itemize}}
+\cvitem{}{
+  \begin{itemize}
+    \project{How much does Rust's bounds checking actually cost?}
+    {\url{https://blog.readyset.io/bounds-checks/}}{Blog post providing a deep
+        evaluation of the runtime cost of bounds checking in safe languages like Rust.
+        Front page of Hacker News, doubled month-over-month ReadySet waitlist signups}
+    \project{Tvix}{\url{https://cs.tvl.fyi/depot/-/blob/third\_party/nix/README.md}}{
+        Fork of the Nix build tool delivering increased reliability, code
+        quality, and pluggability}
+    \project{Panettone}{\url{https://cs.tvl.fyi/depot/-/tree/web/panettone}}{
+        Aggressively simple bug-tracker developed in Common Lisp for the community
+        involved in the development of Tvix. Hosted at https://b.tvl.fyi}
+    \project{Org-Clubhouse}{\url{https://github.com/glittershark/org-clubhouse}}{
+        Emacs library for integration between org-mode and the Clubhouse issue
+        tracker}
+    \project{Github Bug Bounty}{\url{https://bounty.github.com/researchers/glittershark.html}}{
+        Discovered and responsibly disclosed a persistent XSS on Github's main
+        website}
+    \project{core-async-storage}{\url{https://github.com/glittershark/core-async-storage}}{
+        Simple Clojurescript wrapper around React Native's AsyncStorage using
+        core.async}
+  \end{itemize}
+}
 
 \end{document}
 % vim: set tw=95 colorcolumn=-1:
diff --git a/users/grfn/resume/tweaklist.sty b/users/aspen/resume/tweaklist.sty
index adc9398932..adc9398932 100644
--- a/users/grfn/resume/tweaklist.sty
+++ b/users/aspen/resume/tweaklist.sty
diff --git a/users/aspen/secrets/.envrc b/users/aspen/secrets/.envrc
new file mode 100644
index 0000000000..051d09d292
--- /dev/null
+++ b/users/aspen/secrets/.envrc
@@ -0,0 +1 @@
+eval "$(lorri direnv)"
diff --git a/users/aspen/secrets/bbbg.age b/users/aspen/secrets/bbbg.age
new file mode 100644
index 0000000000..ebc0df2338
--- /dev/null
+++ b/users/aspen/secrets/bbbg.age
Binary files differdiff --git a/users/aspen/secrets/buildkite-ssh-key.age b/users/aspen/secrets/buildkite-ssh-key.age
new file mode 100644
index 0000000000..d9587f11df
--- /dev/null
+++ b/users/aspen/secrets/buildkite-ssh-key.age
Binary files differdiff --git a/users/aspen/secrets/buildkite-token.age b/users/aspen/secrets/buildkite-token.age
new file mode 100644
index 0000000000..320ee06c09
--- /dev/null
+++ b/users/aspen/secrets/buildkite-token.age
Binary files differdiff --git a/users/aspen/secrets/cloudflare.age b/users/aspen/secrets/cloudflare.age
new file mode 100644
index 0000000000..4f42ee7821
--- /dev/null
+++ b/users/aspen/secrets/cloudflare.age
@@ -0,0 +1,9 @@
+age-encryption.org/v1
+-> ssh-ed25519 CpJBgQ AVkUs8tuzVlDq3FH/zRrBr5f4KR05fONM6iCluq6hyM
+feS2cxFowSWfDdUQjtmIiMc5338n805yownSZ/ZWfS8
+-> ssh-ed25519 LfBFbQ F67irB+DYQ8WMhaFcO+3o0O0lJsf+tWFZ9cSGSuHgA8
+EKS4zRGUEgeldjxdx4sIsnorWHoeTlXa9LJtNf9lkAM
+-> QvY:XSvC-grease 04
+pBnXsOF6qugcSBp+pw
+--- +g65NbIxu6bVVerS93kYZpEO5ssUZfCD+sZMzOjDUdU
+R๎TภmaF[BรŠบ๘ี0ƒa_&ห•=3dlzRViด6-9แ:ณฟU.Eศเ…	ช —Jฮ™Œ฿๗ฦAท-€qเพŸ๗ทะ|ก™ะ}}a=žHบ+]m™tภฏR๘–เ%9\˜๕Jt€š|1Bฟ
\ No newline at end of file
diff --git a/users/aspen/secrets/ddclient-password.age b/users/aspen/secrets/ddclient-password.age
new file mode 100644
index 0000000000..8d25e3b539
--- /dev/null
+++ b/users/aspen/secrets/ddclient-password.age
Binary files differdiff --git a/users/aspen/secrets/default.nix b/users/aspen/secrets/default.nix
new file mode 100644
index 0000000000..26b1998f56
--- /dev/null
+++ b/users/aspen/secrets/default.nix
@@ -0,0 +1,2 @@
+{ depot, ... }:
+depot.ops.secrets.mkSecrets ./. (import ./secrets.nix)
diff --git a/users/aspen/secrets/secrets.nix b/users/aspen/secrets/secrets.nix
new file mode 100644
index 0000000000..5bfb1c3eb0
--- /dev/null
+++ b/users/aspen/secrets/secrets.nix
@@ -0,0 +1,15 @@
+let
+  grfn = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMcBGBoWd5pPIIQQP52rcFOQN3wAY0J/+K2fuU6SffjA";
+  mugwump = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFE2fxPgWO+zeQoLBTgsgxP7Vg7QNHlrQ+Rb3fHFTomB";
+  ogopogo = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINoS7PqM8d7xc8nn0yfiPGfRaH8U/nq2Jm27nRO3L5P0";
+  bbbg = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL/VzrNEY47KPTce3dgfORkAbweWkr4BI8j54BAIs7bG";
+in
+
+{
+  "bbbg.age".publicKeys = [ grfn mugwump bbbg ];
+  "cloudflare.age".publicKeys = [ grfn mugwump ];
+  "ddclient-password.age".publicKeys = [ grfn mugwump ];
+  "buildkite-ssh-key.age".publicKeys = [ grfn mugwump ogopogo ];
+  "buildkite-token.age".publicKeys = [ grfn mugwump ogopogo ];
+  "windtunnel-bot-github-token.age".publicKeys = [ grfn mugwump ogopogo ];
+}
diff --git a/users/aspen/secrets/shell.nix b/users/aspen/secrets/shell.nix
new file mode 100644
index 0000000000..6e70458d19
--- /dev/null
+++ b/users/aspen/secrets/shell.nix
@@ -0,0 +1,8 @@
+let
+  depot = import ../../.. { };
+in
+depot.third_party.nixpkgs.mkShell {
+  buildInputs = [
+    depot.third_party.agenix.cli
+  ];
+}
diff --git a/users/aspen/secrets/windtunnel-bot-github-token.age b/users/aspen/secrets/windtunnel-bot-github-token.age
new file mode 100644
index 0000000000..daae999582
--- /dev/null
+++ b/users/aspen/secrets/windtunnel-bot-github-token.age
@@ -0,0 +1,11 @@
+age-encryption.org/v1
+-> ssh-ed25519 CpJBgQ YaZ2VHyXofn2qnxRrOYO4yPPu77BEPFq/cbnfa+5WAA
+VgJQoyJVxirvASD0aDsuzmbNJdIP0kpHa5b72Ri7kr8
+-> ssh-ed25519 LfBFbQ cXXW3kQzZL7sU4heujIJGzvfpbX0toL2AgsJl5AZPEg
+mhkKn69c/QeCJhYAFgx/MsHrIrXim3OcjkZ/rrckVLs
+-> ssh-ed25519 GeE7sQ /XcP3pWg+aKF1F0sPu6RpYv3Rfj2J/QI0yjg3Wgfjm0
+d+rsgbMlDJx0VrjD4/nO4UcM10hcrLxcPA3QlY1t7sQ
+-> "0?-grease k}d?h6 |v
+7mV6AFUdCMCrkmLVQaWJPQ
+--- I9Ls9AWMkSFCKw7y4pLoTkeGw7h5iROwXLuUm0nfuj8
+~‚v‰8‚&‚ฃน3\ฒา.ป%$ผ›ษบฐณt๒๓ˆุQฉˆภจแ”ล้ผอœ}ˆ—๓,BEวh
w96”็๖?ำU
\ No newline at end of file
diff --git a/users/grfn/system/.gitignore b/users/aspen/system/.gitignore
index 41fbeb02c4..41fbeb02c4 100644
--- a/users/grfn/system/.gitignore
+++ b/users/aspen/system/.gitignore
diff --git a/users/grfn/system/home/.skip-subtree b/users/aspen/system/home/.skip-subtree
index e69de29bb2..e69de29bb2 100644
--- a/users/grfn/system/home/.skip-subtree
+++ b/users/aspen/system/home/.skip-subtree
diff --git a/users/aspen/system/home/common/solarized.nix b/users/aspen/system/home/common/solarized.nix
new file mode 100644
index 0000000000..554ee0523e
--- /dev/null
+++ b/users/aspen/system/home/common/solarized.nix
@@ -0,0 +1,18 @@
+rec {
+  base03 = "#002B36";
+  base02 = "#073642";
+  base01 = "#586e75";
+  base00 = "#657b83";
+  base0 = "#839496";
+  base1 = "#93a1a1";
+  base2 = "#eee8d5";
+  base3 = "#fdf6e3";
+  yellow = "#b58900";
+  orange = "#cb4b16";
+  red = "#dc322f";
+  magenta = "#d33682";
+  violet = "#6c71c4";
+  blue = "#268bd2";
+  cyan = "#2aa198";
+  green = "#859900";
+}
diff --git a/users/grfn/system/home/default.nix b/users/aspen/system/home/default.nix
index f62704152b..90df02b378 100644
--- a/users/grfn/system/home/default.nix
+++ b/users/aspen/system/home/default.nix
@@ -3,7 +3,7 @@
 with lib;
 
 rec {
-  home = confPath: (import "${pkgs.home-manager.src}/modules" {
+  home = confPath: (import (pkgs.home-manager.src + "/modules") {
     inherit pkgs;
 
     configuration = { config, lib, ... }: {
@@ -22,11 +22,21 @@ rec {
 
   dobharchuHome = dobharchu.activation-script;
 
+  ogopogo = home ./machines/ogopogo.nix;
+
+  ogopogoHome = ogopogo.activation-script;
+
   yeren = home ./machines/yeren.nix;
 
   yerenHome = yeren.activation-script;
 
-  meta.targets = [
+  lusca = home ./machines/lusca.nix;
+
+  luscaHome = lusca.activation-script;
+
+  meta.ci.targets = [
+    "ogopogoHome"
+    "luscaHome"
     "yerenHome"
   ];
 }
diff --git a/users/grfn/system/home/home.nix b/users/aspen/system/home/home.nix
index 39045c147d..39045c147d 100644
--- a/users/grfn/system/home/home.nix
+++ b/users/aspen/system/home/home.nix
diff --git a/users/grfn/system/home/machines/dobharchu.nix b/users/aspen/system/home/machines/dobharchu.nix
index 0b8503a00e..c26f3baef1 100644
--- a/users/grfn/system/home/machines/dobharchu.nix
+++ b/users/aspen/system/home/machines/dobharchu.nix
@@ -4,7 +4,7 @@
   imports = [
     ../platforms/darwin.nix
     ../modules/common.nix
-    ../modules/games.nix
+    # ../modules/games.nix
   ];
 
   home.packages = with pkgs; [
@@ -14,4 +14,7 @@
     pass
     pinentry_mac
   ];
+
+  programs.home-manager.enable = true;
+  home.stateVersion = "21.11";
 }
diff --git a/users/aspen/system/home/machines/lusca.nix b/users/aspen/system/home/machines/lusca.nix
new file mode 100644
index 0000000000..fc5f606639
--- /dev/null
+++ b/users/aspen/system/home/machines/lusca.nix
@@ -0,0 +1,34 @@
+{ pkgs, lib, config, ... }:
+
+let
+  inherit (builtins) pathExists;
+in
+{
+  imports = [
+    ../platforms/linux.nix
+    ../modules/common.nix
+
+    ../modules/email.nix
+    ../modules/desktop.nix
+  ] ++ (lib.optional (pathExists ../modules/private.nix)
+    ../modules/private.nix);
+
+  home.username = lib.mkForce "aspen";
+  home.homeDirectory = lib.mkForce "/home/aspen";
+
+  # for when hacking
+  programs.home-manager.enable = true;
+  home.stateVersion = "20.03";
+
+  system.machine = {
+    wirelessInterface = "wlp1s0";
+    i3FontSize = 9;
+    battery = 1;
+  };
+
+  programs.alacritty.settings.font.size = lib.mkForce 5.5;
+
+  home.packages = with pkgs; [ discord steam tdesktop slack ];
+
+  xsession.windowManager.i3.config.keybindings.XF86AudioMedia = "exec lock";
+}
diff --git a/users/aspen/system/home/machines/ogopogo.nix b/users/aspen/system/home/machines/ogopogo.nix
new file mode 100644
index 0000000000..37396a5aa1
--- /dev/null
+++ b/users/aspen/system/home/machines/ogopogo.nix
@@ -0,0 +1,78 @@
+{ pkgs, lib, config, ... }:
+
+let
+  inherit (builtins) pathExists;
+  laptopKeyboardId = "5";
+in
+
+{
+  imports = [
+    ../platforms/linux.nix
+    ../modules/common.nix
+    ../modules/desktop.nix
+    ../modules/games.nix
+    ../modules/obs.nix
+    ../modules/development/agda.nix
+    ../modules/development/readyset.nix
+    ../modules/development/ocaml.nix
+  ] ++ (lib.optional (pathExists ../modules/private.nix) ../modules/private.nix);
+
+  programs.home-manager.enable = true;
+  home.stateVersion = "21.11";
+
+  system.machine = {
+    wirelessInterface = "wlp4s0";
+    i3FontSize = 9;
+    battery = null;
+  };
+
+  home.packages = with pkgs; [
+    zoom-us
+    slack
+    mariadb
+    graphviz
+    gnuplot
+    mypaint
+    xdot
+    tdesktop
+    subsurface
+    (discord.override rec {
+      version = "0.0.22";
+      src = fetchurl {
+        url = "https://dl.discordapp.net/apps/linux/${version}/discord-${version}.tar.gz";
+        sha256 = "19xbmrd782m4lp2l0ww5v3ip227g0z8pplxigxga96q43rvp6p0p";
+      };
+    })
+    steam
+  ];
+
+  systemd.user.services.laptop-keyboard = {
+    Unit = {
+      Description = "Swap caps+escape and alt+super, but only on the built-in laptop keyboard";
+      After = [ "graphical-session-pre.target" ];
+      PartOf = [ "graphical-session.target" ];
+    };
+
+    Install = { WantedBy = [ "graphical-session.target" ]; };
+
+    Service = {
+      Type = "oneshot";
+      RemainAfterExit = true;
+      ExecStart = (
+        "${pkgs.xorg.setxkbmap}/bin/setxkbmap "
+        + "-device ${laptopKeyboardId} "
+        + "-option caps:swapescape "
+        + "-option compose:ralt "
+        + "-option altwin:swap_alt_win"
+      );
+    };
+  };
+
+  xsession.windowManager.i3.config.keybindings.F9 = "exec lock";
+
+  # Telegram adds this to ~/.config/mimeapps.list if it isn't already there,
+  # preventing home manager from installing (since it doesn't want to overwrite
+  # the file)
+  xdg.mimeApps.defaultApplications."x-scheme-handler/tg" =
+    "userapp-Telegram Desktop-K290F1.desktop";
+}
diff --git a/users/grfn/system/home/machines/roswell.nix b/users/aspen/system/home/machines/roswell.nix
index f68f3a7be3..135477b12d 100644
--- a/users/grfn/system/home/machines/roswell.nix
+++ b/users/aspen/system/home/machines/roswell.nix
@@ -1,7 +1,7 @@
 { pkgs, lib, config, ... }:
 
 let
-  laptopKeyboardId = "5";
+  inherit (builtins) pathExists;
 in
 
 {
@@ -11,7 +11,9 @@ in
     ../modules/development.nix
     ../modules/emacs.nix
     ../modules/vim.nix
-  ];
+    ../modules/development/readyset.nix
+    ../modules/tmux.nix
+  ] ++ (lib.optional (pathExists ../modules/private.nix) ../modules/private.nix);
 
   home.packages = with pkgs; [
     # System utilities
@@ -19,9 +21,10 @@ in
     htop
     killall
     bind
-    zip unzip
+    zip
+    unzip
     tree
-    ncat
+    nmap
     bc
     pv
 
@@ -33,8 +36,14 @@ in
     # Nix things
     nixfmt
     nix-prefetch-github
-    nix-review
+    nixpkgs-review
     cachix
+
+    # ReadySet stuff
+    nodejs
+    mysql80
+
+    (writeShellScriptBin "xdg-open" "echo xdg-open: \"$@\"")
   ];
 
   programs.password-store.enable = true;
diff --git a/users/grfn/system/home/machines/yeren.nix b/users/aspen/system/home/machines/yeren.nix
index 5431b8ebc5..9a7a561b5e 100644
--- a/users/grfn/system/home/machines/yeren.nix
+++ b/users/aspen/system/home/machines/yeren.nix
@@ -9,7 +9,10 @@ in
   imports = [
     ../platforms/linux.nix
     ../modules/common.nix
+    ../modules/desktop.nix
+    ../modules/development/agda.nix
     ../modules/development/readyset.nix
+    ../modules/development/ocaml.nix
   ] ++ (lib.optional (pathExists ../modules/private.nix) ../modules/private.nix);
 
   # for when hacking
@@ -24,24 +27,15 @@ in
   home.packages = with pkgs; [
     zoom-us
     slack
-    mysql
+    mariadb
     graphviz
+    gnuplot
     mypaint
     xdot
     tdesktop
     subsurface
-
-    (discord.override rec {
-      version = "0.0.16";
-      src = fetchurl {
-        url = "https://dl.discordapp.net/apps/linux/${version}/discord-${version}.tar.gz";
-        sha256 = "1s9qym58cjm8m8kg3zywvwai2i3adiq6sdayygk2zv72ry74ldai";
-      };
-    })
-
+    discord
     steam
-
-    awscli2
   ];
 
   systemd.user.services.laptop-keyboard = {
@@ -58,10 +52,10 @@ in
       RemainAfterExit = true;
       ExecStart = (
         "${pkgs.xorg.setxkbmap}/bin/setxkbmap "
-          + "-device ${laptopKeyboardId} "
-          + "-option caps:swapescape "
-          + "-option compose:ralt "
-          + "-option altwin:swap_alt_win"
+        + "-device ${laptopKeyboardId} "
+        + "-option caps:swapescape "
+        + "-option compose:ralt "
+        + "-option altwin:swap_alt_win"
       );
     };
   };
@@ -70,4 +64,14 @@ in
 
   xdg.mimeApps.defaultApplications."x-scheme-handler/tg" =
     "telegramdesktop.desktop";
+
+  programs.zsh.shellAliases = {
+    "graph" = "curl -s localhost:6033/graph | dot -Tpng | feh -";
+  };
+
+  programs.ssh.matchBlocks."grfn-dev" = {
+    host = "grfn-dev";
+    forwardAgent = true;
+    user = "ubuntu";
+  };
 }
diff --git a/users/aspen/system/home/modules/.gitignore b/users/aspen/system/home/modules/.gitignore
new file mode 100644
index 0000000000..a211cae6c6
--- /dev/null
+++ b/users/aspen/system/home/modules/.gitignore
@@ -0,0 +1 @@
+private.nix
diff --git a/users/grfn/system/home/modules/alacritty.nix b/users/aspen/system/home/modules/alacritty.nix
index 67d6638a31..561cab4d79 100644
--- a/users/grfn/system/home/modules/alacritty.nix
+++ b/users/aspen/system/home/modules/alacritty.nix
@@ -7,9 +7,7 @@
       font.size = 6;
       font.normal.family = "Meslo LGSDZ Nerd Font";
 
-      draw_bold_text_with_bright_colors = false;
-
-      key_bindings = [
+      keyboard.bindings = [
         {
           key = "Escape";
           mods = "Control";
@@ -18,6 +16,8 @@
       ];
 
       colors = with import ../common/solarized.nix; rec {
+        draw_bold_text_with_bright_colors = false;
+
         # Default colors
         primary = {
           background = base3;
diff --git a/users/aspen/system/home/modules/common.nix b/users/aspen/system/home/modules/common.nix
new file mode 100644
index 0000000000..b51ae1c7db
--- /dev/null
+++ b/users/aspen/system/home/modules/common.nix
@@ -0,0 +1,88 @@
+{ config, lib, pkgs, ... }:
+
+# Everything in here needs to work on linux or darwin, with or without a desktop
+# environment
+
+{
+  imports = [
+    ../modules/shell.nix
+    # ../modules/development.nix
+    ../modules/emacs.nix
+    ../modules/vim.nix
+    ../modules/tarsnap.nix
+    ../modules/twitter.nix
+    ../modules/lib/cloneRepo.nix
+  ];
+
+  home.username = "aspen";
+  home.homeDirectory = "/home/aspen";
+
+  programs.password-store.enable = true;
+
+  aspen.impure.clonedRepos.passwordStore = {
+    github = "glittershark/pass";
+    path = ".local/share/password-store";
+  };
+
+  home.packages = with pkgs; [
+    # System utilities
+    bat
+    htop
+    killall
+    bind
+    zip
+    unzip
+    tree
+    nmap
+    bc
+    pv
+
+    # Security
+    gnupg
+    keybase
+    openssl
+
+    # Nix things
+    nixfmt
+    nix-prefetch-github
+    nixpkgs-review
+    cachix
+    (writeShellScriptBin "rebuild-mugwump" ''
+      set -eo pipefail
+      cd ~/code/depot
+      nix build -f . users.aspen.system.system.mugwumpSystem -o /tmp/mugwump
+      nix copy -f . users.aspen.system.system.mugwumpSystem \
+        --to ssh://mugwump
+      system=$(readlink -ef /tmp/mugwump)
+      ssh mugwump sudo nix-env -p /nix/var/nix/profiles/system --set $system
+      ssh mugwump sudo $system/bin/switch-to-configuration switch
+      rm /tmp/mugwump
+    '')
+    (writeShellScriptBin "rebuild-roswell" ''
+      set -eo pipefail
+      cd ~/code/depot
+      nix build -f . users.aspen.system.system.roswellSystem -o /tmp/roswell
+      nix copy -f . users.aspen.system.system.roswellSystem \
+        --to ssh://roswell
+      system=$(readlink -ef /tmp/roswell)
+      ssh roswell sudo nix-env -p /nix/var/nix/profiles/system --set $system
+      ssh roswell sudo $system/bin/switch-to-configuration switch
+      rm /tmp/roswell
+    '')
+    (writeShellScriptBin "rebuild-home" ''
+      set -eo pipefail
+      cd ~/code/depot
+      home=$(nix-build -A users.aspen.system.home.$(hostname)Home -o /tmp/home)
+      nix-env -p /nix/var/nix/per-user/aspen/home --set $home
+      $home/activate
+    '')
+  ];
+
+  programs.ssh = { enable = true; };
+
+  programs.direnv = {
+    enable = true;
+    enableBashIntegration = true;
+    enableZshIntegration = true;
+  };
+}
diff --git a/users/aspen/system/home/modules/desktop.nix b/users/aspen/system/home/modules/desktop.nix
new file mode 100644
index 0000000000..e1841cd3c3
--- /dev/null
+++ b/users/aspen/system/home/modules/desktop.nix
@@ -0,0 +1,40 @@
+{ config, lib, pkgs, ... }:
+
+# Things that only work in the presence of a linux desktop environment
+
+{
+  imports = [
+    ./i3.nix
+    ./alacritty.nix
+  ];
+
+  home.packages = with pkgs; [
+    (writeShellApplication {
+      name = "edit-input";
+
+      runtimeInputs = [ xdotool xclip ];
+      text = ''
+        set -euo pipefail
+
+        sleep 0.2
+        xdotool key ctrl+a ctrl+c
+        xclip -out -selection clipboard > /tmp/EDIT
+        emacsclient -c /tmp/EDIT
+        xclip -in -selection clipboard < /tmp/EDIT
+        sleep 0.2
+        xdotool key ctrl+v
+        rm /tmp/EDIT
+      '';
+    })
+  ];
+
+  services.syncthing.tray.enable = true;
+
+  gtk = {
+    enable = true;
+    gtk3.bookmarks = [
+      "file:///home/aspen/code"
+      "file:///home/aspen/notes"
+    ];
+  };
+}
diff --git a/users/grfn/system/home/modules/development.nix b/users/aspen/system/home/modules/development.nix
index 1a5e912916..ca6ef131a3 100644
--- a/users/grfn/system/home/modules/development.nix
+++ b/users/aspen/system/home/modules/development.nix
@@ -2,12 +2,14 @@
 
 let
 
-  clj2nix = pkgs.callPackage (pkgs.fetchFromGitHub {
-    owner = "hlolli";
-    repo = "clj2nix";
-    rev = "3ab3480a25e850b35d1f532a5e4e7b3202232383";
-    sha256 = "1lry026mlpxp1j563qs13nhxf37i2zpl7lh0lgfdwc44afybqka6";
-  }) {};
+  clj2nix = pkgs.callPackage
+    (pkgs.fetchFromGitHub {
+      owner = "hlolli";
+      repo = "clj2nix";
+      rev = "3ab3480a25e850b35d1f532a5e4e7b3202232383";
+      sha256 = "1lry026mlpxp1j563qs13nhxf37i2zpl7lh0lgfdwc44afybqka6";
+    })
+    { };
 
   pg-dump-upsert = pkgs.buildGoModule rec {
     pname = "pg-dump-upsert";
@@ -20,7 +22,7 @@ let
       sha256 = "1an4h8jjbj3r618ykjwk9brii4h9cxjqy47c4c8rivnvhimgf4wm";
     };
 
-    vendorSha256 = "1a5fx6mrv30cl46kswicd8lf5i5shn1fykchvbnbhdpgxhbz6qi4";
+    vendorHash = "sha256:1a5fx6mrv30cl46kswicd8lf5i5shn1fykchvbnbhdpgxhbz6qi4";
   };
 
 in
@@ -30,8 +32,7 @@ with lib;
 {
   imports = [
     ./lib/zshFunctions.nix
-    ./development/kube.nix
-    # TODO(grfn): agda build is broken in the nixpkgs checkout
+    # TODO(aspen): agda build is broken in the nixpkgs checkout
     # ./development/agda.nix
     ./development/rust.nix
   ];
@@ -40,7 +41,6 @@ with lib;
     jq
     yq
     gron
-    gitAndTools.hub
     gitAndTools.tig
     gitAndTools.gh
     shellcheck
@@ -52,21 +52,12 @@ with lib;
     jsonnet
     ngrok
     amber
+    ocamlPackages.patdiff
 
     gdb
     lldb
     hyperfine
-    clang-tools_11
-    (rr.overrideAttrs (_: rec {
-      version = "f25671d094edac8059cec56b98d7f10f2c740697";
-      src = pkgs.fetchFromGitHub {
-        owner = "rr-debugger";
-        repo = "rr";
-        rev = version;
-        sha256 = "149s4mw8vl8d3nx15sfp62z0izp2dibz99k720j75rjnkwk2bq2z";
-        fetchSubmodules = true;
-      };
-    }))
+    clang-tools
 
     clj2nix
     clojure
@@ -77,17 +68,20 @@ with lib;
 
     nodePackages.prettier
   ] ++ optionals (stdenv.isLinux) [
-    # TODO(grfn): replace with stable again once the current julia debacle
+    # TODO(aspen): replace with stable again once the current julia debacle
     # is resolved upstream, see https://github.com/NixOS/nixpkgs/pull/121114
     julia_16-bin
     valgrind
+
+    linuxPackages.perf
+    rr
   ];
 
   programs.git = {
     enable = true;
     package = pkgs.gitFull;
     userEmail = "root@gws.fyi";
-    userName  = "Griffin Smith";
+    userName = "Aspen Smith";
     ignores = [
       "*.sw*"
       ".classpath"
@@ -101,6 +95,7 @@ with lib;
       github.user = "glittershark";
       merge.conflictstyle = "diff3";
       rerere.enabled = "true";
+      advice.skippedCherryPicks = "false";
     };
 
     delta = {
@@ -123,6 +118,7 @@ with lib;
 
   home.file.".psqlrc".text = ''
     \set QUIET 1
+
     \timing
     \set ON_ERROR_ROLLBACK interactive
     \set VERBOSITY verbose
@@ -132,6 +128,12 @@ with lib;
     \set HISTFILE ~/.psql_history- :DBNAME
     \set HISTCONTROL ignoredups
     \pset null [null]
+
+    \pset linestyle 'unicode'
+    \pset unicode_border_linestyle single
+    \pset unicode_column_linestyle single
+    \pset unicode_header_linestyle double
+
     \unset QUIET
   '';
 
@@ -185,7 +187,6 @@ with lib;
       "ga" = "git add";
       "gnp" = "git --no-pager";
       "g" = "git";
-      "git" = "hub";
       "grim" = "git fetch && git rebase -i --autostash origin/master";
       "grom" = "git fetch && git rebase --autostash origin/master";
       "groc" = "git fetch && git rebase --autostash origin/canon";
@@ -205,7 +206,11 @@ with lib;
 
     functions = {
       gdelmerged = ''
-      git branch --merged | egrep -v 'master' | tr -d '+ ' | xargs git branch -d
+        git branch --merged | egrep -v 'master' | tr -d '+ ' | xargs git branch -d
+      '';
+
+      gref = ''
+        git show -s --pretty=reference "$1" | xclip -selection clipboard
       '';
     };
   };
diff --git a/users/grfn/system/home/modules/development/agda.nix b/users/aspen/system/home/modules/development/agda.nix
index afd22a306d..55381994c4 100644
--- a/users/grfn/system/home/modules/development/agda.nix
+++ b/users/aspen/system/home/modules/development/agda.nix
@@ -29,7 +29,7 @@ in
       ]))
   ];
 
-  grfn.impure.clonedRepos = {
+  aspen.impure.clonedRepos = {
     agda-stdlib = {
       github = "agda/agda-stdlib";
       path = "code/agda-stdlib";
@@ -51,8 +51,8 @@ in
   '';
 
   home.file.".agda/libraries".text = ''
-    /home/grfn/code/agda-stdlib/standard-library.agda-lib
-    /home/grfn/code/agda-categories/agda-categories.agda-lib
+    /home/aspen/code/agda-stdlib/standard-library.agda-lib
+    /home/aspen/code/agda-categories/agda-categories.agda-lib
   '';
 
 }
diff --git a/users/grfn/system/home/modules/development/kube.nix b/users/aspen/system/home/modules/development/kube.nix
index 97ae4760d4..876b0c08df 100644
--- a/users/grfn/system/home/modules/development/kube.nix
+++ b/users/aspen/system/home/modules/development/kube.nix
@@ -16,7 +16,7 @@
     "kpa" = "kubectl get pods --all-namespaces";
     "klf" = "kubectl logs -f";
     "kdep" = "kubectl get deployments";
-    "ked" =  "kubectl edit deployment";
+    "ked" = "kubectl edit deployment";
     "kpw" = "kubectl get pods -w";
     "kew" = "kubectl get events -w";
     "kdel" = "kubectl delete";
diff --git a/users/aspen/system/home/modules/development/ocaml.nix b/users/aspen/system/home/modules/development/ocaml.nix
new file mode 100644
index 0000000000..5dcdd8980e
--- /dev/null
+++ b/users/aspen/system/home/modules/development/ocaml.nix
@@ -0,0 +1,17 @@
+{ config, lib, pkgs, ... }:
+
+{
+  home.packages = with pkgs; [
+    ocaml
+
+    # ocamlPackages.merlin
+    # ocamlPackages.utop
+    # ocamlPackages.ocp-indent
+    # ocamlPackages.ocamlformat
+  ];
+
+  programs.opam = {
+    enable = true;
+    enableZshIntegration = true;
+  };
+}
diff --git a/users/aspen/system/home/modules/development/readyset.nix b/users/aspen/system/home/modules/development/readyset.nix
new file mode 100644
index 0000000000..afe762468a
--- /dev/null
+++ b/users/aspen/system/home/modules/development/readyset.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+{
+  imports = [
+    ./rust.nix
+  ];
+
+  home.packages = with pkgs; [
+    # These go in $PATH so I can run it from rofi and parent to my WM
+    (writeShellScriptBin "dotclip" "xclip -out -selection clipboard | dot -Tpng | feh -")
+    (writeShellScriptBin "dotcontroller" "curl -s localhost:6033/graph | dot -Tpng | feh -")
+
+    rain
+    awscli2
+    ssm-session-manager-plugin
+    amazon-ecr-credential-helper
+    postgresql_15
+
+    # TODO remove override when https://github.com/NixOS/nixpkgs/pull/233826 is merged
+    (sysbench.overrideDerivation (oldAttrs: {
+      configureFlags = oldAttrs.configureFlags ++ [ "--with-pgsql" ];
+      buildInputs = oldAttrs.buildInputs ++ [ postgresql ];
+    }))
+  ];
+
+  programs.zsh.shellAliases = {
+    "tf" = "terraform";
+  };
+
+  home.file.".docker/config.json".text = builtins.toJSON {
+    credHelpers = {
+      "305232526136.dkr.ecr.us-east-2.amazonaws.com" = "ecr-login";
+    };
+  };
+
+  programs.zsh.functions."purge_deployment" = ''
+    for key in $(http :8500/v1/kv/$1 keys==true | jq -r .'[]'); do http DELETE ":8500/v1/kv/$key"; done
+  '';
+}
diff --git a/users/grfn/system/home/modules/development/rust.nix b/users/aspen/system/home/modules/development/rust.nix
index 018b338bff..c4b20f2315 100644
--- a/users/grfn/system/home/modules/development/rust.nix
+++ b/users/aspen/system/home/modules/development/rust.nix
@@ -1,18 +1,33 @@
 { config, lib, pkgs, ... }:
 
+let
+  inherit (config.lib) depot;
+in
+
+with lib;
 
 {
+
   home.packages = with pkgs; [
     rustup
-    rust-analyzer
     cargo-edit
     cargo-expand
-    cargo-flamegraph
-    cargo-rr
     cargo-udeps
     cargo-bloat
     sccache
     evcxr
+
+    depot.users.aspen.pkgs.cargo-hakari
+    depot.users.aspen.pkgs.cargo-nextest
+
+    # benchmarking+profiling
+    cargo-criterion
+    cargo-flamegraph
+    coz
+    inferno
+    hotspot
+  ] ++ optionals (stdenv.isLinux) [
+    cargo-rr
   ];
 
   programs.zsh.shellAliases = {
@@ -26,5 +41,9 @@
   home.file.".cargo/config".text = ''
     [build]
     rustc-wrapper = "${pkgs.sccache}/bin/sccache"
+
+    [target.x86_64-unknown-linux-gnu]
+    linker = "clang"
+    rustflags = ["-C", "link-arg=-fuse-ld=${pkgs.mold}/bin/mold"]
   '';
 }
diff --git a/users/grfn/system/home/modules/emacs.nix b/users/aspen/system/home/modules/emacs.nix
index 6cc38bc7ab..6936da4b9d 100644
--- a/users/grfn/system/home/modules/emacs.nix
+++ b/users/aspen/system/home/modules/emacs.nix
@@ -3,16 +3,17 @@
 with lib;
 
 let
- # doom-emacs = pkgs.callPackage (builtins.fetchTarball {
- #   url = https://github.com/vlaci/nix-doom-emacs/archive/master.tar.gz;
- # }) {
- #   doomPrivateDir = ./doom.d;  # Directory containing your config.el init.el
- #                               # and packages.el files
- # };
+  # doom-emacs = pkgs.callPackage (builtins.fetchTarball {
+  #   url = https://github.com/vlaci/nix-doom-emacs/archive/master.tar.gz;
+  # }) {
+  #   doomPrivateDir = ./doom.d;  # Directory containing your config.el init.el
+  #                               # and packages.el files
+  # };
 
   depot = config.lib.depot;
 
-in {
+in
+{
   imports = [
     ./lib/cloneRepo.nix
   ];
@@ -47,7 +48,9 @@ in {
             upquote
             varwidth
             wrapfig
-          ;
+            bussproofs
+            bussproofs-extra
+            ;
         })
 
         ispell
@@ -62,7 +65,7 @@ in {
 
       programs.emacs = {
         enable = true;
-        package = pkgs.emacsUnstable;
+        package = pkgs.emacs;
         extraPackages = (epkgs:
           (with epkgs; [
             tvlPackages.dottime
@@ -73,7 +76,7 @@ in {
         );
       };
 
-      grfn.impure.clonedRepos = {
+      aspen.impure.clonedRepos = {
         orgClubhouse = {
           github = "glittershark/org-clubhouse";
           path = "code/org-clubhouse";
@@ -82,14 +85,14 @@ in {
         doomEmacs = {
           github = "hlissner/doom-emacs";
           path = ".emacs.d";
-          after = ["emacs.d"];
+          after = [ "emacs.d" ];
           onClone = "bin/doom install";
         };
 
         "emacs.d" = {
           github = "glittershark/emacs.d";
           path = ".doom.d";
-          after = ["orgClubhouse"];
+          after = [ "orgClubhouse" ];
         };
       };
 
@@ -99,10 +102,7 @@ in {
     }
     (mkIf pkgs.stdenv.isLinux {
       # Notes
-      services.syncthing = {
-        enable = true;
-        tray = true;
-      };
+      services.syncthing.enable = true;
     })
   ];
 }
diff --git a/users/aspen/system/home/modules/email.nix b/users/aspen/system/home/modules/email.nix
new file mode 100644
index 0000000000..cb92c40cee
--- /dev/null
+++ b/users/aspen/system/home/modules/email.nix
@@ -0,0 +1,86 @@
+{ lib, pkgs, config, ... }:
+
+with lib;
+
+let
+
+  # from home-manager/modules/services/lieer.nix
+  escapeUnitName = name:
+    let
+      good = upperChars ++ lowerChars ++ stringToCharacters "0123456789-_";
+      subst = c: if any (x: x == c) good then c else "-";
+    in
+    stringAsChars subst name;
+
+  accounts = {
+    personal = {
+      primary = true;
+      address = "root@gws.fyi";
+      aliases = [ "aspen@gws.fyi" "aspen@gws.fyi" ];
+      passEntry = "root-gws-msmtp";
+    };
+  };
+
+in
+{
+  # 2022-09-26: workaround for home-manager defaulting to removed pkgs.gmailieer
+  # attribute, can likely be removed soon
+  programs.lieer.package = pkgs.lieer;
+
+  programs.lieer.enable = true;
+  programs.notmuch.enable = true;
+  services.lieer.enable = true;
+  programs.msmtp.enable = true;
+
+  home.packages = with pkgs; [
+    mu
+    msmtp
+    config.lib.depot.users.aspen.pkgs.notmuch-extract-patch
+  ];
+
+  systemd.user.services = mapAttrs'
+    (name: account: {
+      name = escapeUnitName "lieer-${name}";
+      value.Service = {
+        ExecStart = mkForce "${pkgs.writeShellScript "sync-${name}" ''
+        ${pkgs.lieer}/bin/gmi sync --path ~/mail/${name}
+      ''}";
+        Environment =
+          "NOTMUCH_CONFIG=${config.home.sessionVariables.NOTMUCH_CONFIG}";
+      };
+
+    })
+    accounts;
+
+  accounts.email.maildirBasePath = "mail";
+  accounts.email.accounts = mapAttrs
+    (_:
+      params@{ passEntry, ... }:
+      {
+        realName = "Aspen Smith";
+        passwordCommand = "pass ${passEntry}";
+
+        flavor = "gmail.com";
+
+        imapnotify = {
+          enable = true;
+          boxes = [ "Inbox" ];
+        };
+
+        gpg = {
+          key = "0F11A989879E8BBBFDC1E23644EF5B5E861C09A7";
+          signByDefault = true;
+        };
+
+        notmuch.enable = true;
+        lieer = {
+          enable = true;
+          sync = {
+            enable = true;
+            frequency = "*:*";
+          };
+        };
+        msmtp.enable = true;
+      } // builtins.removeAttrs params [ "passEntry" ])
+    accounts;
+}
diff --git a/users/grfn/system/home/modules/firefox.nix b/users/aspen/system/home/modules/firefox.nix
index c7e78685a5..c7e78685a5 100644
--- a/users/grfn/system/home/modules/firefox.nix
+++ b/users/aspen/system/home/modules/firefox.nix
diff --git a/users/grfn/system/home/modules/games.nix b/users/aspen/system/home/modules/games.nix
index da54f99e5b..dc6331d648 100644
--- a/users/grfn/system/home/modules/games.nix
+++ b/users/aspen/system/home/modules/games.nix
@@ -11,17 +11,18 @@ let
     theme = null;
     enableIntro = false;
     enableFPS = true;
+    enableDFHack = true;
   });
 
-  init = runCommand "init.txt" {} ''
-    substitute "${df-orig}/data/init/init.txt" $out \
+  init = runCommand "init.txt" { } ''
+    substitute "${df-orig}/data/init/init_default.txt" $out \
       --replace "[INTRO:YES]" "[INTRO:NO]" \
       --replace "[VOLUME:255]" "[VOLUME:0]" \
       --replace "[FPS:NO]" "[FPS:YES]"
   '';
 
-  d_init = runCommand "d_init.txt" {} ''
-    substitute "${df-orig}/data/init/d_init.txt" $out \
+  d_init = runCommand "d_init.txt" { } ''
+    substitute "${df-orig}/data/init/d_init_default.txt" $out \
       --replace "[AUTOSAVE:NONE]" "[AUTOSAVE:SEASONAL]" \
       --replace "[AUTOSAVE_PAUSE:NO]" "[AUTOSAVE_PAUSE:YES]" \
       --replace "[INITIAL_SAVE:NO]" "[INITIAL_SAVE:YES]" \
@@ -30,7 +31,7 @@ let
       --replace "[SHOW_FLOW_AMOUNTS:NO]" "[SHOW_FLOW_AMOUNTS:YES]"
   '';
 
-  df = runCommand "dwarf-fortress" {} ''
+  df = runCommand "dwarf-fortress" { } ''
     mkdir -p $out/bin
     sed \
       -e '4icp -f ${init} "$DF_DIR/data/init/init.txt"' \
@@ -43,17 +44,8 @@ let
     chmod +x $out/bin/dwarf-fortress
   '';
 
-in mkMerge [
-  {
-    home.packages = [
-      crawl
-      xonotic
-    ];
-  }
-  (mkIf stdenv.isLinux {
-    home.packages = [
-      df
-      multimc
-    ];
-  })
+in
+mkMerge [
+  { home.packages = [ crawl ]; }
+  (mkIf stdenv.isLinux { home.packages = [ df prismlauncher ]; })
 ]
diff --git a/users/aspen/system/home/modules/i3.nix b/users/aspen/system/home/modules/i3.nix
new file mode 100644
index 0000000000..f91527da44
--- /dev/null
+++ b/users/aspen/system/home/modules/i3.nix
@@ -0,0 +1,397 @@
+{ config, lib, pkgs, ... }:
+let
+  inherit (config.lib) depot;
+
+  mod = "Mod4";
+  solarized = import ../common/solarized.nix;
+  # TODO pull this out into lib
+  emacsclient = eval: pkgs.writeShellScript "emacsclient-eval" ''
+    msg=$(emacsclient --eval '${eval}' 2>&1)
+    echo "''${msg:1:-1}"
+  '';
+
+  i3status-conf = pkgs.writeText "i3status.conf" ''
+    general {
+        output_format = i3bar
+        colors = true
+        color_good = "#859900"
+
+        interval = 1
+    }
+
+    order += "external_script current_task"
+    order += "external_script inbox"
+    order += "spotify"
+    order += "weather_owm"
+    order += "volume_status"
+    order += "wireless ${config.system.machine.wirelessInterface}"
+    # order += "ethernet enp3s0f0"
+    order += "cpu_usage"
+    ${lib.optionalString (!isNull config.system.machine.battery) ''
+      order += "battery ${toString config.system.machine.battery}"
+    ''}
+    # order += "volume master"
+    order += "time"
+    order += "tztime utc"
+
+    mpd {
+        format = "%artist - %album - %title"
+    }
+
+    wireless ${config.system.machine.wirelessInterface} {
+        format_up = "W: (%quality - %essid - %bitrate) %ip"
+        format_down = "W: -"
+    }
+
+    ethernet enp3s0f0 {
+        format_up = "E: %ip"
+        format_down = "E: -"
+    }
+
+    battery ${toString config.system.machine.battery} {
+        format = "%status %percentage (%remaining)"
+        path = "/sys/class/power_supply/BAT%d/uevent"
+        low_threshold = 10
+    }
+
+    cpu_usage {
+        format = "CPU: %usage"
+    }
+
+    load {
+        format = "%5min"
+    }
+
+    time {
+        format = "    %a %h %d โŒš   %I:%M     "
+    }
+
+    spotify {
+        color_playing = "#fdf6e3"
+        color_paused = "#93a1a1"
+        format_stopped = ""
+        format_down = ""
+        format = "{title} - {artist} ({album})"
+    }
+
+    external_script inbox {
+        script_path = '${emacsclient "(aspen/num-inbox-items-message)"}'
+        format = 'Inbox: {output}'
+        cache_timeout = 120
+        color = "#93a1a1"
+    }
+
+    external_script current_task {
+        script_path = '${
+          emacsclient "(aspen/org-current-clocked-in-task-message)"
+        }'
+        # format = '{output}'
+        cache_timeout = 60
+        color = "#93a1a1"
+    }
+
+    tztime utc {
+        timezone = "UTC"
+        format = "    %Hยท%M    "
+    }
+
+    volume_status {
+        format = "โ˜Š {percentage}"
+        format_muted = "โ˜Š X"
+        # device = "default"
+        # mixer_idx = 0
+    }
+
+    weather_owm {
+        api_key = '@owmApiKey@'
+        unit_temperature = 'c'
+        format = '{icon} {temperature}[ {rain}]'
+    }
+  '';
+
+  i3status-command = pkgs.writeShellScript "i3status.sh" ''
+    sed -s "s/@owmApiKey@/$(pass owm-api-key)/" \
+      < ${i3status-conf} \
+      > /tmp/i3status.conf
+    py3status -c /tmp/i3status.conf
+  '';
+
+  inherit (builtins) map;
+  inherit (lib) mkMerge range;
+in
+{
+  options = with lib; {
+    system.machine = {
+      wirelessInterface = mkOption {
+        description = ''
+          Name of the primary wireless interface. Used by i3status, etc.
+        '';
+        default = "wlp3s0";
+        type = types.str;
+      };
+
+      i3FontSize = mkOption {
+        description = "Font size to use in i3 window decorations etc.";
+        default = 6;
+        type = types.int;
+      };
+
+      battery = mkOption {
+        description = "Battery index for this system's battery";
+        default = 0;
+        type = types.nullOr types.int;
+      };
+    };
+  };
+
+  config =
+    let
+      fontName = "MesloLGSDZ";
+      fontSize = config.system.machine.i3FontSize;
+      fonts = {
+        names = [ fontName ];
+        size = fontSize * 1.0;
+      };
+      decorationFont = "${fontName} ${toString fontSize}";
+    in
+    {
+      home.packages = with pkgs; [
+        rofi
+        rofi-pass
+        depot.users.aspen.pkgs.py3status
+        i3lock
+        i3status
+        dconf # for gtk
+
+        # Screenshots
+        maim
+
+        # GIFs
+        picom
+        peek
+
+        (pkgs.writeShellScriptBin "lock" ''
+          playerctl pause
+          ${pkgs.i3lock}/bin/i3lock -c 222222
+        '')
+      ];
+
+      xsession.scriptPath = ".xsession";
+
+      xsession.windowManager.i3 = {
+        enable = true;
+        config = {
+          modifier = mod;
+          keybindings =
+            mkMerge (
+              (map
+                (n: {
+                  "${mod}+${toString n}" =
+                    "workspace ${toString n}";
+                  "${mod}+Shift+${toString n}" =
+                    "move container to workspace ${toString n}";
+                })
+                (range 0 9))
+              ++ [
+                (rec {
+                  "${mod}+h" = "focus left";
+                  "${mod}+j" = "focus down";
+                  "${mod}+k" = "focus up";
+                  "${mod}+l" = "focus right";
+                  "${mod}+semicolon" = "focus parent";
+
+                  "${mod}+Shift+h" = "move left";
+                  "${mod}+Shift+j" = "move down";
+                  "${mod}+Shift+k" = "move up";
+                  "${mod}+Shift+l" = "move right";
+
+                  "${mod}+Shift+x" = "kill";
+
+                  "${mod}+Return" = "exec alacritty";
+
+                  "${mod}+Shift+s" = "split h";
+                  "${mod}+Shift+v" = "split v";
+                  "${mod}+e" = "layout toggle split";
+                  "${mod}+w" = "layout tabbed";
+                  "${mod}+s" = "layout stacking";
+
+                  "${mod}+f" = "fullscreen";
+
+                  "${mod}+Shift+r" = "restart";
+
+                  "${mod}+r" = "mode resize";
+
+                  # Marks
+                  "${mod}+Shift+m" = ''exec i3-input -F "mark %s" -l 1 -P 'Mark: ' '';
+                  "${mod}+m" = ''exec i3-input -F '[con_mark="%s"] focus' -l 1 -P 'Go to: ' '';
+
+                  # Screenshots
+                  "${mod}+q" = "exec \"maim | xclip -selection clipboard -t image/png\"";
+                  "${mod}+Shift+q" = "exec \"maim -s | xclip -selection clipboard -t image/png\"";
+                  "${mod}+Ctrl+q" = "exec ${pkgs.writeShellScript "peek.sh" ''
+              ${pkgs.picom}/bin/picom &
+              picom_pid=$!
+              ${pkgs.peek}/bin/peek || true
+              kill -SIGINT $picom_pid
+            ''}";
+
+                  # Launching applications
+                  "${mod}+u" = "exec ${pkgs.writeShellScript "rofi" ''
+              rofi \
+                -modi 'combi' \
+                -combi-modi "window,drun,ssh,run" \
+                -font '${decorationFont}' \
+                -show combi
+            ''}";
+
+                  # Passwords
+                  "${mod}+p" = "exec rofi-pass -font '${decorationFont}'";
+
+                  # Edit current buffer
+                  "${mod}+v" = "exec edit-input";
+
+                  # Media
+                  "XF86AudioPlay" = "exec playerctl -p spotify play-pause";
+                  "XF86AudioNext" = "exec playerctl -p spotify next";
+                  "XF86AudioPrev" = "exec playerctl -p spotify previous";
+                  "XF86AudioRaiseVolume" = "exec pulseaudio-ctl up";
+                  "XF86AudioLowerVolume" = "exec pulseaudio-ctl down";
+                  "XF86AudioMute" = "exec pulseaudio-ctl mute";
+
+                  # Lock
+                  Pause = "exec lock";
+
+                  # Brightness
+                  "XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl -q s 5%-";
+                  "XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl -q s 5%+";
+
+                  # Sleep/hibernate
+                  # "${mod}+Escape" = "exec systemctl suspend";
+                  # "${mod}+Shift+Escape" = "exec systemctl hibernate";
+
+                  # Scratch buffer
+                  "${mod}+minus" = "scratchpad show";
+                  "${mod}+Shift+minus" = "move scratchpad";
+                  "${mod}+space" = "focus mode_toggle";
+                  "${mod}+Shift+space" = "floating toggle";
+
+                  # Screen Layout
+                  "${mod}+Shift+t" = "exec xrandr --auto";
+
+                  # Notifications
+                  "${mod}+Shift+n" = "exec killall -SIGUSR1 .dunst-wrapped";
+                  "${mod}+n" = "exec killall -SIGUSR2 .dunst-wrapped";
+                  "Control+space" = "exec ${pkgs.dunst}/bin/dunstctl close";
+                  "Control+Shift+space" = "exec ${pkgs.dunst}/bin/dunstctl close-all";
+                  "Control+grave" = "exec ${pkgs.dunst}/bin/dunstctl history-pop";
+                  "Control+Shift+period" = "exec ${pkgs.dunst}/bin/dunstctl action";
+                })
+              ]
+            );
+
+          inherit fonts;
+
+          colors = with solarized; rec {
+            focused = {
+              border = base01;
+              background = base01;
+              text = base3;
+              indicator = red;
+              childBorder = base02;
+            };
+            focusedInactive = focused // {
+              border = base03;
+              background = base03;
+              # text = base1;
+            };
+            unfocused = focusedInactive;
+            background = base03;
+          };
+
+          modes.resize = {
+            l = "resize shrink width 5 px or 5 ppt";
+            k = "resize grow height 5 px or 5 ppt";
+            j = "resize shrink height 5 px or 5 ppt";
+            h = "resize grow width 5 px or 5 ppt";
+
+            Return = "mode \"default\"";
+          };
+
+          bars = [{
+            statusCommand = "${i3status-command}";
+            inherit fonts;
+            position = "top";
+            colors = with solarized; rec {
+              background = base03;
+              statusline = base3;
+              separator = base1;
+              activeWorkspace = {
+                border = base03;
+                background = base1;
+                text = base3;
+              };
+              focusedWorkspace = activeWorkspace;
+              inactiveWorkspace = activeWorkspace // {
+                background = base01;
+              };
+              urgentWorkspace = activeWorkspace // {
+                background = red;
+              };
+            };
+          }];
+
+          window.titlebar = true;
+        };
+      };
+
+      services.dunst = {
+        enable = true;
+        settings = with solarized; {
+          global = {
+            font = "MesloLGSDZ ${toString (config.system.machine.i3FontSize * 1.5)}";
+            allow_markup = true;
+            format = "<b>%s</b>\n%b";
+            sort = true;
+            alignment = "left";
+            geometry = "600x15-40+40";
+            idle_threshold = 120;
+            separator_color = "frame";
+            separator_height = 1;
+            word_wrap = true;
+            padding = 8;
+            horizontal_padding = 8;
+            max_icon_size = 45;
+          };
+
+          frame = {
+            width = 0;
+            color = "#aaaaaa";
+          };
+
+          urgency_low = {
+            background = base03;
+            foreground = base3;
+            timeout = 5;
+          };
+
+          urgency_normal = {
+            background = base02;
+            foreground = base3;
+            timeout = 7;
+          };
+
+          urgency_critical = {
+            background = red;
+            foreground = base3;
+            timeout = 0;
+          };
+        };
+      };
+
+      gtk = {
+        enable = true;
+        iconTheme.name = "Adwaita";
+        theme.name = "Adwaita";
+      };
+    };
+}
diff --git a/users/aspen/system/home/modules/lib/cloneRepo.nix b/users/aspen/system/home/modules/lib/cloneRepo.nix
new file mode 100644
index 0000000000..f3cf59d249
--- /dev/null
+++ b/users/aspen/system/home/modules/lib/cloneRepo.nix
@@ -0,0 +1,76 @@
+{ lib, config, ... }:
+with lib;
+{
+  options = {
+    aspen.impure.clonedRepos = mkOption {
+      description = "Repositories to clone";
+      default = { };
+      type = with types; attrsOf (
+        let
+          sm = submodule {
+            options = {
+              url = mkOption {
+                type = nullOr str;
+                description = "URL of repository to clone";
+                default = null;
+              };
+
+              github = mkOption {
+                type = nullOr str;
+                description = "Github owner/repo of repository to clone";
+                default = null;
+              };
+
+              path = mkOption {
+                type = str;
+                description = "Path to clone to";
+              };
+
+              onClone = mkOption {
+                type = str;
+                description = ''
+                  Shell command to run after cloning the repo for the first time.
+                  Runs inside the repo itself.
+                '';
+                default = "";
+              };
+
+              after = mkOption {
+                type = listOf str;
+                description = "Activation hooks that this repository must be cloned after";
+                default = [ ];
+              };
+            };
+          };
+        in
+        addCheck sm (cr: (! isNull cr.url || ! isNull cr.github))
+      );
+    };
+  };
+
+  config = {
+    home.activation =
+      mapAttrs
+        (_: { url
+            , path
+            , github
+            , onClone
+            , after
+            , ...
+            }:
+          let repoURL = if isNull url then "git@github.com:${github}" else url;
+          in hm.dag.entryAfter ([ "writeBoundary" ] ++ after) ''
+            $DRY_RUN_CMD mkdir -p $(dirname "${path}")
+            if [[ ! -d ${path} ]]; then
+              if $DRY_RUN_CMD git clone "${repoURL}" "${path}"; then
+                pushd ${path}
+                $DRY_RUN_CMD ${onClone}
+                popd
+              else
+                echo "Git repository ${path} failed to clone"
+              fi
+            fi
+          '')
+        config.aspen.impure.clonedRepos;
+  };
+}
diff --git a/users/grfn/system/home/modules/lib/zshFunctions.nix b/users/aspen/system/home/modules/lib/zshFunctions.nix
index 7c39b3478c..228dc6379f 100644
--- a/users/grfn/system/home/modules/lib/zshFunctions.nix
+++ b/users/aspen/system/home/modules/lib/zshFunctions.nix
@@ -6,16 +6,18 @@ with lib;
   options = {
     programs.zsh.functions = mkOption {
       description = "An attribute set that maps function names to their source";
-      default = {};
+      default = { };
       type = with types; attrsOf (either str path);
     };
   };
 
   config.programs.zsh.initExtra = concatStringsSep "\n" (
-    mapAttrsToList (name: funSrc: ''
-      function ${name}() {
-        ${funSrc}
-      }
-    '') config.programs.zsh.functions
+    mapAttrsToList
+      (name: funSrc: ''
+        function ${name}() {
+          ${funSrc}
+        }
+      '')
+      config.programs.zsh.functions
   );
 }
diff --git a/users/aspen/system/home/modules/obs.nix b/users/aspen/system/home/modules/obs.nix
new file mode 100644
index 0000000000..7962320f8a
--- /dev/null
+++ b/users/aspen/system/home/modules/obs.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (pkgs) obs-studio;
+  obs-input-overlay = pkgs.obs-studio-plugins.input-overlay;
+in
+
+{
+  home.packages = [
+    obs-studio
+    obs-input-overlay
+  ];
+
+  xdg.configFile."obs-studio/plugins/input-overlay/bin/64bit/input-overlay.so".source =
+    "${obs-input-overlay}/lib/obs-plugins/input-overlay.so";
+  xdg.configFile."obs-studio/plugins/input-overlay/data".source =
+    "${obs-input-overlay}/share/obs/obs-plugins/input-overlay";
+}
diff --git a/users/grfn/system/home/modules/ptt.nix b/users/aspen/system/home/modules/ptt.nix
index 436c8f2617..436c8f2617 100644
--- a/users/grfn/system/home/modules/ptt.nix
+++ b/users/aspen/system/home/modules/ptt.nix
diff --git a/users/grfn/system/home/modules/pure.zsh-theme b/users/aspen/system/home/modules/pure.zsh-theme
index b4776e8159..666e28259c 100755
--- a/users/grfn/system/home/modules/pure.zsh-theme
+++ b/users/aspen/system/home/modules/pure.zsh-theme
@@ -92,6 +92,10 @@ prompt_pure_nix_info() {
 }
 
 prompt_pure_precmd() {
+	if [[ "$TERM" == "dumb" ]]; then
+		return
+	fi
+
 	# shows the full path in the title
 	print -Pn '\e]0;%~\a'
 
diff --git a/users/aspen/system/home/modules/rtlsdr.nix b/users/aspen/system/home/modules/rtlsdr.nix
new file mode 100644
index 0000000000..c8a404a1f4
--- /dev/null
+++ b/users/aspen/system/home/modules/rtlsdr.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  nixpkgs-gnuradio = import
+    (pkgs.fetchFromGitHub {
+      owner = "doronbehar";
+      repo = "nixpkgs";
+      rev = "712561aa5f10bfe6112a1726a912585612a70d1f";
+      sha256 = "04yqflbwjcfl9vlplphpj82csqqz9k6m3nj1ybhwgmsc4by7vivl";
+    })
+    { };
+
+in
+
+{
+  home.packages = with pkgs; [
+    rtl-sdr
+    nixpkgs-gnuradio.gnuradio
+    nixpkgs-gnuradio.gnuradio.plugins.osmosdr
+    nixpkgs-gnuradio.gqrx
+  ];
+}
diff --git a/users/grfn/system/home/modules/shell.nix b/users/aspen/system/home/modules/shell.nix
index 088fe9238c..844f76c286 100644
--- a/users/grfn/system/home/modules/shell.nix
+++ b/users/aspen/system/home/modules/shell.nix
@@ -2,14 +2,7 @@
 let
   shellAliases = rec {
     # NixOS stuff
-    hms = "home-manager switch";
-    nor = "sudo nixos-rebuild switch";
-    nrs = nor;
-    nrb = "sudo nixos-rebuild boot";
     ncg = "nix-collect-garbage";
-    vihome = "vim ~/.config/nixpkgs/home.nix && home-manager switch";
-    virc = "vim ~/code/system/home/modules/shell.nix && home-manager switch && source ~/.zshrc";
-    visystem = "sudo vim /etc/nixos/configuration.nix && sudo nixos-rebuild switch";
 
     # Nix
     ns = "nix-shell";
@@ -25,12 +18,14 @@ let
     "dclf" = "docker-compose logs -f";
     "dck" = "docker";
     "dockerclean" = "dockercleancontainers && dockercleanimages";
-    "dockercleanimages" = "docker images -a --no-trunc | grep none | awk '{print \$$3}' | xargs -L 1 -r docker rmi";
-    "dockercleancontainers" = "docker ps -a --no-trunc| grep 'Exit' | awk '{print \$$1}' | xargs -L 1 -r docker rm";
+    "dockercleanimages" =
+      "docker images -a --no-trunc | grep none | awk '{print $$3}' | xargs -L 1 -r docker rmi";
+    "dockercleancontainers" =
+      "docker ps -a --no-trunc| grep 'Exit' | awk '{print $$1}' | xargs -L 1 -r docker rm";
 
     # Directories
     stck = "dirs -v";
-    b= "cd ~1";
+    b = "cd ~1";
     ".." = "cd ..";
     "..." = "cd ../..";
     "...." = "cd ../../..";
@@ -39,40 +34,18 @@ let
     # Aliases from old config
     "http" = "http --style solarized";
     "grep" = "grep $GREP_OPTIONS";
-    "bak" = "~/bin/backup.sh";
-    "xmm" = "xmodmap ~/.Xmodmap";
-    "asdflkj" = "asdf";
-    "asdf" = "asdfghjkl";
-    "asdfghjkl" = "echo \"Having some trouble?\"";
-    "ift" = "sudo iftop -i wlp3s0";
-    "first" = "awk '{print \$$1}'";
-    "cmt" = "git log --oneline | fzf-tmux | awk '{print \$$1}'";
-    "workmon" = "xrandr --output DP-2 --pos 1440x900 --primary";
-    "vi" = "vim";
-    "adbdev" = "adb devices";
-    "adbcon" = "adb connect $GNEX_IP";
-    "mpalb" = "mpc search album";
-    "mpart" = "mpc search artist";
-    "mps" = "mpc search";
-    "mpa" = "mpc add";
-    "mpt" = "mpc toggle";
-    "mpl" = "mpc playlist";
-    "dsstore" = "find . -name '*.DS_Store' -type f -ls -delete";
     "df" = "df -h";
-    "fs" = "stat -f '%z bytes'";
     "ll" = "ls -al";
     "la" = "ls -a";
   };
-in {
-  home.packages = with pkgs; [
-    zsh
-    autojump
-    ntfy
-  ];
+in
+{
+  home.packages = with pkgs; [ zsh autojump ];
 
   home.sessionVariables = {
     EDITOR = "vim";
-    LS_COLORS = "no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.ogg=01;35:*.mp3=01;35:*.wav=01;35:";
+    LS_COLORS =
+      "no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.ogg=01;35:*.mp3=01;35:*.wav=01;35:";
     BROWSER = "firefox";
     BAT_THEME = "ansi-light";
   };
@@ -84,7 +57,7 @@ in {
 
   programs.zsh = {
     enable = true;
-    enableAutosuggestions = true;
+    autosuggestion.enable = true;
     autocd = true;
 
     inherit shellAliases;
@@ -132,6 +105,12 @@ in {
       };
     }];
 
+    initExtraFirst = ''
+      if [[ "$TERM" = "dumb" ]]; then
+        return
+      fi
+    '';
+
     initExtraBeforeCompInit = ''
       zstyle ':completion:*' completer _complete _ignored _correct _approximate
       zstyle ':completion:*' matcher-list \'\' 'm:{[:lower:]}={[:upper:]} m:{[:lower:][:upper:]}={[:upper:][:lower:]} r:|[._- :]=** r:|=**' 'l:|=* r:|=*'
@@ -144,33 +123,36 @@ in {
     '';
 
     initExtra = ''
-      source ${./zshrc}
-      source ${pkgs.fetchFromGitHub {
-        owner = "zsh-users";
-        repo = "zsh-syntax-highlighting";
-        rev = "7678a8a22780141617f809002eeccf054bf8f448";
-        sha256 = "0xh4fbd54kvwwpqvabk8lpw7m80phxdzrd75q3y874jw0xx1a9q6";
-      }}/zsh-syntax-highlighting.zsh
-      source ${pkgs.autojump}/share/autojump/autojump.zsh
-      source ${pkgs.fetchFromGitHub {
-        owner = "chisui";
-        repo = "zsh-nix-shell";
-        rev = "a65382a353eaee5a98f068c330947c032a1263bb";
-        sha256 = "0l41ac5b7p8yyjvpfp438kw7zl9dblrpd7icjg1v3ig3xy87zv0n";
-      }}/nix-shell.plugin.zsh
-
-      eval "$(${pkgs.ntfy}/bin/ntfy shell-integration)"
-
-      export RPS1=""
-      autoload -U promptinit; promptinit
-      prompt pure
+      if [[ "$TERM" != "dumb" ]]; then
+        source ${./zshrc}
+        source ${
+          pkgs.fetchFromGitHub {
+            owner = "zsh-users";
+            repo = "zsh-syntax-highlighting";
+            rev = "7678a8a22780141617f809002eeccf054bf8f448";
+            sha256 = "0xh4fbd54kvwwpqvabk8lpw7m80phxdzrd75q3y874jw0xx1a9q6";
+          }
+        }/zsh-syntax-highlighting.zsh
+        source ${pkgs.autojump}/share/autojump/autojump.zsh
+        source ${
+          pkgs.fetchFromGitHub {
+            owner = "chisui";
+            repo = "zsh-nix-shell";
+            rev = "a65382a353eaee5a98f068c330947c032a1263bb";
+            sha256 = "0l41ac5b7p8yyjvpfp438kw7zl9dblrpd7icjg1v3ig3xy87zv0n";
+          }
+        }/nix-shell.plugin.zsh
+
+        export RPS1=""
+        autoload -U promptinit; promptinit
+        prompt pure
+      fi
 
       if [[ "$TERM" == "dumb" ]]; then
         unsetopt zle
         unsetopt prompt_cr
         unsetopt prompt_subst
-        unfunction precmd
-        unfunction preexec
+        unset zle_bracketed_paste
         export PS1='$ '
       fi
     '';
diff --git a/users/aspen/system/home/modules/tarsnap.nix b/users/aspen/system/home/modules/tarsnap.nix
new file mode 100644
index 0000000000..fb2a050852
--- /dev/null
+++ b/users/aspen/system/home/modules/tarsnap.nix
@@ -0,0 +1,64 @@
+{ config, lib, pkgs, ... }:
+
+{
+  home.packages = with pkgs; [
+    tarsnap
+  ];
+
+  home.file.".tarsnaprc".text = ''
+    ### Recommended options
+
+    # Tarsnap cache directory
+    cachedir /home/aspen/.cache/tarsnap
+
+    # Tarsnap key file
+    keyfile /home/aspen/.private/tarsnap.key
+
+    # Don't archive files which have the nodump flag set.
+    nodump
+
+    # Print statistics when creating or deleting archives.
+    print-stats
+
+    # Create a checkpoint once per GB of uploaded data.
+    checkpoint-bytes 1G
+
+    ### Commonly useful options
+
+    # Use SI prefixes to make numbers printed by --print-stats more readable.
+    humanize-numbers
+
+    ### Other options, not applicable to most systems
+
+    # Aggressive network behaviour: Use multiple TCP connections when
+    # writing archives.  Use of this option is recommended only in
+    # cases where TCP congestion control is known to be the limiting
+    # factor in upload performance.
+    #aggressive-networking
+
+    # Exclude files and directories matching specified patterns.
+    # Only one file or directory per command; multiple "exclude"
+    # commands may be given.
+    #exclude
+
+    # Include only files and directories matching specified patterns.
+    # Only one file or directory per command; multiple "include"
+    # commands may be given.
+    #include
+
+    # Attempt to reduce tarsnap memory consumption.  This option
+    # will slow down the process of creating archives, but may help
+    # on systems where the average size of files being backed up is
+    # less than 1 MB.
+    #lowmem
+
+    # Try even harder to reduce tarsnap memory consumption.  This can
+    # significantly slow down tarsnap, but reduces its memory usage
+    # by an additional factor of 2 beyond what the lowmem option does.
+    #verylowmem
+
+    # Snapshot time.  Use this option if you are backing up files
+    # from a filesystem snapshot rather than from a "live" filesystem.
+    #snaptime <file>
+  '';
+}
diff --git a/users/aspen/system/home/modules/tmux.nix b/users/aspen/system/home/modules/tmux.nix
new file mode 100644
index 0000000000..adbaa02f32
--- /dev/null
+++ b/users/aspen/system/home/modules/tmux.nix
@@ -0,0 +1,42 @@
+{ config, lib, pkgs, ... }:
+
+{
+  programs.tmux = {
+    enable = true;
+    customPaneNavigationAndResize = true;
+    keyMode = "vi";
+    newSession = true;
+    prefix = "C-a";
+    shell = "${pkgs.zsh}/bin/zsh";
+    shortcut = "a";
+
+    extraConfig = ''
+      set -g status-bg "colour0"
+      set -g message-command-fg "colour7"
+      set -g status-justify "centre"
+      set -g status-left-length "100"
+      set -g status "on"
+      set -g pane-active-border-fg "colour14"
+      set -g message-bg "colour11"
+      set -g status-right-length "100"
+      set -g status-right-attr "none"
+      set -g message-fg "colour7"
+      set -g message-command-bg "colour11"
+      set -g status-attr "none"
+      # set -g status-utf8 "on"
+      set -g pane-border-fg "colour11"
+      set -g status-left-attr "none"
+      setw -g window-status-fg "colour10"
+      setw -g window-status-attr "none"
+      setw -g window-status-activity-bg "colour0"
+      setw -g window-status-activity-attr "none"
+      setw -g window-status-activity-fg "colour14"
+      setw -g window-status-separator ""
+      setw -g window-status-bg "colour0"
+      set -g status-left "#[fg=colour15,bg=colour14,bold] #S #[fg=colour14,bg=colour11,nobold,nounderscore,noitalics]๎‚ฐ#[fg=colour7,bg=colour11] #F #[fg=colour11,bg=colour0,nobold,nounderscore,noitalics]๎‚ฐ#[fg=colour10,bg=colour0] #W #[fg=colour0,bg=colour0,nobold,nounderscore,noitalics]๎‚ฐ"
+      set -g status-right "#{battery_status_bg} Batt: #{battery_percentage} #{battery_remain} | #[fg=colour0,bg=colour0,nobold,nounderscore,noitalics]๎‚ฒ#[fg=colour10,bg=colour0] %a #[fg=colour11,bg=colour0,nobold,nounderscore,noitalics]๎‚ฒ#[fg=colour7,bg=colour11] %b %d ๎‚ณ %R #[fg=colour14,bg=colour11,nobold,nounderscore,noitalics]๎‚ฒ#[fg=colour15,bg=colour14] #H "
+      setw -g window-status-format "#[fg=colour0,bg=colour0,nobold,nounderscore,noitalics]๎‚ฐ#[default] #I ๎‚ฑ #W #[fg=colour0,bg=colour0,nobold,nounderscore,noitalics]๎‚ฐ"
+      setw -g window-status-current-format "#[fg=colour0,bg=colour11,nobold,nounderscore,noitalics]๎‚ฐ#[fg=colour7,bg=colour11] #I ๎‚ฑ #W #[fg=colour11,bg=colour0,nobold,nounderscore,noitalics]๎‚ฐ"
+    '';
+  };
+}
diff --git a/users/grfn/system/home/modules/twitter.nix b/users/aspen/system/home/modules/twitter.nix
index 3cb2e90adc..ab5647e418 100644
--- a/users/grfn/system/home/modules/twitter.nix
+++ b/users/aspen/system/home/modules/twitter.nix
@@ -1,6 +1,10 @@
 { pkgs, lib, ... }:
 
 {
+  imports = [
+    ./lib/zshFunctions.nix
+  ];
+
   home.packages = with pkgs; [
     t
   ];
diff --git a/users/grfn/system/home/modules/vim.nix b/users/aspen/system/home/modules/vim.nix
index b87cb09ad1..b87cb09ad1 100644
--- a/users/grfn/system/home/modules/vim.nix
+++ b/users/aspen/system/home/modules/vim.nix
diff --git a/users/grfn/system/home/modules/vimrc b/users/aspen/system/home/modules/vimrc
index 3e33b5e2be..01572f3946 100644
--- a/users/grfn/system/home/modules/vimrc
+++ b/users/aspen/system/home/modules/vimrc
@@ -22,7 +22,7 @@ set expandtab
 set noerrorbells visualbell t_vb=
 set laststatus=2
 set hidden
-let mapleader = ','
+let mapleader = ' '
 let maplocalleader = '\'
 set undofile
 " set undodir=~/.vim/undo
diff --git a/users/grfn/system/home/modules/zshrc b/users/aspen/system/home/modules/zshrc
index a12173d684..a12173d684 100644
--- a/users/grfn/system/home/modules/zshrc
+++ b/users/aspen/system/home/modules/zshrc
diff --git a/users/grfn/system/home/platforms/darwin.nix b/users/aspen/system/home/platforms/darwin.nix
index cf0375e941..f98b80f269 100644
--- a/users/grfn/system/home/platforms/darwin.nix
+++ b/users/aspen/system/home/platforms/darwin.nix
@@ -10,7 +10,7 @@ with lib;
       pinentry_mac
     ];
 
-    home.activation.linkApplications = lib.hm.dag.entryAfter ["writeBoundary"] ''
+    home.activation.linkApplications = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
       $DRY_RUN_CMD ln -sf $VERBOSE_ARG \
         ~/.nix-profile/Applications/* ~/Applications/
     '';
diff --git a/users/grfn/system/home/platforms/linux.nix b/users/aspen/system/home/platforms/linux.nix
index 92924e4199..d7a04e872d 100644
--- a/users/grfn/system/home/platforms/linux.nix
+++ b/users/aspen/system/home/platforms/linux.nix
@@ -5,28 +5,20 @@ let
   depot = config.lib.depot;
 
 in
-
 {
   imports = [
     ../modules/alacritty.nix
-    ../modules/alsi.nix
     ../modules/development.nix
     ../modules/emacs.nix
     ../modules/email.nix
     ../modules/firefox.nix
-    ../modules/games.nix
-    ../modules/obs.nix
-    ../modules/i3.nix
     ../modules/shell.nix
-    ../modules/tarsnap.nix
     ../modules/vim.nix
   ];
 
   xsession.enable = true;
 
   home.packages = with pkgs; [
-    (import (fetchTarball "https://github.com/ashkitten/nixpkgs/archive/init-glimpse.tar.gz") {}).glimpse
-
     # Desktop stuff
     arandr
     firefox
@@ -43,7 +35,7 @@ in
     gnutls
     pandoc
     barrier
-    depot.tools.nsfv-setup
+    gimp # TODO(aspen): use glimpse once it build again
 
     # System utilities
     powertop
@@ -52,7 +44,7 @@ in
     gdmap
     lsof
     tree
-    ncat
+    nmap
     iftop
 
     # Security
@@ -60,7 +52,8 @@ in
     keybase
     openssl
     yubikey-manager
-    yubikey-manager-qt
+    # TODO(aspen): lagging behind yubikey-manager and doesn't support cryptography >= 39
+    # yubikey-manager-qt
 
     # Spotify...etc
     spotify
@@ -76,18 +69,10 @@ in
 
   services.gpg-agent = {
     enable = true;
+    pinentryPackage = pkgs.pinentry-qt;
   };
 
-  gtk = {
-    enable = true;
-    gtk3.bookmarks = [
-      "file:///home/grfn/code"
-    ];
-  };
-
-  programs.zsh.initExtra = ''
-    [[ ! $IN_NIX_SHELL ]] && alsi -l
-  '';
-
   services.lorri.enable = true;
+
+  services.dropbox = { enable = true; };
 }
diff --git a/users/grfn/system/install b/users/aspen/system/install
index a9a45953da..a9a45953da 100755
--- a/users/grfn/system/install
+++ b/users/aspen/system/install
diff --git a/users/grfn/system/system/.skip-subtree b/users/aspen/system/system/.skip-subtree
index e69de29bb2..e69de29bb2 100644
--- a/users/grfn/system/system/.skip-subtree
+++ b/users/aspen/system/system/.skip-subtree
diff --git a/users/grfn/system/system/configuration.nix b/users/aspen/system/system/configuration.nix
index eae567015b..eae567015b 100644
--- a/users/grfn/system/system/configuration.nix
+++ b/users/aspen/system/system/configuration.nix
diff --git a/users/grfn/system/system/default.nix b/users/aspen/system/system/default.nix
index 445d4ad243..07bc886c6c 100644
--- a/users/grfn/system/system/default.nix
+++ b/users/aspen/system/system/default.nix
@@ -16,21 +16,31 @@ rec {
     # Use the same nixpkgs as everything else
     home-manager.useGlobalPkgs = true;
 
-    home-manager.users.grfn = { config, lib, ... }: {
+    home-manager.users.aspen = { config, lib, ... }: {
       imports = [ ../home/machines/roswell.nix ];
       lib.depot = depot;
     };
   })).system;
 
+  ogopogo = import ./machines/ogopogo.nix;
+
+  ogopogoSystem = (depot.ops.nixos.nixosFor ogopogo).system;
+
   yeren = import ./machines/yeren.nix;
 
   yerenSystem = (depot.ops.nixos.nixosFor yeren).system;
 
+  lusca = import ./machines/lusca.nix;
+
+  luscaSystem = (depot.ops.nixos.nixosFor lusca).system;
+
   iso = import ./iso.nix args;
 
-  meta.targets = [
+  meta.ci.targets = [
     "mugwumpSystem"
     "roswellSystem"
+    "luscaSystem"
+    "ogopogoSystem"
     "yerenSystem"
 
     "iso"
diff --git a/users/aspen/system/system/iso.nix b/users/aspen/system/system/iso.nix
new file mode 100644
index 0000000000..ef5d3ed78b
--- /dev/null
+++ b/users/aspen/system/system/iso.nix
@@ -0,0 +1,22 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  configuration = { ... }: {
+    imports = [
+      (pkgs.path + "/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix")
+      (pkgs.path + "/nixos/modules/installer/cd-dvd/channel.nix")
+    ];
+
+    networking.networkmanager.enable = true;
+    networking.useDHCP = false;
+    networking.firewall.enable = false;
+    networking.wireless.enable = lib.mkForce false;
+
+    # TODO(aspen): enabling this (in the minimal profile) fails the iso build,
+    # since gtk+3 needs to be built which fails due to cairo without xlibs
+    environment.noXlibs = false;
+  };
+in
+(depot.third_party.nixos {
+  inherit configuration;
+}).config.system.build.isoImage
diff --git a/users/grfn/system/system/machines/bumblebee.nix b/users/aspen/system/system/machines/bumblebee.nix
index 0fec214092..8bb52f75f0 100644
--- a/users/grfn/system/system/machines/bumblebee.nix
+++ b/users/aspen/system/system/machines/bumblebee.nix
@@ -19,5 +19,5 @@
     thresholdPercentage = 5;
   };
 
-  services.xserver.xkbOptions = "caps:swapescape";
+  services.xserver.xkb.options = "caps:swapescape";
 }
diff --git a/users/aspen/system/system/machines/lusca.nix b/users/aspen/system/system/machines/lusca.nix
new file mode 100644
index 0000000000..782d504aa9
--- /dev/null
+++ b/users/aspen/system/system/machines/lusca.nix
@@ -0,0 +1,142 @@
+{ depot, modulesPath, config, lib, pkgs, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+    ../modules/common.nix
+    ../modules/laptop.nix
+    ../modules/xserver.nix
+    ../modules/fonts.nix
+    ../modules/sound.nix
+    ../modules/tvl.nix
+    ../modules/development.nix
+  ];
+
+  networking.hostName = "lusca";
+
+  system.stateVersion = "24.05";
+
+  time.timeZone = "America/New_York";
+
+  services.avahi = {
+    enable = true;
+    nssmdns4 = true;
+  };
+
+  boot = {
+    initrd = {
+      availableKernelModules =
+        [ "nvme" "xhci_pci" "thunderbolt" "usb_storage" "sd_mod" ];
+      kernelModules = [ ];
+
+      luks.devices."cryptroot".device =
+        "/dev/disk/by-uuid/9e525746-5bca-4451-8710-a6f0e09b751c";
+    };
+
+    kernelModules = [ "kvm-amd" ];
+
+    kernelParams = [
+      "resume=LABEL=SWAP"
+      "resume_offset=795904" # sudo btrfs inspect-internal map-swapfile -r /swap/swapfile
+    ];
+
+    resumeDevice = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+
+    kernel.sysctl = { "kernel.perf_event_paranoid" = -1; };
+  };
+
+  hardware.cpu.amd.updateMicrocode =
+    lib.mkDefault config.hardware.enableRedistributableFirmware;
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+      fsType = "btrfs";
+      options = [ "subvol=root" ];
+    };
+
+    "/home" = {
+      device = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+      fsType = "btrfs";
+      options = [ "subvol=home" ];
+    };
+
+    "/nix" = {
+      device = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+      fsType = "btrfs";
+      options = [ "subvol=nix" ];
+    };
+
+    "/swap" = {
+      device = "/dev/disk/by-uuid/4c099cee-8d42-49c1-916c-62a0b5effbd2";
+      fsType = "btrfs";
+      options = [ "subvol=swap" ];
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/0E7D-3C3F";
+      fsType = "vfat";
+    };
+  };
+
+  swapDevices = [{ device = "/swap/swapfile"; }];
+
+  systemd.sleep.extraConfig = ''
+    HibernateDelaySec=30m
+    SuspendState=mem
+  '';
+
+  services.earlyoom = {
+    enable = true;
+    freeMemThreshold = 5;
+  };
+
+  services.tailscale.enable = true;
+
+  services.fwupd = {
+    enable = true;
+    extraRemotes = [ "lvfs-testing" ];
+  };
+
+  services.tlp.enable = lib.mkForce false;
+  services.power-profiles-daemon.enable = true;
+
+  services.thermald.enable = true;
+
+  services.fprintd.enable = true;
+  security.pam.services = {
+    login.fprintAuth = true;
+    sudo.fprintAuth = true;
+    i3lock.fprintAuth = true;
+    i3lock-color.fprintAuth = true;
+    lightdm.fprintAuth = true;
+    lightdm-greeter.fprintAuth = true;
+  };
+
+  security.polkit.extraConfig = ''
+    polkit.addRule(function(action, subject) {
+      if (action.id.indexOf("net.reactivated.fprint.") == 0 || action.id.indexOf("net.reactivated.Fprint.") == 0) {
+          polkit.log("action=" + action);
+          polkit.log("subject=" + subject);
+          return polkit.Result.YES;
+      }
+    });
+  '';
+
+  services.udev.extraRules = ''
+    # Ethernet expansion card support
+    ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0bda", ATTR{idProduct}=="8156", ATTR{power/autosuspend}="20"
+  '';
+
+  hardware.sensor.iio.enable = true;
+
+  hardware.opengl.driSupport32Bit = true;
+
+  # TPM
+  security.tpm2 = {
+    enable = true;
+    pkcs11.enable = true;
+    tctiEnvironment.enable = true;
+  };
+  users.users.aspen.extraGroups = [ "tss" ];
+}
diff --git a/users/aspen/system/system/machines/mugwump.nix b/users/aspen/system/system/machines/mugwump.nix
new file mode 100644
index 0000000000..4cfa117134
--- /dev/null
+++ b/users/aspen/system/system/machines/mugwump.nix
@@ -0,0 +1,306 @@
+{ config, lib, pkgs, modulesPath, depot, ... }:
+
+with lib;
+
+{
+  imports = [
+    ../modules/common.nix
+    (modulesPath + "/installer/scan/not-detected.nix")
+    (depot.path.origSrc + "/ops/modules/prometheus-fail2ban-exporter.nix")
+    (depot.path.origSrc + "/users/aspen/xanthous/server/module.nix")
+    (depot.third_party.agenix.src + "/modules/age.nix")
+    depot.third_party.ddclient.module
+  ];
+
+  networking.hostName = "mugwump";
+
+  system.stateVersion = "22.05";
+
+  boot = {
+    loader.systemd-boot.enable = true;
+
+    kernelModules = [ "kvm-intel" ];
+    extraModulePackages = [ ];
+
+    initrd = {
+      availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
+      kernelModules = [
+        "uas"
+        "usbcore"
+        "usb_storage"
+        "vfat"
+        "nls_cp437"
+        "nls_iso8859_1"
+      ];
+
+      postDeviceCommands = pkgs.lib.mkBefore ''
+        mkdir -m 0755 -p /key
+        sleep 2
+        mount -n -t vfat -o ro `findfs UUID=9048-A9D5` /key
+      '';
+
+      luks.devices."cryptroot" = {
+        device = "/dev/disk/by-uuid/803a9028-339c-4617-a213-4fe138161f6d";
+        keyFile = "/key/keyfile";
+        preLVM = false;
+      };
+    };
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/mapper/cryptroot";
+      fsType = "btrfs";
+    };
+    "/boot" = {
+      device = "/dev/disk/by-uuid/7D74-0E4B";
+      fsType = "vfat";
+    };
+  };
+
+  networking.interfaces = {
+    enp0s25.useDHCP = false;
+    wlp2s0.useDHCP = false;
+  };
+
+  networking.firewall.enable = true;
+  networking.firewall.allowedTCPPorts = [ 22 80 443 ];
+
+  security.sudo.extraRules = [{
+    groups = [ "wheel" ];
+    commands = [{ command = "ALL"; options = [ "NOPASSWD" ]; }];
+  }];
+
+  nix.gc.dates = "monthly";
+
+  users.users.aspen.openssh.authorizedKeys.keys = [
+    depot.users.aspen.keys.whitby
+  ];
+
+  age.secrets =
+    let
+      secret = name: depot.users.aspen.secrets."${name}.age";
+    in
+    {
+      cloudflare.file = secret "cloudflare";
+      ddclient-password.file = secret "ddclient-password";
+
+      buildkite-ssh-key = {
+        file = secret "buildkite-ssh-key";
+        group = "keys";
+        mode = "0440";
+      };
+
+      buildkite-token = {
+        file = secret "buildkite-token";
+        group = "keys";
+        mode = "0440";
+      };
+
+      windtunnel-bot-github-token = {
+        file = secret "windtunnel-bot-github-token";
+        group = "keys";
+        mode = "0440";
+      };
+    };
+
+  services.fail2ban = {
+    enable = true;
+    ignoreIP = [
+      "172.16.0.0/16"
+    ];
+  };
+
+  services.openssh = {
+    allowSFTP = false;
+    settings = {
+      PasswordAuthentication = false;
+      PermitRootLogin = "no";
+    };
+  };
+
+  services.grafana = {
+    enable = true;
+    dataDir = "/var/lib/grafana";
+
+    settings = {
+      server = {
+        http_port = 3000;
+        root_url = "https://metrics.gws.fyi";
+        domain = "metrics.gws.fyi";
+      };
+      analytics.reporting_enabled = false;
+    };
+
+    provision = {
+      enable = true;
+      datasources.settings.datasources = [{
+        name = "Prometheus";
+        type = "prometheus";
+        url = "http://localhost:9090";
+      }];
+    };
+  };
+
+  security.acme.defaults.email = "root@gws.fyi";
+  security.acme.acceptTerms = true;
+
+  services.nginx = {
+    enable = true;
+    statusPage = true;
+    recommendedGzipSettings = true;
+    recommendedOptimisation = true;
+    recommendedTlsSettings = true;
+    recommendedProxySettings = true;
+
+    virtualHosts = {
+      "metrics.gws.fyi" = {
+        enableACME = true;
+        forceSSL = true;
+        locations."/" = {
+          proxyPass = "http://localhost:${toString config.services.grafana.settings.server.http_port}";
+        };
+      };
+    };
+  };
+
+  services.deprecated-ddclient = {
+    package = depot.third_party.ddclient;
+    enable = true;
+    domains = [ "home.gws.fyi" ];
+    interval = "1d";
+    zone = "gws.fyi";
+    protocol = "cloudflare";
+    username = "root@gws.fyi";
+    passwordFile = config.age.secretsDir + "/ddclient-password";
+    quiet = true;
+  };
+
+  security.acme.certs."metrics.gws.fyi" = {
+    dnsProvider = "cloudflare";
+    credentialsFile = config.age.secretsDir + "/cloudflare";
+    webroot = mkForce null;
+  };
+
+  services.prometheus = {
+    enable = true;
+    exporters = {
+      node = {
+        enable = true;
+        openFirewall = false;
+
+        enabledCollectors = [
+          "processes"
+          "systemd"
+          "tcpstat"
+          "wifi"
+        ];
+      };
+
+      nginx = {
+        enable = true;
+        openFirewall = true;
+        sslVerify = false;
+        constLabels = [ "host=mugwump" ];
+      };
+
+      blackbox = {
+        enable = true;
+        openFirewall = true;
+        configFile = pkgs.writeText "blackbox-exporter.yaml" (builtins.toJSON {
+          modules = {
+            https_2xx = {
+              prober = "http";
+              http = {
+                method = "GET";
+                fail_if_ssl = false;
+                fail_if_not_ssl = true;
+                preferred_ip_protocol = "ip4";
+              };
+            };
+          };
+        });
+      };
+    };
+
+    scrapeConfigs = [
+      {
+        job_name = "node";
+        scrape_interval = "5s";
+        static_configs = [{
+          targets = [ "localhost:${toString config.services.prometheus.exporters.node.port}" ];
+        }];
+      }
+      {
+        job_name = "nginx";
+        scrape_interval = "5s";
+        static_configs = [{
+          targets = [ "localhost:${toString config.services.prometheus.exporters.nginx.port}" ];
+        }];
+      }
+      {
+        job_name = "xanthous_server";
+        scrape_interval = "1s";
+        static_configs = [{
+          targets = [ "localhost:${toString config.services.xanthous-server.metricsPort}" ];
+        }];
+      }
+      {
+        job_name = "blackbox";
+        metrics_path = "/probe";
+        params.module = [ "https_2xx" ];
+        scrape_interval = "5s";
+        static_configs = [{
+          targets = [
+            "https://gws.fyi"
+            "https://windtunnel.ci"
+            "https://app.windtunnel.ci"
+            "https://metrics.gws.fyi"
+          ];
+        }];
+        relabel_configs = [{
+          source_labels = [ "__address__" ];
+          target_label = "__param_target";
+        }
+          {
+            source_labels = [ "__param_target" ];
+            target_label = "instance";
+          }
+          {
+            target_label = "__address__";
+            replacement = "localhost:${toString config.services.prometheus.exporters.blackbox.port}";
+          }];
+      }
+    ];
+  };
+
+  services.xanthous-server.enable = true;
+
+  virtualisation.docker = {
+    enable = true;
+    storageDriver = "btrfs";
+  };
+
+  services.buildkite-agents = listToAttrs (map
+    (n: rec {
+      name = "mugwump-${toString n}";
+      value = {
+        inherit name;
+        enable = true;
+        tokenPath = config.age.secretsDir + "/buildkite-token";
+        privateSshKeyPath = config.age.secretsDir + "/buildkite-ssh-key";
+        runtimePackages = with pkgs; [
+          docker
+          nix
+          gnutar
+          gzip
+        ];
+      };
+    })
+    (range 1 1));
+
+  users.users."buildkite-agent-mugwump-1" = {
+    isSystemUser = true;
+    extraGroups = [ "docker" "keys" ];
+  };
+}
diff --git a/users/aspen/system/system/machines/ogopogo.nix b/users/aspen/system/system/machines/ogopogo.nix
new file mode 100644
index 0000000000..e80a0906db
--- /dev/null
+++ b/users/aspen/system/system/machines/ogopogo.nix
@@ -0,0 +1,107 @@
+{ depot, modulesPath, config, lib, pkgs, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+    (depot.third_party.agenix.src + "/modules/age.nix")
+    ../modules/common.nix
+    ../modules/xserver.nix
+    ../modules/fonts.nix
+    ../modules/sound.nix
+    ../modules/tvl.nix
+    ../modules/development.nix
+    ../modules/wireshark.nix
+  ];
+
+  networking.hostName = "ogopogo";
+
+  system.stateVersion = "22.11";
+
+  boot = {
+    initrd = {
+      availableKernelModules = [ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
+      kernelModules = [ ];
+    };
+
+    kernelModules = [ "kvm-amd" ];
+    blacklistedKernelModules = [ ];
+    extraModulePackages = [ ];
+
+    kernel.sysctl = {
+      "kernel.perf_event_paranoid" = -1;
+    };
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/disk/by-uuid/d67506cf-7039-484d-97c0-00321a7858dc";
+      fsType = "ext4";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/AE73-03A3";
+      fsType = "vfat";
+    };
+
+    "/data" = {
+      device = "/dev/disk/by-uuid/03e0f4dc-9778-42e2-a59e-45522610e509";
+      fsType = "ext4";
+    };
+  };
+
+  swapDevices = [{
+    device = "/dev/disk/by-uuid/8bdae7c8-5160-491f-8cd0-4f0a79acadf9";
+  }];
+
+  services.earlyoom = {
+    enable = true;
+    freeMemThreshold = 5;
+  };
+
+  hardware.enableAllFirmware = true;
+
+  hardware.pulseaudio.extraConfig = ''
+    load-module module-remap-source source_name=KompleteAudio6_1 source_properties=device.description=KompleteAudio6Input1 master=alsa_input.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.multichannel-input remix=no channels=1 master_channel_map=front-left channel_map=mono
+    load-module module-remap-source source_name=KompleteAudio6_2 source_properties=device.description=KompleteAudio6Input2 master=alsa_input.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.multichannel-input remix=no channels=1 master_channel_map=front-right channel_map=mono
+    load-module module-remap-sink sink_name=KompleteAudio6_12 sink_properties=device.description=KompleteAudio6_12 remix=no master=alsa_output.usb-Native_Instruments_Komplete_Audio_6_458E0FFD-00.analog-surround-21 channels=2 master_channel_map=front-left,front-right channel_map=front-left,front-right
+  '';
+
+  services.fwupd.enable = true;
+
+  services.tailscale.enable = true;
+
+  hardware.keyboard.zsa.enable = true;
+
+  # Nvidia
+  services.xserver = {
+    videoDrivers = [ "nvidia" ];
+    dpi = 100;
+  };
+  hardware.opengl.enable = true;
+  services.picom = {
+    enable = true;
+    vSync = true;
+  };
+  hardware.opengl.driSupport32Bit = true;
+
+  services.postgresql = {
+    enable = true;
+    enableTCPIP = true;
+    authentication = "host all all 0.0.0.0/0 md5";
+    dataDir = "/data/postgresql";
+    package = pkgs.postgresql_15;
+    port = 5431;
+    settings = {
+      wal_level = "logical";
+    };
+  };
+
+  nix.settings.substituters = [ "ssh://grfn@172.16.0.5" ];
+  nix.settings.trusted-substituters = [ "ssh://grfn@172.16.0.5" ];
+  programs.ssh.knownHosts.mugwump = {
+    extraHostNames = [ "172.16.0.5" ];
+    publicKeyFile = pkgs.writeText "mugwump.pub" ''
+      ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFE2fxPgWO+zeQoLBTgsgxP7Vg7QNHlrQ+Rb3fHFTomB
+    '';
+  };
+}
diff --git a/users/aspen/system/system/machines/roswell.nix b/users/aspen/system/system/machines/roswell.nix
new file mode 100644
index 0000000000..da62eec93e
--- /dev/null
+++ b/users/aspen/system/system/machines/roswell.nix
@@ -0,0 +1,27 @@
+{ depot, config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports = [
+    ../modules/common.nix
+    ../modules/development.nix
+    "${modulesPath}/installer/scan/not-detected.nix"
+    "${modulesPath}/virtualisation/amazon-image.nix"
+  ];
+
+  system.stateVersion = "22.05";
+
+  networking.hostName = "roswell";
+
+  boot.loader.systemd-boot.enable = lib.mkForce false;
+  boot.loader.efi.canTouchEfiVariables = lib.mkForce false;
+
+  services.openssh.settings.PasswordAuthentication = false;
+
+  services.tailscale.enable = true;
+
+  security.sudo.wheelNeedsPassword = false;
+
+  environment.systemPackages = with pkgs; [
+    cloud-utils
+  ];
+}
diff --git a/users/grfn/system/system/machines/yeren.nix b/users/aspen/system/system/machines/yeren.nix
index 2f52024f09..653f0cd44c 100644
--- a/users/grfn/system/system/machines/yeren.nix
+++ b/users/aspen/system/system/machines/yeren.nix
@@ -10,13 +10,19 @@
     ../modules/sound.nix
     ../modules/tvl.nix
     ../modules/development.nix
-    ../modules/work/kolide.nix
   ];
 
   networking.hostName = "yeren";
 
   system.stateVersion = "21.03";
 
+  time.timeZone = "America/New_York";
+
+  services.avahi = {
+    enable = true;
+    nssmdns4 = true;
+  };
+
   boot = {
     initrd = {
       availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
@@ -27,8 +33,6 @@
       };
     };
 
-    kernelPackages = pkgs.linuxPackages_latest;
-
     kernelModules = [ "kvm-intel" ];
     blacklistedKernelModules = [ "psmouse" ];
     extraModulePackages = [
@@ -65,6 +69,11 @@
     device = "/dev/disk/by-uuid/b627cb0e-0451-4f25-94d0-6497e01f0da4";
   }];
 
+  services.earlyoom = {
+    enable = true;
+    freeMemThreshold = 5;
+  };
+
   services.xserver = {
     exportConfiguration = true;
     extraConfig = ''
@@ -100,15 +109,6 @@
   systemd.services.fprintd.environment.FP_TOD_DRIVERS_DIR =
     "${pkgs.libfprint-2-tod1-goodix}/usr/lib/libfprint-2/tod-1";
 
-  security.pam.loginLimits = [
-    {
-      domain = "grfn";
-      type = "soft";
-      item = "nofile";
-      value = "65535";
-    }
-  ];
-
   security.pam.services = {
     login.fprintAuth = true;
     sudo.fprintAuth = true;
diff --git a/users/aspen/system/system/modules/common.nix b/users/aspen/system/system/modules/common.nix
new file mode 100644
index 0000000000..3eaeb2efc6
--- /dev/null
+++ b/users/aspen/system/system/modules/common.nix
@@ -0,0 +1,97 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  depot = import ../../../../.. { };
+
+in
+
+with lib;
+
+{
+  boot = {
+    loader.systemd-boot.enable = true;
+    loader.efi.canTouchEfiVariables = true;
+    tmp.cleanOnBoot = true;
+  };
+
+  networking.useDHCP = false;
+  networking.networkmanager.enable = true;
+  systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
+  systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false;
+
+  i18n = {
+    defaultLocale = "en_US.UTF-8";
+  };
+
+  time.timeZone = lib.mkDefault "America/New_York";
+
+  environment.systemPackages = with pkgs; [
+    wget
+    vim
+    zsh
+    git
+    w3m
+    libnotify
+    file
+    lm_sensors
+    dnsutils
+    htop
+    man-pages
+    man-pages-posix
+  ];
+
+  documentation.dev.enable = true;
+  documentation.man.generateCaches = true;
+
+  services.openssh = {
+    enable = true;
+    settings = { X11Forwarding = true; };
+  };
+
+  users.users.aspen.openssh.authorizedKeys.keys =
+    [ depot.users.aspen.keys.main ];
+
+  programs.ssh.startAgent = true;
+
+  networking.firewall.enable = mkDefault false;
+
+  users.mutableUsers = true;
+  programs.zsh.enable = true;
+  environment.pathsToLink = [ "/share/zsh" ];
+  users.users.aspen = {
+    isNormalUser = true;
+    initialPassword = "password";
+    extraGroups = [
+      "wheel"
+      "networkmanager"
+      "audio"
+    ];
+    shell = pkgs.zsh;
+  };
+
+  nix = {
+    settings.trusted-users = [ "aspen" ];
+    distributedBuilds = true;
+
+    gc = {
+      automatic = true;
+      dates = mkDefault "weekly";
+      options = "--delete-older-than 30d";
+    };
+  };
+
+  services.udev.packages = with pkgs; [
+    yubikey-personalization
+  ];
+
+  services.pcscd.enable = true;
+
+  services.udev.extraRules = ''
+    # UDEV rules for Teensy USB devices
+    ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
+    ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
+    SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", MODE:="0666"
+    KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", MODE:="0666"
+  '';
+}
diff --git a/users/grfn/system/system/modules/desktop.nix b/users/aspen/system/system/modules/desktop.nix
index 3adbd9d9b0..9a5fc825e1 100644
--- a/users/grfn/system/system/modules/desktop.nix
+++ b/users/aspen/system/system/modules/desktop.nix
@@ -10,7 +10,7 @@
 
   programs.nm-applet.enable = true;
 
-  users.users.grfn.extraGroups = [
+  users.users.aspen.extraGroups = [
     "audio"
     "video"
   ];
diff --git a/users/aspen/system/system/modules/development.nix b/users/aspen/system/system/modules/development.nix
new file mode 100644
index 0000000000..bd5e326b2e
--- /dev/null
+++ b/users/aspen/system/system/modules/development.nix
@@ -0,0 +1,15 @@
+{ config, lib, pkgs, ... }:
+
+{
+  virtualisation.docker.enable = true;
+  users.users.aspen.extraGroups = [ "docker" ];
+
+  security.pam.loginLimits = [
+    {
+      domain = "aspen";
+      type = "soft";
+      item = "nofile";
+      value = "65535";
+    }
+  ];
+}
diff --git a/users/grfn/system/system/modules/fcitx.nix b/users/aspen/system/system/modules/fcitx.nix
index 812f598f9f..812f598f9f 100644
--- a/users/grfn/system/system/modules/fcitx.nix
+++ b/users/aspen/system/system/modules/fcitx.nix
diff --git a/users/grfn/system/system/modules/fonts.nix b/users/aspen/system/system/modules/fonts.nix
index babe30d427..598336790a 100644
--- a/users/grfn/system/system/modules/fonts.nix
+++ b/users/aspen/system/system/modules/fonts.nix
@@ -1,12 +1,13 @@
 { config, lib, pkgs, ... }:
 {
   fonts = {
-    fonts = with pkgs; [
+    packages = with pkgs; [
       nerdfonts
       noto-fonts-emoji
       twitter-color-emoji
+      weather-icons
     ];
 
-    fontconfig.defaultFonts.emoji = ["Twitter Color Emoji"];
+    fontconfig.defaultFonts.emoji = [ "Twitter Color Emoji" ];
   };
 }
diff --git a/users/aspen/system/system/modules/laptop.nix b/users/aspen/system/system/modules/laptop.nix
new file mode 100644
index 0000000000..89c880973d
--- /dev/null
+++ b/users/aspen/system/system/modules/laptop.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+{
+  services.logind = {
+    powerKey = "hibernate";
+    powerKeyLongPress = "poweroff";
+    lidSwitch = "suspend-then-hibernate";
+    lidSwitchExternalPower = "ignore";
+  };
+
+  systemd.sleep.extraConfig = ''
+    HibernateDelaySec=30m
+    SuspendState=mem
+  '';
+
+  services.tlp.enable = true;
+
+  services.upower = {
+    enable = true;
+    criticalPowerAction = "Hibernate";
+    percentageAction = 3;
+  };
+}
diff --git a/users/grfn/system/system/modules/reusable/README.org b/users/aspen/system/system/modules/reusable/README.org
index 34d9bfdcb7..34d9bfdcb7 100644
--- a/users/grfn/system/system/modules/reusable/README.org
+++ b/users/aspen/system/system/modules/reusable/README.org
diff --git a/users/grfn/system/system/modules/rtlsdr.nix b/users/aspen/system/system/modules/rtlsdr.nix
index ce58ebb0dc..ce58ebb0dc 100644
--- a/users/grfn/system/system/modules/rtlsdr.nix
+++ b/users/aspen/system/system/modules/rtlsdr.nix
diff --git a/users/grfn/system/system/modules/sound.nix b/users/aspen/system/system/modules/sound.nix
index 07a67a1ec4..07a67a1ec4 100644
--- a/users/grfn/system/system/modules/sound.nix
+++ b/users/aspen/system/system/modules/sound.nix
diff --git a/users/aspen/system/system/modules/tvl.nix b/users/aspen/system/system/modules/tvl.nix
new file mode 100644
index 0000000000..f91315fc79
--- /dev/null
+++ b/users/aspen/system/system/modules/tvl.nix
@@ -0,0 +1,35 @@
+{ config, lib, pkgs, ... }:
+
+{
+  nix = {
+    buildMachines = [{
+      hostName = "whitby.tvl.fyi";
+      sshUser = "aspen";
+      sshKey = "/root/.ssh/id_rsa";
+      system = "x86_64-linux";
+      maxJobs = 64;
+      supportedFeatures = [ "big-parallel" "kvm" "nixos-test" "benchmark" ];
+    }];
+
+    extraOptions = ''
+      builders-use-substitutes = true
+    '';
+
+    settings = {
+      substituters = [
+        "https://cache.nixos.org"
+      ];
+      trusted-substituters = [
+        "https://cache.nixos.org"
+        "ssh://nix-ssh@whitby.tvl.fyi"
+      ];
+    };
+  };
+
+  programs.ssh.knownHosts.whitby = {
+    extraHostNames = [ "whitby" "whitby.tvl.fyi" "49.12.129.211" ];
+    publicKeyFile = pkgs.writeText "whitby.pub" ''
+      ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILNh/w4BSKov0jdz3gKBc98tpoLta5bb87fQXWBhAl2I
+    '';
+  };
+}
diff --git a/users/aspen/system/system/modules/wireshark.nix b/users/aspen/system/system/modules/wireshark.nix
new file mode 100644
index 0000000000..b233d40041
--- /dev/null
+++ b/users/aspen/system/system/modules/wireshark.nix
@@ -0,0 +1,9 @@
+{ config, lib, pkgs, ... }:
+
+{
+  programs.wireshark = {
+    enable = true;
+    package = pkgs.wireshark;
+  };
+  users.users.aspen.extraGroups = [ "wireshark" ];
+}
diff --git a/users/grfn/system/system/modules/xserver.nix b/users/aspen/system/system/modules/xserver.nix
index 35ee44112e..f78edb207e 100644
--- a/users/grfn/system/system/modules/xserver.nix
+++ b/users/aspen/system/system/modules/xserver.nix
@@ -3,7 +3,7 @@
   # Enable the X11 windowing system.
   services.xserver = {
     enable = true;
-    layout = "us";
+    xkb.layout = "us";
 
     libinput.enable = true;
 
diff --git a/users/aspen/terraform/globals.nix b/users/aspen/terraform/globals.nix
new file mode 100644
index 0000000000..c6bc24c22b
--- /dev/null
+++ b/users/aspen/terraform/globals.nix
@@ -0,0 +1,27 @@
+{ pkgs, ... }:
+
+{
+  provider.aws = map
+    (region: {
+      inherit region;
+      alias = region;
+      profile = "personal";
+    }) [
+    "us-east-1"
+    "us-east-2"
+    "us-west-2"
+  ];
+
+  data.external.cloudflare_api_key = {
+    program = [
+      (pkgs.writeShellScript "cloudflare_api_key" ''
+        jq -n --arg api_key "$(pass cloudflare-api-key)" '{"api_key":$api_key}'
+      '')
+    ];
+  };
+
+  provider.cloudflare = {
+    email = "root@gws.fyi";
+    api_key = "\${data.external.cloudflare_api_key.result.api_key}";
+  };
+}
diff --git a/users/aspen/terraform/nixosMachine.nix b/users/aspen/terraform/nixosMachine.nix
new file mode 100644
index 0000000000..23cd838804
--- /dev/null
+++ b/users/aspen/terraform/nixosMachine.nix
@@ -0,0 +1,208 @@
+{ depot, pkgs, lib, ... }:
+
+# mostly stolen from espes
+
+{ name
+, instanceType
+, configuration
+, prefix ? "${name}_"
+, region ? "us-east-2"
+, rootVolumeSizeGb ? 50
+, securityGroupId ? null
+, extraIngressPorts ? [ ]
+}:
+
+let
+  os = depot.ops.nixos.nixosFor ({ modulesPath, ... }: {
+    imports = [
+      (pkgs.path + "/nixos/modules/virtualisation/amazon-image.nix")
+      configuration
+    ];
+
+    ec2.hvm = true;
+    networking.hostName = name;
+    # TODO: remove this once the terraform tls provider supports ed25519 keys
+    # https://github.com/hashicorp/terraform-provider-tls/issues/26
+    services.openssh.extraConfig = ''
+      PubkeyAcceptedKeyTypes=+ssh-rsa
+      PubkeyAcceptedAlgorithms=+ssh-rsa
+    '';
+  });
+
+  targetUser = "root";
+
+  ec2Amis = import "${pkgs.path}/nixos/modules/virtualisation/ec2-amis.nix";
+
+  osRoot = os.config.system.build.toplevel;
+
+  osRootPath = builtins.unsafeDiscardStringContext (toString osRoot.outPath);
+  drvPath = builtins.unsafeDiscardStringContext (toString osRoot.drvPath);
+
+  machineResource = "aws_instance.${prefix}machine";
+
+  recursiveMerge = builtins.foldl' lib.recursiveUpdate { };
+
+  securityGroupId' =
+    if isNull securityGroupId
+    then "\${aws_security_group.${prefix}group.id}"
+    else securityGroupId;
+in
+recursiveMerge [
+  (lib.optionalAttrs (isNull securityGroupId) {
+    resource.aws_security_group."${prefix}group" = {
+      provider = "aws.${region}";
+      vpc_id = null;
+
+      # terraform isn't good about knowing what other resources depend on
+      # security groups
+      lifecycle.create_before_destroy = true;
+    };
+
+    resource.aws_security_group_rule.all_egress = {
+      provider = "aws.${region}";
+      security_group_id = securityGroupId';
+      type = "egress";
+      protocol = "-1";
+      from_port = 0;
+      to_port = 0;
+      cidr_blocks = [ "0.0.0.0/0" ];
+      ipv6_cidr_blocks = [ "::/0" ];
+
+      description = null;
+      prefix_list_ids = null;
+      self = null;
+    };
+  })
+  rec {
+    data.external.my_ip = {
+      program = [
+        (pkgs.writeShellScript "my_ip" ''
+          ${pkgs.jq}/bin/jq \
+            -n \
+            --arg ip "$(curl ifconfig.me)" \
+            '{"ip":$ip}'
+        '')
+      ];
+    };
+
+    resource.aws_security_group_rule.provision_ssh_access = {
+      provider = "aws.${region}";
+      security_group_id = securityGroupId';
+      type = "ingress";
+      protocol = "TCP";
+      from_port = 22;
+      to_port = 22;
+      cidr_blocks = [ "\${data.external.my_ip.result.ip}/32" ];
+      ipv6_cidr_blocks = [ ];
+      description = null;
+      prefix_list_ids = null;
+      self = null;
+    };
+
+    resource.tls_private_key."${prefix}key" = {
+      algorithm = "RSA";
+    };
+
+    resource.aws_key_pair."${prefix}generated_key" = {
+      provider = "aws.${region}";
+      key_name = "generated-key-\${sha256(tls_private_key.${prefix}key.public_key_openssh)}";
+      public_key = "\${tls_private_key.${prefix}key.public_key_openssh}";
+    };
+
+    resource.aws_instance."${prefix}machine" = {
+      provider = "aws.${region}";
+      ami = ec2Amis."21.05"."${region}".hvm-ebs;
+      instance_type = instanceType;
+      vpc_security_group_ids = [ securityGroupId' ];
+      key_name = "\${aws_key_pair.${prefix}generated_key.key_name}";
+      root_block_device = {
+        volume_size = rootVolumeSizeGb;
+        tags.Name = name;
+      };
+      tags.Name = name;
+    };
+
+    resource.null_resource."${prefix}deploy_nixos" = {
+      triggers = {
+        # deploy if the machine is recreated
+        machine_id = "\${${machineResource}.id}";
+
+        # deploy on os changes
+        os_drv = drvPath;
+      };
+
+      connection = {
+        type = "ssh";
+        host = "\${${machineResource}.public_ip}";
+        user = targetUser;
+        private_key = "\${tls_private_key.${prefix}key.private_key_pem}";
+      };
+
+      # do the actual deployment
+      provisioner = [
+        # wait till ssh is up
+        { remote-exec.inline = [ "true" ]; }
+
+        # copy the nixos closure
+        {
+          local-exec.command = ''
+            export PATH="${pkgs.openssh}/bin:$PATH"
+
+            scratch="$(mktemp -d)"
+            trap 'rm -rf -- "$scratch"' EXIT
+
+            # write out ssh key
+            echo -n "''${tls_private_key.${prefix}key.private_key_pem}" > $scratch/id_rsa.pem
+            chmod 0600 $scratch/id_rsa.pem
+
+            export NIX_SSHOPTS="\
+                -o StrictHostKeyChecking=no\
+                -o UserKnownHostsFile=/dev/null\
+                -o GlobalKnownHostsFile=/dev/null\
+                -o IdentityFile=$scratch/id_rsa.pem"
+
+            nix-build ${drvPath}
+            nix-copy-closure \
+              --to ${targetUser}@''${${machineResource}.public_ip} \
+              ${osRootPath} \
+              --gzip \
+              --use-substitutes
+          '';
+        }
+
+        # activate it
+        {
+          remote-exec.inline = [
+            # semicolons mandatory
+            ''
+              set -e;
+              nix-env --profile /nix/var/nix/profiles/system --set ${osRootPath};
+              ${osRootPath}/bin/switch-to-configuration switch;
+            ''
+          ];
+        }
+      ];
+    };
+  }
+
+  {
+    resource.aws_security_group_rule = builtins.listToAttrs (map
+      (port: {
+        name = "ingress_${toString port}";
+        value = {
+          provider = "aws.${region}";
+          security_group_id = securityGroupId';
+          type = "ingress";
+          protocol = "TCP";
+          from_port = port;
+          to_port = port;
+          cidr_blocks = [ "0.0.0.0/0" ];
+          ipv6_cidr_blocks = [ ];
+          description = null;
+          prefix_list_ids = null;
+          self = null;
+        };
+      })
+      extraIngressPorts);
+  }
+]
diff --git a/users/aspen/terraform/workspace.nix b/users/aspen/terraform/workspace.nix
new file mode 100644
index 0000000000..f1563d2a84
--- /dev/null
+++ b/users/aspen/terraform/workspace.nix
@@ -0,0 +1,107 @@
+{ pkgs, depot, ... }:
+name: { plugins }: module_tf:
+
+let
+
+  inherit (pkgs) lib runCommand writeText writeScript;
+  inherit (lib) filterAttrsRecursive;
+
+  allPlugins = (p: plugins p ++ (with p; [
+    external
+    local
+    tls
+    p.null
+  ]));
+
+  tf = pkgs.terraform.withPlugins allPlugins;
+
+  cleanTerraform = filterAttrsRecursive (k: _: ! (builtins.elem k [
+    "__readTree"
+    "__readTreeChildren"
+  ]));
+
+  plugins_tf = {
+    terraform.required_providers = (builtins.listToAttrs (map
+      (p: {
+        name = lib.last (lib.splitString "/" p.provider-source-address);
+        value = {
+          source = p.provider-source-address;
+          version = p.version;
+        };
+      })
+      (allPlugins pkgs.terraform.plugins)));
+  };
+
+
+  module_tf' = module_tf // {
+    inherit (depot.users.aspen.terraform) globals;
+    plugins = plugins_tf;
+  };
+
+  module = runCommand "module" { } ''
+    mkdir $out
+    ${lib.concatStrings (lib.mapAttrsToList (k: config_tf:
+      (let
+        # TODO: filterAttrsRecursive?
+        configJson = writeText "${k}.tf.json"
+          (builtins.toJSON (cleanTerraform config_tf));
+      in ''
+        ${pkgs.jq}/bin/jq . ${configJson} > $out/${lib.escapeShellArg k}.tf.json
+      ''))
+      (cleanTerraform module_tf'))}
+  '';
+
+
+  tfcmd = writeScript "${name}-tfcmd" ''
+    set -e
+    dir="''${TF_STATE_ROOT:-$HOME/tfstate}/${name}"
+    cd "$dir"
+    rm -f *.json
+    cp ${module}/*.json .
+    exec ${tf}/bin/terraform "$(basename "$0")"
+  '';
+
+  init = writeScript "${name}-init" ''
+    set -e
+    dir="''${TF_STATE_ROOT:-$HOME/tfstate}/${name}"
+    [ -d "$dir" ] || mkdir -p "$dir"
+    cd "$dir"
+    rm -f *.json
+    cp ${module}/*.json .
+    exec ${tf}/bin/terraform init
+  '';
+
+  # TODO: import (-config)
+  tfcmds = runCommand "${name}-tfcmds" { } ''
+    mkdir -p $out/bin
+    ln -s ${init} $out/bin/init
+    ln -s ${tfcmd} $out/bin/validate
+    ln -s ${tfcmd} $out/bin/plan
+    ln -s ${tfcmd} $out/bin/apply
+    ln -s ${tfcmd} $out/bin/destroy
+  '';
+
+in
+{
+  inherit name module;
+  terraform = tf;
+  cmds = tfcmds;
+
+  # run = {
+  #   init = depot.nix.nixRunWrapper "init" tfcmds;
+  #   validate = depot.nix.nixRunWrapper "validate" tfcmds;
+  #   plan = depot.nix.nixRunWrapper "plan" tfcmds;
+  #   apply = depot.nix.nixRunWrapper "apply" tfcmds;
+  #   destroy = depot.nix.nixRunWrapper "destroy" tfcmds;
+  # };
+
+  test = runCommand "${name}-test" { } ''
+    set -e
+    export TF_STATE_ROOT=$(pwd)
+    ${tfcmds}/bin/init
+    ${tfcmds}/bin/validate
+    touch $out
+  '';
+
+  meta.targets = [ "module" "test" ];
+}
diff --git a/users/aspen/web/.envrc b/users/aspen/web/.envrc
new file mode 100644
index 0000000000..b80e28b4b8
--- /dev/null
+++ b/users/aspen/web/.envrc
@@ -0,0 +1,2 @@
+source_up
+eval "$(lorri direnv)"
diff --git a/users/grfn/gws.fyi/.gitignore b/users/aspen/web/.gitignore
index 2b72eaed29..2b72eaed29 100644
--- a/users/grfn/gws.fyi/.gitignore
+++ b/users/aspen/web/.gitignore
diff --git a/users/grfn/gws.fyi/Makefile b/users/aspen/web/Makefile
index d6c9f40c95..ee6ed2fd8b 100644
--- a/users/grfn/gws.fyi/Makefile
+++ b/users/aspen/web/Makefile
@@ -1,7 +1,13 @@
-.PHONY: deploy
+.PHONY: deploy purge_cf do_deploy renew backup open
+
+deploy: do_deploy purge_cf
+
+purge_cf:
+	@$(shell nix-build `git rev-parse --show-toplevel` -A 'users.aspen.web.purge-cf')/bin/purge-cf.sh
+
+do_deploy:
+	@$(shell nix-build `git rev-parse --show-toplevel` -A 'users.aspen.web')/bin/deploy.sh
 
-deploy:
-	@$(shell nix-build `git rev-parse --show-toplevel` -A 'users.grfn."gws.fyi"')
 
 renew:
 	@echo Renewing...
@@ -29,3 +35,6 @@ backup:
 
 open:
 	$$BROWSER "https://www.gws.fyi"
+
+preview:
+	$$BROWSER "$(shell mg build website)/index.html"
diff --git a/users/grfn/gws.fyi/config.el b/users/aspen/web/config.el
index b05d897d3d..b05d897d3d 100644
--- a/users/grfn/gws.fyi/config.el
+++ b/users/aspen/web/config.el
diff --git a/users/aspen/web/default.nix b/users/aspen/web/default.nix
new file mode 100644
index 0000000000..a16cd16c06
--- /dev/null
+++ b/users/aspen/web/default.nix
@@ -0,0 +1,62 @@
+args@{ pkgs, depot, ... }:
+with pkgs;
+let
+  site = import ./site.nix args;
+  resume = import ../resume args;
+  bucket = "s3://gws.fyi";
+  distributionID = "E2ST43JNBH8C64";
+
+  css = runCommand "main.css"
+    {
+      buildInputs = [ pkgs.minify ];
+    } ''
+    minify --type css < ${./main.css} > $out
+  '';
+
+  keys = runCommand "ssh-keys" { } ''
+    touch $out
+    echo "${depot.users.aspen.keys.main}" >> $out
+  '';
+
+  website =
+    runCommand "gws.fyi" { } ''
+      mkdir -p $out
+      cp ${css} $out/main.css
+      cp ${site.index} $out/index.html
+      cp -r ${site.recipes} $out/recipes
+      cp ${resume} $out/resume.pdf
+      cp ${keys} $out/keys
+      cp ${./pubkey.gpg} $out/pubkey.gpg
+    '';
+
+  purge-cf = writeShellApplication {
+    name = "purge-cf.sh";
+    runtimeInputs = [ httpie jq pass ];
+    text = ''
+      cfapi() {
+        http \
+          "https://api.cloudflare.com/client/v4/$1" \
+          X-Auth-Email:root@gws.fyi \
+          "X-Auth-Key: $(pass cloudflare-api-key)" \
+          "''${@:2}"
+      }
+
+      zone_id=$(
+        cfapi zones \
+          | jq -r '.result[] | select(.name == "gws.fyi") | .id'
+      )
+
+      cfapi "zones/$zone_id/purge_cache" purge_everything:=true
+    '';
+  };
+in
+(writeShellApplication {
+  name = "deploy.sh";
+  runtimeInputs = [ awscli2 ];
+  text = ''
+    aws --profile personal s3 sync ${website}/ ${bucket}
+    echo "Deployed to http://gws.fyi"
+  '';
+}).overrideAttrs {
+  passthru = { inherit website site purge-cf; };
+}
diff --git a/users/aspen/web/index.org b/users/aspen/web/index.org
new file mode 100644
index 0000000000..4be79fd797
--- /dev/null
+++ b/users/aspen/web/index.org
@@ -0,0 +1,40 @@
+#+OPTIONS: title:nil toc:nil num:nil
+#+HTML_HEAD: <title>aspen smith</title>
+#+HTML_HEAD: <link rel="stylesheet" href="./main.css">
+
+my name is aspen smith and i'm a software engineer and musician.
+
+* code
+
+- [[https://github.com/glittershark/][github]]
+- [[https://cs.tvl.fyi/depot/-/tree/users/aspen][my directory in the tvl monorepo]]
+
+* work
+
+most recently, i worked on database internals at [[https://readyset.io/][readyset]], an incrementally
+maintained, partially stateful materialized view maintenance system for sql
+that's wire-compatible with postgresql and mysql, based on [[https://github.com/mit-pdos/noria][noria]].
+
+* projects
+
+- [[https://windtunnel.ci/][windtunnel]], a continuous benchmarking software-as-a-service currently accepting early alpha users (send me an email if you want to try it out!)
+- [[https://cs.tvl.fyi/depot/-/tree/users/aspen/achilles][achilles]], a compiler for (what I plan to become) a dependently typed, low-level functional programming language targeting LLVM
+- [[https://github.com/glittershark/org-clubhouse][org-clubhouse]], an emacs package for lightweight integration between [[https://orgmode.org/][org-mode]] and [[https://clubhouse.io/][the clubhouse project management tool]]
+- [[https://cs.tvl.fyi/depot/-/tree/users/aspen/xanthous][xanthous]], a terminal roguelike in haskell that I work on intermittently and exclusively for fun
+
+* music
+
+- https://sacrosanct.bandcamp.com/, a post-rock project with a [[https://bandcamp.com/h34rken][friend of mine]]
+- [[https://soundcloud.com/missingggg][my current soundcloud]], releasing instrumental music under the name *missing*
+- i play bass in [[https://goodcry.band][good cry]], a rock band based in brooklyn
+- you can also find a log of all the music I listen to [[https://www.last.fm/user/wildgriffin45][on last.fm]]
+
+* contact
+
+- [[mailto:web@gws.fyi][web@gws.fyi]]
+- [[https://twitter.com/glittershark1][twitter]]
+- [[https://bsky.app/profile/gws.fyi][bluesky]]
+- https://keybase.io/glittershark
+- aspen on IRC (hackint or libera.chat)
+- [[https://gws.fyi/pubkey.gpg][gpg key: 0F11A989879E8BBBFDC1E23644EF5B5E861C09A7]]
+- signal / telegram / discord available upon request
diff --git a/users/grfn/gws.fyi/main.css b/users/aspen/web/main.css
index cdcd440766..cdcd440766 100644
--- a/users/grfn/gws.fyi/main.css
+++ b/users/aspen/web/main.css
diff --git a/users/grfn/gws.fyi/orgExportHTML.nix b/users/aspen/web/orgExportHTML.nix
index ac28580a59..aac4e32e7a 100644
--- a/users/grfn/gws.fyi/orgExportHTML.nix
+++ b/users/aspen/web/orgExportHTML.nix
@@ -5,11 +5,7 @@ with lib;
 
 let
 
-  emacsWithPackages = (pkgs.emacsPackagesGen pkgs.emacs27).emacsWithPackages;
-
-  emacs = emacsWithPackages (p: with p; [
-    org
-  ]);
+  emacs = pkgs.emacs28;
 
 in
 
@@ -25,15 +21,16 @@ let
   outName =
     if isNull headline
     then
-      let bn = builtins.baseNameOf src;
-          filename = elemAt (splitString "." bn) 0;
+      let
+        bn = builtins.baseNameOf src;
+        filename = elemAt (splitString "." bn) 0;
       in
-        if depot.nix.utils.isDirectory src
-        then filename
-        else filename + ".html"
+      if depot.nix.utils.isDirectory src
+      then filename
+      else filename + ".html"
     else "${filename}-${replaceStrings [" "] ["-"] filename}.html";
 
-  escapeDoubleQuotes = replaceStrings ["\""] ["\\\""];
+  escapeDoubleQuotes = replaceStrings [ "\"" ] [ "\\\"" ];
 
   navToHeadline = optionalString (! isNull headline) ''
     (search-forward "${escapeDoubleQuotes headline}")
diff --git a/users/aspen/web/pubkey.gpg b/users/aspen/web/pubkey.gpg
new file mode 100644
index 0000000000..2a7e744cc3
--- /dev/null
+++ b/users/aspen/web/pubkey.gpg
@@ -0,0 +1,206 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFSsgHkBCAC8hkveavGgqfK4zN+ZKWmzToa/YQHHatA68cIzlmXebuevV9bE
+vNVYz6qI9Y6QPvxoIXlnmKzcZkWzE9X3bXdtlucojXeUzVxfkTIWWL50+DMZtzTY
+IGaK4h7J6xuF27S2WZjE18Bf+mvwrGgl2B6eCd2L2Jw3PwdH7LI9+zl0B9Eo6QUJ
+1O1NZu5ry135uzIfs4u1U0s+OVnBcq+y4NR0Tejsw7WwY5/JLlljM2CJYPhFGT7h
+yx6ApI4TeoOwDOXg4UKl78znMpUiJP0yD8u5/BHiq64H4oxkDiRenOneZ9B6vupX
+o0+ri94h1Dirb/D2Bq/LxdNWqLz/xDlU5GEBABEBAAG0HEdyaWZmaW4gU21pdGgg
+PHJvb3RAZ3dzLmZ5aT6JATwEEwEIACYCGwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIe
+AQIXgAUCWVumdgIZAQAKCRBE71tehhwJp2dZB/9QimuyGMtXB3ElfdRLlQ3dT4uT
+KMW38VBMyEXOlMF1VhRolRWp4CTsEUYSJhhNg/b/NW7KgXBaX3ZJEAZqwyttxx0r
+1v9Rysb9yrxkKAsnyNs774sMaEj+Zj+gtik4jxT8EZ3hV3ohCUrEKiImPKkPTeFl
+xYk9QjXZPE4CAMBfmybawIk7cMHp0XzJppia9XAwA7zDXQnoc/rL8feVxXUbcPb1
+RjAtGI/Pg4vhZKCJ4urvtkSvcKXkiqq3taGjIUV1cvTjwTs1ceL32J0oghR6zbIt
+OLj9AfqlLjBY+9Mdyeo+Jd1FGBu8i+C13sv+yLFZHEopi5hnHXgsEvIZwcSviQE5
+BBMBCAAjBQJZW6VqAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQRO9b
+XoYcCafMiwgAggljWWQuVBf/vTK6Yg7VgOo7XEPShzu7mNt1fucCdiAk01NvZV3/
+EnkpuWJHyQ8hPU+xOHxCz22Mhv4IpIhmCSXQiOhGrQ+byPRMXEeyse1Pn7YVW9jx
+7rk5Lh95YYqBy4LLACi8Cvo8OfbwtIPO5G/JKD3rGFHWcANya3ZdNlLr8RlJQa1/
+W7nZ/i+TnfGC7AvaR/5JfwBAxA5YMI4hK+RHiRWGuIPTwYrel2bKToRboXL76fRI
+9pI8SKzuRr7dSX/pCWFLPQRdlwlF3n5iiADo73XyXcOUP2g6oadNzvsqctq6kGML
+KEHAoqhsAUMs+J8sSPcyfhJOIQPQRYExFokCMwQQAQgAHRYhBBP5Ly1Y78tmaShO
+tNywW0z4mvxmBQJZvsjBAAoJENywW0z4mvxm76sP/3DZ3i0f7B7zRCkzzbyYrTU6
+kbZK8FzvGhNenU8JNQ5370lxiebVp7gwUsYORmgcA7mkenqVjSjYNK+/hTNcaiIU
+KVn/TxrrTWtCNE3d5fNHRdR9Ac9FOw7CHJGac3/UOsxE++cpW2+7S0SH59usl+17
+tLbO9YzsTncvsWR8maGnjaTV0Jz+Ccd1GkBHv0Edpmzki+oseqI8YlRK5uNmhuMB
+G06k51hgvjwYpF6fUwRauv77XnI71fRa4m8AX+LGt/NMz9GmFfhDTlC+YnsiVbk8
+bkAAR4axCfKvbUQm5GRs/iKLFBlpwC1/FsjTdgWOWhpF1J/jhiIFZsG4DyqnkJLb
+W7WI6QJPeB05i6EjI+ub/2SGJXVlPIRH9S5DCX8k0QNk2kANplaLoE6+xkki09TE
+4PTEK9c63Hi6X39Z/pgw25/mgArp3VVTSrK1ZIiiL+o9bvTowtc3uHHtG1fV8RMK
+DOfWhI00TqpTend8DsgfpYR4vfLasmDBkXI9GmeIzX4XABVMtb5IS3ZDUNMEgPuk
+kMwrASg8ln8UqZpRMtXPg9l3INiIoXoDfLHjyS4FXBqSLIi778uuNZvYXyJNKYlz
+aYfma4I9m6bpkS9U4gFYnkuX3rAL8RiyKru5EGlE8+GL6PxnRuChj1g0mgazSNBA
+79Af1iA6JpwnUsQxXxkWiQIcBBABCAAGBQJZwDbFAAoJEMelPMWNOx+MLYoP/3CT
+16deQZCNjfVmBKWzvwEEOTQVoRs1xEl63lKtXOrWtStZPPnnIXqfXKi22p/owykB
+WGdE84RgRAGBJYPriHjbxx/BK6eaUkmyekaa0tFadDOpctK/YmTZkp3l/FOCezGl
+QK0je2koxBei63qL+tfFIP4MNTuIwxezrSwCXaXRlsblIvLNBSK8cBHz1WjZG4Ch
+bV4RxoW75teHqtbsLId97WRXojErjIpA6ypqmUEyG9YrOdOFd1QA9QXWm5RO8xhw
+8JTnUDeyrL00H+2zEnxz02vDT5qJyXLBF5FrHgQvvdq9ZJo85/LZH6EZZt8D4N+l
+sWij2wTQZ+GrzhXgOWyWeVWsYiWRegrCVc1HDJnT3c4ALjHDdHQvZjYG1alPT06h
+J+MEdGvPWRcGEuM4m4MP6p/PYq7xqmf4Kxw2JWd1e3gVffRTC04dXcxmMzVHG+vo
+mtdPjquPzawd7rMKqvECbzwBIHSVLAqDvcian+U83fv0NK1vP5wSQFK3531/1xwp
+UVx2jJHpuVw6goW8vm5RMHP3fgaDjBU9E9l7K7UszYr6zUxOGNJ6S1IPkBcz5K4e
+ncrqhG5Tr0mFP+iV6V+bQ5hfBhXIYhVBqieumGD/ZU8QUjyxj/myb4lPoIBsBxxi
+FWwHRTKn8qQFZvJY7xXrX0BsLGAgTxKupEADLR/0tCdHcmlmZmluIFNtaXRoIDx3
+aWxkZ3JpZmZpbjQ1QGdtYWlsLmNvbT6JATcEEwEIACEFAlSsgHkCGwMFCwkIBwIG
+FQgJCgsCBBYCAwECHgECF4AACgkQRO9bXoYcCaed+Qf+JSDZ3odMwlnrbb2kwsld
+uAt9VhRm+dfdIm25nAgxJUxIju8uIgE9v+8dRdGFwrV8pKBYYOCMi8MFNYuu9zS6
+6wXS4opd/DeYDj2yaN0wBYEfeXMCwVLVDHU7AHrsxQWRSxbcUOi2Mm2sig70ZSq2
+iNicX2f6eUSr/4CjocTP6jOqcHd6Di4odEy/hK6ukCCW8ia1Uujh7JYCU7quHnuE
+1N184W2Jf6hUieFC2kE+Nmhix0LsYYe6c1InembHRZ85BpOsWWuE9cS6IuVO/jbN
+ZcgS7NkuCHkG7CubPnSZX/EDwmyr9Pd57tr9BANuDNvTGgcbaXhJj5nlIx/usDsr
+RIkBOgQTAQgAJAIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAUCWCyKLgIZAQAK
+CRBE71tehhwJp/Q/B/9I+URRL9FuzA3bnljchtDVUtPVWfKzPUzKTJkUGnrmO3J6
+YM97JUoU9YZUP0JGvBuTzM4FVqNG7aEdyQBiHa4b7rBzVIUD+hMYlvowJTVKsG3j
+f+TcX4Ms9wTpDRd9+kjZPTONZom5C+a5yTqipKK78CubCQJESQktYJoMr3P3FOG/
+nTi9NCeVEah2ud2fsrfOvqTqpcL02O/jTqA4jDDznw2TmeQ/NMc+Dz5hIIHod8D5
+QXoNPvMPFcCGe3oSPzQ5RlKpF1MvzSb35c9TY5BbOVRZPSUR5elkAE1yrXPpwkNo
+bQl9/2eCxhcOcT9bcP+hmD0O4QJw5h8SALVAYaZJiQIzBBABCAAdFiEEE/kvLVjv
+y2ZpKE603LBbTPia/GYFAlm+yMUACgkQ3LBbTPia/GYgvBAAgKZffTlMvOjnmYB2
+TiG5sn9fqnU8cDmnaVLwD/XLSZ8wI3dBuLnlfEOTIAWlrAnnIXyEdH4isR3zLPuW
+bXLsIy4xW5BddjnWaRpE/2kdPTxzo+tI/JjeJFmxp6MggAMV+XdaxDXTMZGwA3Gv
+ZXqLiTvlCYT3YBM4MtEA7HkXjpQuvYJ2rMFLhTrf8IhWg7PQMJJKqaM2RawwJ7XX
+L4HzoQyHD5iJV8H/1gtSayxHeVwPVJYlUXtpPabbPhRtrJlI39RVCWejhI0h2RU3
+U9lvVA+BP3Dv3l9kDm9XYz9/WH1sYgjatsPSFgStYg4kjvrgIuHN1ALCPv3wZUmW
+0NhV26pTps667M5onEAn+0rfHYp6KF4FUX/3kPgRzuXRt+gAY2BdgFvLd+BucQxz
+oaZzl3ykK9ZwbylgtX6kmaspBE/TcxKwGG8RUJZZf8VEqaIk6nZ24F+oaK9zjLgw
+3eOOWsk6UefibiLGXpTRIIhmBw5ki6LJi2I/IpQ73hOZvyQKlZCKEBJL72MQbZeA
+25xkO8a4ZVdmeyYTJbxL9f10t8ZNBwYJQW7MAhGRKAkg9McrK4Wh73zs8ux+jp9U
+i7wM6HWRARyfbjCoJ3V7Q3FbN+H0zk8/+wvlTabpSNIyUiRp7d9Yw6fmVr/Fq2I5
+55Rlg725RwN/hJHRUvLGkoP1xSCJAhwEEAEIAAYFAlnANsUACgkQx6U8xY07H4z/
+5Q//UGyEHeDu4mm/5h57ryUR32/0NG4yBeQrn5HLaOD+bpWD+Z5ZBV3TOJHeuJMj
+xA/scBHIkfnwW+e7DQxudPBhxp6+ZMyLje6en9uZY/IVGXVKe63+XzRojwJRHhbe
+7YUyym0en1RBWkI/TIdx4KjZ55Xz78/Dmb6QnBCuYr2Vb6h0mZz3p8KoTaIFEWM9
+m9/84Xz2ppCcVBoaavMyo1P4/Fxluh4T51qjR1UeLIuwDdDyS1aYQ9+MXIl8tHz1
+x05HdDbdU7Y2tAg4WUZ5Lg42m9hjRPJcyuw6P+jpF/YrxyAbY962ibZNTep1urTG
+/oBwSnhJ8nIK5ISwkacbyeN3Tu2sCmJceiCfOz1M9CaEffnyxP36ALZsF9jl+PWM
+bGsN+ZeZMwdnPsExBrqNbPn+Ce2LNm/dxIlfrFXJXqm7BZAuOCuL9UAm5vihR/LE
+ijqYWsTI32N8nz4RclU3d1N6w5KZ/XzyVfMYHhhgrncPG/21A1YlR9CBK/FBh7nu
+hBLU2Q92VV47oEIG1BUVHct7SOR1fV4Auvg2d9mTPWZV2mfGjebywaALq6kpc5l0
+bUTDew8FXxXO+arsvM5ngdI/XHIAai9+L13jnsciTjQurpnhC9ZNwUz49pyJSQy4
+fWDrYe/RldGa3ggCioNCZM9oCJUYYXzJpfeCWcCw7SmwcGO0K0dyaWZmaW4gU21p
+dGggPGdzbWl0aEBzZWN1cml0eXNjb3JlY2FyZC5pbz6JAWIEMAEIAEwWIQQPEamJ
+h56Lu/3B4jZE71tehhwJpwUCWcAx0S4dIEkgYW0gbm8gbG9uZ2VyIGVtcGxveWVk
+IGF0IFNlY3VyaXR5U2NvcmVjYXJkAAoJEETvW16GHAmnXKMH/0JpyvK92knWLcBI
+fzatSWHSyTM7CYxIgB6kj6DhIUd5h0ahVx5CLMUNLlBZSdbCKzgifSbP0HLBVC7O
+mn3tCwPuE2GM5dgnxxawaOqK7SdwB8nq1wFZD2uPlFdiocHXRBG7OQH/nkaJP+V/
+eO0F+2vLEER3NxdOuIE1PZVXDRVBaeRGuyG5MXW78PdnV/GizzxoKVBw+ZRIHonj
+IpCozPjFzA3/ZLpQtawKZMyO0un20QEDEXJAoAsgIAyYFYjb568+VBb/8Ghe7V5q
+7yekKOW1HzxBsN6/YGvOBLedKANsG1C180klDQIt6GkBk1V7RUs59DpGRlfYC7la
+X/cewumJATcEEwEIACEFAlgrhmUCGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AA
+CgkQRO9bXoYcCadpQggAok7C6Bm6uOANdfpNuKFiLWyU64OaSEsClMH8LYb4pNKE
+D1n0KM/7K9MPyHtK1JwrksjlVF7iWUoMxToxHMJsNXQ79IbGp8rfjWd0gq/4UmrA
+DjCHl0u/oci/PeBOjSiBg4tufmIaY9QBZ3grvXDqMZRF6sp1WogSW6RyMGrcAeYu
+2b1M/pK0wY3+sMIC8wYtRB8OyCWIYEtWHvQP/oforq+3f+PwunAt9SIAXa/HfIjW
+zIaspOuvn4XWeVXdkDuA1KuAlIBaGpDvV9/NyX/Gs0dM1yDz6YSdkKUMw7H64lE9
+CdfGz+eOrZJ8DrDWJbdVz2MJS71ypY169WJEck3T04kCMwQQAQgAHRYhBBP5Ly1Y
+78tmaShOtNywW0z4mvxmBQJZvsjGAAoJENywW0z4mvxm3AwP/1qwyr5JlHndaU7K
+6osZ/hCL6h+yx/m/tG6HbBCAsV7CO5PxeVZAZoJsEbiPLoQ2FCWyYh6sjKoBlw4o
+n+pPDWZHw///35Y+H4/srCOAWkONpBb9ZQM5Kb943thQXNLrzpzV6itcqLM/Gr3d
+lhgg74srezsculLbLXHo6glxCddEr0a21gSKHu35oTcKkwepF+AmZbjS7wj4uYZC
+AvUVN0HdOhb3c4+PN7Y32qq5GuElmtiUej56GKCBD10XwNKaCABsKIaPArqO+068
+hjb5iur1mkNjdKE7Wl2aqLWwweOwrPAYIGNNh/CHHLCamcRMeapfBqJqsqPdOOuG
+Xjx0JKoVSL8glu+Yj7jWBg78rdm1Uot8nx94THsTOxi7ZT4Ui8GDTc/oQBPewGBz
+bWxGI+P4I7r/ZWJsiKjWXCmduTM5itzSM/zG7mMgoB+Kn9qgla5XM/auiupfHcJc
+ju66rDy1xBDOVuxJ49ga9FY2lrob0aLUB9ROmh8/JlgcRLWFdTbMpTRJU+6chWFn
+R6UG9MPk+H7cG/IrCRPHKtUiHSgYnhCE3E/klIIIMLTGgAbJxt1BidyL2oBCkQwk
+IZwynYwiIDaundIUAoWGf/t5Ca+U9trLG8cNH7g3g7Ojd0WJbOLVMEQ0Lh5nPHdu
+y4C8Ejt95R2tzw5h339GiqEqrsVZtDFHcmlmZmluIFNtaXRoIChLZXliYXNlKSA8
+Z2xpdHRlcnNoYXJrQGtleWJhc2UuaW8+iQE3BBMBCAAhBQJYK4eWAhsDBQsJCAcC
+BhUICQoLAgQWAgMBAh4BAheAAAoJEETvW16GHAmnFZMH/R6FknUGOR/sC+1Zifs8
+TCvXL1oBYcuEDE01c4QzkiFeOekvmjc+JzwOidSvMNXFGw3FoZTyO8C9tClaC1Bc
+kWLSqAPFLpvWF/kJKVKRyOq2RwwyrKLtcDJ1LIJevmzSxnZkGfS2uLQAo3slbhZ3
+Rpcms/9GaZc15NMBEj8sbLkSb88EWiAM9MdFbAlDm4Jp13B6/elw7M2+tK9HSbUl
+cb5m0/9n5zhBADdgZ1NNLFqcrbD01dNBVOgko7wuKDeK9g2CluxwTTLCttdV+7en
+MHX725NrfN30yZDRAv2ZfdGIl4TE0T5XGPmMb6QTwaFxHFcp59eSD5TB5ph+Rxfy
+8QSJAjMEEAEIAB0WIQQT+S8tWO/LZmkoTrTcsFtM+Jr8ZgUCWb7IxgAKCRDcsFtM
++Jr8ZjISEAC3N0/l7M6YOhybEoa9Z8Fr9LBiN5QZ1MU0AUjrBUk/IOQyP5PUagSn
+v0VBUopr9hEdNxuCCNk/DNyQpJPPpJAvTrb6A2/UwBB36i1C1GadtHKi0t4djFnv
+dIcpVLeA2+92bI1q504JTR5zLGuaIGf30YpUnyVOs/tlHYRNl6vHB0ccMl6XtTZG
+Nw/OaSO6zsZ7b2mtjmMmDgpa/A9cyQ6NJMSPGdd8M9wrA7CZNK4SSQABdKRCMQRp
+MHC18rNm14RmJ8skIWsCw3HJYyKGOj7xupHRki9/3W1Knx2q7OSNO5KnbEFfyOmN
+oyIj5NiPb/P1sd5z6qtySlzZ1fYOSQh9HtxCrxGUqW/oQHFb6/ljJA5z4XYasNrM
+DViI++DicrobY4nXYoui0ZLevg/FQ05GpF2ge7z07tk0slRj3ssgZ+TrizRl0VST
+81cvrdNQIUmi1p3I2OzHxu9bRyjWKwOVsklq091y5wZabEvA7JbmA8nNB8HV10OZ
+8mx1IjVha3GrLHiaOk/Gkomd/1p/1gX8VDyVVMezIv0pBJZpamtg0qu3aeF7rv83
+QY44LD0cBDgtohpCu1vqaxz7154seqj3hpmxRDkRAdJqfNuVgJdJhpFZeC8XOf2u
+4wD5jrIoQefLx5ExAXFwQwGDgtJzS3HWYl/q7Csz0z+CjTMsI7yQDokCHAQQAQgA
+BgUCWcA2xQAKCRDHpTzFjTsfjOHSD/9GloneXphdCKAT589zMjIxuWC6pCclk3Qu
+elePI9GgRUS2STkWw7tSmKZ5BFY8ywIE56D7ay/Q02rUYymIQn4oRdbpS4tH6fit
+nYUOBFrHDbLPWX55KpImjY7APoaBCfvF0S+rJuMT+6S4R36UF+PxCv2z5yzkz7u6
+9PvVFRQkgMQmKo3q2PrdSt9xlFtFe9Y5KAT5/UWlz2KNK4/rw2s7FEbQBv9ONzVE
+2txTe1iqUpVmNKN6TpO3hLdj0ldqiPQUsxUuhIdjFPoMXZpsO1t63T9I/jkD+d5/
+DPzVQRVgpf3Dcg5GIpy2PBmW5upSWwxh96QcplLeqN7LmmQLYHOs6mJMmqCtN6lk
+eaks9heso/i2zkIixDWdDPYOlEtnH50zheLtS1J2Ic6Xmn2FmWNS61KtgTfy6MLH
+19Tqz5Dsu+RB8uqGBA499YUJWvBSexW0yRb488EP5SYARhhk5roNOhPDroBXTvEV
+cXVmc54Ze4/cfT2/wwNyHMqbpG8itE0L0Qw+lfET7C0lcWi0tflowIqaFbDiVbEc
+7qk7FoGmTSyqfJJ0NhuDfev/wRwiE1Wak+R+sfQlqk696+vPWcIO3q/Mk9RO/oCM
+7Rf9I7y3ep3zyRAr1JjHvGRiO8IPECUE2LPL9rHonC+h0rJMyERojR2VKDxLCxlm
+/HtaKvUwuLQiR3JpZmZpbiBTbWl0aCA8Z3JpZmZpbkB1cmJpbnQuY29tPokBUgQw
+AQgAPBYhBA8RqYmHnou7/cHiNkTvW16GHAmnBQJfxmAMHh0gTm8gbG9uZ2VyIGVt
+cGxveWVkIGF0IFVyYmludAAKCRBE71tehhwJp77kB/9BmSOkTEBcG98Zk0RE6HvZ
+cJwEDKZhSv+ttQCfqwGp0NAQj5dd+zA9WIMNF0wnX0IHNA0BrZXCnlw8YZTG/hbP
+H6VBmH700xuTlfPiBE+VbUpf5LK2h0TBjAnO3KITLZqc55Y0zev3xCaLY0Fvj1Y4
+TcTDB+julW4qtSmCgVFwe2Viz6Z0X9TQrtzCHXA9WFzRqXjcj6RqQ/tZLhWEHvje
+ZwSGDMR9Pp977OZCDwd6VTl2gmvQpPcTyRVcjOLCZyqI1sPoeMyFnyBMZxbI4sV4
+lv3MuWcRQMXscTzKIcOA8sX/0aX+YOgxI1R/1K/G2dH67CcxLhg3mzIRBkss3xsA
+iQFOBBMBCAA4FiEEDxGpiYeei7v9weI2RO9bXoYcCacFAlnCi/0CGwMFCwkIBwIG
+FQgJCgsCBBYCAwECHgECF4AACgkQRO9bXoYcCacWvAgAkq088Xv4eXI9dpd+Cz8M
+wQELaKZcjtZTFwYnsuJZEWFDAfGPz/gzn2XhX7PoLAK+kRgdA7u93JbSBKC0ibBC
+KHs8NrU9E0CVPQowHDfvUzeG2A0gb0IBg+COMR6CZzMsCljicP1qnaxaMt8AuZkX
+ym0+k4vvOTZGpsTFgJ7OMlW+6U9IC8LlkPxz5dEjsWbY5iOEmd3zt1iSW6iUR+0c
+suDRimxqocptCWeprJy3zRBaJO0y9fP1GZsOchBFHGoZ6mJptxHmnOVXnrHTn/jP
+AXr1ESofNkeTbwedP1JzjhjfrJvgZ1R7vQw/b/9SGMgyU4c8IxIu48pAv/3X/DVV
++YkCHAQQAQgABgUCWcKOvwAKCRDHpTzFjTsfjP+XEACUACOvOZef0avzBnTfom6b
+ewcZ2USnrfqNP0GpVxkDD4e4e38YDFA8zLt1nIkFPtpg28+wcR+mY9by80JGz0ZY
+boNM9sqdGMQJyhJ2h91g/oZnw7jmqvSWLF3LejWLmNsEUal0Y/KG5wK6LKukn5OY
+PmDbiQZPTZcZ4y4neCruzm1NDQ9pbZmGkO1UQ37TTb91hyBDFcIfNoS1LHBU2cF5
+Lsol3TftaYO0QQZ90N2AC5u0i2PpgN6IN8N6pzkQ3bRe+YgeaADaQ67WrFZ0z4zS
+td17Q25OXBOQdv1IYJWFnom1nqGjfzy2MUiNM/ByGyVqrMQFSOdNi4IHyq9qk8MP
+ONMvhtSVNXr+8AlfYBG3fymOKP4RKEg8W/lR+0qmBNu3kxUEjMeTZEN2BYE4Bhq9
+Li1H/j7PZ/Y/fvsXS30ORrMkOunZswd4tCT5sXAaPtIJKmdsx9B5QiN6gqOYY5kG
+QdRR35UvLx1WlrAhuoGNXnL0xUZc8aNQfomLAwQMxLIMXNw6GXxI8DKrc54QFyTE
+VgCd9SmTO3opW/q8dqg2TYO418SF27iiG5RFsghOAb4TrqvqTDccPgv6hy5NPFQj
+G9BRyK86byc/VoXVbhCok/2oJCHDPO5fE5OJozblOyAh1aQIR/LWPUQ9wen+xD14
+tDPeJ+c7VFLSX9lYq9yzJrQfR3JpZmZpbiBTbWl0aCA8Z3JmbkB1cmJpbnQuY29t
+PokBUgQwAQgAPBYhBA8RqYmHnou7/cHiNkTvW16GHAmnBQJfxmAiHh0gTm8gbG9u
+Z2VyIGVtcGxveWVkIGF0IFVyYmludAAKCRBE71tehhwJp0nvB/9tYQQ5Q2dnlzBJ
+hrW8/aiRRkuJjog1ZlOKVc3WsA//qWH3UwPvz2pWMMyExzXMezOmy+mwZ4BgRRzD
+K42o2ICj6wqifzqVr2wGeKsO1qLu0GDdIfokwuQ4RbQqfQ3BHCBWFCJF3kkE1Iee
+h81DPskpI2lhIXGgjzeOA+4JRqmhG4CijXY5BKkMNdsFxHo8qx5WupvjEbF4ziSV
+Uafa2XmMywtzhALvBlHBM5hpO3AGFPC/pX9w64RTPu64jWTYC4ErOhOsjVDNCn8V
+APIvM9Fcoy2JRIC9N3OLlmHBpnbPZvb7b5dv1+yBWOUg1poaRDmyyMtSmS12XXl8
+PrAgiDoHiQFOBBMBCAA4FiEEDxGpiYeei7v9weI2RO9bXoYcCacFAlnCjA0CGwMF
+CwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQRO9bXoYcCafEAQf8DYC4TNjO9kf1
+MD+0Xsd7N/UOWY0osGD0qzkzuDGmvEtj9P82srWNkpQ+7ecGU4o6rK4YMeaBGBIv
+mbI5LpXvT9pAla1jeG2Yv/ig74doU0Ygj1nz2o7ec5+ONz2uqzdYC2GU9gYJPqeN
+fgNlNwMi3DwLOrRg6XxDTTP3iaWGbGXLHFw+kQxuwvV/TeCuT4zU1F1PVUxyD1Cg
+sE4Rx146JPSR07FPxDBGwJCOe4a6ylrZGijPotUEnOQT7Y6CInUt+Q3VHOb/MRtM
+LITJqLCLQkYBhZs92zLC16AxdU905Uwkli+5tRRXb0zgNhWCsX8IVJuMxdJ+Tut6
+gw06osLlj4kCHAQQAQgABgUCWcKOvwAKCRDHpTzFjTsfjM1CEAC3oxjE1GnZT66H
+XzT44v19MVB4Wu5zkLrIdcb3YqszibYXWusP5B/fyqCDHIE4dkmC/kTGSsMO31UO
+//yXsatpOe8785d+9/T5afRE3OUKn0y55bgFwMaMHB3jBe6edaz/dR04ggzBxmAk
+LE+8PdfpZrgYCoORyapnXsh2P0VkH92uSCUhWGIx6+ag813eGBjqfJF0Q++SeRfN
+n4olR1Na/LDFI8eG3Lv2ku0iuHuJoiAPSsQr2/kfFYMFIFA0KtOkJvf/p93irzjB
+Doc3FhDd5iQLoU0dhivkUcTjeyuQtzuvkdb+WKEVTPGNHgtd78jRRDyBh2zLiwNM
+GriD1fgE57UuNMkGg85PYJYAO5br0jxHp4ZSM+jpe3xgwMsnjw9b3eknpXgG2jtt
+vYmc/HpAM11ulcMUksaehg1TryOqbAmO8Ehu0Lqdh6BpQIaSLicZlpF2pymu9SyE
+B4B9ZeLC265NU3GLGDKu1gwrEYcWWOF90Q6mA8XVQoTCtSiczUoxPOjzkBpZUzgu
+jh3pu/TqAYnw4V7kDYimvmnp6GbiW/nc9iugAUzu7CckzlwBKATtPNNmtLnMEr2W
+aOf6JkIVV24RHT61sFhUIqFhbr8mpxRTOv3iXhTYH3VbiujXPd5pHWt0ZmyBJBbU
++uR8PsXf1Il9tzFUdV0f6gqgkkRJyrkBDQRUrIB5AQgAyBP+r0INTlvWhn386d2r
+ax7QoxB1OmTjROrp5Xs+Ahh2VyGWEwGwNDZoHd3dyQO8HXdKePMISP7URBR5QQ+J
+cUATd8K6ZgxVatDoki7jEgGWk161+XLWOAohyVTiCVCteus7KEBar0wVu4WE7JhZ
+pdX6zma58nmA/R8o9SpJNZGlJH/ia7Z2c3H7ag9IRc2B3uuOWLC0trm/2aF65VdY
+c5w3SB+uE2MJb2m7wXGUehMZ+voQcjEm3ptZzageDzdhlf3Ttjq5KJU4XLRmsVaA
+oQ6GrgPr31EEVm6cczCUBwCd/z7SjCsjI79Pk00iDtc7KYtpmsiO11Mly7uX3E30
+MQARAQABiQEfBBgBCAAJBQJUrIB5AhsMAAoJEETvW16GHAmntvkH/0FifrFYRRzR
+1Iz1ki9aPnUlDCL+qrHghsqIxi2cQJfc+iClJ4Ot/FzK1ZIAMtWqMvdTfh2NCD4O
+OYbk71N+rhaA9h6/XXfmVX7Tt/I374oqMKrvoj7derH2an/VVEw0yabC86MYsKvs
+eRWyWLdoqg6it9CckA9hDJ+S4tKrUdaIbp8UdghOW8jUQNcppNQSjA29LQx5fO3w
+mOfmx2QaxtLL6sg7McStE+G6kZdIElH4VQOu8XC9Qu8BtTJrTWTPirPQHqRZcZkQ
+yZ19FzXWsOz+kLSuIoC8OYUohaVWJnXxMi/VtMp+DaOD46yZ0L6wryCaiftQDcWP
+R/QKwpd4hf8=
+=qj6y
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/users/grfn/gws.fyi/recipes/tomato-sauce.org b/users/aspen/web/recipes/tomato-sauce.org
index 74e9b103c6..dec7468ac2 100644
--- a/users/grfn/gws.fyi/recipes/tomato-sauce.org
+++ b/users/aspen/web/recipes/tomato-sauce.org
@@ -34,8 +34,8 @@ some kind of fat (I prefer butter).
      sauce, it's added whole later (so ignore this bullet point).
    - If you feel like it (sometimes I do, usually I don't) you can also mince
      garlic here and sautรฉ that in with everything else. Add a little after the
-     onion, as garlic cooks slower than onion unless you want something roastier
-     (usually you don't for tomato sauce)
+     onion, as garlic cooks faster than onion, unless you want something
+     roastier (usually you don't for tomato sauce)
    - The traditional (so I'm told) thing to do with amatriciana, but also nice
      with all variations, is to add in a little crushed red pepper with the
      fat to flavor it slightly, but do this late so it doesn't burn
diff --git a/users/grfn/gws.fyi/shell.nix b/users/aspen/web/shell.nix
index 846bdb6677..7e7fccdc93 100644
--- a/users/grfn/gws.fyi/shell.nix
+++ b/users/aspen/web/shell.nix
@@ -3,7 +3,7 @@ mkShell {
   buildInputs = [
     awscli
     gnumake
-    letsencrypt
     tarsnap
+    certbot
   ];
 }
diff --git a/users/grfn/gws.fyi/site.nix b/users/aspen/web/site.nix
index 33d4a71e7b..057c4d3ee6 100644
--- a/users/grfn/gws.fyi/site.nix
+++ b/users/aspen/web/site.nix
@@ -1,4 +1,4 @@
-args@{ pkgs ? import <nixpkgs> {}, ... }:
+args@{ pkgs ? import <nixpkgs> { }, ... }:
 
 let
 
diff --git a/users/aspen/wigglydonke.rs/.well-known/cf-2fa-verify.txt b/users/aspen/wigglydonke.rs/.well-known/cf-2fa-verify.txt
new file mode 100644
index 0000000000..1012e8282d
--- /dev/null
+++ b/users/aspen/wigglydonke.rs/.well-known/cf-2fa-verify.txt
@@ -0,0 +1 @@
+debe85916969017
diff --git a/users/grfn/wigglydonke.rs/index.html b/users/aspen/wigglydonke.rs/index.html
index 4fd7f25fcf..4fd7f25fcf 100644
--- a/users/grfn/wigglydonke.rs/index.html
+++ b/users/aspen/wigglydonke.rs/index.html
diff --git a/users/grfn/wigglydonke.rs/wd.png b/users/aspen/wigglydonke.rs/wd.png
index 217443e2df..217443e2df 100644
--- a/users/grfn/wigglydonke.rs/wd.png
+++ b/users/aspen/wigglydonke.rs/wd.png
Binary files differdiff --git a/users/grfn/gws.fyi/.envrc b/users/aspen/xanthous/.envrc
index be81feddb1..be81feddb1 100644
--- a/users/grfn/gws.fyi/.envrc
+++ b/users/aspen/xanthous/.envrc
diff --git a/users/grfn/xanthous/.github/actions/nix-build/Dockerfile b/users/aspen/xanthous/.github/actions/nix-build/Dockerfile
index cfe8e35df0..cfe8e35df0 100644
--- a/users/grfn/xanthous/.github/actions/nix-build/Dockerfile
+++ b/users/aspen/xanthous/.github/actions/nix-build/Dockerfile
diff --git a/users/grfn/xanthous/.github/actions/nix-build/entrypoint.sh b/users/aspen/xanthous/.github/actions/nix-build/entrypoint.sh
index cb7aca541a..cb7aca541a 100755
--- a/users/grfn/xanthous/.github/actions/nix-build/entrypoint.sh
+++ b/users/aspen/xanthous/.github/actions/nix-build/entrypoint.sh
diff --git a/users/grfn/xanthous/.github/workflows/haskell.yml b/users/aspen/xanthous/.github/workflows/haskell.yml
index df82de3e8c..df82de3e8c 100644
--- a/users/grfn/xanthous/.github/workflows/haskell.yml
+++ b/users/aspen/xanthous/.github/workflows/haskell.yml
diff --git a/users/grfn/xanthous/.gitignore b/users/aspen/xanthous/.gitignore
index 74014978ff..2ad31c01d4 100644
--- a/users/grfn/xanthous/.gitignore
+++ b/users/aspen/xanthous/.gitignore
@@ -19,6 +19,7 @@ cabal.sandbox.config
 .stack-work/
 cabal.project.local
 cabal.project.local~
+cabal.project.local~*
 .HTF/
 .ghc.environment.*
 
@@ -32,3 +33,5 @@ result
 # app-specific
 debug.log
 data
+*.save
+.tasty-rerun-log
diff --git a/users/grfn/xanthous/LICENSE b/users/aspen/xanthous/LICENSE
index 45644ff764..45644ff764 100644
--- a/users/grfn/xanthous/LICENSE
+++ b/users/aspen/xanthous/LICENSE
diff --git a/users/grfn/xanthous/README.org b/users/aspen/xanthous/README.org
index 7e1fedb069..7e1fedb069 100644
--- a/users/grfn/xanthous/README.org
+++ b/users/aspen/xanthous/README.org
diff --git a/users/grfn/xanthous/Setup.hs b/users/aspen/xanthous/Setup.hs
index 9a994af677..9a994af677 100644
--- a/users/grfn/xanthous/Setup.hs
+++ b/users/aspen/xanthous/Setup.hs
diff --git a/users/grfn/xanthous/app/Main.hs b/users/aspen/xanthous/app/Main.hs
index 6d88405fd9..c771a0d932 100644
--- a/users/grfn/xanthous/app/Main.hs
+++ b/users/aspen/xanthous/app/Main.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE RecordWildCards #-}
+--------------------------------------------------------------------------------
 module Main ( main ) where
 --------------------------------------------------------------------------------
 import           Xanthous.Prelude hiding (finally)
@@ -12,6 +14,7 @@ import           System.Exit (die)
 --------------------------------------------------------------------------------
 import qualified Xanthous.Game as Game
 import           Xanthous.Game.Env (GameEnv(..))
+import qualified Xanthous.Game.Env as Game
 import           Xanthous.App
 import           Xanthous.Generators.Level
                  ( GeneratorInput
@@ -26,9 +29,17 @@ import           Xanthous.Data (Dimensions, Dimensions'(Dimensions))
 import           Data.Array.IArray ( amap )
 --------------------------------------------------------------------------------
 
+parseGameConfig :: Opt.Parser Game.Config
+parseGameConfig = Game.Config
+  <$> Opt.switch
+      ( Opt.long "disable-saving"
+      <> Opt.help "Disallow saving games"
+      )
+
 data RunParams = RunParams
   { seed :: Maybe Int
   , characterName :: Maybe Text
+  , gameConfig :: Game.Config
   }
   deriving stock (Show, Eq)
 
@@ -46,6 +57,7 @@ parseRunParams = RunParams
         <> "will be prompted for at runtime"
         )
       ))
+  <*> parseGameConfig
 
 data Command
   = Run RunParams
@@ -104,7 +116,7 @@ newGame rparams = do
   let initialState = Game.initialStateFromSeed gameSeed &~ do
         for_ (characterName rparams) $ \cn ->
           Game.character . Character.characterName ?= cn
-  runGame NewGame initialState `finally` do
+  runGame NewGame (gameConfig rparams) initialState `finally` do
     thanks
     when (isNothing $ seed rparams)
       . putStrLn
@@ -113,21 +125,21 @@ newGame rparams = do
 
 loadGame :: FilePath -> IO ()
 loadGame saveFile = do
-  gameState <- maybe (die "Invalid save file!") pure
-              =<< Game.loadGame . fromStrict <$> readFile @IO saveFile
-  gameState `deepseq` runGame LoadGame gameState
+  gameState <- maybe (die "Invalid save file!") pure . Game.loadGame  . fromStrict
+              =<< readFile @IO saveFile
+  gameState `deepseq` runGame (LoadGame saveFile) Game.defaultConfig gameState
 
-runGame :: RunType -> Game.GameState -> IO ()
-runGame rt gameState = do
-  eventChan <- Brick.BChan.newBChan 10
-  let gameEnv = GameEnv eventChan
+runGame :: RunType -> Game.Config -> Game.GameState -> IO ()
+runGame rt _config gameState = do
+  _eventChan <- Brick.BChan.newBChan 10
+  let gameEnv = GameEnv {..}
   app <- makeApp gameEnv rt
   let buildVty = Vty.mkVty Vty.defaultConfig
   initialVty <- buildVty
   _game' <- customMain
     initialVty
     buildVty
-    (Just eventChan)
+    (Just _eventChan)
     app
     gameState
   pure ()
diff --git a/users/grfn/xanthous/bench/Bench.hs b/users/aspen/xanthous/bench/Bench.hs
index 5889618ee4..5889618ee4 100644
--- a/users/grfn/xanthous/bench/Bench.hs
+++ b/users/aspen/xanthous/bench/Bench.hs
diff --git a/users/grfn/xanthous/bench/Bench/Prelude.hs b/users/aspen/xanthous/bench/Bench/Prelude.hs
index c553abd6d5..c553abd6d5 100644
--- a/users/grfn/xanthous/bench/Bench/Prelude.hs
+++ b/users/aspen/xanthous/bench/Bench/Prelude.hs
diff --git a/users/grfn/xanthous/bench/Xanthous/Generators/UtilBench.hs b/users/aspen/xanthous/bench/Xanthous/Generators/UtilBench.hs
index 56310e691c..56310e691c 100644
--- a/users/grfn/xanthous/bench/Xanthous/Generators/UtilBench.hs
+++ b/users/aspen/xanthous/bench/Xanthous/Generators/UtilBench.hs
diff --git a/users/grfn/xanthous/bench/Xanthous/RandomBench.hs b/users/aspen/xanthous/bench/Xanthous/RandomBench.hs
index fae4af92a7..fae4af92a7 100644
--- a/users/grfn/xanthous/bench/Xanthous/RandomBench.hs
+++ b/users/aspen/xanthous/bench/Xanthous/RandomBench.hs
diff --git a/users/grfn/xanthous/build/generic-arbitrary-export-garbitrary.patch b/users/aspen/xanthous/build/generic-arbitrary-export-garbitrary.patch
index f0c936bfca..f0c936bfca 100644
--- a/users/grfn/xanthous/build/generic-arbitrary-export-garbitrary.patch
+++ b/users/aspen/xanthous/build/generic-arbitrary-export-garbitrary.patch
diff --git a/users/grfn/xanthous/build/hgeometry-fix-haddock.patch b/users/aspen/xanthous/build/hgeometry-fix-haddock.patch
index 748c65b3e0..748c65b3e0 100644
--- a/users/grfn/xanthous/build/hgeometry-fix-haddock.patch
+++ b/users/aspen/xanthous/build/hgeometry-fix-haddock.patch
diff --git a/users/grfn/xanthous/build/update-comonad-extras.patch b/users/aspen/xanthous/build/update-comonad-extras.patch
index cd1dbe24d3..cd1dbe24d3 100644
--- a/users/grfn/xanthous/build/update-comonad-extras.patch
+++ b/users/aspen/xanthous/build/update-comonad-extras.patch
diff --git a/users/aspen/xanthous/default.nix b/users/aspen/xanthous/default.nix
new file mode 100644
index 0000000000..049c92fb4c
--- /dev/null
+++ b/users/aspen/xanthous/default.nix
@@ -0,0 +1,27 @@
+{ depot ? (import ../../../. { })
+, pkgs ? depot.third_party.nixpkgs
+, ...
+}:
+
+let
+  ignore = depot.third_party.gitignoreSource.gitignoreFilter ./.;
+  src = builtins.path {
+    name = "xanthous-source";
+    path = ./.;
+    filter = path: type:
+      !(type == "directory" && builtins.baseNameOf path == "server")
+      && !(type == "directory" && builtins.baseNameOf path == "docs")
+      && (ignore path type
+      || builtins.baseNameOf path == "package.yaml");
+  };
+  # generated by cabal2nix
+  basePkg = pkgs.haskell.packages.ghc8107.callPackage ./pkg.nix { };
+in
+
+pkgs.haskell.lib.overrideCabal basePkg (default: {
+  inherit src;
+  version = "canon";
+  configureFlags = [
+    "--ghc-option=-Wall --ghc-option=-Werror"
+  ] ++ (default.configureFlags or [ ]);
+})
diff --git a/users/aspen/xanthous/docs/raw-types.org b/users/aspen/xanthous/docs/raw-types.org
new file mode 100644
index 0000000000..e5bcda0426
--- /dev/null
+++ b/users/aspen/xanthous/docs/raw-types.org
@@ -0,0 +1,24 @@
+#+TITLE: Raw Types (WIP)
+
+
+* Raw Types
+** Item
+*** Attributes
+| name            | type                      | commentary                                                       |
+|-----------------+---------------------------+------------------------------------------------------------------|
+| name            | string                    |                                                                  |
+| description     | string                    | Not capitalized, should usually start with an indefinite article |
+| longDescription | string                    | Capitalized, should usually start with an indefinite article     |
+| char            | [[*EntityChar][EntityChar]]                |                                                                  |
+| wieldable       | [[*EntityWieldable][EntityWieldable]]           |                                                                  |
+| density         | number , [number, number] | Density, or range for random density, in g/mยณ                    |
+| volume          | number , [number, number] | Volume, or range for random volume, in mยณ                        |
+* Data Types
+** EntityChar
+*** Attributes
+| name  | type | commentary                                            |
+|-------+------+-------------------------------------------------------|
+| char  | char | How the entity is displayed when dropped on the floor |
+| style | Attr |                                                       |
+** TODO EntityWieldable
+** TODO Attr
diff --git a/users/grfn/xanthous/hie.yaml b/users/aspen/xanthous/hie.yaml
index 49f8ec1fbb..e7cf01d158 100644
--- a/users/grfn/xanthous/hie.yaml
+++ b/users/aspen/xanthous/hie.yaml
@@ -4,7 +4,7 @@ cradle:
       component: 'lib:xanthous'
     - path: './test'
       component: 'test:test'
-    - path: './src'
+    - path: './app'
       component: 'exe:xanthous'
     - path: './bench'
       component: 'bench:benchmark'
diff --git a/users/grfn/xanthous/nixpkgs.nix b/users/aspen/xanthous/nixpkgs.nix
index 7d7c164405..7d7c164405 100644
--- a/users/grfn/xanthous/nixpkgs.nix
+++ b/users/aspen/xanthous/nixpkgs.nix
diff --git a/users/grfn/xanthous/package.yaml b/users/aspen/xanthous/package.yaml
index 605b1cab8c..15a36fe964 100644
--- a/users/grfn/xanthous/package.yaml
+++ b/users/aspen/xanthous/package.yaml
@@ -111,6 +111,7 @@ default-extensions:
 
 ghc-options:
 - -Wall
+- -fconstraint-solver-iterations=6 # Xanthous.Data, Xanthous.Command
 
 library:
   source-dirs: src
@@ -140,6 +141,7 @@ tests:
     - tasty
     - tasty-hunit
     - tasty-quickcheck
+    - tasty-rerun
     - lens-properties
 
 benchmarks:
diff --git a/users/aspen/xanthous/pkg.nix b/users/aspen/xanthous/pkg.nix
new file mode 100644
index 0000000000..f8364c467a
--- /dev/null
+++ b/users/aspen/xanthous/pkg.nix
@@ -0,0 +1,349 @@
+{ mkDerivation
+, aeson
+, array
+, async
+, base
+, bifunctors
+, brick
+, checkers
+, classy-prelude
+, comonad
+, comonad-extras
+, constraints
+, containers
+, criterion
+, data-default
+, data-interval
+, deepseq
+, directory
+, fgl
+, fgl-arbitrary
+, file-embed
+, filepath
+, generic-arbitrary
+, generic-lens
+, groups
+, hgeometry
+, hgeometry-combinatorial
+, hpack
+, JuicyPixels
+, lens
+, lens-properties
+, lib
+, lifted-async
+, linear
+, megaparsec
+, mmorph
+, monad-control
+, MonadRandom
+, mtl
+, optparse-applicative
+, parallel
+, parser-combinators
+, pointed
+, QuickCheck
+, quickcheck-instances
+, quickcheck-text
+, random
+, random-extras
+, random-fu
+, random-source
+, Rasterific
+, raw-strings-qq
+, reflection
+, semigroupoids
+, semigroups
+, splitmix
+, stache
+, streams
+, tasty
+, tasty-hunit
+, tasty-quickcheck
+, tasty-rerun
+, text
+, text-zipper
+, tomland
+, transformers
+, vector
+, vty
+, witherable
+, yaml
+, zlib
+}:
+mkDerivation {
+  pname = "xanthous";
+  version = "0.1.0.0";
+  src = ./.;
+  isLibrary = true;
+  isExecutable = true;
+  libraryHaskellDepends = [
+    aeson
+    array
+    async
+    base
+    bifunctors
+    brick
+    checkers
+    classy-prelude
+    comonad
+    comonad-extras
+    constraints
+    containers
+    criterion
+    data-default
+    data-interval
+    deepseq
+    directory
+    fgl
+    fgl-arbitrary
+    file-embed
+    filepath
+    generic-arbitrary
+    generic-lens
+    groups
+    hgeometry
+    hgeometry-combinatorial
+    JuicyPixels
+    lens
+    lifted-async
+    linear
+    megaparsec
+    mmorph
+    monad-control
+    MonadRandom
+    mtl
+    optparse-applicative
+    parallel
+    parser-combinators
+    pointed
+    QuickCheck
+    quickcheck-instances
+    quickcheck-text
+    random
+    random-extras
+    random-fu
+    random-source
+    Rasterific
+    raw-strings-qq
+    reflection
+    semigroupoids
+    semigroups
+    splitmix
+    stache
+    streams
+    text
+    text-zipper
+    tomland
+    transformers
+    vector
+    vty
+    witherable
+    yaml
+    zlib
+  ];
+  libraryToolDepends = [ hpack ];
+  executableHaskellDepends = [
+    aeson
+    array
+    async
+    base
+    bifunctors
+    brick
+    checkers
+    classy-prelude
+    comonad
+    comonad-extras
+    constraints
+    containers
+    criterion
+    data-default
+    data-interval
+    deepseq
+    directory
+    fgl
+    fgl-arbitrary
+    file-embed
+    filepath
+    generic-arbitrary
+    generic-lens
+    groups
+    hgeometry
+    hgeometry-combinatorial
+    JuicyPixels
+    lens
+    lifted-async
+    linear
+    megaparsec
+    mmorph
+    monad-control
+    MonadRandom
+    mtl
+    optparse-applicative
+    parallel
+    parser-combinators
+    pointed
+    QuickCheck
+    quickcheck-instances
+    quickcheck-text
+    random
+    random-extras
+    random-fu
+    random-source
+    Rasterific
+    raw-strings-qq
+    reflection
+    semigroupoids
+    semigroups
+    splitmix
+    stache
+    streams
+    text
+    text-zipper
+    tomland
+    transformers
+    vector
+    vty
+    witherable
+    yaml
+    zlib
+  ];
+  testHaskellDepends = [
+    aeson
+    array
+    async
+    base
+    bifunctors
+    brick
+    checkers
+    classy-prelude
+    comonad
+    comonad-extras
+    constraints
+    containers
+    criterion
+    data-default
+    data-interval
+    deepseq
+    directory
+    fgl
+    fgl-arbitrary
+    file-embed
+    filepath
+    generic-arbitrary
+    generic-lens
+    groups
+    hgeometry
+    hgeometry-combinatorial
+    JuicyPixels
+    lens
+    lens-properties
+    lifted-async
+    linear
+    megaparsec
+    mmorph
+    monad-control
+    MonadRandom
+    mtl
+    optparse-applicative
+    parallel
+    parser-combinators
+    pointed
+    QuickCheck
+    quickcheck-instances
+    quickcheck-text
+    random
+    random-extras
+    random-fu
+    random-source
+    Rasterific
+    raw-strings-qq
+    reflection
+    semigroupoids
+    semigroups
+    splitmix
+    stache
+    streams
+    tasty
+    tasty-hunit
+    tasty-quickcheck
+    tasty-rerun
+    text
+    text-zipper
+    tomland
+    transformers
+    vector
+    vty
+    witherable
+    yaml
+    zlib
+  ];
+  benchmarkHaskellDepends = [
+    aeson
+    array
+    async
+    base
+    bifunctors
+    brick
+    checkers
+    classy-prelude
+    comonad
+    comonad-extras
+    constraints
+    containers
+    criterion
+    data-default
+    data-interval
+    deepseq
+    directory
+    fgl
+    fgl-arbitrary
+    file-embed
+    filepath
+    generic-arbitrary
+    generic-lens
+    groups
+    hgeometry
+    hgeometry-combinatorial
+    JuicyPixels
+    lens
+    lifted-async
+    linear
+    megaparsec
+    mmorph
+    monad-control
+    MonadRandom
+    mtl
+    optparse-applicative
+    parallel
+    parser-combinators
+    pointed
+    QuickCheck
+    quickcheck-instances
+    quickcheck-text
+    random
+    random-extras
+    random-fu
+    random-source
+    Rasterific
+    raw-strings-qq
+    reflection
+    semigroupoids
+    semigroups
+    splitmix
+    stache
+    streams
+    text
+    text-zipper
+    tomland
+    transformers
+    vector
+    vty
+    witherable
+    yaml
+    zlib
+  ];
+  prePatch = "hpack";
+  homepage = "https://github.com/glittershark/xanthous#readme";
+  description = "A WIP TUI RPG";
+  license = lib.licenses.gpl3Only;
+}
diff --git a/users/aspen/xanthous/server/.envrc b/users/aspen/xanthous/server/.envrc
new file mode 100644
index 0000000000..051d09d292
--- /dev/null
+++ b/users/aspen/xanthous/server/.envrc
@@ -0,0 +1 @@
+eval "$(lorri direnv)"
diff --git a/users/aspen/xanthous/server/.gitignore b/users/aspen/xanthous/server/.gitignore
new file mode 100644
index 0000000000..2f7896d1d1
--- /dev/null
+++ b/users/aspen/xanthous/server/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/aspen/xanthous/server/Cargo.lock b/users/aspen/xanthous/server/Cargo.lock
new file mode 100644
index 0000000000..173298b158
--- /dev/null
+++ b/users/aspen/xanthous/server/Cargo.lock
@@ -0,0 +1,1874 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aes"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+ "ctr",
+ "opaque-debug",
+]
+
+[[package]]
+name = "ahash"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "version_check",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "atomic-shim"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67cd4b51d303cf3501c301e8125df442128d3c6d7c69f71b27833d253de47e77"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64ct"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6b4d9b1225d28d360ec6a231d65af1fd99a2a095154c8040689617290569c5c"
+
+[[package]]
+name = "bcrypt-pbkdf"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c38c03b9506bd92bf1ef50665a81eda156f615438f7654bffba58907e6149d7"
+dependencies = [
+ "blowfish",
+ "crypto-mac",
+ "pbkdf2",
+ "sha2",
+ "zeroize",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block-modes"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e"
+dependencies = [
+ "block-padding",
+ "cipher",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
+
+[[package]]
+name = "blowfish"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe3ff3fc1de48c1ac2e3341c4df38b0d1bfb8fdf04632a187c8b75aaa319a7ab"
+dependencies = [
+ "byteorder",
+ "cipher",
+ "opaque-debug",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "bytes"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
+
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
+dependencies = [
+ "iana-time-zone",
+ "num-integer",
+ "num-traits",
+ "winapi",
+]
+
+[[package]]
+name = "cipher"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "clap"
+version = "3.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
+dependencies = [
+ "atty",
+ "bitflags",
+ "clap_derive",
+ "clap_lex",
+ "indexmap",
+ "once_cell",
+ "strsim",
+ "termcolor",
+ "textwrap",
+]
+
+[[package]]
+name = "clap_derive"
+version = "3.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
+]
+
+[[package]]
+name = "color-eyre"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7"
+dependencies = [
+ "backtrace",
+ "color-spantrace",
+ "eyre",
+ "indenter",
+ "once_cell",
+ "owo-colors",
+ "tracing-error",
+]
+
+[[package]]
+name = "color-spantrace"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1"
+dependencies = [
+ "once_cell",
+ "owo-colors",
+ "tracing-core",
+ "tracing-error",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crypto-mac"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
+[[package]]
+name = "cryptovec"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccc7fa13a6bbb2322d325292c57f4c8e7291595506f8289968a0eb61c3130bdf"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "ctr"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
+name = "dashmap"
+version = "4.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
+dependencies = [
+ "cfg-if",
+ "num_cpus",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
+
+[[package]]
+name = "digest"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "dirs"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users",
+ "winapi",
+]
+
+[[package]]
+name = "endian-type"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
+
+[[package]]
+name = "eyre"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
+dependencies = [
+ "indenter",
+ "once_cell",
+]
+
+[[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "flate2"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "futures"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56"
+
+[[package]]
+name = "futures-task"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1"
+
+[[package]]
+name = "futures-util"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "gimli"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
+
+[[package]]
+name = "hashbrown"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hmac"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+dependencies = [
+ "crypto-mac",
+ "digest",
+]
+
+[[package]]
+name = "http"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
+
+[[package]]
+name = "hyper"
+version = "0.14.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "js-sys",
+ "wasm-bindgen",
+ "winapi",
+]
+
+[[package]]
+name = "indenter"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
+
+[[package]]
+name = "itoa"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+
+[[package]]
+name = "js-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.134"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
+
+[[package]]
+name = "libsodium-sys"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "walkdir",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "mach"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "matchers"
+version = "0.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
+dependencies = [
+ "regex-automata",
+]
+
+[[package]]
+name = "md5"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "metrics"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55586aa936c35f34ba8aa5d97356d554311206e1ce1f9e68fe7b07288e5ad827"
+dependencies = [
+ "ahash",
+ "metrics-macros",
+]
+
+[[package]]
+name = "metrics-exporter-prometheus"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "343a5ceb38235928e7a5687412590f07e6d281522dcd9ff51246f8856eef5fe5"
+dependencies = [
+ "hyper",
+ "ipnet",
+ "metrics",
+ "metrics-util",
+ "parking_lot",
+ "quanta",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "metrics-macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0daa0ab3a0ae956d0e2c1f42511422850e577d36a255357d1a7d08d45ee3a2f1"
+dependencies = [
+ "lazy_static",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "syn",
+]
+
+[[package]]
+name = "metrics-util"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1174223789e331d9d47a4a953dac36e397db60fa8d2a111ac505388c6c7fe32e"
+dependencies = [
+ "ahash",
+ "aho-corasick",
+ "atomic-shim",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+ "dashmap",
+ "hashbrown 0.11.2",
+ "indexmap",
+ "metrics",
+ "num_cpus",
+ "ordered-float",
+ "parking_lot",
+ "quanta",
+ "radix_trie",
+ "sketches-ddsketch",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
+dependencies = [
+ "libc",
+ "log",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys",
+]
+
+[[package]]
+name = "nibble_vec"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "nix"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if",
+ "libc",
+ "memoffset",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "ordered-float"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "os_str_bytes"
+version = "6.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
+
+[[package]]
+name = "owo-colors"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+dependencies = [
+ "cfg-if",
+ "instant",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "password-hash"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7"
+dependencies = [
+ "base64ct",
+ "rand_core",
+ "subtle",
+]
+
+[[package]]
+name = "pbkdf2"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa"
+dependencies = [
+ "base64ct",
+ "crypto-mac",
+ "hmac",
+ "password-hash",
+ "sha2",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quanta"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8"
+dependencies = [
+ "crossbeam-utils",
+ "libc",
+ "mach",
+ "once_cell",
+ "raw-cpuid",
+ "wasi 0.10.2+wasi-snapshot-preview1",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "radix_trie"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
+dependencies = [
+ "endian-type",
+ "nibble_vec",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "raw-cpuid"
+version = "10.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
+dependencies = [
+ "getrandom",
+ "redox_syscall",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.145"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.145"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
+dependencies = [
+ "block-buffer",
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+ "opaque-debug",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "sketches-ddsketch"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04d2ecae5fcf33b122e2e6bd520a57ccf152d2dde3b38c71039df1a6867264ee"
+
+[[package]]
+name = "slab"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
+
+[[package]]
+name = "socket2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "subtle"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+
+[[package]]
+name = "syn"
+version = "1.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
+
+[[package]]
+name = "thiserror"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "thrussh"
+version = "0.33.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e6540238a9adf83df6e66541c182a52acf892ab335595ca965c229ade8536f8"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "cryptovec",
+ "digest",
+ "flate2",
+ "futures",
+ "generic-array",
+ "log",
+ "rand",
+ "sha2",
+ "thiserror",
+ "thrussh-keys",
+ "thrussh-libsodium",
+ "tokio",
+]
+
+[[package]]
+name = "thrussh-keys"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a72cc51a2932b18d92f7289332d8564cec4a5014063722a9d3fdca52c5d8f5ab"
+dependencies = [
+ "aes",
+ "bcrypt-pbkdf",
+ "bit-vec",
+ "block-modes",
+ "byteorder",
+ "cryptovec",
+ "data-encoding",
+ "dirs",
+ "futures",
+ "hmac",
+ "log",
+ "md5",
+ "num-bigint",
+ "num-integer",
+ "pbkdf2",
+ "rand",
+ "serde",
+ "serde_derive",
+ "sha2",
+ "thiserror",
+ "thrussh-libsodium",
+ "tokio",
+ "tokio-stream",
+ "yasna",
+]
+
+[[package]]
+name = "thrussh-libsodium"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfe89c70d27b1cb92e13bc8af63493e890d0de46dae4df0e28233f62b4ed9500"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "libsodium-sys",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "tokio"
+version = "1.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
+dependencies = [
+ "autocfg",
+ "bytes",
+ "libc",
+ "memchr",
+ "mio",
+ "num_cpus",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "winapi",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-error"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
+dependencies = [
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
+dependencies = [
+ "lazy_static",
+ "log",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-serde"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
+dependencies = [
+ "ansi_term",
+ "chrono",
+ "lazy_static",
+ "matchers",
+ "regex",
+ "serde",
+ "serde_json",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+ "tracing-serde",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
+
+[[package]]
+name = "typenum"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
+dependencies = [
+ "log",
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+
+[[package]]
+name = "web-sys"
+version = "0.3.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+dependencies = [
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+
+[[package]]
+name = "xanthous-server"
+version = "0.1.0"
+dependencies = [
+ "base64ct",
+ "clap",
+ "color-eyre",
+ "eyre",
+ "futures",
+ "libc",
+ "metrics",
+ "metrics-exporter-prometheus",
+ "nix",
+ "pbkdf2",
+ "tempfile",
+ "thrussh",
+ "thrussh-keys",
+ "tokio",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "yasna"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75"
+dependencies = [
+ "bit-vec",
+ "num-bigint",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
diff --git a/users/aspen/xanthous/server/Cargo.toml b/users/aspen/xanthous/server/Cargo.toml
new file mode 100644
index 0000000000..d4a064beb6
--- /dev/null
+++ b/users/aspen/xanthous/server/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "xanthous-server"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies]
+clap = { version = "3.0", features = [ "derive", "env" ] }
+color-eyre = "0.5.11"
+eyre = "0.6.5"
+thrussh = "0.33.5"
+thrussh-keys = "0.21.0"
+tracing = "0.1.29"
+tracing-subscriber = "0.2.25"
+metrics = "0.17.0"
+metrics-exporter-prometheus = "0.6.1"
+futures = "0.3.17"
+libc = "0.2.103"
+nix = "0.23.0"
+
+# Pins for rust 1.55 (2018 edition) until we have 1.56 in nixpkgs-unstable
+pbkdf2 = "<0.9"
+base64ct = "<1.2"
+
+[dependencies.tokio]
+version = "1.13"
+features = ["rt", "rt-multi-thread", "macros", "net", "process", "fs", "signal"]
+
+[dev-dependencies]
+tempfile = "3.2.0"
diff --git a/users/aspen/xanthous/server/default.nix b/users/aspen/xanthous/server/default.nix
new file mode 100644
index 0000000000..572230a56c
--- /dev/null
+++ b/users/aspen/xanthous/server/default.nix
@@ -0,0 +1,24 @@
+args@{ depot ? import ../../../.. { }
+, pkgs ? depot.third_party.nixpkgs
+, ...
+}:
+
+depot.third_party.naersk.buildPackage {
+  name = "xanthous-server";
+  version = "0.0.1";
+  src = depot.third_party.gitignoreSource ./.;
+
+  # Workaround for a potential Nix bug related to restricted eval.
+  # See https://github.com/nix-community/naersk/issues/169
+  root = depot.nix.sparseTree {
+    root = ./.;
+    paths = [
+      ./Cargo.toml
+      ./Cargo.lock
+    ];
+  };
+
+  passthru = {
+    docker = import ./docker.nix args;
+  };
+}
diff --git a/users/aspen/xanthous/server/docker.nix b/users/aspen/xanthous/server/docker.nix
new file mode 100644
index 0000000000..5eaef4553b
--- /dev/null
+++ b/users/aspen/xanthous/server/docker.nix
@@ -0,0 +1,21 @@
+{ depot ? import ../../../.. { }
+, pkgs ? depot.third_party.nixpkgs
+, ...
+}:
+
+let
+  inherit (depot.users.aspen) xanthous;
+  xanthous-server = xanthous.server;
+in
+pkgs.dockerTools.buildLayeredImage {
+  name = "xanthous-server";
+  tag = "latest";
+  contents = [ xanthous xanthous-server ];
+  config = {
+    Cmd = [
+      "${xanthous-server}/bin/xanthous-server"
+      "--xanthous-binary-path"
+      "${xanthous}/bin/xanthous"
+    ];
+  };
+}
diff --git a/users/aspen/xanthous/server/module.nix b/users/aspen/xanthous/server/module.nix
new file mode 100644
index 0000000000..6d1bdc6873
--- /dev/null
+++ b/users/aspen/xanthous/server/module.nix
@@ -0,0 +1,49 @@
+{ config, lib, pkgs, depot, ... }:
+
+let
+  cfg = config.services.xanthous-server;
+in
+{
+  options = with lib; {
+    services.xanthous-server = {
+      enable = mkEnableOption "xanthous server";
+
+      port = mkOption {
+        type = types.int;
+        default = 2222;
+        description = "Port to listen to for SSH connections";
+      };
+
+      metricsPort = mkOption {
+        type = types.int;
+        default = 9000;
+        description = "Port to listen to for prometheus metrics";
+      };
+
+      image = mkOption {
+        type = types.package;
+        default = depot.users.aspen.xanthous.server.docker;
+        description = "OCI image file to run";
+      };
+
+      ed25519SecretKeyFile = mkOption {
+        type = with types; uniq string;
+        description = "Path to the ed25519 secret key for the server";
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    virtualisation.oci-containers.containers."xanthous-server" = {
+      autoStart = true;
+      image = "${cfg.image.imageName}:${cfg.image.imageTag}";
+      imageFile = cfg.image;
+      ports = [
+        "${toString cfg.port}:22"
+        "${toString cfg.metricsPort}:9000"
+      ];
+      environment.SECRET_KEY_FILE = "/secret-key";
+      volumes = [ "/etc/secrets/xanthous-server-secret-key:/secret-key" ];
+    };
+  };
+}
diff --git a/users/aspen/xanthous/server/shell.nix b/users/aspen/xanthous/server/shell.nix
new file mode 100644
index 0000000000..e01c0316a6
--- /dev/null
+++ b/users/aspen/xanthous/server/shell.nix
@@ -0,0 +1,11 @@
+let
+  depot = import ../../../.. { };
+  pkgs = depot.third_party.nixpkgs;
+in
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    rustup
+    rust-analyzer
+  ];
+}
diff --git a/users/aspen/xanthous/server/src/main.rs b/users/aspen/xanthous/server/src/main.rs
new file mode 100644
index 0000000000..1b2c1c104b
--- /dev/null
+++ b/users/aspen/xanthous/server/src/main.rs
@@ -0,0 +1,385 @@
+use std::net::SocketAddr;
+use std::path::PathBuf;
+use std::pin::Pin;
+use std::process::Command;
+use std::str;
+use std::sync::Arc;
+
+use clap::Parser;
+use color_eyre::eyre::Result;
+use eyre::{bail, Context};
+use futures::future::{ready, Ready};
+use futures::Future;
+use metrics_exporter_prometheus::PrometheusBuilder;
+use nix::pty::Winsize;
+use pty::ChildHandle;
+use thrussh::server::{self, Auth, Session};
+use thrussh::{ChannelId, CryptoVec};
+use thrussh_keys::decode_secret_key;
+use thrussh_keys::key::KeyPair;
+use tokio::fs::File;
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio::net::TcpListener;
+use tokio::select;
+use tokio::time::Instant;
+use tracing::{debug, error, info, info_span, trace, warn, Instrument};
+use tracing_subscriber::EnvFilter;
+
+use crate::pty::WaitPid;
+
+mod metrics;
+mod pty;
+
+use crate::metrics::reported::*;
+use crate::metrics::{decrement_gauge, histogram, increment_counter, increment_gauge};
+
+/// SSH-compatible server for playing Xanthous
+#[derive(Parser, Debug)]
+struct Opts {
+    /// Address to bind to
+    #[clap(long, short = 'a', default_value = "0.0.0.0:22")]
+    address: String,
+
+    /// Address to listen to for metrics
+    #[clap(long, default_value = "0.0.0.0:9000")]
+    metrics_address: SocketAddr,
+
+    /// Format to use when emitting log events
+    #[clap(
+        long,
+        env = "LOG_FORMAT",
+        default_value = "full",
+        possible_values = &["compact", "full", "pretty", "json"]
+    )]
+    log_format: String,
+
+    /// Full path to the xanthous binary
+    #[clap(long, env = "XANTHOUS_BINARY_PATH")]
+    xanthous_binary_path: String,
+
+    /// Path to a file containing the ed25519 secret key for the server
+    #[clap(long, env = "SECRET_KEY_FILE")]
+    secret_key_file: PathBuf,
+
+    /// Level to log at
+    #[clap(long, env = "LOG_LEVEL", default_value = "info")]
+    log_level: String,
+}
+
+impl Opts {
+    async fn read_secret_key(&self) -> Result<KeyPair> {
+        let mut file = File::open(&self.secret_key_file)
+            .await
+            .context("Reading secret key file")?;
+        let mut secret_key = Vec::with_capacity(464);
+        file.read_to_end(&mut secret_key).await?;
+        Ok(decode_secret_key(str::from_utf8(&secret_key)?, None)?)
+    }
+
+    async fn ssh_server_config(&self) -> Result<server::Config> {
+        let key_pair = self.read_secret_key().await?;
+
+        Ok(server::Config {
+            server_id: "SSH-2.0-xanthous".to_owned(),
+            keys: vec![key_pair],
+            ..Default::default()
+        })
+    }
+
+    fn init_logging(&self) -> Result<()> {
+        let filter = EnvFilter::try_new(&self.log_level)?;
+        let s = tracing_subscriber::fmt().with_env_filter(filter);
+
+        match self.log_format.as_str() {
+            "compact" => s.compact().init(),
+            "full" => s.init(),
+            "pretty" => s.pretty().init(),
+            "json" => s.json().with_current_span(true).init(),
+            f => bail!("Invalid log format `{}`", f),
+        }
+
+        Ok(())
+    }
+}
+
+struct Handler {
+    address: SocketAddr,
+    xanthous_binary_path: &'static str,
+    username: Option<String>,
+    child: Option<ChildHandle>,
+}
+
+async fn run_child(
+    mut child: pty::Child,
+    mut server_handle: server::Handle,
+    channel_id: ChannelId,
+) -> Result<()> {
+    let mut buf = [0; 2048];
+    loop {
+        select! {
+            r = child.tty.read(&mut buf)  => {
+                let read_bytes = r?;
+                if read_bytes == 0 {
+                    info!("EOF received from process");
+                    let _ = server_handle.close(channel_id).await;
+                    return Ok(())
+                } else {
+                    trace!(?read_bytes, "read bytes from child");
+                    let _ = server_handle.data(channel_id, CryptoVec::from_slice(&buf[..read_bytes])).await;
+                }
+            }
+            status = WaitPid::new(child.pid) => {
+                match status {
+                    Ok(_status) => info!("Child exited"),
+                    Err(error) => error!(%error, "Child failed"),
+                }
+                let _ = server_handle.close(channel_id).await;
+                return Ok(())
+            }
+        }
+    }
+}
+
+impl Handler {
+    async fn spawn_shell(
+        &mut self,
+        mut handle: server::Handle,
+        channel_id: ChannelId,
+        term: String,
+        winsize: Winsize,
+    ) -> Result<()> {
+        let mut cmd = Command::new(self.xanthous_binary_path);
+        cmd.env("TERM", term);
+        if let Some(username) = &self.username {
+            cmd.args(["--name", username]);
+        }
+        cmd.arg("--disable-saving");
+
+        let child = pty::spawn(cmd, Some(winsize), None).await?;
+        info!(pid = %child.pid, "Spawned child");
+        increment_gauge!(RUNNING_PROCESSES, 1.0);
+        self.child = Some(child.handle().await?);
+        tokio::spawn(
+            async move {
+                let span = info_span!("child", pid = %child.pid);
+                if let Err(error) = run_child(child, handle.clone(), channel_id)
+                    .instrument(span.clone())
+                    .await
+                {
+                    span.in_scope(|| error!(%error, "Error running child"));
+                    let _ = handle.close(channel_id).await;
+                }
+                decrement_gauge!(RUNNING_PROCESSES, 1.0);
+            }
+            .in_current_span(),
+        );
+        Ok(())
+    }
+}
+
+#[allow(clippy::type_complexity)]
+impl server::Handler for Handler {
+    type Error = eyre::Error;
+    type FutureAuth = Ready<Result<(Self, Auth)>>;
+    type FutureUnit = Pin<Box<dyn Future<Output = Result<(Self, Session)>> + Send + 'static>>;
+    type FutureBool = Ready<Result<(Self, Session, bool)>>;
+
+    fn finished_auth(self, auth: Auth) -> Self::FutureAuth {
+        ready(Ok((self, auth)))
+    }
+
+    fn finished_bool(self, b: bool, session: Session) -> Self::FutureBool {
+        ready(Ok((self, session, b)))
+    }
+
+    fn finished(self, session: Session) -> Self::FutureUnit {
+        Box::pin(ready(Ok((self, session))))
+    }
+
+    fn auth_none(mut self, username: &str) -> Self::FutureAuth {
+        info!(%username, "Accepted new connection");
+        self.username = Some(username.to_owned());
+        self.finished_auth(Auth::Accept)
+    }
+
+    fn auth_password(mut self, username: &str, _password: &str) -> Self::FutureAuth {
+        info!(%username, "Accepted new connection");
+        self.username = Some(username.to_owned());
+        self.finished_auth(Auth::Accept)
+    }
+
+    fn auth_publickey(
+        mut self,
+        username: &str,
+        _: &thrussh_keys::key::PublicKey,
+    ) -> Self::FutureAuth {
+        info!(%username, "Accepted new connection");
+        self.username = Some(username.to_owned());
+        self.finished_auth(Auth::Accept)
+    }
+
+    fn pty_request(
+        mut self,
+        channel: thrussh::ChannelId,
+        term: &str,
+        col_width: u32,
+        row_height: u32,
+        pix_width: u32,
+        pix_height: u32,
+        modes: &[(thrussh::Pty, u32)],
+        session: Session,
+    ) -> Self::FutureUnit {
+        let term = term.to_owned();
+        let modes = modes.to_vec();
+        Box::pin(async move {
+            debug!(
+                %term,
+                %col_width,
+                %row_height,
+                %pix_width,
+                %pix_height,
+                ?modes,
+                "PTY Requested"
+            );
+
+            self.spawn_shell(
+                session.handle(),
+                channel,
+                term,
+                Winsize {
+                    ws_row: row_height as _,
+                    ws_col: col_width as _,
+                    ws_xpixel: pix_width as _,
+                    ws_ypixel: pix_height as _,
+                },
+            )
+            .await?;
+
+            Ok((self, session))
+        })
+    }
+
+    fn window_change_request(
+        mut self,
+        _channel: ChannelId,
+        col_width: u32,
+        row_height: u32,
+        pix_width: u32,
+        pix_height: u32,
+        session: Session,
+    ) -> Self::FutureUnit {
+        Box::pin(async move {
+            if let Some(child) = self.child.as_mut() {
+                trace!(%row_height, %col_width, "Window resize request received");
+                child
+                    .resize_window(Winsize {
+                        ws_row: row_height as _,
+                        ws_col: col_width as _,
+                        ws_xpixel: pix_width as _,
+                        ws_ypixel: pix_height as _,
+                    })
+                    .await?;
+            } else {
+                warn!("Resize request received without child process; ignoring");
+            }
+
+            Ok((self, session))
+        })
+    }
+
+    fn data(
+        mut self,
+        _channel: thrussh::ChannelId,
+        data: &[u8],
+        session: Session,
+    ) -> Self::FutureUnit {
+        trace!(data = %String::from_utf8_lossy(data), raw_data = ?data);
+        let data = data.to_owned();
+        Box::pin(async move {
+            if let Some(child) = self.child.as_mut() {
+                child.write_all(&data).await?;
+            } else {
+                warn!("Data received without child process; ignoring");
+            }
+
+            Ok((self, session))
+        })
+    }
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+    color_eyre::install()?;
+    let opts = Box::leak::<'static>(Box::new(Opts::parse()));
+    opts.init_logging()?;
+    PrometheusBuilder::new()
+        .listen_address(opts.metrics_address)
+        .install()?;
+    metrics::register();
+
+    let config = Arc::new(opts.ssh_server_config().await?);
+    info!(address = %opts.address, "Listening for new SSH connections");
+    let listener = TcpListener::bind(&opts.address).await?;
+
+    loop {
+        let (stream, address) = listener.accept().await?;
+        increment_counter!(CONNECTIONS_ACCEPTED);
+        increment_gauge!(ACTIVE_CONNECTIONS, 1.0);
+        let config = config.clone();
+        let handler = Handler {
+            xanthous_binary_path: &opts.xanthous_binary_path,
+            address,
+            username: None,
+            child: None,
+        };
+        tokio::spawn(async move {
+            let span = info_span!("client", address = %handler.address);
+            let start = Instant::now();
+            if let Err(error) = server::run_stream(config, stream, handler)
+                .instrument(span.clone())
+                .await
+            {
+                span.in_scope(|| error!(%error));
+            }
+            let duration = start.elapsed();
+            span.in_scope(|| info!(duration_ms = %duration.as_millis(), "Client disconnected"));
+            histogram!(CONNECTION_DURATION, duration);
+            decrement_gauge!(ACTIVE_CONNECTIONS, 1.0);
+        });
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use tempfile::NamedTempFile;
+
+    use super::*;
+
+    #[tokio::test]
+    async fn read_secret_key() {
+        use std::io::Write;
+
+        let mut file = NamedTempFile::new().unwrap();
+        file.write_all(
+            b"
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACAYz80xcK7jYxZMAl6apIHKRtB0Z2U78gG39c1QaIhgMwAAAJB9vxK9fb8S
+vQAAAAtzc2gtZWQyNTUxOQAAACAYz80xcK7jYxZMAl6apIHKRtB0Z2U78gG39c1QaIhgMw
+AAAEDNZ0d3lLNBGU6Im4JOpr490TOjm+cB7kMVXjVg3iCowBjPzTFwruNjFkwCXpqkgcpG
+0HRnZTvyAbf1zVBoiGAzAAAACHRlc3Qta2V5AQIDBAU=
+-----END OPENSSH PRIVATE KEY-----
+",
+        )
+        .unwrap();
+
+        let opts: Opts = Opts::parse_from(&[
+            "xanthous-server".as_ref(),
+            "--xanthous-binary-path".as_ref(),
+            "/bin/xanthous".as_ref(),
+            "--secret-key-file".as_ref(),
+            file.path().as_os_str(),
+        ]);
+        opts.read_secret_key().await.unwrap();
+    }
+}
diff --git a/users/aspen/xanthous/server/src/metrics.rs b/users/aspen/xanthous/server/src/metrics.rs
new file mode 100644
index 0000000000..6912cdd9c9
--- /dev/null
+++ b/users/aspen/xanthous/server/src/metrics.rs
@@ -0,0 +1,24 @@
+pub use ::metrics::*;
+
+pub mod reported {
+    /// Counter: Connections accepted on the TCP listener
+    pub const CONNECTIONS_ACCEPTED: &str = "ssh.connections.accepted";
+
+    /// Histogram: Connection duration
+    pub const CONNECTION_DURATION: &str = "ssh.connections.duration";
+
+    /// Gauge: Currently active connections
+    pub const ACTIVE_CONNECTIONS: &str = "ssh.connections.active";
+
+    /// Gauge: Currently running xanthous processes
+    pub const RUNNING_PROCESSES: &str = "ssh.child.processes";
+}
+
+pub fn register() {
+    use reported::*;
+
+    register_counter!(CONNECTIONS_ACCEPTED);
+    register_histogram!(CONNECTION_DURATION);
+    register_gauge!(ACTIVE_CONNECTIONS);
+    register_gauge!(RUNNING_PROCESSES);
+}
diff --git a/users/aspen/xanthous/server/src/pty.rs b/users/aspen/xanthous/server/src/pty.rs
new file mode 100644
index 0000000000..234ecd8f23
--- /dev/null
+++ b/users/aspen/xanthous/server/src/pty.rs
@@ -0,0 +1,172 @@
+use std::io::{self};
+use std::os::unix::prelude::{AsRawFd, CommandExt, FromRawFd};
+use std::pin::Pin;
+use std::process::{abort, Command};
+use std::task::{Context, Poll};
+
+use eyre::{bail, Result};
+use futures::Future;
+use nix::pty::{forkpty, Winsize};
+use nix::sys::termios::Termios;
+use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
+use nix::unistd::{ForkResult, Pid};
+use tokio::fs::File;
+use tokio::io::{AsyncRead, AsyncWrite};
+use tokio::signal::unix::{signal, Signal, SignalKind};
+use tokio::task::spawn_blocking;
+
+mod ioctl {
+    use super::Winsize;
+    use libc::TIOCSWINSZ;
+    use nix::ioctl_write_ptr_bad;
+
+    ioctl_write_ptr_bad!(tiocswinsz, TIOCSWINSZ, Winsize);
+}
+
+async fn asyncify<F, T>(f: F) -> Result<T>
+where
+    F: FnOnce() -> Result<T> + Send + 'static,
+    T: Send + 'static,
+{
+    match spawn_blocking(f).await {
+        Ok(res) => res,
+        Err(_) => bail!("background task failed",),
+    }
+}
+
+pub struct Child {
+    pub tty: File,
+    pub pid: Pid,
+}
+
+pub struct ChildHandle {
+    pub tty: File,
+}
+
+pub struct WaitPid {
+    pid: Pid,
+    signal: Signal,
+}
+
+impl WaitPid {
+    pub fn new(pid: Pid) -> Self {
+        Self {
+            pid,
+            signal: signal(SignalKind::child()).unwrap(),
+        }
+    }
+}
+
+impl Future for WaitPid {
+    type Output = nix::Result<WaitStatus>;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        let _ = self.signal.poll_recv(cx);
+        match waitpid(self.pid, Some(WaitPidFlag::WNOHANG)) {
+            Ok(WaitStatus::StillAlive) => Poll::Pending,
+            result => Poll::Ready(result),
+        }
+    }
+}
+
+impl Child {
+    pub async fn handle(&self) -> io::Result<ChildHandle> {
+        Ok(ChildHandle {
+            tty: self.tty.try_clone().await?,
+        })
+    }
+}
+
+impl ChildHandle {
+    pub async fn resize_window(&mut self, winsize: Winsize) -> Result<()> {
+        let fd = self.tty.as_raw_fd();
+        asyncify(move || unsafe {
+            ioctl::tiocswinsz(fd, &winsize as *const Winsize)?;
+            Ok(())
+        })
+        .await
+    }
+}
+
+pub async fn spawn(
+    mut cmd: Command,
+    winsize: Option<Winsize>,
+    termios: Option<Termios>,
+) -> Result<Child> {
+    asyncify(move || unsafe {
+        let res = forkpty(winsize.as_ref(), termios.as_ref())?;
+        match res.fork_result {
+            ForkResult::Parent { child } => Ok(Child {
+                pid: child,
+                tty: File::from_raw_fd(res.master),
+            }),
+            ForkResult::Child => {
+                cmd.exec();
+                abort();
+            }
+        }
+    })
+    .await
+}
+
+impl AsyncRead for Child {
+    fn poll_read(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &mut tokio::io::ReadBuf<'_>,
+    ) -> Poll<io::Result<()>> {
+        Pin::new(&mut self.tty).poll_read(cx, buf)
+    }
+}
+
+impl AsyncWrite for Child {
+    fn poll_write(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &[u8],
+    ) -> Poll<Result<usize, io::Error>> {
+        Pin::new(&mut self.tty).poll_write(cx, buf)
+    }
+
+    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
+        Pin::new(&mut self.tty).poll_flush(cx)
+    }
+
+    fn poll_shutdown(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+    ) -> Poll<Result<(), io::Error>> {
+        Pin::new(&mut self.tty).poll_shutdown(cx)
+    }
+}
+
+impl AsyncRead for ChildHandle {
+    fn poll_read(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &mut tokio::io::ReadBuf<'_>,
+    ) -> Poll<io::Result<()>> {
+        Pin::new(&mut self.tty).poll_read(cx, buf)
+    }
+}
+
+impl AsyncWrite for ChildHandle {
+    fn poll_write(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &[u8],
+    ) -> Poll<Result<usize, io::Error>> {
+        Pin::new(&mut self.tty).poll_write(cx, buf)
+    }
+
+    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
+        Pin::new(&mut self.tty).poll_flush(cx)
+    }
+
+    fn poll_shutdown(
+        mut self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+    ) -> Poll<Result<(), io::Error>> {
+        Pin::new(&mut self.tty).poll_shutdown(cx)
+    }
+}
diff --git a/users/grfn/xanthous/shell.nix b/users/aspen/xanthous/shell.nix
index 572ed211bc..2c41cb4aa8 100644
--- a/users/grfn/xanthous/shell.nix
+++ b/users/aspen/xanthous/shell.nix
@@ -1,16 +1,16 @@
 let
-  depot = import ../../../. {};
+  depot = import ../../../. { };
   inherit (depot) third_party;
   pkgs = third_party.nixpkgs;
 in
 
-(pkgs.haskellPackages.extend (pkgs.haskell.lib.packageSourceOverrides {
+(pkgs.haskell.packages.ghc8107.extend (pkgs.haskell.lib.packageSourceOverrides {
   xanthous = third_party.gitignoreSource ./.;
 })).shellFor {
-  packages = p: [p.xanthous];
+  packages = p: [ p.xanthous ];
   withHoogle = true;
   doBenchmark = true;
-  buildInputs = (with pkgs.haskellPackages; [
+  buildInputs = (with pkgs.haskell.packages.ghc8107; [
     cabal-install
     ghc-prof-flamegraph
     hp2pretty
diff --git a/users/grfn/xanthous/src/Data/Aeson/Generic/DerivingVia.hs b/users/aspen/xanthous/src/Data/Aeson/Generic/DerivingVia.hs
index e89fcd6211..e89fcd6211 100644
--- a/users/grfn/xanthous/src/Data/Aeson/Generic/DerivingVia.hs
+++ b/users/aspen/xanthous/src/Data/Aeson/Generic/DerivingVia.hs
diff --git a/users/grfn/xanthous/src/Xanthous/AI/Gormlak.hs b/users/aspen/xanthous/src/Xanthous/AI/Gormlak.hs
index 59be5383de..1f2b513ffe 100644
--- a/users/grfn/xanthous/src/Xanthous/AI/Gormlak.hs
+++ b/users/aspen/xanthous/src/Xanthous/AI/Gormlak.hs
@@ -26,8 +26,10 @@ import qualified Xanthous.Entities.Character as Character
 import qualified Xanthous.Entities.RawTypes as Raw
 import           Xanthous.Entities.RawTypes
                  ( CreatureType, HasLanguage(language), getLanguage
-                 , HasAttacks (attacks)
+                 , HasAttacks (attacks), creatureAttackMessage
                  )
+import           Xanthous.Entities.Common
+                 ( wielded, Inventory, wieldedItems, WieldedItem (WieldedItem) )
 import           Xanthous.Game.State
 import           Xanthous.Game.Lenses
                  ( entitiesCollision, collisionAt
@@ -36,7 +38,7 @@ import           Xanthous.Game.Lenses
                  )
 import           Xanthous.Data.EntityMap.Graphics (linesOfSight, canSee)
 import           Xanthous.Random
-import           Xanthous.Monad (say)
+import           Xanthous.Monad (say, message)
 import           Xanthous.Generators.Speech (word)
 import qualified Linear.Metric as Metric
 import qualified Xanthous.Messages as Messages
@@ -50,6 +52,7 @@ type IsCreature entity =
   ( HasVisionRadius entity
   , HasField "_hippocampus" entity entity Hippocampus Hippocampus
   , HasField "_creatureType" entity entity CreatureType CreatureType
+  , HasField "_inventory" entity entity Inventory Inventory
   , A.ToJSON entity
   )
 
@@ -87,21 +90,22 @@ stepGormlak ticks pe@(Positioned pos creature) = do
         else pure pe
 
   dest <- maybe (selectDestination pos creature) pure
-         $ creature ^. field @"_hippocampus" . destination
+         . mfilter (\(Destination p _) -> p /= pos)
+         $ creature ^. hippocampus . destination
   let progress' =
         dest ^. destinationProgress
-        + creatureType ^. Raw.speed . invertedRate |*| ticks
+        + creature ^. creatureType . Raw.speed . invertedRate |*| ticks
   if progress' < 1
     then pure
          $ pe'
-         & positioned . field @"_hippocampus" . destination
+         & positioned . hippocampus . destination
          ?~ (dest & destinationProgress .~ progress')
     else do
       let newPos = dest ^. destinationPosition
           remainingSpeed = progress' - 1
       newDest <- selectDestination newPos creature
                 <&> destinationProgress +~ remainingSpeed
-      let pe'' = pe' & positioned . field @"_hippocampus" . destination ?~ newDest
+      let pe'' = pe' & positioned . hippocampus . destination ?~ newDest
       collisionAt newPos >>= \case
         Nothing -> pure $ pe'' & position .~ newPos
         Just Stop -> pure pe''
@@ -110,25 +114,36 @@ stepGormlak ticks pe@(Positioned pos creature) = do
           when (any (entityIs @Character) ents) attackCharacter
           pure pe'
   where
-    creatureType = creature ^. field @"_creatureType"
     vision = visionRadius creature
     attackCharacter = do
-      attack <- choose $ creatureType ^. attacks
-      attackDescription <- Messages.render (attack ^. Raw.description)
-                          $ object []
-      say ["combat", "creatureAttack"]
-        $ object [ "creature" A..= creature
-                 , "attackDescription" A..= attackDescription
-                 ]
-      character %= Character.damage (attack ^. Raw.damage)
-
-    yellAtCharacter = for_ (creature ^. field @"_creatureType" . language)
+      dmg <- case creature ^? inventory . wielded . wieldedItems of
+        Just (WieldedItem item wi) -> do
+          let msg = fromMaybe
+                    (Messages.lookup ["combat", "creatureAttack", "genericWeapon"])
+                    $ wi ^. creatureAttackMessage
+          message msg $ object [ "creature" A..= creature
+                               , "item" A..= item
+                               ]
+          pure $ wi ^. Raw.damage
+        Nothing -> do
+          attack <- choose $ creature ^. creatureType . attacks
+          attackDescription <- Messages.render (attack ^. Raw.description)
+                              $ object []
+          say ["combat", "creatureAttack", "natural"]
+              $ object [ "creature" A..= creature
+                       , "attackDescription" A..= attackDescription
+                       ]
+          pure $ attack ^. Raw.damage
+
+      character %= Character.damage dmg
+
+    yellAtCharacter = for_ (creature ^. creatureType . language)
       $ \lang -> do
           utterance <- fmap (<> "!") . word $ getLanguage lang
           creatureSaysText pe utterance
 
     creatureGreeted :: Lens' entity Bool
-    creatureGreeted = field @"_hippocampus" . greetedCharacter
+    creatureGreeted = hippocampus . greetedCharacter
 
 
 -- | A creature sends some text
@@ -165,6 +180,15 @@ instance (IsCreature entity) => Brain (GormlakBrain entity) where
     . fmap _unGormlakBrain
   entityCanMove = const True
 
+hippocampus :: HasField "_hippocampus" s t a b => Lens s t a b
+hippocampus = field @"_hippocampus"
+
+creatureType :: HasField "_creatureType" s t a b => Lens s t a b
+creatureType = field @"_creatureType"
+
+inventory :: HasField "_inventory" s t a b => Lens s t a b
+inventory = field @"_inventory"
+
 --------------------------------------------------------------------------------
 
 -- instance Brain Creature where
diff --git a/users/grfn/xanthous/src/Xanthous/App.hs b/users/aspen/xanthous/src/Xanthous/App.hs
index 8c5cffc9f8..426230cdc2 100644
--- a/users/grfn/xanthous/src/Xanthous/App.hs
+++ b/users/aspen/xanthous/src/Xanthous/App.hs
@@ -34,7 +34,7 @@ import           Xanthous.Data
                  , position
                  , Position
                  , (|*|)
-                 , Tiles(..)
+                 , Tiles(..), Hitpoints, fromScalar
                  )
 import           Xanthous.Data.App (ResourceName, Panel(..), AppEvent(..))
 import qualified Xanthous.Data.EntityMap as EntityMap
@@ -45,15 +45,25 @@ import           Xanthous.Game
 import           Xanthous.Game.State
 import           Xanthous.Game.Env
 import           Xanthous.Game.Draw (drawGame)
-import           Xanthous.Game.Prompt
+import           Xanthous.Game.Prompt hiding (Fire)
 import qualified Xanthous.Messages as Messages
 import           Xanthous.Random
-import           Xanthous.Util (removeVectorIndex)
+import           Xanthous.Util (removeVectorIndex, useListOf)
 import           Xanthous.Util.Inflection (toSentence)
+import           Xanthous.Physics (throwDistance, bluntThrowDamage)
+import           Xanthous.Data.EntityMap.Graphics (lineOfSight)
+import           Xanthous.Data.EntityMap (EntityID)
 --------------------------------------------------------------------------------
+import           Xanthous.Entities.Common
+                 ( InventoryPosition, describeInventoryPosition, backpack
+                 , wieldableItem, wieldedItems, wielded, itemsWithPosition
+                 , removeItemFromPosition, asWieldedItem
+                 , wieldedItem, items, Hand (..), describeHand, wieldInHand
+                 , WieldedItem, Wielded (..)
+                 )
 import qualified Xanthous.Entities.Character as Character
 import           Xanthous.Entities.Character hiding (pickUpItem)
-import           Xanthous.Entities.Item (Item)
+import           Xanthous.Entities.Item (Item, weight)
 import qualified Xanthous.Entities.Item as Item
 import           Xanthous.Entities.Creature (Creature)
 import qualified Xanthous.Entities.Creature as Creature
@@ -70,7 +80,7 @@ import qualified Xanthous.Generators.Level.Dungeon as Dungeon
 
 type App = Brick.App GameState AppEvent ResourceName
 
-data RunType = NewGame | LoadGame
+data RunType = NewGame | LoadGame FilePath
   deriving stock (Eq)
 
 makeApp :: GameEnv -> RunType -> IO App
@@ -80,7 +90,7 @@ makeApp env rt = pure $ Brick.App
   , appHandleEvent = \game event -> runAppM (handleEvent event) env game
   , appStartEvent = case rt of
       NewGame -> runAppM (startEvent >> get) env
-      LoadGame -> pure
+      LoadGame save -> pure . (savefile ?~ save)
   , appAttrMap = const $ attrMap defAttr []
   }
 
@@ -125,6 +135,9 @@ handleNoPromptEvent _ = continue
 
 handleCommand :: Command -> AppM (Next GameState)
 handleCommand Quit = confirm_ ["quit", "confirm"] (liftIO exitSuccess) >> continue
+
+handleCommand Help = showPanel HelpPanel >> continue
+
 handleCommand (Move dir) = do
   newPos <- uses characterPosition $ move dir
   collisionAt newPos >>= \case
@@ -283,37 +296,124 @@ handleCommand DescribeInventory = do
 
 
 handleCommand Wield = do
-  takeItemFromInventory_ ["wield", "menu"] Cancellable asWieldedItem
-    (say_ ["wield", "nothing"])
-    $ \(MenuResult item) -> do
-      prevItems <- character . inventory . wielded <<.= inRightHand item
+  hs <- use $ character . inventory . wielded
+  selectItem $ \(MenuResult (invPos, (item :: WieldedItem))) -> do
+    selectHand hs $ \(MenuResult hand) -> do
+      character . inventory
+        %= removeItemFromPosition invPos (asWieldedItem # item)
+      prevItems <- character . inventory . wielded %%= wieldInHand hand item
       character . inventory . backpack
-        <>= fromList (prevItems ^.. wieldedItems . wieldedItem)
-      say ["wield", "wielded"] item
+        <>= fromList (map (view wieldedItem) prevItems)
+      say ["wield", "wielded"] $ object [ "item" A..= item
+                                        , "hand" A..= describeHand hand
+                                        ]
   continue
-
-handleCommand Save = do
-  -- TODO default save locations / config file?
-  prompt_ @'StringPrompt ["save", "location"] Cancellable
-    $ \(StringResult filename) -> do
-       exists <- liftIO . doesFileExist $ unpack filename
-       if exists
-       then confirm ["save", "overwrite"] (object ["filename" A..= filename])
-            $ doSave filename
-       else doSave filename
+  where
+    selectItem =
+      selectItemFromInventory_ ["wield", "menu"] Cancellable asWieldedItem
+        (say_ ["wield", "nothing"])
+    selectHand hs = menu_ ["wield", "hand"] Cancellable $ handsMenu hs
+    itemsInHand (Hands i _) LeftHand       = toList i
+    itemsInHand (DoubleHanded _) LeftHand  = []
+    itemsInHand (Hands _ i) RightHand      = toList i
+    itemsInHand (DoubleHanded _) RightHand = []
+    itemsInHand (Hands l r) BothHands      = toList l <> toList r
+    itemsInHand (DoubleHanded i) BothHands = [i]
+    describeItems [] = ""
+    describeItems is
+      = " (currently holding "
+      <> (intercalate " and" $ map (view $ wieldedItem . to description) is)
+      <> ")"
+    handsMenu hs = mapFromList
+      . map (second $ \hand ->
+                MenuOption
+                ( describeHand hand
+                <> describeItems (itemsInHand hs hand)
+                )
+                hand
+            )
+      $ [ ('l', LeftHand)
+        , ('r', RightHand)
+        , ('b', BothHands)
+        ]
+
+handleCommand Fire = do
+  selectItemFromInventory_ ["fire", "menu"] Cancellable id
+    (say_ ["fire", "nothing"])
+    $ \(MenuResult (invPos, item)) ->
+      let wt = weight item
+          dist = throwDistance wt
+          dam = bluntThrowDamage wt
+      in if dist < fromScalar 1
+         then say_ ["fire", "zeroRange"]
+         else firePrompt_ ["fire", "target"] Cancellable dist $
+          \(FireResult targetPos) -> do
+              charPos <- use characterPosition
+              mTarget <- uses entities $ firstEnemy . lineOfSight charPos targetPos
+              case mTarget of
+                Just target -> do
+                  creature' <- damageCreature target dam
+                  unless (Creature.isDead creature') $
+                    let msgPath = ["fire", "fired"] <> [if dam == 0
+                                                        then "noDamage"
+                                                        else "someDamage"]
+                    in say msgPath $ object [ "item" A..= item
+                                            , "creature" A..= creature'
+                                            ]
+                Nothing ->
+                  say ["fire", "fired", "noTarget"] $ object [ "item" A..= item ]
+              character . inventory %= removeItemFromPosition invPos item
+              entities . EntityMap.atPosition targetPos %= (SomeEntity item <|)
+              stepGame -- TODO(grfn): should this be based on distance?
   continue
   where
-    doSave filename = do
-      src <- gets saveGame
-      lift . liftIO $ do
-        writeFile (unpack filename) $ toStrict src
-        exitSuccess
+    firstEnemy
+      :: [(Position, Vector (EntityID, SomeEntity))]
+      -> Maybe (EntityID, Creature)
+    firstEnemy los =
+      let enemies = los >>= \(_, es) -> toList $ headMay es
+      in enemies ^? folded . below _SomeEntity
+
+handleCommand Save =
+  view (config . disableSaving) >>= \case
+    True -> say_ ["save", "disabled"] >> continue
+    False -> do
+      -- TODO default save locations / config file?
+      use savefile >>= \case
+        Just filepath ->
+          stringPromptWithDefault_
+            ["save", "location"]
+            Cancellable
+            (pack filepath)
+            promptCallback
+        Nothing -> prompt_ @'StringPrompt ["save", "location"] Cancellable promptCallback
+      continue
+      where
+        promptCallback :: PromptResult 'StringPrompt -> AppM ()
+        promptCallback (StringResult filename) = do
+          sf <- use savefile
+          exists <- liftIO . doesFileExist $ unpack filename
+          if exists && sf /= Just (unpack filename)
+          then confirm ["save", "overwrite"] (object ["filename" A..= filename])
+              $ doSave filename
+          else doSave filename
+        doSave filename = do
+          src <- gets saveGame
+          lift . liftIO $ do
+            writeFile (unpack filename) $ toStrict src
+            exitSuccess
 
 handleCommand GoUp = do
   hasStairs <- uses entitiesAtCharacter $ elem (SomeEntity UpStaircase)
   if hasStairs
   then uses levels prevLevel >>= \case
-    Just levs' -> levels .= levs'
+    Just levs' -> do
+      cEID <- use characterEntityID
+      pCharacter <- entities . at cEID <<.= Nothing
+      levels .= levs'
+      charPos <- use characterPosition
+      entities . at cEID .= pCharacter
+      characterPosition .= charPos
     Nothing ->
       -- TODO in nethack, this leaves the game. Maybe something similar here?
       say_ ["cant", "goUp"]
@@ -342,7 +442,10 @@ handleCommand (StartAutoMove dir) = do
   runAutocommand $ AutoMove dir
   continue
 
-handleCommand Rest = runAutocommand AutoRest >> continue
+handleCommand Rest = do
+  say_ ["autocommands", "resting"]
+  runAutocommand AutoRest
+  continue
 
 --
 
@@ -361,30 +464,27 @@ attackAt pos =
       menu_ ["combat", "menu"] Cancellable (entityMenu_ creatures)
       $ \(MenuResult creature) -> attackCreature creature
  where
-  attackCreature (creatureID, creature) = do
+  attackCreature creature = do
     charDamage <- uses character characterDamage
-    let creature' = Creature.damage charDamage creature
-        msgParams = object ["creature" A..= creature']
-    if Creature.isDead creature'
-      then do
-        say ["combat", "killed"] msgParams
-        entities . at creatureID .= Nothing
-      else do
-        msg <- uses character getAttackMessage
-        message msg msgParams
-        entities . ix creatureID . positioned .= SomeEntity creature'
-
+    creature' <- damageCreature creature charDamage
+    unless (Creature.isDead creature') $ writeAttackMessage creature'
     whenM (uses character $ isNothing . weapon) handleFists
-
-    stepGame -- TODO
+    stepGame
   weapon chr = chr ^? inventory . wielded . wieldedItems . wieldableItem
-  getAttackMessage chr =
-    case weapon chr of
-      Just wi ->
-        fromMaybe (Messages.lookup ["combat", "hit", "generic"])
-        $ wi ^. attackMessage
-      Nothing ->
-        Messages.lookup ["combat", "hit", "fists"]
+  writeAttackMessage creature = do
+    let params = object ["creature" A..= creature]
+    attackMessages <- uses character getAttackMessages
+    msg <- intercalate " and " <$> for attackMessages (`Messages.render` params)
+    writeMessage $ "You " <> msg
+  getAttackMessages chr =
+    case chr ^.. inventory . wielded . wieldedItems . wieldableItem of
+      [] -> [Messages.lookup ["combat", "hit", "fists"]]
+      is ->
+        is
+        <&> \wi ->
+              fromMaybe (Messages.lookup ["combat", "hit", "generic"])
+              $ wi ^. attackMessage
+
 
   handleFists = do
     damageChance <- use $ character . body . knuckles . to fistDamageChance
@@ -396,6 +496,29 @@ attackAt pos =
       character %= Character.damage damageAmount
       character . body . knuckles %= damageKnuckles
 
+damageCreature :: (EntityID, Creature) -> Hitpoints -> AppM Creature
+damageCreature (creatureID, creature) dam = do
+  let creature' = Creature.damage dam creature
+      msgParams = object ["creature" A..= creature']
+  if Creature.isDead creature'
+    then do
+      say ["combat", "killed"] msgParams
+      floorItems <- useListOf
+                   $ entities
+                   . ix creatureID
+                   . positioned
+                   . _SomeEntity @Creature
+                   . inventory
+                   . items
+      mCreaturePos <- preuse $ entities . ix creatureID . position
+      entities . at creatureID .= Nothing
+      for_ mCreaturePos $ \creaturePos ->
+        entities . EntityMap.atPosition creaturePos
+          %= (<> fromList (SomeEntity <$> floorItems))
+    else entities . ix creatureID . positioned .= SomeEntity creature'
+  pure creature'
+
+
 entityMenu_
   :: (Comonad w, Entity entity)
   => [w entity]
@@ -504,14 +627,16 @@ showPanel panel = do
 --------------------------------------------------------------------------------
 
 genLevel
-  :: Int -- ^ level number
+  :: Word -- ^ Level number, starting at 0
   -> AppM Level
-genLevel _num = do
+genLevel num = do
   let dims = Dimensions 80 80
   generator <- choose $ CaveAutomata :| [Dungeon]
-  level <- case generator of
-    CaveAutomata -> generateLevel SCaveAutomata CaveAutomata.defaultParams dims
-    Dungeon -> generateLevel SDungeon Dungeon.defaultParams dims
+  let
+    doGen = case generator of
+      CaveAutomata -> generateLevel SCaveAutomata CaveAutomata.defaultParams
+      Dungeon -> generateLevel SDungeon Dungeon.defaultParams
+  level <- doGen dims num
   pure $!! level
 
 levelToGameLevel :: Level -> GameLevel
diff --git a/users/grfn/xanthous/src/Xanthous/App/Autocommands.hs b/users/aspen/xanthous/src/Xanthous/App/Autocommands.hs
index 5d4db1a474..5d4db1a474 100644
--- a/users/grfn/xanthous/src/Xanthous/App/Autocommands.hs
+++ b/users/aspen/xanthous/src/Xanthous/App/Autocommands.hs
diff --git a/users/grfn/xanthous/src/Xanthous/App/Common.hs b/users/aspen/xanthous/src/Xanthous/App/Common.hs
index 69ba6f0e05..69ba6f0e05 100644
--- a/users/grfn/xanthous/src/Xanthous/App/Common.hs
+++ b/users/aspen/xanthous/src/Xanthous/App/Common.hs
diff --git a/users/grfn/xanthous/src/Xanthous/App/Prompt.hs b/users/aspen/xanthous/src/Xanthous/App/Prompt.hs
index 9b5a3bf24f..799281a1c2 100644
--- a/users/grfn/xanthous/src/Xanthous/App/Prompt.hs
+++ b/users/aspen/xanthous/src/Xanthous/App/Prompt.hs
@@ -5,10 +5,13 @@ module Xanthous.App.Prompt
   , clearPrompt
   , prompt
   , prompt_
+  , stringPromptWithDefault
+  , stringPromptWithDefault_
   , confirm_
   , confirm
   , menu
   , menu_
+  , firePrompt_
   ) where
 --------------------------------------------------------------------------------
 import           Xanthous.Prelude
@@ -17,15 +20,19 @@ import           Brick (BrickEvent(..), Next)
 import           Brick.Widgets.Edit (handleEditorEvent)
 import           Data.Aeson (ToJSON, object)
 import           Graphics.Vty.Input.Events (Event(EvKey), Key(..))
-import           GHC.TypeLits (ErrorMessage(..))
 --------------------------------------------------------------------------------
 import           Xanthous.App.Common
-import           Xanthous.Data (move)
+import           Xanthous.Data (move, Tiles, Position, positioned, _Position)
+import qualified Xanthous.Data as Data
 import           Xanthous.Command (directionFromChar)
 import           Xanthous.Data.App (ResourceName, AppEvent)
 import           Xanthous.Game.Prompt
 import           Xanthous.Game.State
 import qualified Xanthous.Messages as Messages
+import qualified Xanthous.Data.EntityMap as EntityMap
+import           Xanthous.Entities.Creature (creatureType, Creature)
+import           Xanthous.Entities.RawTypes (hostile)
+import qualified Linear.Metric as Metric
 --------------------------------------------------------------------------------
 
 handlePromptEvent
@@ -77,6 +84,17 @@ handlePromptEvent
 handlePromptEvent _ (Prompt _ SPointOnMap _ _ _) _ = continue
 
 handlePromptEvent
+  msg
+  (Prompt c SFire (FirePromptState pos) pri@(origin, range) cb)
+  (VtyEvent (EvKey (KChar (directionFromChar -> Just dir)) []))
+  = do
+  let pos' = move dir pos
+      prompt' = Prompt c SFire (FirePromptState pos') pri cb
+  when (Data.distance origin pos' <= range) $
+    promptState .= WaitingPrompt msg prompt'
+  continue
+
+handlePromptEvent
   _
   (Prompt Cancellable _ _ _ _)
   (VtyEvent (EvKey (KChar 'q') []))
@@ -86,19 +104,15 @@ handlePromptEvent _ _ _ = continue
 clearPrompt :: AppM ()
 clearPrompt = promptState .= NoPrompt
 
-class NotMenu (pt :: PromptType)
-instance NotMenu 'StringPrompt
-instance NotMenu 'Confirm
-instance NotMenu 'DirectionPrompt
-instance NotMenu 'PointOnMap
-instance NotMenu 'Continue
-instance TypeError ('Text "Cannot use `prompt` or `prompt_` for menu prompts"
-                    ':$$: 'Text "Use `menu` or `menu_` instead")
-         => NotMenu ('Menu a)
+type PromptParams :: PromptType -> Type
+type family PromptParams pt where
+  PromptParams ('Menu a) = Map Char (MenuOption a) -- Menu items
+  PromptParams 'Fire     = Tiles -- Range
+  PromptParams _         = ()
 
 prompt
   :: forall (pt :: PromptType) (params :: Type).
-    (ToJSON params, SingPromptType pt, NotMenu pt)
+    (ToJSON params, SingPromptType pt, PromptParams pt ~ ())
   => [Text]                     -- ^ Message key
   -> params                     -- ^ Message params
   -> PromptCancellable
@@ -107,26 +121,46 @@ prompt
 prompt msgPath params cancellable cb = do
   let pt = singPromptType @pt
   msg <- Messages.message msgPath params
-  p <- case pt of
+  mp :: Maybe (Prompt AppM) <- case pt of
     SPointOnMap -> do
       charPos <- use characterPosition
-      pure $ mkPointOnMapPrompt cancellable charPos cb
-    SStringPrompt -> pure $ mkPrompt cancellable pt cb
-    SConfirm -> pure $ mkPrompt cancellable pt cb
-    SDirectionPrompt -> pure $ mkPrompt cancellable pt cb
-    SContinue -> pure $ mkPrompt cancellable pt cb
-    SMenu -> error "unreachable"
-  promptState .= WaitingPrompt msg p
+      pure . Just $ mkPointOnMapPrompt cancellable charPos cb
+    SStringPrompt -> pure . Just $ mkStringPrompt cancellable cb
+    SConfirm -> pure . Just $ mkPrompt cancellable pt cb
+    SDirectionPrompt -> pure . Just $ mkPrompt cancellable pt cb
+    SContinue -> pure . Just $ mkPrompt cancellable pt cb
+  for_ mp $ \p -> promptState .= WaitingPrompt msg p
 
 prompt_
   :: forall (pt :: PromptType).
-    (SingPromptType pt, NotMenu pt)
+    (SingPromptType pt, PromptParams pt ~ ())
   => [Text] -- ^ Message key
   -> PromptCancellable
   -> (PromptResult pt -> AppM ()) -- ^ Prompt promise handler
   -> AppM ()
 prompt_ msg = prompt msg $ object []
 
+stringPromptWithDefault
+  :: forall (params :: Type). (ToJSON params)
+  => [Text]                                -- ^ Message key
+  -> params                                -- ^ Message params
+  -> PromptCancellable
+  -> Text                                  -- ^ Prompt default
+  -> (PromptResult 'StringPrompt -> AppM ()) -- ^ Prompt promise handler
+  -> AppM ()
+stringPromptWithDefault msgPath params cancellable def cb = do
+  msg <- Messages.message msgPath params
+  let p = mkStringPromptWithDefault cancellable def cb
+  promptState .= WaitingPrompt msg p
+
+stringPromptWithDefault_
+  :: [Text]                                -- ^ Message key
+  -> PromptCancellable
+  -> Text                                  -- ^ Prompt default
+  -> (PromptResult 'StringPrompt -> AppM ()) -- ^ Prompt promise handler
+  -> AppM ()
+stringPromptWithDefault_ msg = stringPromptWithDefault msg $ object []
+
 confirm
   :: ToJSON params
   => [Text] -- ^ Message key
@@ -159,3 +193,36 @@ menu_ :: forall (a :: Type).
       -> (PromptResult ('Menu a) -> AppM ()) -- ^ Menu promise handler
       -> AppM ()
 menu_ msgPath = menu msgPath $ object []
+
+firePrompt_
+  :: [Text]                        -- ^ Message key
+  -> PromptCancellable
+  -> Tiles                         -- ^ Range
+  -> (PromptResult 'Fire -> AppM ()) -- ^ Promise handler
+  -> AppM ()
+firePrompt_ msgPath cancellable range cb = do
+  msg <- Messages.message msgPath $ object []
+  initialPos <- maybe (use characterPosition) pure =<< nearestEnemyPosition
+  let p = mkFirePrompt cancellable initialPos range cb
+  promptState .= WaitingPrompt msg p
+
+-- | Returns the position of the nearest visible hostile creature, if any
+nearestEnemyPosition :: AppM (Maybe Position)
+nearestEnemyPosition = do
+  charPos <- use characterPosition
+  em <- use entities
+  ps <- characterVisiblePositions
+  let candidates = toList ps >>= \p ->
+        let ents = EntityMap.atPositionWithIDs p em
+        in ents
+           ^.. folded
+           . _2
+           . positioned
+           . _SomeEntity @Creature
+           . creatureType
+           . filtered (view hostile)
+           . to (const (distance charPos p, p))
+  pure . headMay . fmap snd $ sortOn fst candidates
+  where
+    distance :: Position -> Position -> Double
+    distance = Metric.distance `on` (fmap fromIntegral . view _Position)
diff --git a/users/grfn/xanthous/src/Xanthous/App/Time.hs b/users/aspen/xanthous/src/Xanthous/App/Time.hs
index cca352858d..cca352858d 100644
--- a/users/grfn/xanthous/src/Xanthous/App/Time.hs
+++ b/users/aspen/xanthous/src/Xanthous/App/Time.hs
diff --git a/users/aspen/xanthous/src/Xanthous/Command.hs b/users/aspen/xanthous/src/Xanthous/Command.hs
new file mode 100644
index 0000000000..6e6274a02c
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Command.hs
@@ -0,0 +1,145 @@
+{-# LANGUAGE TemplateHaskell #-}
+--------------------------------------------------------------------------------
+module Xanthous.Command
+  ( -- * Commands
+    Command(..)
+  , commandIsHidden
+    -- * Keybindings
+  , Keybinding(..)
+  , keybindings
+  , commands
+  , commandFromKey
+  , directionFromChar
+  ) where
+--------------------------------------------------------------------------------
+import Xanthous.Prelude hiding (Left, Right, Down, try)
+--------------------------------------------------------------------------------
+import           Graphics.Vty.Input (Key(..), Modifier(..))
+import qualified Data.Char as Char
+import           Data.Aeson (FromJSON (parseJSON), FromJSONKey, FromJSONKeyFunction (FromJSONKeyTextParser))
+import qualified Data.Aeson as A
+import           Data.Aeson.Generic.DerivingVia
+import           Text.Megaparsec (Parsec, errorBundlePretty, parse, eof, try)
+import           Text.Megaparsec.Char (string', char', printChar)
+import           Data.FileEmbed (embedFile)
+import qualified Data.Yaml as Yaml
+import           Test.QuickCheck.Arbitrary
+import           Data.Aeson.Types (Parser)
+--------------------------------------------------------------------------------
+import           Xanthous.Data (Direction(..))
+import           Xanthous.Util.QuickCheck (GenericArbitrary(..))
+--------------------------------------------------------------------------------
+
+data Command
+  = Quit
+  | Help
+  | Move !Direction
+  | StartAutoMove !Direction
+  | PreviousMessage
+  | PickUp
+  | Drop
+  | Open
+  | Close
+  | Wait
+  | Eat
+  | Look
+  | Save
+  | Read
+  | ShowInventory
+  | DescribeInventory
+  | Wield
+  | Fire
+  | GoUp
+  | GoDown
+  | Rest
+
+    -- | TODO replace with `:` commands
+  | ToggleRevealAll
+  deriving stock (Show, Eq, Generic)
+  deriving anyclass (Hashable, NFData)
+  deriving Arbitrary via GenericArbitrary Command
+  deriving (FromJSON)
+       via WithOptions '[ SumEnc UntaggedVal ]
+           Command
+
+-- | Should the command be hidden from the help menu?
+--
+-- Note that this is true for both debug commands and movement commands, as the
+-- latter is documented non-automatically
+commandIsHidden :: Command -> Bool
+commandIsHidden (Move _) = True
+commandIsHidden (StartAutoMove _) = True
+commandIsHidden ToggleRevealAll = True
+commandIsHidden _ = False
+
+--------------------------------------------------------------------------------
+
+data Keybinding = Keybinding !Key ![Modifier]
+  deriving stock (Show, Eq, Generic)
+  deriving anyclass (Hashable, NFData)
+
+parseKeybindingFromText :: Text -> Parser Keybinding
+parseKeybindingFromText
+  = either (fail . errorBundlePretty) pure
+  . parse keybinding "<JSON>"
+  where
+    key :: Parsec Void Text Key
+    key = KUp <$ string' "<up>"
+      <|> KDown <$ string' "<down>"
+      <|> KLeft <$ string' "<left>"
+      <|> KRight <$ string' "<right>"
+      <|> KChar <$> printChar
+
+    modifier :: Parsec Void Text Modifier
+    modifier = modf <* char' '-'
+      where
+        modf = MAlt <$ char' 'a'
+          <|> MMeta <$ char' 'm'
+          <|> MCtrl  <$ char' 'c'
+          <|> MShift  <$ char' 's'
+
+    keybinding :: Parsec Void Text Keybinding
+    keybinding = do
+      mods <- many (try modifier)
+      k <- key
+      eof
+      pure $ Keybinding k mods
+
+instance FromJSON Keybinding where
+  parseJSON = A.withText "Keybinding" parseKeybindingFromText
+
+instance FromJSONKey Keybinding where
+  fromJSONKey = FromJSONKeyTextParser parseKeybindingFromText
+
+rawKeybindings :: ByteString
+rawKeybindings = $(embedFile "src/Xanthous/keybindings.yaml")
+
+keybindings :: HashMap Keybinding Command
+keybindings = either (error . Yaml.prettyPrintParseException) id
+  $ Yaml.decodeEither' rawKeybindings
+
+commands :: HashMap Command Keybinding
+commands = mapFromList . map swap . itoList $ keybindings
+
+commandFromKey :: Key -> [Modifier] -> Maybe Command
+commandFromKey (KChar (directionFromChar -> Just dir)) [] = Just $ Move dir
+commandFromKey (KChar c) []
+  | Char.isUpper c
+  , Just dir <- directionFromChar $ Char.toLower c
+  = Just $ StartAutoMove dir
+commandFromKey k mods = keybindings ^. at keybinding
+  where keybinding = Keybinding k mods
+
+--------------------------------------------------------------------------------
+
+directionFromChar :: Char -> Maybe Direction
+directionFromChar 'h' = Just Left
+directionFromChar 'j' = Just Down
+directionFromChar 'k' = Just Up
+directionFromChar 'l' = Just Right
+directionFromChar 'y' = Just UpLeft
+directionFromChar 'u' = Just UpRight
+directionFromChar 'b' = Just DownLeft
+directionFromChar 'n' = Just DownRight
+directionFromChar '.' = Just Here
+directionFromChar _   = Nothing
diff --git a/users/grfn/xanthous/src/Xanthous/Data.hs b/users/aspen/xanthous/src/Xanthous/Data.hs
index 9b3c35c545..703955206a 100644
--- a/users/grfn/xanthous/src/Xanthous/Data.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data.hs
@@ -8,10 +8,9 @@
 {-# LANGUAGE DuplicateRecordFields  #-}
 {-# LANGUAGE QuantifiedConstraints  #-}
 {-# LANGUAGE UndecidableInstances   #-}
+{-# LANGUAGE AllowAmbiguousTypes    #-}
 --------------------------------------------------------------------------------
--- | Common data types for Xanthous
---------------------------------------------------------------------------------
-{-# LANGUAGE AllowAmbiguousTypes #-}
+-- | Common data types for Xanthous ------------------------------------------------------------------------------
 module Xanthous.Data
   ( Opposite(..)
 
@@ -34,6 +33,7 @@ module Xanthous.Data
   , diffPositions
   , stepTowards
   , isUnit
+  , distance
 
     -- * Boxes
   , Box(..)
@@ -47,20 +47,29 @@ module Xanthous.Data
   , boxEdge
   , module Linear.V2
 
-    -- *
+    -- * Unit math
+  , Scalar(..)
   , Per(..)
   , invertRate
   , invertedRate
+  , (|+|)
   , (|*|)
+  , (|/|)
+  , (:+:)
+  , (:*:)
+  , (:/:)
+  , (:**:)(..)
   , Ticks(..)
   , Tiles(..)
   , TicksPerTile
   , TilesPerTick
   , timesTiles
   , Square(..)
+  , squared
   , Cubic(..)
   , Grams
   , Meters
+  , Uno(..)
   , Unit(..)
   , UnitSymbol(..)
 
@@ -122,9 +131,9 @@ import           Data.Coerce
 import           Data.Proxy (Proxy(Proxy))
 --------------------------------------------------------------------------------
 import           Xanthous.Util (EqEqProp(..), EqProp, between)
-import           Xanthous.Util.QuickCheck (GenericArbitrary(..))
 import           Xanthous.Orphans ()
 import           Xanthous.Util.Graphics
+import qualified Linear.Metric as Metric
 --------------------------------------------------------------------------------
 
 -- | opposite โˆ˜ opposite โ‰ก id
@@ -182,7 +191,7 @@ y = lens (\(Position _ yy) -> yy) (\(Position xx _) yy -> Position xx yy)
 
 type Position = Position' Int
 
-instance Arbitrary a => Arbitrary (Position' a) where
+instance (Arbitrary a) => Arbitrary (Position' a) where
   arbitrary = genericArbitrary
   shrink (Position px py) = Position <$> shrink px <*> shrink py
 
@@ -246,7 +255,7 @@ loc = iso hither yon
 _Position :: Iso' (Position' a) (V2 a)
 _Position = iso hither yon
   where
-    hither (Position px py) = (V2 px py)
+    hither (Position px py) = V2 px py
     yon (V2 lx ly) = Position lx ly
 
 positionFromPair :: (Num a, Integral i, Integral j) => (i, j) -> Position' a
@@ -304,7 +313,8 @@ data Direction where
   Here      :: Direction
   deriving stock (Show, Eq, Ord, Generic)
   deriving anyclass (CoArbitrary, Function, NFData, ToJSON, FromJSON, Hashable)
-  deriving Arbitrary via GenericArbitrary Direction
+
+deriving via (GenericArbitrary Direction) instance Arbitrary Direction
 
 instance Opposite Direction where
   opposite Up        = Down
@@ -423,7 +433,8 @@ data Neighbors a = Neighbors
   }
   deriving stock (Show, Eq, Ord, Functor, Foldable, Traversable, Generic)
   deriving anyclass (NFData, CoArbitrary, Function, MonoFoldable)
-  deriving Arbitrary via GenericArbitrary (Neighbors a)
+
+deriving via (GenericArbitrary (Neighbors a)) instance (Arbitrary a) => Arbitrary (Neighbors a)
 
 type instance Element (Neighbors a) = a
 
@@ -531,11 +542,28 @@ invertRate (Rate p) = Rate $ 1 / p
 invertedRate :: Iso (a `Per` b) (b' `Per` a') (b `Per` a) (a' `Per` b')
 invertedRate = iso invertRate invertRate
 
+type (:+:) :: Type -> Type -> Type
+type family (:+:) a b where
+  a :+: a       = a
+  a :+: (Uno b) = a
+
+infixl 6 |+|
+class AddUnit a b where
+  (|+|) :: a -> b -> a :+: b
+
+instance Scalar a => AddUnit a a where
+  x' |+| y' = fromScalar $ scalar x' + scalar y'
+
+instance (Scalar a, Scalar b) => AddUnit a (Uno b) where
+  x' |+| y' = fromScalar $ scalar x' + scalar y'
+
 type (:*:) :: Type -> Type -> Type
 type family (:*:) a b where
-  (a `Per` b) :*: b = a
-  (Square a) :*: a = Cubic a
-  a :*: a = Square a
+  (a `Per` b) :*: b     = a
+  (Square a)  :*: a     = Cubic a
+  a           :*: a     = Square a
+  a           :*: Uno b = a
+  a           :*: b     = a :**: b
 
 infixl 7 |*|
 class MulUnit a b where
@@ -550,6 +578,58 @@ instance forall a. (Scalar a, a :*: a ~ Square a) => MulUnit a a where
 instance forall a. (Scalar a) => MulUnit (Square a) a where
   x' |*| y' = Cubic @a . fromScalar $ scalar x' * scalar y'
 
+instance {-# INCOHERENT #-} forall a b.
+  (Scalar a, Scalar b, Scalar (a :*: Uno b))
+    => MulUnit a (Uno b) where
+  x' |*| y' = fromScalar $ scalar x' * scalar y'
+
+type (:/:) :: Type -> Type -> Type
+type family (:/:) a b where
+  (Square a) :/: a          = a
+  (Cubic a)  :/: a          = Square a
+  (Cubic a)  :/: (Square a) = a
+  (a :**: b) :/: b          = a
+  (a :**: b) :/: a          = b
+  a          :/: Uno b      = a
+  a          :/: b          = a `Per` b
+
+infixl 7 |/|
+class DivUnit a b where
+  (|/|) :: a -> b -> a :/: b
+
+instance Scalar a => DivUnit (Square a) a where
+  (Square a) |/| b = fromScalar $ scalar a / scalar b
+
+instance Scalar a => DivUnit (Cubic a) a where
+  (Cubic a) |/| b = fromScalar $ scalar a / scalar b
+
+instance (Scalar a, Cubic a :/: Square a ~ a)
+       => DivUnit (Cubic a) (Square a) where
+  (Cubic a) |/| (Square b) = fromScalar $ scalar a / scalar b
+
+instance (Scalar a, Scalar b) => DivUnit (a :**: b) b where
+  (Times a) |/| b = fromScalar $ scalar a / scalar b
+
+instance (Scalar a, Scalar b) => DivUnit (a :**: b) a where
+  (Times a) |/| b = fromScalar $ scalar a / scalar b
+
+instance {-# INCOHERENT #-} forall a b.
+  (Scalar a, Scalar b, Scalar (a :/: Uno b))
+    => DivUnit a (Uno b) where
+  x' |/| y' = fromScalar $ scalar x' / scalar y'
+
+-- | Dimensionless quantitites (mass per unit mass, radians, etc)
+--
+-- see <https://en.wikipedia.org/wiki/Parts-per_notation#Uno>
+newtype Uno a = Uno a
+  deriving stock (Eq, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function)
+  deriving ( Arbitrary, Num, Ord, Enum, Real, Fractional, ToJSON, FromJSON
+           , Scalar, Show
+           )
+       via a
+  deriving Unit via UnitSymbol "" (Uno a)
+
 newtype Square a = Square a
   deriving stock (Eq, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
@@ -569,6 +649,9 @@ instance Unit a => Unit (Square a) where
 instance Show a => Show (Square a) where
   show (Square n) = show n <> "ยฒ"
 
+squared :: (Scalar a, a :*: a ~ Square a) => a -> Square a
+squared v = v |*| v
+
 newtype Cubic a = Cubic a
   deriving stock (Eq, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
@@ -588,6 +671,21 @@ instance Unit a => Unit (Cubic a) where
 instance Show a => Show (Cubic a) where
   show (Cubic n) = show n <> "ยณ"
 
+newtype (:**:) a b = Times Double
+  deriving stock (Eq, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function)
+  deriving (Arbitrary, Num, Ord, Enum, Real, Fractional, ToJSON, FromJSON)
+       via Double
+  deriving (Semigroup, Monoid) via Sum Double
+  deriving Show via ShowUnitSuffix (a :**: b) Double
+deriving via Double
+  instance ( Distribution d Double
+           , forall xx yy. Coercible xx yy => Coercible (d xx) (d yy)
+           )
+  => Distribution d (a :**: b)
+
+instance (Unit a, Unit b) => Unit (a :**: b) where
+  unitSuffix = unitSuffix @a <> " " <> unitSuffix @b
 
 --------------------------------------------------------------------------------
 
@@ -626,12 +724,23 @@ type TilesPerTick = Tiles `Per` Ticks
 timesTiles :: TicksPerTile -> Tiles -> Ticks
 timesTiles = (|*|)
 
+-- | Calculate the (cartesian) distance between two 'Position's, floored and
+-- represented as a number of 'Tile's
+--
+-- Note that this is imprecise, and may be different than the length of a
+-- bresenham's line between the points
+distance :: Position -> Position -> Tiles
+distance
+  = (fromScalar .) . (Metric.distance `on` (fmap fromIntegral . view _Position))
+
 --------------------------------------------------------------------------------
 
 newtype Hitpoints = Hitpoints Word
   deriving stock (Eq, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
-  deriving (Arbitrary, Num, Ord, Bounded, Enum, Integral, Real, ToJSON, FromJSON)
+  deriving ( Arbitrary, Num, Ord, Bounded, Enum, Integral, Real, Scalar
+           , ToJSON, FromJSON
+           )
        via Word
   deriving (Semigroup, Monoid) via Sum Word
   deriving Unit via UnitSymbol "hp" Hitpoints
@@ -661,9 +770,12 @@ data Box a = Box
   , _dimensions    :: V2 a
   }
   deriving stock (Show, Eq, Ord, Functor, Generic)
-  deriving Arbitrary via GenericArbitrary (Box a)
 makeFieldsNoPrefix ''Box
 
+-- It seems to be necessary to have an `Arg (V2 a) a` constraint, as a is passed
+-- to V2 internally, in order to make GHC figure out this deriving via correctly.
+deriving via (GenericArbitrary (Box a)) instance (Arbitrary a) => Arbitrary (Box a)
+
 bottomRightCorner :: Num a => Box a -> V2 a
 bottomRightCorner box =
   V2 (box ^. topLeftCorner . L._x + box ^. dimensions . L._x)
diff --git a/users/grfn/xanthous/src/Xanthous/Data/App.hs b/users/aspen/xanthous/src/Xanthous/Data/App.hs
index a2cfcb8001..13c4b5d610 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/App.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/App.hs
@@ -16,7 +16,9 @@ import Xanthous.Util.QuickCheck
 
 -- | Enum for "panels" displayed in the game's UI.
 data Panel
-  = -- | A panel displaying the character's inventory
+  = -- | A panel providing help with the game's commands
+    HelpPanel
+  | -- | A panel displaying the character's inventory
     InventoryPanel
   | -- | A panel describing an item in the inventory in detail
     --
diff --git a/users/grfn/xanthous/src/Xanthous/Data/Entities.hs b/users/aspen/xanthous/src/Xanthous/Data/Entities.hs
index 39953410f2..39953410f2 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/Entities.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/Entities.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/EntityChar.hs b/users/aspen/xanthous/src/Xanthous/Data/EntityChar.hs
index 855a3462da..855a3462da 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/EntityChar.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/EntityChar.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/EntityMap.hs b/users/aspen/xanthous/src/Xanthous/Data/EntityMap.hs
index d24defa841..33a98f1ae5 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/EntityMap.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/EntityMap.hs
@@ -20,6 +20,7 @@ module Xanthous.Data.EntityMap
   , positions
   , lookup
   , lookupWithPosition
+  , positionOf
   -- , positionedEntities
   , neighbors
   , Deduplicate(..)
@@ -37,7 +38,7 @@ import Xanthous.Data
   , Positioned(..)
   , positioned
   , Neighbors(..)
-  , neighborPositions
+  , neighborPositions, position
   )
 import Xanthous.Data.VectorBag
 import Xanthous.Orphans ()
@@ -132,7 +133,6 @@ instance FunctorWithIndex EntityID EntityMap
 instance FoldableWithIndex EntityID EntityMap
 
 instance TraversableWithIndex EntityID EntityMap where
-  itraversed = byID . itraversed . rmap sequenceA . distrib
   itraverse = itraverseOf itraversed
 
 type instance Element (EntityMap a) = a
@@ -268,5 +268,9 @@ lookup eid = fmap (view positioned) . lookupWithPosition eid
 neighbors :: (Ord a, Show a) => Position -> EntityMap a -> Neighbors (VectorBag a)
 neighbors pos em = (\p -> view (atPosition p) em) <$> neighborPositions pos
 
+-- | Traversal to the position of the entity with the given ID
+positionOf :: EntityID -> Traversal' (EntityMap a) Position
+positionOf eid = ix eid . position
+
 --------------------------------------------------------------------------------
 makeWrapped ''Deduplicate
diff --git a/users/grfn/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs b/users/aspen/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs
index 19e7b0cdf0..1398c611cf 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/EntityMap/Graphics.hs
@@ -2,6 +2,7 @@
 module Xanthous.Data.EntityMap.Graphics
   ( visiblePositions
   , visibleEntities
+  , lineOfSight
   , linesOfSight
   , canSee
   ) where
@@ -27,27 +28,34 @@ visiblePositions
 visiblePositions pos radius
   = setFromList . positions . visibleEntities pos radius
 
+-- | Returns a list of entities on the *line of sight* from the first position
+-- to the second position
+lineOfSight
+  :: forall e. Entity e
+  => Position -- ^ Origin
+  -> Position -- ^ Destination
+  -> EntityMap e
+  -> [(Position, Vector (EntityID, e))]
+lineOfSight (view _Position -> origin) (view _Position -> destination) em =
+  takeWhileInclusive (none (view blocksVision . entityAttributes . snd) . snd)
+    $ getPositionedAt <$> line origin destination
+  where
+    getPositionedAt :: V2 Int -> (Position, Vector (EntityID, e))
+    getPositionedAt (review _Position -> p) =
+      (p, over _2 (view positioned) <$> atPositionWithIDs p em)
+
 -- | Returns a list of individual lines of sight, each of which is a list of
 -- entities at positions on that line of sight
 linesOfSight
   :: forall e. Entity e
-  => Position
-  -> Word
+  => Position    -- ^ Centerpoint
+  -> Word        -- ^ Radius
   -> EntityMap e
   -> [[(Position, Vector (EntityID, e))]]
-linesOfSight (view _Position -> pos) visionRadius em
-  = entitiesOnLines
-  <&> takeWhileInclusive
-      (none (view blocksVision . entityAttributes . snd) . snd)
+linesOfSight pos visionRadius em =
+  radius <&> \edge -> lineOfSight pos (_Position # edge) em
   where
-    radius = circle pos $ fromIntegral visionRadius
-    lines = line pos <$> radius
-    entitiesOnLines :: [[(Position, Vector (EntityID, e))]]
-    entitiesOnLines = lines <&> map getPositionedAt
-    getPositionedAt :: V2 Int -> (Position, Vector (EntityID, e))
-    getPositionedAt p =
-      let ppos = _Position # p
-      in (ppos, over _2 (view positioned) <$> atPositionWithIDs ppos em)
+    radius = circle (pos ^. _Position) $ fromIntegral visionRadius
 
 -- | Given a point and a radius of vision, returns a list of all entities that
 -- are *visible* (eg, not blocked by an entity that obscures vision) from that
diff --git a/users/grfn/xanthous/src/Xanthous/Data/Levels.hs b/users/aspen/xanthous/src/Xanthous/Data/Levels.hs
index efc0f53ace..13251d8afd 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/Levels.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/Levels.hs
@@ -5,6 +5,7 @@
 module Xanthous.Data.Levels
   ( Levels
   , allLevels
+  , numLevels
   , nextLevel
   , prevLevel
   , mkLevels1
@@ -46,20 +47,23 @@ import           Test.QuickCheck
 newtype Levels a = Levels { levelZipper :: Zipper Seq a }
     deriving stock (Generic)
     deriving (Functor, Comonad, Foldable) via (Zipper Seq)
-    deriving (ComonadStore Int) via (Zipper Seq)
 
 type instance Element (Levels a) = a
 instance MonoFoldable (Levels a)
 instance MonoFunctor (Levels a)
 instance MonoTraversable (Levels a)
 
+instance ComonadStore Word Levels where
+  pos = toEnum . pos . levelZipper
+  peek i = peek (fromEnum i) . levelZipper
+
 instance Traversable Levels where
   traverse f (Levels z) = Levels <$> traverse f z
 
 instance Foldable1 Levels
 
 instance Traversable1 Levels where
-  traverse1 f (Levels z) = seek (pos z) . partialMkLevels <$> go (unzipper z)
+  traverse1 f levs@(Levels z) = seek (pos levs) . partialMkLevels <$> go (unzipper z)
     where
       go Empty = error "empty seq, unreachable"
       go (x :<| xs) = (<|) <$> f x <.> go xs
@@ -71,6 +75,12 @@ instance Semigroup (Levels a) where
     . partialMkLevels
     $ allLevels levsโ‚ <> allLevels levsโ‚‚
 
+-- | The number of levels stored in 'Levels'
+--
+-- Equivalent to 'Data.Foldable.length', but likely faster
+numLevels :: Levels a -> Word
+numLevels = toEnum . size . levelZipper
+
 -- | Make Levels from a Seq. Throws an error if the seq is not empty
 partialMkLevels :: Seq a -> Levels a
 partialMkLevels = Levels . fromJust . zipper
@@ -98,7 +108,7 @@ nextLevel
   -> Levels level
   -> m (Levels level)
 nextLevel genLevel levs
-  | pos levs + 1 < size (levelZipper levs)
+  | succ (pos levs) < numLevels levs
   = pure $ seeks succ levs
   | otherwise
   = genLevel <&> \level ->
@@ -115,7 +125,7 @@ prevLevel levs | pos levs == 0 = Nothing
 -- various operations
 data AltLevels a = AltLevels
   { _levels :: NonEmpty a
-  , _currentLevel :: Int -- ^ invariant: is within the bounds of _levels
+  , _currentLevel :: Word -- ^ invariant: is within the bounds of _levels
   }
   deriving stock (Show, Eq, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
@@ -150,11 +160,11 @@ instance FromJSON a => FromJSON (Levels a) where
 instance Arbitrary a => Arbitrary (AltLevels a) where
   arbitrary = do
     _levels <- arbitrary
-    _currentLevel <- choose (0, length _levels - 1)
+    _currentLevel <- choose (0, pred . toEnum . length $ _levels)
     pure AltLevels {..}
   shrink als = do
     _levels <- shrink $ als ^. levels
-    _currentLevel <- filter (between 0 $ length _levels - 1)
+    _currentLevel <- filter (between 0 $ pred . toEnum . length $ _levels)
                     $ shrink $ als ^. currentLevel
     pure AltLevels {..}
 
diff --git a/users/grfn/xanthous/src/Xanthous/Data/Memo.hs b/users/aspen/xanthous/src/Xanthous/Data/Memo.hs
index 2b2ee0f960..2b2ee0f960 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/Memo.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/Memo.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/NestedMap.hs b/users/aspen/xanthous/src/Xanthous/Data/NestedMap.hs
index 1b875d4483..1b875d4483 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/NestedMap.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/NestedMap.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Data/VectorBag.hs b/users/aspen/xanthous/src/Xanthous/Data/VectorBag.hs
index 2e6d48062a..2e6d48062a 100644
--- a/users/grfn/xanthous/src/Xanthous/Data/VectorBag.hs
+++ b/users/aspen/xanthous/src/Xanthous/Data/VectorBag.hs
diff --git a/users/aspen/xanthous/src/Xanthous/Entities/Character.hs b/users/aspen/xanthous/src/Xanthous/Entities/Character.hs
new file mode 100644
index 0000000000..c8153086f1
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Character.hs
@@ -0,0 +1,241 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE RecordWildCards #-}
+--------------------------------------------------------------------------------
+module Xanthous.Entities.Character
+
+  ( -- * Character datatype
+    Character(..)
+  , characterName
+  , HasInventory(..)
+  , characterDamage
+  , characterHitpoints'
+  , characterHitpoints
+  , hitpointRecoveryRate
+  , speed
+  , body
+
+    -- *** Body
+  , Body(..)
+  , initialBody
+  , knuckles
+  , Knuckles(..)
+  , fistDamageChance
+  , damageKnuckles
+  , fistfightingDamage
+
+    -- * Character functions
+  , mkCharacter
+  , pickUpItem
+  , isDead
+  , isFullyHealed
+  , damage
+  ) where
+--------------------------------------------------------------------------------
+import Xanthous.Prelude
+--------------------------------------------------------------------------------
+import           Brick
+import           Data.Aeson.Generic.DerivingVia
+import           Data.Aeson (ToJSON, FromJSON)
+import           Data.Coerce (coerce)
+import           Test.QuickCheck
+import           Test.QuickCheck.Instances.Vector ()
+import           Test.QuickCheck.Arbitrary.Generic
+import           Test.QuickCheck.Gen (chooseUpTo)
+import           Test.QuickCheck.Checkers (EqProp)
+import           Control.Monad.State.Lazy (execState)
+import           Control.Monad.Trans.State.Lazy (execStateT)
+--------------------------------------------------------------------------------
+import           Xanthous.Game.State
+import           Xanthous.Entities.Item
+import           Xanthous.Entities.Common
+import           Xanthous.Data
+                 ( TicksPerTile, Hitpoints, Per, Ticks, (|*|), positioned )
+import qualified Xanthous.Entities.RawTypes as Raw
+import           Xanthous.Util (EqEqProp(EqEqProp), modifyKL)
+import           Xanthous.Monad (say_)
+--------------------------------------------------------------------------------
+
+-- | The status of the character's knuckles
+--
+-- This struct is used to track the damage and then eventual build-up of
+-- calluses when the character is fighting with their fists
+data Knuckles = Knuckles
+  { -- | How damaged are the knuckles currently, from 0 to 5?
+    --
+    -- At 0, no calluses will form
+    -- At 1 and up, the character will form calluses after a while
+    -- At 5, continuing to fistfight will deal the character even more damage
+    _knuckleDamage   :: !Word
+    -- | How built-up are the character's calluses, from 0 to 5?
+    --
+    -- Each level of calluses decreases the likelihood of being damaged when
+    -- fistfighting by 1%, up to 5 where the character will never be damaged
+    -- fistfighting
+  , _knuckleCalluses :: !Word
+
+    -- | Number of turns that have passed since the last time the knuckles were
+    -- damaged
+  , _ticksSinceDamaged :: Ticks
+  }
+  deriving stock (Show, Eq, Ord, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function)
+  deriving EqProp via EqEqProp Knuckles
+  deriving (ToJSON, FromJSON)
+       via WithOptions '[ FieldLabelModifier '[Drop 1] ]
+           Knuckles
+makeLenses ''Knuckles
+
+instance Semigroup Knuckles where
+  (Knuckles dโ‚ cโ‚ tโ‚) <> (Knuckles dโ‚‚ cโ‚‚ tโ‚‚) = Knuckles
+    (min (dโ‚ + dโ‚‚) 5)
+    (min (cโ‚ + cโ‚‚) 5)
+    (max tโ‚ tโ‚‚)
+
+instance Monoid Knuckles where
+  mempty = Knuckles 0 0 0
+
+instance Arbitrary Knuckles where
+  arbitrary = do
+    _knuckleDamage <- fromIntegral <$> chooseUpTo 5
+    _knuckleCalluses <- fromIntegral <$> chooseUpTo 5
+    _ticksSinceDamaged <- arbitrary
+    pure Knuckles{..}
+
+-- | Likelihood that the character fighting with their fists will damage
+-- themselves
+fistDamageChance :: Knuckles -> Float
+fistDamageChance knuckles
+  | calluses == 5 = 0
+  | otherwise = baseChance - (0.01 * fromIntegral calluses)
+  where
+    baseChance = 0.08
+    calluses = knuckles ^. knuckleCalluses
+
+-- | Damage the knuckles by a level (capping at the max knuckle damage)
+damageKnuckles :: Knuckles -> Knuckles
+damageKnuckles = execState $ do
+  knuckleDamage %= min 5 . succ
+  ticksSinceDamaged .= 0
+
+-- | Damage taken when fistfighting and 'fistDamageChance' has occurred
+fistfightingDamage :: Knuckles -> Hitpoints
+fistfightingDamage knuckles
+  | knuckles ^. knuckleDamage == 5 = 2
+  | otherwise = 1
+
+stepKnuckles :: Ticks -> Knuckles -> AppM Knuckles
+stepKnuckles ticks = execStateT . whenM (uses knuckleDamage (> 0)) $ do
+  ticksSinceDamaged += ticks
+  whenM (uses ticksSinceDamaged (>= 2000)) $ do
+    dam <- knuckleDamage <<.= 0
+    knuckleCalluses %= min 5 . (+ dam)
+    ticksSinceDamaged .= 0
+    lift $ say_ ["character", "body", "knuckles", "calluses"]
+
+
+-- | Status of the character's body
+data Body = Body
+  { _knuckles :: !Knuckles
+  }
+  deriving stock (Show, Eq, Ord, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function)
+  deriving Arbitrary via GenericArbitrary Body
+  deriving (ToJSON, FromJSON)
+       via WithOptions '[ FieldLabelModifier '[Drop 1] ]
+           Body
+makeLenses ''Body
+
+initialBody :: Body
+initialBody = Body { _knuckles = mempty }
+
+--------------------------------------------------------------------------------
+
+data Character = Character
+  { _inventory           :: !Inventory
+  , _characterName       :: !(Maybe Text)
+  , _characterHitpoints' :: !Double
+  , _speed               :: !TicksPerTile
+  , _body                :: !Body
+  }
+  deriving stock (Show, Eq, Ord, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function)
+  deriving (ToJSON, FromJSON)
+       via WithOptions '[ FieldLabelModifier '[Drop 1] ]
+           Character
+makeFieldsNoPrefix ''Character
+
+characterHitpoints :: Character -> Hitpoints
+characterHitpoints = views characterHitpoints' floor
+
+scrollOffset :: Int
+scrollOffset = 5
+
+instance Draw Character where
+  draw _ = visibleRegion rloc rreg $ str "@"
+    where
+      rloc = Location (negate scrollOffset, negate scrollOffset)
+      rreg = (2 * scrollOffset, 2 * scrollOffset)
+  drawPriority = const maxBound -- Character should always be on top, for now
+
+instance Brain Character where
+  step ticks = execStateT $ do
+    positioned . characterHitpoints' %= \hp ->
+      if hp > fromIntegral initialHitpoints
+      then hp
+      else hp + hitpointRecoveryRate |*| ticks
+    modifyKL (positioned . body . knuckles) $ lift . stepKnuckles ticks
+
+instance Entity Character where
+  description _ = "yourself"
+  entityChar _ = "@"
+
+instance Arbitrary Character where
+  arbitrary = genericArbitrary
+
+initialHitpoints :: Hitpoints
+initialHitpoints = 10
+
+hitpointRecoveryRate :: Double `Per` Ticks
+hitpointRecoveryRate = 1.0 / (15 * coerce defaultSpeed)
+
+defaultSpeed :: TicksPerTile
+defaultSpeed = 100
+
+mkCharacter :: Character
+mkCharacter = Character
+  { _inventory           = mempty
+  , _characterName       = Nothing
+  , _characterHitpoints' = fromIntegral initialHitpoints
+  , _speed               = defaultSpeed
+  , _body                = initialBody
+  }
+
+defaultCharacterDamage :: Hitpoints
+defaultCharacterDamage = 1
+
+-- | Returns the damage that the character currently does with an attack
+-- TODO use double-handed/left-hand/right-hand here
+characterDamage :: Character -> Hitpoints
+characterDamage
+  = fromMaybe defaultCharacterDamage
+  . filter (/= 0)
+  . Just
+  . sumOf (inventory . wielded . wieldedItems . wieldableItem . Raw.damage)
+
+-- | Is the character fully healed up to or past their initial hitpoints?
+isFullyHealed :: Character -> Bool
+isFullyHealed = (>= initialHitpoints) . characterHitpoints
+
+-- | Is the character dead?
+isDead :: Character -> Bool
+isDead = (== 0) . characterHitpoints
+
+pickUpItem :: Item -> Character -> Character
+pickUpItem it = inventory . backpack %~ (it <|)
+
+damage :: Hitpoints -> Character -> Character
+damage (fromIntegral -> amount) = characterHitpoints' %~ \case
+  n | n <= amount -> 0
+    | otherwise  -> n - amount
+
+{-# ANN module ("Hlint: ignore Use newtype instead of data" :: String) #-}
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Character.hs b/users/aspen/xanthous/src/Xanthous/Entities/Common.hs
index 4d641e46dc..368b03f25b 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Character.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Common.hs
@@ -1,21 +1,14 @@
 {-# LANGUAGE TemplateHaskell #-}
-{-# LANGUAGE RecordWildCards #-}
 --------------------------------------------------------------------------------
-module Xanthous.Entities.Character
-
-  ( -- * Character datatype
-    Character(..)
-  , characterName
-  , inventory
-  , characterDamage
-  , characterHitpoints'
-  , characterHitpoints
-  , hitpointRecoveryRate
-  , speed
-  , body
-
-    -- ** Inventory
-  , Inventory(..)
+-- |
+-- Module      : Xanthous.Entities.Common
+-- Description : Common data type definitions and utilities for entities
+--
+--------------------------------------------------------------------------------
+module Xanthous.Entities.Common
+  ( -- * Inventory
+    Inventory(..)
+  , HasInventory(..)
   , backpack
   , wielded
   , items
@@ -24,62 +17,41 @@ module Xanthous.Entities.Character
   , inventoryPosition
   , itemsWithPosition
   , removeItemFromPosition
-    -- *** Wielded items
+
+    -- ** Wielded items
   , Wielded(..)
+  , nothingWielded
   , hands
   , leftHand
   , rightHand
   , inLeftHand
   , inRightHand
   , doubleHanded
+  , Hand(..)
+  , itemsInHand
+  , inHand
+  , wieldInHand
+  , describeHand
   , wieldedItems
   , WieldedItem(..)
   , wieldedItem
   , wieldableItem
   , asWieldedItem
-
-    -- *** Body
-  , Body(..)
-  , initialBody
-  , knuckles
-  , Knuckles(..)
-  , fistDamageChance
-  , damageKnuckles
-  , fistfightingDamage
-
-    -- * Character functions
-  , mkCharacter
-  , pickUpItem
-  , isDead
-  , isFullyHealed
-  , damage
   ) where
 --------------------------------------------------------------------------------
-import Xanthous.Prelude
+import           Xanthous.Prelude
 --------------------------------------------------------------------------------
-import           Brick
-import           Data.Aeson.Generic.DerivingVia
 import           Data.Aeson (ToJSON, FromJSON)
-import           Data.Coerce (coerce)
+import           Data.Aeson.Generic.DerivingVia
 import           Test.QuickCheck
-import           Test.QuickCheck.Instances.Vector ()
-import           Test.QuickCheck.Arbitrary.Generic
-import           Test.QuickCheck.Gen (chooseUpTo)
 import           Test.QuickCheck.Checkers (EqProp)
-import           Control.Monad.State.Lazy (execState)
-import           Control.Monad.Trans.State.Lazy (execStateT)
 --------------------------------------------------------------------------------
+import           Xanthous.Data (Positioned(..), positioned)
 import           Xanthous.Util.QuickCheck
 import           Xanthous.Game.State
 import           Xanthous.Entities.Item
-import           Xanthous.Data
-                 ( TicksPerTile, Hitpoints, Per, Ticks, (|*|), positioned
-                 , Positioned(..)
-                 )
 import           Xanthous.Entities.RawTypes (WieldableItem, wieldable)
-import qualified Xanthous.Entities.RawTypes as Raw
-import           Xanthous.Util (EqEqProp(EqEqProp), modifyKL, removeFirst)
-import           Xanthous.Monad (say_)
+import           Xanthous.Util (removeFirst, EqEqProp(..))
 --------------------------------------------------------------------------------
 
 data WieldedItem = WieldedItem
@@ -129,6 +101,7 @@ data Wielded
        via WithOptions '[ 'SumEnc 'ObjWithSingleField ]
            Wielded
 
+
 nothingWielded :: Wielded
 nothingWielded = Hands Nothing Nothing
 
@@ -158,6 +131,43 @@ wieldedItems :: Traversal' Wielded WieldedItem
 wieldedItems k (DoubleHanded wielded) = DoubleHanded <$> k wielded
 wieldedItems k (Hands l r) = Hands <$> _Just k l <*> _Just k r
 
+
+data Hand
+  = LeftHand
+  | RightHand
+  | BothHands
+  deriving stock (Eq, Show, Ord, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function)
+  deriving Arbitrary via GenericArbitrary Hand
+
+itemsInHand :: Hand -> Wielded -> [WieldedItem]
+itemsInHand LeftHand (DoubleHanded wi) = [wi]
+itemsInHand LeftHand (Hands lh _) = toList lh
+itemsInHand RightHand (DoubleHanded wi) = [wi]
+itemsInHand RightHand (Hands _ rh) = toList rh
+itemsInHand BothHands (DoubleHanded wi) = [wi]
+itemsInHand BothHands (Hands lh rh) = toList lh <> toList rh
+
+inHand :: Hand -> WieldedItem -> Wielded
+inHand LeftHand = inLeftHand
+inHand RightHand = inRightHand
+inHand BothHands = review doubleHanded
+
+wieldInHand :: Hand -> WieldedItem -> Wielded -> ([WieldedItem], Wielded)
+wieldInHand hand item w = (itemsInHand hand w, doWield)
+  where
+    doWield = case (hand, w) of
+      (LeftHand, Hands _ r) -> Hands (Just item) r
+      (LeftHand, DoubleHanded _) -> inLeftHand item
+      (RightHand, Hands l _) -> Hands l (Just item)
+      (RightHand, DoubleHanded _) -> inRightHand item
+      (BothHands, _) -> DoubleHanded item
+
+describeHand :: Hand -> Text
+describeHand LeftHand = "your left hand"
+describeHand RightHand = "your right hand"
+describeHand BothHands = "both hands"
+
 data Inventory = Inventory
   { _backpack :: Vector Item
   , _wielded :: Wielded
@@ -165,6 +175,7 @@ data Inventory = Inventory
   deriving stock (Eq, Show, Ord, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
   deriving Arbitrary via GenericArbitrary Inventory
+  deriving EqProp via EqEqProp Inventory
   deriving (ToJSON, FromJSON)
        via WithOptions '[ FieldLabelModifier '[Drop 1] ]
            Inventory
@@ -225,30 +236,30 @@ instance Semigroup Inventory where
 instance Monoid Inventory where
   mempty = Inventory mempty $ Hands Nothing Nothing
 
+class HasInventory s a | s -> a where
+  inventory :: Lens' s a
+  {-# MINIMAL inventory #-}
+
 -- | Representation for where in the inventory an item might be
 data InventoryPosition
   = Backpack
-  | LeftHand
-  | RightHand
-  | BothHands
+  | InHand Hand
   deriving stock (Eq, Show, Ord, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
   deriving Arbitrary via GenericArbitrary InventoryPosition
 
 -- | Return a human-readable description of the given 'InventoryPosition'
 describeInventoryPosition :: InventoryPosition -> Text
-describeInventoryPosition Backpack  = "In backpack"
-describeInventoryPosition LeftHand  = "Wielded, in left hand"
-describeInventoryPosition RightHand = "Wielded, in right hand"
-describeInventoryPosition BothHands = "Wielded, in both hands"
+describeInventoryPosition Backpack       = "In backpack"
+describeInventoryPosition (InHand hand)  = "Wielded, in " <> describeHand hand
 
 -- | Given a position in the inventory, return a traversal on the inventory over
 -- all the items in that position
 inventoryPosition :: InventoryPosition -> Traversal' Inventory Item
 inventoryPosition Backpack = backpack . traversed
-inventoryPosition LeftHand = wielded . leftHand . _Just . wieldedItem
-inventoryPosition RightHand = wielded . leftHand . _Just . wieldedItem
-inventoryPosition BothHands = wielded . doubleHanded . wieldedItem
+inventoryPosition (InHand LeftHand) = wielded . leftHand . _Just . wieldedItem
+inventoryPosition (InHand RightHand) = wielded . leftHand . _Just . wieldedItem
+inventoryPosition (InHand BothHands) = wielded . doubleHanded . wieldedItem
 
 -- | A fold over all the items in the inventory accompanied by their position in
 -- the inventory
@@ -258,207 +269,22 @@ itemsWithPosition :: Fold Inventory (InventoryPosition, Item)
 itemsWithPosition = folding $ (<>) <$> backpackItems <*> handItems
   where
     backpackItems = toListOf $ backpack . folded . to (Backpack ,)
-    handItems inventory = case inventory ^. wielded of
-       DoubleHanded i -> pure (BothHands, i ^. wieldedItem)
-       Hands l r -> (l ^.. folded . wieldedItem . to (LeftHand ,))
-                 <> (r ^.. folded . wieldedItem . to (RightHand ,))
+    handItems inv = case inv ^. wielded of
+       DoubleHanded i -> pure (InHand BothHands, i ^. wieldedItem)
+       Hands l r -> (l ^.. folded . wieldedItem . to (InHand LeftHand ,))
+                 <> (r ^.. folded . wieldedItem . to (InHand RightHand ,))
 
 -- | Remove the first item equal to 'Item' from the given position in the
 -- inventory
 removeItemFromPosition :: InventoryPosition -> Item -> Inventory -> Inventory
 removeItemFromPosition Backpack item inv
   = inv & backpack %~ removeFirst (== item)
-removeItemFromPosition LeftHand item inv
+removeItemFromPosition (InHand LeftHand) item inv
   = inv & wielded . leftHand %~ filter ((/= item) . view wieldedItem)
-removeItemFromPosition RightHand item inv
+removeItemFromPosition (InHand RightHand) item inv
   = inv & wielded . rightHand %~ filter ((/= item) . view wieldedItem)
-removeItemFromPosition BothHands item inv
+removeItemFromPosition (InHand BothHands) item inv
   | has (wielded . doubleHanded . wieldedItem . filtered (== item)) inv
   = inv & wielded .~ nothingWielded
   | otherwise
   = inv
-
---------------------------------------------------------------------------------
-
--- | The status of the character's knuckles
---
--- This struct is used to track the damage and then eventual build-up of
--- calluses when the character is fighting with their fists
-data Knuckles = Knuckles
-  { -- | How damaged are the knuckles currently, from 0 to 5?
-    --
-    -- At 0, no calluses will form
-    -- At 1 and up, the character will form calluses after a while
-    -- At 5, continuing to fistfight will deal the character even more damage
-    _knuckleDamage   :: !Word
-    -- | How built-up are the character's calluses, from 0 to 5?
-    --
-    -- Each level of calluses decreases the likelihood of being damaged when
-    -- fistfighting by 1%, up to 5 where the character will never be damaged
-    -- fistfighting
-  , _knuckleCalluses :: !Word
-
-    -- | Number of turns that have passed since the last time the knuckles were
-    -- damaged
-  , _ticksSinceDamaged :: Ticks
-  }
-  deriving stock (Show, Eq, Ord, Generic)
-  deriving anyclass (NFData, CoArbitrary, Function)
-  deriving EqProp via EqEqProp Knuckles
-  deriving (ToJSON, FromJSON)
-       via WithOptions '[ FieldLabelModifier '[Drop 1] ]
-           Knuckles
-makeLenses ''Knuckles
-
-instance Semigroup Knuckles where
-  (Knuckles dโ‚ cโ‚ tโ‚) <> (Knuckles dโ‚‚ cโ‚‚ tโ‚‚) = Knuckles
-    (min (dโ‚ + dโ‚‚) 5)
-    (min (cโ‚ + cโ‚‚) 5)
-    (max tโ‚ tโ‚‚)
-
-instance Monoid Knuckles where
-  mempty = Knuckles 0 0 0
-
-instance Arbitrary Knuckles where
-  arbitrary = do
-    _knuckleDamage <- fromIntegral <$> chooseUpTo 5
-    _knuckleCalluses <- fromIntegral <$> chooseUpTo 5
-    _ticksSinceDamaged <- arbitrary
-    pure Knuckles{..}
-
--- | Likelihood that the character fighting with their fists will damage
--- themselves
-fistDamageChance :: Knuckles -> Float
-fistDamageChance knuckles
-  | calluses == 5 = 0
-  | otherwise = baseChance - (0.01 * fromIntegral calluses)
-  where
-    baseChance = 0.08
-    calluses = knuckles ^. knuckleCalluses
-
--- | Damage the knuckles by a level (capping at the max knuckle damage)
-damageKnuckles :: Knuckles -> Knuckles
-damageKnuckles = execState $ do
-  knuckleDamage %= min 5 . succ
-  ticksSinceDamaged .= 0
-
--- | Damage taken when fistfighting and 'fistDamageChance' has occurred
-fistfightingDamage :: Knuckles -> Hitpoints
-fistfightingDamage knuckles
-  | knuckles ^. knuckleDamage == 5 = 2
-  | otherwise = 1
-
-stepKnuckles :: Ticks -> Knuckles -> AppM Knuckles
-stepKnuckles ticks = execStateT . whenM (uses knuckleDamage (> 0)) $ do
-  ticksSinceDamaged += ticks
-  whenM (uses ticksSinceDamaged (>= 2000)) $ do
-    dam <- knuckleDamage <<.= 0
-    knuckleCalluses %= min 5 . (+ dam)
-    ticksSinceDamaged .= 0
-    lift $ say_ ["character", "body", "knuckles", "calluses"]
-
-
--- | Status of the character's body
-data Body = Body
-  { _knuckles :: !Knuckles
-  }
-  deriving stock (Show, Eq, Ord, Generic)
-  deriving anyclass (NFData, CoArbitrary, Function)
-  deriving Arbitrary via GenericArbitrary Body
-  deriving (ToJSON, FromJSON)
-       via WithOptions '[ FieldLabelModifier '[Drop 1] ]
-           Body
-makeLenses ''Body
-
-initialBody :: Body
-initialBody = Body { _knuckles = mempty }
-
---------------------------------------------------------------------------------
-
-data Character = Character
-  { _inventory           :: !Inventory
-  , _characterName       :: !(Maybe Text)
-  , _characterHitpoints' :: !Double
-  , _speed               :: !TicksPerTile
-  , _body                :: !Body
-  }
-  deriving stock (Show, Eq, Ord, Generic)
-  deriving anyclass (NFData, CoArbitrary, Function)
-  deriving (ToJSON, FromJSON)
-       via WithOptions '[ FieldLabelModifier '[Drop 1] ]
-           Character
-makeLenses ''Character
-
-characterHitpoints :: Character -> Hitpoints
-characterHitpoints = views characterHitpoints' floor
-
-scrollOffset :: Int
-scrollOffset = 5
-
-instance Draw Character where
-  draw _ = visibleRegion rloc rreg $ str "@"
-    where
-      rloc = Location (negate scrollOffset, negate scrollOffset)
-      rreg = (2 * scrollOffset, 2 * scrollOffset)
-  drawPriority = const maxBound -- Character should always be on top, for now
-
-instance Brain Character where
-  step ticks = execStateT $ do
-    positioned . characterHitpoints' %= \hp ->
-      if hp > fromIntegral initialHitpoints
-      then hp
-      else hp + hitpointRecoveryRate |*| ticks
-    modifyKL (positioned . body . knuckles) $ lift . stepKnuckles ticks
-
-instance Entity Character where
-  description _ = "yourself"
-  entityChar _ = "@"
-
-instance Arbitrary Character where
-  arbitrary = genericArbitrary
-
-initialHitpoints :: Hitpoints
-initialHitpoints = 10
-
-hitpointRecoveryRate :: Double `Per` Ticks
-hitpointRecoveryRate = 1.0 / (15 * coerce defaultSpeed)
-
-defaultSpeed :: TicksPerTile
-defaultSpeed = 100
-
-mkCharacter :: Character
-mkCharacter = Character
-  { _inventory           = mempty
-  , _characterName       = Nothing
-  , _characterHitpoints' = fromIntegral initialHitpoints
-  , _speed               = defaultSpeed
-  , _body                = initialBody
-  }
-
-defaultCharacterDamage :: Hitpoints
-defaultCharacterDamage = 1
-
--- | Returns the damage that the character currently does with an attack
--- TODO use double-handed/left-hand/right-hand here
-characterDamage :: Character -> Hitpoints
-characterDamage
-  = fromMaybe defaultCharacterDamage
-  . preview (inventory . wielded . wieldedItems . wieldableItem . Raw.damage)
-
--- | Is the character fully healed up to or past their initial hitpoints?
-isFullyHealed :: Character -> Bool
-isFullyHealed = (>= initialHitpoints) . characterHitpoints
-
--- | Is the character dead?
-isDead :: Character -> Bool
-isDead = (== 0) . characterHitpoints
-
-pickUpItem :: Item -> Character -> Character
-pickUpItem it = inventory . backpack %~ (it <|)
-
-damage :: Hitpoints -> Character -> Character
-damage (fromIntegral -> amount) = characterHitpoints' %~ \case
-  n | n <= amount -> 0
-    | otherwise  -> n - amount
-
-{-# ANN module ("Hlint: ignore Use newtype instead of data" :: String) #-}
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Creature.hs b/users/aspen/xanthous/src/Xanthous/Entities/Creature.hs
index f23cf25b43..3ea610795e 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Creature.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Creature.hs
@@ -8,9 +8,9 @@ module Xanthous.Entities.Creature
   , creatureType
   , hitpoints
   , hippocampus
+  , inventory
 
     -- ** Creature functions
-  , newWithType
   , damage
   , isDead
   , visionRadius
@@ -30,10 +30,8 @@ module Xanthous.Entities.Creature
 import           Xanthous.Prelude
 --------------------------------------------------------------------------------
 import           Test.QuickCheck
-import           Test.QuickCheck.Arbitrary.Generic
 import           Data.Aeson.Generic.DerivingVia
 import           Data.Aeson (ToJSON, FromJSON)
-import           Control.Monad.Random (MonadRandom)
 --------------------------------------------------------------------------------
 import           Xanthous.AI.Gormlak
 import           Xanthous.Entities.RawTypes hiding
@@ -43,21 +41,24 @@ import           Xanthous.Game.State
 import           Xanthous.Data
 import           Xanthous.Data.Entities
 import           Xanthous.Entities.Creature.Hippocampus
+import           Xanthous.Util.QuickCheck (GenericArbitrary(..))
+import           Xanthous.Entities.Common (Inventory, HasInventory(..))
 --------------------------------------------------------------------------------
 
 data Creature = Creature
-  { _creatureType :: !CreatureType
-  , _hitpoints    :: !Hitpoints
-  , _hippocampus  :: !Hippocampus
+  { _creatureType   :: !CreatureType
+  , _hitpoints      :: !Hitpoints
+  , _hippocampus    :: !Hippocampus
+  , _inventory      :: !Inventory
   }
   deriving stock (Eq, Show, Ord, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
   deriving Draw via DrawRawCharPriority "_creatureType" 1000 Creature
+  deriving Arbitrary via GenericArbitrary Creature
   deriving (ToJSON, FromJSON)
        via WithOptions '[ FieldLabelModifier '[Drop 1] ]
                        Creature
-instance Arbitrary Creature where arbitrary = genericArbitrary
-makeLenses ''Creature
+makeFieldsNoPrefix ''Creature
 
 instance HasVisionRadius Creature where
   visionRadius = const 50 -- TODO
@@ -75,12 +76,6 @@ instance Entity Creature where
 
 --------------------------------------------------------------------------------
 
-newWithType :: MonadRandom m => CreatureType -> m Creature
-newWithType _creatureType =
-  let _hitpoints = _creatureType ^. maxHitpoints
-      _hippocampus = initialHippocampus
-  in pure Creature {..}
-
 damage :: Hitpoints -> Creature -> Creature
 damage amount = hitpoints %~ \hp ->
   if hp <= amount
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs b/users/aspen/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs
index 9d5cc13451..d13ea8055c 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Creature/Hippocampus.hs
@@ -25,7 +25,6 @@ import           Test.QuickCheck
 import           Test.QuickCheck.Arbitrary.Generic
 --------------------------------------------------------------------------------
 import           Xanthous.Data
-import           Xanthous.Util.QuickCheck
 --------------------------------------------------------------------------------
 
 
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Draw/Util.hs b/users/aspen/xanthous/src/Xanthous/Entities/Draw/Util.hs
index aa6c5fa4fc..aa6c5fa4fc 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Draw/Util.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Draw/Util.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Entities.hs b/users/aspen/xanthous/src/Xanthous/Entities/Entities.hs
index a0c037a1b4..a0c037a1b4 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Entities.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Entities.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Entities.hs-boot b/users/aspen/xanthous/src/Xanthous/Entities/Entities.hs-boot
index 519a862c6a..519a862c6a 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Entities.hs-boot
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Entities.hs-boot
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Environment.hs b/users/aspen/xanthous/src/Xanthous/Entities/Environment.hs
index b45a91eabe..b45a91eabe 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Environment.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Environment.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Item.hs b/users/aspen/xanthous/src/Xanthous/Entities/Item.hs
index eadd625696..eadd625696 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Item.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Item.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Marker.hs b/users/aspen/xanthous/src/Xanthous/Entities/Marker.hs
index 14d02872ed..14d02872ed 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Marker.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Marker.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/RawTypes.hs b/users/aspen/xanthous/src/Xanthous/Entities/RawTypes.hs
index b0fb5e086e..a7021d76cf 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/RawTypes.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/RawTypes.hs
@@ -10,6 +10,9 @@ module Xanthous.Entities.RawTypes
     -- * Creatures
   , CreatureType(..)
   , hostile
+    -- ** Generation parameters
+  , CreatureGenerateParams(..)
+  , canGenerate
     -- ** Language
   , LanguageName(..)
   , getLanguage
@@ -27,17 +30,23 @@ module Xanthous.Entities.RawTypes
   , isWieldable
 
     -- * Lens classes
-  , HasAttacks(..)
   , HasAttackMessage(..)
+  , HasAttacks(..)
+  , HasChance(..)
   , HasChar(..)
+  , HasCreatureAttackMessage(..)
   , HasDamage(..)
   , HasDensity(..)
   , HasDescription(..)
   , HasEatMessage(..)
   , HasEdible(..)
+  , HasEntityName(..)
+  , HasEquippedItem(..)
   , HasFriendly(..)
+  , HasGenerateParams(..)
   , HasHitpointsHealed(..)
   , HasLanguage(..)
+  , HasLevelRange(..)
   , HasLongDescription(..)
   , HasMaxHitpoints(..)
   , HasName(..)
@@ -47,18 +56,20 @@ module Xanthous.Entities.RawTypes
   , HasWieldable(..)
   ) where
 --------------------------------------------------------------------------------
-import Xanthous.Prelude
-import Test.QuickCheck
-import Data.Aeson.Generic.DerivingVia
-import Data.Aeson (ToJSON, FromJSON)
+import           Xanthous.Prelude
+import           Test.QuickCheck
+import           Data.Aeson.Generic.DerivingVia
+import           Data.Aeson (ToJSON, FromJSON)
+import           Data.Interval (Interval, lowerBound', upperBound')
+import qualified Data.Interval as Interval
 --------------------------------------------------------------------------------
-import Xanthous.Messages (Message(..))
-import Xanthous.Data (TicksPerTile, Hitpoints, Per, Grams, Cubic, Meters)
-import Xanthous.Data.EntityChar
-import Xanthous.Util.QuickCheck
-import Xanthous.Generators.Speech (Language, gormlak, english)
-import Xanthous.Orphans ()
-import Data.Interval (Interval, lowerBound', upperBound')
+import           Xanthous.Messages (Message(..))
+import           Xanthous.Data (TicksPerTile, Hitpoints, Per, Grams, Cubic, Meters)
+import           Xanthous.Data.EntityChar
+import           Xanthous.Util.QuickCheck
+import           Xanthous.Generators.Speech (Language, gormlak, english)
+import           Xanthous.Orphans ()
+import           Xanthous.Util (EqProp, EqEqProp(..))
 --------------------------------------------------------------------------------
 
 -- | Identifiers for languages that creatures can speak.
@@ -97,18 +108,68 @@ data Attack = Attack
                        Attack
 makeFieldsNoPrefix ''Attack
 
+-- | Description for generating an item equipped to a creature
+data CreatureEquippedItem = CreatureEquippedItem
+  { -- | Name of the entity type to generate
+    _entityName :: !Text
+    -- | Chance of generating the item when generating the creature
+    --
+    -- A chance of 1.0 will always generate the item
+  , _chance :: !Double
+  }
+  deriving stock (Show, Eq, Ord, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function)
+  deriving Arbitrary via GenericArbitrary CreatureEquippedItem
+  deriving (ToJSON, FromJSON)
+       via WithOptions '[ FieldLabelModifier '[Drop 1]
+                        , OmitNothingFields 'True
+                        ]
+                       CreatureEquippedItem
+makeFieldsNoPrefix ''CreatureEquippedItem
+
+
+data CreatureGenerateParams = CreatureGenerateParams
+  { -- | Range of dungeon levels at which to generate this creature
+    _levelRange :: !(Interval Word)
+    -- | Item equipped to the creature
+  , _equippedItem :: !(Maybe CreatureEquippedItem)
+  }
+  deriving stock (Eq, Show, Generic)
+  deriving anyclass (NFData, CoArbitrary, Function)
+  deriving Arbitrary via GenericArbitrary CreatureGenerateParams
+  deriving EqProp via EqEqProp CreatureGenerateParams
+  deriving (ToJSON, FromJSON)
+       via WithOptions '[ FieldLabelModifier '[Drop 1] ]
+                       CreatureGenerateParams
+makeFieldsNoPrefix ''CreatureGenerateParams
+
+instance Ord CreatureGenerateParams where
+  compare
+    = (compare `on` lowerBound' . _levelRange)
+    <> (compare `on` upperBound' . _levelRange)
+    <> (compare `on` _equippedItem)
+
+-- | Can a creature with these generate params be generated on this level?
+canGenerate
+  :: Word -- ^ Level number
+  -> CreatureGenerateParams
+  -> Bool
+canGenerate levelNumber gps = Interval.member levelNumber $ gps ^. levelRange
+
 data CreatureType = CreatureType
-  { _name         :: !Text
-  , _description  :: !Text
-  , _char         :: !EntityChar
-  , _maxHitpoints :: !Hitpoints
-  , _friendly     :: !Bool
-  , _speed        :: !TicksPerTile
-  , _language     :: !(Maybe LanguageName)
+  { _name           :: !Text
+  , _description    :: !Text
+  , _char           :: !EntityChar
+  , _maxHitpoints   :: !Hitpoints
+  , _friendly       :: !Bool
+  , _speed          :: !TicksPerTile
+  , _language       :: !(Maybe LanguageName)
   , -- | The verb, in present tense, for when the creature says something
-    _sayVerb      :: !(Maybe Text)
+    _sayVerb        :: !(Maybe Text)
   , -- | The creature's natural attacks
-    _attacks       :: !(NonNull (Vector Attack))
+    _attacks        :: !(NonNull (Vector Attack))
+    -- | Parameters for generating the creature in levels
+  , _generateParams :: !(Maybe CreatureGenerateParams)
   }
   deriving stock (Show, Eq, Ord, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
@@ -139,7 +200,27 @@ makeFieldsNoPrefix ''EdibleItem
 
 data WieldableItem = WieldableItem
   { _damage :: !Hitpoints
+    -- | Message to use when the character is using this item to attack a
+    --  creature.
+    --
+    -- Grammatically, this should be of the form "slash at the
+    -- {{creature.creatureType.name}} with your dagger"
+    --
+    -- = Parameters
+    --
+    -- [@creature@ (type: 'Creature')] The creature being attacked
   , _attackMessage :: !(Maybe Message)
+    -- | Message to use when a creature is using this item to attack the
+    -- character.
+    --
+    -- Grammatically, should be of the form "The creature slashes you with its
+    -- dagger".
+    --
+    -- = Parameters
+    --
+    -- [@creature@ (type: 'Creature')] The creature doing the attacking
+    -- [@item@ (type: 'Item')] The item itself
+  , _creatureAttackMessage :: !(Maybe Message)
   }
   deriving stock (Show, Eq, Ord, Generic)
   deriving anyclass (NFData, CoArbitrary, Function)
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws.hs b/users/aspen/xanthous/src/Xanthous/Entities/Raws.hs
index 441e870160..10f0d83193 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws.hs
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws.hs
@@ -5,19 +5,14 @@ module Xanthous.Entities.Raws
   , raw
   , RawType(..)
   , rawsWithType
-  , entityFromRaw
   ) where
 --------------------------------------------------------------------------------
 import           Data.FileEmbed
 import qualified Data.Yaml as Yaml
 import           Xanthous.Prelude
 import           System.FilePath.Posix
-import           Control.Monad.Random (MonadRandom)
 --------------------------------------------------------------------------------
 import           Xanthous.Entities.RawTypes
-import           Xanthous.Game.State
-import qualified Xanthous.Entities.Creature as Creature
-import qualified Xanthous.Entities.Item as Item
 import           Xanthous.AI.Gormlak ()
 --------------------------------------------------------------------------------
 rawRaws :: [(FilePath, ByteString)]
@@ -52,9 +47,3 @@ rawsWithType :: forall a. RawType a => HashMap Text a
 rawsWithType = mapFromList . itoListOf (ifolded . _RawType) $ raws
 
 --------------------------------------------------------------------------------
-
-entityFromRaw :: MonadRandom m => EntityRaw -> m SomeEntity
-entityFromRaw (Creature creatureType)
-  = SomeEntity <$> Creature.newWithType creatureType
-entityFromRaw (Item itemType)
-  = SomeEntity <$> Item.newWithType itemType
diff --git a/users/aspen/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml
new file mode 100644
index 0000000000..12c76fc14b
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/broken-dagger.yaml
@@ -0,0 +1,24 @@
+Item:
+  name: broken dagger
+  description: a short, broken dagger
+  longDescription: A short dagger with a twisted, chipped blade
+  char:
+    char: โ€ 
+    style:
+      foreground: black
+  wieldable:
+    damage: 3
+    attackMessage:
+      - slash at the {{creature.creatureType.name}} with your dagger
+      - stab the {{creature.creatureType.name}} with your dagger
+    creatureAttackMessage:
+      - The {{creature.creatureType.name}} slashes at you with its dagger.
+      - The {{creature.creatureType.name}} stabs you with its dagger.
+  # Just the steel, not the handle, for now
+  density: [7750 , 8050000]
+  # 15cm โ€“ 45cm
+  # ร—
+  # 2cm โ€“ 3cm
+  # ร—
+  # .5cm โ€“ 1cm
+  volume: [0.15, 1.35]
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml
index ad3d9cb147..ad3d9cb147 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/gormlak.yaml
diff --git a/users/aspen/xanthous/src/Xanthous/Entities/Raws/husk.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/husk.yaml
new file mode 100644
index 0000000000..cdfcde616d
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/husk.yaml
@@ -0,0 +1,26 @@
+Creature:
+  name: husk
+  description: an empty husk of some humanoid creature
+  longDescription: |
+    An empty husk of a humanoid creature. All semblance of sentience has long
+    left its eyes; instead it shambles about aimlessly, always hungering for the
+    warmth of life.
+  char:
+    char: h
+    style:
+      foreground: black
+  maxHitpoints: 6
+  speed: 110
+  friendly: false
+  attacks:
+  - description:
+      - swings its arms at you
+      - elbows you
+    damage: 1
+  - description: kicks you
+    damage: 2
+  generateParams:
+    levelRange: [1, PosInf]
+    equippedItem:
+      entityName: broken-dagger
+      chance: 0.9
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/noodles.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/noodles.yaml
index c0501a18a8..c0501a18a8 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/noodles.yaml
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/noodles.yaml
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/ooze.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/ooze.yaml
index fe427c94ab..fe427c94ab 100644
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/ooze.yaml
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/ooze.yaml
diff --git a/users/aspen/xanthous/src/Xanthous/Entities/Raws/rock.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/rock.yaml
new file mode 100644
index 0000000000..3f4e133fe2
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/rock.yaml
@@ -0,0 +1,10 @@
+Item:
+  name: rock
+  description: a rock
+  longDescription: a medium-sized rock made out of some unknown stone
+  char: .
+  wieldable:
+    damage: 1
+    attackMessage: hit the {{creature.creatureType.name}} in the head with your rock
+  density: [ 1500000, 2500000 ]
+  volume: [ 0.000125, 0.001 ]
diff --git a/users/aspen/xanthous/src/Xanthous/Entities/Raws/stick.yaml b/users/aspen/xanthous/src/Xanthous/Entities/Raws/stick.yaml
new file mode 100644
index 0000000000..7f9e1faffe
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Entities/Raws/stick.yaml
@@ -0,0 +1,22 @@
+Item:
+  name: stick
+  description: a wooden stick
+  longDescription: A sturdy branch broken off from some sort of tree
+  char:
+    char: โˆค
+    style:
+      foreground: yellow
+  wieldable:
+    damage: 2
+    attackMessage:
+      - bonk the {{creature.creatureType.name}} over the head with your stick
+      - bash the {{creature.creatureType.name}} on the noggin with your stick
+      - whack the {{creature.creatureType.name}} with your stick
+    creatureAttackMessage:
+      - The {{creature.creatureType.name}} bonks you over the head with its stick.
+      - The {{creature.creatureType.name}} bashes you on the noggin with its stick.
+      - The {{creature.creatureType.name}} whacks you with its stick.
+  # https://www.sciencedirect.com/topics/agricultural-and-biological-sciences/wood-density
+  # it's a hard stick. so it's dense wood.
+  density: 890000 # g/mยณ
+  volume: [ 0.003, 0.006 ] # โ‰ˆ3.5 cm radius ร— โ‰ˆ1m length
diff --git a/users/grfn/xanthous/src/Xanthous/Game.hs b/users/aspen/xanthous/src/Xanthous/Game.hs
index 89c23f0de8..89c23f0de8 100644
--- a/users/grfn/xanthous/src/Xanthous/Game.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Arbitrary.hs b/users/aspen/xanthous/src/Xanthous/Game/Arbitrary.hs
index bb9b64b0b3..679bfe5459 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Arbitrary.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Arbitrary.hs
@@ -43,6 +43,7 @@ instance Arbitrary GameState where
     _debugState <- arbitrary
     let _autocommand = NoAutocommand
     _memo <- arbitrary
+    _savefile <- arbitrary
     pure $ GameState {..}
 
 
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Draw.hs b/users/aspen/xanthous/src/Xanthous/Game/Draw.hs
index 3f148e8428..291dfd8b5e 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Draw.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Draw.hs
@@ -4,16 +4,20 @@ module Xanthous.Game.Draw
   ) where
 --------------------------------------------------------------------------------
 import           Xanthous.Prelude
+--------------------------------------------------------------------------------
 import           Brick hiding (loc, on)
 import           Brick.Widgets.Border
 import           Brick.Widgets.Border.Style
 import           Brick.Widgets.Edit
+import           Control.Monad.State.Lazy (evalState)
+import           Control.Monad.State.Class ( get, MonadState, gets )
 --------------------------------------------------------------------------------
 import           Xanthous.Data
 import           Xanthous.Data.App (ResourceName, Panel(..))
 import qualified Xanthous.Data.App as Resource
 import qualified Xanthous.Data.EntityMap as EntityMap
 import           Xanthous.Game.State
+import           Xanthous.Entities.Common (Wielded(..), wielded, backpack)
 import           Xanthous.Entities.Character
 import           Xanthous.Entities.Item (Item)
 import           Xanthous.Game
@@ -23,13 +27,16 @@ import           Xanthous.Game
                  )
 import           Xanthous.Game.Prompt
 import           Xanthous.Orphans ()
-import Control.Monad.State.Lazy (evalState)
-import Control.Monad.State.Class ( get, MonadState, gets )
+import Brick.Widgets.Center (hCenter)
+import Xanthous.Command (Keybinding (..), keybindings, Command, commandIsHidden)
+import Graphics.Vty.Input.Events (Modifier(..))
+import Graphics.Vty.Input (Key(..))
+import Brick.Widgets.Table
 --------------------------------------------------------------------------------
 
 cursorPosition :: GameState -> Widget ResourceName -> Widget ResourceName
 cursorPosition game
-  | WaitingPrompt _ (Prompt _ SPointOnMap (PointOnMapPromptState pos) _ _)
+  | WaitingPrompt _ (Prompt _ _ (preview promptStatePosition -> Just pos) _ _)
     <- game ^. promptState
   = showCursor Resource.Prompt (pos ^. loc)
   | otherwise
@@ -42,10 +49,11 @@ drawPromptState :: GamePromptState m -> Widget ResourceName
 drawPromptState NoPrompt = emptyWidget
 drawPromptState (WaitingPrompt msg (Prompt _ pt ps pri _)) =
   case (pt, ps, pri) of
-    (SStringPrompt, StringPromptState edit, _) ->
-      txtWrap msg <+> txt " " <+> renderEditor (txt . fold) True edit
+    (SStringPrompt, StringPromptState edit, mDef) ->
+      txt msg
+      <+> txt (maybe "" (\def -> "(default: " <> def <> ") ") mDef)
+      <+> renderEditor (txt . fold) True edit
     (SDirectionPrompt, DirectionPromptState, _) -> txtWrap msg
-    (SContinue, _, _) -> txtWrap msg
     (SMenu, _, menuItems) ->
       txtWrap msg
       <=> foldl' (<=>) emptyWidget (map drawMenuItem $ itoList menuItems)
@@ -108,16 +116,84 @@ drawInventoryPanel game
               (txtWrap . ((bullet <| " ") <>) . description)
               backpackItems)
 
+drawHelpPanel :: Widget ResourceName
+drawHelpPanel
+  = txtWrap "To move in a direction or attack, use vi keys (hjklyubn):"
+  <=> txt " "
+  <=> hCenter keyStar
+  <=> txt " "
+  <=> cmds
+  where
+    keyStar
+      =   txt "y k u"
+      <=> txt " \\|/"
+      <=> txt "h-.-l"
+      <=> txt " /|\\"
+      <=> txt "b j n"
+
+    cmds
+      = renderTable
+      . alignRight 0
+      . setDefaultRowAlignment AlignTop
+      . surroundingBorder False
+      . rowBorders False
+      . columnBorders False
+      . table $ help <&> \(key, cmd) -> [ txt $ key <> " : "
+                                       , hLimitPercent 100 $ txtWrap cmd]
+
+    help =
+      extraHelp <>
+      keybindings
+        ^.. ifolded
+          . filtered (not . commandIsHidden)
+          . withIndex
+          . to (bimap displayKeybinding displayCommand)
+    extraHelp
+      = [("Shift-Dir", "Auto-move")]
+
+    displayCommand = tshow @Command
+    displayKeybinding (Keybinding k mods) = foldMap showMod mods <> showKey k
+
+    showMod MCtrl  = "Ctrl-"
+    showMod MShift = "Shift-"
+    showMod MAlt   = "Alt-"
+    showMod MMeta  = "Meta-"
+
+    showKey (KChar c) = pack [c]
+    showKey KEsc = "<Esc>"
+    showKey KBS = "<Backspace>"
+    showKey KEnter = "<Enter>"
+    showKey KLeft = "<Left>"
+    showKey KRight = "<Right>"
+    showKey KUp = "<Up>"
+    showKey KDown = "<Down>"
+    showKey KUpLeft = "<UpLeft>"
+    showKey KUpRight = "<UpRight>"
+    showKey KDownLeft = "<DownLeft>"
+    showKey KDownRight = "<DownRight>"
+    showKey KCenter = "<Center>"
+    showKey (KFun n) = "<F" <> tshow n <> ">"
+    showKey KBackTab = "<BackTab>"
+    showKey KPrtScr = "<PrtScr>"
+    showKey KPause = "<Pause>"
+    showKey KIns = "<Ins>"
+    showKey KHome = "<Home>"
+    showKey KPageUp = "<PageUp>"
+    showKey KDel = "<Del>"
+    showKey KEnd = "<End>"
+    showKey KPageDown = "<PageDown>"
+    showKey KBegin = "<Begin>"
+    showKey KMenu = "<Menu>"
 
 drawPanel :: GameState -> Panel -> Widget ResourceName
 drawPanel game panel
   = border
   . hLimit 35
   . viewport (Resource.Panel panel) Vertical
-  . case panel of
-      InventoryPanel -> drawInventoryPanel
-      ItemDescriptionPanel desc -> const $ txtWrap desc
-  $ game
+  $ case panel of
+      HelpPanel -> drawHelpPanel
+      InventoryPanel -> drawInventoryPanel game
+      ItemDescriptionPanel desc -> txtWrap desc
 
 drawCharacterInfo :: Character -> Widget ResourceName
 drawCharacterInfo ch = txt " " <+> charName <+> charHitpoints
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Env.hs b/users/aspen/xanthous/src/Xanthous/Game/Env.hs
index 6e10d0f735..5d7b275c8a 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Env.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Env.hs
@@ -1,8 +1,12 @@
 {-# LANGUAGE TemplateHaskell #-}
 --------------------------------------------------------------------------------
 module Xanthous.Game.Env
-  ( GameEnv(..)
+  ( Config(..)
+  , defaultConfig
+  , disableSaving
+  , GameEnv(..)
   , eventChan
+  , config
   ) where
 --------------------------------------------------------------------------------
 import Xanthous.Prelude
@@ -11,9 +15,23 @@ import Brick.BChan (BChan)
 import Xanthous.Data.App (AppEvent)
 --------------------------------------------------------------------------------
 
+data Config = Config
+  { _disableSaving :: Bool
+  }
+  deriving stock (Generic, Show, Eq)
+makeLenses ''Config
+{-# ANN Config ("HLint: ignore Use newtype instead of data" :: String) #-}
+
+defaultConfig :: Config
+defaultConfig = Config
+  { _disableSaving = False
+  }
+
+--------------------------------------------------------------------------------
+
 data GameEnv = GameEnv
   { _eventChan :: BChan AppEvent
+  , _config :: Config
   }
   deriving stock (Generic)
 makeLenses ''GameEnv
-{-# ANN GameEnv ("HLint: ignore Use newtype instead of data" :: String) #-}
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Lenses.hs b/users/aspen/xanthous/src/Xanthous/Game/Lenses.hs
index f7b4d5fb9f..c692a3b479 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Lenses.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Lenses.hs
@@ -1,6 +1,6 @@
-{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE RecordWildCards       #-}
 {-# LANGUAGE QuantifiedConstraints #-}
-{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE AllowAmbiguousTypes   #-}
 --------------------------------------------------------------------------------
 module Xanthous.Game.Lenses
   ( clearMemo
@@ -65,6 +65,7 @@ initialStateFromSeed seed =
       _debugState = DebugState
         { _allRevealed = False
         }
+      _savefile = Nothing
       _autocommand = NoAutocommand
       _memo = emptyMemoState
   in GameState {..}
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Memo.hs b/users/aspen/xanthous/src/Xanthous/Game/Memo.hs
index 9e483a8d4a..154063b5dd 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Memo.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Memo.hs
@@ -45,7 +45,7 @@ emptyMemoState :: MemoState
 emptyMemoState = MemoState { _characterVisiblePositions = UnMemoized }
 {-# INLINE emptyMemoState #-}
 
-clear :: Lens' MemoState (Memoized k v) -> MemoState -> MemoState
+clear :: ASetter' MemoState (Memoized key val) -> MemoState -> MemoState
 clear = flip set UnMemoized
 {-# INLINE clear #-}
 
diff --git a/users/grfn/xanthous/src/Xanthous/Game/Prompt.hs b/users/aspen/xanthous/src/Xanthous/Game/Prompt.hs
index fa4c3903de..2d6c0a280f 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/Prompt.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/Prompt.hs
@@ -1,8 +1,7 @@
-{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveFunctor        #-}
 {-# LANGUAGE UndecidableInstances #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-# LANGUAGE GADTs #-}
-{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE StandaloneDeriving   #-}
+{-# LANGUAGE GADTs                #-}
 --------------------------------------------------------------------------------
 module Xanthous.Game.Prompt
   ( PromptType(..)
@@ -11,26 +10,29 @@ module Xanthous.Game.Prompt
   , PromptCancellable(..)
   , PromptResult(..)
   , PromptState(..)
+  , promptStatePosition
   , MenuOption(..)
   , mkMenuItems
   , PromptInput
   , Prompt(..)
   , mkPrompt
+  , mkStringPrompt
+  , mkStringPromptWithDefault
   , mkMenu
   , mkPointOnMapPrompt
+  , mkFirePrompt
   , isCancellable
   , submitPrompt
   ) where
 --------------------------------------------------------------------------------
-import Xanthous.Prelude
+import           Xanthous.Prelude
 --------------------------------------------------------------------------------
 import           Brick.Widgets.Edit (Editor, editorText, getEditContents)
 import           Test.QuickCheck
 import           Test.QuickCheck.Arbitrary.Generic
 --------------------------------------------------------------------------------
---------------------------------------------------------------------------------
 import           Xanthous.Util (smallestNotIn, AlphaChar (..))
-import           Xanthous.Data (Direction, Position)
+import           Xanthous.Data (Direction, Position, Tiles)
 import           Xanthous.Data.App (ResourceName)
 import qualified Xanthous.Data.App as Resource
 --------------------------------------------------------------------------------
@@ -41,6 +43,9 @@ data PromptType where
   Menu            :: Type -> PromptType
   DirectionPrompt :: PromptType
   PointOnMap      :: PromptType
+  -- | Throw an item or fire a projectile weapon. Prompt is to select the
+  -- direction
+  Fire            :: PromptType
   Continue        :: PromptType
   deriving stock (Generic)
 
@@ -51,14 +56,16 @@ instance Show PromptType where
   show DirectionPrompt = "DirectionPrompt"
   show PointOnMap = "PointOnMap"
   show Continue = "Continue"
+  show Fire = "Fire"
 
 data SPromptType :: PromptType -> Type where
-  SStringPrompt    ::      SPromptType 'StringPrompt
-  SConfirm         ::      SPromptType 'Confirm
-  SMenu            ::      SPromptType ('Menu a)
-  SDirectionPrompt ::      SPromptType 'DirectionPrompt
-  SPointOnMap      ::      SPromptType 'PointOnMap
-  SContinue        ::      SPromptType 'Continue
+  SStringPrompt    :: SPromptType 'StringPrompt
+  SConfirm         :: SPromptType 'Confirm
+  SMenu            :: SPromptType ('Menu a)
+  SDirectionPrompt :: SPromptType 'DirectionPrompt
+  SPointOnMap      :: SPromptType 'PointOnMap
+  SContinue        :: SPromptType 'Continue
+  SFire            :: SPromptType 'Fire
 
 instance NFData (SPromptType pt) where
   rnf SStringPrompt = ()
@@ -67,6 +74,7 @@ instance NFData (SPromptType pt) where
   rnf SDirectionPrompt = ()
   rnf SPointOnMap = ()
   rnf SContinue = ()
+  rnf SFire = ()
 
 class SingPromptType pt where singPromptType :: SPromptType pt
 instance SingPromptType 'StringPrompt where singPromptType = SStringPrompt
@@ -74,6 +82,7 @@ instance SingPromptType 'Confirm where singPromptType = SConfirm
 instance SingPromptType 'DirectionPrompt where singPromptType = SDirectionPrompt
 instance SingPromptType 'PointOnMap where singPromptType = SPointOnMap
 instance SingPromptType 'Continue where singPromptType = SContinue
+instance SingPromptType 'Fire where singPromptType = SFire
 
 instance Show (SPromptType pt) where
   show SStringPrompt    = "SStringPrompt"
@@ -82,6 +91,7 @@ instance Show (SPromptType pt) where
   show SDirectionPrompt = "SDirectionPrompt"
   show SPointOnMap      = "SPointOnMap"
   show SContinue        = "SContinue"
+  show SFire            = "SFire"
 
 data PromptCancellable
   = Cancellable
@@ -98,6 +108,7 @@ data PromptResult (pt :: PromptType) where
   MenuResult       :: forall a. a    -> PromptResult ('Menu a)
   DirectionResult  :: Direction -> PromptResult 'DirectionPrompt
   PointOnMapResult :: Position  -> PromptResult 'PointOnMap
+  FireResult       :: Position  -> PromptResult 'Fire
   ContinueResult   ::             PromptResult 'Continue
 
 instance Arbitrary (PromptResult 'StringPrompt) where
@@ -118,6 +129,9 @@ instance Arbitrary (PromptResult 'PointOnMap) where
 instance Arbitrary (PromptResult 'Continue) where
   arbitrary = pure ContinueResult
 
+instance Arbitrary (PromptResult 'Fire) where
+  arbitrary = FireResult <$> arbitrary
+
 --------------------------------------------------------------------------------
 
 data PromptState pt where
@@ -128,6 +142,7 @@ data PromptState pt where
   ConfirmPromptState    ::            PromptState 'Confirm
   MenuPromptState       :: forall a.       PromptState ('Menu a)
   PointOnMapPromptState :: Position -> PromptState 'PointOnMap
+  FirePromptState       :: Position -> PromptState 'Fire
 
 instance NFData (PromptState pt) where
   rnf sps@(StringPromptState ed) = sps `deepseq` ed `deepseq` ()
@@ -136,6 +151,7 @@ instance NFData (PromptState pt) where
   rnf ConfirmPromptState = ()
   rnf MenuPromptState = ()
   rnf pomps@(PointOnMapPromptState pos) = pomps `deepseq` pos `deepseq` ()
+  rnf fps@(FirePromptState pos) = fps `deepseq` pos `deepseq` ()
 
 instance Arbitrary (PromptState 'StringPrompt) where
   arbitrary = StringPromptState <$> arbitrary
@@ -149,6 +165,9 @@ instance Arbitrary (PromptState 'Continue) where
 instance Arbitrary (PromptState ('Menu a)) where
   arbitrary = pure MenuPromptState
 
+instance Arbitrary (PromptState 'Fire) where
+  arbitrary = FirePromptState <$> arbitrary
+
 instance CoArbitrary (PromptState 'StringPrompt) where
   coarbitrary (StringPromptState ed) = coarbitrary ed
 
@@ -161,8 +180,22 @@ instance CoArbitrary (PromptState 'Continue) where
 instance CoArbitrary (PromptState ('Menu a)) where
   coarbitrary MenuPromptState = coarbitrary ()
 
+instance CoArbitrary (PromptState 'Fire) where
+  coarbitrary (FirePromptState pos) = coarbitrary pos
+
 deriving stock instance Show (PromptState pt)
 
+-- | Traversal over the position for the prompt types with positions in their
+-- prompt state (currently 'Fire' and 'PointOnMap')
+promptStatePosition :: forall pt. Traversal' (PromptState pt) Position
+promptStatePosition _ ps@(StringPromptState _) = pure ps
+promptStatePosition _ DirectionPromptState = pure DirectionPromptState
+promptStatePosition _ ContinuePromptState = pure ContinuePromptState
+promptStatePosition _ ConfirmPromptState = pure ConfirmPromptState
+promptStatePosition _ MenuPromptState = pure MenuPromptState
+promptStatePosition f (PointOnMapPromptState p) = PointOnMapPromptState <$> f p
+promptStatePosition f (FirePromptState p) = FirePromptState <$> f p
+
 data MenuOption a = MenuOption Text a
   deriving stock (Eq, Generic, Functor)
   deriving anyclass (NFData, CoArbitrary, Function)
@@ -184,8 +217,10 @@ instance Show (MenuOption a) where
   show (MenuOption m _) = show m
 
 type family PromptInput (pt :: PromptType) :: Type where
-  PromptInput ('Menu a) = Map Char (MenuOption a)
-  PromptInput 'PointOnMap = Position -- Character pos
+  PromptInput ('Menu a)     = Map Char (MenuOption a)
+  PromptInput 'PointOnMap   = Position -- Character pos
+  PromptInput 'Fire         = (Position, Tiles) -- Nearest enemy, range
+  PromptInput 'StringPrompt = Maybe Text -- Default value
   PromptInput _ = ()
 
 data Prompt (m :: Type -> Type) where
@@ -239,6 +274,8 @@ instance CoArbitrary (m ()) => CoArbitrary (Prompt m) where
     variant @Int 5 . coarbitrary (c, pri, cb)
   coarbitrary (Prompt c SContinue ps pri cb) =
     variant @Int 6 . coarbitrary (c, ps, pri, cb)
+  coarbitrary (Prompt c SFire ps pri cb) =
+    variant @Int 7 . coarbitrary (c, ps, pri, cb)
 
 -- instance Function (Prompt m) where
 --   function = functionMap toTuple _fromTuple
@@ -246,14 +283,33 @@ instance CoArbitrary (m ()) => CoArbitrary (Prompt m) where
 --       toTuple (Prompt c pt ps pri cb) = (c, pt, ps, pri, cb)
 
 
-mkPrompt :: (PromptInput pt ~ ()) => PromptCancellable -> SPromptType pt -> (PromptResult pt -> m ()) -> Prompt m
-mkPrompt c pt@SStringPrompt cb =
-  let ps = StringPromptState $ editorText Resource.Prompt (Just 1) ""
-  in Prompt c pt ps () cb
+mkPrompt
+  :: (PromptInput pt ~ ())
+  => PromptCancellable       -- ^ Is the prompt cancellable or not?
+  -> SPromptType pt          -- ^ The type of the prompt
+  -> (PromptResult pt -> m ()) -- ^ Function to call when the prompt is complete
+  -> Prompt m
 mkPrompt c pt@SDirectionPrompt cb = Prompt c pt DirectionPromptState () cb
 mkPrompt c pt@SContinue cb = Prompt c pt ContinuePromptState () cb
 mkPrompt c pt@SConfirm cb = Prompt c pt ConfirmPromptState () cb
 
+mkStringPrompt
+  :: PromptCancellable                  -- ^ Is the prompt cancellable or not?
+  -> (PromptResult 'StringPrompt -> m ()) -- ^ Function to call when the prompt is complete
+  -> Prompt m
+mkStringPrompt c =
+  let ps = StringPromptState $ editorText Resource.Prompt (Just 1) ""
+  in Prompt c SStringPrompt ps Nothing
+
+mkStringPromptWithDefault
+  :: PromptCancellable                  -- ^ Is the prompt cancellable or not?
+  -> Text                               -- ^ Default value for the prompt
+  -> (PromptResult 'StringPrompt -> m ()) -- ^ Function to call when the prompt is complete
+  -> Prompt m
+mkStringPromptWithDefault c def =
+  let ps = StringPromptState $ editorText Resource.Prompt (Just 1) ""
+  in Prompt c SStringPrompt ps (Just def)
+
 mkMenu
   :: forall a m.
     PromptCancellable
@@ -269,22 +325,35 @@ mkPointOnMapPrompt
   -> Prompt m
 mkPointOnMapPrompt c pos = Prompt c SPointOnMap (PointOnMapPromptState pos) pos
 
+mkFirePrompt
+  :: PromptCancellable
+  -> Position -- ^ Initial position
+  -> Tiles    -- ^ Range
+  -> (PromptResult 'Fire -> m ())
+  -> Prompt m
+mkFirePrompt c pos range = Prompt c SFire (FirePromptState pos) (pos, range)
+
 isCancellable :: Prompt m -> Bool
 isCancellable (Prompt Cancellable _ _ _ _)   = True
 isCancellable (Prompt Uncancellable _ _ _ _) = False
 
 submitPrompt :: Applicative m => Prompt m -> m ()
-submitPrompt (Prompt _ pt ps _ cb) =
-  case (pt, ps) of
-    (SStringPrompt, StringPromptState edit) ->
-      cb . StringResult . mconcat . getEditContents $ edit
-    (SDirectionPrompt, DirectionPromptState) ->
+submitPrompt (Prompt _ pt ps pri cb) =
+  case (pt, ps, pri) of
+    (SStringPrompt, StringPromptState edit, mDef) ->
+      let inputVal = mconcat . getEditContents $ edit
+          val | null inputVal, Just def <- mDef = def
+              | otherwise = inputVal
+      in cb $ StringResult val
+    (SDirectionPrompt, DirectionPromptState, _) ->
       pure () -- Don't use submit with a direction prompt
-    (SContinue, ContinuePromptState) ->
+    (SContinue, ContinuePromptState, _) ->
       cb ContinueResult
-    (SMenu, MenuPromptState) ->
+    (SMenu, MenuPromptState, _) ->
       pure () -- Don't use submit with a menu prompt
-    (SPointOnMap, PointOnMapPromptState pos) ->
+    (SPointOnMap, PointOnMapPromptState pos, _) ->
       cb $ PointOnMapResult pos
-    (SConfirm, ConfirmPromptState) ->
+    (SConfirm, ConfirmPromptState, _) ->
       cb $ ConfirmResult True
+    (SFire, FirePromptState pos, _) ->
+      cb $ FireResult pos
diff --git a/users/grfn/xanthous/src/Xanthous/Game/State.hs b/users/aspen/xanthous/src/Xanthous/Game/State.hs
index 10883ce06e..13b1ba1588 100644
--- a/users/grfn/xanthous/src/Xanthous/Game/State.hs
+++ b/users/aspen/xanthous/src/Xanthous/Game/State.hs
@@ -16,6 +16,7 @@ module Xanthous.Game.State
   , promptState
   , characterEntityID
   , autocommand
+  , savefile
   , memo
   , GamePromptState(..)
 
@@ -99,7 +100,6 @@ import qualified Graphics.Vty.Attributes as Vty
 import qualified Graphics.Vty.Image as Vty
 --------------------------------------------------------------------------------
 import           Xanthous.Util (KnownBool(..))
-import           Xanthous.Util.QuickCheck (GenericArbitrary(..))
 import           Xanthous.Data
 import           Xanthous.Data.App
 import           Xanthous.Data.Levels
@@ -512,6 +512,9 @@ data GameState = GameState
   , _debugState        :: !DebugState
   , _autocommand       :: !AutocommandState
 
+  -- | The path to the savefile that was loaded for this game, if any
+  , _savefile          :: !(Maybe FilePath)
+
   , _memo              :: MemoState
   }
   deriving stock (Show, Generic)
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level.hs
index 81f21f55ff..fc57402e7d 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level.hs
@@ -85,7 +85,7 @@ parseGeneratorInput = Opt.subparser
     generatorCommand sgen name desc parseParams =
       Opt.command name
         (Opt.info
-          (GeneratorInput <$> pure sgen <*> parseParams)
+          (GeneratorInput sgen <$> parseParams)
           (Opt.progDesc desc)
         )
 
@@ -132,21 +132,25 @@ generateLevel
   => SGenerator gen
   -> Params gen
   -> Dimensions
+  -> Word -- ^ Level number, starting at 0
   -> m Level
-generateLevel gen ps dims = do
+generateLevel gen ps dims num = do
   rand <- mkStdGen <$> getRandom
   let cells = generate gen ps dims rand
       _levelWalls = cellsToWalls cells
   village <- generateVillage cells gen
   let _levelExtra = village
   _levelItems <- randomItems cells
-  _levelCreatures <- randomCreatures cells
+  _levelCreatures <- randomCreatures num cells
   _levelDoors <- randomDoors cells
   _levelCharacterPosition <- chooseCharacterPosition cells
   let upStaircase = _EntityMap # [(_levelCharacterPosition, UpStaircase)]
   downStaircase <- placeDownStaircase cells
   let _levelStaircases = upStaircase <> downStaircase
-  _levelTutorialMessage <- tutorialMessage cells _levelCharacterPosition
+  _levelTutorialMessage <-
+    if num == 0
+    then tutorialMessage cells _levelCharacterPosition
+    else pure mempty
   pure Level {..}
 
 levelToEntityMap :: Level -> EntityMap SomeEntity
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs
index 03d534ca39..03d534ca39 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/CaveAutomata.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/Dungeon.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/Dungeon.hs
index 4851b02269..0be7c0435c 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/Dungeon.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/Dungeon.hs
@@ -23,7 +23,7 @@ import           Linear.Metric
 import qualified Options.Applicative as Opt
 --------------------------------------------------------------------------------
 import           Xanthous.Random
-import           Xanthous.Data hiding (x, y, _x, _y, edges)
+import           Xanthous.Data hiding (x, y, _x, _y, edges, distance)
 import           Xanthous.Generators.Level.Util
 import           Xanthous.Util.Graphics (delaunay, straightLine)
 import           Xanthous.Util.Graph (mstSubGraph)
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/LevelContents.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/LevelContents.hs
index 3cad569336..4f8a2f42ee 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/LevelContents.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/LevelContents.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE RecordWildCards #-}
 --------------------------------------------------------------------------------
 module Xanthous.Generators.Level.LevelContents
   ( chooseCharacterPosition
@@ -6,6 +7,7 @@ module Xanthous.Generators.Level.LevelContents
   , randomDoors
   , placeDownStaircase
   , tutorialMessage
+  , entityFromRaw
   ) where
 --------------------------------------------------------------------------------
 import           Xanthous.Prelude hiding (any, toList)
@@ -17,14 +19,15 @@ import           Data.Foldable (any, toList)
 import           Linear.V2
 --------------------------------------------------------------------------------
 import           Xanthous.Generators.Level.Util
-import           Xanthous.Random
+import           Xanthous.Random hiding (chance)
+import qualified Xanthous.Random as Random
 import           Xanthous.Data
                  ( positionFromV2,  Position, _Position
                  , rotations, arrayNeighbors, Neighbors(..)
                  , neighborPositions
                  )
 import           Xanthous.Data.EntityMap (EntityMap, _EntityMap)
-import           Xanthous.Entities.Raws (rawsWithType, RawType)
+import           Xanthous.Entities.Raws (rawsWithType, RawType, raw)
 import qualified Xanthous.Entities.Item as Item
 import           Xanthous.Entities.Item (Item)
 import qualified Xanthous.Entities.Creature as Creature
@@ -33,13 +36,17 @@ import           Xanthous.Entities.Environment
                  (GroundMessage(..), Door(..), unlockedDoor, Staircase(..))
 import           Xanthous.Messages (message_)
 import           Xanthous.Util.Graphics (circle)
+import           Xanthous.Entities.RawTypes
+import           Xanthous.Entities.Creature.Hippocampus (initialHippocampus)
+import           Xanthous.Entities.Common (inRightHand, asWieldedItem, wielded)
+import           Xanthous.Game.State (SomeEntity(SomeEntity))
 --------------------------------------------------------------------------------
 
 chooseCharacterPosition :: MonadRandom m => Cells -> m Position
 chooseCharacterPosition = randomPosition
 
 randomItems :: MonadRandom m => Cells -> m (EntityMap Item)
-randomItems = randomEntities Item.newWithType (0.0004, 0.001)
+randomItems = randomEntities (fmap Identity . Item.newWithType) (0.0004, 0.001)
 
 placeDownStaircase :: MonadRandom m => Cells -> m (EntityMap Staircase)
 placeDownStaircase cells = do
@@ -76,8 +83,46 @@ randomDoors cells = do
     teeish (fmap not -> (Neighbors tl t tr l r _ b _ )) =
       and [tl, t, tr, b] && (and . fmap not) [l, r]
 
-randomCreatures :: MonadRandom m => Cells -> m (EntityMap Creature)
-randomCreatures = randomEntities Creature.newWithType (0.0007, 0.002)
+randomCreatures
+  :: MonadRandom m
+  => Word -- ^ Level number, starting at 0
+  -> Cells
+  -> m (EntityMap Creature)
+randomCreatures levelNumber
+  = randomEntities maybeNewCreature (0.0007, 0.002)
+  where
+    maybeNewCreature cType
+      | maybe True (canGenerate levelNumber) $ cType ^. generateParams
+      = Just <$> newCreatureWithType cType
+      | otherwise
+      = pure Nothing
+
+newCreatureWithType :: MonadRandom m => CreatureType -> m Creature
+newCreatureWithType _creatureType = do
+  let _hitpoints = _creatureType ^. maxHitpoints
+      _hippocampus = initialHippocampus
+
+  equipped <- fmap join
+            . traverse genEquipped
+            $ _creatureType
+            ^.. generateParams . _Just . equippedItem . _Just
+  let _inventory = maybe id (\ei -> wielded .~ inRightHand ei) (headMay equipped) mempty
+  pure Creature.Creature {..}
+  where
+    genEquipped cei = do
+      doGen <- Random.chance $ cei ^. chance
+      let entName = cei ^. entityName
+          itemType =
+            fromMaybe (error $ "raw \"" <> unpack entName <> "\" not of type Item")
+            . preview _Item
+            . fromMaybe (error $ "Could not find raw: " <> unpack entName)
+            $ raw entName
+      item <- Item.newWithType itemType
+      if doGen
+        then pure [fromMaybe (error $ "raw \"" <> unpack entName <> "\" not wieldable")
+                  $ preview asWieldedItem item]
+        else pure []
+
 
 tutorialMessage :: MonadRandom m
   => Cells
@@ -99,8 +144,8 @@ tutorialMessage cells characterPosition = do
             (circle (pos ^. _Position) dist)
 
 randomEntities
-  :: forall entity raw m. (MonadRandom m, RawType raw)
-  => (raw -> m entity)
+  :: forall entity raw m t. (MonadRandom m, RawType raw, Functor t, Foldable t)
+  => (raw -> m (t entity))
   -> (Float, Float)
   -> Cells
   -> m (EntityMap entity)
@@ -113,10 +158,10 @@ randomEntities newWithType sizeRange cells =
         floor . (* fromIntegral len) <$> getRandomR sizeRange
       entities <- for [0..numEntities] $ const $ do
         pos <- randomPosition cells
-        raw <- choose raws
-        entity <- newWithType raw
-        pure (pos, entity)
-      pure $ _EntityMap # entities
+        r <- choose raws
+        entities <- newWithType r
+        pure $ (pos, ) <$> entities
+      pure $ _EntityMap # (entities >>= toList)
 
 randomPosition :: MonadRandom m => Cells -> m Position
 randomPosition = fmap positionFromV2 . choose . impureNonNull . cellCandidates
@@ -131,3 +176,7 @@ cellCandidates
   . regions
   -- cells ends up with true = wall, we want true = can put an item here
   . amap not
+
+entityFromRaw :: MonadRandom m => EntityRaw -> m SomeEntity
+entityFromRaw (Creature ct) = SomeEntity <$> newCreatureWithType ct
+entityFromRaw (Item it) = SomeEntity <$> Item.newWithType it
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/Util.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/Util.hs
index c643778179..0008eb965c 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/Util.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/Util.hs
@@ -52,13 +52,18 @@ initializeEmpty :: RandomGen g => Dimensions -> CellM g s (MCells s)
 initializeEmpty dims =
   lift $ newArray (0, V2 (dims ^. width) (dims ^. height)) False
 
+-- | Returns the number of neighbors of the given point in the given array that
+-- are True.
+--
+-- Behavior if point is out-of-bounds for the array is undefined, but will not
+-- error
 numAliveNeighborsM
   :: forall a i m
   . (MArray a Bool m, Ix i, Integral i)
   => a (V2 i) Bool
   -> V2 i
   -> m Word
-numAliveNeighborsM cells (V2 x y) = do
+numAliveNeighborsM cells pt@(V2 x y) = do
   cellBounds <- getBounds cells
   getSum <$> foldlMapM'
     (fmap (Sum . fromIntegral . fromEnum) . boundedGet cellBounds)
@@ -66,24 +71,32 @@ numAliveNeighborsM cells (V2 x y) = do
 
   where
     boundedGet :: (V2 i, V2 i) -> (Int, Int) -> m Bool
+    boundedGet bnds _
+      | not (inRange bnds pt)
+      = pure True
     boundedGet (V2 minX minY, V2 maxX maxY) (i, j)
-      | x <= minX
-        || y <= minY
-        || x >= maxX
-        || y >= maxY
+      | (x <= minX && i < 0)
+      || (y <= minY && j < 0)
+      || (x >= maxX && i > 0)
+      || (y >= maxY && j > 0)
       = pure True
       | otherwise =
         let nx = fromIntegral $ fromIntegral x + i
             ny = fromIntegral $ fromIntegral y + j
         in readArray cells $ V2 nx ny
 
+-- | Returns the number of neighbors of the given point in the given array that
+-- are True.
+--
+-- Behavior if point is out-of-bounds for the array is undefined, but will not
+-- error
 numAliveNeighbors
   :: forall a i
   . (IArray a Bool, Ix i, Integral i)
   => a (V2 i) Bool
   -> V2 i
   -> Word
-numAliveNeighbors cells (V2 x y) =
+numAliveNeighbors cells pt@(V2 x y) =
   let cellBounds = bounds cells
   in getSum $ foldMap
       (Sum . fromIntegral . fromEnum . boundedGet cellBounds)
@@ -91,11 +104,14 @@ numAliveNeighbors cells (V2 x y) =
 
   where
     boundedGet :: (V2 i, V2 i) -> (Int, Int) -> Bool
+    boundedGet bnds _
+      | not (inRange bnds pt)
+      = True
     boundedGet (V2 minX minY, V2 maxX maxY) (i, j)
-      | x <= minX
-        || y <= minY
-        || x >= maxX
-        || y >= maxY
+      | (x <= minX && i < 0)
+      || (y <= minY && j < 0)
+      || (x >= maxX && i > 0)
+      || (y >= maxY && j > 0)
       = True
       | otherwise =
         let nx = fromIntegral $ fromIntegral x + i
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Level/Village.hs b/users/aspen/xanthous/src/Xanthous/Generators/Level/Village.hs
index ab7de95e68..ab7de95e68 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Level/Village.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Level/Village.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Generators/Speech.hs b/users/aspen/xanthous/src/Xanthous/Generators/Speech.hs
index 8abc00b6a2..8abc00b6a2 100644
--- a/users/grfn/xanthous/src/Xanthous/Generators/Speech.hs
+++ b/users/aspen/xanthous/src/Xanthous/Generators/Speech.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Messages.hs b/users/aspen/xanthous/src/Xanthous/Messages.hs
index c273d65082..c273d65082 100644
--- a/users/grfn/xanthous/src/Xanthous/Messages.hs
+++ b/users/aspen/xanthous/src/Xanthous/Messages.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Messages/Template.hs b/users/aspen/xanthous/src/Xanthous/Messages/Template.hs
index 5176880355..5176880355 100644
--- a/users/grfn/xanthous/src/Xanthous/Messages/Template.hs
+++ b/users/aspen/xanthous/src/Xanthous/Messages/Template.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Monad.hs b/users/aspen/xanthous/src/Xanthous/Monad.hs
index db602de56f..db602de56f 100644
--- a/users/grfn/xanthous/src/Xanthous/Monad.hs
+++ b/users/aspen/xanthous/src/Xanthous/Monad.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Orphans.hs b/users/aspen/xanthous/src/Xanthous/Orphans.hs
index e9cfddc0e6..66004163f6 100644
--- a/users/grfn/xanthous/src/Xanthous/Orphans.hs
+++ b/users/aspen/xanthous/src/Xanthous/Orphans.hs
@@ -11,9 +11,11 @@ module Xanthous.Orphans
 --------------------------------------------------------------------------------
 import           Xanthous.Prelude hiding (elements, (.=))
 --------------------------------------------------------------------------------
-import           Data.Aeson
+import           Data.Aeson hiding (Key)
+import qualified Data.Aeson.KeyMap as KM
 import           Data.Aeson.Types (typeMismatch)
 import           Data.List.NonEmpty (NonEmpty(..))
+import qualified Graphics.Vty.Input
 import           Graphics.Vty.Attributes
 import           Brick.Widgets.Edit
 import           Data.Text.Zipper.Generic (GenericTextZipper)
@@ -21,6 +23,7 @@ import           Brick.Widgets.Core (getName)
 import           System.Random.Internal (StdGen (..))
 import           System.Random.SplitMix (SMGen ())
 import           Test.QuickCheck
+-- import           Test.QuickCheck.Arbitrary.Generic (Arg ())
 import           "quickcheck-instances" Test.QuickCheck.Instances ()
 import           Text.Megaparsec (errorBundlePretty)
 import           Text.Megaparsec.Pos
@@ -37,6 +40,7 @@ import           Test.QuickCheck.Checkers (EqProp ((=-=)))
 import           Xanthous.Util.JSON
 import           Xanthous.Util.QuickCheck
 import           Xanthous.Util (EqEqProp(EqEqProp))
+import qualified Graphics.Vty.Input.Events
 --------------------------------------------------------------------------------
 
 instance forall s a.
@@ -304,6 +308,9 @@ deriving stock instance Ord Color
 deriving stock instance Ord a => Ord (MaybeDefault a)
 deriving stock instance Ord Attr
 
+deriving anyclass instance Hashable Graphics.Vty.Input.Events.Key
+deriving anyclass instance Hashable Graphics.Vty.Input.Events.Modifier
+
 --------------------------------------------------------------------------------
 
 instance (SemiSequence a, Arbitrary (Element a), Arbitrary a)
@@ -367,7 +374,7 @@ deriving newtype instance (Arbitrary s, CoArbitrary (m (a, s)))
 
 --------------------------------------------------------------------------------
 
-deriving via (GenericArbitrary (V2 a)) instance Arbitrary a => Arbitrary (V2 a)
+deriving via (GenericArbitrary (V2 a)) instance (Arbitrary a) => Arbitrary (V2 a)
 instance CoArbitrary a => CoArbitrary (V2 a)
 instance Function a => Function (V2 a)
 
@@ -461,7 +468,7 @@ instance forall a. (FromJSON a, Ord a) => FromJSON (Interval a) where
         upper <- parseBound $ arr ^?! ix 1
         pure $ interval lower upper
       parseBound = withObject "Bound" $ \obj -> do
-        when (length obj /= 1) $ fail "Expected an object with a single key"
+        when (KM.size obj /= 1) $ fail "Expected an object with a single key"
         let [(k, v)] = obj ^@.. ifolded
         boundary <- case k of
           "Excluded" -> pure Open
@@ -481,3 +488,8 @@ instance forall a. (FromJSON a, Ord a) => FromJSON (Interval a) where
         pure $ val <=..<= val
       checkLength arr =
         when (length arr /= 2) $ fail "Expected array of length 2"
+
+--------------------------------------------------------------------------------
+
+deriving anyclass instance NFData Graphics.Vty.Input.Key
+deriving anyclass instance NFData Graphics.Vty.Input.Modifier
diff --git a/users/aspen/xanthous/src/Xanthous/Physics.hs b/users/aspen/xanthous/src/Xanthous/Physics.hs
new file mode 100644
index 0000000000..37530cbbc2
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/Physics.hs
@@ -0,0 +1,71 @@
+--------------------------------------------------------------------------------
+module Xanthous.Physics
+  ( throwDistance
+  , bluntThrowDamage
+  ) where
+--------------------------------------------------------------------------------
+import Xanthous.Prelude
+import Xanthous.Data
+       ( Meters
+       , (:**:)(..)
+       , Square
+       , Grams
+       , (|*|)
+       , (|/|)
+       , Hitpoints
+       , Per (..)
+       , squared
+       , Uno(..), (|+|)
+       )
+--------------------------------------------------------------------------------
+
+-- university shotputter can put a 16 lb shot about 14 meters
+-- โ‰ˆ 7.25 kg 14 meters
+-- 14m = x / (7.25kg ร— y + z)ยฒ
+-- 14m = x / (7250g ร— y + z)ยฒ
+--
+-- we don't want to scale down too much:
+--
+-- 10 kg 10 meters
+-- = 10000 g 10 meters
+--
+-- 15 kg w meters
+-- = 15000 g w meters
+--
+-- 14m = x / (7250g ร— y + z)ยฒ
+-- 10m = x / (10000g ร— y + z)ยฒ
+-- wm = x / (15000g ร— y + z)ยฒ
+--
+-- wโ‰ˆ0.527301 โˆง yโ‰ˆ0.000212178 sqrt(x) โˆง zโ‰ˆ1.80555 sqrt(x) โˆง 22824.1 sqrt(x)!=0
+--
+-- x = 101500
+-- y = 0.0675979
+-- z = 575.231
+--
+
+-- TODO make this dynamic
+strength :: Meters :**: Square Grams
+strength = Times 10150000
+
+yCoeff :: Uno Double
+yCoeff = Uno 0.0675979
+
+zCoeff :: Uno Double
+zCoeff = Uno 575.231
+
+-- | Calculate the maximum distance an object with the given weight can be
+-- thrown
+throwDistance
+  :: Grams  -- ^ Weight of the object
+  -> Meters -- ^ Max distance thrown
+throwDistance weight = strength |/| squared (weight |*| yCoeff |+| zCoeff)
+
+-- | Returns the damage dealt by a blunt object with the given weight when
+-- thrown
+bluntThrowDamage
+  :: Grams
+  -> Hitpoints
+bluntThrowDamage weight = throwDamageRatio |*| weight
+  where
+    throwDamageRatio :: Hitpoints `Per` Grams
+    throwDamageRatio = Rate $ 1 / 5000
diff --git a/users/grfn/xanthous/src/Xanthous/Prelude.hs b/users/aspen/xanthous/src/Xanthous/Prelude.hs
index 2cb4299303..2cb4299303 100644
--- a/users/grfn/xanthous/src/Xanthous/Prelude.hs
+++ b/users/aspen/xanthous/src/Xanthous/Prelude.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Random.hs b/users/aspen/xanthous/src/Xanthous/Random.hs
index 329b321b8b..329b321b8b 100644
--- a/users/grfn/xanthous/src/Xanthous/Random.hs
+++ b/users/aspen/xanthous/src/Xanthous/Random.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util.hs b/users/aspen/xanthous/src/Xanthous/Util.hs
index 2f9606b29c..f918340f05 100644
--- a/users/grfn/xanthous/src/Xanthous/Util.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util.hs
@@ -34,7 +34,7 @@ module Xanthous.Util
   , times, times_, endoTimes
 
     -- * State utilities
-  , modifyK, modifyKL
+  , modifyK, modifyKL, useListOf
 
     -- * Type-level programming utils
   , KnownBool(..)
@@ -80,6 +80,14 @@ foldlMapM' f xs = foldr f' pure xs mempty
     let !b = mappend bl br
     k b
 
+-- | Returns whether the third argument is in the range given by the first two
+-- arguments, inclusive
+--
+-- >>> between (0 :: Int) 2 2
+-- True
+--
+-- >>> between (0 :: Int) 2 3
+-- False
 between
   :: Ord a
   => a -- ^ lower bound
@@ -303,6 +311,14 @@ modifyK k = get >>= k >>= put
 modifyKL :: MonadState s m => LensLike m s s a b -> (a -> m b) -> m ()
 modifyKL l k = get >>= traverseOf l k >>= put
 
+-- | Use a list of all the targets of a 'Fold' in the current state
+--
+-- @@
+-- evalState (useListOf folded) === toList
+-- @@
+useListOf :: MonadState s m => Getting (Endo [a]) s a -> m [a]
+useListOf = gets . toListOf
+
 --------------------------------------------------------------------------------
 
 -- | A newtype wrapper around 'Char' whose 'Enum' and 'Bounded' instances only
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Comonad.hs b/users/aspen/xanthous/src/Xanthous/Util/Comonad.hs
index 9e158cc8e2..9e158cc8e2 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Comonad.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Comonad.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Graph.hs b/users/aspen/xanthous/src/Xanthous/Util/Graph.hs
index 8e5c04f4bf..8e5c04f4bf 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Graph.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Graph.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Graphics.hs b/users/aspen/xanthous/src/Xanthous/Util/Graphics.hs
index 6ba63a2d8a..0cb009f45a 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Graphics.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Graphics.hs
@@ -126,7 +126,7 @@ line pa@(V2 xa ya) pb@(V2 xb yb)
     ystep                = if yโ‚ < yโ‚‚ then 1 else -1
     go (xTemp, yTemp, err)
       | xTemp > xโ‚‚ = Nothing
-      | otherwise  = Just ((V2 xTemp yTemp), (xTemp + 1, newY, newError))
+      | otherwise  = Just (V2 xTemp yTemp, (xTemp + 1, newY, newError))
       where
         tempError        = err + ฮดy
         (newY, newError) = if (2 * tempError) >= ฮดx
@@ -139,7 +139,6 @@ straightLine :: (Num i, Ord i) => V2 i -> V2 i -> [V2 i]
 straightLine pa@(V2 xa _) pb@(V2 _ yb) = line pa midpoint ++ line midpoint pb
   where midpoint = V2 xa yb
 
-
 delaunay
   :: (Ord n, Fractional n)
   => NonEmpty (V2 n, p)
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Inflection.hs b/users/aspen/xanthous/src/Xanthous/Util/Inflection.hs
index 724f2339dd..724f2339dd 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Inflection.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Inflection.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/JSON.hs b/users/aspen/xanthous/src/Xanthous/Util/JSON.hs
index 91d1328e4a..91d1328e4a 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/JSON.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/JSON.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/Optparse.hs b/users/aspen/xanthous/src/Xanthous/Util/Optparse.hs
index dfa6537235..dfa6537235 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/Optparse.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/Optparse.hs
diff --git a/users/grfn/xanthous/src/Xanthous/Util/QuickCheck.hs b/users/aspen/xanthous/src/Xanthous/Util/QuickCheck.hs
index be12bc2945..aa881b3227 100644
--- a/users/grfn/xanthous/src/Xanthous/Util/QuickCheck.hs
+++ b/users/aspen/xanthous/src/Xanthous/Util/QuickCheck.hs
@@ -14,7 +14,6 @@ import Test.QuickCheck.Function
 import Test.QuickCheck.Instances.ByteString ()
 import Test.QuickCheck.Arbitrary.Generic
 import Data.Aeson
-import GHC.Generics (Rep)
 --------------------------------------------------------------------------------
 
 newtype FunctionShow a = FunctionShow a
@@ -31,12 +30,3 @@ newtype FunctionJSON a = FunctionJSON a
 
 instance (ToJSON a, FromJSON a) => Function (FunctionJSON a) where
   function = functionJSON
-
---------------------------------------------------------------------------------
-
-newtype GenericArbitrary a = GenericArbitrary a
-  deriving newtype Generic
-
-instance (Generic a, GArbitrary rep, Rep a ~ rep)
-  => Arbitrary (GenericArbitrary a) where
-  arbitrary = genericArbitrary
diff --git a/users/aspen/xanthous/src/Xanthous/keybindings.yaml b/users/aspen/xanthous/src/Xanthous/keybindings.yaml
new file mode 100644
index 0000000000..cffb27cb03
--- /dev/null
+++ b/users/aspen/xanthous/src/Xanthous/keybindings.yaml
@@ -0,0 +1,22 @@
+q: Quit
+?: Help
+.: Wait
+C-p: PreviousMessage
+',': PickUp
+d: Drop
+o: Open
+c: Close
+;: Look
+e: Eat
+S: Save
+r: Read
+i: ShowInventory
+I: DescribeInventory
+w: Wield
+f: Fire
+'<': GoUp
+'>': GoDown
+R: Rest
+
+# Debug commands
+M-r: ToggleRevealAll
diff --git a/users/grfn/xanthous/src/Xanthous/messages.yaml b/users/aspen/xanthous/src/Xanthous/messages.yaml
index b50dc321fd..bc08ec1ad2 100644
--- a/users/grfn/xanthous/src/Xanthous/messages.yaml
+++ b/users/aspen/xanthous/src/Xanthous/messages.yaml
@@ -1,4 +1,4 @@
-welcome: Welcome to Xanthous, {{characterName}}! It's dangerous out there, why not stay inside? Use hjklybnu to move.
+welcome: Welcome to Xanthous, {{characterName}}! It's dangerous out there, why not stay inside? Press ? for help.
 dead:
   - You have died...
   - You die...
@@ -9,6 +9,7 @@ generic:
   continue: Press enter to continue...
 
 save:
+  disabled: "Sorry, saving is currently disabled"
   location: "Enter filename to save to: "
   overwrite: "A file named {{filename}} already exists. Would you like to overwrite it? "
 
@@ -79,7 +80,9 @@ combat:
     generic:
       - You hit the {{creature.creatureType.name}}.
       - You attack the {{creature.creatureType.name}}.
-  creatureAttack: The {{creature.creatureType.name}} {{attackDescription}}
+  creatureAttack:
+    natural: The {{creature.creatureType.name}} {{attackDescription}}.
+    genericWeapon: The {{creature.creatureType.name}} attacks you with its {{item.itemType.name}}.
   killed:
     - You kill the {{creature.creatureType.name}}!
     - You've killed the {{creature.creatureType.name}}!
@@ -112,8 +115,30 @@ wield:
     - You can't wield anything in your backpack
     - You can't wield anything currently in your backpack
   menu: What would you like to wield?
-  # TODO: use actual hands
-  wielded : You wield the {{wieldedItem.itemType.name}} in your right hand.
+  hand: Wield in which hand?
+  wielded: You wield the {{item.wieldedItem.itemType.name}} in {{hand}}
+
+fire:
+  nothing:
+    - You don't currently have anything you can throw
+    - You don't have anything to throw
+  zeroRange:
+    - That item is too heavy to throw!
+    - That's too heavy to throw
+    - You're not strong enough to throw that any meaningful distance
+  menu: What would you like to throw?
+  target: Choose a target
+  atRange:
+    - It's too heavy for you to throw any further than this
+  fired:
+    noTarget:
+      - You throw the {{item.itemType.name}} at the ground
+    noDamage:
+      - You throw the {{item.itemType.name}} at the {{creature.creatureType.name}}. It doesn't seem to care.
+      - You throw the {{item.itemType.name}} at the {{creature.creatureType.name}}. It doesn't seem to do anything.
+      - You throw the {{item.itemType.name}} at the {{creature.creatureType.name}}. It doesn't seem to hurt it.
+    someDamage:
+      - You throw the {{item.itemType.name}} at the {{creature.creatureType.name}}. It hits it on the head!.
 
 drop:
   nothing: You aren't carrying anything
@@ -128,6 +153,7 @@ drop:
 
 autocommands:
   enemyInSight: There's a {{firstEntity.creatureType.name}} nearby!
+  resting: Resting...
   doneResting: Done resting
 ###
 
diff --git a/users/grfn/xanthous/test/Spec.hs b/users/aspen/xanthous/test/Spec.hs
index dfecfbdd21..51758d6a25 100644
--- a/users/grfn/xanthous/test/Spec.hs
+++ b/users/aspen/xanthous/test/Spec.hs
@@ -1,6 +1,7 @@
 --------------------------------------------------------------------------------
 import           Test.Prelude
 --------------------------------------------------------------------------------
+import qualified Xanthous.CommandSpec
 import qualified Xanthous.Data.EntitiesSpec
 import qualified Xanthous.Data.EntityCharSpec
 import qualified Xanthous.Data.EntityMap.GraphicsSpec
@@ -9,7 +10,9 @@ import qualified Xanthous.Data.LevelsSpec
 import qualified Xanthous.Data.MemoSpec
 import qualified Xanthous.Data.NestedMapSpec
 import qualified Xanthous.DataSpec
+import qualified Xanthous.Entities.CommonSpec
 import qualified Xanthous.Entities.RawsSpec
+import qualified Xanthous.Entities.RawTypesSpec
 import qualified Xanthous.Entities.CharacterSpec
 import qualified Xanthous.GameSpec
 import qualified Xanthous.Game.StateSpec
@@ -26,19 +29,22 @@ import qualified Xanthous.UtilSpec
 --------------------------------------------------------------------------------
 
 main :: IO ()
-main = defaultMain test
+main = defaultMainWithRerun test
 
 test :: TestTree
 test = testGroup "Xanthous"
-  [ Xanthous.Data.EntitiesSpec.test
+  [ Xanthous.CommandSpec.test
+  , Xanthous.Data.EntitiesSpec.test
   , Xanthous.Data.EntityMap.GraphicsSpec.test
   , Xanthous.Data.EntityMapSpec.test
   , Xanthous.Data.LevelsSpec.test
   , Xanthous.Data.MemoSpec.test
   , Xanthous.Data.NestedMapSpec.test
   , Xanthous.DataSpec.test
+  , Xanthous.Entities.CommonSpec.test
   , Xanthous.Entities.RawsSpec.test
   , Xanthous.Entities.CharacterSpec.test
+  , Xanthous.Entities.RawTypesSpec.test
   , Xanthous.GameSpec.test
   , Xanthous.Game.StateSpec.test
   , Xanthous.Game.PromptSpec.test
diff --git a/users/grfn/xanthous/test/Test/Prelude.hs b/users/aspen/xanthous/test/Test/Prelude.hs
index 2df0a6290a..75c1ebf5e7 100644
--- a/users/grfn/xanthous/test/Test/Prelude.hs
+++ b/users/aspen/xanthous/test/Test/Prelude.hs
@@ -5,6 +5,7 @@ module Test.Prelude
   , module Test.Tasty
   , module Test.Tasty.HUnit
   , module Test.Tasty.QuickCheck
+  , module Test.Tasty.Ingredients.Rerun
   , module Test.QuickCheck.Classes
   , testBatch
   , jsonRoundTrip
@@ -15,6 +16,7 @@ import           Xanthous.Prelude hiding (assert, elements)
 import           Test.Tasty
 import           Test.Tasty.QuickCheck
 import           Test.Tasty.HUnit
+import           Test.Tasty.Ingredients.Rerun
 import           Test.QuickCheck.Classes
 import           Test.QuickCheck.Checkers (TestBatch, EqProp ((=-=)))
 import           Test.QuickCheck.Instances.ByteString ()
diff --git a/users/aspen/xanthous/test/Xanthous/CommandSpec.hs b/users/aspen/xanthous/test/Xanthous/CommandSpec.hs
new file mode 100644
index 0000000000..13f69a808d
--- /dev/null
+++ b/users/aspen/xanthous/test/Xanthous/CommandSpec.hs
@@ -0,0 +1,40 @@
+--------------------------------------------------------------------------------
+module Xanthous.CommandSpec (main, test) where
+--------------------------------------------------------------------------------
+import           Test.Prelude
+--------------------------------------------------------------------------------
+import           Xanthous.Command
+--------------------------------------------------------------------------------
+import           Data.Aeson (fromJSON, Value(String))
+import qualified Data.Aeson as A
+import           Graphics.Vty.Input (Key(..), Modifier(..))
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain test
+
+test :: TestTree
+test = testGroup "Xanthous.CommandSpec"
+  [ testGroup "keybindings"
+    [ testCase "all are valid" $ keybindings `deepseq` pure ()
+    , testProperty "all non-move commands are bound" $ \cmd ->
+        let isn'tMove = case cmd of
+                          Move _ -> False
+                          StartAutoMove _ -> False
+                          _ -> True
+        in isn'tMove ==> member cmd commands
+    ]
+  , testGroup "instance FromJSON Keybinding" $
+    [ ("q", Keybinding (KChar 'q') [])
+    , ("<up>", Keybinding KUp [])
+    , ("<left>", Keybinding KLeft [])
+    , ("<right>", Keybinding KRight [])
+    , ("<down>", Keybinding KDown [])
+    , ("S-q", Keybinding (KChar 'q') [MShift])
+    , ("C-S-q", Keybinding (KChar 'q') [MCtrl, MShift])
+    , ("m-<UP>", Keybinding KUp [MMeta])
+    , ("S", Keybinding (KChar 'S') [])
+    ] <&> \(s, kb) ->
+      testCase (fromString $ unpack s <> " -> " <> show kb)
+       $ fromJSON (String s) @?= A.Success kb
+  ]
diff --git a/users/grfn/xanthous/test/Xanthous/Data/EntitiesSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/EntitiesSpec.hs
index e403503743..e403503743 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/EntitiesSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/EntitiesSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/EntityCharSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/EntityCharSpec.hs
index 9e8024c9d2..9e8024c9d2 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/EntityCharSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/EntityCharSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs
index fd37548ce8..fd37548ce8 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/EntityMap/GraphicsSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/EntityMapSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/EntityMapSpec.hs
index 7c5cad0196..7c5cad0196 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/EntityMapSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/EntityMapSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/LevelsSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/LevelsSpec.hs
index 4e46946a93..a752833162 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/LevelsSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/LevelsSpec.hs
@@ -33,13 +33,13 @@ test = testGroup "Xanthous.Data.Levels"
           === pos levels + 1
       , testProperty "maintains the invariant" $ \(levels :: Levels Int) genned ->
           let levels' = runIdentity . nextLevel (Identity genned) $ levels
-          in between 0 (length levels') $ pos levels'
+          in between 0 (toEnum $ length levels') $ pos levels'
       , testProperty "extract is total" $ \(levels :: Levels Int) genned ->
           let levels' = runIdentity . nextLevel (Identity genned) $ levels
           in total $ extract levels'
       , testProperty "uses the generated level as the next level"
         $ \(levels :: Levels Int) genned ->
-          let levels' = seek (length levels - 1) levels
+          let levels' = seek (toEnum $ length levels - 1) levels
               levels'' = runIdentity . nextLevel (Identity genned) $ levels'
           in counterexample (show levels'')
              $ extract levels'' === genned
@@ -52,7 +52,7 @@ test = testGroup "Xanthous.Data.Levels"
       , testProperty "maintains the invariant" $ \(levels :: Levels Int) ->
           case prevLevel levels of
             Nothing -> property Discard
-            Just levels' -> property $ between 0 (length levels') $ pos levels'
+            Just levels' -> property $ between 0 (toEnum $ length levels') $ pos levels'
       , testProperty "extract is total" $ \(levels :: Levels Int) ->
           case prevLevel levels of
             Nothing -> property Discard
diff --git a/users/grfn/xanthous/test/Xanthous/Data/MemoSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/MemoSpec.hs
index ad81f1984d..ad81f1984d 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/MemoSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/MemoSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Data/NestedMapSpec.hs b/users/aspen/xanthous/test/Xanthous/Data/NestedMapSpec.hs
index acf7a67268..acf7a67268 100644
--- a/users/grfn/xanthous/test/Xanthous/Data/NestedMapSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Data/NestedMapSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/DataSpec.hs b/users/aspen/xanthous/test/Xanthous/DataSpec.hs
index 9e67505ba9..9e67505ba9 100644
--- a/users/grfn/xanthous/test/Xanthous/DataSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/DataSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Entities/CharacterSpec.hs b/users/aspen/xanthous/test/Xanthous/Entities/CharacterSpec.hs
index 9210355d2d..734cce1efb 100644
--- a/users/grfn/xanthous/test/Xanthous/Entities/CharacterSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Entities/CharacterSpec.hs
@@ -3,7 +3,6 @@
 module Xanthous.Entities.CharacterSpec (main, test) where
 --------------------------------------------------------------------------------
 import           Test.Prelude
-import           Data.Vector.Lens (toVectorOf)
 --------------------------------------------------------------------------------
 import           Xanthous.Entities.Character
 import           Xanthous.Util (endoTimes)
@@ -22,21 +21,4 @@ test = testGroup "Xanthous.Entities.CharacterSpec"
           in _knuckleDamage knuckles' @?= 5
       ]
     ]
-  , testGroup "Inventory"
-    [ testProperty "items === itemsWithPosition . _2" $ \inv ->
-        inv ^.. items === inv ^.. itemsWithPosition . _2
-    , testGroup "removeItemFromPosition" $
-      let rewield w inv =
-            let (old, inv') = inv & wielded <<.~ w
-            in inv' & backpack <>~ toVectorOf (wieldedItems . wieldedItem) old
-      in [ (Backpack, \item -> backpack %~ (item ^. wieldedItem <|))
-         , (LeftHand, rewield . inLeftHand)
-         , (RightHand, rewield . inRightHand)
-         , (BothHands, rewield . review doubleHanded)
-         ] <&> \(pos, addItem) ->
-           testProperty (show pos) $ \inv item ->
-             let inv' = addItem item inv
-                 inv'' = removeItemFromPosition pos (item ^. wieldedItem) inv'
-             in inv'' ^.. items === inv ^.. items
-    ]
   ]
diff --git a/users/aspen/xanthous/test/Xanthous/Entities/CommonSpec.hs b/users/aspen/xanthous/test/Xanthous/Entities/CommonSpec.hs
new file mode 100644
index 0000000000..a6f8401cf7
--- /dev/null
+++ b/users/aspen/xanthous/test/Xanthous/Entities/CommonSpec.hs
@@ -0,0 +1,65 @@
+--------------------------------------------------------------------------------
+module Xanthous.Entities.CommonSpec (main, test) where
+--------------------------------------------------------------------------------
+import           Test.Prelude
+import           Data.Vector.Lens (toVectorOf)
+--------------------------------------------------------------------------------
+import           Xanthous.Entities.Common
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain test
+
+newtype OneHand = OneHand Hand
+  deriving stock Show
+
+instance Arbitrary OneHand where
+  arbitrary = OneHand <$> elements [LeftHand, RightHand]
+
+otherHand :: Hand -> Hand
+otherHand LeftHand = RightHand
+otherHand RightHand = LeftHand
+otherHand BothHands = error "OtherHand BothHands"
+
+test :: TestTree
+test = testGroup "Xanthous.Entities.CommonSpec"
+  [ testGroup "Inventory"
+    [ testProperty "items === itemsWithPosition . _2" $ \inv ->
+        inv ^.. items === inv ^.. itemsWithPosition . _2
+    , testGroup "removeItemFromPosition" $
+      let rewield w inv =
+            let (old, inv') = inv & wielded <<.~ w
+            in inv' & backpack <>~ toVectorOf (wieldedItems . wieldedItem) old
+      in [ (Backpack, \item -> backpack %~ (item ^. wieldedItem <|))
+         , (InHand LeftHand, rewield . inLeftHand)
+         , (InHand RightHand, rewield . inRightHand)
+         , (InHand BothHands, rewield . review doubleHanded)
+         ] <&> \(pos, addItem) ->
+           testProperty (show pos) $ \inv item ->
+             let inv' = addItem item inv
+                 inv'' = removeItemFromPosition pos (item ^. wieldedItem) inv'
+             in inv'' ^.. items === inv ^.. items
+    ]
+  , testGroup "Wielded items"
+    [ testGroup "wieldInHand"
+      [ testProperty "puts the item in the hand" $ \w hand item ->
+          let (_, w') = wieldInHand hand item w
+          in itemsInHand hand w' === [item]
+      , testProperty "returns items in both hands when wielding double-handed"
+        $ \lh rh newItem ->
+          let w = Hands (Just lh) (Just rh)
+              (prevItems, _) = wieldInHand BothHands newItem w
+          in prevItems === [lh, rh]
+      , testProperty "wielding in one hand leaves the item in the other hand"
+        $ \(OneHand h) existingItem newItem ->
+          let (_, w) = wieldInHand h existingItem nothingWielded
+              (prevItems, w') = wieldInHand (otherHand h) newItem w
+          in   prevItems === []
+          .&&. sort (w' ^.. wieldedItems) === sort [existingItem, newItem]
+      , testProperty "always leaves the same items overall" $ \w hand item ->
+          let (prevItems, w') = wieldInHand hand item w
+          in  sort (prevItems <> (w' ^.. wieldedItems))
+          === sort (item : w ^.. wieldedItems)
+      ]
+    ]
+  ]
diff --git a/users/aspen/xanthous/test/Xanthous/Entities/RawTypesSpec.hs b/users/aspen/xanthous/test/Xanthous/Entities/RawTypesSpec.hs
new file mode 100644
index 0000000000..e23f7faba3
--- /dev/null
+++ b/users/aspen/xanthous/test/Xanthous/Entities/RawTypesSpec.hs
@@ -0,0 +1,45 @@
+{-# LANGUAGE RecordWildCards #-}
+--------------------------------------------------------------------------------
+module Xanthous.Entities.RawTypesSpec (main, test) where
+--------------------------------------------------------------------------------
+import           Test.Prelude
+--------------------------------------------------------------------------------
+import           Data.Interval (Extended(..), (<=..<=))
+--------------------------------------------------------------------------------
+import           Xanthous.Entities.RawTypes
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = defaultMain test
+
+test :: TestTree
+test = testGroup "Xanthous.Entities.RawTypesSpec"
+  [ testGroup "CreatureGenerateParams"
+    [ testGroup "Ord laws"
+      [ testProperty "comparability" $ \(a :: CreatureGenerateParams) b ->
+          a <= b || b <= a
+      , testProperty "transitivity" $ \(a :: CreatureGenerateParams) b c ->
+          a <= b && b <= c ==> a <= c
+      , testProperty "reflexivity" $ \(a :: CreatureGenerateParams) ->
+          a <= a
+      , testProperty "antisymmetry" $ \(a :: CreatureGenerateParams) b ->
+          (a <= b && b <= a) == (a == b)
+      ]
+    , testGroup "canGenerate" $
+      let makeParams minB maxB =
+            let _levelRange = maybe NegInf Finite minB <=..<= maybe PosInf Finite maxB
+                _equippedItem = Nothing
+            in CreatureGenerateParams {..}
+      in
+        [ testProperty "no bounds" $ \level ->
+            let gps = makeParams Nothing Nothing
+            in canGenerate level gps
+        , testProperty "min bound" $ \level minB ->
+            let gps = makeParams (Just minB) Nothing
+            in canGenerate level gps === (level >= minB)
+        , testProperty "max bound" $ \level maxB ->
+            let gps = makeParams Nothing (Just maxB)
+            in canGenerate level gps === (level <= maxB)
+        ]
+    ]
+  ]
diff --git a/users/aspen/xanthous/test/Xanthous/Entities/RawsSpec.hs b/users/aspen/xanthous/test/Xanthous/Entities/RawsSpec.hs
new file mode 100644
index 0000000000..b6c80be51b
--- /dev/null
+++ b/users/aspen/xanthous/test/Xanthous/Entities/RawsSpec.hs
@@ -0,0 +1,30 @@
+-- |
+
+module Xanthous.Entities.RawsSpec (main, test) where
+
+import Test.Prelude
+import Xanthous.Entities.Raws
+import Xanthous.Entities.RawTypes
+       (_Creature, entityName, generateParams, HasEquippedItem (equippedItem))
+
+main :: IO ()
+main = defaultMain test
+
+test :: TestTree
+test = testGroup "Xanthous.Entities.Raws"
+  [ testGroup "raws"
+    [ testCase "are all valid" $ raws `deepseq` pure ()
+    , testCase "all CreatureEquippedItems reference existent entity names" $
+      let notFound
+            = raws
+              ^.. folded
+              . _Creature
+              . generateParams
+              . _Just
+              . equippedItem
+              . _Just
+              . entityName
+              . filtered (isNothing . raw)
+      in null notFound @? ("Some entities weren't found: " <> show notFound)
+    ]
+  ]
diff --git a/users/grfn/xanthous/test/Xanthous/Game/PromptSpec.hs b/users/aspen/xanthous/test/Xanthous/Game/PromptSpec.hs
index d7a3df4aca..d7a3df4aca 100644
--- a/users/grfn/xanthous/test/Xanthous/Game/PromptSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Game/PromptSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Game/StateSpec.hs b/users/aspen/xanthous/test/Xanthous/Game/StateSpec.hs
index b02abb04b4..34584f73b2 100644
--- a/users/grfn/xanthous/test/Xanthous/Game/StateSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Game/StateSpec.hs
@@ -4,9 +4,10 @@ module Xanthous.Game.StateSpec (main, test) where
 import           Test.Prelude
 --------------------------------------------------------------------------------
 import           Xanthous.Game.State
-import           Xanthous.Entities.Raws (raws, entityFromRaw)
-import Control.Monad.Random (evalRandT)
-import System.Random (getStdGen)
+import           Xanthous.Entities.Raws (raws)
+import           Xanthous.Generators.Level.LevelContents (entityFromRaw)
+import           Control.Monad.Random (evalRandT)
+import           System.Random (getStdGen)
 --------------------------------------------------------------------------------
 
 main :: IO ()
diff --git a/users/grfn/xanthous/test/Xanthous/GameSpec.hs b/users/aspen/xanthous/test/Xanthous/GameSpec.hs
index 2fa8527d0e..2fa8527d0e 100644
--- a/users/grfn/xanthous/test/Xanthous/GameSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/GameSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs b/users/aspen/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs
index 2d70a55e88..b53c657f75 100644
--- a/users/grfn/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Generators/Level/UtilSpec.hs
@@ -6,7 +6,7 @@ import Test.Prelude
 import System.Random (mkStdGen)
 import Control.Monad.Random (runRandT)
 import Data.Array.ST (STUArray, runSTUArray, thaw)
-import Data.Array.IArray (bounds)
+import Data.Array.IArray (bounds, array)
 import Data.Array.MArray (newArray, readArray, writeArray)
 import Data.Array (Array, range, listArray, Ix)
 import Control.Monad.ST (ST, runST)
@@ -15,6 +15,7 @@ import Linear.V2
 --------------------------------------------------------------------------------
 import Xanthous.Util
 import Xanthous.Data (width, height)
+--------------------------------------------------------------------------------
 import Xanthous.Generators.Level.Util
 --------------------------------------------------------------------------------
 
@@ -57,6 +58,30 @@ test = testGroup "Xanthous.Generators.Util"
             numAliveNeighborsM mArr loc
           res = runST act
         in counterexample (show res) $ between 0 8 res
+    , testCase "on the outer x edge" $
+      let act :: forall s. ST s Word
+          act = do
+            cells <- thaw @_ @_ @_ @(STUArray s) $ array @Array @Bool @(V2 Word)
+              (V2 0 0, V2 2 2)
+              [ (V2 0 0, True),  (V2 1 0, True),  (V2 2 0, True)
+              , (V2 0 1, False), (V2 1 1, False), (V2 2 1, True)
+              , (V2 0 2, True),  (V2 1 2, True),  (V2 2 2, True)
+              ]
+            numAliveNeighborsM cells (V2 0 1)
+          res = runST act
+      in res @?= 7
+    , testCase "on the outer y edge" $
+      let act :: forall s. ST s Word
+          act = do
+            cells <- thaw @_ @_ @_ @(STUArray s) $ array @Array @Bool @(V2 Word)
+              (V2 0 0, V2 2 2)
+              [ (V2 0 0, True),  (V2 1 0, True),  (V2 2 0, True)
+              , (V2 0 1, False), (V2 1 1, False), (V2 2 1, True)
+              , (V2 0 2, True),  (V2 1 2, True),  (V2 2 2, True)
+              ]
+            numAliveNeighborsM cells (V2 1 0)
+          res = runST act
+      in res @?= 6
     ]
   , testGroup "numAliveNeighbors"
     [ testProperty "is equivalient to runST . numAliveNeighborsM . thaw" $
@@ -68,6 +93,24 @@ test = testGroup "Xanthous.Generators.Util"
             numAliveNeighborsM mArr loc
           res = runST act
         in numAliveNeighbors arr loc === res
+    , testCase "on the outer x edge" $
+      let cells =
+            array @Array @Bool @(V2 Word)
+            (V2 0 0, V2 2 2)
+            [ (V2 0 0, True),  (V2 1 0, True),  (V2 2 0, True)
+            , (V2 0 1, False), (V2 1 1, False), (V2 2 1, True)
+            , (V2 0 2, True),  (V2 1 2, True),  (V2 2 2, True)
+            ]
+      in numAliveNeighbors cells (V2 0 1) @?= 7
+    , testCase "on the outer y edge" $
+      let cells =
+            array @Array @Bool @(V2 Word)
+            (V2 0 0, V2 2 2)
+            [ (V2 0 0, True),  (V2 1 0, True),  (V2 2 0, True)
+            , (V2 0 1, False), (V2 1 1, False), (V2 2 1, True)
+            , (V2 0 2, True),  (V2 1 2, True),  (V2 2 2, True)
+            ]
+      in numAliveNeighbors cells (V2 1 0) @?= 6
     ]
   , testGroup "cloneMArray"
       [ testCase "clones the array" $ runST $
diff --git a/users/grfn/xanthous/test/Xanthous/MessageSpec.hs b/users/aspen/xanthous/test/Xanthous/MessageSpec.hs
index 2068e338ba..2068e338ba 100644
--- a/users/grfn/xanthous/test/Xanthous/MessageSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/MessageSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Messages/TemplateSpec.hs b/users/aspen/xanthous/test/Xanthous/Messages/TemplateSpec.hs
index 2a3873c3b0..2a3873c3b0 100644
--- a/users/grfn/xanthous/test/Xanthous/Messages/TemplateSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Messages/TemplateSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/OrphansSpec.hs b/users/aspen/xanthous/test/Xanthous/OrphansSpec.hs
index 0d800e8a91..0d800e8a91 100644
--- a/users/grfn/xanthous/test/Xanthous/OrphansSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/OrphansSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/RandomSpec.hs b/users/aspen/xanthous/test/Xanthous/RandomSpec.hs
index c88bd95629..c88bd95629 100644
--- a/users/grfn/xanthous/test/Xanthous/RandomSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/RandomSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Util/GraphSpec.hs b/users/aspen/xanthous/test/Xanthous/Util/GraphSpec.hs
index 35ff090b28..35ff090b28 100644
--- a/users/grfn/xanthous/test/Xanthous/Util/GraphSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Util/GraphSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Util/GraphicsSpec.hs b/users/aspen/xanthous/test/Xanthous/Util/GraphicsSpec.hs
index 61e5892803..61e5892803 100644
--- a/users/grfn/xanthous/test/Xanthous/Util/GraphicsSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Util/GraphicsSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/Util/InflectionSpec.hs b/users/aspen/xanthous/test/Xanthous/Util/InflectionSpec.hs
index fad8410431..fad8410431 100644
--- a/users/grfn/xanthous/test/Xanthous/Util/InflectionSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/Util/InflectionSpec.hs
diff --git a/users/grfn/xanthous/test/Xanthous/UtilSpec.hs b/users/aspen/xanthous/test/Xanthous/UtilSpec.hs
index 684a03b2c7..684a03b2c7 100644
--- a/users/grfn/xanthous/test/Xanthous/UtilSpec.hs
+++ b/users/aspen/xanthous/test/Xanthous/UtilSpec.hs
diff --git a/users/grfn/xanthous/xanthous.cabal b/users/aspen/xanthous/xanthous.cabal
index fc17ceaa20..12222c2673 100644
--- a/users/grfn/xanthous/xanthous.cabal
+++ b/users/aspen/xanthous/xanthous.cabal
@@ -1,10 +1,10 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.34.4.
+-- This file has been generated from package.yaml by hpack version 0.35.0.
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: c12ae4038a2e1f287de557b72b8493da05ccbf428e7ac7862349c46d241f342f
+-- hash: b3bf8e65d621856081832c9d3c8e8ad38799e23a7f5084dc4f972daa654a0ff3
 
 name:           xanthous
 version:        0.1.0.0
@@ -47,6 +47,7 @@ library
       Xanthous.Data.NestedMap
       Xanthous.Data.VectorBag
       Xanthous.Entities.Character
+      Xanthous.Entities.Common
       Xanthous.Entities.Creature
       Xanthous.Entities.Creature.Hippocampus
       Xanthous.Entities.Draw.Util
@@ -75,6 +76,7 @@ library
       Xanthous.Messages.Template
       Xanthous.Monad
       Xanthous.Orphans
+      Xanthous.Physics
       Xanthous.Prelude
       Xanthous.Random
       Xanthous.Util
@@ -117,7 +119,7 @@ library
       TypeFamilies
       TypeOperators
       ViewPatterns
-  ghc-options: -Wall
+  ghc-options: -Wall -fconstraint-solver-iterations=6
   build-depends:
       JuicyPixels
     , MonadRandom
@@ -218,7 +220,7 @@ executable xanthous
       TypeFamilies
       TypeOperators
       ViewPatterns
-  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -O2
+  ghc-options: -Wall -fconstraint-solver-iterations=6 -threaded -rtsopts -with-rtsopts=-N -O2
   build-depends:
       JuicyPixels
     , MonadRandom
@@ -291,6 +293,7 @@ test-suite test
   main-is: Spec.hs
   other-modules:
       Test.Prelude
+      Xanthous.CommandSpec
       Xanthous.Data.EntitiesSpec
       Xanthous.Data.EntityCharSpec
       Xanthous.Data.EntityMap.GraphicsSpec
@@ -300,7 +303,9 @@ test-suite test
       Xanthous.Data.NestedMapSpec
       Xanthous.DataSpec
       Xanthous.Entities.CharacterSpec
+      Xanthous.Entities.CommonSpec
       Xanthous.Entities.RawsSpec
+      Xanthous.Entities.RawTypesSpec
       Xanthous.Game.PromptSpec
       Xanthous.Game.StateSpec
       Xanthous.GameSpec
@@ -344,7 +349,7 @@ test-suite test
       TypeFamilies
       TypeOperators
       ViewPatterns
-  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -O0
+  ghc-options: -Wall -fconstraint-solver-iterations=6 -threaded -rtsopts -with-rtsopts=-N -O0
   build-depends:
       JuicyPixels
     , MonadRandom
@@ -404,6 +409,7 @@ test-suite test
     , tasty
     , tasty-hunit
     , tasty-quickcheck
+    , tasty-rerun
     , text
     , text-zipper
     , tomland
@@ -454,7 +460,7 @@ benchmark benchmark
       TypeFamilies
       TypeOperators
       ViewPatterns
-  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
+  ghc-options: -Wall -fconstraint-solver-iterations=6 -threaded -rtsopts -with-rtsopts=-N
   build-depends:
       JuicyPixels
     , MonadRandom
diff --git a/users/cynthia/OWNERS b/users/cynthia/OWNERS
index da62f3777a..762a4ec9ea 100644
--- a/users/cynthia/OWNERS
+++ b/users/cynthia/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - cynthia
+set noparent
+
+cynthia
diff --git a/users/cynthia/keys.nix b/users/cynthia/keys.nix
index bac8dc1c57..e2f4ce488c 100644
--- a/users/cynthia/keys.nix
+++ b/users/cynthia/keys.nix
@@ -4,4 +4,4 @@
   all = [
     "cert-authority ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICsj3W6QczgxE3s5GGT8qg0aLrCM+QeRnSq9RkiZtKvz meow"
   ];
-}
\ No newline at end of file
+}
diff --git a/users/edef/OWNERS b/users/edef/OWNERS
index 05f7639c89..c0d1024f14 100644
--- a/users/edef/OWNERS
+++ b/users/edef/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - edef
+set noparent
+
+edef
diff --git a/users/edef/depot-scan/wrap.nix b/users/edef/depot-scan/wrap.nix
index dcb557a24b..77362b3f61 100644
--- a/users/edef/depot-scan/wrap.nix
+++ b/users/edef/depot-scan/wrap.nix
@@ -3,13 +3,14 @@
 let
 
   global = {
-    import = global.scopedImport {};
+    import = global.scopedImport { };
     scopedImport = x: builtins.scopedImport (global // x);
     builtins = builtins // {
       inherit (global) import scopedImport;
       readFile = path: builtins.trace "depot-scan '${toString path}'" (builtins.readFile path);
-      readDir  = path: builtins.trace "depot-scan '${toString path}'" (builtins.readDir  path);
+      readDir = path: builtins.trace "depot-scan '${toString path}'" (builtins.readDir path);
     };
   };
 
-in global.import
+in
+global.import
diff --git a/users/edef/refscan/.gitignore b/users/edef/refscan/.gitignore
new file mode 100644
index 0000000000..ee4b088aee
--- /dev/null
+++ b/users/edef/refscan/.gitignore
@@ -0,0 +1,5 @@
+# SPDX-FileCopyrightText: edef <edef@edef.eu>
+# SPDX-License-Identifier: CC0-1.0
+
+/target
+**/*.rs.bk
diff --git a/users/edef/refscan/Cargo.lock b/users/edef/refscan/Cargo.lock
new file mode 100644
index 0000000000..a3515a75d7
--- /dev/null
+++ b/users/edef/refscan/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "refscan"
+version = "0.1.0"
diff --git a/users/edef/refscan/Cargo.lock.license b/users/edef/refscan/Cargo.lock.license
new file mode 100644
index 0000000000..2cd6276751
--- /dev/null
+++ b/users/edef/refscan/Cargo.lock.license
@@ -0,0 +1,3 @@
+SPDX-FileCopyrightText: edef <edef@edef.eu>
+SPDX-License-Identifier: CC0-1.0
+
diff --git a/users/edef/refscan/Cargo.toml b/users/edef/refscan/Cargo.toml
new file mode 100644
index 0000000000..dfac9a899e
--- /dev/null
+++ b/users/edef/refscan/Cargo.toml
@@ -0,0 +1,10 @@
+# SPDX-FileCopyrightText: edef <edef@edef.eu>
+# SPDX-License-Identifier: MPL-2.0
+
+[package]
+name = "refscan"
+version = "0.1.0"
+authors = ["edef <edef@edef.eu>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/users/edef/refscan/LICENSES/CC0-1.0.txt b/users/edef/refscan/LICENSES/CC0-1.0.txt
new file mode 100644
index 0000000000..0e259d42c9
--- /dev/null
+++ b/users/edef/refscan/LICENSES/CC0-1.0.txt
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/users/edef/refscan/LICENSES/MPL-2.0.txt b/users/edef/refscan/LICENSES/MPL-2.0.txt
new file mode 100644
index 0000000000..ee6256cdb6
--- /dev/null
+++ b/users/edef/refscan/LICENSES/MPL-2.0.txt
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in 
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.
diff --git a/users/edef/refscan/src/lib.rs b/users/edef/refscan/src/lib.rs
new file mode 100644
index 0000000000..3d4a07f3dd
--- /dev/null
+++ b/users/edef/refscan/src/lib.rs
@@ -0,0 +1,154 @@
+// SPDX-FileCopyrightText: edef <edef@edef.eu>
+// SPDX-License-Identifier: MPL-2.0
+
+use self::simd::u8x32;
+
+fn prefilter(haystack: u8x32) -> u32 {
+    let alp = haystack.gt(u8x32::splat(b'a' - 1)) & haystack.lt(u8x32::splat(b'z' + 1));
+    let num = haystack.gt(u8x32::splat(b'0' - 1)) & haystack.lt(u8x32::splat(b'9' + 1));
+    alp | num
+}
+
+/// scan_clean returns `Err(&buffer[..n])` of known pointer-free data,
+/// or `Ok(buffer)` if the entire buffer is pointer-free.
+pub fn scan_clean(buffer: &[u8]) -> Result<&[u8], &[u8]> {
+    let buffer = {
+        let n = buffer.len() & !31;
+        &buffer[..n]
+    };
+
+    let mut masks = buffer
+        .chunks_exact(32)
+        .map(|chunk| prefilter(u8x32::from_slice_unaligned(chunk)))
+        .enumerate()
+        .map(|e| (e.0 * 32, e.1))
+        .peekable();
+
+    while let Some((offset, mask)) = masks.next() {
+        let peek = masks.peek().map(|x| x.1).unwrap_or(!0 >> 1);
+        let n = (!mask).leading_zeros() + (!peek).trailing_zeros();
+        if n >= 32 {
+            let offset = offset + mask.trailing_zeros() as usize;
+            return Err(&buffer[..offset]);
+        }
+    }
+
+    Ok(buffer)
+}
+
+#[cfg(test)]
+mod test {
+    #[test]
+    fn scan_tail() {
+        let buffer = b"_xfbmj7sl2ikicym9x3yq7cms5qx1w39k";
+        assert_eq!(crate::scan_clean(buffer), Err(&buffer[..1]));
+    }
+    #[test]
+    fn scan_straddle() {
+        let buffer = b"________________xfbmj7sl2ikicym9x3yq7cms5qx1w39k________________";
+        assert_eq!(crate::scan_clean(buffer), Err(&buffer[..16]));
+    }
+    #[test]
+    fn scan_clean() {
+        let buffer = b"x_______________xfbmj7sl2ikicym9x3yq-cms5qx1w3-k________________";
+        assert_eq!(crate::scan_clean(buffer), Ok(&buffer[..]));
+    }
+}
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+mod simd {
+    #[cfg(target_arch = "x86")]
+    use std::arch::x86 as arch;
+    #[cfg(target_arch = "x86_64")]
+    use std::arch::x86_64 as arch;
+    use {
+        arch::{__m256i, _mm256_cmpgt_epi8, _mm256_movemask_epi8, _mm256_set1_epi8},
+        std::ptr,
+    };
+
+    #[allow(non_camel_case_types)]
+    #[derive(Copy, Clone)]
+    pub struct u8x32(__m256i);
+
+    impl u8x32 {
+        #[inline(always)]
+        pub fn from_slice_unaligned(slice: &[u8]) -> Self {
+            assert_eq!(slice.len(), 32);
+            u8x32(unsafe { ptr::read_unaligned(slice.as_ptr().cast()) })
+        }
+
+        #[inline(always)]
+        pub fn splat(x: u8) -> Self {
+            u8x32(unsafe { _mm256_set1_epi8(x as i8) })
+        }
+
+        #[inline(always)]
+        pub fn gt(self, b: Self) -> u32 {
+            unsafe { _mm256_movemask_epi8(_mm256_cmpgt_epi8(self.0, b.0)) as u32 }
+        }
+
+        #[inline(always)]
+        pub fn lt(self, b: Self) -> u32 {
+            b.gt(self)
+        }
+    }
+}
+
+#[cfg(target_arch = "aarch64")]
+mod simd {
+    use std::{
+        arch::aarch64::{
+            uint8x16_t as u8x16, vaddv_u8, vandq_u8, vcgtq_u8, vdupq_n_u8, vget_high_u8,
+            vget_low_u8, vshlq_u8,
+        },
+        mem, ptr,
+    };
+
+    #[allow(non_camel_case_types)]
+    #[derive(Copy, Clone)]
+    #[repr(transparent)]
+    pub struct u8x32([u8x16; 2]);
+
+    impl u8x32 {
+        #[cfg(target_endian = "little")]
+        #[inline(always)]
+        pub fn from_slice_unaligned(slice: &[u8]) -> Self {
+            assert_eq!(slice.len(), 32);
+            u8x32(unsafe { ptr::read_unaligned(slice.as_ptr().cast()) })
+        }
+
+        #[inline(always)]
+        pub fn splat(x: u8) -> Self {
+            u8x32(unsafe {
+                let x = vdupq_n_u8(x);
+                [x, x]
+            })
+        }
+
+        #[inline(always)]
+        pub fn gt(&self, b: Self) -> u32 {
+            let u8x32([al, ah]) = *self;
+            let u8x32([bl, bh]) = b;
+
+            fn f(a: u8x16, b: u8x16) -> u32 {
+                unsafe {
+                    let c = vshlq_u8(
+                        vandq_u8(vdupq_n_u8(0x80), vcgtq_u8(a, b)),
+                        mem::transmute([
+                            -7, -6, -5, -4, -3, -2, -1, 0, -7, -6, -5, -4, -3, -2, -1, 0i8,
+                        ]),
+                    );
+
+                    (vaddv_u8(vget_low_u8(c)) as u32) << 0 | (vaddv_u8(vget_high_u8(c)) as u32) << 8
+                }
+            }
+
+            f(al, bl) << 0 | f(ah, bh) << 16
+        }
+
+        #[inline(always)]
+        pub fn lt(self, b: Self) -> u32 {
+            b.gt(self)
+        }
+    }
+}
diff --git a/users/edef/refscan/src/main.rs b/users/edef/refscan/src/main.rs
new file mode 100644
index 0000000000..e572abf0a1
--- /dev/null
+++ b/users/edef/refscan/src/main.rs
@@ -0,0 +1,58 @@
+// SPDX-FileCopyrightText: edef <edef@edef.eu>
+// SPDX-License-Identifier: MPL-2.0
+
+use std::{
+    collections::BTreeSet as Set,
+    convert::TryInto,
+    io::{self, Read},
+    str,
+};
+
+fn main() {
+    let max_refs: Set<[u8; 32]> = include_str!("../testdata/maxrefs")
+        .lines()
+        .map(|l| l.as_bytes().try_into().unwrap())
+        .collect();
+
+    let input = {
+        let stdin = io::stdin();
+        let mut buffer = Vec::new();
+        stdin.lock().read_to_end(&mut buffer).unwrap();
+        buffer
+    };
+
+    let base = input.as_ptr() as usize;
+    let mut input: &[u8] = &input;
+    while input.len() >= 32 {
+        match refscan::scan_clean(&input) {
+            Ok(buffer) | Err(buffer) => {
+                let n = buffer.len();
+                input = &input[n..];
+            }
+        }
+
+        let buffer = {
+            let idx = input.iter().position(|x| match x {
+                b'a'..=b'z' | b'0'..=b'9' => false,
+                _ => true,
+            });
+            idx.map(|idx| &input[..idx]).unwrap_or(input)
+        };
+
+        for chunk in buffer.windows(32) {
+            let offset = (chunk.as_ptr() as usize) - base;
+            let chunk = {
+                let mut fixed = [0u8; 32];
+                fixed.copy_from_slice(chunk);
+                fixed
+            };
+            if max_refs.contains(&chunk) {
+                let seen = unsafe { str::from_utf8_unchecked(&chunk) };
+                println!("{} {}", seen, offset);
+            }
+        }
+
+        let n = buffer.len();
+        input = &input[n..];
+    }
+}
diff --git a/users/edef/refscan/testdata/.gitignore b/users/edef/refscan/testdata/.gitignore
new file mode 100644
index 0000000000..1d278bd6ce
--- /dev/null
+++ b/users/edef/refscan/testdata/.gitignore
@@ -0,0 +1,6 @@
+# SPDX-FileCopyrightText: edef <edef@edef.eu>
+# SPDX-License-Identifier: CC0-1.0
+
+/maxrefs
+/nar
+/result
diff --git a/users/edef/refscan/testdata/generate.sh b/users/edef/refscan/testdata/generate.sh
new file mode 100755
index 0000000000..9f416024c1
--- /dev/null
+++ b/users/edef/refscan/testdata/generate.sh
@@ -0,0 +1,8 @@
+#! /usr/bin/env bash
+# SPDX-FileCopyrightText: edef <edef@edef.eu>
+# SPDX-License-Identifier: CC0-1.0
+set -euo pipefail
+
+drv=$(nix-instantiate '<nixpkgs>' -A ghc)
+nix --extra-experimental-features nix-command show-derivation -r "$drv" | jq -r '.[] | .outputs[].path, .inputSrcs[]' | sort -u | cut -d/ -f4 | cut -d- -f1 > maxrefs
+nix-store --dump "$(nix-build "$drv")" > nar
diff --git a/users/ericvolp12/OWNERS b/users/ericvolp12/OWNERS
index 5a012a695b..a43910023c 100644
--- a/users/ericvolp12/OWNERS
+++ b/users/ericvolp12/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - ericvolp12
+set noparent
+
+ericvolp12
diff --git a/users/espes/OWNERS b/users/espes/OWNERS
new file mode 100644
index 0000000000..257a1b8200
--- /dev/null
+++ b/users/espes/OWNERS
@@ -0,0 +1 @@
+espes
diff --git a/users/eta/OWNERS b/users/eta/OWNERS
index f212e89e2a..59e5e2120a 100644
--- a/users/eta/OWNERS
+++ b/users/eta/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - eta
+set noparent
+
+eta
diff --git a/users/eta/keys.nix b/users/eta/keys.nix
index 247a182843..ebdc8479a5 100644
--- a/users/eta/keys.nix
+++ b/users/eta/keys.nix
@@ -9,4 +9,4 @@ let
     whitby = [ keys.yubikey4 keys.yubikey5 ];
   };
 in
-  configs
+configs
diff --git a/users/firefly/OWNERS b/users/firefly/OWNERS
index 55d62a5723..1b7dd7189f 100644
--- a/users/firefly/OWNERS
+++ b/users/firefly/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - firefly
+set noparent
+
+firefly
diff --git a/users/flokli/OWNERS b/users/flokli/OWNERS
index 63e0fbda3c..a70f5eefff 100644
--- a/users/flokli/OWNERS
+++ b/users/flokli/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - flokli
+set noparent
+
+flokli
diff --git a/users/flokli/archeology/OWNERS b/users/flokli/archeology/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/users/flokli/archeology/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/users/flokli/archeology/README.md b/users/flokli/archeology/README.md
new file mode 100644
index 0000000000..e4cd9b84b0
--- /dev/null
+++ b/users/flokli/archeology/README.md
@@ -0,0 +1,5 @@
+# archeology
+
+This directory contains various scripts and helpers used for nix-archeology tasks.
+
+It's used from some of the archeology instances, as well as standalone.
diff --git a/users/flokli/archeology/default.nix b/users/flokli/archeology/default.nix
new file mode 100644
index 0000000000..d642399cbe
--- /dev/null
+++ b/users/flokli/archeology/default.nix
@@ -0,0 +1,51 @@
+{ depot, pkgs, ... }:
+
+let
+  clickhouseConfigAWS = builtins.toFile "clickhouse-local.xml" ''
+    <clickhouse>
+        <s3>
+          <use_environment_credentials>true</use_environment_credentials>
+        </s3>
+    </clickhouse>
+  '';
+  # clickhouse has a very odd AWS config concept.
+  # Configure it to be a bit more sane.
+  clickhoseLocalFixedAWS = pkgs.runCommand "clickhouse-local-fixed"
+    {
+      nativeBuildInputs = [ pkgs.makeWrapper ];
+    } ''
+    mkdir -p $out/bin
+    makeWrapper ${pkgs.clickhouse}/bin/clickhouse-local $out/bin/clickhouse-local \
+      --append-flags "-C ${clickhouseConfigAWS}"
+  '';
+in
+
+depot.nix.readTree.drvTargets {
+  inherit clickhoseLocalFixedAWS;
+  parse-bucket-logs = pkgs.runCommand "archeology-parse-bucket-logs"
+    {
+      nativeBuildInputs = [ pkgs.makeWrapper ];
+    } ''
+    mkdir -p $out/bin
+    makeWrapper ${(pkgs.writers.writeRust "parse-bucket-logs-unwrapped" {} ./parse_bucket_logs.rs)} $out/bin/archeology-parse-bucket-logs \
+      --prefix PATH : ${pkgs.lib.makeBinPath [ clickhoseLocalFixedAWS ]}
+  '';
+
+  shell = pkgs.mkShell {
+    name = "archeology-shell";
+    packages = with pkgs; [ awscli2 clickhoseLocalFixedAWS rust-analyzer rustc rustfmt ];
+
+    AWS_PROFILE = "sso";
+    AWS_CONFIG_FILE = pkgs.writeText "aws-config" ''
+      [sso-session nixos]
+      sso_region = eu-north-1
+      sso_start_url = https://nixos.awsapps.com/start
+      sso_registration_scopes = sso:account:access
+
+      [profile "sso"]
+      sso_session = nixos
+      sso_account_id = 080433136561
+      sso_role_name = archeologist
+    '';
+  };
+}
diff --git a/users/flokli/archeology/parse_bucket_logs.rs b/users/flokli/archeology/parse_bucket_logs.rs
new file mode 100644
index 0000000000..3ab2e133b3
--- /dev/null
+++ b/users/flokli/archeology/parse_bucket_logs.rs
@@ -0,0 +1,42 @@
+use std::env;
+use std::process::Command;
+use std::process::ExitCode;
+
+fn main() -> ExitCode {
+    let args: Vec<String> = env::args().collect();
+    if args.len() != 3 {
+        eprintln!("needs two args, input s3 url (glob) and output pq file");
+        return ExitCode::FAILURE;
+    }
+
+    let input_files = &args[1];
+    let output_file = &args[2];
+
+    let mut cmd = Command::new("clickhouse-local");
+    cmd.arg("--progress")
+        .arg("-q")
+        .arg(format!(r#"SELECT
+        key,
+        toInt64(nullif(http_status, '-')) AS http_status,
+        toInt64(nullif(object_size_str, '-')) AS object_size,
+        toInt64(nullif(bytes_sent_str, '-')) AS bytes_sent,
+        nullif(user_agent, '-') AS user_agent,
+        operation,
+        nullif(requester, '-') AS requester,
+        parseDateTime(timestamp_str, '%d/%b/%Y:%k:%i:%s %z') AS timestamp
+    FROM s3(
+        '{}',
+        'Regexp',
+        'owner String , bucket String, timestamp_str String, remote_ip String, requester LowCardinality(String), request_id String, operation LowCardinality(String), key String, request_uri String, http_status String, error_code String, bytes_sent_str String, object_size_str String, total_time String, turn_around_time String, referer String, user_agent String, version_id String, host_id String, signature_version String, cipher_suite String, authentication_type String, host_header String, tls_version String, access_point_arn String, acl_required String'
+    )
+    ORDER BY timestamp ASC
+    SETTINGS
+        format_regexp_skip_unmatched = 1,
+        format_regexp = '(\\S+) (\\S+) \\[(.*)\\] (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) ((?:\\S+ \\S+ \\S+)|\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) ("\\S+") (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+) (\\S+).*',
+        output_format_parquet_compression_method = 'zstd'
+    INTO OUTFILE '{}' FORMAT Parquet"#, input_files, output_file));
+
+    cmd.status().expect("clickhouse-local failed");
+
+    ExitCode::SUCCESS
+}
diff --git a/users/flokli/archivist/OWNERS b/users/flokli/archivist/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/users/flokli/archivist/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/users/flokli/archivist/default.nix b/users/flokli/archivist/default.nix
new file mode 100644
index 0000000000..ef49c46db2
--- /dev/null
+++ b/users/flokli/archivist/default.nix
@@ -0,0 +1,28 @@
+{ depot
+, pkgs
+, ...
+}:
+depot.nix.readTree.drvTargets {
+  shell = pkgs.mkShell {
+    name = "archivist-shell";
+    packages = with pkgs; [ awscli2 ];
+
+    AWS_PROFILE = "archivist";
+    AWS_CONFIG_FILE = pkgs.writeText "aws-config" ''
+      [sso-session nixos]
+      sso_region = eu-north-1
+      sso_start_url = https://nixos.awsapps.com/start
+      sso_registration_scopes = sso:account:access
+
+      [profile "archivist"]
+      sso_session = nixos
+      sso_account_id = 286553126452
+      sso_role_name = AWSAdministratorAccess
+
+      [profile "archeologist"]
+      sso_session = nixos
+      sso_account_id = 080433136561
+      sso_role_name = archeologist
+    '';
+  };
+}
diff --git a/users/flokli/ipu6-softisp/README.md b/users/flokli/ipu6-softisp/README.md
new file mode 100644
index 0000000000..2ab727ace4
--- /dev/null
+++ b/users/flokli/ipu6-softisp/README.md
@@ -0,0 +1,29 @@
+# ipu6-softisp
+
+This code adds support for the ipu6 webcams via libcamera, based on the work in
+https://copr.fedorainfracloud.org/coprs/jwrdegoede/ipu6-softisp/.
+
+It's supposed to be included in your NixOS configuration imports, and will:
+
+ - Add some patches to your kernel, which should apply on 6.8.x
+ - Add the `ipu6-camera-bins` firmware (still needed)
+ - Enable some kernel config options
+ - Add an udev rule so libcamera can do DMABUF things
+ - Override `services.pipewire.package` and
+   `services.pipewire.wireplumber.package` to use a pipewire built with a libcamera
+   with support for this webcam.
+
+Please make sure you don't have any of the `hardware.ipu6` options still
+enabled, as they use the closed-source userspace stack and will conflict.
+
+Also make sure to track nixos-unstable for this. This code will get periodically
+updated to be compatible with nixos-unstable!
+
+The testing instructions from
+https://copr.fedorainfracloud.org/coprs/jwrdegoede/ipu6-softisp/ still apply.
+
+`qcam` can be found in `libcamera-qcam` (pending on
+https://github.com/NixOS/nixpkgs/pull/284964 to trickle into master).
+
+Thanks to Hans de Goede for helping me bringing this up, as well as to
+puckipedia for sorting out some pipewire-related confusion.
diff --git a/users/flokli/ipu6-softisp/config.nix b/users/flokli/ipu6-softisp/config.nix
new file mode 100644
index 0000000000..95cb3e5c25
--- /dev/null
+++ b/users/flokli/ipu6-softisp/config.nix
@@ -0,0 +1,74 @@
+{ pkgs, lib, ... }:
+
+let
+  libcamera = pkgs.libcamera.overrideAttrs (old: {
+    mesonFlags = old.mesonFlags or [ ] ++ [
+      "-Dpipelines=simple,ipu3,uvcvideo"
+      "-Dipas=simple,ipu3"
+    ];
+
+    # This is
+    # https://copr-dist-git.fedorainfracloud.org/cgit/jwrdegoede/ipu6-softisp/libcamera.git/plain/libcamera-0.2.0-softisp.patch?h=f39&id=60e6b3d5e366a360a75942073dc0d642e4900982,
+    # but manually piped to git and back, as some renames were not processed properly.
+    # It was later refreshed with https://patchwork.libcamera.org/cover/19663/
+    patches = old.patches or [ ] ++ [
+      ./libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch
+      ./libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch
+      ./libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch
+      ./libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch
+      ./libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch
+      ./libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch
+      ./libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch
+      ./libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch
+      ./libcamera/0009-libcamera-ipa-add-Soft-IPA.patch
+      ./libcamera/0010-libcamera-introduce-SoftwareIsp.patch
+      ./libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
+      ./libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
+      ./libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
+      ./libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
+      ./libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch
+      ./libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
+      ./libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
+      ./libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch
+      ./libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch
+      ./libcamera/0020-ov01a1s-HACK.patch
+      ./libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
+    ];
+  });
+
+  # use patched libcamera
+  pipewire' = (pkgs.pipewire.override {
+    inherit libcamera;
+  });
+
+  wireplumber' = (pkgs.wireplumber.override {
+    pipewire = pipewire';
+  });
+in
+{
+  boot.kernelPatches = [{
+    name = "linux-kernel-test.patch";
+    patch = ./kernel/softisp.patch;
+    extraStructuredConfig = {
+      # needed for /dev/dma_heap
+      DMABUF_HEAPS_CMA = lib.kernel.yes;
+      DMABUF_HEAPS_SYSTEM = lib.kernel.yes;
+      DMABUF_HEAPS = lib.kernel.yes;
+    };
+  }];
+
+
+  services.udev.extraRules = ''
+    KERNEL=="system", SUBSYSTEM=="dma_heap", TAG+="uaccess"
+  '';
+
+  # provide qcam in $PATH.
+  environment.systemPackages = [
+    (libcamera.override {
+      withQcam = true;
+    })
+  ];
+
+  services.pipewire.package = pipewire';
+  services.pipewire.wireplumber.package = wireplumber';
+}
diff --git a/users/flokli/ipu6-softisp/default.nix b/users/flokli/ipu6-softisp/default.nix
new file mode 100644
index 0000000000..1f603dbb42
--- /dev/null
+++ b/users/flokli/ipu6-softisp/default.nix
@@ -0,0 +1,57 @@
+# This file ensures the fixes from ./config.nix build with the version of
+# nixpkgs from depot.
+# If you're an outside user of this, import config.nix as a NixOS module (and
+# check the README.md file).
+
+{ depot
+, pkgs
+, ...
+}:
+
+let
+  systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
+in
+depot.nix.readTree.drvTargets rec {
+  testSystem = systemFor ({ modulesPath, pkgs, ... }: {
+    imports = [
+      # Import the module, this is something a user would do in their config.
+      ./config.nix
+    ];
+
+    # Make sure we use the linuxPackages_latest.
+    boot.kernelPackages = pkgs.linuxPackages_latest;
+
+    # Enable firmware.
+    hardware.enableAllFirmware = true;
+
+    # Set some options necessary to evaluate.
+    boot.loader.systemd-boot.enable = true;
+    fileSystems."/" = {
+      device = "/dev/disk/by-partlabel/root";
+      fsType = "xfs";
+    };
+
+    # Enable pipewire and wireplumber.
+    services.pipewire = {
+      enable = true;
+      wireplumber.enable = true;
+    };
+
+    # Shut off the warning.
+    system.stateVersion = "24.05";
+  });
+
+  # Make sure the firmware requested by the driver is present in our firmware.
+  # We do have a .xz suffix here, but that's fine, since request_firmware does
+  # check ${name}.xz too in case CONFIG_FW_LOADER_COMPRESS is set.
+  # The path needs to be kept in sync with the ones used in the kernel patch.
+  checkFirmware = pkgs.runCommand "check-firmware" { } ''
+    stat ${testSystem}/firmware/intel/ipu/ipu6se_fw.bin.xz
+    stat ${testSystem}/firmware/intel/ipu/ipu6ep_fw.bin.xz
+    stat ${testSystem}/firmware/intel/ipu/ipu6_fw.bin.xz
+    stat ${testSystem}/firmware/intel/ipu/ipu6epmtl_fw.bin.xz
+
+    # all good, succeed build
+    touch $out
+  '';
+}
diff --git a/users/riking/dotfiles/regolith/flags/first-time-setup-r1-4-1 b/users/flokli/ipu6-softisp/kernel/.skip-tree
index e69de29bb2..e69de29bb2 100644
--- a/users/riking/dotfiles/regolith/flags/first-time-setup-r1-4-1
+++ b/users/flokli/ipu6-softisp/kernel/.skip-tree
diff --git a/users/flokli/ipu6-softisp/kernel/softisp.patch b/users/flokli/ipu6-softisp/kernel/softisp.patch
new file mode 100644
index 0000000000..8731ed914c
--- /dev/null
+++ b/users/flokli/ipu6-softisp/kernel/softisp.patch
@@ -0,0 +1,18077 @@
+From 037f05a9772f3243907bb826e913971ee20e9487 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:31 +0300
+Subject: [PATCH 01/33] media: mc: Add INTERNAL pad flag
+
+Internal source pads will be used as routing endpoints in V4L2
+[GS]_ROUTING IOCTLs, to indicate that the stream begins in the entity.
+
+Also prevent creating links to pads that have been flagged as internal.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ Documentation/userspace-api/media/glossary.rst             | 6 ++++++
+ Documentation/userspace-api/media/mediactl/media-types.rst | 6 ++++++
+ drivers/media/mc/mc-entity.c                               | 6 +++++-
+ include/uapi/linux/media.h                                 | 1 +
+ 4 files changed, 18 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/userspace-api/media/glossary.rst b/Documentation/userspace-api/media/glossary.rst
+index 96a360edbf3b..f7b99a4527c7 100644
+--- a/Documentation/userspace-api/media/glossary.rst
++++ b/Documentation/userspace-api/media/glossary.rst
+@@ -173,6 +173,12 @@ Glossary
+ 	An integrated circuit that integrates all components of a computer
+ 	or other electronic systems.
+ 
++_media-glossary-stream:
++    Stream
++	A distinct flow of data (image data or metadata) over a media pipeline
++	from source to sink. A source may be e.g. an image sensor and a sink
++	e.g. a memory buffer.
++
+     V4L2 API
+ 	**V4L2 userspace API**
+ 
+diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst
+index 0ffeece1e0c8..28941da27790 100644
+--- a/Documentation/userspace-api/media/mediactl/media-types.rst
++++ b/Documentation/userspace-api/media/mediactl/media-types.rst
+@@ -361,6 +361,7 @@ Types and flags used to represent the media graph elements
+ .. _MEDIA-PAD-FL-SINK:
+ .. _MEDIA-PAD-FL-SOURCE:
+ .. _MEDIA-PAD-FL-MUST-CONNECT:
++.. _MEDIA-PAD-FL-INTERNAL:
+ 
+ .. flat-table:: Media pad flags
+     :header-rows:  0
+@@ -382,6 +383,11 @@ Types and flags used to represent the media graph elements
+ 	  when this flag isn't set; the absence of the flag doesn't imply
+ 	  there is none.
+ 
++    *  -  ``MEDIA_PAD_FL_INTERNAL``
++       -  The internal flag indicates an internal pad that has no external
++	  connections. Such a pad shall not be connected with a link. The
++	  internal flag indicates that the :ref:``stream
++	  <media-glossary-stream>`` either starts or ends in the entity.
+ 
+ One and only one of ``MEDIA_PAD_FL_SINK`` and ``MEDIA_PAD_FL_SOURCE``
+ must be set for every pad.
+diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c
+index 543a392f8635..1fc80fd3e5e3 100644
+--- a/drivers/media/mc/mc-entity.c
++++ b/drivers/media/mc/mc-entity.c
+@@ -1075,7 +1075,8 @@ int media_get_pad_index(struct media_entity *entity, u32 pad_type,
+ 
+ 	for (i = 0; i < entity->num_pads; i++) {
+ 		if ((entity->pads[i].flags &
+-		     (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type)
++		     (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE |
++		      MEDIA_PAD_FL_INTERNAL)) != pad_type)
+ 			continue;
+ 
+ 		if (entity->pads[i].sig_type == sig_type)
+@@ -1098,6 +1099,9 @@ media_create_pad_link(struct media_entity *source, u16 source_pad,
+ 		return -EINVAL;
+ 	if (WARN_ON(!(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE)))
+ 		return -EINVAL;
++	if (WARN_ON(source->pads[source_pad].flags & MEDIA_PAD_FL_SOURCE &&
++		    source->pads[source_pad].flags & MEDIA_PAD_FL_INTERNAL))
++		return -EINVAL;
+ 	if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK)))
+ 		return -EINVAL;
+ 
+diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
+index 1c80b1d6bbaf..80cfd12a43fc 100644
+--- a/include/uapi/linux/media.h
++++ b/include/uapi/linux/media.h
+@@ -208,6 +208,7 @@ struct media_entity_desc {
+ #define MEDIA_PAD_FL_SINK			(1U << 0)
+ #define MEDIA_PAD_FL_SOURCE			(1U << 1)
+ #define MEDIA_PAD_FL_MUST_CONNECT		(1U << 2)
++#define MEDIA_PAD_FL_INTERNAL			(1U << 3)
+ 
+ struct media_pad_desc {
+ 	__u32 entity;		/* entity ID */
+-- 
+2.43.2
+
+
+From 5f0cdae874f1c0237936c2c12a9fc019b93de4c9 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:32 +0300
+Subject: [PATCH 02/33] media: uapi: Add generic serial metadata mbus formats
+
+Add generic serial metadata mbus formats. These formats describe data
+width and packing but not the content itself. The reason for specifying
+such formats is that the formats as such are fairly device specific but
+they are still handled by CSI-2 receiver drivers that should not be aware
+of device specific formats. What makes generic metadata formats possible
+is that these formats are parsed by software only, after capturing the
+data to system memory.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ .../media/v4l/subdev-formats.rst              | 257 ++++++++++++++++++
+ include/uapi/linux/media-bus-format.h         |   9 +
+ 2 files changed, 266 insertions(+)
+
+diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst
+index eb3cd20b0cf2..7d107873cddd 100644
+--- a/Documentation/userspace-api/media/v4l/subdev-formats.rst
++++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst
+@@ -8306,3 +8306,260 @@ The following table lists the existing metadata formats.
+ 	both sides of the link and the bus format is a fixed
+ 	metadata format that is not configurable from userspace.
+ 	Width and height will be set to 0 for this format.
++
++Generic Serial Metadata Formats
++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++Generic serial metadata formats are used on serial busses where the actual data
++content is more or less device specific but the data is transmitted and received
++by multiple devices that do not process the data in any way, simply writing
++it to system memory for processing in software at the end of the pipeline.
++
++The more specific variant describing the actual data is used on the internal
++source pad of the originating sub-device.
++
++"b" in an array cell signifies a byte of data, followed by the number of byte
++and finally the bit number in subscript. "p" indicates a padding bit.
++
++.. _media-bus-format-generic-meta:
++
++.. cssclass: longtable
++
++.. flat-table:: Generic Serial Metadata Formats
++    :header-rows:  2
++    :stub-columns: 0
++
++    * - Identifier
++      - Code
++      -
++      - :cspan:`23` Data organization
++    * -
++      -
++      - Bit
++      - 23
++      - 22
++      - 21
++      - 20
++      - 19
++      - 18
++      - 17
++      - 16
++      - 15
++      - 14
++      - 13
++      - 12
++      - 11
++      - 10
++      - 9
++      - 8
++      - 7
++      - 6
++      - 5
++      - 4
++      - 3
++      - 2
++      - 1
++      - 0
++    * .. _MEDIA-BUS-FMT-META-8:
++
++      - MEDIA_BUS_FMT_META_8
++      - 0x8001
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++    * .. _MEDIA-BUS-FMT-META-10:
++
++      - MEDIA_BUS_FMT_META_10
++      - 0x8002
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-12:
++
++      - MEDIA_BUS_FMT_META_12
++      - 0x8003
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-14:
++
++      - MEDIA_BUS_FMT_META_14
++      - 0x8004
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-16:
++
++      - MEDIA_BUS_FMT_META_16
++      - 0x8005
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-20:
++
++      - MEDIA_BUS_FMT_META_20
++      - 0x8007
++      -
++      -
++      -
++      -
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++    * .. _MEDIA-BUS-FMT-META-24:
++
++      - MEDIA_BUS_FMT_META_24
++      - 0x8009
++      -
++      - b0\ :sub:`7`
++      - b0\ :sub:`6`
++      - b0\ :sub:`5`
++      - b0\ :sub:`4`
++      - b0\ :sub:`3`
++      - b0\ :sub:`2`
++      - b0\ :sub:`1`
++      - b0\ :sub:`0`
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
++      - p
+diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
+index f05f747e444d..d4c1d991014b 100644
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -174,4 +174,13 @@
+  */
+ #define MEDIA_BUS_FMT_METADATA_FIXED		0x7001
+ 
++/* Generic line based metadata formats for serial buses. Next is 0x8008. */
++#define MEDIA_BUS_FMT_META_8			0x8001
++#define MEDIA_BUS_FMT_META_10			0x8002
++#define MEDIA_BUS_FMT_META_12			0x8003
++#define MEDIA_BUS_FMT_META_14			0x8004
++#define MEDIA_BUS_FMT_META_16			0x8005
++#define MEDIA_BUS_FMT_META_20			0x8006
++#define MEDIA_BUS_FMT_META_24			0x8007
++
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
+-- 
+2.43.2
+
+
+From 8af4eeaee34159605ec86b57fa638a82fd968f31 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:33 +0300
+Subject: [PATCH 03/33] media: uapi: Document which mbus format fields are
+ valid for metadata
+
+Now that metadata mbus formats have been added, it is necessary to define
+which fields in struct v4l2_mbus_format are applicable to them (not many).
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ include/uapi/linux/v4l2-mediabus.h | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/include/uapi/linux/v4l2-mediabus.h b/include/uapi/linux/v4l2-mediabus.h
+index 6b07b73473b5..3cadb3b58b85 100644
+--- a/include/uapi/linux/v4l2-mediabus.h
++++ b/include/uapi/linux/v4l2-mediabus.h
+@@ -19,12 +19,18 @@
+  * @width:	image width
+  * @height:	image height
+  * @code:	data format code (from enum v4l2_mbus_pixelcode)
+- * @field:	used interlacing type (from enum v4l2_field)
+- * @colorspace:	colorspace of the data (from enum v4l2_colorspace)
+- * @ycbcr_enc:	YCbCr encoding of the data (from enum v4l2_ycbcr_encoding)
+- * @hsv_enc:	HSV encoding of the data (from enum v4l2_hsv_encoding)
+- * @quantization: quantization of the data (from enum v4l2_quantization)
+- * @xfer_func:  transfer function of the data (from enum v4l2_xfer_func)
++ * @field:	used interlacing type (from enum v4l2_field), not applicable
++ *		to metadata mbus codes
++ * @colorspace:	colorspace of the data (from enum v4l2_colorspace), zero on
++ *		metadata mbus codes
++ * @ycbcr_enc:	YCbCr encoding of the data (from enum v4l2_ycbcr_encoding), zero
++ *		on metadata mbus codes
++ * @hsv_enc:	HSV encoding of the data (from enum v4l2_hsv_encoding), zero on
++ *		metadata mbus codes
++ * @quantization: quantization of the data (from enum v4l2_quantization), zero
++ *		on metadata mbus codes
++ * @xfer_func:  transfer function of the data (from enum v4l2_xfer_func), zero
++ *		on metadata mbus codes
+  * @flags:	flags (V4L2_MBUS_FRAMEFMT_*)
+  * @reserved:  reserved bytes that can be later used
+  */
+-- 
+2.43.2
+
+
+From ed5884d40def9adfa77841427e52733746158a77 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:34 +0300
+Subject: [PATCH 04/33] media: uapi: Add a macro to tell whether an mbus code
+ is metadata
+
+Add a macro to tell whether a given mbus code is metadata.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ include/uapi/linux/media-bus-format.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
+index d4c1d991014b..6fcd7d276bc6 100644
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -183,4 +183,7 @@
+ #define MEDIA_BUS_FMT_META_20			0x8006
+ #define MEDIA_BUS_FMT_META_24			0x8007
+ 
++#define MEDIA_BUS_FMT_IS_META(code)		\
++	((code) & 0xf000 == 0x7000 || (code) & 0xf000 == 0x8000)
++
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
+-- 
+2.43.2
+
+
+From c0e682a815c5baf012c8963968385c197e7e0943 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:35 +0300
+Subject: [PATCH 05/33] media: uapi: Add generic 8-bit metadata format
+ definitions
+
+Generic 8-bit metadata formats define the in-memory data layout but not
+the format of the data itself. The reasoning for having such formats is to
+allow CSI-2 receiver drivers to receive and DMA drivers to write the data
+to memory without knowing a large number of device specific formats.
+
+These formats may be used only in conjunction of a Media controller
+pipeline where the internal pad of the source sub-device defines the
+specific format of the data (using an mbus code).
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ .../userspace-api/media/v4l/meta-formats.rst  |   1 +
+ .../media/v4l/metafmt-generic.rst             | 331 ++++++++++++++++++
+ drivers/media/v4l2-core/v4l2-ioctl.c          |   8 +
+ include/uapi/linux/videodev2.h                |   9 +
+ 4 files changed, 349 insertions(+)
+ create mode 100644 Documentation/userspace-api/media/v4l/metafmt-generic.rst
+
+diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst
+index 0bb61fc5bc00..919f595576b9 100644
+--- a/Documentation/userspace-api/media/v4l/meta-formats.rst
++++ b/Documentation/userspace-api/media/v4l/meta-formats.rst
+@@ -19,3 +19,4 @@ These formats are used for the :ref:`metadata` interface only.
+     metafmt-vsp1-hgo
+     metafmt-vsp1-hgt
+     metafmt-vivid
++    metafmt-generic
+diff --git a/Documentation/userspace-api/media/v4l/metafmt-generic.rst b/Documentation/userspace-api/media/v4l/metafmt-generic.rst
+new file mode 100644
+index 000000000000..a27bfc721edf
+--- /dev/null
++++ b/Documentation/userspace-api/media/v4l/metafmt-generic.rst
+@@ -0,0 +1,331 @@
++.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
++
++**************************************************************************************************************************************************************************************************************************************************************************************************************************
++V4L2_META_FMT_GENERIC_8 ('MET8'), V4L2_META_FMT_GENERIC_CSI2_10 ('MC1A'), V4L2_META_FMT_GENERIC_CSI2_12 ('MC1C'), V4L2_META_FMT_GENERIC_CSI2_14 ('MC1E'), V4L2_META_FMT_GENERIC_CSI2_16 ('MC1G'), V4L2_META_FMT_GENERIC_CSI2_20 ('MC1K'), V4L2_META_FMT_GENERIC_CSI2_24 ('MC1O'), V4L2_META_FMT_GENERIC_CSI2_2_24 ('MC2O')
++**************************************************************************************************************************************************************************************************************************************************************************************************************************
++
++
++Generic line-based metadata formats
++
++
++Description
++===========
++
++These generic line-based metadata formats define the memory layout of the data
++without defining the format or meaning of the metadata itself. These formats may
++only be used with a Media controller pipeline where the more specific format is
++defined in an :ref:`internal source pad <MEDIA-PAD-FL-INTERNAL>` of the source
++sub-device. See also :ref:`source routes <v4l2-subdev-source-routes>`.
++
++.. _v4l2-meta-fmt-generic-8:
++
++V4L2_META_FMT_GENERIC_8
++-----------------------
++
++The V4L2_META_FMT_GENERIC_8 format is a plain 8-bit metadata format.
++
++This format is also used on CSI-2 on both 8 bits per sample as well as on
++16 bits per sample when two bytes of metadata are packed into one sample.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_8.**
++Each cell is one byte. "M" denotes a byte of metadata.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++    * - start + 4:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++
++.. _v4l2-meta-fmt-generic-csi2-10:
++
++V4L2_META_FMT_GENERIC_CSI2_10
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_10 contains packed 8-bit generic metadata, 10 bits
++for each 8 bits of data. Every four bytes of metadata is followed by a single
++byte of padding. The way the data is stored follows the CSI-2 specification.
++
++This format is also used on CSI-2 on 20 bits per sample format that packs two
++bytes of metadata into one sample.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_10.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++      - p
++    * - start + 5:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-12:
++
++V4L2_META_FMT_GENERIC_CSI2_12
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_12 contains packed 8-bit generic metadata, 12 bits
++for each 8 bits of data. Every four bytes of metadata is followed by two bytes
++of padding. The way the data is stored follows the CSI-2 specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_12.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++      - p
++      - p
++    * - start + 6:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++      - p
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-14:
++
++V4L2_META_FMT_GENERIC_CSI2_14
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_14 contains packed 8-bit generic metadata, 14 bits
++for each 8 bits of data. Every four bytes of metadata is followed by three
++bytes of padding. The way the data is stored follows the CSI-2 specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_14.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++      - p
++      - p
++      - p
++    * - start + 7:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++      - p
++      - p
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-16:
++
++V4L2_META_FMT_GENERIC_CSI2_16
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_16 contains packed 8-bit generic metadata, 16 bits
++for each 8 bits of data. Every byte of metadata is followed by one byte of
++padding. The way the data is stored follows the CSI-2 specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_16.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - p
++      - M\ :sub:`10`
++      - p
++      - M\ :sub:`20`
++      - p
++      - M\ :sub:`30`
++      - p
++    * - start + 8:
++      - M\ :sub:`01`
++      - p
++      - M\ :sub:`11`
++      - p
++      - M\ :sub:`21`
++      - p
++      - M\ :sub:`31`
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-20:
++
++V4L2_META_FMT_GENERIC_CSI2_20
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_20 contains packed 8-bit generic metadata, 20 bits
++for each 8 bits of data. Every byte of metadata is followed by alternating one
++and two bytes of padding. The way the data is stored follows the CSI-2
++specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_20.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - p
++      - M\ :sub:`10`
++      - p
++      - p
++      - M\ :sub:`20`
++      - p
++      - M\ :sub:`30`
++      - p
++      - p
++    * - start + 10:
++      - M\ :sub:`01`
++      - p
++      - M\ :sub:`11`
++      - p
++      - p
++      - M\ :sub:`21`
++      - p
++      - M\ :sub:`31`
++      - p
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-24:
++
++V4L2_META_FMT_GENERIC_CSI2_24
++-----------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_24 contains packed 8-bit generic metadata, 24 bits
++for each 8 bits of data. Every byte of metadata is followed by two bytes of
++padding. The way the data is stored follows the CSI-2 specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_24.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|p{1.2cm}|p{.8cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - p
++      - p
++      - M\ :sub:`10`
++      - p
++      - p
++      - M\ :sub:`20`
++      - p
++      - p
++      - M\ :sub:`30`
++      - p
++      - p
++    * - start + 12:
++      - M\ :sub:`01`
++      - p
++      - p
++      - M\ :sub:`11`
++      - p
++      - p
++      - M\ :sub:`21`
++      - p
++      - p
++      - M\ :sub:`31`
++      - p
++      - p
++
++.. _v4l2-meta-fmt-generic-csi2-2-24:
++
++V4L2_META_FMT_GENERIC_CSI2_2_24
++-------------------------------
++
++V4L2_META_FMT_GENERIC_CSI2_2_24 contains packed 8-bit generic metadata, 24 bits
++for each two times 8 bits of data. Every two bytes of metadata are followed by
++one byte of padding. The way the data is stored follows the CSI-2
++specification.
++
++This format is little endian.
++
++**Byte Order Of V4L2_META_FMT_GENERIC_CSI2_2_24.**
++Each cell is one byte. "M" denotes a byte of metadata and "p" a byte of padding.
++
++.. tabularcolumns:: |p{2.4cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|p{1.2cm}|p{1.2cm}|p{.8cm}|
++
++.. flat-table::
++    :header-rows:  0
++    :stub-columns: 0
++    :widths: 12 8 8 8 8 8 8
++
++    * - start + 0:
++      - M\ :sub:`00`
++      - M\ :sub:`10`
++      - p
++      - M\ :sub:`20`
++      - M\ :sub:`30`
++      - p
++    * - start + 6:
++      - M\ :sub:`01`
++      - M\ :sub:`11`
++      - p
++      - M\ :sub:`21`
++      - M\ :sub:`31`
++      - p
++
+diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
+index 33076af4dfdb..4eb3db1773e1 100644
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1452,6 +1452,14 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
+ 	case V4L2_PIX_FMT_Y210:		descr = "10-bit YUYV Packed"; break;
+ 	case V4L2_PIX_FMT_Y212:		descr = "12-bit YUYV Packed"; break;
+ 	case V4L2_PIX_FMT_Y216:		descr = "16-bit YUYV Packed"; break;
++	case V4L2_META_FMT_GENERIC_8:	descr = "8-bit Generic Metadata"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_10:	descr = "8b Generic Meta, 10b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_12:	descr = "8b Generic Meta, 12b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_14:	descr = "8b Generic Meta, 14b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_16:	descr = "8b Generic Meta, 16b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_20:	descr = "8b Generic Meta, 20b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_24:	descr = "8b Generic Meta, 24b CSI-2"; break;
++	case V4L2_META_FMT_GENERIC_CSI2_2_24:	descr = "2x8b Generic Meta, 24b CSI-2"; break;
+ 
+ 	default:
+ 		/* Compressed formats */
+diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
+index 68e7ac178cc2..2c4e03d47789 100644
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -839,6 +839,15 @@ struct v4l2_pix_format {
+ #define V4L2_META_FMT_RK_ISP1_PARAMS	v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */
+ #define V4L2_META_FMT_RK_ISP1_STAT_3A	v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */
+ 
++#define V4L2_META_FMT_GENERIC_8		v4l2_fourcc('M', 'E', 'T', '8') /* Generic 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_10	v4l2_fourcc('M', 'C', '1', 'A') /* 10-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_12	v4l2_fourcc('M', 'C', '1', 'C') /* 12-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_14	v4l2_fourcc('M', 'C', '1', 'E') /* 14-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_16	v4l2_fourcc('M', 'C', '1', 'G') /* 16-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_20	v4l2_fourcc('M', 'C', '1', 'K') /* 20-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_24	v4l2_fourcc('M', 'C', '1', 'O') /* 24-bit CSI-2 packed 8-bit metadata */
++#define V4L2_META_FMT_GENERIC_CSI2_2_24	v4l2_fourcc('M', 'C', '2', 'O') /* 2 bytes of 8-bit metadata, 24-bit CSI-2 packed */
++
+ /* priv field value to indicates that subsequent fields are valid. */
+ #define V4L2_PIX_FMT_PRIV_MAGIC		0xfeedcafe
+ 
+-- 
+2.43.2
+
+
+From 453627c23062ff0aa01e0e46e3b7922ddf82f998 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:36 +0300
+Subject: [PATCH 06/33] media: v4l: Support line-based metadata capture
+
+many camera sensors, among other devices, transmit embedded data and image
+data for each CSI-2 frame. This embedded data typically contains register
+configuration of the sensor that has been used to capture the image data
+of the same frame.
+
+The embedded data is received by the CSI-2 receiver and has the same
+properties as the image data, including that it is line based: it has
+width, height and bytesperline (stride).
+
+Add these fields to struct v4l2_meta_format and document them.
+
+Also add V4L2_FMT_FLAG_META_LINE_BASED to tell a given format is
+line-based i.e. these fields of struct v4l2_meta_format are valid for it.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ .../userspace-api/media/v4l/dev-meta.rst          | 15 +++++++++++++++
+ .../userspace-api/media/v4l/vidioc-enum-fmt.rst   |  7 +++++++
+ .../media/videodev2.h.rst.exceptions              |  1 +
+ drivers/media/v4l2-core/v4l2-ioctl.c              |  5 +++--
+ include/uapi/linux/videodev2.h                    | 10 ++++++++++
+ 5 files changed, 36 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/userspace-api/media/v4l/dev-meta.rst b/Documentation/userspace-api/media/v4l/dev-meta.rst
+index 0e7e1ee1471a..4b24bae6e171 100644
+--- a/Documentation/userspace-api/media/v4l/dev-meta.rst
++++ b/Documentation/userspace-api/media/v4l/dev-meta.rst
+@@ -65,3 +65,18 @@ to 0.
+       - ``buffersize``
+       - Maximum buffer size in bytes required for data. The value is set by the
+         driver.
++    * - __u32
++      - ``width``
++      - Width of a line of metadata in samples. Valid when :c:type`v4l2_fmtdesc`
++	flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See
++	:c:func:`VIDIOC_ENUM_FMT`.
++    * - __u32
++      - ``height``
++      - Number of rows of metadata. Valid when :c:type`v4l2_fmtdesc` flag
++	``V4L2_FMT_FLAG_META_LINE_BASED`` is set, otherwise zero. See
++	:c:func:`VIDIOC_ENUM_FMT`.
++    * - __u32
++      - ``bytesperline``
++      - Offset in bytes between the beginning of two consecutive lines. Valid
++	when :c:type`v4l2_fmtdesc` flag ``V4L2_FMT_FLAG_META_LINE_BASED`` is
++	set, otherwise zero. See :c:func:`VIDIOC_ENUM_FMT`.
+diff --git a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst
+index 000c154b0f98..6d7664345a4e 100644
+--- a/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst
++++ b/Documentation/userspace-api/media/v4l/vidioc-enum-fmt.rst
+@@ -227,6 +227,13 @@ the ``mbus_code`` field is handled differently:
+ 	The application can ask to configure the quantization of the capture
+ 	device when calling the :ref:`VIDIOC_S_FMT <VIDIOC_G_FMT>` ioctl with
+ 	:ref:`V4L2_PIX_FMT_FLAG_SET_CSC <v4l2-pix-fmt-flag-set-csc>` set.
++    * - ``V4L2_FMT_FLAG_META_LINE_BASED``
++      - 0x0200
++      - The metadata format is line-based. In this case the ``width``,
++	``height`` and ``bytesperline`` fields of :c:type:`v4l2_meta_format` are
++	valid. The buffer consists of ``height`` lines, each having ``width``
++	bytes of data and offset between the beginning of each two consecutive
++	lines is ``bytesperline``.
+ 
+ Return Value
+ ============
+diff --git a/Documentation/userspace-api/media/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+index 3e58aac4ef0b..bdc628e8c1d6 100644
+--- a/Documentation/userspace-api/media/videodev2.h.rst.exceptions
++++ b/Documentation/userspace-api/media/videodev2.h.rst.exceptions
+@@ -215,6 +215,7 @@ replace define V4L2_FMT_FLAG_CSC_XFER_FUNC fmtdesc-flags
+ replace define V4L2_FMT_FLAG_CSC_YCBCR_ENC fmtdesc-flags
+ replace define V4L2_FMT_FLAG_CSC_HSV_ENC fmtdesc-flags
+ replace define V4L2_FMT_FLAG_CSC_QUANTIZATION fmtdesc-flags
++replace define V4L2_FMT_FLAG_META_LINE_BASED fmtdesc-flags
+ 
+ # V4L2 timecode types
+ replace define V4L2_TC_TYPE_24FPS timecode-type
+diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
+index 4eb3db1773e1..1fcec1515bcc 100644
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -343,8 +343,9 @@ static void v4l_print_format(const void *arg, bool write_only)
+ 	case V4L2_BUF_TYPE_META_OUTPUT:
+ 		meta = &p->fmt.meta;
+ 		pixelformat = meta->dataformat;
+-		pr_cont(", dataformat=%p4cc, buffersize=%u\n",
+-			&pixelformat, meta->buffersize);
++		pr_cont(", dataformat=%p4cc, buffersize=%u, width=%u, height=%u, bytesperline=%u\n",
++			&pixelformat, meta->buffersize, meta->width,
++			meta->height, meta->bytesperline);
+ 		break;
+ 	}
+ }
+diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
+index 2c4e03d47789..48fb44772098 100644
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -878,6 +878,7 @@ struct v4l2_fmtdesc {
+ #define V4L2_FMT_FLAG_CSC_YCBCR_ENC		0x0080
+ #define V4L2_FMT_FLAG_CSC_HSV_ENC		V4L2_FMT_FLAG_CSC_YCBCR_ENC
+ #define V4L2_FMT_FLAG_CSC_QUANTIZATION		0x0100
++#define V4L2_FMT_FLAG_META_LINE_BASED		0x0200
+ 
+ 	/* Frame Size and frame rate enumeration */
+ /*
+@@ -2424,10 +2425,19 @@ struct v4l2_sdr_format {
+  * struct v4l2_meta_format - metadata format definition
+  * @dataformat:		little endian four character code (fourcc)
+  * @buffersize:		maximum size in bytes required for data
++ * @width:		number of bytes of data per line (valid for line based
++ *			formats only, see format documentation)
++ * @height:		number of lines of data per buffer (valid for line based
++ *			formats only)
++ * @bytesperline:	offset between the beginnings of two adjacent lines in
++ *			bytes (valid for line based formats only)
+  */
+ struct v4l2_meta_format {
+ 	__u32				dataformat;
+ 	__u32				buffersize;
++	__u32				width;
++	__u32				height;
++	__u32				bytesperline;
+ } __attribute__ ((packed));
+ 
+ /**
+-- 
+2.43.2
+
+
+From 090bb3894bda739ff06e8a431815210b731056b7 Mon Sep 17 00:00:00 2001
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+Date: Tue, 8 Aug 2023 10:55:37 +0300
+Subject: [PATCH 07/33] media: Add media bus codes for MIPI CCS embedded data
+
+Add new MIPI CCS embedded data media bus formats.
+
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+---
+ .../media/v4l/subdev-formats.rst              | 32 +++++++++++++++++++
+ include/uapi/linux/media-bus-format.h         | 10 +++++-
+ 2 files changed, 41 insertions(+), 1 deletion(-)
+
+diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst
+index 7d107873cddd..7afb057a09c5 100644
+--- a/Documentation/userspace-api/media/v4l/subdev-formats.rst
++++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst
+@@ -8563,3 +8563,35 @@ and finally the bit number in subscript. "p" indicates a padding bit.
+       - p
+       - p
+       - p
++
++MIPI CCS Embedded Data Formats
++^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++`MIPI CCS <https://www.mipi.org/specifications/camera-command-set>`_ defines an
++metadata format for sensor embedded data, which is used to store the register
++configuration used for capturing a given frame. The format is defined in the CCS
++specification.
++
++The bit depth of the CCS embedded data matches the pixel data bit depth
++configured on the sensor. The formats used and their corresponding generic
++formats are listed in the table below.
++
++.. flat-table: CCS embedded data mbus formats and corresponding generic formats
++    :header-rows: 1
++
++    * - CCS embedded data mbus format
++      - Generic metadata format
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_8
++      - MEDIA_BUS_FMT_META_8
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_10
++      - MEDIA_BUS_FMT_META_10
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_12
++      - MEDIA_BUS_FMT_META_12
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_14
++      - MEDIA_BUS_FMT_META_14
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_16
++      - MEDIA_BUS_FMT_META_16
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_20
++      - MEDIA_BUS_FMT_META_20
++    * - MEDIA_BUS_FMT_CCS_EMBEDDED_24
++      - MEDIA_BUS_FMT_META_24
+diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
+index 6fcd7d276bc6..d5014ef3c43a 100644
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -183,7 +183,15 @@
+ #define MEDIA_BUS_FMT_META_20			0x8006
+ #define MEDIA_BUS_FMT_META_24			0x8007
+ 
++/* Specific metadata formats. Next is 0x9008. */
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_8		0x9001
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_10		0x9002
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_12		0x9003
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_14		0x9004
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_16		0x9005
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_20		0x9006
++#define MEDIA_BUS_FMT_CCS_EMBEDDED_24		0x9007
++
+ #define MEDIA_BUS_FMT_IS_META(code)		\
+ 	((code) & 0xf000 == 0x7000 || (code) & 0xf000 == 0x8000)
+-
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
+-- 
+2.43.2
+
+
+From 44f8084f055969874d2216ba4e6e225046931e73 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:15 +0800
+Subject: [PATCH 08/33] media: intel/ipu6: add Intel IPU6 PCI device driver
+
+Intel Image Processing Unit 6th Gen includes input and processing systems
+but the hardware presents itself as a single PCI device in system.
+
+IPU6 PCI device driver basically does PCI configurations and load
+the firmware binary, initialises IPU virtual bus, and sets up platform
+specific variants to support multiple IPU6 devices in single device
+driver.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ .../media/pci/intel/ipu6/ipu6-platform-regs.h | 179 ++++
+ drivers/media/pci/intel/ipu6/ipu6.c           | 966 ++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6.h           | 356 +++++++
+ 3 files changed, 1501 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-platform-regs.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h
+new file mode 100644
+index 000000000000..cae26009c9fc
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-platform-regs.h
+@@ -0,0 +1,179 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2018 - 2023 Intel Corporation */
++
++#ifndef IPU6_PLATFORM_REGS_H
++#define IPU6_PLATFORM_REGS_H
++
++#include <linux/bits.h>
++
++/*
++ * IPU6 uses uniform address within IPU6, therefore all subsystem registers
++ * locates in one single space starts from 0 but in different sctions with
++ * different addresses, the subsystem offsets are defined to 0 as the
++ * register definition will have the address offset to 0.
++ */
++#define IPU6_UNIFIED_OFFSET			0
++
++#define IPU6_ISYS_IOMMU0_OFFSET		0x2e0000
++#define IPU6_ISYS_IOMMU1_OFFSET		0x2e0500
++#define IPU6_ISYS_IOMMUI_OFFSET		0x2e0a00
++
++#define IPU6_PSYS_IOMMU0_OFFSET		0x1b0000
++#define IPU6_PSYS_IOMMU1_OFFSET		0x1b0700
++#define IPU6_PSYS_IOMMU1R_OFFSET	0x1b0e00
++#define IPU6_PSYS_IOMMUI_OFFSET		0x1b1500
++
++/* the offset from IOMMU base register */
++#define IPU6_MMU_L1_STREAM_ID_REG_OFFSET	0x0c
++#define IPU6_MMU_L2_STREAM_ID_REG_OFFSET	0x4c
++#define IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET	0x8c
++
++#define IPU6_MMU_INFO_OFFSET		0x8
++
++#define IPU6_ISYS_SPC_OFFSET		0x210000
++
++#define IPU6SE_PSYS_SPC_OFFSET		0x110000
++#define IPU6_PSYS_SPC_OFFSET		0x118000
++
++#define IPU6_ISYS_DMEM_OFFSET		0x200000
++#define IPU6_PSYS_DMEM_OFFSET		0x100000
++
++#define IPU6_REG_ISYS_UNISPART_IRQ_EDGE			0x27c000
++#define IPU6_REG_ISYS_UNISPART_IRQ_MASK			0x27c004
++#define IPU6_REG_ISYS_UNISPART_IRQ_STATUS		0x27c008
++#define IPU6_REG_ISYS_UNISPART_IRQ_CLEAR		0x27c00c
++#define IPU6_REG_ISYS_UNISPART_IRQ_ENABLE		0x27c010
++#define IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE	0x27c014
++#define IPU6_REG_ISYS_UNISPART_SW_IRQ_REG		0x27c414
++#define IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG		0x27c418
++#define IPU6_ISYS_UNISPART_IRQ_CSI0			BIT(2)
++#define IPU6_ISYS_UNISPART_IRQ_CSI1			BIT(3)
++#define IPU6_ISYS_UNISPART_IRQ_SW			BIT(22)
++
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_EDGE			0x2b0200
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_MASK			0x2b0204
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_STATUS		0x2b0208
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_CLEAR			0x2b020c
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_ENABLE		0x2b0210
++#define IPU6_REG_ISYS_ISL_TOP_IRQ_LEVEL_NOT_PULSE	0x2b0214
++
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_EDGE			0x2d2100
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_MASK			0x2d2104
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_STATUS		0x2d2108
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_CLEAR		0x2d210c
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_ENABLE		0x2d2110
++#define IPU6_REG_ISYS_CMPR_TOP_IRQ_LEVEL_NOT_PULSE	0x2d2114
++
++/* CDC Burst collector thresholds for isys - 3 FIFOs i = 0..2 */
++#define IPU6_REG_ISYS_CDC_THRESHOLD(i)		(0x27c400 + ((i) * 4))
++
++#define IPU6_CSI_IRQ_NUM_PER_PIPE			4
++#define IPU6SE_ISYS_CSI_PORT_NUM			4
++#define IPU6_ISYS_CSI_PORT_NUM				8
++
++#define IPU6_ISYS_CSI_PORT_IRQ(irq_num)		BIT(irq_num)
++
++/* PKG DIR OFFSET in IMR in secure mode */
++#define IPU6_PKG_DIR_IMR_OFFSET			0x40
++
++#define IPU6_ISYS_REG_SPC_STATUS_CTRL		0x0
++
++#define IPU6_ISYS_SPC_STATUS_START			BIT(1)
++#define IPU6_ISYS_SPC_STATUS_RUN			BIT(3)
++#define IPU6_ISYS_SPC_STATUS_READY			BIT(5)
++#define IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE	BIT(12)
++#define IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH		BIT(13)
++
++#define IPU6_PSYS_REG_SPC_STATUS_CTRL			0x0
++#define IPU6_PSYS_REG_SPC_START_PC			0x4
++#define IPU6_PSYS_REG_SPC_ICACHE_BASE			0x10
++#define IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER	0x14
++
++#define IPU6_PSYS_SPC_STATUS_START			BIT(1)
++#define IPU6_PSYS_SPC_STATUS_RUN			BIT(3)
++#define IPU6_PSYS_SPC_STATUS_READY			BIT(5)
++#define IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE	BIT(12)
++#define IPU6_PSYS_SPC_STATUS_ICACHE_PREFETCH		BIT(13)
++
++#define IPU6_PSYS_REG_SPP0_STATUS_CTRL			0x20000
++
++#define IPU6_INFO_ENABLE_SNOOP			BIT(0)
++#define IPU6_INFO_DEC_FORCE_FLUSH		BIT(1)
++#define IPU6_INFO_DEC_PASS_THROUGH		BIT(2)
++#define IPU6_INFO_ZLW				BIT(3)
++#define IPU6_INFO_REQUEST_DESTINATION_IOSF	BIT(9)
++#define IPU6_INFO_IMR_BASE			BIT(10)
++#define IPU6_INFO_IMR_DESTINED			BIT(11)
++
++#define IPU6_INFO_REQUEST_DESTINATION_PRIMARY IPU6_INFO_REQUEST_DESTINATION_IOSF
++
++/*
++ * s2m_pixel_soc_pixel_remapping is dedicated for the enabling of the
++ * pixel s2m remp ability.Remap here  means that s2m rearange the order
++ * of the pixels in each 4 pixels group.
++ * For examle, mirroring remping means that if input's 4 first pixels
++ * are 1 2 3 4 then in output we should see 4 3 2 1 in this 4 first pixels.
++ * 0xE4 is from s2m MAS document. It means no remapping.
++ */
++#define S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING 0xe4
++/*
++ * csi_be_soc_pixel_remapping is for the enabling of the pixel remapping.
++ * This remapping is exactly like the stream2mmio remapping.
++ */
++#define CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING    0xe4
++
++#define IPU6_REG_DMA_TOP_AB_GROUP1_BASE_ADDR		0x1ae000
++#define IPU6_REG_DMA_TOP_AB_GROUP2_BASE_ADDR		0x1af000
++#define IPU6_REG_DMA_TOP_AB_RING_MIN_OFFSET(n)		(0x4 + (n) * 0xc)
++#define IPU6_REG_DMA_TOP_AB_RING_MAX_OFFSET(n)		(0x8 + (n) * 0xc)
++#define IPU6_REG_DMA_TOP_AB_RING_ACCESS_OFFSET(n)	(0xc + (n) * 0xc)
++
++enum ipu6_device_ab_group1_target_id {
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R0_SPC_DMEM,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R1_SPC_DMEM,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R2_SPC_DMEM,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R3_SPC_STATUS_REG,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R4_SPC_MASTER_BASE_ADDR,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R5_SPC_PC_STALL,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R6_SPC_EQ,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R7_SPC_RESERVED,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R8_SPC_RESERVED,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R9_SPP0,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R10_SPP1,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R11_CENTRAL_R1,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R12_IRQ,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R13_CENTRAL_R2,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R14_DMA,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R15_DMA,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R16_GP,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R17_ZLW_INSERTER,
++	IPU6_DEVICE_AB_GROUP1_TARGET_ID_R18_AB,
++};
++
++enum nci_ab_access_mode {
++	NCI_AB_ACCESS_MODE_RW,	/* read & write */
++	NCI_AB_ACCESS_MODE_RO,	/* read only */
++	NCI_AB_ACCESS_MODE_WO,	/* write only */
++	NCI_AB_ACCESS_MODE_NA,	/* No access at all */
++};
++
++/* IRQ-related registers in PSYS */
++#define IPU6_REG_PSYS_GPDEV_IRQ_EDGE		0x1aa200
++#define IPU6_REG_PSYS_GPDEV_IRQ_MASK		0x1aa204
++#define IPU6_REG_PSYS_GPDEV_IRQ_STATUS		0x1aa208
++#define IPU6_REG_PSYS_GPDEV_IRQ_CLEAR		0x1aa20c
++#define IPU6_REG_PSYS_GPDEV_IRQ_ENABLE		0x1aa210
++#define IPU6_REG_PSYS_GPDEV_IRQ_LEVEL_NOT_PULSE	0x1aa214
++/* There are 8 FW interrupts, n = 0..7 */
++#define IPU6_PSYS_GPDEV_FWIRQ0			5
++#define IPU6_PSYS_GPDEV_FWIRQ1			6
++#define IPU6_PSYS_GPDEV_FWIRQ2			7
++#define IPU6_PSYS_GPDEV_FWIRQ3			8
++#define IPU6_PSYS_GPDEV_FWIRQ4			9
++#define IPU6_PSYS_GPDEV_FWIRQ5			10
++#define IPU6_PSYS_GPDEV_FWIRQ6			11
++#define IPU6_PSYS_GPDEV_FWIRQ7			12
++#define IPU6_PSYS_GPDEV_IRQ_FWIRQ(n)		BIT(n)
++#define IPU6_REG_PSYS_GPDEV_FWIRQ(n)		(4 * (n) + 0x1aa100)
++
++#endif /* IPU6_PLATFORM_REGS_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c
+new file mode 100644
+index 000000000000..abd40a783729
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6.c
+@@ -0,0 +1,966 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/firmware.h>
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/pci-ats.h>
++#include <linux/pm_runtime.h>
++#include <linux/property.h>
++#include <linux/scatterlist.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include <media/ipu-bridge.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-buttress.h"
++#include "ipu6-cpd.h"
++#include "ipu6-isys.h"
++#include "ipu6-mmu.h"
++#include "ipu6-platform-buttress-regs.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++#include "ipu6-platform-regs.h"
++
++#define IPU6_PCI_BAR		0
++
++struct ipu6_cell_program {
++	u32 magic_number;
++
++	u32 blob_offset;
++	u32 blob_size;
++
++	u32 start[3];
++
++	u32 icache_source;
++	u32 icache_target;
++	u32 icache_size;
++
++	u32 pmem_source;
++	u32 pmem_target;
++	u32 pmem_size;
++
++	u32 data_source;
++	u32 data_target;
++	u32 data_size;
++
++	u32 bss_target;
++	u32 bss_size;
++
++	u32 cell_id;
++	u32 regs_addr;
++
++	u32 cell_pmem_data_bus_address;
++	u32 cell_dmem_data_bus_address;
++	u32 cell_pmem_control_bus_address;
++	u32 cell_dmem_control_bus_address;
++
++	u32 next;
++	u32 dummy[2];
++};
++
++static u32 ipu6se_csi_offsets[] = {
++	IPU6_CSI_PORT_A_ADDR_OFFSET,
++	IPU6_CSI_PORT_B_ADDR_OFFSET,
++	IPU6_CSI_PORT_C_ADDR_OFFSET,
++	IPU6_CSI_PORT_D_ADDR_OFFSET
++};
++
++/*
++ * IPU6 on TGL support maximum 8 csi2 ports
++ * IPU6SE on JSL and IPU6EP on ADL support maximum 4 csi2 ports
++ * IPU6EP on MTL support maximum 6 csi2 ports
++ */
++static u32 ipu6_tgl_csi_offsets[] = {
++	IPU6_CSI_PORT_A_ADDR_OFFSET,
++	IPU6_CSI_PORT_B_ADDR_OFFSET,
++	IPU6_CSI_PORT_C_ADDR_OFFSET,
++	IPU6_CSI_PORT_D_ADDR_OFFSET,
++	IPU6_CSI_PORT_E_ADDR_OFFSET,
++	IPU6_CSI_PORT_F_ADDR_OFFSET,
++	IPU6_CSI_PORT_G_ADDR_OFFSET,
++	IPU6_CSI_PORT_H_ADDR_OFFSET
++};
++
++static u32 ipu6ep_mtl_csi_offsets[] = {
++	IPU6_CSI_PORT_A_ADDR_OFFSET,
++	IPU6_CSI_PORT_B_ADDR_OFFSET,
++	IPU6_CSI_PORT_C_ADDR_OFFSET,
++	IPU6_CSI_PORT_D_ADDR_OFFSET,
++	IPU6_CSI_PORT_E_ADDR_OFFSET,
++	IPU6_CSI_PORT_F_ADDR_OFFSET
++};
++
++static u32 ipu6_csi_offsets[] = {
++	IPU6_CSI_PORT_A_ADDR_OFFSET,
++	IPU6_CSI_PORT_B_ADDR_OFFSET,
++	IPU6_CSI_PORT_C_ADDR_OFFSET,
++	IPU6_CSI_PORT_D_ADDR_OFFSET
++};
++
++static struct ipu6_isys_internal_pdata isys_ipdata = {
++	.hw_variant = {
++		.offset = IPU6_UNIFIED_OFFSET,
++		.nr_mmus = 3,
++		.mmu_hw = {
++			{
++				.offset = IPU6_ISYS_IOMMU0_OFFSET,
++				.info_bits = IPU6_INFO_REQUEST_DESTINATION_IOSF,
++				.nr_l1streams = 16,
++				.l1_block_sz = {
++					3, 8, 2, 2, 2, 2, 2, 2, 1, 1,
++					1, 1, 1, 1, 1, 1
++				},
++				.nr_l2streams = 16,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_ISYS_IOMMU1_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 16,
++				.l1_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 1, 1, 4
++				},
++				.nr_l2streams = 16,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_ISYS_IOMMUI_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 0,
++				.nr_l2streams = 0,
++				.insert_read_before_invalidate = false,
++			},
++		},
++		.cdc_fifos = 3,
++		.cdc_fifo_threshold = {6, 8, 2},
++		.dmem_offset = IPU6_ISYS_DMEM_OFFSET,
++		.spc_offset = IPU6_ISYS_SPC_OFFSET,
++	},
++	.isys_dma_overshoot = IPU6_ISYS_OVERALLOC_MIN,
++};
++
++static struct ipu6_psys_internal_pdata psys_ipdata = {
++	.hw_variant = {
++		.offset = IPU6_UNIFIED_OFFSET,
++		.nr_mmus = 4,
++		.mmu_hw = {
++			{
++				.offset = IPU6_PSYS_IOMMU0_OFFSET,
++				.info_bits =
++				IPU6_INFO_REQUEST_DESTINATION_IOSF,
++				.nr_l1streams = 16,
++				.l1_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.nr_l2streams = 16,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_PSYS_IOMMU1_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 32,
++				.l1_block_sz = {
++					1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 10,
++					5, 4, 14, 6, 4, 14, 6, 4, 8,
++					4, 2, 1, 1, 1, 1, 14
++				},
++				.nr_l2streams = 32,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_PSYS_IOMMU1R_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 16,
++				.l1_block_sz = {
++					1, 4, 4, 4, 4, 16, 8, 4, 32,
++					16, 16, 2, 2, 2, 1, 12
++				},
++				.nr_l2streams = 16,
++				.l2_block_sz = {
++					2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
++					2, 2, 2, 2, 2, 2
++				},
++				.insert_read_before_invalidate = false,
++				.l1_stream_id_reg_offset =
++				IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
++				.l2_stream_id_reg_offset =
++				IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
++			},
++			{
++				.offset = IPU6_PSYS_IOMMUI_OFFSET,
++				.info_bits = 0,
++				.nr_l1streams = 0,
++				.nr_l2streams = 0,
++				.insert_read_before_invalidate = false,
++			},
++		},
++		.dmem_offset = IPU6_PSYS_DMEM_OFFSET,
++	},
++};
++
++static const struct ipu6_buttress_ctrl isys_buttress_ctrl = {
++	.ratio = IPU6_IS_FREQ_CTL_DEFAULT_RATIO,
++	.qos_floor = IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO,
++	.freq_ctl = IPU6_BUTTRESS_REG_IS_FREQ_CTL,
++	.pwr_sts_shift = IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT,
++	.pwr_sts_mask = IPU6_BUTTRESS_PWR_STATE_IS_PWR_MASK,
++	.pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE,
++	.pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE,
++};
++
++static const struct ipu6_buttress_ctrl psys_buttress_ctrl = {
++	.ratio = IPU6_PS_FREQ_CTL_DEFAULT_RATIO,
++	.qos_floor = IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO,
++	.freq_ctl = IPU6_BUTTRESS_REG_PS_FREQ_CTL,
++	.pwr_sts_shift = IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT,
++	.pwr_sts_mask = IPU6_BUTTRESS_PWR_STATE_PS_PWR_MASK,
++	.pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE,
++	.pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE,
++};
++
++static void
++ipu6_pkg_dir_configure_spc(struct ipu6_device *isp,
++			   const struct ipu6_hw_variants *hw_variant,
++			   int pkg_dir_idx, void __iomem *base,
++			   u64 *pkg_dir, dma_addr_t pkg_dir_vied_address)
++{
++	struct ipu6_cell_program *prog;
++	void __iomem *spc_base;
++	u32 server_fw_addr;
++	dma_addr_t dma_addr;
++	u32 pg_offset;
++
++	server_fw_addr = lower_32_bits(*(pkg_dir + (pkg_dir_idx + 1) * 2));
++	if (pkg_dir_idx == IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX)
++		dma_addr = sg_dma_address(isp->isys->fw_sgt.sgl);
++	else
++		dma_addr = sg_dma_address(isp->psys->fw_sgt.sgl);
++
++	pg_offset = server_fw_addr - dma_addr;
++	prog = (struct ipu6_cell_program *)((u64)isp->cpd_fw->data + pg_offset);
++	spc_base = base + prog->regs_addr;
++	if (spc_base != (base + hw_variant->spc_offset))
++		dev_warn(&isp->pdev->dev,
++			 "SPC reg addr %p not matching value from CPD %p\n",
++			 base + hw_variant->spc_offset, spc_base);
++	writel(server_fw_addr + prog->blob_offset +
++	       prog->icache_source, spc_base + IPU6_PSYS_REG_SPC_ICACHE_BASE);
++	writel(IPU6_INFO_REQUEST_DESTINATION_IOSF,
++	       spc_base + IPU6_REG_PSYS_INFO_SEG_0_CONFIG_ICACHE_MASTER);
++	writel(prog->start[1], spc_base + IPU6_PSYS_REG_SPC_START_PC);
++	writel(pkg_dir_vied_address, base + hw_variant->dmem_offset);
++}
++
++void ipu6_configure_spc(struct ipu6_device *isp,
++			const struct ipu6_hw_variants *hw_variant,
++			int pkg_dir_idx, void __iomem *base, u64 *pkg_dir,
++			dma_addr_t pkg_dir_dma_addr)
++{
++	void __iomem *dmem_base = base + hw_variant->dmem_offset;
++	void __iomem *spc_regs_base = base + hw_variant->spc_offset;
++	u32 val;
++
++	val = readl(spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL);
++	val |= IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE;
++	writel(val, spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL);
++
++	if (isp->secure_mode)
++		writel(IPU6_PKG_DIR_IMR_OFFSET, dmem_base);
++	else
++		ipu6_pkg_dir_configure_spc(isp, hw_variant, pkg_dir_idx, base,
++					   pkg_dir, pkg_dir_dma_addr);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, INTEL_IPU6);
++
++static void ipu6_internal_pdata_init(struct ipu6_device *isp)
++{
++	u8 hw_ver = isp->hw_ver;
++
++	isys_ipdata.num_parallel_streams = IPU6_ISYS_NUM_STREAMS;
++	isys_ipdata.sram_gran_shift = IPU6_SRAM_GRANULARITY_SHIFT;
++	isys_ipdata.sram_gran_size = IPU6_SRAM_GRANULARITY_SIZE;
++	isys_ipdata.max_sram_size = IPU6_MAX_SRAM_SIZE;
++	isys_ipdata.sensor_type_start = IPU6_FW_ISYS_SENSOR_TYPE_START;
++	isys_ipdata.sensor_type_end = IPU6_FW_ISYS_SENSOR_TYPE_END;
++	isys_ipdata.max_streams = IPU6_ISYS_NUM_STREAMS;
++	isys_ipdata.max_send_queues = IPU6_N_MAX_SEND_QUEUES;
++	isys_ipdata.max_sram_blocks = IPU6_NOF_SRAM_BLOCKS_MAX;
++	isys_ipdata.max_devq_size = IPU6_DEV_SEND_QUEUE_SIZE;
++	isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6_csi_offsets);
++	isys_ipdata.csi2.offsets = ipu6_csi_offsets;
++	isys_ipdata.csi2.irq_mask = IPU6_CSI_RX_ERROR_IRQ_MASK;
++	isys_ipdata.csi2.ctrl0_irq_edge = IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE;
++	isys_ipdata.csi2.ctrl0_irq_clear =
++		IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR;
++	isys_ipdata.csi2.ctrl0_irq_mask = IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK;
++	isys_ipdata.csi2.ctrl0_irq_enable =
++		IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE;
++	isys_ipdata.csi2.ctrl0_irq_status =
++		IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS;
++	isys_ipdata.csi2.ctrl0_irq_lnp =
++		IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE;
++	isys_ipdata.enhanced_iwake = is_ipu6ep_mtl(hw_ver) || is_ipu6ep(hw_ver);
++	psys_ipdata.hw_variant.spc_offset = IPU6_PSYS_SPC_OFFSET;
++	isys_ipdata.csi2.fw_access_port_ofs = CSI_REG_HUB_FW_ACCESS_PORT_OFS;
++
++	if (is_ipu6ep(hw_ver)) {
++		isys_ipdata.ltr = IPU6EP_LTR_VALUE;
++		isys_ipdata.memopen_threshold = IPU6EP_MIN_MEMOPEN_TH;
++	}
++
++	if (is_ipu6_tgl(hw_ver)) {
++		isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6_tgl_csi_offsets);
++		isys_ipdata.csi2.offsets = ipu6_tgl_csi_offsets;
++	}
++
++	if (is_ipu6ep_mtl(hw_ver)) {
++		isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6ep_mtl_csi_offsets);
++		isys_ipdata.csi2.offsets = ipu6ep_mtl_csi_offsets;
++
++		isys_ipdata.csi2.ctrl0_irq_edge =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE;
++		isys_ipdata.csi2.ctrl0_irq_clear =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR;
++		isys_ipdata.csi2.ctrl0_irq_mask =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK;
++		isys_ipdata.csi2.ctrl0_irq_enable =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE;
++		isys_ipdata.csi2.ctrl0_irq_lnp =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE;
++		isys_ipdata.csi2.ctrl0_irq_status =
++			IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS;
++		isys_ipdata.csi2.fw_access_port_ofs =
++			CSI_REG_HUB_FW_ACCESS_PORT_V6OFS;
++		isys_ipdata.ltr = IPU6EP_MTL_LTR_VALUE;
++		isys_ipdata.memopen_threshold = IPU6EP_MTL_MIN_MEMOPEN_TH;
++	}
++
++	if (is_ipu6se(hw_ver)) {
++		isys_ipdata.csi2.nports = ARRAY_SIZE(ipu6se_csi_offsets);
++		isys_ipdata.csi2.irq_mask = IPU6SE_CSI_RX_ERROR_IRQ_MASK;
++		isys_ipdata.csi2.offsets = ipu6se_csi_offsets;
++		isys_ipdata.num_parallel_streams = IPU6SE_ISYS_NUM_STREAMS;
++		isys_ipdata.sram_gran_shift = IPU6SE_SRAM_GRANULARITY_SHIFT;
++		isys_ipdata.sram_gran_size = IPU6SE_SRAM_GRANULARITY_SIZE;
++		isys_ipdata.max_sram_size = IPU6SE_MAX_SRAM_SIZE;
++		isys_ipdata.sensor_type_start =
++			IPU6SE_FW_ISYS_SENSOR_TYPE_START;
++		isys_ipdata.sensor_type_end = IPU6SE_FW_ISYS_SENSOR_TYPE_END;
++		isys_ipdata.max_streams = IPU6SE_ISYS_NUM_STREAMS;
++		isys_ipdata.max_send_queues = IPU6SE_N_MAX_SEND_QUEUES;
++		isys_ipdata.max_sram_blocks = IPU6SE_NOF_SRAM_BLOCKS_MAX;
++		isys_ipdata.max_devq_size = IPU6SE_DEV_SEND_QUEUE_SIZE;
++		psys_ipdata.hw_variant.spc_offset = IPU6SE_PSYS_SPC_OFFSET;
++	}
++}
++
++static int ipu6_isys_check_fwnode_graph(struct fwnode_handle *fwnode)
++{
++	struct fwnode_handle *endpoint;
++
++	if (IS_ERR_OR_NULL(fwnode))
++		return -EINVAL;
++
++	endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
++	if (endpoint) {
++		fwnode_handle_put(endpoint);
++		return 0;
++	}
++
++	return ipu6_isys_check_fwnode_graph(fwnode->secondary);
++}
++
++static struct ipu6_bus_device *
++ipu6_isys_init(struct pci_dev *pdev, struct device *parent,
++	       struct ipu6_buttress_ctrl *ctrl, void __iomem *base,
++	       const struct ipu6_isys_internal_pdata *ipdata)
++{
++	struct device *dev = &pdev->dev;
++	struct fwnode_handle *fwnode = dev_fwnode(dev);
++	struct ipu6_bus_device *isys_adev;
++	struct ipu6_isys_pdata *pdata;
++	int ret;
++
++	/* check fwnode at first, fallback into bridge if no fwnode graph */
++	ret = ipu6_isys_check_fwnode_graph(fwnode);
++	if (ret) {
++		if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) {
++			dev_err(dev,
++				"fwnode graph has no endpoints connection\n");
++			return ERR_PTR(-EINVAL);
++		}
++
++		ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb);
++		if (ret) {
++			dev_err_probe(dev, ret, "IPU6 bridge init failed\n");
++			return ERR_PTR(ret);
++		}
++	}
++
++	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
++	if (!pdata)
++		return ERR_PTR(-ENOMEM);
++
++	pdata->base = base;
++	pdata->ipdata = ipdata;
++
++	isys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl,
++					       IPU6_ISYS_NAME);
++	if (IS_ERR(isys_adev)) {
++		dev_err_probe(dev, PTR_ERR(isys_adev),
++			      "ipu6_bus_initialize_device isys failed\n");
++		kfree(pdata);
++		return ERR_CAST(isys_adev);
++	}
++
++	isys_adev->mmu = ipu6_mmu_init(dev, base, ISYS_MMID,
++				       &ipdata->hw_variant);
++	if (IS_ERR(isys_adev->mmu)) {
++		dev_err_probe(dev, PTR_ERR(isys_adev),
++			      "ipu6_mmu_init(isys_adev->mmu) failed\n");
++		put_device(&isys_adev->auxdev.dev);
++		kfree(pdata);
++		return ERR_CAST(isys_adev->mmu);
++	}
++
++	isys_adev->mmu->dev = &isys_adev->auxdev.dev;
++
++	ret = ipu6_bus_add_device(isys_adev);
++	if (ret) {
++		kfree(pdata);
++		return ERR_PTR(ret);
++	}
++
++	return isys_adev;
++}
++
++static struct ipu6_bus_device *
++ipu6_psys_init(struct pci_dev *pdev, struct device *parent,
++	       struct ipu6_buttress_ctrl *ctrl, void __iomem *base,
++	       const struct ipu6_psys_internal_pdata *ipdata)
++{
++	struct ipu6_bus_device *psys_adev;
++	struct ipu6_psys_pdata *pdata;
++	int ret;
++
++	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
++	if (!pdata)
++		return ERR_PTR(-ENOMEM);
++
++	pdata->base = base;
++	pdata->ipdata = ipdata;
++
++	psys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl,
++					       IPU6_PSYS_NAME);
++	if (IS_ERR(psys_adev)) {
++		dev_err_probe(&pdev->dev, PTR_ERR(psys_adev),
++			      "ipu6_bus_initialize_device psys failed\n");
++		kfree(pdata);
++		return ERR_CAST(psys_adev);
++	}
++
++	psys_adev->mmu = ipu6_mmu_init(&pdev->dev, base, PSYS_MMID,
++				       &ipdata->hw_variant);
++	if (IS_ERR(psys_adev->mmu)) {
++		dev_err_probe(&pdev->dev, PTR_ERR(psys_adev),
++			      "ipu6_mmu_init(psys_adev->mmu) failed\n");
++		put_device(&psys_adev->auxdev.dev);
++		kfree(pdata);
++		return ERR_CAST(psys_adev->mmu);
++	}
++
++	psys_adev->mmu->dev = &psys_adev->auxdev.dev;
++
++	ret = ipu6_bus_add_device(psys_adev);
++	if (ret) {
++		kfree(pdata);
++		return ERR_PTR(ret);
++	}
++
++	return psys_adev;
++}
++
++static int ipu6_pci_config_setup(struct pci_dev *dev, u8 hw_ver)
++{
++	int ret;
++
++	/* disable IPU6 PCI ATS on mtl ES2 */
++	if (is_ipu6ep_mtl(hw_ver) && boot_cpu_data.x86_stepping == 0x2 &&
++	    pci_ats_supported(dev))
++		pci_disable_ats(dev);
++
++	/* No PCI msi capability for IPU6EP */
++	if (is_ipu6ep(hw_ver) || is_ipu6ep_mtl(hw_ver)) {
++		/* likely do nothing as msi not enabled by default */
++		pci_disable_msi(dev);
++		return 0;
++	}
++
++	ret = pci_alloc_irq_vectors(dev, 1, 1, PCI_IRQ_MSI);
++	if (ret < 0)
++		return dev_err_probe(&dev->dev, ret, "Request msi failed");
++
++	return 0;
++}
++
++static void ipu6_configure_vc_mechanism(struct ipu6_device *isp)
++{
++	u32 val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
++
++	if (IPU6_BTRS_ARB_STALL_MODE_VC0 == IPU6_BTRS_ARB_MODE_TYPE_STALL)
++		val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0;
++	else
++		val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0;
++
++	if (IPU6_BTRS_ARB_STALL_MODE_VC1 == IPU6_BTRS_ARB_MODE_TYPE_STALL)
++		val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1;
++	else
++		val &= ~BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1;
++
++	writel(val, isp->base + BUTTRESS_REG_BTRS_CTRL);
++}
++
++static int request_cpd_fw(const struct firmware **firmware_p, const char *name,
++			  struct device *device)
++{
++	const struct firmware *fw;
++	struct firmware *dst;
++	int ret = 0;
++
++	ret = request_firmware(&fw, name, device);
++	if (ret)
++		return ret;
++
++	if (is_vmalloc_addr(fw->data)) {
++		*firmware_p = fw;
++		return 0;
++	}
++
++	dst = kzalloc(sizeof(*dst), GFP_KERNEL);
++	if (!dst) {
++		ret = -ENOMEM;
++		goto release_firmware;
++	}
++
++	dst->size = fw->size;
++	dst->data = vmalloc(fw->size);
++	if (!dst->data) {
++		kfree(dst);
++		ret = -ENOMEM;
++		goto release_firmware;
++	}
++
++	memcpy((void *)dst->data, fw->data, fw->size);
++	*firmware_p = dst;
++
++release_firmware:
++	release_firmware(fw);
++
++	return ret;
++}
++
++static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
++{
++	struct ipu6_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL;
++	struct device *dev = &pdev->dev;
++	void __iomem *isys_base = NULL;
++	void __iomem *psys_base = NULL;
++	struct ipu6_device *isp;
++	phys_addr_t phys;
++	u32 val, version, sku_id;
++	int ret;
++
++	isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
++	if (!isp)
++		return -ENOMEM;
++
++	isp->pdev = pdev;
++	INIT_LIST_HEAD(&isp->devices);
++
++	ret = pcim_enable_device(pdev);
++	if (ret)
++		return dev_err_probe(dev, ret, "Enable PCI device failed\n");
++
++	phys = pci_resource_start(pdev, IPU6_PCI_BAR);
++	dev_dbg(dev, "IPU6 PCI bar[%u] = %pa\n", IPU6_PCI_BAR, &phys);
++
++	ret = pcim_iomap_regions(pdev, 1 << IPU6_PCI_BAR, pci_name(pdev));
++	if (ret)
++		return dev_err_probe(dev, ret, "Failed to I/O mem remappinp\n");
++
++	isp->base = pcim_iomap_table(pdev)[IPU6_PCI_BAR];
++	pci_set_drvdata(pdev, isp);
++	pci_set_master(pdev);
++
++	isp->cpd_metadata_cmpnt_size = sizeof(struct ipu6_cpd_metadata_cmpnt);
++	switch (id->device) {
++	case PCI_DEVICE_ID_INTEL_IPU6:
++		isp->hw_ver = IPU6_VER_6;
++		isp->cpd_fw_name = IPU6_FIRMWARE_NAME;
++		break;
++	case PCI_DEVICE_ID_INTEL_IPU6SE:
++		isp->hw_ver = IPU6_VER_6SE;
++		isp->cpd_fw_name = IPU6SE_FIRMWARE_NAME;
++		isp->cpd_metadata_cmpnt_size =
++			sizeof(struct ipu6se_cpd_metadata_cmpnt);
++		break;
++	case PCI_DEVICE_ID_INTEL_IPU6EP_ADLP:
++	case PCI_DEVICE_ID_INTEL_IPU6EP_ADLN:
++	case PCI_DEVICE_ID_INTEL_IPU6EP_RPLP:
++		isp->hw_ver = IPU6_VER_6EP;
++		isp->cpd_fw_name = IPU6EP_FIRMWARE_NAME;
++		break;
++	case PCI_DEVICE_ID_INTEL_IPU6EP_MTL:
++		isp->hw_ver = IPU6_VER_6EP_MTL;
++		isp->cpd_fw_name = IPU6EPMTL_FIRMWARE_NAME;
++		break;
++	default:
++		return dev_err_probe(dev, -ENODEV,
++				     "Unsupported IPU6 device %x\n",
++				     id->device);
++	}
++
++	ipu6_internal_pdata_init(isp);
++
++	isys_base = isp->base + isys_ipdata.hw_variant.offset;
++	psys_base = isp->base + psys_ipdata.hw_variant.offset;
++
++	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(39));
++	if (ret)
++		return dev_err_probe(dev, ret, "Failed to set DMA mask\n");
++
++	ret = dma_set_max_seg_size(dev, UINT_MAX);
++	if (ret)
++		return dev_err_probe(dev, ret, "Failed to set max_seg_size\n");
++
++	ret = ipu6_pci_config_setup(pdev, isp->hw_ver);
++	if (ret)
++		return ret;
++
++	ret = ipu6_buttress_init(isp);
++	if (ret)
++		return ret;
++
++	ret = request_cpd_fw(&isp->cpd_fw, isp->cpd_fw_name, dev);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "Requesting signed firmware %s failed\n",
++			      isp->cpd_fw_name);
++		goto buttress_exit;
++	}
++
++	ret = ipu6_cpd_validate_cpd_file(isp, isp->cpd_fw->data,
++					 isp->cpd_fw->size);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "Failed to validate cpd\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	isys_ctrl = devm_kmemdup(dev, &isys_buttress_ctrl,
++				 sizeof(isys_buttress_ctrl), GFP_KERNEL);
++	if (!isys_ctrl) {
++		ret = -ENOMEM;
++		goto out_ipu6_bus_del_devices;
++	}
++
++	isp->isys = ipu6_isys_init(pdev, dev, isys_ctrl, isys_base,
++				   &isys_ipdata);
++	if (IS_ERR(isp->isys)) {
++		ret = PTR_ERR(isp->isys);
++		goto out_ipu6_bus_del_devices;
++	}
++
++	psys_ctrl = devm_kmemdup(dev, &psys_buttress_ctrl,
++				 sizeof(psys_buttress_ctrl), GFP_KERNEL);
++	if (!psys_ctrl) {
++		ret = -ENOMEM;
++		goto out_ipu6_bus_del_devices;
++	}
++
++	isp->psys = ipu6_psys_init(pdev, &isp->isys->auxdev.dev, psys_ctrl,
++				   psys_base, &psys_ipdata);
++	if (IS_ERR(isp->psys)) {
++		ret = PTR_ERR(isp->psys);
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev);
++	if (ret < 0)
++		goto out_ipu6_bus_del_devices;
++
++	ret = ipu6_mmu_hw_init(isp->psys->mmu);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "Failed to set MMU hardware\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = ipu6_buttress_map_fw_image(isp->psys, isp->cpd_fw,
++					 &isp->psys->fw_sgt);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret, "failed to map fw image\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = ipu6_cpd_create_pkg_dir(isp->psys, isp->cpd_fw->data);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "failed to create pkg dir\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = devm_request_threaded_irq(dev, pdev->irq, ipu6_buttress_isr,
++					ipu6_buttress_isr_threaded,
++					IRQF_SHARED, IPU6_NAME, isp);
++	if (ret) {
++		dev_err_probe(dev, ret, "Requesting irq failed\n");
++		goto out_ipu6_bus_del_devices;
++	}
++
++	ret = ipu6_buttress_authenticate(isp);
++	if (ret) {
++		dev_err_probe(&isp->pdev->dev, ret,
++			      "FW authentication failed\n");
++		goto out_free_irq;
++	}
++
++	ipu6_mmu_hw_cleanup(isp->psys->mmu);
++	pm_runtime_put(&isp->psys->auxdev.dev);
++
++	/* Configure the arbitration mechanisms for VC requests */
++	ipu6_configure_vc_mechanism(isp);
++
++	val = readl(isp->base + BUTTRESS_REG_SKU);
++	sku_id = FIELD_GET(GENMASK(6, 4), val);
++	version = FIELD_GET(GENMASK(3, 0), val);
++	dev_info(dev, "IPU%u-v%u[%x] hardware version %d\n", version, sku_id,
++		 pdev->device, isp->hw_ver);
++
++	pm_runtime_put_noidle(dev);
++	pm_runtime_allow(dev);
++
++	isp->bus_ready_to_probe = true;
++
++	return 0;
++
++out_free_irq:
++	devm_free_irq(dev, pdev->irq, isp);
++out_ipu6_bus_del_devices:
++	if (isp->psys) {
++		ipu6_cpd_free_pkg_dir(isp->psys);
++		ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
++	}
++	if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu))
++		ipu6_mmu_cleanup(isp->psys->mmu);
++	if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu))
++		ipu6_mmu_cleanup(isp->isys->mmu);
++	ipu6_bus_del_devices(pdev);
++	release_firmware(isp->cpd_fw);
++buttress_exit:
++	ipu6_buttress_exit(isp);
++
++	return ret;
++}
++
++static void ipu6_pci_remove(struct pci_dev *pdev)
++{
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	struct ipu6_mmu *isys_mmu = isp->isys->mmu;
++	struct ipu6_mmu *psys_mmu = isp->psys->mmu;
++
++	devm_free_irq(&pdev->dev, pdev->irq, isp);
++	ipu6_cpd_free_pkg_dir(isp->psys);
++
++	ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
++	ipu6_buttress_exit(isp);
++
++	ipu6_bus_del_devices(pdev);
++
++	pm_runtime_forbid(&pdev->dev);
++	pm_runtime_get_noresume(&pdev->dev);
++
++	pci_release_regions(pdev);
++	pci_disable_device(pdev);
++
++	release_firmware(isp->cpd_fw);
++
++	ipu6_mmu_cleanup(psys_mmu);
++	ipu6_mmu_cleanup(isys_mmu);
++}
++
++static void ipu6_pci_reset_prepare(struct pci_dev *pdev)
++{
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++
++	pm_runtime_forbid(&isp->pdev->dev);
++}
++
++static void ipu6_pci_reset_done(struct pci_dev *pdev)
++{
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++
++	ipu6_buttress_restore(isp);
++	if (isp->secure_mode)
++		ipu6_buttress_reset_authentication(isp);
++
++	isp->need_ipc_reset = true;
++	pm_runtime_allow(&isp->pdev->dev);
++}
++
++/*
++ * PCI base driver code requires driver to provide these to enable
++ * PCI device level PM state transitions (D0<->D3)
++ */
++static int ipu6_suspend(struct device *dev)
++{
++	return 0;
++}
++
++static int ipu6_resume(struct device *dev)
++{
++	struct pci_dev *pdev = to_pci_dev(dev);
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	struct ipu6_buttress *b = &isp->buttress;
++	int ret;
++
++	/* Configure the arbitration mechanisms for VC requests */
++	ipu6_configure_vc_mechanism(isp);
++
++	isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
++	dev_info(dev, "IPU6 in %s mode\n",
++		 isp->secure_mode ? "secure" : "non-secure");
++
++	ipu6_buttress_restore(isp);
++
++	ret = ipu6_buttress_ipc_reset(isp, &b->cse);
++	if (ret)
++		dev_err(&isp->pdev->dev, "IPC reset protocol failed!\n");
++
++	ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev);
++	if (ret < 0) {
++		dev_err(&isp->psys->auxdev.dev, "Failed to get runtime PM\n");
++		return 0;
++	}
++
++	ret = ipu6_buttress_authenticate(isp);
++	if (ret)
++		dev_err(&isp->pdev->dev, "FW authentication failed(%d)\n", ret);
++
++	pm_runtime_put(&isp->psys->auxdev.dev);
++
++	return 0;
++}
++
++static int ipu6_runtime_resume(struct device *dev)
++{
++	struct pci_dev *pdev = to_pci_dev(dev);
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	int ret;
++
++	ipu6_configure_vc_mechanism(isp);
++	ipu6_buttress_restore(isp);
++
++	if (isp->need_ipc_reset) {
++		struct ipu6_buttress *b = &isp->buttress;
++
++		isp->need_ipc_reset = false;
++		ret = ipu6_buttress_ipc_reset(isp, &b->cse);
++		if (ret)
++			dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
++	}
++
++	return 0;
++}
++
++static const struct dev_pm_ops ipu6_pm_ops = {
++	SET_SYSTEM_SLEEP_PM_OPS(&ipu6_suspend, &ipu6_resume)
++	SET_RUNTIME_PM_OPS(&ipu6_suspend, &ipu6_runtime_resume, NULL)
++};
++
++static const struct pci_device_id ipu6_pci_tbl[] = {
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6SE) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_ADLP) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_ADLN) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_RPLP) },
++	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_MTL) },
++	{ }
++};
++MODULE_DEVICE_TABLE(pci, ipu6_pci_tbl);
++
++static const struct pci_error_handlers pci_err_handlers = {
++	.reset_prepare = ipu6_pci_reset_prepare,
++	.reset_done = ipu6_pci_reset_done,
++};
++
++static struct pci_driver ipu6_pci_driver = {
++	.name = IPU6_NAME,
++	.id_table = ipu6_pci_tbl,
++	.probe = ipu6_pci_probe,
++	.remove = ipu6_pci_remove,
++	.driver = {
++		.pm = pm_ptr(&ipu6_pm_ops),
++	},
++	.err_handler = &pci_err_handlers,
++};
++
++module_pci_driver(ipu6_pci_driver);
++
++MODULE_IMPORT_NS(INTEL_IPU_BRIDGE);
++MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
++MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
++MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
++MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>");
++MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>");
++MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Intel IPU6 PCI driver");
+diff --git a/drivers/media/pci/intel/ipu6/ipu6.h b/drivers/media/pci/intel/ipu6/ipu6.h
+new file mode 100644
+index 000000000000..04e7e7e61ca5
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6.h
+@@ -0,0 +1,356 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_H
++#define IPU6_H
++
++#include <linux/list.h>
++#include <linux/pci.h>
++#include <linux/types.h>
++
++#include "ipu6-buttress.h"
++
++struct firmware;
++struct pci_dev;
++struct ipu6_bus_device;
++
++#define PCI_DEVICE_ID_INTEL_IPU6		0x9a19
++#define PCI_DEVICE_ID_INTEL_IPU6SE		0x4e19
++#define PCI_DEVICE_ID_INTEL_IPU6EP_ADLP		0x465d
++#define PCI_DEVICE_ID_INTEL_IPU6EP_ADLN		0x462e
++#define PCI_DEVICE_ID_INTEL_IPU6EP_RPLP		0xa75d
++#define PCI_DEVICE_ID_INTEL_IPU6EP_MTL		0x7d19
++
++#define IPU6_NAME			"intel-ipu6"
++#define IPU6_MEDIA_DEV_MODEL_NAME	"ipu6"
++
++#define IPU6SE_FIRMWARE_NAME		"intel/ipu6se_fw.bin"
++#define IPU6EP_FIRMWARE_NAME		"intel/ipu6ep_fw.bin"
++#define IPU6_FIRMWARE_NAME		"intel/ipu6_fw.bin"
++#define IPU6EPMTL_FIRMWARE_NAME		"intel/ipu6epmtl_fw.bin"
++
++enum ipu6_version {
++	IPU6_VER_INVALID = 0,
++	IPU6_VER_6 = 1,
++	IPU6_VER_6SE = 3,
++	IPU6_VER_6EP = 5,
++	IPU6_VER_6EP_MTL = 6,
++};
++
++/*
++ * IPU6 - TGL
++ * IPU6SE - JSL
++ * IPU6EP - ADL/RPL
++ * IPU6EP_MTL - MTL
++ */
++static inline bool is_ipu6se(u8 hw_ver)
++{
++	return hw_ver == IPU6_VER_6SE;
++}
++
++static inline bool is_ipu6ep(u8 hw_ver)
++{
++	return hw_ver == IPU6_VER_6EP;
++}
++
++static inline bool is_ipu6ep_mtl(u8 hw_ver)
++{
++	return hw_ver == IPU6_VER_6EP_MTL;
++}
++
++static inline bool is_ipu6_tgl(u8 hw_ver)
++{
++	return hw_ver == IPU6_VER_6;
++}
++
++/*
++ * ISYS DMA can overshoot. For higher resolutions over allocation is one line
++ * but it must be at minimum 1024 bytes. Value could be different in
++ * different versions / generations thus provide it via platform data.
++ */
++#define IPU6_ISYS_OVERALLOC_MIN		1024
++
++/* Physical pages in GDA is 128, page size is 2K for IPU6, 1K for others */
++#define IPU6_DEVICE_GDA_NR_PAGES		128
++
++/* Virtualization factor to calculate the available virtual pages */
++#define IPU6_DEVICE_GDA_VIRT_FACTOR	32
++
++#define NR_OF_MMU_RESOURCES			2
++
++struct ipu6_device {
++	struct pci_dev *pdev;
++	struct list_head devices;
++	struct ipu6_bus_device *isys;
++	struct ipu6_bus_device *psys;
++	struct ipu6_buttress buttress;
++
++	const struct firmware *cpd_fw;
++	const char *cpd_fw_name;
++	u32 cpd_metadata_cmpnt_size;
++
++	void __iomem *base;
++	bool need_ipc_reset;
++	bool secure_mode;
++	u8 hw_ver;
++	bool bus_ready_to_probe;
++};
++
++#define IPU6_ISYS_NAME "isys"
++#define IPU6_PSYS_NAME "psys"
++
++#define IPU6_MMU_MAX_DEVICES		4
++#define IPU6_MMU_ADDR_BITS		32
++/* The firmware is accessible within the first 2 GiB only in non-secure mode. */
++#define IPU6_MMU_ADDR_BITS_NON_SECURE	31
++
++#define IPU6_MMU_MAX_TLB_L1_STREAMS	32
++#define IPU6_MMU_MAX_TLB_L2_STREAMS	32
++#define IPU6_MAX_LI_BLOCK_ADDR		128
++#define IPU6_MAX_L2_BLOCK_ADDR		64
++
++#define IPU6_ISYS_MAX_CSI2_LEGACY_PORTS	4
++#define IPU6_ISYS_MAX_CSI2_COMBO_PORTS	2
++
++#define IPU6_MAX_FRAME_COUNTER	0xff
++
++#define IPU6SE_ISYS_NUM_STREAMS          IPU6SE_NONSECURE_STREAM_ID_MAX
++#define IPU6_ISYS_NUM_STREAMS            IPU6_NONSECURE_STREAM_ID_MAX
++
++/*
++ * To maximize the IOSF utlization, IPU6 need to send requests in bursts.
++ * At the DMA interface with the buttress, there are CDC FIFOs with burst
++ * collection capability. CDC FIFO burst collectors have a configurable
++ * threshold and is configured based on the outcome of performance measurements.
++ *
++ * isys has 3 ports with IOSF interface for VC0, VC1 and VC2
++ * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2
++ *
++ * Threshold values are pre-defined and are arrived at after performance
++ * evaluations on a type of IPU6
++ */
++#define IPU6_MAX_VC_IOSF_PORTS		4
++
++/*
++ * IPU6 must configure correct arbitration mechanism related to the IOSF VC
++ * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on
++ * stall and 1 means stall until the request is completed.
++ */
++#define IPU6_BTRS_ARB_MODE_TYPE_REARB	0
++#define IPU6_BTRS_ARB_MODE_TYPE_STALL	1
++
++/* Currently chosen arbitration mechanism for VC0 */
++#define IPU6_BTRS_ARB_STALL_MODE_VC0	\
++			IPU6_BTRS_ARB_MODE_TYPE_REARB
++
++/* Currently chosen arbitration mechanism for VC1 */
++#define IPU6_BTRS_ARB_STALL_MODE_VC1	\
++			IPU6_BTRS_ARB_MODE_TYPE_REARB
++
++/*
++ * MMU Invalidation HW bug workaround by ZLW mechanism
++ *
++ * Old IPU6 MMUV2 has a bug in the invalidation mechanism which might result in
++ * wrong translation or replication of the translation. This will cause data
++ * corruption. So we cannot directly use the MMU V2 invalidation registers
++ * to invalidate the MMU. Instead, whenever an invalidate is called, we need to
++ * clear the TLB by evicting all the valid translations by filling it with trash
++ * buffer (which is guaranteed not to be used by any other processes). ZLW is
++ * used to fill the L1 and L2 caches with the trash buffer translations. ZLW
++ * or Zero length write, is pre-fetch mechanism to pre-fetch the pages in
++ * advance to the L1 and L2 caches without triggering any memory operations.
++ *
++ * In MMU V2, L1 -> 16 streams and 64 blocks, maximum 16 blocks per stream
++ * One L1 block has 16 entries, hence points to 16 * 4K pages
++ * L2 -> 16 streams and 32 blocks. 2 blocks per streams
++ * One L2 block maps to 1024 L1 entries, hence points to 4MB address range
++ * 2 blocks per L2 stream means, 1 stream points to 8MB range
++ *
++ * As we need to clear the caches and 8MB being the biggest cache size, we need
++ * to have trash buffer which points to 8MB address range. As these trash
++ * buffers are not used for any memory transactions, we need only the least
++ * amount of physical memory. So we reserve 8MB IOVA address range but only
++ * one page is reserved from physical memory. Each of this 8MB IOVA address
++ * range is then mapped to the same physical memory page.
++ */
++/* One L2 entry maps 1024 L1 entries and one L1 entry per page */
++#define IPU6_MMUV2_L2_RANGE		(1024 * PAGE_SIZE)
++/* Max L2 blocks per stream */
++#define IPU6_MMUV2_MAX_L2_BLOCKS	2
++/* Max L1 blocks per stream */
++#define IPU6_MMUV2_MAX_L1_BLOCKS	16
++#define IPU6_MMUV2_TRASH_RANGE	(IPU6_MMUV2_L2_RANGE * IPU6_MMUV2_MAX_L2_BLOCKS)
++/* Entries per L1 block */
++#define MMUV2_ENTRIES_PER_L1_BLOCK	16
++#define MMUV2_TRASH_L1_BLOCK_OFFSET	(MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE)
++#define MMUV2_TRASH_L2_BLOCK_OFFSET	IPU6_MMUV2_L2_RANGE
++
++/*
++ * In some of the IPU6 MMUs, there is provision to configure L1 and L2 page
++ * table caches. Both these L1 and L2 caches are divided into multiple sections
++ * called streams. There is maximum 16 streams for both caches. Each of these
++ * sections are subdivided into multiple blocks. When nr_l1streams = 0 and
++ * nr_l2streams = 0, means the MMU is of type MMU_V1 and do not support
++ * L1/L2 page table caches.
++ *
++ * L1 stream per block sizes are configurable and varies per usecase.
++ * L2 has constant block sizes - 2 blocks per stream.
++ *
++ * MMU1 support pre-fetching of the pages to have less cache lookup misses. To
++ * enable the pre-fetching, MMU1 AT (Address Translator) device registers
++ * need to be configured.
++ *
++ * There are four types of memory accesses which requires ZLW configuration.
++ * ZLW(Zero Length Write) is a mechanism to enable VT-d pre-fetching on IOMMU.
++ *
++ * 1. Sequential Access or 1D mode
++ *	Set ZLW_EN -> 1
++ *	set ZLW_PAGE_CROSS_1D -> 1
++ *	Set ZLW_N to "N" pages so that ZLW will be inserte N pages ahead where
++ *		  N is pre-defined and hardcoded in the platform data
++ *	Set ZLW_2D -> 0
++ *
++ * 2. ZLW 2D mode
++ *	Set ZLW_EN -> 1
++ *	set ZLW_PAGE_CROSS_1D -> 1,
++ *	Set ZLW_N -> 0
++ *	Set ZLW_2D -> 1
++ *
++ * 3. ZLW Enable (no 1D or 2D mode)
++ *	Set ZLW_EN -> 1
++ *	set ZLW_PAGE_CROSS_1D -> 0,
++ *	Set ZLW_N -> 0
++ *	Set ZLW_2D -> 0
++ *
++ * 4. ZLW disable
++ *	Set ZLW_EN -> 0
++ *	set ZLW_PAGE_CROSS_1D -> 0,
++ *	Set ZLW_N -> 0
++ *	Set ZLW_2D -> 0
++ *
++ * To configure the ZLW for the above memory access, four registers are
++ * available. Hence to track these four settings, we have the following entries
++ * in the struct ipu6_mmu_hw. Each of these entries are per stream and
++ * available only for the L1 streams.
++ *
++ * a. l1_zlw_en -> To track zlw enabled per stream (ZLW_EN)
++ * b. l1_zlw_1d_mode -> Track 1D mode per stream. ZLW inserted at page boundary
++ * c. l1_ins_zlw_ahead_pages -> to track how advance the ZLW need to be inserted
++ *			Insert ZLW request N pages ahead address.
++ * d. l1_zlw_2d_mode -> To track 2D mode per stream (ZLW_2D)
++ *
++ *
++ * Currently L1/L2 streams, blocks, AT ZLW configurations etc. are pre-defined
++ * as per the usecase specific calculations. Any change to this pre-defined
++ * table has to happen in sync with IPU6 FW.
++ */
++struct ipu6_mmu_hw {
++	union {
++		unsigned long offset;
++		void __iomem *base;
++	};
++	u32 info_bits;
++	u8 nr_l1streams;
++	/*
++	 * L1 has variable blocks per stream - total of 64 blocks and maximum of
++	 * 16 blocks per stream. Configurable by using the block start address
++	 * per stream. Block start address is calculated from the block size
++	 */
++	u8 l1_block_sz[IPU6_MMU_MAX_TLB_L1_STREAMS];
++	/* Is ZLW is enabled in each stream */
++	bool l1_zlw_en[IPU6_MMU_MAX_TLB_L1_STREAMS];
++	bool l1_zlw_1d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS];
++	u8 l1_ins_zlw_ahead_pages[IPU6_MMU_MAX_TLB_L1_STREAMS];
++	bool l1_zlw_2d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS];
++
++	u32 l1_stream_id_reg_offset;
++	u32 l2_stream_id_reg_offset;
++
++	u8 nr_l2streams;
++	/*
++	 * L2 has fixed 2 blocks per stream. Block address is calculated
++	 * from the block size
++	 */
++	u8 l2_block_sz[IPU6_MMU_MAX_TLB_L2_STREAMS];
++	/* flag to track if WA is needed for successive invalidate HW bug */
++	bool insert_read_before_invalidate;
++};
++
++struct ipu6_mmu_pdata {
++	u32 nr_mmus;
++	struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES];
++	int mmid;
++};
++
++struct ipu6_isys_csi2_pdata {
++	void __iomem *base;
++};
++
++struct ipu6_isys_internal_csi2_pdata {
++	u32 nports;
++	u32 irq_mask;
++	u32 *offsets;
++	u32 ctrl0_irq_edge;
++	u32 ctrl0_irq_clear;
++	u32 ctrl0_irq_mask;
++	u32 ctrl0_irq_enable;
++	u32 ctrl0_irq_lnp;
++	u32 ctrl0_irq_status;
++	u32 fw_access_port_ofs;
++};
++
++struct ipu6_isys_internal_tpg_pdata {
++	u32 ntpgs;
++	u32 *offsets;
++	u32 *sels;
++};
++
++struct ipu6_hw_variants {
++	unsigned long offset;
++	u32 nr_mmus;
++	struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES];
++	u8 cdc_fifos;
++	u8 cdc_fifo_threshold[IPU6_MAX_VC_IOSF_PORTS];
++	u32 dmem_offset;
++	u32 spc_offset;
++};
++
++struct ipu6_isys_internal_pdata {
++	struct ipu6_isys_internal_csi2_pdata csi2;
++	struct ipu6_hw_variants hw_variant;
++	u32 num_parallel_streams;
++	u32 isys_dma_overshoot;
++	u32 sram_gran_shift;
++	u32 sram_gran_size;
++	u32 max_sram_size;
++	u32 max_streams;
++	u32 max_send_queues;
++	u32 max_sram_blocks;
++	u32 max_devq_size;
++	u32 sensor_type_start;
++	u32 sensor_type_end;
++	u32 ltr;
++	u32 memopen_threshold;
++	bool enhanced_iwake;
++};
++
++struct ipu6_isys_pdata {
++	void __iomem *base;
++	const struct ipu6_isys_internal_pdata *ipdata;
++};
++
++struct ipu6_psys_internal_pdata {
++	struct ipu6_hw_variants hw_variant;
++};
++
++struct ipu6_psys_pdata {
++	void __iomem *base;
++	const struct ipu6_psys_internal_pdata *ipdata;
++};
++
++int ipu6_fw_authenticate(void *data, u64 val);
++void ipu6_configure_spc(struct ipu6_device *isp,
++			const struct ipu6_hw_variants *hw_variant,
++			int pkg_dir_idx, void __iomem *base, u64 *pkg_dir,
++			dma_addr_t pkg_dir_dma_addr);
++#endif /* IPU6_H */
+-- 
+2.43.2
+
+
+From f52c1b80222269f99d52b0af5937995e22c9ed6d Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:16 +0800
+Subject: [PATCH 09/33] media: intel/ipu6: add IPU auxiliary devices
+
+Even the IPU input system and processing system are in a single PCI
+device, each system has its own power sequence, the processing system
+power up depends on the input system power up.
+
+Besides, input system and processing system have their own MMU
+hardware for IPU DMA address mapping.
+
+Register the IS/PS devices on auxiliary bus and attach power domain
+to implement the power sequence dependency.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-bus.c | 165 ++++++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-bus.h |  58 +++++++++
+ 2 files changed, 223 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-bus.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-bus.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.c b/drivers/media/pci/intel/ipu6/ipu6-bus.c
+new file mode 100644
+index 000000000000..e81b9a6518a1
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-bus.c
+@@ -0,0 +1,165 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/auxiliary_bus.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++#include <linux/pci.h>
++#include <linux/pm_domain.h>
++#include <linux/pm_runtime.h>
++#include <linux/slab.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-buttress.h"
++#include "ipu6-dma.h"
++
++static int bus_pm_runtime_suspend(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++	int ret;
++
++	ret = pm_generic_runtime_suspend(dev);
++	if (ret)
++		return ret;
++
++	ret = ipu6_buttress_power(dev, adev->ctrl, false);
++	if (!ret)
++		return 0;
++
++	dev_err(dev, "power down failed!\n");
++
++	/* Powering down failed, attempt to resume device now */
++	ret = pm_generic_runtime_resume(dev);
++	if (!ret)
++		return -EBUSY;
++
++	return -EIO;
++}
++
++static int bus_pm_runtime_resume(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++	int ret;
++
++	ret = ipu6_buttress_power(dev, adev->ctrl, true);
++	if (ret)
++		return ret;
++
++	ret = pm_generic_runtime_resume(dev);
++	if (ret)
++		goto out_err;
++
++	return 0;
++
++out_err:
++	ipu6_buttress_power(dev, adev->ctrl, false);
++
++	return -EBUSY;
++}
++
++static struct dev_pm_domain ipu6_bus_pm_domain = {
++	.ops = {
++		.runtime_suspend = bus_pm_runtime_suspend,
++		.runtime_resume = bus_pm_runtime_resume,
++	},
++};
++
++static DEFINE_MUTEX(ipu6_bus_mutex);
++
++static void ipu6_bus_release(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++
++	kfree(adev->pdata);
++	kfree(adev);
++}
++
++struct ipu6_bus_device *
++ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
++			   void *pdata, struct ipu6_buttress_ctrl *ctrl,
++			   char *name)
++{
++	struct auxiliary_device *auxdev;
++	struct ipu6_bus_device *adev;
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	int ret;
++
++	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
++	if (!adev)
++		return ERR_PTR(-ENOMEM);
++
++	adev->dma_mask = DMA_BIT_MASK(isp->secure_mode ? IPU6_MMU_ADDR_BITS :
++				      IPU6_MMU_ADDR_BITS_NON_SECURE);
++	adev->isp = isp;
++	adev->ctrl = ctrl;
++	adev->pdata = pdata;
++	auxdev = &adev->auxdev;
++	auxdev->name = name;
++	auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
++		      PCI_DEVID(pdev->bus->number, pdev->devfn);
++
++	auxdev->dev.parent = parent;
++	auxdev->dev.release = ipu6_bus_release;
++	auxdev->dev.dma_ops = &ipu6_dma_ops;
++	auxdev->dev.dma_mask = &adev->dma_mask;
++	auxdev->dev.dma_parms = pdev->dev.dma_parms;
++	auxdev->dev.coherent_dma_mask = adev->dma_mask;
++
++	ret = auxiliary_device_init(auxdev);
++	if (ret < 0) {
++		dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
++			ret);
++		kfree(adev);
++		return ERR_PTR(ret);
++	}
++
++	dev_pm_domain_set(&auxdev->dev, &ipu6_bus_pm_domain);
++
++	pm_runtime_forbid(&adev->auxdev.dev);
++	pm_runtime_enable(&adev->auxdev.dev);
++
++	return adev;
++}
++
++int ipu6_bus_add_device(struct ipu6_bus_device *adev)
++{
++	struct auxiliary_device *auxdev = &adev->auxdev;
++	int ret;
++
++	ret = auxiliary_device_add(auxdev);
++	if (ret) {
++		auxiliary_device_uninit(auxdev);
++		return ret;
++	}
++
++	mutex_lock(&ipu6_bus_mutex);
++	list_add(&adev->list, &adev->isp->devices);
++	mutex_unlock(&ipu6_bus_mutex);
++
++	pm_runtime_allow(&auxdev->dev);
++
++	return 0;
++}
++
++void ipu6_bus_del_devices(struct pci_dev *pdev)
++{
++	struct ipu6_device *isp = pci_get_drvdata(pdev);
++	struct ipu6_bus_device *adev, *save;
++
++	mutex_lock(&ipu6_bus_mutex);
++
++	list_for_each_entry_safe(adev, save, &isp->devices, list) {
++		pm_runtime_disable(&adev->auxdev.dev);
++		list_del(&adev->list);
++		auxiliary_device_delete(&adev->auxdev);
++		auxiliary_device_uninit(&adev->auxdev);
++	}
++
++	mutex_unlock(&ipu6_bus_mutex);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.h b/drivers/media/pci/intel/ipu6/ipu6-bus.h
+new file mode 100644
+index 000000000000..d46181354836
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-bus.h
+@@ -0,0 +1,58 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_BUS_H
++#define IPU6_BUS_H
++
++#include <linux/auxiliary_bus.h>
++#include <linux/container_of.h>
++#include <linux/device.h>
++#include <linux/irqreturn.h>
++#include <linux/list.h>
++#include <linux/scatterlist.h>
++#include <linux/types.h>
++
++struct firmware;
++struct pci_dev;
++
++#define IPU6_BUS_NAME	IPU6_NAME "-bus"
++
++struct ipu6_buttress_ctrl;
++
++struct ipu6_bus_device {
++	struct auxiliary_device auxdev;
++	struct auxiliary_driver *auxdrv;
++	const struct ipu6_auxdrv_data *auxdrv_data;
++	struct list_head list;
++	void *pdata;
++	struct ipu6_mmu *mmu;
++	struct ipu6_device *isp;
++	struct ipu6_buttress_ctrl *ctrl;
++	u64 dma_mask;
++	const struct firmware *fw;
++	struct sg_table fw_sgt;
++	u64 *pkg_dir;
++	dma_addr_t pkg_dir_dma_addr;
++	unsigned int pkg_dir_size;
++};
++
++struct ipu6_auxdrv_data {
++	irqreturn_t (*isr)(struct ipu6_bus_device *adev);
++	irqreturn_t (*isr_threaded)(struct ipu6_bus_device *adev);
++	bool wake_isr_thread;
++};
++
++#define to_ipu6_bus_device(_dev) \
++	container_of(to_auxiliary_dev(_dev), struct ipu6_bus_device, auxdev)
++#define auxdev_to_adev(_auxdev) \
++	container_of(_auxdev, struct ipu6_bus_device, auxdev)
++#define ipu6_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev)
++
++struct ipu6_bus_device *
++ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
++			   void *pdata, struct ipu6_buttress_ctrl *ctrl,
++			   char *name);
++int ipu6_bus_add_device(struct ipu6_bus_device *adev);
++void ipu6_bus_del_devices(struct pci_dev *pdev);
++
++#endif
+-- 
+2.43.2
+
+
+From a74d85716ec13ff2f55997c73c9f06367174d7a6 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:17 +0800
+Subject: [PATCH 10/33] media: intel/ipu6: add IPU6 buttress interface driver
+
+The IPU6 buttress is the interface between IPU device (input system
+and processing system) with rest of the SoC. It contains overall IPU
+hardware control registers, these control registers are used as the
+interfaces with the Intel Converged Security Engine and Punit to do
+firmware authentication and power management.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-buttress.c  | 912 ++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-buttress.h  | 102 ++
+ .../intel/ipu6/ipu6-platform-buttress-regs.h  | 232 +++++
+ 3 files changed, 1246 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-buttress.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-buttress.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c
+new file mode 100644
+index 000000000000..2f73302812f3
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c
+@@ -0,0 +1,912 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/firmware.h>
++#include <linux/interrupt.h>
++#include <linux/iopoll.h>
++#include <linux/math64.h>
++#include <linux/mm.h>
++#include <linux/mutex.h>
++#include <linux/pci.h>
++#include <linux/pfn.h>
++#include <linux/pm_runtime.h>
++#include <linux/scatterlist.h>
++#include <linux/slab.h>
++#include <linux/time64.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-buttress.h"
++#include "ipu6-platform-buttress-regs.h"
++
++#define BOOTLOADER_STATUS_OFFSET       0x15c
++
++#define BOOTLOADER_MAGIC_KEY		0xb00710ad
++
++#define ENTRY	BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1
++#define EXIT	BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2
++#define QUERY	BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE
++
++#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX	10
++
++#define BUTTRESS_POWER_TIMEOUT_US		(200 * USEC_PER_MSEC)
++
++#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US	(5 * USEC_PER_SEC)
++#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US	(10 * USEC_PER_SEC)
++#define BUTTRESS_CSE_FWRESET_TIMEOUT_US		(100 * USEC_PER_MSEC)
++
++#define BUTTRESS_IPC_TX_TIMEOUT_MS		MSEC_PER_SEC
++#define BUTTRESS_IPC_RX_TIMEOUT_MS		MSEC_PER_SEC
++#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US	(1 * USEC_PER_SEC)
++#define BUTTRESS_TSC_SYNC_TIMEOUT_US		(5 * USEC_PER_MSEC)
++
++#define BUTTRESS_IPC_RESET_RETRY		2000
++#define BUTTRESS_CSE_IPC_RESET_RETRY	4
++#define BUTTRESS_IPC_CMD_SEND_RETRY	1
++
++#define BUTTRESS_MAX_CONSECUTIVE_IRQS	100
++
++static const u32 ipu6_adev_irq_mask[2] = {
++	BUTTRESS_ISR_IS_IRQ,
++	BUTTRESS_ISR_PS_IRQ
++};
++
++int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
++			    struct ipu6_buttress_ipc *ipc)
++{
++	unsigned int retries = BUTTRESS_IPC_RESET_RETRY;
++	struct ipu6_buttress *b = &isp->buttress;
++	u32 val = 0, csr_in_clr;
++
++	if (!isp->secure_mode) {
++		dev_dbg(&isp->pdev->dev, "Skip IPC reset for non-secure mode");
++		return 0;
++	}
++
++	mutex_lock(&b->ipc_mutex);
++
++	/* Clear-by-1 CSR (all bits), corresponding internal states. */
++	val = readl(isp->base + ipc->csr_in);
++	writel(val, isp->base + ipc->csr_in);
++
++	/* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */
++	writel(ENTRY, isp->base + ipc->csr_out);
++	/*
++	 * Clear-by-1 all CSR bits EXCEPT following
++	 * bits:
++	 * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
++	 * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
++	 * C. Possibly custom bits, depending on
++	 * their role.
++	 */
++	csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ |
++		BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID |
++		BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY;
++
++	do {
++		usleep_range(400, 500);
++		val = readl(isp->base + ipc->csr_in);
++		switch (val) {
++		case ENTRY | EXIT:
++		case ENTRY | EXIT | QUERY:
++			/*
++			 * 1) Clear-by-1 CSR bits
++			 * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE2).
++			 * 2) Set peer CSR bit
++			 * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
++			 */
++			writel(ENTRY | EXIT, isp->base + ipc->csr_in);
++			writel(QUERY, isp->base + ipc->csr_out);
++			break;
++		case ENTRY:
++		case ENTRY | QUERY:
++			/*
++			 * 1) Clear-by-1 CSR bits
++			 * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
++			 * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE).
++			 * 2) Set peer CSR bit
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE1.
++			 */
++			writel(ENTRY | QUERY, isp->base + ipc->csr_in);
++			writel(ENTRY, isp->base + ipc->csr_out);
++			break;
++		case EXIT:
++		case EXIT | QUERY:
++			/*
++			 * Clear-by-1 CSR bit
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
++			 * 1) Clear incoming doorbell.
++			 * 2) Clear-by-1 all CSR bits EXCEPT following
++			 * bits:
++			 * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
++			 * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
++			 * C. Possibly custom bits, depending on
++			 * their role.
++			 * 3) Set peer CSR bit
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
++			 */
++			writel(EXIT, isp->base + ipc->csr_in);
++			writel(0, isp->base + ipc->db0_in);
++			writel(csr_in_clr, isp->base + ipc->csr_in);
++			writel(EXIT, isp->base + ipc->csr_out);
++
++			/*
++			 * Read csr_in again to make sure if RST_PHASE2 is done.
++			 * If csr_in is QUERY, it should be handled again.
++			 */
++			usleep_range(200, 300);
++			val = readl(isp->base + ipc->csr_in);
++			if (val & QUERY) {
++				dev_dbg(&isp->pdev->dev,
++					"RST_PHASE2 retry csr_in = %x\n", val);
++				break;
++			}
++			mutex_unlock(&b->ipc_mutex);
++			return 0;
++		case QUERY:
++			/*
++			 * 1) Clear-by-1 CSR bit
++			 * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
++			 * 2) Set peer CSR bit
++			 * IPC_PEER_COMP_ACTIONS_RST_PHASE1
++			 */
++			writel(QUERY, isp->base + ipc->csr_in);
++			writel(ENTRY, isp->base + ipc->csr_out);
++			break;
++		default:
++			dev_warn_ratelimited(&isp->pdev->dev,
++					     "Unexpected CSR 0x%x\n", val);
++			break;
++		}
++	} while (retries--);
++
++	mutex_unlock(&b->ipc_mutex);
++	dev_err(&isp->pdev->dev, "Timed out while waiting for CSE\n");
++
++	return -ETIMEDOUT;
++}
++
++static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp,
++					     struct ipu6_buttress_ipc *ipc)
++{
++	writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ,
++	       isp->base + ipc->csr_out);
++}
++
++static int
++ipu6_buttress_ipc_validity_open(struct ipu6_device *isp,
++				struct ipu6_buttress_ipc *ipc)
++{
++	unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID;
++	void __iomem *addr;
++	int ret;
++	u32 val;
++
++	writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ,
++	       isp->base + ipc->csr_out);
++
++	addr = isp->base + ipc->csr_in;
++	ret = readl_poll_timeout(addr, val, val & mask, 200,
++				 BUTTRESS_IPC_VALIDITY_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val);
++		ipu6_buttress_ipc_validity_close(isp, ipc);
++	}
++
++	return ret;
++}
++
++static void ipu6_buttress_ipc_recv(struct ipu6_device *isp,
++				   struct ipu6_buttress_ipc *ipc, u32 *ipc_msg)
++{
++	if (ipc_msg)
++		*ipc_msg = readl(isp->base + ipc->data0_in);
++	writel(0, isp->base + ipc->db0_in);
++}
++
++static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
++				       enum ipu6_buttress_ipc_domain ipc_domain,
++				       struct ipu6_ipc_buttress_bulk_msg *msgs,
++				       u32 size)
++{
++	unsigned long tx_timeout_jiffies, rx_timeout_jiffies;
++	unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY;
++	struct ipu6_buttress *b = &isp->buttress;
++	struct ipu6_buttress_ipc *ipc;
++	u32 val;
++	int ret;
++	int tout;
++
++	ipc = ipc_domain == IPU6_BUTTRESS_IPC_CSE ? &b->cse : &b->ish;
++
++	mutex_lock(&b->ipc_mutex);
++
++	ret = ipu6_buttress_ipc_validity_open(isp, ipc);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "IPC validity open failed\n");
++		goto out;
++	}
++
++	tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS);
++	rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS);
++
++	for (i = 0; i < size; i++) {
++		reinit_completion(&ipc->send_complete);
++		if (msgs[i].require_resp)
++			reinit_completion(&ipc->recv_complete);
++
++		dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n",
++			msgs[i].cmd);
++		writel(msgs[i].cmd, isp->base + ipc->data0_out);
++		val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size;
++		writel(val, isp->base + ipc->db0_out);
++
++		tout = wait_for_completion_timeout(&ipc->send_complete,
++						   tx_timeout_jiffies);
++		if (!tout) {
++			dev_err(&isp->pdev->dev, "send IPC response timeout\n");
++			if (!retry--) {
++				ret = -ETIMEDOUT;
++				goto out;
++			}
++
++			/* Try again if CSE is not responding on first try */
++			writel(0, isp->base + ipc->db0_out);
++			i--;
++			continue;
++		}
++
++		retry = BUTTRESS_IPC_CMD_SEND_RETRY;
++
++		if (!msgs[i].require_resp)
++			continue;
++
++		tout = wait_for_completion_timeout(&ipc->recv_complete,
++						   rx_timeout_jiffies);
++		if (!tout) {
++			dev_err(&isp->pdev->dev, "recv IPC response timeout\n");
++			ret = -ETIMEDOUT;
++			goto out;
++		}
++
++		if (ipc->nack_mask &&
++		    (ipc->recv_data & ipc->nack_mask) == ipc->nack) {
++			dev_err(&isp->pdev->dev,
++				"IPC NACK for cmd 0x%x\n", msgs[i].cmd);
++			ret = -EIO;
++			goto out;
++		}
++
++		if (ipc->recv_data != msgs[i].expected_resp) {
++			dev_err(&isp->pdev->dev,
++				"expected resp: 0x%x, IPC response: 0x%x ",
++				msgs[i].expected_resp, ipc->recv_data);
++			ret = -EIO;
++			goto out;
++		}
++	}
++
++	dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n");
++
++out:
++	ipu6_buttress_ipc_validity_close(isp, ipc);
++	mutex_unlock(&b->ipc_mutex);
++	return ret;
++}
++
++static int
++ipu6_buttress_ipc_send(struct ipu6_device *isp,
++		       enum ipu6_buttress_ipc_domain ipc_domain,
++		       u32 ipc_msg, u32 size, bool require_resp,
++		       u32 expected_resp)
++{
++	struct ipu6_ipc_buttress_bulk_msg msg = {
++		.cmd = ipc_msg,
++		.cmd_size = size,
++		.require_resp = require_resp,
++		.expected_resp = expected_resp,
++	};
++
++	return ipu6_buttress_ipc_send_bulk(isp, ipc_domain, &msg, 1);
++}
++
++static irqreturn_t ipu6_buttress_call_isr(struct ipu6_bus_device *adev)
++{
++	irqreturn_t ret = IRQ_WAKE_THREAD;
++
++	if (!adev || !adev->auxdrv || !adev->auxdrv_data)
++		return IRQ_NONE;
++
++	if (adev->auxdrv_data->isr)
++		ret = adev->auxdrv_data->isr(adev);
++
++	if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded)
++		ret = IRQ_NONE;
++
++	return ret;
++}
++
++irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
++{
++	struct ipu6_device *isp = isp_ptr;
++	struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
++	struct ipu6_buttress *b = &isp->buttress;
++	u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS;
++	irqreturn_t ret = IRQ_NONE;
++	u32 disable_irqs = 0;
++	u32 irq_status;
++	u32 i, count = 0;
++
++	pm_runtime_get_noresume(&isp->pdev->dev);
++
++	irq_status = readl(isp->base + reg_irq_sts);
++	if (!irq_status) {
++		pm_runtime_put_noidle(&isp->pdev->dev);
++		return IRQ_NONE;
++	}
++
++	do {
++		writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR);
++
++		for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) {
++			irqreturn_t r = ipu6_buttress_call_isr(adev[i]);
++
++			if (!(irq_status & ipu6_adev_irq_mask[i]))
++				continue;
++
++			if (r == IRQ_WAKE_THREAD) {
++				ret = IRQ_WAKE_THREAD;
++				disable_irqs |= ipu6_adev_irq_mask[i];
++			} else if (ret == IRQ_NONE && r == IRQ_HANDLED) {
++				ret = IRQ_HANDLED;
++			}
++		}
++
++		if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE)
++			ret = IRQ_HANDLED;
++
++		if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) {
++			dev_dbg(&isp->pdev->dev,
++				"BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n");
++			ipu6_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data);
++			complete(&b->cse.recv_complete);
++		}
++
++		if (irq_status & BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING) {
++			dev_dbg(&isp->pdev->dev,
++				"BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING\n");
++			ipu6_buttress_ipc_recv(isp, &b->ish, &b->ish.recv_data);
++			complete(&b->ish.recv_complete);
++		}
++
++		if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) {
++			dev_dbg(&isp->pdev->dev,
++				"BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
++			complete(&b->cse.send_complete);
++		}
++
++		if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH) {
++			dev_dbg(&isp->pdev->dev,
++				"BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
++			complete(&b->ish.send_complete);
++		}
++
++		if (irq_status & BUTTRESS_ISR_SAI_VIOLATION &&
++		    ipu6_buttress_get_secure_mode(isp))
++			dev_err(&isp->pdev->dev,
++				"BUTTRESS_ISR_SAI_VIOLATION\n");
++
++		if (irq_status & (BUTTRESS_ISR_IS_FATAL_MEM_ERR |
++				  BUTTRESS_ISR_PS_FATAL_MEM_ERR))
++			dev_err(&isp->pdev->dev,
++				"BUTTRESS_ISR_FATAL_MEM_ERR\n");
++
++		if (irq_status & BUTTRESS_ISR_UFI_ERROR)
++			dev_err(&isp->pdev->dev, "BUTTRESS_ISR_UFI_ERROR\n");
++
++		if (++count == BUTTRESS_MAX_CONSECUTIVE_IRQS) {
++			dev_err(&isp->pdev->dev, "too many consecutive IRQs\n");
++			ret = IRQ_NONE;
++			break;
++		}
++
++		irq_status = readl(isp->base + reg_irq_sts);
++	} while (irq_status);
++
++	if (disable_irqs)
++		writel(BUTTRESS_IRQS & ~disable_irqs,
++		       isp->base + BUTTRESS_REG_ISR_ENABLE);
++
++	pm_runtime_put(&isp->pdev->dev);
++
++	return ret;
++}
++
++irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr)
++{
++	struct ipu6_device *isp = isp_ptr;
++	struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
++	const struct ipu6_auxdrv_data *drv_data = NULL;
++	irqreturn_t ret = IRQ_NONE;
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask) && adev[i]; i++) {
++		drv_data = adev[i]->auxdrv_data;
++		if (!drv_data)
++			continue;
++
++		if (drv_data->wake_isr_thread &&
++		    drv_data->isr_threaded(adev[i]) == IRQ_HANDLED)
++			ret = IRQ_HANDLED;
++	}
++
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
++
++	return ret;
++}
++
++int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
++			bool on)
++{
++	struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
++	u32 pwr_sts, val;
++	int ret;
++
++	if (!ctrl)
++		return 0;
++
++	mutex_lock(&isp->buttress.power_mutex);
++
++	if (!on) {
++		val = 0;
++		pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift;
++	} else {
++		val = BUTTRESS_FREQ_CTL_START |
++			FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK,
++				   ctrl->ratio) |
++			FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK,
++				   ctrl->qos_floor) |
++			BUTTRESS_FREQ_CTL_ICCMAX_LEVEL;
++
++		pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift;
++	}
++
++	writel(val, isp->base + ctrl->freq_ctl);
++
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE,
++				 val, (val & ctrl->pwr_sts_mask) == pwr_sts,
++				 100, BUTTRESS_POWER_TIMEOUT_US);
++	if (ret)
++		dev_err(&isp->pdev->dev,
++			"Change power status timeout with 0x%x\n", val);
++
++	ctrl->started = !ret && on;
++
++	mutex_unlock(&isp->buttress.power_mutex);
++
++	return ret;
++}
++
++bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp)
++{
++	u32 val;
++
++	val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
++
++	return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE;
++}
++
++bool ipu6_buttress_auth_done(struct ipu6_device *isp)
++{
++	u32 val;
++
++	if (!isp->secure_mode)
++		return true;
++
++	val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
++	val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val);
++
++	return val == BUTTRESS_SECURITY_CTL_AUTH_DONE;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_auth_done, INTEL_IPU6);
++
++int ipu6_buttress_reset_authentication(struct ipu6_device *isp)
++{
++	int ret;
++	u32 val;
++
++	if (!isp->secure_mode) {
++		dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
++		return 0;
++	}
++
++	writel(BUTTRESS_FW_RESET_CTL_START, isp->base +
++	       BUTTRESS_REG_FW_RESET_CTL);
++
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val,
++				 val & BUTTRESS_FW_RESET_CTL_DONE, 500,
++				 BUTTRESS_CSE_FWRESET_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev,
++			"Time out while resetting authentication state\n");
++		return ret;
++	}
++
++	dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n");
++	writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL);
++	/* leave some time for HW restore */
++	usleep_range(800, 1000);
++
++	return 0;
++}
++
++int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
++			       const struct firmware *fw, struct sg_table *sgt)
++{
++	struct page **pages;
++	const void *addr;
++	unsigned long n_pages;
++	unsigned int i;
++	int ret;
++
++	n_pages = PHYS_PFN(PAGE_ALIGN(fw->size));
++
++	pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL);
++	if (!pages)
++		return -ENOMEM;
++
++	addr = fw->data;
++	for (i = 0; i < n_pages; i++) {
++		struct page *p = vmalloc_to_page(addr);
++
++		if (!p) {
++			ret = -ENOMEM;
++			goto out;
++		}
++		pages[i] = p;
++		addr += PAGE_SIZE;
++	}
++
++	ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size,
++					GFP_KERNEL);
++	if (ret) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	ret = dma_map_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
++	if (ret < 0) {
++		ret = -ENOMEM;
++		sg_free_table(sgt);
++		goto out;
++	}
++
++	dma_sync_sgtable_for_device(&sys->auxdev.dev, sgt, DMA_TO_DEVICE);
++
++out:
++	kfree(pages);
++
++	return ret;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, INTEL_IPU6);
++
++void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
++				  struct sg_table *sgt)
++{
++	dma_unmap_sgtable(&sys->auxdev.dev, sgt, DMA_TO_DEVICE, 0);
++	sg_free_table(sgt);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, INTEL_IPU6);
++
++int ipu6_buttress_authenticate(struct ipu6_device *isp)
++{
++	struct ipu6_buttress *b = &isp->buttress;
++	struct ipu6_psys_pdata *psys_pdata;
++	u32 data, mask, done, fail;
++	int ret;
++
++	if (!isp->secure_mode) {
++		dev_dbg(&isp->pdev->dev, "Skip auth for non-secure mode\n");
++		return 0;
++	}
++
++	psys_pdata = isp->psys->pdata;
++
++	mutex_lock(&b->auth_mutex);
++
++	if (ipu6_buttress_auth_done(isp)) {
++		ret = 0;
++		goto out_unlock;
++	}
++
++	/*
++	 * Write address of FIT table to FW_SOURCE register
++	 * Let's use fw address. I.e. not using FIT table yet
++	 */
++	data = lower_32_bits(isp->psys->pkg_dir_dma_addr);
++	writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO);
++
++	data = upper_32_bits(isp->psys->pkg_dir_dma_addr);
++	writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI);
++
++	/*
++	 * Write boot_load into IU2CSEDATA0
++	 * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
++	 * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
++	 */
++	dev_info(&isp->pdev->dev, "Sending BOOT_LOAD to CSE\n");
++
++	ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
++				     BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD,
++				     1, true,
++				     BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
++		goto out_unlock;
++	}
++
++	mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK;
++	done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE;
++	fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED;
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
++				 ((data & mask) == done ||
++				  (data & mask) == fail), 500,
++				 BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE boot_load timeout\n");
++		goto out_unlock;
++	}
++
++	if ((data & mask) == fail) {
++		dev_err(&isp->pdev->dev, "CSE auth failed\n");
++		ret = -EINVAL;
++		goto out_unlock;
++	}
++
++	ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET,
++				 data, data == BOOTLOADER_MAGIC_KEY, 500,
++				 BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n",
++			data);
++		goto out_unlock;
++	}
++
++	/*
++	 * Write authenticate_run into IU2CSEDATA0
++	 * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
++	 * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
++	 */
++	dev_info(&isp->pdev->dev, "Sending AUTHENTICATE_RUN to CSE\n");
++	ret = ipu6_buttress_ipc_send(isp, IPU6_BUTTRESS_IPC_CSE,
++				     BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN,
++				     1, true,
++				     BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE authenticate_run failed\n");
++		goto out_unlock;
++	}
++
++	done = BUTTRESS_SECURITY_CTL_AUTH_DONE;
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
++				 ((data & mask) == done ||
++				  (data & mask) == fail), 500,
++				 BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "CSE authenticate timeout\n");
++		goto out_unlock;
++	}
++
++	if ((data & mask) == fail) {
++		dev_err(&isp->pdev->dev, "CSE boot_load failed\n");
++		ret = -EINVAL;
++		goto out_unlock;
++	}
++
++	dev_info(&isp->pdev->dev, "CSE authenticate_run done\n");
++
++out_unlock:
++	mutex_unlock(&b->auth_mutex);
++
++	return ret;
++}
++
++static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp)
++{
++	u32 val, mask, done;
++	int ret;
++
++	mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK;
++
++	writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC,
++	       isp->base + BUTTRESS_REG_FABRIC_CMD);
++
++	val = readl(isp->base + BUTTRESS_REG_PWR_STATE);
++	val = FIELD_GET(mask, val);
++	if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) {
++		dev_err(&isp->pdev->dev, "Start tsc sync failed\n");
++		return -EINVAL;
++	}
++
++	done = BUTTRESS_PWR_STATE_HH_STATE_DONE;
++	ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val,
++				 FIELD_GET(mask, val) == done, 500,
++				 BUTTRESS_TSC_SYNC_TIMEOUT_US);
++	if (ret)
++		dev_err(&isp->pdev->dev, "Start tsc sync timeout\n");
++
++	return ret;
++}
++
++int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp)
++{
++	unsigned int i;
++
++	for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
++		u32 val;
++		int ret;
++
++		ret = ipu6_buttress_send_tsc_request(isp);
++		if (ret != -ETIMEDOUT)
++			return ret;
++
++		val = readl(isp->base + BUTTRESS_REG_TSW_CTL);
++		val = val | BUTTRESS_TSW_CTL_SOFT_RESET;
++		writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
++		val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET;
++		writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
++	}
++
++	dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n");
++
++	return -ETIMEDOUT;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, INTEL_IPU6);
++
++void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val)
++{
++	u32 tsc_hi_1, tsc_hi_2, tsc_lo;
++	unsigned long flags;
++
++	local_irq_save(flags);
++	tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI);
++	tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO);
++	tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI);
++	if (tsc_hi_1 == tsc_hi_2) {
++		*val = (u64)tsc_hi_1 << 32 | tsc_lo;
++	} else {
++		/* Check if TSC has rolled over */
++		if (tsc_lo & BIT(31))
++			*val = (u64)tsc_hi_1 << 32 | tsc_lo;
++		else
++			*val = (u64)tsc_hi_2 << 32 | tsc_lo;
++	}
++	local_irq_restore(flags);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_read, INTEL_IPU6);
++
++u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp)
++{
++	u64 ns = ticks * 10000;
++
++	/*
++	 * converting TSC tick count to ns is calculated by:
++	 * Example (TSC clock frequency is 19.2MHz):
++	 * ns = ticks * 1000 000 000 / 19.2Mhz
++	 *    = ticks * 1000 000 000 / 19200000Hz
++	 *    = ticks * 10000 / 192 ns
++	 */
++	return div_u64(ns, isp->buttress.ref_clk);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, INTEL_IPU6);
++
++void ipu6_buttress_restore(struct ipu6_device *isp)
++{
++	struct ipu6_buttress *b = &isp->buttress;
++
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
++	writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT);
++}
++
++int ipu6_buttress_init(struct ipu6_device *isp)
++{
++	int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY;
++	struct ipu6_buttress *b = &isp->buttress;
++	u32 val;
++
++	mutex_init(&b->power_mutex);
++	mutex_init(&b->auth_mutex);
++	mutex_init(&b->cons_mutex);
++	mutex_init(&b->ipc_mutex);
++	init_completion(&b->ish.send_complete);
++	init_completion(&b->cse.send_complete);
++	init_completion(&b->ish.recv_complete);
++	init_completion(&b->cse.recv_complete);
++
++	b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK;
++	b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK;
++	b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR;
++	b->cse.csr_out = BUTTRESS_REG_IU2CSECSR;
++	b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0;
++	b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0;
++	b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0;
++	b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0;
++
++	/* no ISH on IPU6 */
++	memset(&b->ish, 0, sizeof(b->ish));
++	INIT_LIST_HEAD(&b->constraints);
++
++	isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
++	dev_info(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n",
++		 isp->secure_mode ? "secure" : "non-secure",
++		 readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH),
++		 readl(isp->base + BUTTRESS_REG_CAMERA_MASK));
++
++	b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT);
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
++	writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
++
++	/* get ref_clk frequency by reading the indication in btrs control */
++	val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
++	val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val);
++
++	switch (val) {
++	case 0x0:
++		b->ref_clk = 240;
++		break;
++	case 0x1:
++		b->ref_clk = 192;
++		break;
++	case 0x2:
++		b->ref_clk = 384;
++		break;
++	default:
++		dev_warn(&isp->pdev->dev,
++			 "Unsupported ref clock, use 19.2Mhz by default.\n");
++		b->ref_clk = 192;
++		break;
++	}
++
++	/* Retry couple of times in case of CSE initialization is delayed */
++	do {
++		ret = ipu6_buttress_ipc_reset(isp, &b->cse);
++		if (ret) {
++			dev_warn(&isp->pdev->dev,
++				 "IPC reset protocol failed, retrying\n");
++		} else {
++			dev_dbg(&isp->pdev->dev, "IPC reset done\n");
++			return 0;
++		}
++	} while (ipc_reset_retry--);
++
++	dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
++
++	mutex_destroy(&b->power_mutex);
++	mutex_destroy(&b->auth_mutex);
++	mutex_destroy(&b->cons_mutex);
++	mutex_destroy(&b->ipc_mutex);
++
++	return ret;
++}
++
++void ipu6_buttress_exit(struct ipu6_device *isp)
++{
++	struct ipu6_buttress *b = &isp->buttress;
++
++	writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE);
++
++	mutex_destroy(&b->power_mutex);
++	mutex_destroy(&b->auth_mutex);
++	mutex_destroy(&b->cons_mutex);
++	mutex_destroy(&b->ipc_mutex);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.h b/drivers/media/pci/intel/ipu6/ipu6-buttress.h
+new file mode 100644
+index 000000000000..558e1d70f4af
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.h
+@@ -0,0 +1,102 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_BUTTRESS_H
++#define IPU6_BUTTRESS_H
++
++#include <linux/completion.h>
++#include <linux/irqreturn.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++
++struct device;
++struct firmware;
++struct ipu6_device;
++struct ipu6_bus_device;
++
++#define BUTTRESS_PS_FREQ_STEP		25U
++#define BUTTRESS_MIN_FORCE_PS_FREQ	(BUTTRESS_PS_FREQ_STEP * 8)
++#define BUTTRESS_MAX_FORCE_PS_FREQ	(BUTTRESS_PS_FREQ_STEP * 32)
++
++#define BUTTRESS_IS_FREQ_STEP		25U
++#define BUTTRESS_MIN_FORCE_IS_FREQ	(BUTTRESS_IS_FREQ_STEP * 8)
++#define BUTTRESS_MAX_FORCE_IS_FREQ	(BUTTRESS_IS_FREQ_STEP * 22)
++
++struct ipu6_buttress_ctrl {
++	u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off;
++	unsigned int ratio;
++	unsigned int qos_floor;
++	bool started;
++};
++
++struct ipu6_buttress_ipc {
++	struct completion send_complete;
++	struct completion recv_complete;
++	u32 nack;
++	u32 nack_mask;
++	u32 recv_data;
++	u32 csr_out;
++	u32 csr_in;
++	u32 db0_in;
++	u32 db0_out;
++	u32 data0_out;
++	u32 data0_in;
++};
++
++struct ipu6_buttress {
++	struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex;
++	struct ipu6_buttress_ipc cse;
++	struct ipu6_buttress_ipc ish;
++	struct list_head constraints;
++	u32 wdt_cached_value;
++	bool force_suspend;
++	u32 ref_clk;
++};
++
++struct ipu6_buttress_sensor_clk_freq {
++	unsigned int rate;
++	unsigned int val;
++};
++
++enum ipu6_buttress_ipc_domain {
++	IPU6_BUTTRESS_IPC_CSE,
++	IPU6_BUTTRESS_IPC_ISH,
++};
++
++struct ipu6_buttress_constraint {
++	struct list_head list;
++	unsigned int min_freq;
++};
++
++struct ipu6_ipc_buttress_bulk_msg {
++	u32 cmd;
++	u32 expected_resp;
++	bool require_resp;
++	u8 cmd_size;
++};
++
++int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
++			    struct ipu6_buttress_ipc *ipc);
++int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
++			       const struct firmware *fw,
++			       struct sg_table *sgt);
++void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
++				  struct sg_table *sgt);
++int ipu6_buttress_power(struct device *dev, struct ipu6_buttress_ctrl *ctrl,
++			bool on);
++bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp);
++int ipu6_buttress_authenticate(struct ipu6_device *isp);
++int ipu6_buttress_reset_authentication(struct ipu6_device *isp);
++bool ipu6_buttress_auth_done(struct ipu6_device *isp);
++int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp);
++void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val);
++u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp);
++
++irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr);
++irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr);
++int ipu6_buttress_init(struct ipu6_device *isp);
++void ipu6_buttress_exit(struct ipu6_device *isp);
++void ipu6_buttress_csi_port_config(struct ipu6_device *isp,
++				   u32 legacy, u32 combo);
++void ipu6_buttress_restore(struct ipu6_device *isp);
++#endif /* IPU6_BUTTRESS_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
+new file mode 100644
+index 000000000000..87239af96502
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
+@@ -0,0 +1,232 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2023 Intel Corporation */
++
++#ifndef IPU6_PLATFORM_BUTTRESS_REGS_H
++#define IPU6_PLATFORM_BUTTRESS_REGS_H
++
++#include <linux/bits.h>
++
++/* IS_WORKPOINT_REQ */
++#define IPU6_BUTTRESS_REG_IS_FREQ_CTL		0x34
++/* PS_WORKPOINT_REQ */
++#define IPU6_BUTTRESS_REG_PS_FREQ_CTL		0x38
++
++#define IPU6_IS_FREQ_MAX		533
++#define IPU6_IS_FREQ_MIN		200
++#define IPU6_PS_FREQ_MAX		450
++#define IPU6_IS_FREQ_RATIO_BASE		25
++#define IPU6_PS_FREQ_RATIO_BASE		25
++
++/* should be tuned for real silicon */
++#define IPU6_IS_FREQ_CTL_DEFAULT_RATIO		0x08
++#define IPU6SE_IS_FREQ_CTL_DEFAULT_RATIO	0x0a
++#define IPU6_PS_FREQ_CTL_DEFAULT_RATIO		0x0d
++
++#define IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO	0x10
++#define IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO	0x0708
++
++#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_SHIFT	3
++#define IPU6_BUTTRESS_PWR_STATE_IS_PWR_MASK	GENMASK(4, 3)
++
++#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_SHIFT	6
++#define IPU6_BUTTRESS_PWR_STATE_PS_PWR_MASK	GENMASK(7, 6)
++
++#define IPU6_BUTTRESS_PWR_STATE_DN_DONE		0x0
++#define IPU6_BUTTRESS_PWR_STATE_UP_PROCESS	0x1
++#define IPU6_BUTTRESS_PWR_STATE_DN_PROCESS	0x2
++#define IPU6_BUTTRESS_PWR_STATE_UP_DONE		0x3
++
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_0	0x270
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_1	0x274
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_2	0x278
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_3	0x27c
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_4	0x280
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_5	0x284
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_6	0x288
++#define IPU6_BUTTRESS_REG_FPGA_SUPPORT_7	0x28c
++
++#define BUTTRESS_REG_WDT			0x8
++#define BUTTRESS_REG_BTRS_CTRL			0xc
++#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0	BIT(0)
++#define BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC1	BIT(1)
++#define BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND	GENMASK(9, 8)
++
++#define BUTTRESS_REG_FW_RESET_CTL	0x30
++#define BUTTRESS_FW_RESET_CTL_START	BIT(0)
++#define BUTTRESS_FW_RESET_CTL_DONE	BIT(1)
++
++#define BUTTRESS_REG_IS_FREQ_CTL	0x34
++#define BUTTRESS_REG_PS_FREQ_CTL	0x38
++
++#define BUTTRESS_FREQ_CTL_START		BIT(31)
++#define BUTTRESS_FREQ_CTL_ICCMAX_LEVEL		GENMASK(19, 16)
++#define BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK	GENMASK(15, 8)
++#define BUTTRESS_FREQ_CTL_RATIO_MASK	GENMASK(7, 0)
++
++#define BUTTRESS_REG_PWR_STATE	0x5c
++
++#define BUTTRESS_PWR_STATE_RESET		0x0
++#define BUTTRESS_PWR_STATE_PWR_ON_DONE		0x1
++#define BUTTRESS_PWR_STATE_PWR_RDY		0x3
++#define BUTTRESS_PWR_STATE_PWR_IDLE		0x4
++
++#define BUTTRESS_PWR_STATE_HH_STATUS_MASK	GENMASK(12, 11)
++
++enum {
++	BUTTRESS_PWR_STATE_HH_STATE_IDLE,
++	BUTTRESS_PWR_STATE_HH_STATE_IN_PRGS,
++	BUTTRESS_PWR_STATE_HH_STATE_DONE,
++	BUTTRESS_PWR_STATE_HH_STATE_ERR,
++};
++
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_MASK	GENMASK(23, 19)
++
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IDLE			0x0
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PLL_CMP		0x1
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK		0x2
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PG_ACK		0x3
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_ASSRT_CYCLES		0x4
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES1		0x5
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_STOP_CLK_CYCLES2		0x6
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DEASSRT_CYCLES	0x7
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_FUSE_WR_CMP	0x8
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_BRK_POINT			0x9
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_IS_RDY			0xa
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_HALT_HALTED		0xb
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_RST_DURATION_CNT3		0xc
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_CLKACK_PD		0xd
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_PD_BRK_POINT		0xe
++#define BUTTRESS_PWR_STATE_IS_PWR_FSM_WAIT_4_PD_PG_ACK0		0xf
++
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_MASK	GENMASK(28, 24)
++
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_IDLE			0x0
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_IP_RDY	0x1
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_PRE_CNT_EXH	0x2
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_VGI_PWRGOOD	0x3
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_RO_POST_CNT_EXH	0x4
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WR_PLL_RATIO		0x5
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_PLL_CMP		0x6
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PU_CLKACK		0x7
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_ASSRT_CYCLES		0x8
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES1		0x9
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_STOP_CLK_CYCLES2		0xa
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RST_DEASSRT_CYCLES	0xb
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PU_BRK_PNT		0xc
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_FUSE_ACCPT		0xd
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_PS_PWR_UP			0xf
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_4_HALTED		0x10
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_RESET_CNT3		0x11
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_CLKACK		0x12
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_PD_OFF_IND		0x13
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PH4		0x14
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_PLL_CMP		0x15
++#define BUTTRESS_PWR_STATE_PS_PWR_FSM_WAIT_DVFS_CLKACK		0x16
++
++#define BUTTRESS_REG_SECURITY_CTL	0x300
++#define BUTTRESS_REG_SKU		0x314
++#define BUTTRESS_REG_SECURITY_TOUCH	0x318
++#define BUTTRESS_REG_CAMERA_MASK	0x84
++
++#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE	BIT(16)
++#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK	GENMASK(4, 0)
++
++#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE		BIT(0)
++#define BUTTRESS_SECURITY_CTL_AUTH_DONE			BIT(1)
++#define BUTTRESS_SECURITY_CTL_AUTH_FAILED		BIT(3)
++
++#define BUTTRESS_REG_FW_SOURCE_BASE_LO	0x78
++#define BUTTRESS_REG_FW_SOURCE_BASE_HI	0x7C
++#define BUTTRESS_REG_FW_SOURCE_SIZE	0x80
++
++#define BUTTRESS_REG_ISR_STATUS		0x90
++#define BUTTRESS_REG_ISR_ENABLED_STATUS	0x94
++#define BUTTRESS_REG_ISR_ENABLE		0x98
++#define BUTTRESS_REG_ISR_CLEAR		0x9C
++
++#define BUTTRESS_ISR_IS_IRQ			BIT(0)
++#define BUTTRESS_ISR_PS_IRQ			BIT(1)
++#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE	BIT(2)
++#define BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH	BIT(3)
++#define BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING	BIT(4)
++#define BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING	BIT(5)
++#define BUTTRESS_ISR_CSE_CSR_SET		BIT(6)
++#define BUTTRESS_ISR_ISH_CSR_SET		BIT(7)
++#define BUTTRESS_ISR_SPURIOUS_CMP		BIT(8)
++#define BUTTRESS_ISR_WATCHDOG_EXPIRED		BIT(9)
++#define BUTTRESS_ISR_PUNIT_2_IUNIT_IRQ		BIT(10)
++#define BUTTRESS_ISR_SAI_VIOLATION		BIT(11)
++#define BUTTRESS_ISR_HW_ASSERTION		BIT(12)
++#define BUTTRESS_ISR_IS_CORRECTABLE_MEM_ERR	BIT(13)
++#define BUTTRESS_ISR_IS_FATAL_MEM_ERR		BIT(14)
++#define BUTTRESS_ISR_IS_NON_FATAL_MEM_ERR	BIT(15)
++#define BUTTRESS_ISR_PS_CORRECTABLE_MEM_ERR	BIT(16)
++#define BUTTRESS_ISR_PS_FATAL_MEM_ERR		BIT(17)
++#define BUTTRESS_ISR_PS_NON_FATAL_MEM_ERR	BIT(18)
++#define BUTTRESS_ISR_PS_FAST_THROTTLE		BIT(19)
++#define BUTTRESS_ISR_UFI_ERROR			BIT(20)
++
++#define BUTTRESS_REG_IU2CSEDB0	0x100
++
++#define BUTTRESS_IU2CSEDB0_BUSY		BIT(31)
++#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL	2
++
++#define BUTTRESS_REG_IU2CSEDATA0	0x104
++
++#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD		1
++#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN		2
++#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE		3
++#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH	16
++
++#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE			BIT(0)
++#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE			BIT(1)
++#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE		BIT(2)
++#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE	BIT(4)
++
++#define BUTTRESS_REG_IU2CSECSR		0x108
++
++#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1		BIT(0)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2		BIT(1)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE	BIT(2)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ		BIT(3)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID			BIT(4)
++#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ		BIT(5)
++
++#define BUTTRESS_REG_CSE2IUDB0		0x304
++#define BUTTRESS_REG_CSE2IUCSR		0x30C
++#define BUTTRESS_REG_CSE2IUDATA0	0x308
++
++/* 0x20 == NACK, 0xf == unknown command */
++#define BUTTRESS_CSE2IUDATA0_IPC_NACK      0xf20
++#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK GENMASK(15, 0)
++
++#define BUTTRESS_REG_ISH2IUCSR		0x50
++#define BUTTRESS_REG_ISH2IUDB0		0x54
++#define BUTTRESS_REG_ISH2IUDATA0	0x58
++
++#define BUTTRESS_REG_IU2ISHDB0		0x10C
++#define BUTTRESS_REG_IU2ISHDATA0	0x110
++#define BUTTRESS_REG_IU2ISHDATA1	0x114
++#define BUTTRESS_REG_IU2ISHCSR		0x118
++
++#define BUTTRESS_REG_FABRIC_CMD		0x88
++
++#define BUTTRESS_FABRIC_CMD_START_TSC_SYNC	BIT(0)
++#define BUTTRESS_FABRIC_CMD_IS_DRAIN		BIT(4)
++
++#define BUTTRESS_REG_TSW_CTL		0x120
++#define BUTTRESS_TSW_CTL_SOFT_RESET	BIT(8)
++
++#define BUTTRESS_REG_TSC_LO	0x164
++#define BUTTRESS_REG_TSC_HI	0x168
++
++#define BUTTRESS_IRQS		(BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
++				 BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE |    \
++				 BUTTRESS_ISR_IS_IRQ | BUTTRESS_ISR_PS_IRQ)
++
++#define BUTTRESS_EVENT		 (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
++				  BUTTRESS_ISR_IPC_FROM_ISH_IS_WAITING | \
++				  BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE |    \
++				  BUTTRESS_ISR_IPC_EXEC_DONE_BY_ISH |    \
++				  BUTTRESS_ISR_SAI_VIOLATION)
++#endif /* IPU6_PLATFORM_BUTTRESS_REGS_H */
+-- 
+2.43.2
+
+
+From 12bd5bdd53a7c829cc5cd61a6887f89ffb036f8f Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:18 +0800
+Subject: [PATCH 11/33] media: intel/ipu6: CPD parsing for get firmware
+ components
+
+For IPU6, firmware is generated and released as signed
+Code Partition Directory (CPD) format file, which is aligned with
+the SPI flash code partition definition. CPD format include CPD
+header, manifest, metadata and module data. Driver can parse them
+according to the CPD layout to acquire each component.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-cpd.c | 362 ++++++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-cpd.h | 105 +++++++
+ 2 files changed, 467 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-cpd.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-cpd.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.c b/drivers/media/pci/intel/ipu6/ipu6-cpd.c
+new file mode 100644
+index 000000000000..b0ffd04c4cd3
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.c
+@@ -0,0 +1,362 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/err.h>
++#include <linux/dma-mapping.h>
++#include <linux/gfp_types.h>
++#include <linux/math64.h>
++#include <linux/sizes.h>
++#include <linux/types.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-cpd.h"
++
++/* 15 entries + header*/
++#define MAX_PKG_DIR_ENT_CNT		16
++/* 2 qword per entry/header */
++#define PKG_DIR_ENT_LEN			2
++/* PKG_DIR size in bytes */
++#define PKG_DIR_SIZE			((MAX_PKG_DIR_ENT_CNT) *	\
++					 (PKG_DIR_ENT_LEN) * sizeof(u64))
++/* _IUPKDR_ */
++#define PKG_DIR_HDR_MARK		0x5f4955504b44525fULL
++
++/* $CPD */
++#define CPD_HDR_MARK			0x44504324
++
++#define MAX_MANIFEST_SIZE		(SZ_2K * sizeof(u32))
++#define MAX_METADATA_SIZE		SZ_64K
++
++#define MAX_COMPONENT_ID		127
++#define MAX_COMPONENT_VERSION		0xffff
++
++#define MANIFEST_IDX	0
++#define METADATA_IDX	1
++#define MODULEDATA_IDX	2
++/*
++ * PKG_DIR Entry (type == id)
++ * 63:56        55      54:48   47:32   31:24   23:0
++ * Rsvd         Rsvd    Type    Version Rsvd    Size
++ */
++#define PKG_DIR_SIZE_MASK	GENMASK(23, 0)
++#define PKG_DIR_VERSION_MASK	GENMASK(47, 32)
++#define PKG_DIR_TYPE_MASK	GENMASK(54, 48)
++
++static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd,
++							    u8 idx)
++{
++	const struct ipu6_cpd_hdr *cpd_hdr = cpd;
++	const struct ipu6_cpd_ent *ent;
++
++	ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len);
++	return ent + idx;
++}
++
++#define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX)
++#define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX)
++#define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX)
++
++static const struct ipu6_cpd_metadata_cmpnt_hdr *
++ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata,
++			    unsigned int metadata_size, u8 idx)
++{
++	size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn);
++	size_t cmpnt_count = metadata_size - extn_size;
++
++	cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size);
++
++	if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) {
++		dev_err(&isp->pdev->dev, "Component index out of range (%d)\n",
++			idx);
++		return ERR_PTR(-EINVAL);
++	}
++
++	return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size;
++}
++
++static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp,
++					   const void *metadata,
++					   unsigned int metadata_size, u8 idx)
++{
++	const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
++
++	cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
++	if (IS_ERR(cmpnt))
++		return PTR_ERR(cmpnt);
++
++	return cmpnt->ver;
++}
++
++static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp,
++					  const void *metadata,
++					  unsigned int metadata_size, u8 idx)
++{
++	const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
++
++	cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
++	if (IS_ERR(cmpnt))
++		return PTR_ERR(cmpnt);
++
++	return cmpnt->id;
++}
++
++static int ipu6_cpd_parse_module_data(struct ipu6_device *isp,
++				      const void *module_data,
++				      unsigned int module_data_size,
++				      dma_addr_t dma_addr_module_data,
++				      u64 *pkg_dir, const void *metadata,
++				      unsigned int metadata_size)
++{
++	const struct ipu6_cpd_module_data_hdr *module_data_hdr;
++	const struct ipu6_cpd_hdr *dir_hdr;
++	const struct ipu6_cpd_ent *dir_ent;
++	unsigned int i;
++	u8 len;
++
++	if (!module_data)
++		return -EINVAL;
++
++	module_data_hdr = module_data;
++	dir_hdr = module_data + module_data_hdr->hdr_len;
++	len = dir_hdr->hdr_len;
++	dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len);
++
++	pkg_dir[0] = PKG_DIR_HDR_MARK;
++	/* pkg_dir entry count = component count + pkg_dir header */
++	pkg_dir[1] = dir_hdr->ent_cnt + 1;
++
++	for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) {
++		u64 *p = &pkg_dir[PKG_DIR_ENT_LEN *  (1 + i)];
++		int ver, id;
++
++		*p++ = dma_addr_module_data + dir_ent->offset;
++		id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata,
++						    metadata_size, i);
++		if (id < 0 || id > MAX_COMPONENT_ID) {
++			dev_err(&isp->pdev->dev, "Invalid CPD component id\n");
++			return -EINVAL;
++		}
++
++		ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata,
++						      metadata_size, i);
++		if (ver < 0 || ver > MAX_COMPONENT_VERSION) {
++			dev_err(&isp->pdev->dev,
++				"Invalid CPD component version\n");
++			return -EINVAL;
++		}
++
++		*p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) |
++			FIELD_PREP(PKG_DIR_TYPE_MASK, id) |
++			FIELD_PREP(PKG_DIR_VERSION_MASK, ver);
++	}
++
++	return 0;
++}
++
++int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src)
++{
++	dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl);
++	const struct ipu6_cpd_ent *ent, *man_ent, *met_ent;
++	struct device *dev = &adev->auxdev.dev;
++	struct ipu6_device *isp = adev->isp;
++	unsigned int man_sz, met_sz;
++	void *pkg_dir_pos;
++	int ret;
++
++	man_ent = ipu6_cpd_get_manifest(src);
++	man_sz = man_ent->len;
++
++	met_ent = ipu6_cpd_get_metadata(src);
++	met_sz = met_ent->len;
++
++	adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz;
++	adev->pkg_dir = dma_alloc_attrs(dev, adev->pkg_dir_size,
++					&adev->pkg_dir_dma_addr, GFP_KERNEL, 0);
++	if (!adev->pkg_dir)
++		return -ENOMEM;
++
++	/*
++	 * pkg_dir entry/header:
++	 * qword | 63:56 | 55   | 54:48 | 47:32 | 31:24 | 23:0
++	 * N         Address/Offset/"_IUPKDR_"
++	 * N + 1 | rsvd  | rsvd | type  | ver   | rsvd  | size
++	 *
++	 * We can ignore other fields that size in N + 1 qword as they
++	 * are 0 anyway. Just setting size for now.
++	 */
++
++	ent = ipu6_cpd_get_moduledata(src);
++
++	ret = ipu6_cpd_parse_module_data(isp, src + ent->offset,
++					 ent->len, dma_addr_src + ent->offset,
++					 adev->pkg_dir, src + met_ent->offset,
++					 met_ent->len);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Failed to parse module data\n");
++		dma_free_attrs(dev, adev->pkg_dir_size,
++			       adev->pkg_dir, adev->pkg_dir_dma_addr, 0);
++		return ret;
++	}
++
++	/* Copy manifest after pkg_dir */
++	pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT;
++	memcpy(pkg_dir_pos, src + man_ent->offset, man_sz);
++
++	/* Copy metadata after manifest */
++	pkg_dir_pos += man_sz;
++	memcpy(pkg_dir_pos, src + met_ent->offset, met_sz);
++
++	dma_sync_single_range_for_device(dev, adev->pkg_dir_dma_addr,
++					 0, adev->pkg_dir_size, DMA_TO_DEVICE);
++
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6);
++
++void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev)
++{
++	dma_free_attrs(&adev->auxdev.dev, adev->pkg_dir_size, adev->pkg_dir,
++		       adev->pkg_dir_dma_addr, 0);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6);
++
++static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd,
++				 unsigned long cpd_size,
++				 unsigned long data_size)
++{
++	const struct ipu6_cpd_hdr *cpd_hdr = cpd;
++	const struct ipu6_cpd_ent *ent;
++	unsigned int i;
++	u8 len;
++
++	len = cpd_hdr->hdr_len;
++
++	/* Ensure cpd hdr is within moduledata */
++	if (cpd_size < len) {
++		dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
++		return -EINVAL;
++	}
++
++	/* Sanity check for CPD header */
++	if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {
++		dev_err(&isp->pdev->dev, "Invalid CPD header\n");
++		return -EINVAL;
++	}
++
++	/* Ensure that all entries are within moduledata */
++	ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len);
++	for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) {
++		if (data_size < ent->offset ||
++		    data_size - ent->offset < ent->len) {
++			dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i);
++			return -EINVAL;
++		}
++	}
++
++	return 0;
++}
++
++static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,
++					const void *moduledata,
++					u32 moduledata_size)
++{
++	const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata;
++	int ret;
++
++	/* Ensure moduledata hdr is within moduledata */
++	if (moduledata_size < sizeof(*mod_hdr) ||
++	    moduledata_size < mod_hdr->hdr_len) {
++		dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");
++		return -EINVAL;
++	}
++
++	dev_info(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date);
++	ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len,
++				    moduledata_size - mod_hdr->hdr_len,
++				    moduledata_size);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n");
++		return ret;
++	}
++
++	return 0;
++}
++
++static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,
++				      const void *metadata, u32 meta_size)
++{
++	const struct ipu6_cpd_metadata_extn *extn = metadata;
++
++	/* Sanity check for metadata size */
++	if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) {
++		dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
++		return -EINVAL;
++	}
++
++	/* Validate extension and image types */
++	if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT ||
++	    extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) {
++		dev_err(&isp->pdev->dev,
++			"Invalid CPD metadata descriptor img_type (%d)\n",
++			extn->img_type);
++		return -EINVAL;
++	}
++
++	/* Validate metadata size multiple of metadata components */
++	if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) {
++		dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n");
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
++			       unsigned long cpd_file_size)
++{
++	const struct ipu6_cpd_hdr *hdr = cpd_file;
++	const struct ipu6_cpd_ent *ent;
++	int ret;
++
++	ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size,
++				    cpd_file_size);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Invalid CPD in file\n");
++		return ret;
++	}
++
++	/* Check for CPD file marker */
++	if (hdr->hdr_mark != CPD_HDR_MARK) {
++		dev_err(&isp->pdev->dev, "Invalid CPD header\n");
++		return -EINVAL;
++	}
++
++	/* Sanity check for manifest size */
++	ent = ipu6_cpd_get_manifest(cpd_file);
++	if (ent->len > MAX_MANIFEST_SIZE) {
++		dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n");
++		return -EINVAL;
++	}
++
++	/* Validate metadata */
++	ent = ipu6_cpd_get_metadata(cpd_file);
++	ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len);
++	if (ret) {
++		dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
++		return ret;
++	}
++
++	/* Validate moduledata */
++	ent = ipu6_cpd_get_moduledata(cpd_file);
++	ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset,
++					   ent->len);
++	if (ret)
++		dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n");
++
++	return ret;
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.h b/drivers/media/pci/intel/ipu6/ipu6-cpd.h
+new file mode 100644
+index 000000000000..37465d507386
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.h
+@@ -0,0 +1,105 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2015 - 2023 Intel Corporation */
++
++#ifndef IPU6_CPD_H
++#define IPU6_CPD_H
++
++struct ipu6_device;
++struct ipu6_bus_device;
++
++#define IPU6_CPD_SIZE_OF_FW_ARCH_VERSION	7
++#define IPU6_CPD_SIZE_OF_SYSTEM_VERSION		11
++#define IPU6_CPD_SIZE_OF_COMPONENT_NAME		12
++
++#define IPU6_CPD_METADATA_EXTN_TYPE_IUNIT	0x10
++
++#define IPU6_CPD_METADATA_IMAGE_TYPE_RESERVED		0
++#define IPU6_CPD_METADATA_IMAGE_TYPE_BOOTLOADER		1
++#define IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE	2
++
++#define IPU6_CPD_PKG_DIR_PSYS_SERVER_IDX	0
++#define IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX	1
++
++#define IPU6_CPD_PKG_DIR_CLIENT_PG_TYPE		3
++
++#define IPU6_CPD_METADATA_HASH_KEY_SIZE		48
++#define IPU6SE_CPD_METADATA_HASH_KEY_SIZE	32
++
++struct ipu6_cpd_module_data_hdr {
++	u32 hdr_len;
++	u32 endian;
++	u32 fw_pkg_date;
++	u32 hive_sdk_date;
++	u32 compiler_date;
++	u32 target_platform_type;
++	u8 sys_ver[IPU6_CPD_SIZE_OF_SYSTEM_VERSION];
++	u8 fw_arch_ver[IPU6_CPD_SIZE_OF_FW_ARCH_VERSION];
++	u8 rsvd[2];
++} __packed;
++
++/*
++ * ipu6_cpd_hdr structure updated as the chksum and
++ * sub_partition_name is unused on host side
++ * CSE layout version 1.6 for IPU6SE (hdr_len = 0x10)
++ * CSE layout version 1.7 for IPU6 (hdr_len = 0x14)
++ */
++struct ipu6_cpd_hdr {
++	u32 hdr_mark;
++	u32 ent_cnt;
++	u8 hdr_ver;
++	u8 ent_ver;
++	u8 hdr_len;
++} __packed;
++
++struct ipu6_cpd_ent {
++	u8 name[IPU6_CPD_SIZE_OF_COMPONENT_NAME];
++	u32 offset;
++	u32 len;
++	u8 rsvd[4];
++} __packed;
++
++struct ipu6_cpd_metadata_cmpnt_hdr {
++	u32 id;
++	u32 size;
++	u32 ver;
++} __packed;
++
++struct ipu6_cpd_metadata_cmpnt {
++	struct ipu6_cpd_metadata_cmpnt_hdr hdr;
++	u8 sha2_hash[IPU6_CPD_METADATA_HASH_KEY_SIZE];
++	u32 entry_point;
++	u32 icache_base_offs;
++	u8 attrs[16];
++} __packed;
++
++struct ipu6se_cpd_metadata_cmpnt {
++	struct ipu6_cpd_metadata_cmpnt_hdr hdr;
++	u8 sha2_hash[IPU6SE_CPD_METADATA_HASH_KEY_SIZE];
++	u32 entry_point;
++	u32 icache_base_offs;
++	u8 attrs[16];
++} __packed;
++
++struct ipu6_cpd_metadata_extn {
++	u32 extn_type;
++	u32 len;
++	u32 img_type;
++	u8 rsvd[16];
++} __packed;
++
++struct ipu6_cpd_client_pkg_hdr {
++	u32 prog_list_offs;
++	u32 prog_list_size;
++	u32 prog_desc_offs;
++	u32 prog_desc_size;
++	u32 pg_manifest_offs;
++	u32 pg_manifest_size;
++	u32 prog_bin_offs;
++	u32 prog_bin_size;
++} __packed;
++
++int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src);
++void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev);
++int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
++			       unsigned long cpd_file_size);
++#endif /* IPU6_CPD_H */
+-- 
+2.43.2
+
+
+From 86b4cd540a9cb10de7a521882495f5eba6cc919f Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:19 +0800
+Subject: [PATCH 12/33] media: intel/ipu6: add IPU6 DMA mapping API and MMU
+ table
+
+he Intel IPU6 has an internal microcontroller (scalar processor, SP) which
+is used to execute the firmware. The SP can access IPU internal memory and
+map system DRAM to its an internal 32-bit virtual address space.
+
+This patch adds a driver for the IPU MMU and a DMA mapping implementation
+using the internal MMU. The system IOMMU may be used besides the IPU MMU.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-dma.c | 502 ++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-dma.h |  19 +
+ drivers/media/pci/intel/ipu6/ipu6-mmu.c | 845 ++++++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-mmu.h |  73 ++
+ 4 files changed, 1439 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-dma.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-dma.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-mmu.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-mmu.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.c b/drivers/media/pci/intel/ipu6/ipu6-dma.c
+new file mode 100644
+index 000000000000..3d77c6e5a45e
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-dma.c
+@@ -0,0 +1,502 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/cacheflush.h>
++#include <linux/dma-mapping.h>
++#include <linux/iova.h>
++#include <linux/list.h>
++#include <linux/mm.h>
++#include <linux/vmalloc.h>
++#include <linux/scatterlist.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-dma.h"
++#include "ipu6-mmu.h"
++
++struct vm_info {
++	struct list_head list;
++	struct page **pages;
++	dma_addr_t ipu6_iova;
++	void *vaddr;
++	unsigned long size;
++};
++
++static struct vm_info *get_vm_info(struct ipu6_mmu *mmu, dma_addr_t iova)
++{
++	struct vm_info *info, *save;
++
++	list_for_each_entry_safe(info, save, &mmu->vma_list, list) {
++		if (iova >= info->ipu6_iova &&
++		    iova < (info->ipu6_iova + info->size))
++			return info;
++	}
++
++	return NULL;
++}
++
++static void __dma_clear_buffer(struct page *page, size_t size,
++			       unsigned long attrs)
++{
++	void *ptr;
++
++	if (!page)
++		return;
++	/*
++	 * Ensure that the allocated pages are zeroed, and that any data
++	 * lurking in the kernel direct-mapped region is invalidated.
++	 */
++	ptr = page_address(page);
++	memset(ptr, 0, size);
++	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
++		clflush_cache_range(ptr, size);
++}
++
++static struct page **__dma_alloc_buffer(struct device *dev, size_t size,
++					gfp_t gfp, unsigned long attrs)
++{
++	int count = PHYS_PFN(size);
++	int array_size = count * sizeof(struct page *);
++	struct page **pages;
++	int i = 0;
++
++	pages = kvzalloc(array_size, GFP_KERNEL);
++	if (!pages)
++		return NULL;
++
++	gfp |= __GFP_NOWARN;
++
++	while (count) {
++		int j, order = __fls(count);
++
++		pages[i] = alloc_pages(gfp, order);
++		while (!pages[i] && order)
++			pages[i] = alloc_pages(gfp, --order);
++		if (!pages[i])
++			goto error;
++
++		if (order) {
++			split_page(pages[i], order);
++			j = 1 << order;
++			while (j--)
++				pages[i + j] = pages[i] + j;
++		}
++
++		__dma_clear_buffer(pages[i], PAGE_SIZE << order, attrs);
++		i += 1 << order;
++		count -= 1 << order;
++	}
++
++	return pages;
++error:
++	while (i--)
++		if (pages[i])
++			__free_pages(pages[i], 0);
++	kvfree(pages);
++	return NULL;
++}
++
++static void __dma_free_buffer(struct device *dev, struct page **pages,
++			      size_t size, unsigned long attrs)
++{
++	int count = PHYS_PFN(size);
++	unsigned int i;
++
++	for (i = 0; i < count && pages[i]; i++) {
++		__dma_clear_buffer(pages[i], PAGE_SIZE, attrs);
++		__free_pages(pages[i], 0);
++	}
++
++	kvfree(pages);
++}
++
++static void ipu6_dma_sync_single_for_cpu(struct device *dev,
++					 dma_addr_t dma_handle,
++					 size_t size,
++					 enum dma_data_direction dir)
++{
++	void *vaddr;
++	u32 offset;
++	struct vm_info *info;
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++
++	info = get_vm_info(mmu, dma_handle);
++	if (WARN_ON(!info))
++		return;
++
++	offset = dma_handle - info->ipu6_iova;
++	if (WARN_ON(size > (info->size - offset)))
++		return;
++
++	vaddr = info->vaddr + offset;
++	clflush_cache_range(vaddr, size);
++}
++
++static void ipu6_dma_sync_sg_for_cpu(struct device *dev,
++				     struct scatterlist *sglist,
++				     int nents, enum dma_data_direction dir)
++{
++	struct scatterlist *sg;
++	int i;
++
++	for_each_sg(sglist, sg, nents, i)
++		clflush_cache_range(page_to_virt(sg_page(sg)), sg->length);
++}
++
++static void *ipu6_dma_alloc(struct device *dev, size_t size,
++			    dma_addr_t *dma_handle, gfp_t gfp,
++			    unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
++	dma_addr_t pci_dma_addr, ipu6_iova;
++	struct vm_info *info;
++	unsigned long count;
++	struct page **pages;
++	struct iova *iova;
++	unsigned int i;
++	int ret;
++
++	info = kzalloc(sizeof(*info), GFP_KERNEL);
++	if (!info)
++		return NULL;
++
++	size = PAGE_ALIGN(size);
++	count = PHYS_PFN(size);
++
++	iova = alloc_iova(&mmu->dmap->iovad, count,
++			  PHYS_PFN(dma_get_mask(dev)), 0);
++	if (!iova)
++		goto out_kfree;
++
++	pages = __dma_alloc_buffer(dev, size, gfp, attrs);
++	if (!pages)
++		goto out_free_iova;
++
++	dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n",
++		size, iova->pfn_lo, iova->pfn_hi);
++	for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) {
++		pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0,
++						  PAGE_SIZE, DMA_BIDIRECTIONAL,
++						  attrs);
++		dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n",
++			&pci_dma_addr);
++		if (dma_mapping_error(&pdev->dev, pci_dma_addr)) {
++			dev_err(dev, "pci_dma_mapping for page[%d] failed", i);
++			goto out_unmap;
++		}
++
++		ret = ipu6_mmu_map(mmu->dmap->mmu_info,
++				   PFN_PHYS(iova->pfn_lo + i), pci_dma_addr,
++				   PAGE_SIZE);
++		if (ret) {
++			dev_err(dev, "ipu6_mmu_map for pci_dma[%d] %pad failed",
++				i, &pci_dma_addr);
++			dma_unmap_page_attrs(&pdev->dev, pci_dma_addr,
++					     PAGE_SIZE, DMA_BIDIRECTIONAL,
++					     attrs);
++			goto out_unmap;
++		}
++	}
++
++	info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL);
++	if (!info->vaddr)
++		goto out_unmap;
++
++	*dma_handle = PFN_PHYS(iova->pfn_lo);
++
++	info->pages = pages;
++	info->ipu6_iova = *dma_handle;
++	info->size = size;
++	list_add(&info->list, &mmu->vma_list);
++
++	return info->vaddr;
++
++out_unmap:
++	while (i--) {
++		ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
++		pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
++						     ipu6_iova);
++		dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
++				     DMA_BIDIRECTIONAL, attrs);
++
++		ipu6_mmu_unmap(mmu->dmap->mmu_info, ipu6_iova, PAGE_SIZE);
++	}
++
++	__dma_free_buffer(dev, pages, size, attrs);
++
++out_free_iova:
++	__free_iova(&mmu->dmap->iovad, iova);
++out_kfree:
++	kfree(info);
++
++	return NULL;
++}
++
++static void ipu6_dma_free(struct device *dev, size_t size, void *vaddr,
++			  dma_addr_t dma_handle,
++			  unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
++	struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle));
++	dma_addr_t pci_dma_addr, ipu6_iova;
++	struct vm_info *info;
++	struct page **pages;
++	unsigned int i;
++
++	if (WARN_ON(!iova))
++		return;
++
++	info = get_vm_info(mmu, dma_handle);
++	if (WARN_ON(!info))
++		return;
++
++	if (WARN_ON(!info->vaddr))
++		return;
++
++	if (WARN_ON(!info->pages))
++		return;
++
++	list_del(&info->list);
++
++	size = PAGE_ALIGN(size);
++
++	pages = info->pages;
++
++	vunmap(vaddr);
++
++	for (i = 0; i < PHYS_PFN(size); i++) {
++		ipu6_iova = PFN_PHYS(iova->pfn_lo + i);
++		pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
++						     ipu6_iova);
++		dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
++				     DMA_BIDIRECTIONAL, attrs);
++	}
++
++	ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
++		       PFN_PHYS(iova_size(iova)));
++
++	__dma_free_buffer(dev, pages, size, attrs);
++
++	mmu->tlb_invalidate(mmu);
++
++	__free_iova(&mmu->dmap->iovad, iova);
++
++	kfree(info);
++}
++
++static int ipu6_dma_mmap(struct device *dev, struct vm_area_struct *vma,
++			 void *addr, dma_addr_t iova, size_t size,
++			 unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	size_t count = PHYS_PFN(PAGE_ALIGN(size));
++	struct vm_info *info;
++	size_t i;
++	int ret;
++
++	info = get_vm_info(mmu, iova);
++	if (!info)
++		return -EFAULT;
++
++	if (!info->vaddr)
++		return -EFAULT;
++
++	if (vma->vm_start & ~PAGE_MASK)
++		return -EINVAL;
++
++	if (size > info->size)
++		return -EFAULT;
++
++	for (i = 0; i < count; i++) {
++		ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i),
++				     info->pages[i]);
++		if (ret < 0)
++			return ret;
++	}
++
++	return 0;
++}
++
++static void ipu6_dma_unmap_sg(struct device *dev,
++			      struct scatterlist *sglist,
++			      int nents, enum dma_data_direction dir,
++			      unsigned long attrs)
++{
++	struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct iova *iova = find_iova(&mmu->dmap->iovad,
++				      PHYS_PFN(sg_dma_address(sglist)));
++	int i, npages, count;
++	struct scatterlist *sg;
++	dma_addr_t pci_dma_addr;
++
++	if (!nents)
++		return;
++
++	if (WARN_ON(!iova))
++		return;
++
++	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
++		ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
++
++	/* get the nents as orig_nents given by caller */
++	count = 0;
++	npages = iova_size(iova);
++	for_each_sg(sglist, sg, nents, i) {
++		if (sg_dma_len(sg) == 0 ||
++		    sg_dma_address(sg) == DMA_MAPPING_ERROR)
++			break;
++
++		npages -= PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
++		count++;
++		if (npages <= 0)
++			break;
++	}
++
++	/*
++	 * Before IPU6 mmu unmap, return the pci dma address back to sg
++	 * assume the nents is less than orig_nents as the least granule
++	 * is 1 SZ_4K page
++	 */
++	dev_dbg(dev, "trying to unmap concatenated %u ents\n", count);
++	for_each_sg(sglist, sg, count, i) {
++		dev_dbg(dev, "ipu unmap sg[%d] %pad\n", i, &sg_dma_address(sg));
++		pci_dma_addr = ipu6_mmu_iova_to_phys(mmu->dmap->mmu_info,
++						     sg_dma_address(sg));
++		dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n",
++			&pci_dma_addr, i);
++		sg_dma_address(sg) = pci_dma_addr;
++	}
++
++	dev_dbg(dev, "ipu6_mmu_unmap low pfn %lu high pfn %lu\n",
++		iova->pfn_lo, iova->pfn_hi);
++	ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
++		       PFN_PHYS(iova_size(iova)));
++
++	mmu->tlb_invalidate(mmu);
++
++	dma_unmap_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
++
++	__free_iova(&mmu->dmap->iovad, iova);
++}
++
++static int ipu6_dma_map_sg(struct device *dev, struct scatterlist *sglist,
++			   int nents, enum dma_data_direction dir,
++			   unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct pci_dev *pdev = to_ipu6_bus_device(dev)->isp->pdev;
++	struct scatterlist *sg;
++	struct iova *iova;
++	size_t npages = 0;
++	unsigned long iova_addr;
++	int i, count;
++
++	for_each_sg(sglist, sg, nents, i) {
++		if (sg->offset) {
++			dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n",
++				i, sg->offset);
++			return -EFAULT;
++		}
++	}
++
++	dev_dbg(dev, "pci_dma_map_sg trying to map %d ents\n", nents);
++	count  = dma_map_sg_attrs(&pdev->dev, sglist, nents, dir, attrs);
++	if (count <= 0) {
++		dev_err(dev, "pci_dma_map_sg %d ents failed\n", nents);
++		return 0;
++	}
++
++	dev_dbg(dev, "pci_dma_map_sg %d ents mapped\n", count);
++
++	for_each_sg(sglist, sg, count, i)
++		npages += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
++
++	iova = alloc_iova(&mmu->dmap->iovad, npages,
++			  PHYS_PFN(dma_get_mask(dev)), 0);
++	if (!iova)
++		return 0;
++
++	dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo,
++		iova->pfn_hi);
++
++	iova_addr = iova->pfn_lo;
++	for_each_sg(sglist, sg, count, i) {
++		int ret;
++
++		dev_dbg(dev, "mapping entry %d: iova 0x%llx phy %pad size %d\n",
++			i, PFN_PHYS(iova_addr), &sg_dma_address(sg),
++			sg_dma_len(sg));
++
++		ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
++				   sg_dma_address(sg),
++				   PAGE_ALIGN(sg_dma_len(sg)));
++		if (ret)
++			goto out_fail;
++
++		sg_dma_address(sg) = PFN_PHYS(iova_addr);
++
++		iova_addr += PHYS_PFN(PAGE_ALIGN(sg_dma_len(sg)));
++	}
++
++	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
++		ipu6_dma_sync_sg_for_cpu(dev, sglist, nents, DMA_BIDIRECTIONAL);
++
++	return count;
++
++out_fail:
++	ipu6_dma_unmap_sg(dev, sglist, i, dir, attrs);
++
++	return 0;
++}
++
++/*
++ * Create scatter-list for the already allocated DMA buffer
++ */
++static int ipu6_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
++				void *cpu_addr, dma_addr_t handle, size_t size,
++				unsigned long attrs)
++{
++	struct ipu6_mmu *mmu = to_ipu6_bus_device(dev)->mmu;
++	struct vm_info *info;
++	int n_pages;
++	int ret = 0;
++
++	info = get_vm_info(mmu, handle);
++	if (!info)
++		return -EFAULT;
++
++	if (!info->vaddr)
++		return -EFAULT;
++
++	if (WARN_ON(!info->pages))
++		return -ENOMEM;
++
++	n_pages = PHYS_PFN(PAGE_ALIGN(size));
++
++	ret = sg_alloc_table_from_pages(sgt, info->pages, n_pages, 0, size,
++					GFP_KERNEL);
++	if (ret)
++		dev_warn(dev, "IPU6 get sgt table failed\n");
++
++	return ret;
++}
++
++const struct dma_map_ops ipu6_dma_ops = {
++	.alloc = ipu6_dma_alloc,
++	.free = ipu6_dma_free,
++	.mmap = ipu6_dma_mmap,
++	.map_sg = ipu6_dma_map_sg,
++	.unmap_sg = ipu6_dma_unmap_sg,
++	.sync_single_for_cpu = ipu6_dma_sync_single_for_cpu,
++	.sync_single_for_device = ipu6_dma_sync_single_for_cpu,
++	.sync_sg_for_cpu = ipu6_dma_sync_sg_for_cpu,
++	.sync_sg_for_device = ipu6_dma_sync_sg_for_cpu,
++	.get_sgtable = ipu6_dma_get_sgtable,
++};
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.h b/drivers/media/pci/intel/ipu6/ipu6-dma.h
+new file mode 100644
+index 000000000000..c75ad2462368
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-dma.h
+@@ -0,0 +1,19 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_DMA_H
++#define IPU6_DMA_H
++
++#include <linux/dma-map-ops.h>
++#include <linux/iova.h>
++
++struct ipu6_mmu_info;
++
++struct ipu6_dma_mapping {
++	struct ipu6_mmu_info *mmu_info;
++	struct iova_domain iovad;
++};
++
++extern const struct dma_map_ops ipu6_dma_ops;
++
++#endif /* IPU6_DMA_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.c b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
+new file mode 100644
+index 000000000000..dc16d45187a8
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
+@@ -0,0 +1,845 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++#include <asm/barrier.h>
++
++#include <linux/align.h>
++#include <linux/atomic.h>
++#include <linux/bitops.h>
++#include <linux/bits.h>
++#include <linux/bug.h>
++#include <linux/cacheflush.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/gfp.h>
++#include <linux/io.h>
++#include <linux/iova.h>
++#include <linux/math.h>
++#include <linux/minmax.h>
++#include <linux/mm.h>
++#include <linux/pfn.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++
++#include "ipu6.h"
++#include "ipu6-dma.h"
++#include "ipu6-mmu.h"
++#include "ipu6-platform-regs.h"
++
++#define ISP_PAGE_SHIFT		12
++#define ISP_PAGE_SIZE		BIT(ISP_PAGE_SHIFT)
++#define ISP_PAGE_MASK		(~(ISP_PAGE_SIZE - 1))
++
++#define ISP_L1PT_SHIFT		22
++#define ISP_L1PT_MASK		(~((1U << ISP_L1PT_SHIFT) - 1))
++
++#define ISP_L2PT_SHIFT		12
++#define ISP_L2PT_MASK		(~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK))))
++
++#define ISP_L1PT_PTES           1024
++#define ISP_L2PT_PTES           1024
++
++#define ISP_PADDR_SHIFT		12
++
++#define REG_TLB_INVALIDATE	0x0000
++
++#define REG_L1_PHYS		0x0004	/* 27-bit pfn */
++#define REG_INFO		0x0008
++
++#define TBL_PHYS_ADDR(a)	((phys_addr_t)(a) << ISP_PADDR_SHIFT)
++
++static void tlb_invalidate(struct ipu6_mmu *mmu)
++{
++	unsigned long flags;
++	unsigned int i;
++
++	spin_lock_irqsave(&mmu->ready_lock, flags);
++	if (!mmu->ready) {
++		spin_unlock_irqrestore(&mmu->ready_lock, flags);
++		return;
++	}
++
++	for (i = 0; i < mmu->nr_mmus; i++) {
++		/*
++		 * To avoid the HW bug induced dead lock in some of the IPU6
++		 * MMUs on successive invalidate calls, we need to first do a
++		 * read to the page table base before writing the invalidate
++		 * register. MMUs which need to implement this WA, will have
++		 * the insert_read_before_invalidate flags set as true.
++		 * Disregard the return value of the read.
++		 */
++		if (mmu->mmu_hw[i].insert_read_before_invalidate)
++			readl(mmu->mmu_hw[i].base + REG_L1_PHYS);
++
++		writel(0xffffffff, mmu->mmu_hw[i].base +
++		       REG_TLB_INVALIDATE);
++		/*
++		 * The TLB invalidation is a "single cycle" (IOMMU clock cycles)
++		 * When the actual MMIO write reaches the IPU6 TLB Invalidate
++		 * register, wmb() will force the TLB invalidate out if the CPU
++		 * attempts to update the IOMMU page table (or sooner).
++		 */
++		wmb();
++	}
++	spin_unlock_irqrestore(&mmu->ready_lock, flags);
++}
++
++#ifdef DEBUG
++static void page_table_dump(struct ipu6_mmu_info *mmu_info)
++{
++	u32 l1_idx;
++
++	dev_dbg(mmu_info->dev, "begin IOMMU page table dump\n");
++
++	for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) {
++		u32 l2_idx;
++		u32 iova = (phys_addr_t)l1_idx << ISP_L1PT_SHIFT;
++
++		if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval)
++			continue;
++		dev_dbg(mmu_info->dev,
++			"l1 entry %u; iovas 0x%8.8x-0x%8.8x, at %pa\n",
++			l1_idx, iova, iova + ISP_PAGE_SIZE,
++			TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]));
++
++		for (l2_idx = 0; l2_idx < ISP_L2PT_PTES; l2_idx++) {
++			u32 *l2_pt = mmu_info->l2_pts[l1_idx];
++			u32 iova2 = iova + (l2_idx << ISP_L2PT_SHIFT);
++
++			if (l2_pt[l2_idx] == mmu_info->dummy_page_pteval)
++				continue;
++
++			dev_dbg(mmu_info->dev,
++				"\tl2 entry %u; iova 0x%8.8x, phys %pa\n",
++				l2_idx, iova2,
++				TBL_PHYS_ADDR(l2_pt[l2_idx]));
++		}
++	}
++
++	dev_dbg(mmu_info->dev, "end IOMMU page table dump\n");
++}
++#endif /* DEBUG */
++
++static dma_addr_t map_single(struct ipu6_mmu_info *mmu_info, void *ptr)
++{
++	dma_addr_t dma;
++
++	dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL);
++	if (dma_mapping_error(mmu_info->dev, dma))
++		return 0;
++
++	return dma;
++}
++
++static int get_dummy_page(struct ipu6_mmu_info *mmu_info)
++{
++	void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
++	dma_addr_t dma;
++
++	if (!pt)
++		return -ENOMEM;
++
++	dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt);
++
++	dma = map_single(mmu_info, pt);
++	if (!dma) {
++		dev_err(mmu_info->dev, "Failed to map dummy page\n");
++		goto err_free_page;
++	}
++
++	mmu_info->dummy_page = pt;
++	mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT;
++
++	return 0;
++
++err_free_page:
++	free_page((unsigned long)pt);
++	return -ENOMEM;
++}
++
++static void free_dummy_page(struct ipu6_mmu_info *mmu_info)
++{
++	dma_unmap_single(mmu_info->dev,
++			 TBL_PHYS_ADDR(mmu_info->dummy_page_pteval),
++			 PAGE_SIZE, DMA_BIDIRECTIONAL);
++	free_page((unsigned long)mmu_info->dummy_page);
++}
++
++static int alloc_dummy_l2_pt(struct ipu6_mmu_info *mmu_info)
++{
++	u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
++	dma_addr_t dma;
++	unsigned int i;
++
++	if (!pt)
++		return -ENOMEM;
++
++	dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt);
++
++	dma = map_single(mmu_info, pt);
++	if (!dma) {
++		dev_err(mmu_info->dev, "Failed to map l2pt page\n");
++		goto err_free_page;
++	}
++
++	for (i = 0; i < ISP_L2PT_PTES; i++)
++		pt[i] = mmu_info->dummy_page_pteval;
++
++	mmu_info->dummy_l2_pt = pt;
++	mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT;
++
++	return 0;
++
++err_free_page:
++	free_page((unsigned long)pt);
++	return -ENOMEM;
++}
++
++static void free_dummy_l2_pt(struct ipu6_mmu_info *mmu_info)
++{
++	dma_unmap_single(mmu_info->dev,
++			 TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval),
++			 PAGE_SIZE, DMA_BIDIRECTIONAL);
++	free_page((unsigned long)mmu_info->dummy_l2_pt);
++}
++
++static u32 *alloc_l1_pt(struct ipu6_mmu_info *mmu_info)
++{
++	u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
++	dma_addr_t dma;
++	unsigned int i;
++
++	if (!pt)
++		return NULL;
++
++	dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt);
++
++	for (i = 0; i < ISP_L1PT_PTES; i++)
++		pt[i] = mmu_info->dummy_l2_pteval;
++
++	dma = map_single(mmu_info, pt);
++	if (!dma) {
++		dev_err(mmu_info->dev, "Failed to map l1pt page\n");
++		goto err_free_page;
++	}
++
++	mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT;
++	dev_dbg(mmu_info->dev, "l1 pt %p mapped at %llx\n", pt, dma);
++
++	return pt;
++
++err_free_page:
++	free_page((unsigned long)pt);
++	return NULL;
++}
++
++static u32 *alloc_l2_pt(struct ipu6_mmu_info *mmu_info)
++{
++	u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
++	unsigned int i;
++
++	if (!pt)
++		return NULL;
++
++	dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt);
++
++	for (i = 0; i < ISP_L1PT_PTES; i++)
++		pt[i] = mmu_info->dummy_page_pteval;
++
++	return pt;
++}
++
++static int l2_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		  phys_addr_t paddr, size_t size)
++{
++	u32 l1_idx = iova >> ISP_L1PT_SHIFT;
++	u32 iova_start = iova;
++	u32 *l2_pt, *l2_virt;
++	unsigned int l2_idx;
++	unsigned long flags;
++	dma_addr_t dma;
++	u32 l1_entry;
++
++	dev_dbg(mmu_info->dev,
++		"mapping l2 page table for l1 index %u (iova %8.8x)\n",
++		l1_idx, (u32)iova);
++
++	spin_lock_irqsave(&mmu_info->lock, flags);
++	l1_entry = mmu_info->l1_pt[l1_idx];
++	if (l1_entry == mmu_info->dummy_l2_pteval) {
++		l2_virt = mmu_info->l2_pts[l1_idx];
++		if (likely(!l2_virt)) {
++			l2_virt = alloc_l2_pt(mmu_info);
++			if (!l2_virt) {
++				spin_unlock_irqrestore(&mmu_info->lock, flags);
++				return -ENOMEM;
++			}
++		}
++
++		dma = map_single(mmu_info, l2_virt);
++		if (!dma) {
++			dev_err(mmu_info->dev, "Failed to map l2pt page\n");
++			free_page((unsigned long)l2_virt);
++			spin_unlock_irqrestore(&mmu_info->lock, flags);
++			return -EINVAL;
++		}
++
++		l1_entry = dma >> ISP_PADDR_SHIFT;
++
++		dev_dbg(mmu_info->dev, "page for l1_idx %u %p allocated\n",
++			l1_idx, l2_virt);
++		mmu_info->l1_pt[l1_idx] = l1_entry;
++		mmu_info->l2_pts[l1_idx] = l2_virt;
++		clflush_cache_range((void *)&mmu_info->l1_pt[l1_idx],
++				    sizeof(mmu_info->l1_pt[l1_idx]));
++	}
++
++	l2_pt = mmu_info->l2_pts[l1_idx];
++
++	dev_dbg(mmu_info->dev, "l2_pt at %p with dma 0x%x\n", l2_pt, l1_entry);
++
++	paddr = ALIGN(paddr, ISP_PAGE_SIZE);
++
++	l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
++
++	dev_dbg(mmu_info->dev, "l2_idx %u, phys 0x%8.8x\n", l2_idx,
++		l2_pt[l2_idx]);
++	if (l2_pt[l2_idx] != mmu_info->dummy_page_pteval) {
++		spin_unlock_irqrestore(&mmu_info->lock, flags);
++		return -EINVAL;
++	}
++
++	l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT;
++
++	clflush_cache_range((void *)&l2_pt[l2_idx], sizeof(l2_pt[l2_idx]));
++	spin_unlock_irqrestore(&mmu_info->lock, flags);
++
++	dev_dbg(mmu_info->dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx,
++		l2_pt[l2_idx]);
++
++	return 0;
++}
++
++static int __ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++			  phys_addr_t paddr, size_t size)
++{
++	u32 iova_start = round_down(iova, ISP_PAGE_SIZE);
++	u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE);
++
++	dev_dbg(mmu_info->dev,
++		"mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr 0x%10.10llx\n",
++		iova_start, iova_end, size, paddr);
++
++	return l2_map(mmu_info, iova_start, paddr, size);
++}
++
++static size_t l2_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		       phys_addr_t dummy, size_t size)
++{
++	u32 l1_idx = iova >> ISP_L1PT_SHIFT;
++	u32 iova_start = iova;
++	unsigned int l2_idx;
++	size_t unmapped = 0;
++	unsigned long flags;
++	u32 *l2_pt;
++
++	dev_dbg(mmu_info->dev, "unmapping l2 page table for l1 index %u (iova 0x%8.8lx)\n",
++		l1_idx, iova);
++
++	spin_lock_irqsave(&mmu_info->lock, flags);
++	if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) {
++		spin_unlock_irqrestore(&mmu_info->lock, flags);
++		dev_err(mmu_info->dev,
++			"unmap iova 0x%8.8lx l1 idx %u which was not mapped\n",
++			iova, l1_idx);
++		return 0;
++	}
++
++	for (l2_idx = (iova_start & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
++	     (iova_start & ISP_L1PT_MASK) + (l2_idx << ISP_PAGE_SHIFT)
++		     < iova_start + size && l2_idx < ISP_L2PT_PTES; l2_idx++) {
++		l2_pt = mmu_info->l2_pts[l1_idx];
++		dev_dbg(mmu_info->dev,
++			"unmap l2 index %u with pteval 0x%10.10llx\n",
++			l2_idx, TBL_PHYS_ADDR(l2_pt[l2_idx]));
++		l2_pt[l2_idx] = mmu_info->dummy_page_pteval;
++
++		clflush_cache_range((void *)&l2_pt[l2_idx],
++				    sizeof(l2_pt[l2_idx]));
++		unmapped++;
++	}
++	spin_unlock_irqrestore(&mmu_info->lock, flags);
++
++	return unmapped << ISP_PAGE_SHIFT;
++}
++
++static size_t __ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info,
++			       unsigned long iova, size_t size)
++{
++	return l2_unmap(mmu_info, iova, 0, size);
++}
++
++static int allocate_trash_buffer(struct ipu6_mmu *mmu)
++{
++	unsigned int n_pages = PHYS_PFN(PAGE_ALIGN(IPU6_MMUV2_TRASH_RANGE));
++	struct iova *iova;
++	unsigned int i;
++	dma_addr_t dma;
++	unsigned long iova_addr;
++	int ret;
++
++	/* Allocate 8MB in iova range */
++	iova = alloc_iova(&mmu->dmap->iovad, n_pages,
++			  PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0);
++	if (!iova) {
++		dev_err(mmu->dev, "cannot allocate iova range for trash\n");
++		return -ENOMEM;
++	}
++
++	dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0,
++			   PAGE_SIZE, DMA_BIDIRECTIONAL);
++	if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) {
++		dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n");
++		ret = -ENOMEM;
++		goto out_free_iova;
++	}
++
++	mmu->pci_trash_page = dma;
++
++	/*
++	 * Map the 8MB iova address range to the same physical trash page
++	 * mmu->trash_page which is already reserved at the probe
++	 */
++	iova_addr = iova->pfn_lo;
++	for (i = 0; i < n_pages; i++) {
++		ret = ipu6_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
++				   mmu->pci_trash_page, PAGE_SIZE);
++		if (ret) {
++			dev_err(mmu->dev,
++				"mapping trash buffer range failed\n");
++			goto out_unmap;
++		}
++
++		iova_addr++;
++	}
++
++	mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo);
++	dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n",
++		mmu->mmid, (unsigned int)mmu->iova_trash_page);
++	return 0;
++
++out_unmap:
++	ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
++		       PFN_PHYS(iova_size(iova)));
++	dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page,
++		       PAGE_SIZE, DMA_BIDIRECTIONAL);
++out_free_iova:
++	__free_iova(&mmu->dmap->iovad, iova);
++	return ret;
++}
++
++int ipu6_mmu_hw_init(struct ipu6_mmu *mmu)
++{
++	struct ipu6_mmu_info *mmu_info;
++	unsigned long flags;
++	unsigned int i;
++
++	mmu_info = mmu->dmap->mmu_info;
++
++	/* Initialise the each MMU HW block */
++	for (i = 0; i < mmu->nr_mmus; i++) {
++		struct ipu6_mmu_hw *mmu_hw = &mmu->mmu_hw[i];
++		unsigned int j;
++		u16 block_addr;
++
++		/* Write page table address per MMU */
++		writel((phys_addr_t)mmu_info->l1_pt_dma,
++		       mmu->mmu_hw[i].base + REG_L1_PHYS);
++
++		/* Set info bits per MMU */
++		writel(mmu->mmu_hw[i].info_bits,
++		       mmu->mmu_hw[i].base + REG_INFO);
++
++		/* Configure MMU TLB stream configuration for L1 */
++		for (j = 0, block_addr = 0; j < mmu_hw->nr_l1streams;
++		     block_addr += mmu->mmu_hw[i].l1_block_sz[j], j++) {
++			if (block_addr > IPU6_MAX_LI_BLOCK_ADDR) {
++				dev_err(mmu->dev, "invalid L1 configuration\n");
++				return -EINVAL;
++			}
++
++			/* Write block start address for each streams */
++			writel(block_addr, mmu_hw->base +
++			       mmu_hw->l1_stream_id_reg_offset + 4 * j);
++		}
++
++		/* Configure MMU TLB stream configuration for L2 */
++		for (j = 0, block_addr = 0; j < mmu_hw->nr_l2streams;
++		     block_addr += mmu->mmu_hw[i].l2_block_sz[j], j++) {
++			if (block_addr > IPU6_MAX_L2_BLOCK_ADDR) {
++				dev_err(mmu->dev, "invalid L2 configuration\n");
++				return -EINVAL;
++			}
++
++			writel(block_addr, mmu_hw->base +
++			       mmu_hw->l2_stream_id_reg_offset + 4 * j);
++		}
++	}
++
++	if (!mmu->trash_page) {
++		int ret;
++
++		mmu->trash_page = alloc_page(GFP_KERNEL);
++		if (!mmu->trash_page) {
++			dev_err(mmu->dev, "insufficient memory for trash buffer\n");
++			return -ENOMEM;
++		}
++
++		ret = allocate_trash_buffer(mmu);
++		if (ret) {
++			__free_page(mmu->trash_page);
++			mmu->trash_page = NULL;
++			dev_err(mmu->dev, "trash buffer allocation failed\n");
++			return ret;
++		}
++	}
++
++	spin_lock_irqsave(&mmu->ready_lock, flags);
++	mmu->ready = true;
++	spin_unlock_irqrestore(&mmu->ready_lock, flags);
++
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_init, INTEL_IPU6);
++
++static struct ipu6_mmu_info *ipu6_mmu_alloc(struct ipu6_device *isp)
++{
++	struct ipu6_mmu_info *mmu_info;
++	int ret;
++
++	mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL);
++	if (!mmu_info)
++		return NULL;
++
++	mmu_info->aperture_start = 0;
++	mmu_info->aperture_end = DMA_BIT_MASK(isp->secure_mode ?
++					      IPU6_MMU_ADDR_BITS :
++					      IPU6_MMU_ADDR_BITS_NON_SECURE);
++	mmu_info->pgsize_bitmap = SZ_4K;
++	mmu_info->dev = &isp->pdev->dev;
++
++	ret = get_dummy_page(mmu_info);
++	if (ret)
++		goto err_free_info;
++
++	ret = alloc_dummy_l2_pt(mmu_info);
++	if (ret)
++		goto err_free_dummy_page;
++
++	mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts));
++	if (!mmu_info->l2_pts)
++		goto err_free_dummy_l2_pt;
++
++	/*
++	 * We always map the L1 page table (a single page as well as
++	 * the L2 page tables).
++	 */
++	mmu_info->l1_pt = alloc_l1_pt(mmu_info);
++	if (!mmu_info->l1_pt)
++		goto err_free_l2_pts;
++
++	spin_lock_init(&mmu_info->lock);
++
++	dev_dbg(mmu_info->dev, "domain initialised\n");
++
++	return mmu_info;
++
++err_free_l2_pts:
++	vfree(mmu_info->l2_pts);
++err_free_dummy_l2_pt:
++	free_dummy_l2_pt(mmu_info);
++err_free_dummy_page:
++	free_dummy_page(mmu_info);
++err_free_info:
++	kfree(mmu_info);
++
++	return NULL;
++}
++
++void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&mmu->ready_lock, flags);
++	mmu->ready = false;
++	spin_unlock_irqrestore(&mmu->ready_lock, flags);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, INTEL_IPU6);
++
++static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp)
++{
++	struct ipu6_dma_mapping *dmap;
++
++	dmap = kzalloc(sizeof(*dmap), GFP_KERNEL);
++	if (!dmap)
++		return NULL;
++
++	dmap->mmu_info = ipu6_mmu_alloc(isp);
++	if (!dmap->mmu_info) {
++		kfree(dmap);
++		return NULL;
++	}
++
++	init_iova_domain(&dmap->iovad, SZ_4K, 1);
++	dmap->mmu_info->dmap = dmap;
++
++	dev_dbg(&isp->pdev->dev, "alloc mapping\n");
++
++	iova_cache_get();
++
++	return dmap;
++}
++
++phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info,
++				  dma_addr_t iova)
++{
++	phys_addr_t phy_addr;
++	unsigned long flags;
++	u32 *l2_pt;
++
++	spin_lock_irqsave(&mmu_info->lock, flags);
++	l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT];
++	phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT];
++	phy_addr <<= ISP_PAGE_SHIFT;
++	spin_unlock_irqrestore(&mmu_info->lock, flags);
++
++	return phy_addr;
++}
++
++static size_t ipu6_mmu_pgsize(unsigned long pgsize_bitmap,
++			      unsigned long addr_merge, size_t size)
++{
++	unsigned int pgsize_idx;
++	size_t pgsize;
++
++	/* Max page size that still fits into 'size' */
++	pgsize_idx = __fls(size);
++
++	if (likely(addr_merge)) {
++		/* Max page size allowed by address */
++		unsigned int align_pgsize_idx = __ffs(addr_merge);
++
++		pgsize_idx = min(pgsize_idx, align_pgsize_idx);
++	}
++
++	pgsize = (1UL << (pgsize_idx + 1)) - 1;
++	pgsize &= pgsize_bitmap;
++
++	WARN_ON(!pgsize);
++
++	/* pick the biggest page */
++	pgsize_idx = __fls(pgsize);
++	pgsize = 1UL << pgsize_idx;
++
++	return pgsize;
++}
++
++size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		      size_t size)
++{
++	size_t unmapped_page, unmapped = 0;
++	unsigned int min_pagesz;
++
++	/* find out the minimum page size supported */
++	min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap);
++
++	/*
++	 * The virtual address and the size of the mapping must be
++	 * aligned (at least) to the size of the smallest page supported
++	 * by the hardware
++	 */
++	if (!IS_ALIGNED(iova | size, min_pagesz)) {
++		dev_err(NULL, "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
++			iova, size, min_pagesz);
++		return -EINVAL;
++	}
++
++	/*
++	 * Keep iterating until we either unmap 'size' bytes (or more)
++	 * or we hit an area that isn't mapped.
++	 */
++	while (unmapped < size) {
++		size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap,
++						iova, size - unmapped);
++
++		unmapped_page = __ipu6_mmu_unmap(mmu_info, iova, pgsize);
++		if (!unmapped_page)
++			break;
++
++		dev_dbg(mmu_info->dev, "unmapped: iova 0x%lx size 0x%zx\n",
++			iova, unmapped_page);
++
++		iova += unmapped_page;
++		unmapped += unmapped_page;
++	}
++
++	return unmapped;
++}
++
++int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		 phys_addr_t paddr, size_t size)
++{
++	unsigned long orig_iova = iova;
++	unsigned int min_pagesz;
++	size_t orig_size = size;
++	int ret = 0;
++
++	if (mmu_info->pgsize_bitmap == 0UL)
++		return -ENODEV;
++
++	/* find out the minimum page size supported */
++	min_pagesz = 1 << __ffs(mmu_info->pgsize_bitmap);
++
++	/*
++	 * both the virtual address and the physical one, as well as
++	 * the size of the mapping, must be aligned (at least) to the
++	 * size of the smallest page supported by the hardware
++	 */
++	if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
++		dev_err(mmu_info->dev,
++			"unaligned: iova %lx pa %pa size %zx min_pagesz %x\n",
++			iova, &paddr, size, min_pagesz);
++		return -EINVAL;
++	}
++
++	dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n",
++		iova, &paddr, size);
++
++	while (size) {
++		size_t pgsize = ipu6_mmu_pgsize(mmu_info->pgsize_bitmap,
++						iova | paddr, size);
++
++		dev_dbg(mmu_info->dev,
++			"mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
++			iova, &paddr, pgsize);
++
++		ret = __ipu6_mmu_map(mmu_info, iova, paddr, pgsize);
++		if (ret)
++			break;
++
++		iova += pgsize;
++		paddr += pgsize;
++		size -= pgsize;
++	}
++
++	/* unroll mapping in case something went wrong */
++	if (ret)
++		ipu6_mmu_unmap(mmu_info, orig_iova, orig_size - size);
++
++	return ret;
++}
++
++static void ipu6_mmu_destroy(struct ipu6_mmu *mmu)
++{
++	struct ipu6_dma_mapping *dmap = mmu->dmap;
++	struct ipu6_mmu_info *mmu_info = dmap->mmu_info;
++	struct iova *iova;
++	u32 l1_idx;
++
++	if (mmu->iova_trash_page) {
++		iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page));
++		if (iova) {
++			/* unmap and free the trash buffer iova */
++			ipu6_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo),
++				       PFN_PHYS(iova_size(iova)));
++			__free_iova(&dmap->iovad, iova);
++		} else {
++			dev_err(mmu->dev, "trash buffer iova not found.\n");
++		}
++
++		mmu->iova_trash_page = 0;
++		dma_unmap_page(mmu_info->dev, mmu->pci_trash_page,
++			       PAGE_SIZE, DMA_BIDIRECTIONAL);
++		mmu->pci_trash_page = 0;
++		__free_page(mmu->trash_page);
++	}
++
++	for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) {
++		if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) {
++			dma_unmap_single(mmu_info->dev,
++					 TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]),
++					 PAGE_SIZE, DMA_BIDIRECTIONAL);
++			free_page((unsigned long)mmu_info->l2_pts[l1_idx]);
++		}
++	}
++
++	vfree(mmu_info->l2_pts);
++	free_dummy_page(mmu_info);
++	dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma),
++			 PAGE_SIZE, DMA_BIDIRECTIONAL);
++	free_page((unsigned long)mmu_info->dummy_l2_pt);
++	free_page((unsigned long)mmu_info->l1_pt);
++	kfree(mmu_info);
++}
++
++struct ipu6_mmu *ipu6_mmu_init(struct device *dev,
++			       void __iomem *base, int mmid,
++			       const struct ipu6_hw_variants *hw)
++{
++	struct ipu6_device *isp = pci_get_drvdata(to_pci_dev(dev));
++	struct ipu6_mmu_pdata *pdata;
++	struct ipu6_mmu *mmu;
++	unsigned int i;
++
++	if (hw->nr_mmus > IPU6_MMU_MAX_DEVICES)
++		return ERR_PTR(-EINVAL);
++
++	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
++	if (!pdata)
++		return ERR_PTR(-ENOMEM);
++
++	for (i = 0; i < hw->nr_mmus; i++) {
++		struct ipu6_mmu_hw *pdata_mmu = &pdata->mmu_hw[i];
++		const struct ipu6_mmu_hw *src_mmu = &hw->mmu_hw[i];
++
++		if (src_mmu->nr_l1streams > IPU6_MMU_MAX_TLB_L1_STREAMS ||
++		    src_mmu->nr_l2streams > IPU6_MMU_MAX_TLB_L2_STREAMS)
++			return ERR_PTR(-EINVAL);
++
++		*pdata_mmu = *src_mmu;
++		pdata_mmu->base = base + src_mmu->offset;
++	}
++
++	mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL);
++	if (!mmu)
++		return ERR_PTR(-ENOMEM);
++
++	mmu->mmid = mmid;
++	mmu->mmu_hw = pdata->mmu_hw;
++	mmu->nr_mmus = hw->nr_mmus;
++	mmu->tlb_invalidate = tlb_invalidate;
++	mmu->ready = false;
++	INIT_LIST_HEAD(&mmu->vma_list);
++	spin_lock_init(&mmu->ready_lock);
++
++	mmu->dmap = alloc_dma_mapping(isp);
++	if (!mmu->dmap) {
++		dev_err(dev, "can't alloc dma mapping\n");
++		return ERR_PTR(-ENOMEM);
++	}
++
++	return mmu;
++}
++
++void ipu6_mmu_cleanup(struct ipu6_mmu *mmu)
++{
++	struct ipu6_dma_mapping *dmap = mmu->dmap;
++
++	ipu6_mmu_destroy(mmu);
++	mmu->dmap = NULL;
++	iova_cache_put();
++	put_iova_domain(&dmap->iovad);
++	kfree(dmap);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.h b/drivers/media/pci/intel/ipu6/ipu6-mmu.h
+new file mode 100644
+index 000000000000..95df7931a2e5
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.h
+@@ -0,0 +1,73 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_MMU_H
++#define IPU6_MMU_H
++
++#define ISYS_MMID 1
++#define PSYS_MMID 0
++
++#include <linux/list.h>
++#include <linux/spinlock_types.h>
++#include <linux/types.h>
++
++struct device;
++struct page;
++struct ipu6_hw_variants;
++
++struct ipu6_mmu_info {
++	struct device *dev;
++
++	u32 *l1_pt;
++	u32 l1_pt_dma;
++	u32 **l2_pts;
++
++	u32 *dummy_l2_pt;
++	u32 dummy_l2_pteval;
++	void *dummy_page;
++	u32 dummy_page_pteval;
++
++	dma_addr_t aperture_start;
++	dma_addr_t aperture_end;
++	unsigned long pgsize_bitmap;
++
++	spinlock_t lock;	/* Serialize access to users */
++	struct ipu6_dma_mapping *dmap;
++};
++
++struct ipu6_mmu {
++	struct list_head node;
++
++	struct ipu6_mmu_hw *mmu_hw;
++	unsigned int nr_mmus;
++	unsigned int mmid;
++
++	phys_addr_t pgtbl;
++	struct device *dev;
++
++	struct ipu6_dma_mapping *dmap;
++	struct list_head vma_list;
++
++	struct page *trash_page;
++	dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */
++	dma_addr_t iova_trash_page; /* IOVA for IPU6 child nodes to use */
++
++	bool ready;
++	spinlock_t ready_lock;	/* Serialize access to bool ready */
++
++	void (*tlb_invalidate)(struct ipu6_mmu *mmu);
++};
++
++struct ipu6_mmu *ipu6_mmu_init(struct device *dev,
++			       void __iomem *base, int mmid,
++			       const struct ipu6_hw_variants *hw);
++void ipu6_mmu_cleanup(struct ipu6_mmu *mmu);
++int ipu6_mmu_hw_init(struct ipu6_mmu *mmu);
++void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu);
++int ipu6_mmu_map(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		 phys_addr_t paddr, size_t size);
++size_t ipu6_mmu_unmap(struct ipu6_mmu_info *mmu_info, unsigned long iova,
++		      size_t size);
++phys_addr_t ipu6_mmu_iova_to_phys(struct ipu6_mmu_info *mmu_info,
++				  dma_addr_t iova);
++#endif
+-- 
+2.43.2
+
+
+From a4363013580d8a552e8084faedbb98bd2482c057 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:20 +0800
+Subject: [PATCH 13/33] media: intel/ipu6: add syscom interfaces between
+ firmware and driver
+
+Syscom is an inter-process(or) communication mechanism between an IPU
+and host. Syscom uses message queues for message exchange between IPU
+and host. Each message queue has its consumer and producer, host queue
+messages to firmware as the producer and then firmware to dequeue the
+messages as consumer and vice versa. IPU and host use shared registers
+or memory to reside the read and write indices which are updated by
+consumer and producer.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-fw-com.c | 413 +++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-fw-com.h |  47 +++
+ 2 files changed, 460 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-com.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-com.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-com.c b/drivers/media/pci/intel/ipu6/ipu6-fw-com.c
+new file mode 100644
+index 000000000000..0f893f44e04c
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-fw-com.c
+@@ -0,0 +1,413 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/io.h>
++#include <linux/math.h>
++#include <linux/overflow.h>
++#include <linux/slab.h>
++#include <linux/types.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-fw-com.h"
++
++/*
++ * FWCOM layer is a shared resource between FW and driver. It consist
++ * of token queues to both send and receive directions. Queue is simply
++ * an array of structures with read and write indexes to the queue.
++ * There are 1...n queues to both directions. Queues locates in
++ * system RAM and are mapped to ISP MMU so that both CPU and ISP can
++ * see the same buffer. Indexes are located in ISP DMEM so that FW code
++ * can poll those with very low latency and cost. CPU access to indexes is
++ * more costly but that happens only at message sending time and
++ * interrupt triggered message handling. CPU doesn't need to poll indexes.
++ * wr_reg / rd_reg are offsets to those dmem location. They are not
++ * the indexes itself.
++ */
++
++/* Shared structure between driver and FW - do not modify */
++struct ipu6_fw_sys_queue {
++	u64 host_address;
++	u32 vied_address;
++	u32 size;
++	u32 token_size;
++	u32 wr_reg;	/* reg number in subsystem's regmem */
++	u32 rd_reg;
++	u32 _align;
++} __packed;
++
++struct ipu6_fw_sys_queue_res {
++	u64 host_address;
++	u32 vied_address;
++	u32 reg;
++} __packed;
++
++enum syscom_state {
++	/* Program load or explicit host setting should init to this */
++	SYSCOM_STATE_UNINIT = 0x57a7e000,
++	/* SP Syscom sets this when it is ready for use */
++	SYSCOM_STATE_READY = 0x57a7e001,
++	/* SP Syscom sets this when no more syscom accesses will happen */
++	SYSCOM_STATE_INACTIVE = 0x57a7e002,
++};
++
++enum syscom_cmd {
++	/* Program load or explicit host setting should init to this */
++	SYSCOM_COMMAND_UNINIT = 0x57a7f000,
++	/* Host Syscom requests syscom to become inactive */
++	SYSCOM_COMMAND_INACTIVE = 0x57a7f001,
++};
++
++/* firmware config: data that sent from the host to SP via DDR */
++/* Cell copies data into a context */
++
++struct ipu6_fw_syscom_config {
++	u32 firmware_address;
++
++	u32 num_input_queues;
++	u32 num_output_queues;
++
++	/* ISP pointers to an array of ipu6_fw_sys_queue structures */
++	u32 input_queue;
++	u32 output_queue;
++
++	/* ISYS / PSYS private data */
++	u32 specific_addr;
++	u32 specific_size;
++};
++
++struct ipu6_fw_com_context {
++	struct ipu6_bus_device *adev;
++	void __iomem *dmem_addr;
++	int (*cell_ready)(struct ipu6_bus_device *adev);
++	void (*cell_start)(struct ipu6_bus_device *adev);
++
++	void *dma_buffer;
++	dma_addr_t dma_addr;
++	unsigned int dma_size;
++	unsigned long attrs;
++
++	struct ipu6_fw_sys_queue *input_queue;	/* array of host to SP queues */
++	struct ipu6_fw_sys_queue *output_queue;	/* array of SP to host */
++
++	u32 config_vied_addr;
++
++	unsigned int buttress_boot_offset;
++	void __iomem *base_addr;
++};
++
++#define FW_COM_WR_REG 0
++#define FW_COM_RD_REG 4
++
++#define REGMEM_OFFSET 0
++#define TUNIT_MAGIC_PATTERN 0x5a5a5a5a
++
++enum regmem_id {
++	/* pass pkg_dir address to SPC in non-secure mode */
++	PKG_DIR_ADDR_REG = 0,
++	/* Tunit CFG blob for secure - provided by host.*/
++	TUNIT_CFG_DWR_REG = 1,
++	/* syscom commands - modified by the host */
++	SYSCOM_COMMAND_REG = 2,
++	/* Store interrupt status - updated by SP */
++	SYSCOM_IRQ_REG = 3,
++	/* first syscom queue pointer register */
++	SYSCOM_QPR_BASE_REG = 4
++};
++
++#define BUTTRESS_FW_BOOT_PARAMS_0 0x4000
++#define BUTTRESS_FW_BOOT_PARAM_REG(base, offset, id)			\
++	((base) + BUTTRESS_FW_BOOT_PARAMS_0 + ((offset) + (id)) * 4)
++
++enum buttress_syscom_id {
++	/* pass syscom configuration to SPC */
++	SYSCOM_CONFIG_ID		= 0,
++	/* syscom state - modified by SP */
++	SYSCOM_STATE_ID			= 1,
++	/* syscom vtl0 addr mask */
++	SYSCOM_VTL0_ADDR_MASK_ID	= 2,
++	SYSCOM_ID_MAX
++};
++
++static void ipu6_sys_queue_init(struct ipu6_fw_sys_queue *q, unsigned int size,
++				unsigned int token_size,
++				struct ipu6_fw_sys_queue_res *res)
++{
++	unsigned int buf_size = (size + 1) * token_size;
++
++	q->size = size + 1;
++	q->token_size = token_size;
++
++	/* acquire the shared buffer space */
++	q->host_address = res->host_address;
++	res->host_address += buf_size;
++	q->vied_address = res->vied_address;
++	res->vied_address += buf_size;
++
++	/* acquire the shared read and writer pointers */
++	q->wr_reg = res->reg;
++	res->reg++;
++	q->rd_reg = res->reg;
++	res->reg++;
++}
++
++void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
++			  struct ipu6_bus_device *adev, void __iomem *base)
++{
++	size_t conf_size, inq_size, outq_size, specific_size;
++	struct ipu6_fw_syscom_config *config_host_addr;
++	unsigned int sizeinput = 0, sizeoutput = 0;
++	struct ipu6_fw_sys_queue_res res;
++	struct ipu6_fw_com_context *ctx;
++	struct device *dev = &adev->auxdev.dev;
++	size_t sizeall, offset;
++	unsigned long attrs = 0;
++	void *specific_host_addr;
++	unsigned int i;
++
++	if (!cfg || !cfg->cell_start || !cfg->cell_ready)
++		return NULL;
++
++	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
++	if (!ctx)
++		return NULL;
++	ctx->dmem_addr = base + cfg->dmem_addr + REGMEM_OFFSET;
++	ctx->adev = adev;
++	ctx->cell_start = cfg->cell_start;
++	ctx->cell_ready = cfg->cell_ready;
++	ctx->buttress_boot_offset = cfg->buttress_boot_offset;
++	ctx->base_addr  = base;
++
++	/*
++	 * Allocate DMA mapped memory. Allocate one big chunk.
++	 */
++	/* Base cfg for FW */
++	conf_size = roundup(sizeof(struct ipu6_fw_syscom_config), 8);
++	/* Descriptions of the queues */
++	inq_size = size_mul(cfg->num_input_queues,
++			    sizeof(struct ipu6_fw_sys_queue));
++	outq_size = size_mul(cfg->num_output_queues,
++			     sizeof(struct ipu6_fw_sys_queue));
++	/* FW specific information structure */
++	specific_size = roundup(cfg->specific_size, 8);
++
++	sizeall = conf_size + inq_size + outq_size + specific_size;
++
++	for (i = 0; i < cfg->num_input_queues; i++)
++		sizeinput += size_mul(cfg->input[i].queue_size + 1,
++				      cfg->input[i].token_size);
++
++	for (i = 0; i < cfg->num_output_queues; i++)
++		sizeoutput += size_mul(cfg->output[i].queue_size + 1,
++				       cfg->output[i].token_size);
++
++	sizeall += sizeinput + sizeoutput;
++
++	ctx->dma_buffer = dma_alloc_attrs(dev, sizeall, &ctx->dma_addr,
++					  GFP_KERNEL, attrs);
++	ctx->attrs = attrs;
++	if (!ctx->dma_buffer) {
++		dev_err(dev, "failed to allocate dma memory\n");
++		kfree(ctx);
++		return NULL;
++	}
++
++	ctx->dma_size = sizeall;
++
++	config_host_addr = ctx->dma_buffer;
++	ctx->config_vied_addr = ctx->dma_addr;
++
++	offset = conf_size;
++	ctx->input_queue = ctx->dma_buffer + offset;
++	config_host_addr->input_queue = ctx->dma_addr + offset;
++	config_host_addr->num_input_queues = cfg->num_input_queues;
++
++	offset += inq_size;
++	ctx->output_queue = ctx->dma_buffer + offset;
++	config_host_addr->output_queue = ctx->dma_addr + offset;
++	config_host_addr->num_output_queues = cfg->num_output_queues;
++
++	/* copy firmware specific data */
++	offset += outq_size;
++	specific_host_addr = ctx->dma_buffer + offset;
++	config_host_addr->specific_addr = ctx->dma_addr + offset;
++	config_host_addr->specific_size = cfg->specific_size;
++	if (cfg->specific_addr && cfg->specific_size)
++		memcpy(specific_host_addr, cfg->specific_addr,
++		       cfg->specific_size);
++
++	/* initialize input queues */
++	offset += specific_size;
++	res.reg = SYSCOM_QPR_BASE_REG;
++	res.host_address = (u64)(ctx->dma_buffer + offset);
++	res.vied_address = ctx->dma_addr + offset;
++	for (i = 0; i < cfg->num_input_queues; i++)
++		ipu6_sys_queue_init(ctx->input_queue + i,
++				    cfg->input[i].queue_size,
++				    cfg->input[i].token_size, &res);
++
++	/* initialize output queues */
++	offset += sizeinput;
++	res.host_address = (u64)(ctx->dma_buffer + offset);
++	res.vied_address = ctx->dma_addr + offset;
++	for (i = 0; i < cfg->num_output_queues; i++) {
++		ipu6_sys_queue_init(ctx->output_queue + i,
++				    cfg->output[i].queue_size,
++				    cfg->output[i].token_size, &res);
++	}
++
++	return ctx;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_prepare, INTEL_IPU6);
++
++int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx)
++{
++	/* write magic pattern to disable the tunit trace */
++	writel(TUNIT_MAGIC_PATTERN, ctx->dmem_addr + TUNIT_CFG_DWR_REG * 4);
++	/* Check if SP is in valid state */
++	if (!ctx->cell_ready(ctx->adev))
++		return -EIO;
++
++	/* store syscom uninitialized command */
++	writel(SYSCOM_COMMAND_UNINIT, ctx->dmem_addr + SYSCOM_COMMAND_REG * 4);
++
++	/* store syscom uninitialized state */
++	writel(SYSCOM_STATE_UNINIT,
++	       BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
++					  ctx->buttress_boot_offset,
++					  SYSCOM_STATE_ID));
++
++	/* store firmware configuration address */
++	writel(ctx->config_vied_addr,
++	       BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
++					  ctx->buttress_boot_offset,
++					  SYSCOM_CONFIG_ID));
++	ctx->cell_start(ctx->adev);
++
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_open, INTEL_IPU6);
++
++int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx)
++{
++	int state;
++
++	state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
++						 ctx->buttress_boot_offset,
++						 SYSCOM_STATE_ID));
++	if (state != SYSCOM_STATE_READY)
++		return -EBUSY;
++
++	/* set close request flag */
++	writel(SYSCOM_COMMAND_INACTIVE, ctx->dmem_addr +
++	       SYSCOM_COMMAND_REG * 4);
++
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_close, INTEL_IPU6);
++
++int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force)
++{
++	/* check if release is forced, an verify cell state if it is not */
++	if (!force && !ctx->cell_ready(ctx->adev))
++		return -EBUSY;
++
++	dma_free_attrs(&ctx->adev->auxdev.dev, ctx->dma_size,
++		       ctx->dma_buffer, ctx->dma_addr, ctx->attrs);
++	kfree(ctx);
++	return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_release, INTEL_IPU6);
++
++bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx)
++{
++	int state;
++
++	state = readl(BUTTRESS_FW_BOOT_PARAM_REG(ctx->base_addr,
++						 ctx->buttress_boot_offset,
++						 SYSCOM_STATE_ID));
++
++	return state == SYSCOM_STATE_READY;
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_fw_com_ready, INTEL_IPU6);
++
++void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
++{
++	struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
++	void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
++	unsigned int wr, rd;
++	unsigned int packets;
++	unsigned int index;
++
++	wr = readl(q_dmem + FW_COM_WR_REG);
++	rd = readl(q_dmem + FW_COM_RD_REG);
++
++	if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
++		return NULL;
++
++	if (wr < rd)
++		packets = rd - wr - 1;
++	else
++		packets = q->size - (wr - rd + 1);
++
++	if (!packets)
++		return NULL;
++
++	index = readl(q_dmem + FW_COM_WR_REG);
++
++	return (void *)(q->host_address + index * q->token_size);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_send_get_token, INTEL_IPU6);
++
++void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
++{
++	struct ipu6_fw_sys_queue *q = &ctx->input_queue[q_nbr];
++	void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
++	unsigned int wr = readl(q_dmem + FW_COM_WR_REG) + 1;
++
++	if (wr >= q->size)
++		wr = 0;
++
++	writel(wr, q_dmem + FW_COM_WR_REG);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_send_put_token, INTEL_IPU6);
++
++void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr)
++{
++	struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
++	void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
++	unsigned int wr, rd;
++	unsigned int packets;
++
++	wr = readl(q_dmem + FW_COM_WR_REG);
++	rd = readl(q_dmem + FW_COM_RD_REG);
++
++	if (WARN_ON_ONCE(wr >= q->size || rd >= q->size))
++		return NULL;
++
++	if (wr < rd)
++		wr += q->size;
++
++	packets = wr - rd;
++	if (!packets)
++		return NULL;
++
++	return (void *)(q->host_address + rd * q->token_size);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_recv_get_token, INTEL_IPU6);
++
++void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr)
++{
++	struct ipu6_fw_sys_queue *q = &ctx->output_queue[q_nbr];
++	void __iomem *q_dmem = ctx->dmem_addr + q->wr_reg * 4;
++	unsigned int rd = readl(q_dmem + FW_COM_RD_REG) + 1;
++
++	if (rd >= q->size)
++		rd = 0;
++
++	writel(rd, q_dmem + FW_COM_RD_REG);
++}
++EXPORT_SYMBOL_NS_GPL(ipu6_recv_put_token, INTEL_IPU6);
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-com.h b/drivers/media/pci/intel/ipu6/ipu6-fw-com.h
+new file mode 100644
+index 000000000000..660c406b3ac9
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-fw-com.h
+@@ -0,0 +1,47 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_FW_COM_H
++#define IPU6_FW_COM_H
++
++struct ipu6_fw_com_context;
++struct ipu6_bus_device;
++
++struct ipu6_fw_syscom_queue_config {
++	unsigned int queue_size;	/* tokens per queue */
++	unsigned int token_size;	/* bytes per token */
++};
++
++#define SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET	0
++
++struct ipu6_fw_com_cfg {
++	unsigned int num_input_queues;
++	unsigned int num_output_queues;
++	struct ipu6_fw_syscom_queue_config *input;
++	struct ipu6_fw_syscom_queue_config *output;
++
++	unsigned int dmem_addr;
++
++	/* firmware-specific configuration data */
++	void *specific_addr;
++	unsigned int specific_size;
++	int (*cell_ready)(struct ipu6_bus_device *adev);
++	void (*cell_start)(struct ipu6_bus_device *adev);
++
++	unsigned int buttress_boot_offset;
++};
++
++void *ipu6_fw_com_prepare(struct ipu6_fw_com_cfg *cfg,
++			  struct ipu6_bus_device *adev, void __iomem *base);
++
++int ipu6_fw_com_open(struct ipu6_fw_com_context *ctx);
++bool ipu6_fw_com_ready(struct ipu6_fw_com_context *ctx);
++int ipu6_fw_com_close(struct ipu6_fw_com_context *ctx);
++int ipu6_fw_com_release(struct ipu6_fw_com_context *ctx, unsigned int force);
++
++void *ipu6_recv_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
++void ipu6_recv_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
++void *ipu6_send_get_token(struct ipu6_fw_com_context *ctx, int q_nbr);
++void ipu6_send_put_token(struct ipu6_fw_com_context *ctx, int q_nbr);
++
++#endif
+-- 
+2.43.2
+
+
+From e690b8a96eb4fbe1d948ad80047a9af7c601b761 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:21 +0800
+Subject: [PATCH 14/33] media: intel/ipu6: input system ABI between firmware
+ and driver
+
+Implement the input system firmware ABIs between the firmware and
+driver - include stream configuration, control command, capture
+request and response, etc.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-fw-isys.c | 487 +++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-fw-isys.h | 573 ++++++++++++++++++++
+ 2 files changed, 1060 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
+new file mode 100644
+index 000000000000..e06c1c955d38
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
+@@ -0,0 +1,487 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/cacheflush.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/io.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-fw-com.h"
++#include "ipu6-isys.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++#include "ipu6-platform-regs.h"
++
++static const char send_msg_types[N_IPU6_FW_ISYS_SEND_TYPE][32] = {
++	"STREAM_OPEN",
++	"STREAM_START",
++	"STREAM_START_AND_CAPTURE",
++	"STREAM_CAPTURE",
++	"STREAM_STOP",
++	"STREAM_FLUSH",
++	"STREAM_CLOSE"
++};
++
++static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_fw_isys_proxy_resp_info_abi *resp;
++	int ret;
++
++	resp = ipu6_recv_get_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
++	if (!resp)
++		return 1;
++
++	dev_dbg(dev, "Proxy response: id %u, error %u, details %u\n",
++		resp->request_id, resp->error_info.error,
++		resp->error_info.error_details);
++
++	ret = req_id == resp->request_id ? 0 : -EIO;
++
++	ipu6_recv_put_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
++
++	return ret;
++}
++
++int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
++				  unsigned int req_id,
++				  unsigned int index,
++				  unsigned int offset, u32 value)
++{
++	struct ipu6_fw_com_context *ctx = isys->fwcom;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_fw_proxy_send_queue_token *token;
++	unsigned int timeout = 1000;
++	int ret;
++
++	dev_dbg(dev,
++		"proxy send: req_id 0x%x, index %d, offset 0x%x, value 0x%x\n",
++		req_id, index, offset, value);
++
++	token = ipu6_send_get_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);
++	if (!token)
++		return -EBUSY;
++
++	token->request_id = req_id;
++	token->region_index = index;
++	token->offset = offset;
++	token->value = value;
++	ipu6_send_put_token(ctx, IPU6_BASE_PROXY_SEND_QUEUES);
++
++	do {
++		usleep_range(100, 110);
++		ret = handle_proxy_response(isys, req_id);
++		if (!ret)
++			break;
++		if (ret == -EIO) {
++			dev_err(dev, "Proxy respond with unexpected id\n");
++			break;
++		}
++		timeout--;
++	} while (ret && timeout);
++
++	if (!timeout)
++		dev_err(dev, "Proxy response timed out\n");
++
++	return ret;
++}
++
++int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
++			     const unsigned int stream_handle,
++			     void *cpu_mapped_buf,
++			     dma_addr_t dma_mapped_buf,
++			     size_t size, u16 send_type)
++{
++	struct ipu6_fw_com_context *ctx = isys->fwcom;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_fw_send_queue_token *token;
++
++	if (send_type >= N_IPU6_FW_ISYS_SEND_TYPE)
++		return -EINVAL;
++
++	dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]);
++
++	/*
++	 * Time to flush cache in case we have some payload. Not all messages
++	 * have that
++	 */
++	if (cpu_mapped_buf)
++		clflush_cache_range(cpu_mapped_buf, size);
++
++	token = ipu6_send_get_token(ctx,
++				    stream_handle + IPU6_BASE_MSG_SEND_QUEUES);
++	if (!token)
++		return -EBUSY;
++
++	token->payload = dma_mapped_buf;
++	token->buf_handle = (unsigned long)cpu_mapped_buf;
++	token->send_type = send_type;
++
++	ipu6_send_put_token(ctx, stream_handle + IPU6_BASE_MSG_SEND_QUEUES);
++
++	return 0;
++}
++
++int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys,
++			    const unsigned int stream_handle, u16 send_type)
++{
++	return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0,
++					send_type);
++}
++
++int ipu6_fw_isys_close(struct ipu6_isys *isys)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	int retry = IPU6_ISYS_CLOSE_RETRY;
++	unsigned long flags;
++	void *fwcom;
++	int ret;
++
++	/*
++	 * Stop the isys fw. Actual close takes
++	 * some time as the FW must stop its actions including code fetch
++	 * to SP icache.
++	 * spinlock to wait the interrupt handler to be finished
++	 */
++	spin_lock_irqsave(&isys->power_lock, flags);
++	ret = ipu6_fw_com_close(isys->fwcom);
++	fwcom = isys->fwcom;
++	isys->fwcom = NULL;
++	spin_unlock_irqrestore(&isys->power_lock, flags);
++	if (ret)
++		dev_err(dev, "Device close failure: %d\n", ret);
++
++	/* release probably fails if the close failed. Let's try still */
++	do {
++		usleep_range(400, 500);
++		ret = ipu6_fw_com_release(fwcom, 0);
++		retry--;
++	} while (ret && retry);
++
++	if (ret) {
++		dev_err(dev, "Device release time out %d\n", ret);
++		spin_lock_irqsave(&isys->power_lock, flags);
++		isys->fwcom = fwcom;
++		spin_unlock_irqrestore(&isys->power_lock, flags);
++	}
++
++	return ret;
++}
++
++void ipu6_fw_isys_cleanup(struct ipu6_isys *isys)
++{
++	int ret;
++
++	ret = ipu6_fw_com_release(isys->fwcom, 1);
++	if (ret < 0)
++		dev_warn(&isys->adev->auxdev.dev,
++			 "Device busy, fw_com release failed.");
++	isys->fwcom = NULL;
++}
++
++static void start_sp(struct ipu6_bus_device *adev)
++{
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	void __iomem *spc_regs_base = isys->pdata->base +
++		isys->pdata->ipdata->hw_variant.spc_offset;
++	u32 val = IPU6_ISYS_SPC_STATUS_START |
++		IPU6_ISYS_SPC_STATUS_RUN |
++		IPU6_ISYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE;
++
++	val |= isys->icache_prefetch ? IPU6_ISYS_SPC_STATUS_ICACHE_PREFETCH : 0;
++
++	writel(val, spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);
++}
++
++static int query_sp(struct ipu6_bus_device *adev)
++{
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	void __iomem *spc_regs_base = isys->pdata->base +
++		isys->pdata->ipdata->hw_variant.spc_offset;
++	u32 val;
++
++	val = readl(spc_regs_base + IPU6_ISYS_REG_SPC_STATUS_CTRL);
++	/* return true when READY == 1, START == 0 */
++	val &= IPU6_ISYS_SPC_STATUS_READY | IPU6_ISYS_SPC_STATUS_START;
++
++	return val == IPU6_ISYS_SPC_STATUS_READY;
++}
++
++static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys,
++				    struct ipu6_fw_com_cfg *fwcom,
++				    unsigned int num_streams)
++{
++	unsigned int max_send_queues, max_sram_blocks, max_devq_size;
++	struct ipu6_fw_syscom_queue_config *input_queue_cfg;
++	struct ipu6_fw_syscom_queue_config *output_queue_cfg;
++	struct device *dev = &isys->adev->auxdev.dev;
++	int type_proxy = IPU6_FW_ISYS_QUEUE_TYPE_PROXY;
++	int type_dev = IPU6_FW_ISYS_QUEUE_TYPE_DEV;
++	int type_msg = IPU6_FW_ISYS_QUEUE_TYPE_MSG;
++	int base_dev_send = IPU6_BASE_DEV_SEND_QUEUES;
++	int base_msg_send = IPU6_BASE_MSG_SEND_QUEUES;
++	int base_msg_recv = IPU6_BASE_MSG_RECV_QUEUES;
++	struct ipu6_fw_isys_fw_config *isys_fw_cfg;
++	u32 num_in_message_queues;
++	unsigned int max_streams;
++	unsigned int size;
++	unsigned int i;
++
++	max_streams = isys->pdata->ipdata->max_streams;
++	max_send_queues = isys->pdata->ipdata->max_send_queues;
++	max_sram_blocks = isys->pdata->ipdata->max_sram_blocks;
++	max_devq_size = isys->pdata->ipdata->max_devq_size;
++	num_in_message_queues = clamp(num_streams, 1U, max_streams);
++	isys_fw_cfg = devm_kzalloc(dev, sizeof(*isys_fw_cfg), GFP_KERNEL);
++	if (!isys_fw_cfg)
++		return -ENOMEM;
++
++	isys_fw_cfg->num_send_queues[type_proxy] = IPU6_N_MAX_PROXY_SEND_QUEUES;
++	isys_fw_cfg->num_send_queues[type_dev] = IPU6_N_MAX_DEV_SEND_QUEUES;
++	isys_fw_cfg->num_send_queues[type_msg] = num_in_message_queues;
++	isys_fw_cfg->num_recv_queues[type_proxy] = IPU6_N_MAX_PROXY_RECV_QUEUES;
++	/* Common msg/dev return queue */
++	isys_fw_cfg->num_recv_queues[type_dev] = 0;
++	isys_fw_cfg->num_recv_queues[type_msg] = 1;
++
++	size = sizeof(*input_queue_cfg) * max_send_queues;
++	input_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);
++	if (!input_queue_cfg)
++		return -ENOMEM;
++
++	size = sizeof(*output_queue_cfg) * IPU6_N_MAX_RECV_QUEUES;
++	output_queue_cfg = devm_kzalloc(dev, size, GFP_KERNEL);
++	if (!output_queue_cfg)
++		return -ENOMEM;
++
++	fwcom->input = input_queue_cfg;
++	fwcom->output = output_queue_cfg;
++
++	fwcom->num_input_queues = isys_fw_cfg->num_send_queues[type_proxy] +
++		isys_fw_cfg->num_send_queues[type_dev] +
++		isys_fw_cfg->num_send_queues[type_msg];
++
++	fwcom->num_output_queues = isys_fw_cfg->num_recv_queues[type_proxy] +
++		isys_fw_cfg->num_recv_queues[type_dev] +
++		isys_fw_cfg->num_recv_queues[type_msg];
++
++	/* SRAM partitioning. Equal partitioning is set. */
++	for (i = 0; i < max_sram_blocks; i++) {
++		if (i < num_in_message_queues)
++			isys_fw_cfg->buffer_partition.num_gda_pages[i] =
++				(IPU6_DEVICE_GDA_NR_PAGES *
++				 IPU6_DEVICE_GDA_VIRT_FACTOR) /
++				num_in_message_queues;
++		else
++			isys_fw_cfg->buffer_partition.num_gda_pages[i] = 0;
++	}
++
++	/* FW assumes proxy interface at fwcom queue 0 */
++	for (i = 0; i < isys_fw_cfg->num_send_queues[type_proxy]; i++) {
++		input_queue_cfg[i].token_size =
++			sizeof(struct ipu6_fw_proxy_send_queue_token);
++		input_queue_cfg[i].queue_size = IPU6_ISYS_SIZE_PROXY_SEND_QUEUE;
++	}
++
++	for (i = 0; i < isys_fw_cfg->num_send_queues[type_dev]; i++) {
++		input_queue_cfg[base_dev_send + i].token_size =
++			sizeof(struct ipu6_fw_send_queue_token);
++		input_queue_cfg[base_dev_send + i].queue_size = max_devq_size;
++	}
++
++	for (i = 0; i < isys_fw_cfg->num_send_queues[type_msg]; i++) {
++		input_queue_cfg[base_msg_send + i].token_size =
++			sizeof(struct ipu6_fw_send_queue_token);
++		input_queue_cfg[base_msg_send + i].queue_size =
++			IPU6_ISYS_SIZE_SEND_QUEUE;
++	}
++
++	for (i = 0; i < isys_fw_cfg->num_recv_queues[type_proxy]; i++) {
++		output_queue_cfg[i].token_size =
++			sizeof(struct ipu6_fw_proxy_resp_queue_token);
++		output_queue_cfg[i].queue_size =
++			IPU6_ISYS_SIZE_PROXY_RECV_QUEUE;
++	}
++	/* There is no recv DEV queue */
++	for (i = 0; i < isys_fw_cfg->num_recv_queues[type_msg]; i++) {
++		output_queue_cfg[base_msg_recv + i].token_size =
++			sizeof(struct ipu6_fw_resp_queue_token);
++		output_queue_cfg[base_msg_recv + i].queue_size =
++			IPU6_ISYS_SIZE_RECV_QUEUE;
++	}
++
++	fwcom->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset;
++	fwcom->specific_addr = isys_fw_cfg;
++	fwcom->specific_size = sizeof(*isys_fw_cfg);
++
++	return 0;
++}
++
++int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	int retry = IPU6_ISYS_OPEN_RETRY;
++	struct ipu6_fw_com_cfg fwcom = {
++		.cell_start = start_sp,
++		.cell_ready = query_sp,
++		.buttress_boot_offset = SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET,
++	};
++	int ret;
++
++	ipu6_isys_fwcom_cfg_init(isys, &fwcom, num_streams);
++
++	isys->fwcom = ipu6_fw_com_prepare(&fwcom, isys->adev,
++					  isys->pdata->base);
++	if (!isys->fwcom) {
++		dev_err(dev, "isys fw com prepare failed\n");
++		return -EIO;
++	}
++
++	ret = ipu6_fw_com_open(isys->fwcom);
++	if (ret) {
++		dev_err(dev, "isys fw com open failed %d\n", ret);
++		return ret;
++	}
++
++	do {
++		usleep_range(400, 500);
++		if (ipu6_fw_com_ready(isys->fwcom))
++			break;
++		retry--;
++	} while (retry > 0);
++
++	if (!retry) {
++		dev_err(dev, "isys port open ready failed %d\n", ret);
++		ipu6_fw_isys_close(isys);
++		ret = -EIO;
++	}
++
++	return ret;
++}
++
++struct ipu6_fw_isys_resp_info_abi *
++ipu6_fw_isys_get_resp(void *context, unsigned int queue)
++{
++	return ipu6_recv_get_token(context, queue);
++}
++
++void ipu6_fw_isys_put_resp(void *context, unsigned int queue)
++{
++	ipu6_recv_put_token(context, queue);
++}
++
++void ipu6_fw_isys_dump_stream_cfg(struct device *dev,
++				  struct ipu6_fw_isys_stream_cfg_data_abi *cfg)
++{
++	unsigned int i;
++
++	dev_dbg(dev, "-----------------------------------------------------\n");
++	dev_dbg(dev, "IPU6_FW_ISYS_STREAM_CFG_DATA\n");
++
++	dev_dbg(dev, "compfmt = %d\n", cfg->vc);
++	dev_dbg(dev, "src = %d\n", cfg->src);
++	dev_dbg(dev, "vc = %d\n", cfg->vc);
++	dev_dbg(dev, "isl_use = %d\n", cfg->isl_use);
++	dev_dbg(dev, "sensor_type = %d\n", cfg->sensor_type);
++
++	dev_dbg(dev, "send_irq_sof_discarded = %d\n",
++		cfg->send_irq_sof_discarded);
++	dev_dbg(dev, "send_irq_eof_discarded = %d\n",
++		cfg->send_irq_eof_discarded);
++	dev_dbg(dev, "send_resp_sof_discarded = %d\n",
++		cfg->send_resp_sof_discarded);
++	dev_dbg(dev, "send_resp_eof_discarded = %d\n",
++		cfg->send_resp_eof_discarded);
++
++	dev_dbg(dev, "crop:\n");
++	dev_dbg(dev, "\t.left_top = [%d, %d]\n", cfg->crop.left_offset,
++		cfg->crop.top_offset);
++	dev_dbg(dev, "\t.right_bottom = [%d, %d]\n", cfg->crop.right_offset,
++		cfg->crop.bottom_offset);
++
++	dev_dbg(dev, "nof_input_pins = %d\n", cfg->nof_input_pins);
++	for (i = 0; i < cfg->nof_input_pins; i++) {
++		dev_dbg(dev, "input pin[%d]:\n", i);
++		dev_dbg(dev, "\t.dt = 0x%0x\n", cfg->input_pins[i].dt);
++		dev_dbg(dev, "\t.mipi_store_mode = %d\n",
++			cfg->input_pins[i].mipi_store_mode);
++		dev_dbg(dev, "\t.bits_per_pix = %d\n",
++			cfg->input_pins[i].bits_per_pix);
++		dev_dbg(dev, "\t.mapped_dt = 0x%0x\n",
++			cfg->input_pins[i].mapped_dt);
++		dev_dbg(dev, "\t.input_res = %dx%d\n",
++			cfg->input_pins[i].input_res.width,
++			cfg->input_pins[i].input_res.height);
++		dev_dbg(dev, "\t.mipi_decompression = %d\n",
++			cfg->input_pins[i].mipi_decompression);
++		dev_dbg(dev, "\t.capture_mode = %d\n",
++			cfg->input_pins[i].capture_mode);
++	}
++
++	dev_dbg(dev, "nof_output_pins = %d\n", cfg->nof_output_pins);
++	for (i = 0; i < cfg->nof_output_pins; i++) {
++		dev_dbg(dev, "output_pin[%d]:\n", i);
++		dev_dbg(dev, "\t.input_pin_id = %d\n",
++			cfg->output_pins[i].input_pin_id);
++		dev_dbg(dev, "\t.output_res = %dx%d\n",
++			cfg->output_pins[i].output_res.width,
++			cfg->output_pins[i].output_res.height);
++		dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride);
++		dev_dbg(dev, "\t.pt = %d\n", cfg->output_pins[i].pt);
++		dev_dbg(dev, "\t.payload_buf_size = %d\n",
++			cfg->output_pins[i].payload_buf_size);
++		dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft);
++		dev_dbg(dev, "\t.watermark_in_lines = %d\n",
++			cfg->output_pins[i].watermark_in_lines);
++		dev_dbg(dev, "\t.send_irq = %d\n",
++			cfg->output_pins[i].send_irq);
++		dev_dbg(dev, "\t.reserve_compression = %d\n",
++			cfg->output_pins[i].reserve_compression);
++		dev_dbg(dev, "\t.snoopable = %d\n",
++			cfg->output_pins[i].snoopable);
++		dev_dbg(dev, "\t.error_handling_enable = %d\n",
++			cfg->output_pins[i].error_handling_enable);
++		dev_dbg(dev, "\t.sensor_type = %d\n",
++			cfg->output_pins[i].sensor_type);
++	}
++	dev_dbg(dev, "-----------------------------------------------------\n");
++}
++
++void
++ipu6_fw_isys_dump_frame_buff_set(struct device *dev,
++				 struct ipu6_fw_isys_frame_buff_set_abi *buf,
++				 unsigned int outputs)
++{
++	unsigned int i;
++
++	dev_dbg(dev, "-----------------------------------------------------\n");
++	dev_dbg(dev, "IPU6_FW_ISYS_FRAME_BUFF_SET\n");
++
++	for (i = 0; i < outputs; i++) {
++		dev_dbg(dev, "output_pin[%d]:\n", i);
++		dev_dbg(dev, "\t.out_buf_id = %llu\n",
++			buf->output_pins[i].out_buf_id);
++		dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr);
++		dev_dbg(dev, "\t.compress = %d\n",
++			buf->output_pins[i].compress);
++	}
++
++	dev_dbg(dev, "send_irq_sof = 0x%x\n", buf->send_irq_sof);
++	dev_dbg(dev, "send_irq_eof = 0x%x\n", buf->send_irq_eof);
++	dev_dbg(dev, "send_resp_sof = 0x%x\n", buf->send_resp_sof);
++	dev_dbg(dev, "send_resp_eof = 0x%x\n", buf->send_resp_eof);
++	dev_dbg(dev, "send_irq_capture_ack = 0x%x\n",
++		buf->send_irq_capture_ack);
++	dev_dbg(dev, "send_irq_capture_done = 0x%x\n",
++		buf->send_irq_capture_done);
++	dev_dbg(dev, "send_resp_capture_ack = 0x%x\n",
++		buf->send_resp_capture_ack);
++	dev_dbg(dev, "send_resp_capture_done = 0x%x\n",
++		buf->send_resp_capture_done);
++
++	dev_dbg(dev, "-----------------------------------------------------\n");
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
+new file mode 100644
+index 000000000000..a7ffa0e22bf0
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
+@@ -0,0 +1,573 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_FW_ISYS_H
++#define IPU6_FW_ISYS_H
++
++#include <linux/types.h>
++
++struct device;
++struct ipu6_isys;
++
++/* Max number of Input/Output Pins */
++#define IPU6_MAX_IPINS 4
++
++#define IPU6_MAX_OPINS ((IPU6_MAX_IPINS) + 1)
++
++#define IPU6_STREAM_ID_MAX 16
++#define IPU6_NONSECURE_STREAM_ID_MAX 12
++#define IPU6_DEV_SEND_QUEUE_SIZE (IPU6_STREAM_ID_MAX)
++#define IPU6_NOF_SRAM_BLOCKS_MAX (IPU6_STREAM_ID_MAX)
++#define IPU6_N_MAX_MSG_SEND_QUEUES (IPU6_STREAM_ID_MAX)
++#define IPU6SE_STREAM_ID_MAX 8
++#define IPU6SE_NONSECURE_STREAM_ID_MAX 4
++#define IPU6SE_DEV_SEND_QUEUE_SIZE (IPU6SE_STREAM_ID_MAX)
++#define IPU6SE_NOF_SRAM_BLOCKS_MAX (IPU6SE_STREAM_ID_MAX)
++#define IPU6SE_N_MAX_MSG_SEND_QUEUES (IPU6SE_STREAM_ID_MAX)
++
++/* Single return queue for all streams/commands type */
++#define IPU6_N_MAX_MSG_RECV_QUEUES 1
++/* Single device queue for high priority commands (bypass in-order queue) */
++#define IPU6_N_MAX_DEV_SEND_QUEUES 1
++/* Single dedicated send queue for proxy interface */
++#define IPU6_N_MAX_PROXY_SEND_QUEUES 1
++/* Single dedicated recv queue for proxy interface */
++#define IPU6_N_MAX_PROXY_RECV_QUEUES 1
++/* Send queues layout */
++#define IPU6_BASE_PROXY_SEND_QUEUES 0
++#define IPU6_BASE_DEV_SEND_QUEUES \
++	(IPU6_BASE_PROXY_SEND_QUEUES + IPU6_N_MAX_PROXY_SEND_QUEUES)
++#define IPU6_BASE_MSG_SEND_QUEUES \
++	(IPU6_BASE_DEV_SEND_QUEUES + IPU6_N_MAX_DEV_SEND_QUEUES)
++/* Recv queues layout */
++#define IPU6_BASE_PROXY_RECV_QUEUES 0
++#define IPU6_BASE_MSG_RECV_QUEUES \
++	(IPU6_BASE_PROXY_RECV_QUEUES + IPU6_N_MAX_PROXY_RECV_QUEUES)
++#define IPU6_N_MAX_RECV_QUEUES \
++	(IPU6_BASE_MSG_RECV_QUEUES + IPU6_N_MAX_MSG_RECV_QUEUES)
++
++#define IPU6_N_MAX_SEND_QUEUES \
++	(IPU6_BASE_MSG_SEND_QUEUES + IPU6_N_MAX_MSG_SEND_QUEUES)
++#define IPU6SE_N_MAX_SEND_QUEUES \
++	(IPU6_BASE_MSG_SEND_QUEUES + IPU6SE_N_MAX_MSG_SEND_QUEUES)
++
++/* Max number of supported input pins routed in ISL */
++#define IPU6_MAX_IPINS_IN_ISL 2
++
++/* Max number of planes for frame formats supported by the FW */
++#define IPU6_PIN_PLANES_MAX 4
++
++#define IPU6_FW_ISYS_SENSOR_TYPE_START 14
++#define IPU6_FW_ISYS_SENSOR_TYPE_END 19
++#define IPU6SE_FW_ISYS_SENSOR_TYPE_START 6
++#define IPU6SE_FW_ISYS_SENSOR_TYPE_END 11
++/*
++ * Device close takes some time from last ack message to actual stopping
++ * of the SP processor. As long as the SP processor runs we can't proceed with
++ * clean up of resources.
++ */
++#define IPU6_ISYS_OPEN_RETRY			2000
++#define IPU6_ISYS_CLOSE_RETRY			2000
++#define IPU6_FW_CALL_TIMEOUT_JIFFIES		msecs_to_jiffies(2000)
++
++enum ipu6_fw_isys_resp_type {
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE = 0,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK,
++	IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY,
++	IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_WATERMARK,
++	IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF,
++	IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE,
++	IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_SKIPPED,
++	IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_SKIPPED,
++	IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF_DISCARDED,
++	IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF_DISCARDED,
++	IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY,
++	N_IPU6_FW_ISYS_RESP_TYPE
++};
++
++enum ipu6_fw_isys_send_type {
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN = 0,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_START,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_STOP,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH,
++	IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE,
++	N_IPU6_FW_ISYS_SEND_TYPE
++};
++
++enum ipu6_fw_isys_queue_type {
++	IPU6_FW_ISYS_QUEUE_TYPE_PROXY = 0,
++	IPU6_FW_ISYS_QUEUE_TYPE_DEV,
++	IPU6_FW_ISYS_QUEUE_TYPE_MSG,
++	N_IPU6_FW_ISYS_QUEUE_TYPE
++};
++
++enum ipu6_fw_isys_stream_source {
++	IPU6_FW_ISYS_STREAM_SRC_PORT_0 = 0,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_1,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_2,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_3,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_4,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_5,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_6,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_7,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_8,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_9,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_10,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_11,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_12,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_13,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_14,
++	IPU6_FW_ISYS_STREAM_SRC_PORT_15,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_2,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_3,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_4,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_5,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_6,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_7,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_8,
++	IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_9,
++	N_IPU6_FW_ISYS_STREAM_SRC
++};
++
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 IPU6_FW_ISYS_STREAM_SRC_PORT_0
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT1 IPU6_FW_ISYS_STREAM_SRC_PORT_1
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT2 IPU6_FW_ISYS_STREAM_SRC_PORT_2
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT3 IPU6_FW_ISYS_STREAM_SRC_PORT_3
++
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTA IPU6_FW_ISYS_STREAM_SRC_PORT_4
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_PORTB IPU6_FW_ISYS_STREAM_SRC_PORT_5
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT0 \
++	IPU6_FW_ISYS_STREAM_SRC_PORT_6
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT1 \
++	IPU6_FW_ISYS_STREAM_SRC_PORT_7
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT2 \
++	IPU6_FW_ISYS_STREAM_SRC_PORT_8
++#define IPU6_FW_ISYS_STREAM_SRC_CSI2_3PH_CPHY_PORT3 \
++	IPU6_FW_ISYS_STREAM_SRC_PORT_9
++
++#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT0 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_0
++#define IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_PORT1 IPU6_FW_ISYS_STREAM_SRC_MIPIGEN_1
++
++/**
++ * enum ipu6_fw_isys_mipi_vc: MIPI csi2 spec
++ * supports up to 4 virtual per physical channel
++ */
++enum ipu6_fw_isys_mipi_vc {
++	IPU6_FW_ISYS_MIPI_VC_0 = 0,
++	IPU6_FW_ISYS_MIPI_VC_1,
++	IPU6_FW_ISYS_MIPI_VC_2,
++	IPU6_FW_ISYS_MIPI_VC_3,
++	N_IPU6_FW_ISYS_MIPI_VC
++};
++
++enum ipu6_fw_isys_frame_format_type {
++	IPU6_FW_ISYS_FRAME_FORMAT_NV11 = 0, /* 12 bit YUV 411, Y, UV plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV12,	/* 12 bit YUV 420, Y, UV plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV12_16, /* 16 bit YUV 420, Y, UV plane */
++	/* 12 bit YUV 420, Intel proprietary tiled format */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV12_TILEY,
++
++	IPU6_FW_ISYS_FRAME_FORMAT_NV16,	/* 16 bit YUV 422, Y, UV plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV21,	/* 12 bit YUV 420, Y, VU plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_NV61,	/* 16 bit YUV 422, Y, VU plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YV12,	/* 12 bit YUV 420, Y, V, U plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YV16,	/* 16 bit YUV 422, Y, V, U plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420, /* 12 bit YUV 420, Y, U, V plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420_10, /* yuv420, 10 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420_12, /* yuv420, 12 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420_14, /* yuv420, 14 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV420_16, /* yuv420, 16 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV422, /* 16 bit YUV 422, Y, U, V plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV422_16, /* yuv422, 16 bits per subpixel */
++	IPU6_FW_ISYS_FRAME_FORMAT_UYVY,	/* 16 bit YUV 422, UYVY interleaved */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUYV,	/* 16 bit YUV 422, YUYV interleaved */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV444, /* 24 bit YUV 444, Y, U, V plane */
++	/* Internal format, 2 y lines followed by a uvinterleaved line */
++	IPU6_FW_ISYS_FRAME_FORMAT_YUV_LINE,
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW8,	/* RAW8, 1 plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW10, /* RAW10, 1 plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW12, /* RAW12, 1 plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW14, /* RAW14, 1 plane */
++	IPU6_FW_ISYS_FRAME_FORMAT_RAW16, /* RAW16, 1 plane */
++	/**
++	 * 16 bit RGB, 1 plane. Each 3 sub pixels are packed into one 16 bit
++	 * value, 5 bits for R, 6 bits for G and 5 bits for B.
++	 */
++	IPU6_FW_ISYS_FRAME_FORMAT_RGB565,
++	IPU6_FW_ISYS_FRAME_FORMAT_PLANAR_RGB888, /* 24 bit RGB, 3 planes */
++	IPU6_FW_ISYS_FRAME_FORMAT_RGBA888, /* 32 bit RGBA, 1 plane, A=Alpha */
++	IPU6_FW_ISYS_FRAME_FORMAT_QPLANE6, /* Internal, for advanced ISP */
++	IPU6_FW_ISYS_FRAME_FORMAT_BINARY_8, /* byte stream, used for jpeg. */
++	N_IPU6_FW_ISYS_FRAME_FORMAT
++};
++
++#define IPU6_FW_ISYS_FRAME_FORMAT_RAW	(IPU6_FW_ISYS_FRAME_FORMAT_RAW16)
++
++enum ipu6_fw_isys_pin_type {
++	/* captured as MIPI packets */
++	IPU6_FW_ISYS_PIN_TYPE_MIPI = 0,
++	/* captured through the SoC path */
++	IPU6_FW_ISYS_PIN_TYPE_RAW_SOC = 3,
++};
++
++/**
++ * enum ipu6_fw_isys_mipi_store_mode. Describes if long MIPI packets reach
++ * MIPI SRAM with the long packet header or
++ * if not, then only option is to capture it with pin type MIPI.
++ */
++enum ipu6_fw_isys_mipi_store_mode {
++	IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL = 0,
++	IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER,
++	N_IPU6_FW_ISYS_MIPI_STORE_MODE
++};
++
++enum ipu6_fw_isys_capture_mode {
++	IPU6_FW_ISYS_CAPTURE_MODE_REGULAR = 0,
++	IPU6_FW_ISYS_CAPTURE_MODE_BURST,
++	N_IPU6_FW_ISYS_CAPTURE_MODE,
++};
++
++enum ipu6_fw_isys_sensor_mode {
++	IPU6_FW_ISYS_SENSOR_MODE_NORMAL = 0,
++	IPU6_FW_ISYS_SENSOR_MODE_TOBII,
++	N_IPU6_FW_ISYS_SENSOR_MODE,
++};
++
++enum ipu6_fw_isys_error {
++	IPU6_FW_ISYS_ERROR_NONE = 0,
++	IPU6_FW_ISYS_ERROR_FW_INTERNAL_CONSISTENCY,
++	IPU6_FW_ISYS_ERROR_HW_CONSISTENCY,
++	IPU6_FW_ISYS_ERROR_DRIVER_INVALID_COMMAND_SEQUENCE,
++	IPU6_FW_ISYS_ERROR_DRIVER_INVALID_DEVICE_CONFIGURATION,
++	IPU6_FW_ISYS_ERROR_DRIVER_INVALID_STREAM_CONFIGURATION,
++	IPU6_FW_ISYS_ERROR_DRIVER_INVALID_FRAME_CONFIGURATION,
++	IPU6_FW_ISYS_ERROR_INSUFFICIENT_RESOURCES,
++	IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO,
++	IPU6_FW_ISYS_ERROR_HW_REPORTED_SIG2CIO,
++	IPU6_FW_ISYS_ERROR_SENSOR_FW_SYNC,
++	IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION,
++	IPU6_FW_ISYS_ERROR_RESPONSE_QUEUE_FULL,
++	N_IPU6_FW_ISYS_ERROR
++};
++
++enum ipu6_fw_proxy_error {
++	IPU6_FW_PROXY_ERROR_NONE = 0,
++	IPU6_FW_PROXY_ERROR_INVALID_WRITE_REGION,
++	IPU6_FW_PROXY_ERROR_INVALID_WRITE_OFFSET,
++	N_IPU6_FW_PROXY_ERROR
++};
++
++/* firmware ABI structure below are aligned in firmware, no need pack */
++struct ipu6_fw_isys_buffer_partition_abi {
++	u32 num_gda_pages[IPU6_STREAM_ID_MAX];
++};
++
++struct ipu6_fw_isys_fw_config {
++	struct ipu6_fw_isys_buffer_partition_abi buffer_partition;
++	u32 num_send_queues[N_IPU6_FW_ISYS_QUEUE_TYPE];
++	u32 num_recv_queues[N_IPU6_FW_ISYS_QUEUE_TYPE];
++};
++
++/**
++ * struct ipu6_fw_isys_resolution_abi: Generic resolution structure.
++ */
++struct ipu6_fw_isys_resolution_abi {
++	u32 width;
++	u32 height;
++};
++
++/**
++ * struct ipu6_fw_isys_output_pin_payload_abi
++ * @out_buf_id: Points to output pin buffer - buffer identifier
++ * @addr: Points to output pin buffer - CSS Virtual Address
++ * @compress: Request frame compression (1), or  not (0)
++ */
++struct ipu6_fw_isys_output_pin_payload_abi {
++	u64 out_buf_id;
++	u32 addr;
++	u32 compress;
++};
++
++/**
++ * struct ipu6_fw_isys_output_pin_info_abi
++ * @output_res: output pin resolution
++ * @stride: output stride in Bytes (not valid for statistics)
++ * @watermark_in_lines: pin watermark level in lines
++ * @payload_buf_size: minimum size in Bytes of all buffers that will be
++ *			supplied for capture on this pin
++ * @send_irq: assert if pin event should trigger irq
++ * @pt: pin type -real format "enum ipu6_fw_isys_pin_type"
++ * @ft: frame format type -real format "enum ipu6_fw_isys_frame_format_type"
++ * @input_pin_id: related input pin id
++ * @reserve_compression: reserve compression resources for pin
++ */
++struct ipu6_fw_isys_output_pin_info_abi {
++	struct ipu6_fw_isys_resolution_abi output_res;
++	u32 stride;
++	u32 watermark_in_lines;
++	u32 payload_buf_size;
++	u32 ts_offsets[IPU6_PIN_PLANES_MAX];
++	u32 s2m_pixel_soc_pixel_remapping;
++	u32 csi_be_soc_pixel_remapping;
++	u8 send_irq;
++	u8 input_pin_id;
++	u8 pt;
++	u8 ft;
++	u8 reserved;
++	u8 reserve_compression;
++	u8 snoopable;
++	u8 error_handling_enable;
++	u32 sensor_type;
++};
++
++/**
++ * struct ipu6_fw_isys_input_pin_info_abi
++ * @input_res: input resolution
++ * @dt: mipi data type ((enum ipu6_fw_isys_mipi_data_type)
++ * @mipi_store_mode: defines if legacy long packet header will be stored or
++ *		     discarded if discarded, output pin type for this
++ *		     input pin can only be MIPI
++ *		     (enum ipu6_fw_isys_mipi_store_mode)
++ * @bits_per_pix: native bits per pixel
++ * @mapped_dt: actual data type from sensor
++ * @mipi_decompression: defines which compression will be in mipi backend
++ * @crop_first_and_last_lines    Control whether to crop the
++ *                              first and last line of the
++ *                              input image. Crop done by HW
++ *                              device.
++ * @capture_mode: mode of capture, regular or burst, default value is regular
++ */
++struct ipu6_fw_isys_input_pin_info_abi {
++	struct ipu6_fw_isys_resolution_abi input_res;
++	u8 dt;
++	u8 mipi_store_mode;
++	u8 bits_per_pix;
++	u8 mapped_dt;
++	u8 mipi_decompression;
++	u8 crop_first_and_last_lines;
++	u8 capture_mode;
++	u8 reserved;
++};
++
++/**
++ * struct ipu6_fw_isys_cropping_abi - cropping coordinates
++ */
++struct ipu6_fw_isys_cropping_abi {
++	s32 top_offset;
++	s32 left_offset;
++	s32 bottom_offset;
++	s32 right_offset;
++};
++
++/**
++ * struct ipu6_fw_isys_stream_cfg_data_abi
++ * ISYS stream configuration data structure
++ * @crop: for extended use and is not used in FW currently
++ * @input_pins: input pin descriptors
++ * @output_pins: output pin descriptors
++ * @compfmt: de-compression setting for User Defined Data
++ * @nof_input_pins: number of input pins
++ * @nof_output_pins: number of output pins
++ * @send_irq_sof_discarded: send irq on discarded frame sof response
++ *		- if '1' it will override the send_resp_sof_discarded
++ *		  and send the response
++ *		- if '0' the send_resp_sof_discarded will determine
++ *		  whether to send the response
++ * @send_irq_eof_discarded: send irq on discarded frame eof response
++ *		- if '1' it will override the send_resp_eof_discarded
++ *		  and send the response
++ *		- if '0' the send_resp_eof_discarded will determine
++ *		  whether to send the response
++ * @send_resp_sof_discarded: send response for discarded frame sof detected,
++ *			     used only when send_irq_sof_discarded is '0'
++ * @send_resp_eof_discarded: send response for discarded frame eof detected,
++ *			     used only when send_irq_eof_discarded is '0'
++ * @src: Stream source index e.g. MIPI_generator_0, CSI2-rx_1
++ * @vc: MIPI Virtual Channel (up to 4 virtual per physical channel)
++ * @isl_use: indicates whether stream requires ISL and how
++ * @sensor_type: type of connected sensor, tobii or others, default is 0
++ */
++struct ipu6_fw_isys_stream_cfg_data_abi {
++	struct ipu6_fw_isys_cropping_abi crop;
++	struct ipu6_fw_isys_input_pin_info_abi input_pins[IPU6_MAX_IPINS];
++	struct ipu6_fw_isys_output_pin_info_abi output_pins[IPU6_MAX_OPINS];
++	u32 compfmt;
++	u8 nof_input_pins;
++	u8 nof_output_pins;
++	u8 send_irq_sof_discarded;
++	u8 send_irq_eof_discarded;
++	u8 send_resp_sof_discarded;
++	u8 send_resp_eof_discarded;
++	u8 src;
++	u8 vc;
++	u8 isl_use;
++	u8 sensor_type;
++	u8 reserved;
++	u8 reserved2;
++};
++
++/**
++ * struct ipu6_fw_isys_frame_buff_set - frame buffer set
++ * @output_pins: output pin addresses
++ * @send_irq_sof: send irq on frame sof response
++ *		- if '1' it will override the send_resp_sof and
++ *		  send the response
++ *		- if '0' the send_resp_sof will determine whether to
++ *		  send the response
++ * @send_irq_eof: send irq on frame eof response
++ *		- if '1' it will override the send_resp_eof and
++ *		  send the response
++ *		- if '0' the send_resp_eof will determine whether to
++ *		  send the response
++ * @send_resp_sof: send response for frame sof detected,
++ *		   used only when send_irq_sof is '0'
++ * @send_resp_eof: send response for frame eof detected,
++ *		   used only when send_irq_eof is '0'
++ * @send_resp_capture_ack: send response for capture ack event
++ * @send_resp_capture_done: send response for capture done event
++ */
++struct ipu6_fw_isys_frame_buff_set_abi {
++	struct ipu6_fw_isys_output_pin_payload_abi output_pins[IPU6_MAX_OPINS];
++	u8 send_irq_sof;
++	u8 send_irq_eof;
++	u8 send_irq_capture_ack;
++	u8 send_irq_capture_done;
++	u8 send_resp_sof;
++	u8 send_resp_eof;
++	u8 send_resp_capture_ack;
++	u8 send_resp_capture_done;
++	u8 reserved[8];
++};
++
++/**
++ * struct ipu6_fw_isys_error_info_abi
++ * @error: error code if something went wrong
++ * @error_details: depending on error code, it may contain additional error info
++ */
++struct ipu6_fw_isys_error_info_abi {
++	u32 error;
++	u32 error_details;
++};
++
++/**
++ * struct ipu6_fw_isys_resp_info_comm
++ * @pin: this var is only valid for pin event related responses,
++ *     contains pin addresses
++ * @error_info: error information from the FW
++ * @timestamp: Time information for event if available
++ * @stream_handle: stream id the response corresponds to
++ * @type: response type (enum ipu6_fw_isys_resp_type)
++ * @pin_id: pin id that the pin payload corresponds to
++ */
++struct ipu6_fw_isys_resp_info_abi {
++	u64 buf_id;
++	struct ipu6_fw_isys_output_pin_payload_abi pin;
++	struct ipu6_fw_isys_error_info_abi error_info;
++	u32 timestamp[2];
++	u8 stream_handle;
++	u8 type;
++	u8 pin_id;
++	u8 reserved;
++	u32 reserved2;
++};
++
++/**
++ * struct ipu6_fw_isys_proxy_error_info_comm
++ * @proxy_error: error code if something went wrong
++ * @proxy_error_details: depending on error code, it may contain additional
++ *			error info
++ */
++struct ipu6_fw_isys_proxy_error_info_abi {
++	u32 error;
++	u32 error_details;
++};
++
++struct ipu6_fw_isys_proxy_resp_info_abi {
++	u32 request_id;
++	struct ipu6_fw_isys_proxy_error_info_abi error_info;
++};
++
++/**
++ * struct ipu6_fw_proxy_write_queue_token
++ * @request_id: update id for the specific proxy write request
++ * @region_index: Region id for the proxy write request
++ * @offset: Offset of the write request according to the base address
++ *	    of the region
++ * @value: Value that is requested to be written with the proxy write request
++ */
++struct ipu6_fw_proxy_write_queue_token {
++	u32 request_id;
++	u32 region_index;
++	u32 offset;
++	u32 value;
++};
++
++/**
++ * struct ipu6_fw_resp_queue_token
++ */
++struct ipu6_fw_resp_queue_token {
++	struct ipu6_fw_isys_resp_info_abi resp_info;
++};
++
++/**
++ * struct ipu6_fw_send_queue_token
++ */
++struct ipu6_fw_send_queue_token {
++	u64 buf_handle;
++	u32 payload;
++	u16 send_type;
++	u16 stream_id;
++};
++
++/**
++ * struct ipu6_fw_proxy_resp_queue_token
++ */
++struct ipu6_fw_proxy_resp_queue_token {
++	struct ipu6_fw_isys_proxy_resp_info_abi proxy_resp_info;
++};
++
++/**
++ * struct ipu6_fw_proxy_send_queue_token
++ */
++struct ipu6_fw_proxy_send_queue_token {
++	u32 request_id;
++	u32 region_index;
++	u32 offset;
++	u32 value;
++};
++
++void
++ipu6_fw_isys_dump_stream_cfg(struct device *dev,
++			     struct ipu6_fw_isys_stream_cfg_data_abi *cfg);
++void
++ipu6_fw_isys_dump_frame_buff_set(struct device *dev,
++				 struct ipu6_fw_isys_frame_buff_set_abi *buf,
++				 unsigned int outputs);
++int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams);
++int ipu6_fw_isys_close(struct ipu6_isys *isys);
++int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys,
++			    const unsigned int stream_handle, u16 send_type);
++int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
++			     const unsigned int stream_handle,
++			     void *cpu_mapped_buf, dma_addr_t dma_mapped_buf,
++			     size_t size, u16 send_type);
++int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
++				  unsigned int req_id,
++				  unsigned int index,
++				  unsigned int offset, u32 value);
++void ipu6_fw_isys_cleanup(struct ipu6_isys *isys);
++struct ipu6_fw_isys_resp_info_abi *
++ipu6_fw_isys_get_resp(void *context, unsigned int queue);
++void ipu6_fw_isys_put_resp(void *context, unsigned int queue);
++#endif
+-- 
+2.43.2
+
+
+From e69a245fa4db99e5983170aab73f962dc99d3149 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:22 +0800
+Subject: [PATCH 15/33] media: intel/ipu6: add IPU6 CSI2 receiver v4l2
+ sub-device
+
+Input system CSI2 receiver is exposed as a v4l2 sub-device.
+Each CSI2 sub-device represent one single CSI2 hardware port
+which be linked with external sub-device such camera sensor
+by linked with ISYS CSI2's sink pad. CSI2 source pad is linked
+to the sink pad of video capture device.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c | 666 ++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h |  81 +++
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.c   | 381 ++++++++++
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.h   |  61 ++
+ .../intel/ipu6/ipu6-platform-isys-csi2-reg.h  | 189 +++++
+ 5 files changed, 1378 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+new file mode 100644
+index 000000000000..ac9fa3e0d7ab
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+@@ -0,0 +1,666 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/atomic.h>
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/minmax.h>
++#include <linux/sprintf.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-subdev.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-isys-subdev.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++
++static const u32 csi2_supported_codes[] = {
++	MEDIA_BUS_FMT_RGB565_1X16,
++	MEDIA_BUS_FMT_RGB888_1X24,
++	MEDIA_BUS_FMT_UYVY8_1X16,
++	MEDIA_BUS_FMT_YUYV8_1X16,
++	MEDIA_BUS_FMT_SBGGR10_1X10,
++	MEDIA_BUS_FMT_SGBRG10_1X10,
++	MEDIA_BUS_FMT_SGRBG10_1X10,
++	MEDIA_BUS_FMT_SRGGB10_1X10,
++	MEDIA_BUS_FMT_SBGGR12_1X12,
++	MEDIA_BUS_FMT_SGBRG12_1X12,
++	MEDIA_BUS_FMT_SGRBG12_1X12,
++	MEDIA_BUS_FMT_SRGGB12_1X12,
++	MEDIA_BUS_FMT_SBGGR8_1X8,
++	MEDIA_BUS_FMT_SGBRG8_1X8,
++	MEDIA_BUS_FMT_SGRBG8_1X8,
++	MEDIA_BUS_FMT_SRGGB8_1X8,
++	0
++};
++
++/*
++ * Strings corresponding to CSI-2 receiver errors are here.
++ * Corresponding macros are defined in the header file.
++ */
++static const struct ipu6_csi2_error dphy_rx_errors[] = {
++	{ "Single packet header error corrected", true },
++	{ "Multiple packet header errors detected", true },
++	{ "Payload checksum (CRC) error", true },
++	{ "Transfer FIFO overflow", false },
++	{ "Reserved short packet data type detected", true },
++	{ "Reserved long packet data type detected", true },
++	{ "Incomplete long packet detected", false },
++	{ "Frame sync error", false },
++	{ "Line sync error", false },
++	{ "DPHY recoverable synchronization error", true },
++	{ "DPHY fatal error", false },
++	{ "DPHY elastic FIFO overflow", false },
++	{ "Inter-frame short packet discarded", true },
++	{ "Inter-frame long packet discarded", true },
++	{ "MIPI pktgen overflow", false },
++	{ "MIPI pktgen data loss", false },
++	{ "FIFO overflow", false },
++	{ "Lane deskew", false },
++	{ "SOT sync error", false },
++	{ "HSIDLE detected", false }
++};
++
++s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2)
++{
++	struct media_pad *src_pad;
++	struct v4l2_subdev *ext_sd;
++	struct device *dev;
++
++	if (!csi2)
++		return -EINVAL;
++
++	dev = &csi2->isys->adev->auxdev.dev;
++	src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
++	if (IS_ERR_OR_NULL(src_pad)) {
++		dev_err(dev, "can't get source pad of %s\n", csi2->asd.sd.name);
++		return -ENOLINK;
++	}
++
++	ext_sd = media_entity_to_v4l2_subdev(src_pad->entity);
++	if (WARN(!ext_sd, "Failed to get subdev for %s\n", csi2->asd.sd.name))
++		return -ENODEV;
++
++	return v4l2_get_link_freq(ext_sd->ctrl_handler, 0, 0);
++}
++
++static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
++				struct v4l2_event_subscription *sub)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++
++	dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n",
++		sub->type, sub->id);
++
++	switch (sub->type) {
++	case V4L2_EVENT_FRAME_SYNC:
++		return v4l2_event_subscribe(fh, sub, 10, NULL);
++	case V4L2_EVENT_CTRL:
++		return v4l2_ctrl_subscribe_event(fh, sub);
++	default:
++		return -EINVAL;
++	}
++}
++
++static const struct v4l2_subdev_core_ops csi2_sd_core_ops = {
++	.subscribe_event = csi2_subscribe_event,
++	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++/*
++ * The input system CSI2+ receiver has several
++ * parameters affecting the receiver timings. These depend
++ * on the MIPI bus frequency F in Hz (sensor transmitter rate)
++ * as follows:
++ *	register value = (A/1e9 + B * UI) / COUNT_ACC
++ * where
++ *	UI = 1 / (2 * F) in seconds
++ *	COUNT_ACC = counter accuracy in seconds
++ *	COUNT_ACC = 0.125 ns = 1 / 8 ns, ACCINV = 8.
++ *
++ * A and B are coefficients from the table below,
++ * depending whether the register minimum or maximum value is
++ * calculated.
++ *				       Minimum     Maximum
++ * Clock lane			       A     B     A     B
++ * reg_rx_csi_dly_cnt_termen_clane     0     0    38     0
++ * reg_rx_csi_dly_cnt_settle_clane    95    -8   300   -16
++ * Data lanes
++ * reg_rx_csi_dly_cnt_termen_dlane0    0     0    35     4
++ * reg_rx_csi_dly_cnt_settle_dlane0   85    -2   145    -6
++ * reg_rx_csi_dly_cnt_termen_dlane1    0     0    35     4
++ * reg_rx_csi_dly_cnt_settle_dlane1   85    -2   145    -6
++ * reg_rx_csi_dly_cnt_termen_dlane2    0     0    35     4
++ * reg_rx_csi_dly_cnt_settle_dlane2   85    -2   145    -6
++ * reg_rx_csi_dly_cnt_termen_dlane3    0     0    35     4
++ * reg_rx_csi_dly_cnt_settle_dlane3   85    -2   145    -6
++ *
++ * We use the minimum values of both A and B.
++ */
++
++#define DIV_SHIFT	8
++#define CSI2_ACCINV	8
++
++static u32 calc_timing(s32 a, s32 b, s64 link_freq, s32 accinv)
++{
++	return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT)
++			     / (s32)(link_freq >> DIV_SHIFT));
++}
++
++static int
++ipu6_isys_csi2_calc_timing(struct ipu6_isys_csi2 *csi2,
++			   struct ipu6_isys_csi2_timing *timing, s32 accinv)
++{
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++	s64 link_freq;
++
++	link_freq = ipu6_isys_csi2_get_link_freq(csi2);
++	if (link_freq < 0)
++		return link_freq;
++
++	timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A,
++				      CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B,
++				      link_freq, accinv);
++	timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A,
++				      CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B,
++				      link_freq, accinv);
++	timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A,
++				      CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B,
++				      link_freq, accinv);
++	timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A,
++				      CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B,
++				      link_freq, accinv);
++
++	dev_dbg(dev, "ctermen %u csettle %u dtermen %u dsettle %u\n",
++		timing->ctermen, timing->csettle,
++		timing->dtermen, timing->dsettle);
++
++	return 0;
++}
++
++void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2)
++{
++	u32 irq = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++			CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
++	struct ipu6_isys *isys = csi2->isys;
++	u32 mask;
++
++	mask = isys->pdata->ipdata->csi2.irq_mask;
++	writel(irq & mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++	csi2->receiver_errors |= irq & mask;
++}
++
++void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2)
++{
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++	const struct ipu6_csi2_error *errors;
++	u32 status;
++	u32 i;
++
++	/* register errors once more in case of interrupts are disabled */
++	ipu6_isys_register_errors(csi2);
++	status = csi2->receiver_errors;
++	csi2->receiver_errors = 0;
++	errors = dphy_rx_errors;
++
++	for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) {
++		if (status & BIT(i))
++			dev_err_ratelimited(dev, "csi2-%i error: %s\n",
++					    csi2->port, errors[i].error_string);
++	}
++}
++
++static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd,
++				     const struct ipu6_isys_csi2_timing *timing,
++				     unsigned int nlanes, int enable)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
++	struct ipu6_isys *isys = csi2->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_isys_csi2_config cfg;
++	unsigned int nports;
++	int ret = 0;
++	u32 mask = 0;
++	u32 i;
++
++	dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off",
++		csi2->port, nlanes);
++
++	cfg.port = csi2->port;
++	cfg.nlanes = nlanes;
++
++	mask = isys->pdata->ipdata->csi2.irq_mask;
++	nports = isys->pdata->ipdata->csi2.nports;
++
++	if (!enable) {
++		writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE);
++		writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE);
++
++		writel(0,
++		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++		       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
++		writel(mask,
++		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++		       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++		writel(0,
++		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++		       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
++		writel(0xffffffff,
++		       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++		       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++
++		isys->phy_set_power(isys, &cfg, timing, false);
++
++		writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
++		       (isys->pdata->ipdata->csi2.fw_access_port_ofs,
++			csi2->port));
++		writel(0, isys->pdata->base +
++		       CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port));
++
++		return ret;
++	}
++
++	/* reset port reset */
++	writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST);
++	usleep_range(100, 200);
++	writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST);
++
++	/* enable port clock */
++	for (i = 0; i < nports; i++) {
++		writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i));
++		writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
++		       (isys->pdata->ipdata->csi2.fw_access_port_ofs, i));
++	}
++
++	/* enable all error related irq */
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
++	writel(mask,
++	       csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
++	       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
++
++	/*
++	 * Using event from firmware instead of irq to handle CSI2 sync event
++	 * which can reduce system wakeups. If CSI2 sync irq enabled, we need
++	 * disable the firmware CSI2 sync event to avoid duplicate handling.
++	 */
++	writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
++	writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
++	writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++	writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
++	writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
++
++	/* configure to enable FE and PPI2CSI */
++	writel(0, csi2->base + CSI_REG_CSI_FE_MODE);
++	writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL);
++	writel(CSI_CNTR_SENSOR_LINE_ID | CSI_CNTR_SENSOR_FRAME_ID,
++	       csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL);
++	writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1),
++	       csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF);
++
++	writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE);
++	writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE);
++
++	ret = isys->phy_set_power(isys, &cfg, timing, true);
++	if (ret)
++		dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port,
++			ret);
++
++	return ret;
++}
++
++static int set_stream(struct v4l2_subdev *sd, int enable)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++	struct ipu6_isys_csi2_timing timing = { };
++	unsigned int nlanes;
++	int ret;
++
++	dev_dbg(dev, "csi2 stream %s callback\n", enable ? "on" : "off");
++
++	if (!enable) {
++		csi2->stream_count--;
++		if (csi2->stream_count)
++			return 0;
++
++		ipu6_isys_csi2_set_stream(sd, &timing, 0, enable);
++		return 0;
++	}
++
++	if (csi2->stream_count) {
++		csi2->stream_count++;
++		return 0;
++	}
++
++	nlanes = csi2->nlanes;
++
++	ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);
++	if (ret)
++		return ret;
++
++	ret = ipu6_isys_csi2_set_stream(sd, &timing, nlanes, enable);
++	if (ret)
++		return ret;
++
++	csi2->stream_count++;
++
++	return 0;
++}
++
++static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,
++				  struct v4l2_subdev_state *state,
++				  struct v4l2_subdev_selection *sel)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct device *dev = &asd->isys->adev->auxdev.dev;
++	struct v4l2_mbus_framefmt *sink_ffmt;
++	struct v4l2_mbus_framefmt *src_ffmt;
++	struct v4l2_rect *crop;
++
++	if (sel->pad == CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
++		return -EINVAL;
++
++	sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
++								 sel->pad,
++								 sel->stream);
++	if (!sink_ffmt)
++		return -EINVAL;
++
++	src_ffmt = v4l2_subdev_state_get_stream_format(state, sel->pad,
++						       sel->stream);
++	if (!src_ffmt)
++		return -EINVAL;
++
++	crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream);
++	if (!crop)
++		return -EINVAL;
++
++	/* Only vertical cropping is supported */
++	sel->r.left = 0;
++	sel->r.width = sink_ffmt->width;
++	/* Non-bayer formats can't be single line cropped */
++	if (!ipu6_isys_is_bayer_format(sink_ffmt->code))
++		sel->r.top &= ~1;
++	sel->r.height = clamp(sel->r.height & ~1, IPU6_ISYS_MIN_HEIGHT,
++			      sink_ffmt->height - sel->r.top);
++	*crop = sel->r;
++
++	/* update source pad format */
++	src_ffmt->width = sel->r.width;
++	src_ffmt->height = sel->r.height;
++	if (ipu6_isys_is_bayer_format(sink_ffmt->code))
++		src_ffmt->code = ipu6_isys_convert_bayer_order(sink_ffmt->code,
++							       sel->r.left,
++							       sel->r.top);
++	dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n",
++		sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height,
++		src_ffmt->code);
++
++	return 0;
++}
++
++static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd,
++				  struct v4l2_subdev_state *state,
++				  struct v4l2_subdev_selection *sel)
++{
++	struct v4l2_mbus_framefmt *sink_ffmt;
++	struct v4l2_rect *crop;
++	int ret = 0;
++
++	if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK)
++		return -EINVAL;
++
++	sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
++								 sel->pad,
++								 sel->stream);
++	if (!sink_ffmt)
++		return -EINVAL;
++
++	crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream);
++	if (!crop)
++		return -EINVAL;
++
++	switch (sel->target) {
++	case V4L2_SEL_TGT_CROP_DEFAULT:
++	case V4L2_SEL_TGT_CROP_BOUNDS:
++		sel->r.left = 0;
++		sel->r.top = 0;
++		sel->r.width = sink_ffmt->width;
++		sel->r.height = sink_ffmt->height;
++		break;
++	case V4L2_SEL_TGT_CROP:
++		sel->r = *crop;
++		break;
++	default:
++		ret = -EINVAL;
++	}
++
++	return ret;
++}
++
++static const struct v4l2_subdev_video_ops csi2_sd_video_ops = {
++	.s_stream = set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
++	.init_cfg = ipu6_isys_subdev_init_cfg,
++	.get_fmt = v4l2_subdev_get_fmt,
++	.set_fmt = ipu6_isys_subdev_set_fmt,
++	.get_selection = ipu6_isys_csi2_get_sel,
++	.set_selection = ipu6_isys_csi2_set_sel,
++	.enum_mbus_code = ipu6_isys_subdev_enum_mbus_code,
++	.set_routing = ipu6_isys_subdev_set_routing,
++};
++
++static const struct v4l2_subdev_ops csi2_sd_ops = {
++	.core = &csi2_sd_core_ops,
++	.video = &csi2_sd_video_ops,
++	.pad = &csi2_sd_pad_ops,
++};
++
++static const struct media_entity_operations csi2_entity_ops = {
++	.link_validate = v4l2_subdev_link_validate,
++	.has_pad_interdep = v4l2_subdev_has_pad_interdep,
++};
++
++void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2)
++{
++	if (!csi2->isys)
++		return;
++
++	v4l2_device_unregister_subdev(&csi2->asd.sd);
++	v4l2_subdev_cleanup(&csi2->asd.sd);
++	ipu6_isys_subdev_cleanup(&csi2->asd);
++	csi2->isys = NULL;
++}
++
++int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2,
++			struct ipu6_isys *isys,
++			void __iomem *base, unsigned int index)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	int ret;
++
++	csi2->isys = isys;
++	csi2->base = base;
++	csi2->port = index;
++
++	csi2->asd.sd.entity.ops = &csi2_entity_ops;
++	csi2->asd.isys = isys;
++	ret = ipu6_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0,
++				    NR_OF_CSI2_SINK_PADS, NR_OF_CSI2_SRC_PADS);
++	if (ret)
++		goto fail;
++
++	csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index;
++	csi2->asd.supported_codes = csi2_supported_codes;
++	snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name),
++		 IPU6_ISYS_ENTITY_PREFIX " CSI2 %u", index);
++	v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd);
++	ret = v4l2_subdev_init_finalize(&csi2->asd.sd);
++	if (ret) {
++		dev_err(dev, "failed to init v4l2 subdev\n");
++		goto fail;
++	}
++
++	ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd);
++	if (ret) {
++		dev_err(dev, "failed to register v4l2 subdev\n");
++		goto fail;
++	}
++
++	return 0;
++
++fail:
++	ipu6_isys_csi2_cleanup(csi2);
++
++	return ret;
++}
++
++void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream)
++{
++	struct video_device *vdev = stream->asd->sd.devnode;
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
++	struct v4l2_event ev = {
++		.type = V4L2_EVENT_FRAME_SYNC,
++	};
++
++	ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence);
++	v4l2_event_queue(vdev, &ev);
++
++	dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",
++		csi2->port, ev.u.frame_sync.frame_sequence, stream->vc);
++}
++
++void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream)
++{
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
++	u32 frame_sequence = atomic_read(&stream->sequence);
++
++	dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n",
++		csi2->port, frame_sequence);
++}
++
++int ipu6_isys_csi2_get_remote_desc(u32 source_stream,
++				   struct ipu6_isys_csi2 *csi2,
++				   struct media_entity *source_entity,
++				   struct v4l2_mbus_frame_desc_entry *entry,
++				   int *nr_queues)
++{
++	struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
++	struct device *dev = &csi2->isys->adev->auxdev.dev;
++	struct v4l2_mbus_frame_desc desc;
++	struct v4l2_subdev *source;
++	struct media_pad *pad;
++	unsigned int i;
++	int count = 0;
++	int ret;
++
++	source = media_entity_to_v4l2_subdev(source_entity);
++	if (!source)
++		return -EPIPE;
++
++	pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]);
++	if (!pad)
++		return -EPIPE;
++
++	ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc);
++	if (ret)
++		return ret;
++
++	if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
++		dev_err(dev, "Unsupported frame descriptor type\n");
++		return -EINVAL;
++	}
++
++	for (i = 0; i < desc.num_entries; i++) {
++		if (source_stream == desc.entry[i].stream) {
++			desc_entry = &desc.entry[i];
++			break;
++		}
++	}
++
++	if (!desc_entry) {
++		dev_err(dev, "Failed to find stream %u from remote subdev\n",
++			source_stream);
++		return -EINVAL;
++	}
++
++	if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) {
++		dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc);
++		return -EINVAL;
++	}
++
++	*entry = *desc_entry;
++	for (i = 0; i < desc.num_entries; i++) {
++		if (entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc)
++			count++;
++	}
++
++	*nr_queues = count;
++	return 0;
++}
++
++void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status)
++{
++	struct ipu6_isys_stream *stream = av->stream;
++	struct v4l2_subdev *sd = &stream->asd->sd;
++	struct v4l2_subdev_state *state;
++	struct media_pad *r_pad;
++	unsigned int i;
++	u32 r_stream;
++
++	r_pad = media_pad_remote_pad_first(&av->pad);
++	r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
++
++	state = v4l2_subdev_lock_and_get_active_state(sd);
++
++	for (i = 0; i < state->stream_configs.num_configs; i++) {
++		struct v4l2_subdev_stream_config *cfg =
++			&state->stream_configs.configs[i];
++
++		if (cfg->pad == r_pad->index && r_stream == cfg->stream) {
++			dev_dbg(&av->isys->adev->auxdev.dev,
++				"%s: pad:%u, stream:%u, status:%u\n",
++				sd->entity.name, r_pad->index, r_stream,
++				status);
++			cfg->enabled = status;
++		}
++	}
++
++	v4l2_subdev_unlock_state(state);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
+new file mode 100644
+index 000000000000..d4765bae6112
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
+@@ -0,0 +1,81 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_CSI2_H
++#define IPU6_ISYS_CSI2_H
++
++#include <linux/container_of.h>
++
++#include "ipu6-isys-subdev.h"
++
++struct media_entity;
++struct v4l2_mbus_frame_desc_entry;
++
++struct ipu6_isys_video;
++struct ipu6_isys;
++struct ipu6_isys_csi2_pdata;
++struct ipu6_isys_stream;
++
++#define NR_OF_CSI2_VC			16
++#define INVALID_VC_ID			-1
++#define NR_OF_CSI2_SINK_PADS		1
++#define CSI2_PAD_SINK			0
++#define NR_OF_CSI2_SRC_PADS		8
++#define CSI2_PAD_SRC			1
++#define NR_OF_CSI2_PADS	(NR_OF_CSI2_SINK_PADS + NR_OF_CSI2_SRC_PADS)
++
++#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A		0
++#define CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B		0
++#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A		95
++#define CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B		-8
++
++#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A		0
++#define CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B		0
++#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A		85
++#define CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B		-2
++
++struct ipu6_isys_csi2 {
++	struct ipu6_isys_subdev asd;
++	struct ipu6_isys_csi2_pdata *pdata;
++	struct ipu6_isys *isys;
++
++	void __iomem *base;
++	u32 receiver_errors;
++	unsigned int nlanes;
++	unsigned int port;
++	unsigned int stream_count;
++};
++
++struct ipu6_isys_csi2_timing {
++	u32 ctermen;
++	u32 csettle;
++	u32 dtermen;
++	u32 dsettle;
++};
++
++struct ipu6_csi2_error {
++	const char *error_string;
++	bool is_info_only;
++};
++
++#define ipu6_isys_subdev_to_csi2(__sd) \
++	container_of(__sd, struct ipu6_isys_csi2, asd)
++
++#define to_ipu6_isys_csi2(__asd) container_of(__asd, struct ipu6_isys_csi2, asd)
++
++s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2);
++int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2, struct ipu6_isys *isys,
++			void __iomem *base, unsigned int index);
++void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2);
++void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream);
++void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream);
++void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2);
++void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2);
++int ipu6_isys_csi2_get_remote_desc(u32 source_stream,
++				   struct ipu6_isys_csi2 *csi2,
++				   struct media_entity *source_entity,
++				   struct v4l2_mbus_frame_desc_entry *entry,
++				   int *nr_queues);
++void ipu6_isys_set_csi2_streams_status(struct ipu6_isys_video *av, bool status);
++
++#endif /* IPU6_ISYS_CSI2_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+new file mode 100644
+index 000000000000..510c5ca34f9f
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+@@ -0,0 +1,381 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bug.h>
++#include <linux/device.h>
++#include <linux/minmax.h>
++
++#include <media/media-entity.h>
++#include <media/mipi-csi2.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-subdev.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-subdev.h"
++
++unsigned int ipu6_isys_mbus_code_to_bpp(u32 code)
++{
++	switch (code) {
++	case MEDIA_BUS_FMT_RGB888_1X24:
++		return 24;
++	case MEDIA_BUS_FMT_RGB565_1X16:
++	case MEDIA_BUS_FMT_UYVY8_1X16:
++	case MEDIA_BUS_FMT_YUYV8_1X16:
++		return 16;
++	case MEDIA_BUS_FMT_SBGGR12_1X12:
++	case MEDIA_BUS_FMT_SGBRG12_1X12:
++	case MEDIA_BUS_FMT_SGRBG12_1X12:
++	case MEDIA_BUS_FMT_SRGGB12_1X12:
++		return 12;
++	case MEDIA_BUS_FMT_SBGGR10_1X10:
++	case MEDIA_BUS_FMT_SGBRG10_1X10:
++	case MEDIA_BUS_FMT_SGRBG10_1X10:
++	case MEDIA_BUS_FMT_SRGGB10_1X10:
++		return 10;
++	case MEDIA_BUS_FMT_SBGGR8_1X8:
++	case MEDIA_BUS_FMT_SGBRG8_1X8:
++	case MEDIA_BUS_FMT_SGRBG8_1X8:
++	case MEDIA_BUS_FMT_SRGGB8_1X8:
++		return 8;
++	default:
++		WARN_ON(1);
++		return 8;
++	}
++}
++
++unsigned int ipu6_isys_mbus_code_to_mipi(u32 code)
++{
++	switch (code) {
++	case MEDIA_BUS_FMT_RGB565_1X16:
++		return MIPI_CSI2_DT_RGB565;
++	case MEDIA_BUS_FMT_RGB888_1X24:
++		return MIPI_CSI2_DT_RGB888;
++	case MEDIA_BUS_FMT_UYVY8_1X16:
++	case MEDIA_BUS_FMT_YUYV8_1X16:
++		return MIPI_CSI2_DT_YUV422_8B;
++	case MEDIA_BUS_FMT_SBGGR12_1X12:
++	case MEDIA_BUS_FMT_SGBRG12_1X12:
++	case MEDIA_BUS_FMT_SGRBG12_1X12:
++	case MEDIA_BUS_FMT_SRGGB12_1X12:
++		return MIPI_CSI2_DT_RAW12;
++	case MEDIA_BUS_FMT_SBGGR10_1X10:
++	case MEDIA_BUS_FMT_SGBRG10_1X10:
++	case MEDIA_BUS_FMT_SGRBG10_1X10:
++	case MEDIA_BUS_FMT_SRGGB10_1X10:
++		return MIPI_CSI2_DT_RAW10;
++	case MEDIA_BUS_FMT_SBGGR8_1X8:
++	case MEDIA_BUS_FMT_SGBRG8_1X8:
++	case MEDIA_BUS_FMT_SGRBG8_1X8:
++	case MEDIA_BUS_FMT_SRGGB8_1X8:
++		return MIPI_CSI2_DT_RAW8;
++	default:
++		/* return unavailable MIPI data type - 0x3f */
++		WARN_ON(1);
++		return 0x3f;
++	}
++}
++
++bool ipu6_isys_is_bayer_format(u32 code)
++{
++	switch (ipu6_isys_mbus_code_to_mipi(code)) {
++	case MIPI_CSI2_DT_RAW8:
++	case MIPI_CSI2_DT_RAW10:
++	case MIPI_CSI2_DT_RAW12:
++		return true;
++	default:
++		return false;
++	}
++}
++
++u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y)
++{
++	static const u32 code_map[] = {
++		MEDIA_BUS_FMT_SRGGB8_1X8,
++		MEDIA_BUS_FMT_SGRBG8_1X8,
++		MEDIA_BUS_FMT_SGBRG8_1X8,
++		MEDIA_BUS_FMT_SBGGR8_1X8,
++		MEDIA_BUS_FMT_SRGGB10_1X10,
++		MEDIA_BUS_FMT_SGRBG10_1X10,
++		MEDIA_BUS_FMT_SGBRG10_1X10,
++		MEDIA_BUS_FMT_SBGGR10_1X10,
++		MEDIA_BUS_FMT_SRGGB12_1X12,
++		MEDIA_BUS_FMT_SGRBG12_1X12,
++		MEDIA_BUS_FMT_SGBRG12_1X12,
++		MEDIA_BUS_FMT_SBGGR12_1X12
++	};
++	u32 i;
++
++	for (i = 0; i < ARRAY_SIZE(code_map); i++)
++		if (code_map[i] == code)
++			break;
++
++	if (WARN_ON(i == ARRAY_SIZE(code_map)))
++		return code;
++
++	return code_map[i ^ (((y & 1) << 1) | (x & 1))];
++}
++
++int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
++			     struct v4l2_subdev_state *state,
++			     struct v4l2_subdev_format *format)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	struct v4l2_mbus_framefmt *fmt;
++	struct v4l2_rect *crop;
++	u32 code = asd->supported_codes[0];
++	u32 other_pad, other_stream;
++	unsigned int i;
++	int ret;
++
++	/* No transcoding, source and sink formats must match. */
++	if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) &&
++	    sd->entity.num_pads > 1)
++		return v4l2_subdev_get_fmt(sd, state, format);
++
++	format->format.width = clamp(format->format.width, IPU6_ISYS_MIN_WIDTH,
++				     IPU6_ISYS_MAX_WIDTH);
++	format->format.height = clamp(format->format.height,
++				      IPU6_ISYS_MIN_HEIGHT,
++				      IPU6_ISYS_MAX_HEIGHT);
++
++	for (i = 0; asd->supported_codes[i]; i++) {
++		if (asd->supported_codes[i] == format->format.code) {
++			code = asd->supported_codes[i];
++			break;
++		}
++	}
++	format->format.code = code;
++	format->format.field = V4L2_FIELD_NONE;
++
++	/* Store the format and propagate it to the source pad. */
++	fmt = v4l2_subdev_state_get_stream_format(state, format->pad,
++						  format->stream);
++	if (!fmt)
++		return -EINVAL;
++
++	*fmt = format->format;
++
++	if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK))
++		return 0;
++
++	/* propagate format to following source pad */
++	fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
++							   format->stream);
++	if (!fmt)
++		return -EINVAL;
++
++	*fmt = format->format;
++
++	ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
++						    format->pad,
++						    format->stream,
++						    &other_pad,
++						    &other_stream);
++	if (ret)
++		return -EINVAL;
++
++	crop = v4l2_subdev_state_get_stream_crop(state, other_pad,
++						 other_stream);
++	/* reset crop */
++	crop->left = 0;
++	crop->top = 0;
++	crop->width = fmt->width;
++	crop->height = fmt->height;
++
++	return 0;
++}
++
++int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
++				    struct v4l2_subdev_state *state,
++				    struct v4l2_subdev_mbus_code_enum *code)
++{
++	struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
++	const u32 *supported_codes = asd->supported_codes;
++	u32 index;
++
++	for (index = 0; supported_codes[index]; index++) {
++		if (index == code->index) {
++			code->code = supported_codes[index];
++			return 0;
++		}
++	}
++
++	return -EINVAL;
++}
++
++static int subdev_set_routing(struct v4l2_subdev *sd,
++			      struct v4l2_subdev_state *state,
++			      struct v4l2_subdev_krouting *routing)
++{
++	static const struct v4l2_mbus_framefmt format = {
++		.width = 4096,
++		.height = 3072,
++		.code = MEDIA_BUS_FMT_SGRBG10_1X10,
++		.field = V4L2_FIELD_NONE,
++	};
++	int ret;
++
++	ret = v4l2_subdev_routing_validate(sd, routing,
++					   V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
++	if (ret)
++		return ret;
++
++	return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
++}
++
++int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
++				 struct v4l2_mbus_framefmt *format)
++{
++	struct v4l2_mbus_framefmt *fmt;
++	struct v4l2_subdev_state *state;
++
++	if (!sd || !format)
++		return -EINVAL;
++
++	state = v4l2_subdev_lock_and_get_active_state(sd);
++	fmt = v4l2_subdev_state_get_stream_format(state, pad, stream);
++	if (fmt)
++		*format = *fmt;
++	v4l2_subdev_unlock_state(state);
++
++	return fmt ? 0 : -EINVAL;
++}
++
++int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
++				  struct v4l2_rect *crop)
++{
++	struct v4l2_subdev_state *state;
++	struct v4l2_rect *rect;
++
++	if (!sd || !crop)
++		return -EINVAL;
++
++	state = v4l2_subdev_lock_and_get_active_state(sd);
++	rect = v4l2_subdev_state_get_stream_crop(state, pad, stream);
++	if (rect)
++		*crop = *rect;
++	v4l2_subdev_unlock_state(state);
++
++	return rect ? 0 : -EINVAL;
++}
++
++u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
++{
++	struct v4l2_subdev_state *state;
++	struct v4l2_subdev_route *routes;
++	unsigned int i;
++	u32 source_stream = 0;
++
++	state = v4l2_subdev_lock_and_get_active_state(sd);
++	if (!state)
++		return 0;
++
++	routes = state->routing.routes;
++	for (i = 0; i < state->routing.num_routes; i++) {
++		if (routes[i].source_pad == pad) {
++			source_stream = routes[i].source_stream;
++			break;
++		}
++	}
++
++	v4l2_subdev_unlock_state(state);
++
++	return source_stream;
++}
++
++int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd,
++			      struct v4l2_subdev_state *state)
++{
++	struct v4l2_subdev_route route = {
++		.sink_pad = 0,
++		.sink_stream = 0,
++		.source_pad = 1,
++		.source_stream = 0,
++		.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
++	};
++	struct v4l2_subdev_krouting routing = {
++		.num_routes = 1,
++		.routes = &route,
++	};
++
++	return subdev_set_routing(sd, state, &routing);
++}
++
++int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
++				 struct v4l2_subdev_state *state,
++				 enum v4l2_subdev_format_whence which,
++				 struct v4l2_subdev_krouting *routing)
++{
++	return subdev_set_routing(sd, state, routing);
++}
++
++int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
++			  const struct v4l2_subdev_ops *ops,
++			  unsigned int nr_ctrls,
++			  unsigned int num_sink_pads,
++			  unsigned int num_source_pads)
++{
++	unsigned int num_pads = num_sink_pads + num_source_pads;
++	unsigned int i;
++	int ret;
++
++	v4l2_subdev_init(&asd->sd, ops);
++
++	asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
++			 V4L2_SUBDEV_FL_HAS_EVENTS |
++			 V4L2_SUBDEV_FL_STREAMS;
++	asd->sd.owner = THIS_MODULE;
++	asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
++
++	asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
++				sizeof(*asd->pad), GFP_KERNEL);
++
++	if (!asd->pad)
++		return -ENOMEM;
++
++	for (i = 0; i < num_sink_pads; i++)
++		asd->pad[i].flags = MEDIA_PAD_FL_SINK |
++				    MEDIA_PAD_FL_MUST_CONNECT;
++
++	for (i = num_sink_pads; i < num_pads; i++)
++		asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;
++
++	ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
++	if (ret)
++		return ret;
++
++	if (asd->ctrl_init) {
++		ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
++		if (ret)
++			goto out_media_entity_cleanup;
++
++		asd->ctrl_init(&asd->sd);
++		if (asd->ctrl_handler.error) {
++			ret = asd->ctrl_handler.error;
++			goto out_v4l2_ctrl_handler_free;
++		}
++
++		asd->sd.ctrl_handler = &asd->ctrl_handler;
++	}
++
++	asd->source = -1;
++
++	return 0;
++
++out_v4l2_ctrl_handler_free:
++	v4l2_ctrl_handler_free(&asd->ctrl_handler);
++
++out_media_entity_cleanup:
++	media_entity_cleanup(&asd->sd.entity);
++
++	return ret;
++}
++
++void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd)
++{
++	media_entity_cleanup(&asd->sd.entity);
++	v4l2_ctrl_handler_free(&asd->ctrl_handler);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+new file mode 100644
+index 000000000000..adea2a55761d
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+@@ -0,0 +1,61 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_SUBDEV_H
++#define IPU6_ISYS_SUBDEV_H
++
++#include <linux/container_of.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-subdev.h>
++
++struct ipu6_isys;
++
++struct ipu6_isys_subdev {
++	struct v4l2_subdev sd;
++	struct ipu6_isys *isys;
++	u32 const *supported_codes;
++	struct media_pad *pad;
++	struct v4l2_ctrl_handler ctrl_handler;
++	void (*ctrl_init)(struct v4l2_subdev *sd);
++	int source;	/* SSI stream source; -1 if unset */
++};
++
++#define to_ipu6_isys_subdev(__sd) \
++	container_of(__sd, struct ipu6_isys_subdev, sd)
++
++unsigned int ipu6_isys_mbus_code_to_bpp(u32 code);
++unsigned int ipu6_isys_mbus_code_to_mipi(u32 code);
++bool ipu6_isys_is_bayer_format(u32 code);
++u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y);
++
++int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
++			     struct v4l2_subdev_state *state,
++			     struct v4l2_subdev_format *fmt);
++int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
++				    struct v4l2_subdev_state *state,
++				    struct v4l2_subdev_mbus_code_enum
++				    *code);
++int ipu6_isys_subdev_link_validate(struct v4l2_subdev *sd,
++				   struct media_link *link,
++				   struct v4l2_subdev_format *source_fmt,
++				   struct v4l2_subdev_format *sink_fmt);
++u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad);
++int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
++				 struct v4l2_mbus_framefmt *format);
++int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
++				  struct v4l2_rect *crop);
++int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd,
++			      struct v4l2_subdev_state *state);
++int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
++				 struct v4l2_subdev_state *state,
++				 enum v4l2_subdev_format_whence which,
++				 struct v4l2_subdev_krouting *routing);
++int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
++			  const struct v4l2_subdev_ops *ops,
++			  unsigned int nr_ctrls,
++			  unsigned int num_sink_pads,
++			  unsigned int num_source_pads);
++void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd);
++#endif /* IPU6_ISYS_SUBDEV_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h b/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h
+new file mode 100644
+index 000000000000..2034e1109d98
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-platform-isys-csi2-reg.h
+@@ -0,0 +1,189 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2023 Intel Corporation */
++
++#ifndef IPU6_PLATFORM_ISYS_CSI2_REG_H
++#define IPU6_PLATFORM_ISYS_CSI2_REG_H
++
++#include <linux/bits.h>
++
++#define CSI_REG_BASE			0x220000
++#define CSI_REG_BASE_PORT(id)		((id) * 0x1000)
++
++#define IPU6_CSI_PORT_A_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(0))
++#define IPU6_CSI_PORT_B_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(1))
++#define IPU6_CSI_PORT_C_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(2))
++#define IPU6_CSI_PORT_D_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(3))
++#define IPU6_CSI_PORT_E_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(4))
++#define IPU6_CSI_PORT_F_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(5))
++#define IPU6_CSI_PORT_G_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(6))
++#define IPU6_CSI_PORT_H_ADDR_OFFSET	\
++		(CSI_REG_BASE + CSI_REG_BASE_PORT(7))
++
++/* CSI Port Genral Purpose Registers */
++#define CSI_REG_PORT_GPREG_SRST                 0x0
++#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST    0x4
++#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL    0x8
++
++/*
++ * Port IRQs mapping events:
++ * IRQ0 - CSI_FE event
++ * IRQ1 - CSI_SYNC
++ * IRQ2 - S2M_SIDS0TO7
++ * IRQ3 - S2M_SIDS8TO15
++ */
++#define CSI_PORT_REG_BASE_IRQ_CSI               0x80
++#define CSI_PORT_REG_BASE_IRQ_CSI_SYNC          0xA0
++#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS0TOS7     0xC0
++#define CSI_PORT_REG_BASE_IRQ_S2M_SIDS8TOS15    0xE0
++
++#define CSI_PORT_REG_BASE_IRQ_EDGE_OFFSET	0x0
++#define CSI_PORT_REG_BASE_IRQ_MASK_OFFSET	0x4
++#define CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET	0x8
++#define CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET	0xc
++#define CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET	0x10
++#define CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET	0x14
++
++#define IPU6SE_CSI_RX_ERROR_IRQ_MASK		GENMASK(18, 0)
++#define IPU6_CSI_RX_ERROR_IRQ_MASK		GENMASK(19, 0)
++
++#define CSI_RX_NUM_ERRORS_IN_IRQ		20
++#define CSI_RX_NUM_IRQ				32
++
++#define IPU_CSI_RX_IRQ_FS_VC(chn)	(1 << ((chn) * 2))
++#define IPU_CSI_RX_IRQ_FE_VC(chn)	(2 << ((chn) * 2))
++
++/* PPI2CSI */
++#define CSI_REG_PPI2CSI_ENABLE				0x200
++#define CSI_REG_PPI2CSI_CONFIG_PPI_INTF			0x204
++#define PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK		GENMASK(4, 3)
++#define CSI_REG_PPI2CSI_CONFIG_CSI_FEATURE		0x208
++
++enum CSI_PPI2CSI_CTRL {
++	CSI_PPI2CSI_DISABLE = 0,
++	CSI_PPI2CSI_ENABLE = 1,
++};
++
++/* CSI_FE */
++#define CSI_REG_CSI_FE_ENABLE                   0x280
++#define CSI_REG_CSI_FE_MODE                     0x284
++#define CSI_REG_CSI_FE_MUX_CTRL                 0x288
++#define CSI_REG_CSI_FE_SYNC_CNTR_SEL            0x290
++
++enum CSI_FE_ENABLE_TYPE {
++	CSI_FE_DISABLE = 0,
++	CSI_FE_ENABLE = 1,
++};
++
++enum CSI_FE_MODE_TYPE {
++	CSI_FE_DPHY_MODE = 0,
++	CSI_FE_CPHY_MODE = 1,
++};
++
++enum CSI_FE_INPUT_SELECTOR {
++	CSI_SENSOR_INPUT = 0,
++	CSI_MIPIGEN_INPUT = 1,
++};
++
++enum CSI_FE_SYNC_CNTR_SEL_TYPE {
++	CSI_CNTR_SENSOR_LINE_ID = BIT(0),
++	CSI_CNTR_INT_LINE_PKT_ID = ~CSI_CNTR_SENSOR_LINE_ID,
++	CSI_CNTR_SENSOR_FRAME_ID = BIT(1),
++	CSI_CNTR_INT_FRAME_PKT_ID = ~CSI_CNTR_SENSOR_FRAME_ID,
++};
++
++/* CSI HUB General Purpose Registers */
++#define CSI_REG_HUB_GPREG_SRST			(CSI_REG_BASE + 0x18000)
++#define CSI_REG_HUB_GPREG_SLV_REG_SRST		(CSI_REG_BASE + 0x18004)
++
++#define CSI_REG_HUB_DRV_ACCESS_PORT(id)	(CSI_REG_BASE + 0x18018 + (id) * 4)
++#define CSI_REG_HUB_FW_ACCESS_PORT_OFS		0x17000
++#define CSI_REG_HUB_FW_ACCESS_PORT_V6OFS	0x16000
++#define CSI_REG_HUB_FW_ACCESS_PORT(ofs, id)	\
++					(CSI_REG_BASE + (ofs) + (id) * 4)
++
++enum CSI_PORT_CLK_GATING_SWITCH {
++	CSI_PORT_CLK_GATING_OFF = 0,
++	CSI_PORT_CLK_GATING_ON = 1,
++};
++
++#define CSI_REG_BASE_HUB_IRQ                        0x18200
++
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE			0x238200
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK			0x238204
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS			0x238208
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR			0x23820c
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE			0x238210
++#define IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE		0x238214
++
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE			0x238220
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK			0x238224
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS			0x238228
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR			0x23822c
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE			0x238230
++#define IPU6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE		0x238234
++
++/* MTL IPU6V6 irq ctrl0 & ctrl1 */
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_EDGE			0x238700
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_MASK			0x238704
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS		0x238708
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_CLEAR			0x23870c
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_ENABLE		0x238710
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE	0x238714
++
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_EDGE			0x238720
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_MASK			0x238724
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_STATUS		0x238728
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_CLEAR			0x23872c
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_ENABLE		0x238730
++#define IPU6V6_REG_ISYS_CSI_TOP_CTRL1_IRQ_LEVEL_NOT_PULSE	0x238734
++
++/*
++ * 3:0 CSI_PORT.irq_out[3:0] CSI_PORT_CTRL0 IRQ outputs (4bits)
++ * [0] CSI_PORT.IRQ_CTRL0_csi
++ * [1] CSI_PORT.IRQ_CTRL1_csi_sync
++ * [2] CSI_PORT.IRQ_CTRL2_s2m_sids0to7
++ * [3] CSI_PORT.IRQ_CTRL3_s2m_sids8to15
++ */
++#define IPU6_ISYS_UNISPART_IRQ_CSI2(port)		\
++				   (0x3 << ((port) * IPU6_CSI_IRQ_NUM_PER_PIPE))
++
++/*
++ * ipu6se support 2 front ends, 2 port per front end, 4 ports 0..3
++ * sip0 - 0, 1
++ * sip1 - 2, 3
++ * 0 and 2 support 4 data lanes, 1 and 3 support 2 data lanes
++ * all offset are base from isys base address
++ */
++
++#define CSI2_HUB_GPREG_SIP_SRST(sip)			(0x238038 + (sip) * 4)
++#define CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip)		(0x238050 + (sip) * 4)
++
++#define CSI2_HUB_GPREG_DPHY_TIMER_INCR			0x238040
++#define CSI2_HUB_GPREG_HPLL_FREQ			0x238044
++#define CSI2_HUB_GPREG_IS_CLK_RATIO			0x238048
++#define CSI2_HUB_GPREG_HPLL_FREQ_ISCLK_RATE_OVERRIDE	0x23804c
++#define CSI2_HUB_GPREG_PORT_CLKGATING_DISABLE		0x238058
++#define CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL		0x23805c
++#define CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL		0x238088
++#define CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL		0x2380a4
++#define CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL		0x2380d0
++
++#define CSI2_SIP_TOP_CSI_RX_BASE(sip)		(0x23805c + (sip) * 0x48)
++#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port)	(0x23805c + ((port) / 2) * 0x48)
++#define CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port)	(0x238088 + ((port) / 2) * 0x48)
++
++/* offset from port base */
++#define CSI2_SIP_TOP_CSI_RX_PORT_CONTROL		0x0
++#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE	0x4
++#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE	0x8
++#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(lane)	(0xc + (lane) * 8)
++#define CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(lane)	(0x10 + (lane) * 8)
++
++#endif /* IPU6_ISYS_CSI2_REG_H */
+-- 
+2.43.2
+
+
+From 71d3da5e8f1ea094e26030a12ceed4553cbe182a Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:23 +0800
+Subject: [PATCH 16/33] media: intel/ipu6: add the CSI2 DPHY implementation
+
+IPU6 CSI2 DPHY hardware varies on different platforms, current
+IPU6 has three DPHY hardware instance which maybe used on tigerlake,
+alderlake, metorlake and jasperlake. MCD DPHY is shipped on tigerlake
+and alderlake, DWC DPHY is shipped on metorlake.
+
+Each PHY has its own register space, input system driver call the
+DPHY callback which was set at isys_probe().
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ .../media/pci/intel/ipu6/ipu6-isys-dwc-phy.c  | 536 +++++++++++++
+ .../media/pci/intel/ipu6/ipu6-isys-jsl-phy.c  | 242 ++++++
+ .../media/pci/intel/ipu6/ipu6-isys-mcd-phy.c  | 720 ++++++++++++++++++
+ 3 files changed, 1498 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
+new file mode 100644
+index 000000000000..a4bb5ff51d4e
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
+@@ -0,0 +1,536 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/iopoll.h>
++#include <linux/math64.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++
++#define IPU6_DWC_DPHY_BASE(i)			(0x238038 + 0x34 * (i))
++#define IPU6_DWC_DPHY_RSTZ			0x00
++#define IPU6_DWC_DPHY_SHUTDOWNZ			0x04
++#define IPU6_DWC_DPHY_HSFREQRANGE		0x08
++#define IPU6_DWC_DPHY_CFGCLKFREQRANGE		0x0c
++#define IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE	0x10
++#define IPU6_DWC_DPHY_TEST_IFC_REQ		0x14
++#define IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION	0x18
++#define IPU6_DWC_DPHY_DFT_CTRL0			0x28
++#define IPU6_DWC_DPHY_DFT_CTRL1			0x2c
++#define IPU6_DWC_DPHY_DFT_CTRL2			0x30
++
++/*
++ * test IFC request definition:
++ * - req: 0 for read, 1 for write
++ * - 12 bits address
++ * - 8bits data (will ignore for read)
++ * --24----16------4-----0
++ * --|-data-|-addr-|-req-|
++ */
++#define IFC_REQ(req, addr, data) (FIELD_PREP(GENMASK(23, 16), data) | \
++				  FIELD_PREP(GENMASK(15, 4), addr) | \
++				  FIELD_PREP(GENMASK(1, 0), req))
++
++#define TEST_IFC_REQ_READ	0
++#define TEST_IFC_REQ_WRITE	1
++#define TEST_IFC_REQ_RESET	2
++
++#define TEST_IFC_ACCESS_MODE_FSM	0
++#define TEST_IFC_ACCESS_MODE_IFC_CTL	1
++
++enum phy_fsm_state {
++	PHY_FSM_STATE_POWERON = 0,
++	PHY_FSM_STATE_BGPON = 1,
++	PHY_FSM_STATE_CAL_TYPE = 2,
++	PHY_FSM_STATE_BURNIN_CAL = 3,
++	PHY_FSM_STATE_TERMCAL = 4,
++	PHY_FSM_STATE_OFFSETCAL = 5,
++	PHY_FSM_STATE_OFFSET_LANE = 6,
++	PHY_FSM_STATE_IDLE = 7,
++	PHY_FSM_STATE_ULP = 8,
++	PHY_FSM_STATE_DDLTUNNING = 9,
++	PHY_FSM_STATE_SKEW_BACKWARD = 10,
++	PHY_FSM_STATE_INVALID,
++};
++
++static void dwc_dphy_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++			   u32 data)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
++
++	dev_dbg(dev, "write: reg 0x%lx = data 0x%x", base + addr - isys_base,
++		data);
++	writel(data, base + addr);
++}
++
++static u32 dwc_dphy_read(struct ipu6_isys *isys, u32 phy_id, u32 addr)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
++	u32 data;
++
++	data = readl(base + addr);
++	dev_dbg(dev, "read: reg 0x%lx = data 0x%x", base + addr - isys_base,
++		data);
++
++	return data;
++}
++
++static void dwc_dphy_write_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++				u32 data, u8 shift, u8 width)
++{
++	u32 temp;
++	u32 mask;
++
++	mask = (1 << width) - 1;
++	temp = dwc_dphy_read(isys, phy_id, addr);
++	temp &= ~(mask << shift);
++	temp |= (data & mask) << shift;
++	dwc_dphy_write(isys, phy_id, addr, temp);
++}
++
++static u32 __maybe_unused dwc_dphy_read_mask(struct ipu6_isys *isys, u32 phy_id,
++					     u32 addr, u8 shift,  u8 width)
++{
++	u32 val;
++
++	val = dwc_dphy_read(isys, phy_id, addr) >> shift;
++	return val & ((1 << width) - 1);
++}
++
++#define DWC_DPHY_TIMEOUT (5 * USEC_PER_SEC)
++static int dwc_dphy_ifc_read(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++			     u32 *val)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
++	void __iomem *reg;
++	u32 completion;
++	int ret;
++
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
++		       IFC_REQ(TEST_IFC_REQ_READ, addr, 0));
++	reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
++	ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
++				 10, DWC_DPHY_TIMEOUT);
++	if (ret) {
++		dev_err(dev, "DWC ifc request read timeout\n");
++		return ret;
++	}
++
++	*val = completion >> 8 & 0xff;
++	*val = FIELD_GET(GENMASK(15, 8), completion);
++	dev_dbg(dev, "DWC ifc read 0x%x = 0x%x", addr, *val);
++
++	return 0;
++}
++
++static int dwc_dphy_ifc_write(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++			      u32 data)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	void __iomem *base = isys_base + IPU6_DWC_DPHY_BASE(phy_id);
++	void __iomem *reg;
++	u32 completion;
++	int ret;
++
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
++		       IFC_REQ(TEST_IFC_REQ_WRITE, addr, data));
++	completion = readl(base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION);
++	reg = base + IPU6_DWC_DPHY_TEST_IFC_REQ_COMPLETION;
++	ret = readl_poll_timeout(reg, completion, !(completion & BIT(0)),
++				 10, DWC_DPHY_TIMEOUT);
++	if (ret)
++		dev_err(dev, "DWC ifc request write timeout\n");
++
++	return ret;
++}
++
++static void dwc_dphy_ifc_write_mask(struct ipu6_isys *isys, u32 phy_id,
++				    u32 addr, u32 data, u8 shift, u8 width)
++{
++	u32 temp, mask;
++	int ret;
++
++	ret = dwc_dphy_ifc_read(isys, phy_id, addr, &temp);
++	if (ret)
++		return;
++
++	mask = (1 << width) - 1;
++	temp &= ~(mask << shift);
++	temp |= (data & mask) << shift;
++	dwc_dphy_ifc_write(isys, phy_id, addr, temp);
++}
++
++static u32 dwc_dphy_ifc_read_mask(struct ipu6_isys *isys, u32 phy_id, u32 addr,
++				  u8 shift, u8 width)
++{
++	int ret;
++	u32 val;
++
++	ret = dwc_dphy_ifc_read(isys, phy_id, addr, &val);
++	if (ret)
++		return 0;
++
++	return ((val >> shift) & ((1 << width) - 1));
++}
++
++static int dwc_dphy_pwr_up(struct ipu6_isys *isys, u32 phy_id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	u32 fsm_state;
++	int ret;
++
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 1);
++	usleep_range(10, 20);
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 1);
++
++	ret = read_poll_timeout(dwc_dphy_ifc_read_mask, fsm_state,
++				(fsm_state == PHY_FSM_STATE_IDLE ||
++				 fsm_state == PHY_FSM_STATE_ULP),
++				100, DWC_DPHY_TIMEOUT, false, isys,
++				phy_id, 0x1e, 0, 4);
++
++	if (ret)
++		dev_err(dev, "Dphy %d power up failed, state 0x%x", phy_id,
++			fsm_state);
++
++	return ret;
++}
++
++struct dwc_dphy_freq_range {
++	u8 hsfreq;
++	u16 min;
++	u16 max;
++	u16 default_mbps;
++	u16 osc_freq_target;
++};
++
++#define DPHY_FREQ_RANGE_NUM		(63)
++#define DPHY_FREQ_RANGE_INVALID_INDEX	(0xff)
++static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {
++	{0x00,	80,	97,	80,	335},
++	{0x10,	80,	107,	90,	335},
++	{0x20,	84,	118,	100,	335},
++	{0x30,	93,	128,	110,	335},
++	{0x01,	103,	139,	120,	335},
++	{0x11,	112,	149,	130,	335},
++	{0x21,	122,	160,	140,	335},
++	{0x31,	131,	170,	150,	335},
++	{0x02,	141,	181,	160,	335},
++	{0x12,	150,	191,	170,	335},
++	{0x22,	160,	202,	180,	335},
++	{0x32,	169,	212,	190,	335},
++	{0x03,	183,	228,	205,	335},
++	{0x13,	198,	244,	220,	335},
++	{0x23,	212,	259,	235,	335},
++	{0x33,	226,	275,	250,	335},
++	{0x04,	250,	301,	275,	335},
++	{0x14,	274,	328,	300,	335},
++	{0x25,	297,	354,	325,	335},
++	{0x35,	321,	380,	350,	335},
++	{0x05,	369,	433,	400,	335},
++	{0x16,	416,	485,	450,	335},
++	{0x26,	464,	538,	500,	335},
++	{0x37,	511,	590,	550,	335},
++	{0x07,	559,	643,	600,	335},
++	{0x18,	606,	695,	650,	335},
++	{0x28,	654,	748,	700,	335},
++	{0x39,	701,	800,	750,	335},
++	{0x09,	749,	853,	800,	335},
++	{0x19,	796,	905,	850,	335},
++	{0x29,	844,	958,	900,	335},
++	{0x3a,	891,	1010,	950,	335},
++	{0x0a,	939,	1063,	1000,	335},
++	{0x1a,	986,	1115,	1050,	335},
++	{0x2a,	1034,	1168,	1100,	335},
++	{0x3b,	1081,	1220,	1150,	335},
++	{0x0b,	1129,	1273,	1200,	335},
++	{0x1b,	1176,	1325,	1250,	335},
++	{0x2b,	1224,	1378,	1300,	335},
++	{0x3c,	1271,	1430,	1350,	335},
++	{0x0c,	1319,	1483,	1400,	335},
++	{0x1c,	1366,	1535,	1450,	335},
++	{0x2c,	1414,	1588,	1500,	335},
++	{0x3d,	1461,	1640,	1550,	208},
++	{0x0d,	1509,	1693,	1600,	214},
++	{0x1d,	1556,	1745,	1650,	221},
++	{0x2e,	1604,	1798,	1700,	228},
++	{0x3e,	1651,	1850,	1750,	234},
++	{0x0e,	1699,	1903,	1800,	241},
++	{0x1e,	1746,	1955,	1850,	248},
++	{0x2f,	1794,	2008,	1900,	255},
++	{0x3f,	1841,	2060,	1950,	261},
++	{0x0f,	1889,	2113,	2000,	268},
++	{0x40,	1936,	2165,	2050,	275},
++	{0x41,	1984,	2218,	2100,	281},
++	{0x42,	2031,	2270,	2150,	288},
++	{0x43,	2079,	2323,	2200,	294},
++	{0x44,	2126,	2375,	2250,	302},
++	{0x45,	2174,	2428,	2300,	308},
++	{0x46,	2221,	2480,	2350,	315},
++	{0x47,	2269,	2500,	2400,	321},
++	{0x48,	2316,	2500,	2450,	328},
++	{0x49,	2364,	2500,	2500,	335}
++};
++
++static u16 get_hsfreq_by_mbps(u32 mbps)
++{
++	unsigned int i = DPHY_FREQ_RANGE_NUM;
++
++	while (i--) {
++		if (freqranges[i].default_mbps == mbps ||
++		    (mbps >= freqranges[i].min && mbps <= freqranges[i].max))
++			return i;
++	}
++
++	return DPHY_FREQ_RANGE_INVALID_INDEX;
++}
++
++static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
++				    u32 phy_id, u32 mbps)
++{
++	struct ipu6_bus_device *adev = isys->adev;
++	struct device *dev = &adev->auxdev.dev;
++	struct ipu6_device *isp = adev->isp;
++	u32 cfg_clk_freqrange;
++	u32 osc_freq_target;
++	u32 index;
++
++	dev_dbg(dev, "config Dphy %u with %u mbps", phy_id, mbps);
++
++	index = get_hsfreq_by_mbps(mbps);
++	if (index == DPHY_FREQ_RANGE_INVALID_INDEX) {
++		dev_err(dev, "link freq not found for mbps %u", mbps);
++		return -EINVAL;
++	}
++
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_HSFREQRANGE,
++			    freqranges[index].hsfreq, 0, 7);
++
++	/* Force termination Calibration */
++	if (isys->phy_termcal_val) {
++		dwc_dphy_ifc_write_mask(isys, phy_id, 0x20a, 0x1, 0, 1);
++		dwc_dphy_ifc_write_mask(isys, phy_id, 0x209, 0x3, 0, 2);
++		dwc_dphy_ifc_write_mask(isys, phy_id, 0x209,
++					isys->phy_termcal_val, 4, 4);
++	}
++
++	/*
++	 * Enable override to configure the DDL target oscillation
++	 * frequency on bit 0 of register 0xe4
++	 */
++	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe4, 0x1, 0, 1);
++	/*
++	 * configure registers 0xe2, 0xe3 with the
++	 * appropriate DDL target oscillation frequency
++	 * 0x1cc(460)
++	 */
++	osc_freq_target = freqranges[index].osc_freq_target;
++	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe2,
++				osc_freq_target & 0xff, 0, 8);
++	dwc_dphy_ifc_write_mask(isys, phy_id, 0xe3,
++				(osc_freq_target >> 8) & 0xf, 0, 4);
++
++	if (mbps < 1500) {
++		/* deskew_polarity_rw, for < 1.5Gbps */
++		dwc_dphy_ifc_write_mask(isys, phy_id, 0x8, 0x1, 5, 1);
++	}
++
++	/*
++	 * Set cfgclkfreqrange[5:0] = round[(Fcfg_clk(MHz)-17)*4]
++	 * (38.4 - 17) * 4 = ~85 (0x55)
++	 */
++	cfg_clk_freqrange = (isp->buttress.ref_clk - 170) * 4 / 10;
++	dev_dbg(dev, "ref_clk = %u clk_freqrange = %u",
++		isp->buttress.ref_clk, cfg_clk_freqrange);
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_CFGCLKFREQRANGE,
++			    cfg_clk_freqrange, 0, 8);
++
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 4, 1);
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0x1, 8, 1);
++
++	return 0;
++}
++
++static void ipu6_isys_dwc_phy_aggr_setup(struct ipu6_isys *isys, u32 master,
++					 u32 slave, u32 mbps)
++{
++	/* Config mastermacro */
++	dwc_dphy_ifc_write_mask(isys, master, 0x133, 0x1, 0, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x133, 0x0, 0, 1);
++
++	/* Config master PHY clk lane to drive long channel clk */
++	dwc_dphy_ifc_write_mask(isys, master, 0x307, 0x1, 2, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x0, 2, 1);
++
++	/* Config both PHYs data lanes to get clk from long channel */
++	dwc_dphy_ifc_write_mask(isys, master, 0x508, 0x1, 5, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x508, 0x1, 5, 1);
++	dwc_dphy_ifc_write_mask(isys, master, 0x708, 0x1, 5, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x708, 0x1, 5, 1);
++
++	/* Config slave PHY clk lane to bypass long channel clk to DDR clk */
++	dwc_dphy_ifc_write_mask(isys, master, 0x308, 0x0, 3, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x308, 0x1, 3, 1);
++
++	/* Override slave PHY clk lane enable (DPHYRXCLK_CLL_demux module) */
++	dwc_dphy_ifc_write_mask(isys, slave, 0xe0, 0x3, 0, 2);
++
++	/* Override slave PHY DDR clk lane enable (DPHYHSRX_div124 module) */
++	dwc_dphy_ifc_write_mask(isys, slave, 0xe1, 0x1, 1, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x307, 0x1, 3, 1);
++
++	/* Turn off slave PHY LP-RX clk lane */
++	dwc_dphy_ifc_write_mask(isys, slave, 0x304, 0x1, 7, 1);
++	dwc_dphy_ifc_write_mask(isys, slave, 0x305, 0xa, 0, 5);
++}
++
++#define PHY_E	4
++static int ipu6_isys_dwc_phy_powerup_ack(struct ipu6_isys *isys, u32 phy_id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	u32 rescal_done;
++	int ret;
++
++	ret = dwc_dphy_pwr_up(isys, phy_id);
++	if (ret != 0) {
++		dev_err(dev, "Dphy %u power up failed(%d)", phy_id, ret);
++		return ret;
++	}
++
++	/* reset forcerxmode */
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 4, 1);
++	dwc_dphy_write_mask(isys, phy_id, IPU6_DWC_DPHY_DFT_CTRL2, 0, 8, 1);
++
++	dev_dbg(dev, "Dphy %u is ready!", phy_id);
++
++	if (phy_id != PHY_E || isys->phy_termcal_val)
++		return 0;
++
++	usleep_range(100, 200);
++	rescal_done = dwc_dphy_ifc_read_mask(isys, phy_id, 0x221, 7, 1);
++	if (rescal_done) {
++		isys->phy_termcal_val = dwc_dphy_ifc_read_mask(isys, phy_id,
++							       0x220, 2, 4);
++		dev_dbg(dev, "termcal done with value = %u",
++			isys->phy_termcal_val);
++	}
++
++	return 0;
++}
++
++static void ipu6_isys_dwc_phy_reset(struct ipu6_isys *isys, u32 phy_id)
++{
++	dev_dbg(&isys->adev->auxdev.dev, "Reset phy %u", phy_id);
++
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_SHUTDOWNZ, 0);
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_RSTZ, 0);
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_ACCESS_MODE,
++		       TEST_IFC_ACCESS_MODE_FSM);
++	dwc_dphy_write(isys, phy_id, IPU6_DWC_DPHY_TEST_IFC_REQ,
++		       TEST_IFC_REQ_RESET);
++}
++
++int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u32 phy_id, primary, secondary;
++	u32 nlanes, port, mbps;
++	s64 link_freq;
++	int ret;
++
++	port = cfg->port;
++
++	if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
++		dev_warn(dev, "invalid port ID %d\n", port);
++		return -EINVAL;
++	}
++
++	nlanes = cfg->nlanes;
++	/* only port 0, 2 and 4 support 4 lanes */
++	if (nlanes == 4 && port % 2) {
++		dev_err(dev, "invalid csi-port %u with %u lanes\n", port,
++			nlanes);
++		return -EINVAL;
++	}
++
++	link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[port]);
++	if (link_freq < 0) {
++		dev_err(dev, "get link freq failed(%lld).\n", link_freq);
++		return link_freq;
++	}
++
++	mbps = div_u64(link_freq, 500000);
++
++	phy_id = port;
++	primary = port & ~1;
++	secondary = primary + 1;
++	if (on) {
++		if (nlanes == 4) {
++			dev_dbg(dev, "config phy %u and %u in aggr mode\n",
++				primary, secondary);
++
++			ipu6_isys_dwc_phy_reset(isys, primary);
++			ipu6_isys_dwc_phy_reset(isys, secondary);
++			ipu6_isys_dwc_phy_aggr_setup(isys, primary,
++						     secondary, mbps);
++
++			ret = ipu6_isys_dwc_phy_config(isys, primary, mbps);
++			if (ret)
++				return ret;
++			ret = ipu6_isys_dwc_phy_config(isys, secondary, mbps);
++			if (ret)
++				return ret;
++
++			ret = ipu6_isys_dwc_phy_powerup_ack(isys, primary);
++			if (ret)
++				return ret;
++
++			ret = ipu6_isys_dwc_phy_powerup_ack(isys, secondary);
++			return ret;
++		}
++
++		dev_dbg(dev, "config phy %u with %u lanes in non-aggr mode\n",
++			phy_id, nlanes);
++
++		ipu6_isys_dwc_phy_reset(isys, phy_id);
++		ret = ipu6_isys_dwc_phy_config(isys, phy_id, mbps);
++		if (ret)
++			return ret;
++
++		ret = ipu6_isys_dwc_phy_powerup_ack(isys, phy_id);
++		return ret;
++	}
++
++	if (nlanes == 4) {
++		dev_dbg(dev, "Power down phy %u and phy %u for port %u\n",
++			primary, secondary, port);
++		ipu6_isys_dwc_phy_reset(isys, secondary);
++		ipu6_isys_dwc_phy_reset(isys, primary);
++
++		return 0;
++	}
++
++	dev_dbg(dev, "Powerdown phy %u with %u lanes\n", phy_id, nlanes);
++
++	ipu6_isys_dwc_phy_reset(isys, phy_id);
++
++	return 0;
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c
+new file mode 100644
+index 000000000000..dcc7743e0cee
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-jsl-phy.c
+@@ -0,0 +1,242 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/device.h>
++#include <linux/io.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++
++/* only use BB0, BB2, BB4, and BB6 on PHY0 */
++#define IPU6SE_ISYS_PHY_BB_NUM		4
++#define IPU6SE_ISYS_PHY_0_BASE		0x10000
++
++#define PHY_CPHY_DLL_OVRD(x)		(0x100 + 0x100 * (x))
++#define PHY_CPHY_RX_CONTROL1(x)		(0x110 + 0x100 * (x))
++#define PHY_DPHY_CFG(x)			(0x148 + 0x100 * (x))
++#define PHY_BB_AFE_CONFIG(x)		(0x174 + 0x100 * (x))
++
++/*
++ * use port_cfg to configure that which data lanes used
++ * +---------+     +------+ +-----+
++ * | port0 x4<-----|      | |     |
++ * |         |     | port | |     |
++ * | port1 x2<-----|      | |     |
++ * |         |     |      <-| PHY |
++ * | port2 x4<-----|      | |     |
++ * |         |     |config| |     |
++ * | port3 x2<-----|      | |     |
++ * +---------+     +------+ +-----+
++ */
++static const unsigned int csi2_port_cfg[][3] = {
++	{0, 0, 0x1f}, /* no link */
++	{4, 0, 0x10}, /* x4 + x4 config */
++	{2, 0, 0x12}, /* x2 + x2 config */
++	{1, 0, 0x13}, /* x1 + x1 config */
++	{2, 1, 0x15}, /* x2x1 + x2x1 config */
++	{1, 1, 0x16}, /* x1x1 + x1x1 config */
++	{2, 2, 0x18}, /* x2x2 + x2x2 config */
++	{1, 2, 0x19} /* x1x2 + x1x2 config */
++};
++
++/* port, nlanes, bbindex, portcfg */
++static const unsigned int phy_port_cfg[][4] = {
++	/* sip0 */
++	{0, 1, 0, 0x15},
++	{0, 2, 0, 0x15},
++	{0, 4, 0, 0x15},
++	{0, 4, 2, 0x22},
++	/* sip1 */
++	{2, 1, 4, 0x15},
++	{2, 2, 4, 0x15},
++	{2, 4, 4, 0x15},
++	{2, 4, 6, 0x22}
++};
++
++static void ipu6_isys_csi2_phy_config_by_port(struct ipu6_isys *isys,
++					      unsigned int port,
++					      unsigned int nlanes)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *base = isys->adev->isp->base;
++	unsigned int bbnum;
++	u32 val, reg, i;
++
++	dev_dbg(dev, "port %u with %u lanes", port, nlanes);
++
++	/* only support <1.5Gbps */
++	for (i = 0; i < IPU6SE_ISYS_PHY_BB_NUM; i++) {
++		/* cphy_dll_ovrd.crcdc_fsm_dlane0 = 13 */
++		reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i);
++		val = readl(base + reg);
++		val |= FIELD_PREP(GENMASK(6, 1), 13);
++		writel(val, base + reg);
++
++		/* cphy_rx_control1.en_crc1 = 1 */
++		reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_RX_CONTROL1(i);
++		val = readl(base + reg);
++		val |= BIT(31);
++		writel(val, base + reg);
++
++		/* dphy_cfg.reserved = 1, .lden_from_dll_ovrd_0 = 1 */
++		reg = IPU6SE_ISYS_PHY_0_BASE + PHY_DPHY_CFG(i);
++		val = readl(base + reg);
++		val |= BIT(25) | BIT(26);
++		writel(val, base + reg);
++
++		/* cphy_dll_ovrd.lden_crcdc_fsm_dlane0 = 1 */
++		reg = IPU6SE_ISYS_PHY_0_BASE + PHY_CPHY_DLL_OVRD(i);
++		val = readl(base + reg);
++		val |= BIT(0);
++		writel(val, base + reg);
++	}
++
++	/* Front end config, use minimal channel loss */
++	for (i = 0; i < ARRAY_SIZE(phy_port_cfg); i++) {
++		if (phy_port_cfg[i][0] == port &&
++		    phy_port_cfg[i][1] == nlanes) {
++			bbnum = phy_port_cfg[i][2] / 2;
++			reg = IPU6SE_ISYS_PHY_0_BASE + PHY_BB_AFE_CONFIG(bbnum);
++			val = readl(base + reg);
++			val |= phy_port_cfg[i][3];
++			writel(val, base + reg);
++		}
++	}
++}
++
++static void ipu6_isys_csi2_rx_control(struct ipu6_isys *isys)
++{
++	void __iomem *base = isys->adev->isp->base;
++	u32 val, reg;
++
++	reg = CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL;
++	val = readl(base + reg);
++	val |= BIT(0);
++	writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_A_CONTROL);
++
++	reg = CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL;
++	val = readl(base + reg);
++	val |= BIT(0);
++	writel(val, base + CSI2_HUB_GPREG_SIP0_CSI_RX_B_CONTROL);
++
++	reg = CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL;
++	val = readl(base + reg);
++	val |= BIT(0);
++	writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_A_CONTROL);
++
++	reg = CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL;
++	val = readl(base + reg);
++	val |= BIT(0);
++	writel(val, base + CSI2_HUB_GPREG_SIP1_CSI_RX_B_CONTROL);
++}
++
++static int ipu6_isys_csi2_set_port_cfg(struct ipu6_isys *isys,
++				       unsigned int port, unsigned int nlanes)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int sip = port / 2;
++	unsigned int index;
++
++	switch (nlanes) {
++	case 1:
++		index = 5;
++		break;
++	case 2:
++		index = 6;
++		break;
++	case 4:
++		index = 1;
++		break;
++	default:
++		dev_err(dev, "lanes nr %u is unsupported\n", nlanes);
++		return -EINVAL;
++	}
++
++	dev_dbg(dev, "port config for port %u with %u lanes\n",	port, nlanes);
++
++	writel(csi2_port_cfg[index][2],
++	       isys->pdata->base + CSI2_HUB_GPREG_SIP_FB_PORT_CFG(sip));
++
++	return 0;
++}
++
++static void
++ipu6_isys_csi2_set_timing(struct ipu6_isys *isys,
++			  const struct ipu6_isys_csi2_timing *timing,
++			  unsigned int port, unsigned int nlanes)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *reg;
++	u32 port_base;
++	u32 i;
++
++	port_base = (port % 2) ? CSI2_SIP_TOP_CSI_RX_PORT_BASE_1(port) :
++		CSI2_SIP_TOP_CSI_RX_PORT_BASE_0(port);
++
++	dev_dbg(dev, "set timing for port %u with %u lanes\n", port, nlanes);
++
++	reg = isys->pdata->base + port_base;
++	reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_CLANE;
++
++	writel(timing->ctermen, reg);
++
++	reg = isys->pdata->base + port_base;
++	reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_CLANE;
++	writel(timing->csettle, reg);
++
++	for (i = 0; i < nlanes; i++) {
++		reg = isys->pdata->base + port_base;
++		reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_TERMEN_DLANE(i);
++		writel(timing->dtermen, reg);
++
++		reg = isys->pdata->base + port_base;
++		reg += CSI2_SIP_TOP_CSI_RX_DLY_CNT_SETTLE_DLANE(i);
++		writel(timing->dsettle, reg);
++	}
++}
++
++#define DPHY_TIMER_INCR	0x28
++int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	int ret = 0;
++	u32 nlanes;
++	u32 port;
++
++	if (!on)
++		return 0;
++
++	port = cfg->port;
++	nlanes = cfg->nlanes;
++
++	if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
++		dev_warn(dev, "invalid port ID %d\n", port);
++		return -EINVAL;
++	}
++
++	ipu6_isys_csi2_phy_config_by_port(isys, port, nlanes);
++
++	writel(DPHY_TIMER_INCR,
++	       isys->pdata->base + CSI2_HUB_GPREG_DPHY_TIMER_INCR);
++
++	/* set port cfg and rx timing */
++	ipu6_isys_csi2_set_timing(isys, timing, port, nlanes);
++
++	ret = ipu6_isys_csi2_set_port_cfg(isys, port, nlanes);
++	if (ret)
++		return ret;
++
++	ipu6_isys_csi2_rx_control(isys);
++
++	return 0;
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c
+new file mode 100644
+index 000000000000..9abf389a05f1
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-mcd-phy.c
+@@ -0,0 +1,720 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/bits.h>
++#include <linux/container_of.h>
++#include <linux/device.h>
++#include <linux/iopoll.h>
++#include <linux/list.h>
++#include <linux/refcount.h>
++#include <linux/time64.h>
++
++#include <media/v4l2-async.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++
++#define CSI_REG_HUB_GPREG_PHY_CTL(id) (CSI_REG_BASE + 0x18008 + (id) * 0x8)
++#define CSI_REG_HUB_GPREG_PHY_CTL_RESET			BIT(4)
++#define CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN		BIT(0)
++#define CSI_REG_HUB_GPREG_PHY_STATUS(id) (CSI_REG_BASE + 0x1800c + (id) * 0x8)
++#define CSI_REG_HUB_GPREG_PHY_POWER_ACK			BIT(0)
++#define CSI_REG_HUB_GPREG_PHY_READY			BIT(4)
++
++#define MCD_PHY_POWER_STATUS_TIMEOUT			(200 * USEC_PER_MSEC)
++
++/*
++ * bridge to phy in buttress reg map, each phy has 16 kbytes
++ * only 2 phys for TGL U and Y
++ */
++#define IPU6_ISYS_MCD_PHY_BASE(i)			(0x10000 + (i) * 0x4000)
++
++/*
++ *  There are 2 MCD DPHY instances on TGL and 1 MCD DPHY instance on ADL.
++ *  Each MCD PHY has 12-lanes which has 8 data lanes and 4 clock lanes.
++ *  CSI port 1, 3 (5, 7) can support max 2 data lanes.
++ *  CSI port 0, 2 (4, 6) can support max 4 data lanes.
++ *  PHY configurations are PPI based instead of port.
++ *  Left:
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | PPI     | PPI5    | PPI4    | PPI3    | PPI2   | PPI1    | PPI0     |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x4      | unused  | D3      | D2      | C0     | D0      | D1       |
++ *  |---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x2x2    | C1      | D0      | D1      | C0     | D0      | D1       |
++ *  ----------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x2x1    | C1      | D0      | unused  | C0     | D0      | D1       |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x1x1    | C1      | D0      | unused  | C0     | D0      | unused   |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x1x2    | C1      | D0      | D1      | C0     | D0      | unused   |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *
++ *  Right:
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | PPI     | PPI6    | PPI7    | PPI8    | PPI9   | PPI10   | PPI11    |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x4      | D1      | D0      | C2      | D2     | D3      | unused   |
++ *  |---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x2x2    | D1      | D0      | C2      | D1     | D0      | C3       |
++ *  ----------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x2x1    | D1      | D0      | C2      | unused | D0      | C3       |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x1x1    | unused  | D0      | C2      | unused | D0      | C3       |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *  |         |         |         |         |        |         |          |
++ *  | x1x2    | unused  | D0      | C2      | D1     | D0      | C3       |
++ *  +---------+---------+---------+---------+--------+---------+----------+
++ *
++ * ppi mapping per phy :
++ *
++ * x4 + x4:
++ * Left : port0 - PPI range {0, 1, 2, 3, 4}
++ * Right: port2 - PPI range {6, 7, 8, 9, 10}
++ *
++ * x4 + x2x2:
++ * Left: port0 - PPI range {0, 1, 2, 3, 4}
++ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11}
++ *
++ * x2x2 + x4:
++ * Left: port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5}
++ * Right: port2 - PPI range {6, 7, 8, 9, 10}
++ *
++ * x2x2 + x2x2:
++ * Left : port0 - PPI range {0, 1, 2}, port1 - PPI range {3, 4, 5}
++ * Right: port2 - PPI range {6, 7, 8}, port3 - PPI range {9, 10, 11}
++ */
++
++struct phy_reg {
++	u32 reg;
++	u32 val;
++};
++
++static const struct phy_reg common_init_regs[] = {
++	/* for TGL-U, use 0x80000000 */
++	{0x00000040, 0x80000000},
++	{0x00000044, 0x00a80880},
++	{0x00000044, 0x00b80880},
++	{0x00000010, 0x0000078c},
++	{0x00000344, 0x2f4401e2},
++	{0x00000544, 0x924401e2},
++	{0x00000744, 0x594401e2},
++	{0x00000944, 0x624401e2},
++	{0x00000b44, 0xfc4401e2},
++	{0x00000d44, 0xc54401e2},
++	{0x00000f44, 0x034401e2},
++	{0x00001144, 0x8f4401e2},
++	{0x00001344, 0x754401e2},
++	{0x00001544, 0xe94401e2},
++	{0x00001744, 0xcb4401e2},
++	{0x00001944, 0xfa4401e2}
++};
++
++static const struct phy_reg x1_port0_config_regs[] = {
++	{0x00000694, 0xc80060fa},
++	{0x00000680, 0x3d4f78ea},
++	{0x00000690, 0x10a0140b},
++	{0x000006a8, 0xdf04010a},
++	{0x00000700, 0x57050060},
++	{0x00000710, 0x0030001c},
++	{0x00000738, 0x5f004444},
++	{0x0000073c, 0x78464204},
++	{0x00000748, 0x7821f940},
++	{0x0000074c, 0xb2000433},
++	{0x00000494, 0xfe6030fa},
++	{0x00000480, 0x29ef5ed0},
++	{0x00000490, 0x10a0540b},
++	{0x000004a8, 0x7a01010a},
++	{0x00000500, 0xef053460},
++	{0x00000510, 0xe030101c},
++	{0x00000538, 0xdf808444},
++	{0x0000053c, 0xc8422204},
++	{0x00000540, 0x0180088c},
++	{0x00000574, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x1_port1_config_regs[] = {
++	{0x00000c94, 0xc80060fa},
++	{0x00000c80, 0xcf47abea},
++	{0x00000c90, 0x10a0840b},
++	{0x00000ca8, 0xdf04010a},
++	{0x00000d00, 0x57050060},
++	{0x00000d10, 0x0030001c},
++	{0x00000d38, 0x5f004444},
++	{0x00000d3c, 0x78464204},
++	{0x00000d48, 0x7821f940},
++	{0x00000d4c, 0xb2000433},
++	{0x00000a94, 0xc91030fa},
++	{0x00000a80, 0x5a166ed0},
++	{0x00000a90, 0x10a0540b},
++	{0x00000aa8, 0x5d060100},
++	{0x00000b00, 0xef053460},
++	{0x00000b10, 0xa030101c},
++	{0x00000b38, 0xdf808444},
++	{0x00000b3c, 0xc8422204},
++	{0x00000b40, 0x0180088c},
++	{0x00000b74, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x1_port2_config_regs[] = {
++	{0x00001294, 0x28f000fa},
++	{0x00001280, 0x08130cea},
++	{0x00001290, 0x10a0140b},
++	{0x000012a8, 0xd704010a},
++	{0x00001300, 0x8d050060},
++	{0x00001310, 0x0030001c},
++	{0x00001338, 0xdf008444},
++	{0x0000133c, 0x78422204},
++	{0x00001348, 0x7821f940},
++	{0x0000134c, 0x5a000433},
++	{0x00001094, 0x2d20b0fa},
++	{0x00001080, 0xade75dd0},
++	{0x00001090, 0x10a0540b},
++	{0x000010a8, 0xb101010a},
++	{0x00001100, 0x33053460},
++	{0x00001110, 0x0030101c},
++	{0x00001138, 0xdf808444},
++	{0x0000113c, 0xc8422204},
++	{0x00001140, 0x8180088c},
++	{0x00001174, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x1_port3_config_regs[] = {
++	{0x00001894, 0xc80060fa},
++	{0x00001880, 0x0f90fd6a},
++	{0x00001890, 0x10a0840b},
++	{0x000018a8, 0xdf04010a},
++	{0x00001900, 0x57050060},
++	{0x00001910, 0x0030001c},
++	{0x00001938, 0x5f004444},
++	{0x0000193c, 0x78464204},
++	{0x00001948, 0x7821f940},
++	{0x0000194c, 0xb2000433},
++	{0x00001694, 0x3050d0fa},
++	{0x00001680, 0x0ef6d050},
++	{0x00001690, 0x10a0540b},
++	{0x000016a8, 0xe301010a},
++	{0x00001700, 0x69053460},
++	{0x00001710, 0xa030101c},
++	{0x00001738, 0xdf808444},
++	{0x0000173c, 0xc8422204},
++	{0x00001740, 0x0180088c},
++	{0x00001774, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x2_port0_config_regs[] = {
++	{0x00000694, 0xc80060fa},
++	{0x00000680, 0x3d4f78ea},
++	{0x00000690, 0x10a0140b},
++	{0x000006a8, 0xdf04010a},
++	{0x00000700, 0x57050060},
++	{0x00000710, 0x0030001c},
++	{0x00000738, 0x5f004444},
++	{0x0000073c, 0x78464204},
++	{0x00000748, 0x7821f940},
++	{0x0000074c, 0xb2000433},
++	{0x00000494, 0xc80060fa},
++	{0x00000480, 0x29ef5ed8},
++	{0x00000490, 0x10a0540b},
++	{0x000004a8, 0x7a01010a},
++	{0x00000500, 0xef053460},
++	{0x00000510, 0xe030101c},
++	{0x00000538, 0xdf808444},
++	{0x0000053c, 0xc8422204},
++	{0x00000540, 0x0180088c},
++	{0x00000574, 0x00000000},
++	{0x00000294, 0xc80060fa},
++	{0x00000280, 0xcb45b950},
++	{0x00000290, 0x10a0540b},
++	{0x000002a8, 0x8c01010a},
++	{0x00000300, 0xef053460},
++	{0x00000310, 0x8030101c},
++	{0x00000338, 0x41808444},
++	{0x0000033c, 0x32422204},
++	{0x00000340, 0x0180088c},
++	{0x00000374, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x2_port1_config_regs[] = {
++	{0x00000c94, 0xc80060fa},
++	{0x00000c80, 0xcf47abea},
++	{0x00000c90, 0x10a0840b},
++	{0x00000ca8, 0xdf04010a},
++	{0x00000d00, 0x57050060},
++	{0x00000d10, 0x0030001c},
++	{0x00000d38, 0x5f004444},
++	{0x00000d3c, 0x78464204},
++	{0x00000d48, 0x7821f940},
++	{0x00000d4c, 0xb2000433},
++	{0x00000a94, 0xc80060fa},
++	{0x00000a80, 0x5a166ed8},
++	{0x00000a90, 0x10a0540b},
++	{0x00000aa8, 0x7a01010a},
++	{0x00000b00, 0xef053460},
++	{0x00000b10, 0xa030101c},
++	{0x00000b38, 0xdf808444},
++	{0x00000b3c, 0xc8422204},
++	{0x00000b40, 0x0180088c},
++	{0x00000b74, 0x00000000},
++	{0x00000894, 0xc80060fa},
++	{0x00000880, 0x4d4f21d0},
++	{0x00000890, 0x10a0540b},
++	{0x000008a8, 0x5601010a},
++	{0x00000900, 0xef053460},
++	{0x00000910, 0x8030101c},
++	{0x00000938, 0xdf808444},
++	{0x0000093c, 0xc8422204},
++	{0x00000940, 0x0180088c},
++	{0x00000974, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x2_port2_config_regs[] = {
++	{0x00001294, 0xc80060fa},
++	{0x00001280, 0x08130cea},
++	{0x00001290, 0x10a0140b},
++	{0x000012a8, 0xd704010a},
++	{0x00001300, 0x8d050060},
++	{0x00001310, 0x0030001c},
++	{0x00001338, 0xdf008444},
++	{0x0000133c, 0x78422204},
++	{0x00001348, 0x7821f940},
++	{0x0000134c, 0x5a000433},
++	{0x00001094, 0xc80060fa},
++	{0x00001080, 0xade75dd8},
++	{0x00001090, 0x10a0540b},
++	{0x000010a8, 0xb101010a},
++	{0x00001100, 0x33053460},
++	{0x00001110, 0x0030101c},
++	{0x00001138, 0xdf808444},
++	{0x0000113c, 0xc8422204},
++	{0x00001140, 0x8180088c},
++	{0x00001174, 0x00000000},
++	{0x00000e94, 0xc80060fa},
++	{0x00000e80, 0x0fbf16d0},
++	{0x00000e90, 0x10a0540b},
++	{0x00000ea8, 0x7a01010a},
++	{0x00000f00, 0xf5053460},
++	{0x00000f10, 0xc030101c},
++	{0x00000f38, 0xdf808444},
++	{0x00000f3c, 0xc8422204},
++	{0x00000f40, 0x8180088c},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x2_port3_config_regs[] = {
++	{0x00001894, 0xc80060fa},
++	{0x00001880, 0x0f90fd6a},
++	{0x00001890, 0x10a0840b},
++	{0x000018a8, 0xdf04010a},
++	{0x00001900, 0x57050060},
++	{0x00001910, 0x0030001c},
++	{0x00001938, 0x5f004444},
++	{0x0000193c, 0x78464204},
++	{0x00001948, 0x7821f940},
++	{0x0000194c, 0xb2000433},
++	{0x00001694, 0xc80060fa},
++	{0x00001680, 0x0ef6d058},
++	{0x00001690, 0x10a0540b},
++	{0x000016a8, 0x7a01010a},
++	{0x00001700, 0x69053460},
++	{0x00001710, 0xa030101c},
++	{0x00001738, 0xdf808444},
++	{0x0000173c, 0xc8422204},
++	{0x00001740, 0x0180088c},
++	{0x00001774, 0x00000000},
++	{0x00001494, 0xc80060fa},
++	{0x00001480, 0xf9d34bd0},
++	{0x00001490, 0x10a0540b},
++	{0x000014a8, 0x7a01010a},
++	{0x00001500, 0x1b053460},
++	{0x00001510, 0x0030101c},
++	{0x00001538, 0xdf808444},
++	{0x0000153c, 0xc8422204},
++	{0x00001540, 0x8180088c},
++	{0x00001574, 0x00000000},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x4_port0_config_regs[] = {
++	{0x00000694, 0xc80060fa},
++	{0x00000680, 0x3d4f78fa},
++	{0x00000690, 0x10a0140b},
++	{0x000006a8, 0xdf04010a},
++	{0x00000700, 0x57050060},
++	{0x00000710, 0x0030001c},
++	{0x00000738, 0x5f004444},
++	{0x0000073c, 0x78464204},
++	{0x00000748, 0x7821f940},
++	{0x0000074c, 0xb2000433},
++	{0x00000494, 0xfe6030fa},
++	{0x00000480, 0x29ef5ed8},
++	{0x00000490, 0x10a0540b},
++	{0x000004a8, 0x7a01010a},
++	{0x00000500, 0xef053460},
++	{0x00000510, 0xe030101c},
++	{0x00000538, 0xdf808444},
++	{0x0000053c, 0xc8422204},
++	{0x00000540, 0x0180088c},
++	{0x00000574, 0x00000004},
++	{0x00000294, 0x23e030fa},
++	{0x00000280, 0xcb45b950},
++	{0x00000290, 0x10a0540b},
++	{0x000002a8, 0x8c01010a},
++	{0x00000300, 0xef053460},
++	{0x00000310, 0x8030101c},
++	{0x00000338, 0x41808444},
++	{0x0000033c, 0x32422204},
++	{0x00000340, 0x0180088c},
++	{0x00000374, 0x00000004},
++	{0x00000894, 0x5620b0fa},
++	{0x00000880, 0x4d4f21dc},
++	{0x00000890, 0x10a0540b},
++	{0x000008a8, 0x5601010a},
++	{0x00000900, 0xef053460},
++	{0x00000910, 0x8030101c},
++	{0x00000938, 0xdf808444},
++	{0x0000093c, 0xc8422204},
++	{0x00000940, 0x0180088c},
++	{0x00000974, 0x00000004},
++	{0x00000a94, 0xc91030fa},
++	{0x00000a80, 0x5a166ecc},
++	{0x00000a90, 0x10a0540b},
++	{0x00000aa8, 0x5d01010a},
++	{0x00000b00, 0xef053460},
++	{0x00000b10, 0xa030101c},
++	{0x00000b38, 0xdf808444},
++	{0x00000b3c, 0xc8422204},
++	{0x00000b40, 0x0180088c},
++	{0x00000b74, 0x00000004},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x4_port1_config_regs[] = {
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x4_port2_config_regs[] = {
++	{0x00001294, 0x28f000fa},
++	{0x00001280, 0x08130cfa},
++	{0x00001290, 0x10c0140b},
++	{0x000012a8, 0xd704010a},
++	{0x00001300, 0x8d050060},
++	{0x00001310, 0x0030001c},
++	{0x00001338, 0xdf008444},
++	{0x0000133c, 0x78422204},
++	{0x00001348, 0x7821f940},
++	{0x0000134c, 0x5a000433},
++	{0x00001094, 0x2d20b0fa},
++	{0x00001080, 0xade75dd8},
++	{0x00001090, 0x10a0540b},
++	{0x000010a8, 0xb101010a},
++	{0x00001100, 0x33053460},
++	{0x00001110, 0x0030101c},
++	{0x00001138, 0xdf808444},
++	{0x0000113c, 0xc8422204},
++	{0x00001140, 0x8180088c},
++	{0x00001174, 0x00000004},
++	{0x00000e94, 0xd308d0fa},
++	{0x00000e80, 0x0fbf16d0},
++	{0x00000e90, 0x10a0540b},
++	{0x00000ea8, 0x2c01010a},
++	{0x00000f00, 0xf5053460},
++	{0x00000f10, 0xc030101c},
++	{0x00000f38, 0xdf808444},
++	{0x00000f3c, 0xc8422204},
++	{0x00000f40, 0x8180088c},
++	{0x00000f74, 0x00000004},
++	{0x00001494, 0x136850fa},
++	{0x00001480, 0xf9d34bdc},
++	{0x00001490, 0x10a0540b},
++	{0x000014a8, 0x5a01010a},
++	{0x00001500, 0x1b053460},
++	{0x00001510, 0x0030101c},
++	{0x00001538, 0xdf808444},
++	{0x0000153c, 0xc8422204},
++	{0x00001540, 0x8180088c},
++	{0x00001574, 0x00000004},
++	{0x00001694, 0x3050d0fa},
++	{0x00001680, 0x0ef6d04c},
++	{0x00001690, 0x10a0540b},
++	{0x000016a8, 0xe301010a},
++	{0x00001700, 0x69053460},
++	{0x00001710, 0xa030101c},
++	{0x00001738, 0xdf808444},
++	{0x0000173c, 0xc8422204},
++	{0x00001740, 0x0180088c},
++	{0x00001774, 0x00000004},
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg x4_port3_config_regs[] = {
++	{0x00000000, 0x00000000}
++};
++
++static const struct phy_reg *x1_config_regs[4] = {
++	x1_port0_config_regs,
++	x1_port1_config_regs,
++	x1_port2_config_regs,
++	x1_port3_config_regs
++};
++
++static const struct phy_reg *x2_config_regs[4] = {
++	x2_port0_config_regs,
++	x2_port1_config_regs,
++	x2_port2_config_regs,
++	x2_port3_config_regs
++};
++
++static const struct phy_reg *x4_config_regs[4] = {
++	x4_port0_config_regs,
++	x4_port1_config_regs,
++	x4_port2_config_regs,
++	x4_port3_config_regs
++};
++
++static const struct phy_reg **config_regs[3] = {
++	x1_config_regs,
++	x2_config_regs,
++	x4_config_regs
++};
++
++static int ipu6_isys_mcd_phy_powerup_ack(struct ipu6_isys *isys, u8 id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u32 val;
++	int ret;
++
++	val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++	val |= CSI_REG_HUB_GPREG_PHY_CTL_PWR_EN;
++	writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++
++	ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
++				 val, val & CSI_REG_HUB_GPREG_PHY_POWER_ACK,
++				 200, MCD_PHY_POWER_STATUS_TIMEOUT);
++	if (ret)
++		dev_err(dev, "PHY%d powerup ack timeout", id);
++
++	return ret;
++}
++
++static int ipu6_isys_mcd_phy_powerdown_ack(struct ipu6_isys *isys, u8 id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u32 val;
++	int ret;
++
++	writel(0, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++	ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
++				 val, !(val & CSI_REG_HUB_GPREG_PHY_POWER_ACK),
++				 200, MCD_PHY_POWER_STATUS_TIMEOUT);
++	if (ret)
++		dev_err(dev, "PHY%d powerdown ack timeout", id);
++
++	return ret;
++}
++
++static void ipu6_isys_mcd_phy_reset(struct ipu6_isys *isys, u8 id, bool assert)
++{
++	void __iomem *isys_base = isys->pdata->base;
++	u32 val;
++
++	val = readl(isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++	if (assert)
++		val |= CSI_REG_HUB_GPREG_PHY_CTL_RESET;
++	else
++		val &= ~(CSI_REG_HUB_GPREG_PHY_CTL_RESET);
++
++	writel(val, isys_base + CSI_REG_HUB_GPREG_PHY_CTL(id));
++}
++
++static int ipu6_isys_mcd_phy_ready(struct ipu6_isys *isys, u8 id)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u32 val;
++	int ret;
++
++	ret = readl_poll_timeout(isys_base + CSI_REG_HUB_GPREG_PHY_STATUS(id),
++				 val, val & CSI_REG_HUB_GPREG_PHY_READY,
++				 200, MCD_PHY_POWER_STATUS_TIMEOUT);
++	if (ret)
++		dev_err(dev, "PHY%d ready ack timeout", id);
++
++	return ret;
++}
++
++static void ipu6_isys_mcd_phy_common_init(struct ipu6_isys *isys)
++{
++	struct ipu6_bus_device *adev = isys->adev;
++	struct ipu6_device *isp = adev->isp;
++	void __iomem *isp_base = isp->base;
++	struct sensor_async_sd *s_asd;
++	struct v4l2_async_connection *asc;
++	void __iomem *phy_base;
++	unsigned int i;
++	u8 phy_id;
++
++	list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) {
++		s_asd = container_of(asc, struct sensor_async_sd, asc);
++		phy_id = s_asd->csi2.port / 4;
++		phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id);
++
++		for (i = 0; i < ARRAY_SIZE(common_init_regs); i++)
++			writel(common_init_regs[i].val,
++			       phy_base + common_init_regs[i].reg);
++	}
++}
++
++static int ipu6_isys_driver_port_to_phy_port(struct ipu6_isys_csi2_config *cfg)
++{
++	int phy_port;
++	int ret;
++
++	if (!(cfg->nlanes == 4 || cfg->nlanes == 2 || cfg->nlanes == 1))
++		return -EINVAL;
++
++	/* B,F -> C0 A,E -> C1 C,G -> C2 D,H -> C4 */
++	/* normalize driver port number */
++	phy_port = cfg->port % 4;
++
++	/* swap port number only for A and B */
++	if (phy_port == 0)
++		phy_port = 1;
++	else if (phy_port == 1)
++		phy_port = 0;
++
++	ret = phy_port;
++
++	/* check validity per lane configuration */
++	if (cfg->nlanes == 4 && !(phy_port == 0 || phy_port == 2))
++		ret = -EINVAL;
++	else if ((cfg->nlanes == 2 || cfg->nlanes == 1) &&
++		 !(phy_port >= 0 && phy_port <= 3))
++		ret = -EINVAL;
++
++	return ret;
++}
++
++static int ipu6_isys_mcd_phy_config(struct ipu6_isys *isys)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_bus_device *adev = isys->adev;
++	const struct phy_reg **phy_config_regs;
++	struct ipu6_device *isp = adev->isp;
++	void __iomem *isp_base = isp->base;
++	struct sensor_async_sd *s_asd;
++	struct ipu6_isys_csi2_config cfg;
++	struct v4l2_async_connection *asc;
++	u8 phy_port, phy_id;
++	unsigned int i;
++	void __iomem *phy_base;
++
++	list_for_each_entry(asc, &isys->notifier.done_list, asc_entry) {
++		s_asd = container_of(asc, struct sensor_async_sd, asc);
++		cfg.port = s_asd->csi2.port;
++		cfg.nlanes = s_asd->csi2.nlanes;
++		phy_port = ipu6_isys_driver_port_to_phy_port(&cfg);
++		if (phy_port < 0) {
++			dev_err(dev, "invalid port %d for lane %d", cfg.port,
++				cfg.nlanes);
++			return -ENXIO;
++		}
++
++		phy_id = cfg.port / 4;
++		phy_base = isp_base + IPU6_ISYS_MCD_PHY_BASE(phy_id);
++		dev_dbg(dev, "port%d PHY%u lanes %u\n", cfg.port, phy_id,
++			cfg.nlanes);
++
++		phy_config_regs = config_regs[cfg.nlanes / 2];
++		cfg.port = phy_port;
++		for (i = 0; phy_config_regs[cfg.port][i].reg; i++)
++			writel(phy_config_regs[cfg.port][i].val,
++			       phy_base + phy_config_regs[cfg.port][i].reg);
++	}
++
++	return 0;
++}
++
++#define CSI_MCD_PHY_NUM		2
++static refcount_t phy_power_ref_count[CSI_MCD_PHY_NUM];
++
++int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	void __iomem *isys_base = isys->pdata->base;
++	u8 port, phy_id;
++	refcount_t *ref;
++	int ret;
++
++	port = cfg->port;
++	phy_id = port / 4;
++
++	ref = &phy_power_ref_count[phy_id];
++
++	dev_dbg(dev, "for phy %d port %d, lanes: %d\n", phy_id, port,
++		cfg->nlanes);
++
++	if (!isys_base || port >= isys->pdata->ipdata->csi2.nports) {
++		dev_warn(dev, "invalid port ID %d\n", port);
++		return -EINVAL;
++	}
++
++	if (on) {
++		if (refcount_read(ref)) {
++			dev_dbg(dev, "for phy %d is already UP", phy_id);
++			refcount_inc(ref);
++			return 0;
++		}
++
++		ret = ipu6_isys_mcd_phy_powerup_ack(isys, phy_id);
++		if (ret)
++			return ret;
++
++		ipu6_isys_mcd_phy_reset(isys, phy_id, 0);
++		ipu6_isys_mcd_phy_common_init(isys);
++
++		ret = ipu6_isys_mcd_phy_config(isys);
++		if (ret)
++			return ret;
++
++		ipu6_isys_mcd_phy_reset(isys, phy_id, 1);
++		ret = ipu6_isys_mcd_phy_ready(isys, phy_id);
++		if (ret)
++			return ret;
++
++		refcount_set(ref, 1);
++		return 0;
++	}
++
++	if (!refcount_dec_and_test(ref))
++		return 0;
++
++	return ipu6_isys_mcd_phy_powerdown_ack(isys, phy_id);
++}
+-- 
+2.43.2
+
+
+From 5d8544bd1d6dc7fce10a3bf01d10d926b8990b54 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:24 +0800
+Subject: [PATCH 17/33] media: intel/ipu6: add input system driver
+
+Input system driver do basic isys hardware setup and irq handling
+and work with fwnode and v4l2 to register the ISYS v4l2 devices.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-isys.c | 1353 ++++++++++++++++++++++
+ drivers/media/pci/intel/ipu6/ipu6-isys.h |  207 ++++
+ 2 files changed, 1560 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c
+new file mode 100644
+index 000000000000..e8983363a0da
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c
+@@ -0,0 +1,1353 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/auxiliary_bus.h>
++#include <linux/bitfield.h>
++#include <linux/bits.h>
++#include <linux/completion.h>
++#include <linux/container_of.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/firmware.h>
++#include <linux/io.h>
++#include <linux/irqreturn.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/pci.h>
++#include <linux/pm_runtime.h>
++#include <linux/pm_qos.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/string.h>
++
++#include <media/ipu-bridge.h>
++#include <media/media-device.h>
++#include <media/media-entity.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-fwnode.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-cpd.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-mmu.h"
++#include "ipu6-platform-buttress-regs.h"
++#include "ipu6-platform-isys-csi2-reg.h"
++#include "ipu6-platform-regs.h"
++
++#define IPU6_BUTTRESS_FABIC_CONTROL		0x68
++#define GDA_ENABLE_IWAKE_INDEX			2
++#define GDA_IWAKE_THRESHOLD_INDEX		1
++#define GDA_IRQ_CRITICAL_THRESHOLD_INDEX	0
++#define GDA_MEMOPEN_THRESHOLD_INDEX		3
++#define DEFAULT_DID_RATIO			90
++#define DEFAULT_IWAKE_THRESHOLD			0x42
++#define DEFAULT_MEM_OPEN_TIME			10
++#define ONE_THOUSAND_MICROSECOND		1000
++/* One page is 2KB, 8 x 16 x 16 = 2048B = 2KB */
++#define ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE	0x800
++
++/* LTR & DID value are 10 bit at most */
++#define LTR_DID_VAL_MAX				1023
++#define LTR_DEFAULT_VALUE			0x70503c19
++#define FILL_TIME_DEFAULT_VALUE			0xfff0783c
++#define LTR_DID_PKGC_2R				20
++#define LTR_SCALE_DEFAULT			5
++#define LTR_SCALE_1024NS			2
++#define DID_SCALE_1US				2
++#define DID_SCALE_32US				3
++#define REG_PKGC_PMON_CFG			0xb00
++
++#define VAL_PKGC_PMON_CFG_RESET			0x38
++#define VAL_PKGC_PMON_CFG_START			0x7
++
++#define IS_PIXEL_BUFFER_PAGES			0x80
++/*
++ * when iwake mode is disabled, the critical threshold is statically set
++ * to 75% of the IS pixel buffer, criticalThreshold = (128 * 3) / 4
++ */
++#define CRITICAL_THRESHOLD_IWAKE_DISABLE	(IS_PIXEL_BUFFER_PAGES * 3 / 4)
++
++union fabric_ctrl {
++	struct {
++		u16 ltr_val   : 10;
++		u16 ltr_scale : 3;
++		u16 reserved  : 3;
++		u16 did_val   : 10;
++		u16 did_scale : 3;
++		u16 reserved2 : 1;
++		u16 keep_power_in_D0   : 1;
++		u16 keep_power_override : 1;
++	} bits;
++	u32 value;
++};
++
++enum ltr_did_type {
++	LTR_IWAKE_ON,
++	LTR_IWAKE_OFF,
++	LTR_ISYS_ON,
++	LTR_ISYS_OFF,
++	LTR_ENHANNCE_IWAKE,
++	LTR_TYPE_MAX
++};
++
++#define ISYS_PM_QOS_VALUE	300
++
++static int isys_isr_one(struct ipu6_bus_device *adev);
++
++static int
++isys_complete_ext_device_registration(struct ipu6_isys *isys,
++				      struct v4l2_subdev *sd,
++				      struct ipu6_isys_csi2_config *csi2)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int i;
++	int ret;
++
++	for (i = 0; i < sd->entity.num_pads; i++) {
++		if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
++			break;
++	}
++
++	if (i == sd->entity.num_pads) {
++		dev_warn(dev, "no src pad in external entity\n");
++		ret = -ENOENT;
++		goto unregister_subdev;
++	}
++
++	ret = media_create_pad_link(&sd->entity, i,
++				    &isys->csi2[csi2->port].asd.sd.entity,
++				    0, 0);
++	if (ret) {
++		dev_warn(dev, "can't create link\n");
++		goto unregister_subdev;
++	}
++
++	isys->csi2[csi2->port].nlanes = csi2->nlanes;
++
++	return 0;
++
++unregister_subdev:
++	v4l2_device_unregister_subdev(sd);
++
++	return ret;
++}
++
++static void isys_stream_init(struct ipu6_isys *isys)
++{
++	u32 i;
++
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++		mutex_init(&isys->streams[i].mutex);
++		init_completion(&isys->streams[i].stream_open_completion);
++		init_completion(&isys->streams[i].stream_close_completion);
++		init_completion(&isys->streams[i].stream_start_completion);
++		init_completion(&isys->streams[i].stream_stop_completion);
++		INIT_LIST_HEAD(&isys->streams[i].queues);
++		isys->streams[i].isys = isys;
++		isys->streams[i].stream_handle = i;
++		isys->streams[i].vc = INVALID_VC_ID;
++	}
++}
++
++static void isys_csi2_unregister_subdevices(struct ipu6_isys *isys)
++{
++	const struct ipu6_isys_internal_csi2_pdata *csi2 =
++		&isys->pdata->ipdata->csi2;
++	unsigned int i;
++
++	for (i = 0; i < csi2->nports; i++)
++		ipu6_isys_csi2_cleanup(&isys->csi2[i]);
++}
++
++static int isys_csi2_register_subdevices(struct ipu6_isys *isys)
++{
++	const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
++		&isys->pdata->ipdata->csi2;
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int i;
++	int ret;
++
++	isys->csi2 = devm_kcalloc(dev, csi2_pdata->nports,
++				  sizeof(*isys->csi2), GFP_KERNEL);
++	if (!isys->csi2)
++		return -ENOMEM;
++
++	for (i = 0; i < csi2_pdata->nports; i++) {
++		ret = ipu6_isys_csi2_init(&isys->csi2[i], isys,
++					  isys->pdata->base +
++					  csi2_pdata->offsets[i], i);
++		if (ret)
++			goto fail;
++
++		isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
++	}
++
++	return 0;
++
++fail:
++	while (i--)
++		ipu6_isys_csi2_cleanup(&isys->csi2[i]);
++
++	return ret;
++}
++
++static int isys_csi2_create_media_links(struct ipu6_isys *isys)
++{
++	const struct ipu6_isys_internal_csi2_pdata *csi2_pdata =
++		&isys->pdata->ipdata->csi2;
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int i, j, k;
++	int ret;
++
++	for (i = 0; i < csi2_pdata->nports; i++) {
++		struct media_entity *sd = &isys->csi2[i].asd.sd.entity;
++
++		for (j = 0; j < NR_OF_VIDEO_DEVICE; j++) {
++			struct media_entity *v = &isys->av[j].vdev.entity;
++			u32 flag = MEDIA_LNK_FL_DYNAMIC;
++
++			for (k = CSI2_PAD_SRC; k < NR_OF_CSI2_PADS; k++) {
++				ret = media_create_pad_link(sd, k, v, 0, flag);
++				if (ret) {
++					dev_err(dev, "CSI2 can't create link\n");
++					return ret;
++				}
++			}
++		}
++	}
++
++	return 0;
++}
++
++static void isys_unregister_video_devices(struct ipu6_isys *isys)
++{
++	unsigned int i;
++
++	for (i = 0; i < NR_OF_VIDEO_DEVICE; i++)
++		ipu6_isys_video_cleanup(&isys->av[i]);
++}
++
++static int isys_register_video_devices(struct ipu6_isys *isys)
++{
++	unsigned int i;
++	int ret;
++
++	for (i = 0; i < NR_OF_VIDEO_DEVICE; i++) {
++		snprintf(isys->av[i].vdev.name, sizeof(isys->av[i].vdev.name),
++			 IPU6_ISYS_ENTITY_PREFIX " ISYS Capture %u", i);
++		isys->av[i].isys = isys;
++		isys->av[i].aq.vbq.buf_struct_size =
++			sizeof(struct ipu6_isys_video_buffer);
++		isys->av[i].pfmt = &ipu6_isys_pfmts[0];
++
++		ret = ipu6_isys_video_init(&isys->av[i]);
++		if (ret)
++			goto fail;
++	}
++
++	return 0;
++
++fail:
++	while (i--)
++		ipu6_isys_video_cleanup(&isys->av[i]);
++
++	return ret;
++}
++
++void isys_setup_hw(struct ipu6_isys *isys)
++{
++	void __iomem *base = isys->pdata->base;
++	const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold;
++	u32 irqs = 0;
++	unsigned int i, nports;
++
++	nports = isys->pdata->ipdata->csi2.nports;
++
++	/* Enable irqs for all MIPI ports */
++	for (i = 0; i < nports; i++)
++		irqs |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
++
++	writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_edge);
++	writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_lnp);
++	writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_mask);
++	writel(irqs, base + isys->pdata->ipdata->csi2.ctrl0_irq_enable);
++	writel(GENMASK(19, 0),
++	       base + isys->pdata->ipdata->csi2.ctrl0_irq_clear);
++
++	irqs = ISYS_UNISPART_IRQS;
++	writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_EDGE);
++	writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_LEVEL_NOT_PULSE);
++	writel(GENMASK(28, 0), base + IPU6_REG_ISYS_UNISPART_IRQ_CLEAR);
++	writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
++	writel(irqs, base + IPU6_REG_ISYS_UNISPART_IRQ_ENABLE);
++
++	writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG);
++	writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_MUX_REG);
++
++	/* Write CDC FIFO threshold values for isys */
++	for (i = 0; i < isys->pdata->ipdata->hw_variant.cdc_fifos; i++)
++		writel(thd[i], base + IPU6_REG_ISYS_CDC_THRESHOLD(i));
++}
++
++static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2)
++{
++	struct ipu6_isys_stream *stream;
++	unsigned int i;
++	u32 status;
++	int source;
++
++	ipu6_isys_register_errors(csi2);
++
++	status = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++		       CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
++
++	writel(status, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
++	       CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
++
++	source = csi2->asd.source;
++	for (i = 0; i < NR_OF_CSI2_VC; i++) {
++		if (status & IPU_CSI_RX_IRQ_FS_VC(i)) {
++			stream = ipu6_isys_query_stream_by_source(csi2->isys,
++								  source, i);
++			if (stream) {
++				ipu6_isys_csi2_sof_event_by_stream(stream);
++				ipu6_isys_put_stream(stream);
++			}
++		}
++
++		if (status & IPU_CSI_RX_IRQ_FE_VC(i)) {
++			stream = ipu6_isys_query_stream_by_source(csi2->isys,
++								  source, i);
++			if (stream) {
++				ipu6_isys_csi2_eof_event_by_stream(stream);
++				ipu6_isys_put_stream(stream);
++			}
++		}
++	}
++}
++
++irqreturn_t isys_isr(struct ipu6_bus_device *adev)
++{
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	void __iomem *base = isys->pdata->base;
++	u32 status_sw, status_csi;
++	u32 ctrl0_status, ctrl0_clear;
++
++	spin_lock(&isys->power_lock);
++	if (!isys->power) {
++		spin_unlock(&isys->power_lock);
++		return IRQ_NONE;
++	}
++
++	ctrl0_status = isys->pdata->ipdata->csi2.ctrl0_irq_status;
++	ctrl0_clear = isys->pdata->ipdata->csi2.ctrl0_irq_clear;
++
++	status_csi = readl(isys->pdata->base + ctrl0_status);
++	status_sw = readl(isys->pdata->base +
++			  IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
++
++	writel(ISYS_UNISPART_IRQS & ~IPU6_ISYS_UNISPART_IRQ_SW,
++	       base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
++
++	do {
++		writel(status_csi, isys->pdata->base + ctrl0_clear);
++
++		writel(status_sw, isys->pdata->base +
++		       IPU6_REG_ISYS_UNISPART_IRQ_CLEAR);
++
++		if (isys->isr_csi2_bits & status_csi) {
++			unsigned int i;
++
++			for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) {
++				/* irq from not enabled port */
++				if (!isys->csi2[i].base)
++					continue;
++				if (status_csi & IPU6_ISYS_UNISPART_IRQ_CSI2(i))
++					ipu6_isys_csi2_isr(&isys->csi2[i]);
++			}
++		}
++
++		writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG);
++
++		if (!isys_isr_one(adev))
++			status_sw = IPU6_ISYS_UNISPART_IRQ_SW;
++		else
++			status_sw = 0;
++
++		status_csi = readl(isys->pdata->base + ctrl0_status);
++		status_sw |= readl(isys->pdata->base +
++				   IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
++	} while ((status_csi & isys->isr_csi2_bits) ||
++		 (status_sw & IPU6_ISYS_UNISPART_IRQ_SW));
++
++	writel(ISYS_UNISPART_IRQS, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
++
++	spin_unlock(&isys->power_lock);
++
++	return IRQ_HANDLED;
++}
++
++static void get_lut_ltrdid(struct ipu6_isys *isys, struct ltr_did *pltr_did)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++	struct ltr_did ltrdid_default;
++
++	ltrdid_default.lut_ltr.value = LTR_DEFAULT_VALUE;
++	ltrdid_default.lut_fill_time.value = FILL_TIME_DEFAULT_VALUE;
++
++	if (iwake_watermark->ltrdid.lut_ltr.value)
++		*pltr_did = iwake_watermark->ltrdid;
++	else
++		*pltr_did = ltrdid_default;
++}
++
++static int set_iwake_register(struct ipu6_isys *isys, u32 index, u32 value)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	u32 req_id = index;
++	u32 offset = 0;
++	int ret;
++
++	ret = ipu6_fw_isys_send_proxy_token(isys, req_id, index, offset, value);
++	if (ret)
++		dev_err(dev, "write %d failed %d", index, ret);
++
++	return ret;
++}
++
++/*
++ * When input system is powered up and before enabling any new sensor capture,
++ * or after disabling any sensor capture the following values need to be set:
++ * LTR_value = LTR(usec) from calculation;
++ * LTR_scale = 2;
++ * DID_value = DID(usec) from calculation;
++ * DID_scale = 2;
++ *
++ * When input system is powered down, the LTR and DID values
++ * must be returned to the default values:
++ * LTR_value = 1023;
++ * LTR_scale = 5;
++ * DID_value = 1023;
++ * DID_scale = 2;
++ */
++static void set_iwake_ltrdid(struct ipu6_isys *isys, u16 ltr, u16 did,
++			     enum ltr_did_type use)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	u16 ltr_val, ltr_scale = LTR_SCALE_1024NS;
++	u16 did_val, did_scale = DID_SCALE_1US;
++	struct ipu6_device *isp = isys->adev->isp;
++	union fabric_ctrl fc;
++
++	switch (use) {
++	case LTR_IWAKE_ON:
++		ltr_val = min_t(u16, ltr, (u16)LTR_DID_VAL_MAX);
++		did_val = min_t(u16, did, (u16)LTR_DID_VAL_MAX);
++		ltr_scale = (ltr == LTR_DID_VAL_MAX &&
++			     did == LTR_DID_VAL_MAX) ?
++			LTR_SCALE_DEFAULT : LTR_SCALE_1024NS;
++		break;
++	case LTR_ISYS_ON:
++	case LTR_IWAKE_OFF:
++		ltr_val = LTR_DID_PKGC_2R;
++		did_val = LTR_DID_PKGC_2R;
++		break;
++	case LTR_ISYS_OFF:
++		ltr_val   = LTR_DID_VAL_MAX;
++		did_val   = LTR_DID_VAL_MAX;
++		ltr_scale = LTR_SCALE_DEFAULT;
++		break;
++	case LTR_ENHANNCE_IWAKE:
++		if (ltr == LTR_DID_VAL_MAX && did == LTR_DID_VAL_MAX) {
++			ltr_val = LTR_DID_VAL_MAX;
++			did_val = LTR_DID_VAL_MAX;
++			ltr_scale = LTR_SCALE_DEFAULT;
++		} else if (did < ONE_THOUSAND_MICROSECOND) {
++			ltr_val = ltr;
++			did_val = did;
++		} else {
++			ltr_val = ltr;
++			/* div 90% value by 32 to account for scale change */
++			did_val = did / 32;
++			did_scale = DID_SCALE_32US;
++		}
++		break;
++	default:
++		ltr_val   = LTR_DID_VAL_MAX;
++		did_val   = LTR_DID_VAL_MAX;
++		ltr_scale = LTR_SCALE_DEFAULT;
++		break;
++	}
++
++	fc.value = readl(isp->base + IPU6_BUTTRESS_FABIC_CONTROL);
++	fc.bits.ltr_val = ltr_val;
++	fc.bits.ltr_scale = ltr_scale;
++	fc.bits.did_val = did_val;
++	fc.bits.did_scale = did_scale;
++
++	dev_dbg(dev, "ltr: value %u scale %u, did: value %u scale %u\n",
++		ltr_val, ltr_scale, did_val, did_scale);
++	writel(fc.value, isp->base + IPU6_BUTTRESS_FABIC_CONTROL);
++}
++
++/*
++ * Driver may clear register GDA_ENABLE_IWAKE before FW configures the
++ * stream for debug purpose. Otherwise driver should not access this register.
++ */
++static void enable_iwake(struct ipu6_isys *isys, bool enable)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++	int ret;
++
++	mutex_lock(&iwake_watermark->mutex);
++
++	if (iwake_watermark->iwake_enabled == enable) {
++		mutex_unlock(&iwake_watermark->mutex);
++		return;
++	}
++
++	ret = set_iwake_register(isys, GDA_ENABLE_IWAKE_INDEX, enable);
++	if (!ret)
++		iwake_watermark->iwake_enabled = enable;
++
++	mutex_unlock(&iwake_watermark->mutex);
++}
++
++void update_watermark_setting(struct ipu6_isys *isys)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++	u32 iwake_threshold, iwake_critical_threshold, page_num;
++	struct device *dev = &isys->adev->auxdev.dev;
++	u32 calc_fill_time_us = 0, ltr = 0, did = 0;
++	struct video_stream_watermark *p_watermark;
++	enum ltr_did_type ltr_did_type;
++	struct list_head *stream_node;
++	u64 isys_pb_datarate_mbs = 0;
++	u32 mem_open_threshold = 0;
++	struct ltr_did ltrdid;
++	u64 threshold_bytes;
++	u32 max_sram_size;
++	u32 shift;
++
++	shift = isys->pdata->ipdata->sram_gran_shift;
++	max_sram_size = isys->pdata->ipdata->max_sram_size;
++
++	mutex_lock(&iwake_watermark->mutex);
++	if (iwake_watermark->force_iwake_disable) {
++		set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF);
++		set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
++				   CRITICAL_THRESHOLD_IWAKE_DISABLE);
++		goto unlock_exit;
++	}
++
++	if (list_empty(&iwake_watermark->video_list)) {
++		isys_pb_datarate_mbs = 0;
++	} else {
++		list_for_each(stream_node, &iwake_watermark->video_list) {
++			p_watermark = list_entry(stream_node,
++						 struct video_stream_watermark,
++						 stream_node);
++			isys_pb_datarate_mbs += p_watermark->stream_data_rate;
++		}
++	}
++	mutex_unlock(&iwake_watermark->mutex);
++
++	if (!isys_pb_datarate_mbs) {
++		enable_iwake(isys, false);
++		set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF);
++		mutex_lock(&iwake_watermark->mutex);
++		set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
++				   CRITICAL_THRESHOLD_IWAKE_DISABLE);
++		goto unlock_exit;
++	}
++
++	enable_iwake(isys, true);
++	calc_fill_time_us = max_sram_size / isys_pb_datarate_mbs;
++
++	if (isys->pdata->ipdata->enhanced_iwake) {
++		ltr = isys->pdata->ipdata->ltr;
++		did = calc_fill_time_us * DEFAULT_DID_RATIO / 100;
++		ltr_did_type = LTR_ENHANNCE_IWAKE;
++	} else {
++		get_lut_ltrdid(isys, &ltrdid);
++
++		if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th0)
++			ltr = 0;
++		else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th1)
++			ltr = ltrdid.lut_ltr.bits.val0;
++		else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th2)
++			ltr = ltrdid.lut_ltr.bits.val1;
++		else if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th3)
++			ltr = ltrdid.lut_ltr.bits.val2;
++		else
++			ltr = ltrdid.lut_ltr.bits.val3;
++
++		did = calc_fill_time_us - ltr;
++		ltr_did_type = LTR_IWAKE_ON;
++	}
++
++	set_iwake_ltrdid(isys, ltr, did, ltr_did_type);
++
++	/* calculate iwake threshold with 2KB granularity pages */
++	threshold_bytes = did * isys_pb_datarate_mbs;
++	iwake_threshold = max_t(u32, 1, threshold_bytes >> shift);
++	iwake_threshold = min_t(u32, iwake_threshold, max_sram_size);
++
++	mutex_lock(&iwake_watermark->mutex);
++	if (isys->pdata->ipdata->enhanced_iwake) {
++		set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX,
++				   DEFAULT_IWAKE_THRESHOLD);
++		/* calculate number of pages that will be filled in 10 usec */
++		page_num = (DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) /
++			ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE;
++		page_num += ((DEFAULT_MEM_OPEN_TIME * isys_pb_datarate_mbs) %
++			     ISF_DMA_TOP_GDA_PROFERTY_PAGE_SIZE) ? 1 : 0;
++		mem_open_threshold = isys->pdata->ipdata->memopen_threshold;
++		mem_open_threshold = max_t(u32, mem_open_threshold, page_num);
++		dev_dbg(dev, "mem_open_threshold: %u\n", mem_open_threshold);
++		set_iwake_register(isys, GDA_MEMOPEN_THRESHOLD_INDEX,
++				   mem_open_threshold);
++	} else {
++		set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX,
++				   iwake_threshold);
++	}
++
++	iwake_critical_threshold = iwake_threshold +
++		(IS_PIXEL_BUFFER_PAGES - iwake_threshold) / 2;
++
++	dev_dbg(dev, "threshold: %u critical: %u\n", iwake_threshold,
++		iwake_critical_threshold);
++
++	set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
++			   iwake_critical_threshold);
++
++	writel(VAL_PKGC_PMON_CFG_RESET,
++	       isys->adev->isp->base + REG_PKGC_PMON_CFG);
++	writel(VAL_PKGC_PMON_CFG_START,
++	       isys->adev->isp->base + REG_PKGC_PMON_CFG);
++unlock_exit:
++	mutex_unlock(&iwake_watermark->mutex);
++}
++
++static void isys_iwake_watermark_init(struct ipu6_isys *isys)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++
++	INIT_LIST_HEAD(&iwake_watermark->video_list);
++	mutex_init(&iwake_watermark->mutex);
++
++	iwake_watermark->ltrdid.lut_ltr.value = 0;
++	iwake_watermark->isys = isys;
++	iwake_watermark->iwake_enabled = false;
++	iwake_watermark->force_iwake_disable = false;
++}
++
++static void isys_iwake_watermark_cleanup(struct ipu6_isys *isys)
++{
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++
++	mutex_lock(&iwake_watermark->mutex);
++	list_del(&iwake_watermark->video_list);
++	mutex_unlock(&iwake_watermark->mutex);
++
++	mutex_destroy(&iwake_watermark->mutex);
++}
++
++/* The .bound() notifier callback when a match is found */
++static int isys_notifier_bound(struct v4l2_async_notifier *notifier,
++			       struct v4l2_subdev *sd,
++			       struct v4l2_async_connection *asc)
++{
++	struct ipu6_isys *isys =
++		container_of(notifier, struct ipu6_isys, notifier);
++	struct sensor_async_sd *s_asd =
++		container_of(asc, struct sensor_async_sd, asc);
++	int ret;
++
++	ret = ipu_bridge_instantiate_vcm(sd->dev);
++	if (ret) {
++		dev_err(&isys->adev->auxdev.dev, "instantiate vcm failed\n");
++		return ret;
++	}
++
++	dev_dbg(&isys->adev->auxdev.dev, "bind %s nlanes is %d port is %d\n",
++		sd->name, s_asd->csi2.nlanes, s_asd->csi2.port);
++	ret = isys_complete_ext_device_registration(isys, sd, &s_asd->csi2);
++	if (ret)
++		return ret;
++
++	return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
++}
++
++static int isys_notifier_complete(struct v4l2_async_notifier *notifier)
++{
++	struct ipu6_isys *isys =
++		container_of(notifier, struct ipu6_isys, notifier);
++
++	return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
++}
++
++static const struct v4l2_async_notifier_operations isys_async_ops = {
++	.bound = isys_notifier_bound,
++	.complete = isys_notifier_complete,
++};
++
++#define ISYS_MAX_PORTS 8
++static int isys_notifier_init(struct ipu6_isys *isys)
++{
++	struct ipu6_device *isp = isys->adev->isp;
++	struct device *dev = &isp->pdev->dev;
++	unsigned int i;
++	int ret;
++
++	v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev);
++
++	for (i = 0; i < ISYS_MAX_PORTS; i++) {
++		struct v4l2_fwnode_endpoint vep = {
++			.bus_type = V4L2_MBUS_CSI2_DPHY
++		};
++		struct sensor_async_sd *s_asd;
++		struct fwnode_handle *ep;
++
++		ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0,
++						FWNODE_GRAPH_ENDPOINT_NEXT);
++		if (!ep)
++			continue;
++
++		ret = v4l2_fwnode_endpoint_parse(ep, &vep);
++		if (ret) {
++			dev_err(dev, "fwnode endpoint parse failed: %d\n", ret);
++			goto err_parse;
++		}
++
++		s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep,
++							struct sensor_async_sd);
++		if (IS_ERR(s_asd)) {
++			ret = PTR_ERR(s_asd);
++			dev_err(dev, "add remove fwnode failed: %d\n", ret);
++			goto err_parse;
++		}
++
++		s_asd->csi2.port = vep.base.port;
++		s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes;
++
++		dev_dbg(dev, "remote endpoint port %d with %d lanes added\n",
++			s_asd->csi2.port, s_asd->csi2.nlanes);
++
++		fwnode_handle_put(ep);
++
++		continue;
++
++err_parse:
++		fwnode_handle_put(ep);
++		return ret;
++	}
++
++	isys->notifier.ops = &isys_async_ops;
++	ret = v4l2_async_nf_register(&isys->notifier);
++	if (ret) {
++		dev_err(dev, "failed to register async notifier : %d\n", ret);
++		v4l2_async_nf_cleanup(&isys->notifier);
++	}
++
++	return ret;
++}
++
++static void isys_notifier_cleanup(struct ipu6_isys *isys)
++{
++	v4l2_async_nf_unregister(&isys->notifier);
++	v4l2_async_nf_cleanup(&isys->notifier);
++}
++
++static int isys_register_devices(struct ipu6_isys *isys)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct pci_dev *pdev = isys->adev->isp->pdev;
++	int ret;
++
++	isys->media_dev.dev = dev;
++	media_device_pci_init(&isys->media_dev,
++			      pdev, IPU6_MEDIA_DEV_MODEL_NAME);
++
++	strscpy(isys->v4l2_dev.name, isys->media_dev.model,
++		sizeof(isys->v4l2_dev.name));
++
++	ret = media_device_register(&isys->media_dev);
++	if (ret < 0)
++		goto out_media_device_unregister;
++
++	isys->v4l2_dev.mdev = &isys->media_dev;
++	isys->v4l2_dev.ctrl_handler = NULL;
++
++	ret = v4l2_device_register(dev->parent, &isys->v4l2_dev);
++	if (ret < 0)
++		goto out_media_device_unregister;
++
++	ret = isys_register_video_devices(isys);
++	if (ret)
++		goto out_v4l2_device_unregister;
++
++	ret = isys_csi2_register_subdevices(isys);
++	if (ret)
++		goto out_isys_unregister_video_device;
++
++	ret = isys_csi2_create_media_links(isys);
++	if (ret)
++		goto out_isys_unregister_subdevices;
++
++	ret = isys_notifier_init(isys);
++	if (ret)
++		goto out_isys_unregister_subdevices;
++
++	return 0;
++
++out_isys_unregister_subdevices:
++	isys_csi2_unregister_subdevices(isys);
++
++out_isys_unregister_video_device:
++	isys_unregister_video_devices(isys);
++
++out_v4l2_device_unregister:
++	v4l2_device_unregister(&isys->v4l2_dev);
++
++out_media_device_unregister:
++	media_device_unregister(&isys->media_dev);
++	media_device_cleanup(&isys->media_dev);
++
++	dev_err(dev, "failed to register isys devices\n");
++
++	return ret;
++}
++
++static void isys_unregister_devices(struct ipu6_isys *isys)
++{
++	isys_unregister_video_devices(isys);
++	isys_csi2_unregister_subdevices(isys);
++	v4l2_device_unregister(&isys->v4l2_dev);
++	media_device_unregister(&isys->media_dev);
++	media_device_cleanup(&isys->media_dev);
++}
++
++static int isys_runtime_pm_resume(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	struct ipu6_device *isp = adev->isp;
++	unsigned long flags;
++	int ret;
++
++	if (!isys)
++		return 0;
++
++	ret = ipu6_mmu_hw_init(adev->mmu);
++	if (ret)
++		return ret;
++
++	cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE);
++
++	ret = ipu6_buttress_start_tsc_sync(isp);
++	if (ret)
++		return ret;
++
++	spin_lock_irqsave(&isys->power_lock, flags);
++	isys->power = 1;
++	spin_unlock_irqrestore(&isys->power_lock, flags);
++
++	isys_setup_hw(isys);
++
++	set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON);
++
++	return 0;
++}
++
++static int isys_runtime_pm_suspend(struct device *dev)
++{
++	struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
++	struct ipu6_isys *isys;
++	unsigned long flags;
++
++	isys = dev_get_drvdata(dev);
++	if (!isys)
++		return 0;
++
++	spin_lock_irqsave(&isys->power_lock, flags);
++	isys->power = 0;
++	spin_unlock_irqrestore(&isys->power_lock, flags);
++
++	mutex_lock(&isys->mutex);
++	isys->need_reset = false;
++	mutex_unlock(&isys->mutex);
++
++	isys->phy_termcal_val = 0;
++	cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
++
++	set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF);
++
++	ipu6_mmu_hw_cleanup(adev->mmu);
++
++	return 0;
++}
++
++static int isys_suspend(struct device *dev)
++{
++	struct ipu6_isys *isys = dev_get_drvdata(dev);
++
++	/* If stream is open, refuse to suspend */
++	if (isys->stream_opened)
++		return -EBUSY;
++
++	return 0;
++}
++
++static int isys_resume(struct device *dev)
++{
++	return 0;
++}
++
++static const struct dev_pm_ops isys_pm_ops = {
++	.runtime_suspend = isys_runtime_pm_suspend,
++	.runtime_resume = isys_runtime_pm_resume,
++	.suspend = isys_suspend,
++	.resume = isys_resume,
++};
++
++static void isys_remove(struct auxiliary_device *auxdev)
++{
++	struct ipu6_bus_device *adev = auxdev_to_adev(auxdev);
++	struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev);
++	struct ipu6_device *isp = adev->isp;
++	struct isys_fw_msgs *fwmsg, *safe;
++	unsigned int i;
++
++	list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head)
++		dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs),
++			       fwmsg, fwmsg->dma_addr, 0);
++
++	list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head)
++		dma_free_attrs(&auxdev->dev, sizeof(struct isys_fw_msgs),
++			       fwmsg, fwmsg->dma_addr, 0);
++
++	isys_unregister_devices(isys);
++	isys_notifier_cleanup(isys);
++
++	cpu_latency_qos_remove_request(&isys->pm_qos);
++
++	if (!isp->secure_mode) {
++		ipu6_cpd_free_pkg_dir(adev);
++		ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt);
++		release_firmware(adev->fw);
++	}
++
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++)
++		mutex_destroy(&isys->streams[i].mutex);
++
++	isys_iwake_watermark_cleanup(isys);
++	mutex_destroy(&isys->stream_mutex);
++	mutex_destroy(&isys->mutex);
++}
++
++static int alloc_fw_msg_bufs(struct ipu6_isys *isys, int amount)
++{
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct isys_fw_msgs *addr;
++	dma_addr_t dma_addr;
++	unsigned long flags;
++	unsigned int i;
++
++	for (i = 0; i < amount; i++) {
++		addr = dma_alloc_attrs(dev, sizeof(struct isys_fw_msgs),
++				       &dma_addr, GFP_KERNEL, 0);
++		if (!addr)
++			break;
++		addr->dma_addr = dma_addr;
++
++		spin_lock_irqsave(&isys->listlock, flags);
++		list_add(&addr->head, &isys->framebuflist);
++		spin_unlock_irqrestore(&isys->listlock, flags);
++	}
++
++	if (i == amount)
++		return 0;
++
++	spin_lock_irqsave(&isys->listlock, flags);
++	while (!list_empty(&isys->framebuflist)) {
++		addr = list_first_entry(&isys->framebuflist,
++					struct isys_fw_msgs, head);
++		list_del(&addr->head);
++		spin_unlock_irqrestore(&isys->listlock, flags);
++		dma_free_attrs(dev, sizeof(struct isys_fw_msgs), addr,
++			       addr->dma_addr, 0);
++		spin_lock_irqsave(&isys->listlock, flags);
++	}
++	spin_unlock_irqrestore(&isys->listlock, flags);
++
++	return -ENOMEM;
++}
++
++struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream)
++{
++	struct ipu6_isys *isys = stream->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct isys_fw_msgs *msg;
++	unsigned long flags;
++	int ret;
++
++	spin_lock_irqsave(&isys->listlock, flags);
++	if (list_empty(&isys->framebuflist)) {
++		spin_unlock_irqrestore(&isys->listlock, flags);
++		dev_dbg(dev, "Frame list empty\n");
++
++		ret = alloc_fw_msg_bufs(isys, 5);
++		if (ret < 0)
++			return NULL;
++
++		spin_lock_irqsave(&isys->listlock, flags);
++		if (list_empty(&isys->framebuflist)) {
++			spin_unlock_irqrestore(&isys->listlock, flags);
++			dev_err(dev, "Frame list empty\n");
++			return NULL;
++		}
++	}
++	msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head);
++	list_move(&msg->head, &isys->framebuflist_fw);
++	spin_unlock_irqrestore(&isys->listlock, flags);
++	memset(&msg->fw_msg, 0, sizeof(msg->fw_msg));
++
++	return msg;
++}
++
++void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys)
++{
++	struct isys_fw_msgs *fwmsg, *fwmsg0;
++	unsigned long flags;
++
++	spin_lock_irqsave(&isys->listlock, flags);
++	list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head)
++		list_move(&fwmsg->head, &isys->framebuflist);
++	spin_unlock_irqrestore(&isys->listlock, flags);
++}
++
++void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data)
++{
++	struct isys_fw_msgs *msg;
++	unsigned long flags;
++	u64 *ptr = (u64 *)data;
++
++	if (!ptr)
++		return;
++
++	spin_lock_irqsave(&isys->listlock, flags);
++	msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy);
++	list_move(&msg->head, &isys->framebuflist);
++	spin_unlock_irqrestore(&isys->listlock, flags);
++}
++
++static int isys_probe(struct auxiliary_device *auxdev,
++		      const struct auxiliary_device_id *auxdev_id)
++{
++	struct ipu6_bus_device *adev = auxdev_to_adev(auxdev);
++	struct ipu6_device *isp = adev->isp;
++	const struct firmware *fw;
++	struct ipu6_isys *isys;
++	unsigned int i;
++	int ret;
++
++	if (!isp->bus_ready_to_probe)
++		return -EPROBE_DEFER;
++
++	isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL);
++	if (!isys)
++		return -ENOMEM;
++
++	ret = ipu6_mmu_hw_init(adev->mmu);
++	if (ret)
++		return ret;
++
++	adev->auxdrv_data =
++		(const struct ipu6_auxdrv_data *)auxdev_id->driver_data;
++	adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver);
++	isys->adev = adev;
++	isys->pdata = adev->pdata;
++
++	/* initial sensor type */
++	isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
++
++	spin_lock_init(&isys->streams_lock);
++	spin_lock_init(&isys->power_lock);
++	isys->power = 0;
++	isys->phy_termcal_val = 0;
++
++	mutex_init(&isys->mutex);
++	mutex_init(&isys->stream_mutex);
++
++	spin_lock_init(&isys->listlock);
++	INIT_LIST_HEAD(&isys->framebuflist);
++	INIT_LIST_HEAD(&isys->framebuflist_fw);
++
++	isys->line_align = IPU6_ISYS_2600_MEM_LINE_ALIGN;
++	isys->icache_prefetch = 0;
++
++	dev_set_drvdata(&auxdev->dev, isys);
++
++	isys_stream_init(isys);
++
++	if (!isp->secure_mode) {
++		fw = isp->cpd_fw;
++		ret = ipu6_buttress_map_fw_image(adev, fw, &adev->fw_sgt);
++		if (ret)
++			goto release_firmware;
++
++		ret = ipu6_cpd_create_pkg_dir(adev, isp->cpd_fw->data);
++		if (ret)
++			goto remove_shared_buffer;
++	}
++
++	cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
++
++	ret = alloc_fw_msg_bufs(isys, 20);
++	if (ret < 0)
++		goto out_remove_pkg_dir_shared_buffer;
++
++	isys_iwake_watermark_init(isys);
++
++	if (is_ipu6se(adev->isp->hw_ver))
++		isys->phy_set_power = ipu6_isys_jsl_phy_set_power;
++	else if (is_ipu6ep_mtl(adev->isp->hw_ver))
++		isys->phy_set_power = ipu6_isys_dwc_phy_set_power;
++	else
++		isys->phy_set_power = ipu6_isys_mcd_phy_set_power;
++
++	ret = isys_register_devices(isys);
++	if (ret)
++		goto out_remove_pkg_dir_shared_buffer;
++
++	ipu6_mmu_hw_cleanup(adev->mmu);
++
++	return 0;
++
++out_remove_pkg_dir_shared_buffer:
++	if (!isp->secure_mode)
++		ipu6_cpd_free_pkg_dir(adev);
++remove_shared_buffer:
++	if (!isp->secure_mode)
++		ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt);
++release_firmware:
++	if (!isp->secure_mode)
++		release_firmware(adev->fw);
++
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++)
++		mutex_destroy(&isys->streams[i].mutex);
++
++	mutex_destroy(&isys->mutex);
++	mutex_destroy(&isys->stream_mutex);
++
++	ipu6_mmu_hw_cleanup(adev->mmu);
++
++	return ret;
++}
++
++struct fwmsg {
++	int type;
++	char *msg;
++	bool valid_ts;
++};
++
++static const struct fwmsg fw_msg[] = {
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
++	 "STREAM_START_AND_CAPTURE_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
++	 "STREAM_START_AND_CAPTURE_DONE", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1},
++	{IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1},
++	{-1, "UNKNOWN MESSAGE", 0}
++};
++
++static u32 resp_type_to_index(int type)
++{
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(fw_msg); i++)
++		if (fw_msg[i].type == type)
++			return i;
++
++	return  ARRAY_SIZE(fw_msg) - 1;
++}
++
++static int isys_isr_one(struct ipu6_bus_device *adev)
++{
++	struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
++	struct ipu6_fw_isys_resp_info_abi *resp;
++	struct ipu6_isys_stream *stream;
++	struct ipu6_isys_csi2 *csi2 = NULL;
++	u32 index;
++	u64 ts;
++
++	if (!isys->fwcom)
++		return 1;
++
++	resp = ipu6_fw_isys_get_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES);
++	if (!resp)
++		return 1;
++
++	ts = (u64)resp->timestamp[1] << 32 | resp->timestamp[0];
++
++	index = resp_type_to_index(resp->type);
++	dev_dbg(&adev->auxdev.dev,
++		"FW resp %02d %s, stream %u, ts 0x%16.16llx, pin %d\n",
++		resp->type, fw_msg[index].msg, resp->stream_handle,
++		fw_msg[index].valid_ts ? ts : 0, resp->pin_id);
++
++	if (resp->error_info.error == IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION)
++		/* Suspension is kind of special case: not enough buffers */
++		dev_dbg(&adev->auxdev.dev,
++			"FW error resp SUSPENSION, details %d\n",
++			resp->error_info.error_details);
++	else if (resp->error_info.error)
++		dev_dbg(&adev->auxdev.dev,
++			"FW error resp error %d, details %d\n",
++			resp->error_info.error, resp->error_info.error_details);
++
++	if (resp->stream_handle >= IPU6_ISYS_MAX_STREAMS) {
++		dev_err(&adev->auxdev.dev, "bad stream handle %u\n",
++			resp->stream_handle);
++		goto leave;
++	}
++
++	stream = ipu6_isys_query_stream_by_handle(isys, resp->stream_handle);
++	if (!stream) {
++		dev_err(&adev->auxdev.dev, "stream of stream_handle %u is unused\n",
++			resp->stream_handle);
++		goto leave;
++	}
++	stream->error = resp->error_info.error;
++
++	csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
++
++	switch (resp->type) {
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE:
++		complete(&stream->stream_open_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK:
++		complete(&stream->stream_close_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK:
++		complete(&stream->stream_start_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK:
++		complete(&stream->stream_start_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK:
++		complete(&stream->stream_stop_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK:
++		complete(&stream->stream_stop_completion);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY:
++		/*
++		 * firmware only release the capture msg until software
++		 * get pin_data_ready event
++		 */
++		ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), resp->buf_id);
++		if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS &&
++		    stream->output_pins[resp->pin_id].pin_ready)
++			stream->output_pins[resp->pin_id].pin_ready(stream,
++								    resp);
++		else
++			dev_warn(&adev->auxdev.dev,
++				 "%d:No data pin ready handler for pin id %d\n",
++				 resp->stream_handle, resp->pin_id);
++		if (csi2)
++			ipu6_isys_csi2_error(csi2);
++
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK:
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE:
++	case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE:
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF:
++
++		ipu6_isys_csi2_sof_event_by_stream(stream);
++		stream->seq[stream->seq_index].sequence =
++			atomic_read(&stream->sequence) - 1;
++		stream->seq[stream->seq_index].timestamp = ts;
++		dev_dbg(&adev->auxdev.dev,
++			"sof: handle %d: (index %u), timestamp 0x%16.16llx\n",
++			resp->stream_handle,
++			stream->seq[stream->seq_index].sequence, ts);
++		stream->seq_index = (stream->seq_index + 1)
++			% IPU6_ISYS_MAX_PARALLEL_SOF;
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF:
++		ipu6_isys_csi2_eof_event_by_stream(stream);
++		dev_dbg(&adev->auxdev.dev,
++			"eof: handle %d: (index %u), timestamp 0x%16.16llx\n",
++			resp->stream_handle,
++			stream->seq[stream->seq_index].sequence, ts);
++		break;
++	case IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY:
++		break;
++	default:
++		dev_err(&adev->auxdev.dev, "%d:unknown response type %u\n",
++			resp->stream_handle, resp->type);
++		break;
++	}
++
++	ipu6_isys_put_stream(stream);
++leave:
++	ipu6_fw_isys_put_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES);
++	return 0;
++}
++
++static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data = {
++	.isr = isys_isr,
++	.isr_threaded = NULL,
++	.wake_isr_thread = false,
++};
++
++static const struct auxiliary_device_id ipu6_isys_id_table[] = {
++	{
++		.name = "intel_ipu6.isys",
++		.driver_data = (kernel_ulong_t)&ipu6_isys_auxdrv_data,
++	},
++	{ }
++};
++MODULE_DEVICE_TABLE(auxiliary, ipu6_isys_id_table);
++
++static struct auxiliary_driver isys_driver = {
++	.name = IPU6_ISYS_NAME,
++	.probe = isys_probe,
++	.remove = isys_remove,
++	.id_table = ipu6_isys_id_table,
++	.driver = {
++		.pm = &isys_pm_ops,
++	},
++};
++
++module_auxiliary_driver(isys_driver);
++
++MODULE_AUTHOR("Sakari Ailus <sakari.ailus@linux.intel.com>");
++MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
++MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
++MODULE_AUTHOR("Yunliang Ding <yunliang.ding@intel.com>");
++MODULE_AUTHOR("Hongju Wang <hongju.wang@intel.com>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Intel IPU6 input system driver");
++MODULE_IMPORT_NS(INTEL_IPU6);
++MODULE_IMPORT_NS(INTEL_IPU_BRIDGE);
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h
+new file mode 100644
+index 000000000000..cf7a90bfedc9
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h
+@@ -0,0 +1,207 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_H
++#define IPU6_ISYS_H
++
++#include <linux/irqreturn.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++#include <linux/pm_qos.h>
++#include <linux/spinlock_types.h>
++#include <linux/types.h>
++
++#include <media/media-device.h>
++#include <media/v4l2-async.h>
++#include <media/v4l2-device.h>
++
++#include "ipu6.h"
++#include "ipu6-fw-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-isys-video.h"
++
++struct ipu6_bus_device;
++
++#define IPU6_ISYS_ENTITY_PREFIX		"Intel IPU6"
++/* FW support max 16 streams */
++#define IPU6_ISYS_MAX_STREAMS		16
++#define ISYS_UNISPART_IRQS	(IPU6_ISYS_UNISPART_IRQ_SW |	\
++				 IPU6_ISYS_UNISPART_IRQ_CSI0 |	\
++				 IPU6_ISYS_UNISPART_IRQ_CSI1)
++
++#define IPU6_ISYS_2600_MEM_LINE_ALIGN	64
++
++/*
++ * Current message queue configuration. These must be big enough
++ * so that they never gets full. Queues are located in system memory
++ */
++#define IPU6_ISYS_SIZE_RECV_QUEUE 40
++#define IPU6_ISYS_SIZE_SEND_QUEUE 40
++#define IPU6_ISYS_SIZE_PROXY_RECV_QUEUE 5
++#define IPU6_ISYS_SIZE_PROXY_SEND_QUEUE 5
++#define IPU6_ISYS_NUM_RECV_QUEUE 1
++
++#define IPU6_ISYS_MIN_WIDTH		1U
++#define IPU6_ISYS_MIN_HEIGHT		1U
++#define IPU6_ISYS_MAX_WIDTH		4672U
++#define IPU6_ISYS_MAX_HEIGHT		3416U
++
++/* the threshold granularity is 2KB on IPU6 */
++#define IPU6_SRAM_GRANULARITY_SHIFT	11
++#define IPU6_SRAM_GRANULARITY_SIZE	2048
++/* the threshold granularity is 1KB on IPU6SE */
++#define IPU6SE_SRAM_GRANULARITY_SHIFT	10
++#define IPU6SE_SRAM_GRANULARITY_SIZE	1024
++/* IS pixel buffer is 256KB, MaxSRAMSize is 200KB on IPU6 */
++#define IPU6_MAX_SRAM_SIZE			(200 << 10)
++/* IS pixel buffer is 128KB, MaxSRAMSize is 96KB on IPU6SE */
++#define IPU6SE_MAX_SRAM_SIZE			(96 << 10)
++
++#define IPU6EP_LTR_VALUE			200
++#define IPU6EP_MIN_MEMOPEN_TH			0x4
++#define IPU6EP_MTL_LTR_VALUE			1023
++#define IPU6EP_MTL_MIN_MEMOPEN_TH		0xc
++
++struct ltr_did {
++	union {
++		u32 value;
++		struct {
++			u8 val0;
++			u8 val1;
++			u8 val2;
++			u8 val3;
++		} bits;
++	} lut_ltr;
++	union {
++		u32 value;
++		struct {
++			u8 th0;
++			u8 th1;
++			u8 th2;
++			u8 th3;
++		} bits;
++	} lut_fill_time;
++};
++
++struct isys_iwake_watermark {
++	bool iwake_enabled;
++	bool force_iwake_disable;
++	u32 iwake_threshold;
++	u64 isys_pixelbuffer_datarate;
++	struct ltr_did ltrdid;
++	struct mutex mutex; /* protect whole struct */
++	struct ipu6_isys *isys;
++	struct list_head video_list;
++};
++
++struct ipu6_isys_csi2_config {
++	u32 nlanes;
++	u32 port;
++};
++
++struct sensor_async_sd {
++	struct v4l2_async_connection asc;
++	struct ipu6_isys_csi2_config csi2;
++};
++
++/*
++ * struct ipu6_isys
++ *
++ * @media_dev: Media device
++ * @v4l2_dev: V4L2 device
++ * @adev: ISYS bus device
++ * @power: Is ISYS powered on or not?
++ * @isr_bits: Which bits does the ISR handle?
++ * @power_lock: Serialise access to power (power state in general)
++ * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers
++ * @streams_lock: serialise access to streams
++ * @streams: streams per firmware stream ID
++ * @fwcom: fw communication layer private pointer
++ *         or optional external library private pointer
++ * @line_align: line alignment in memory
++ * @phy_termcal_val: the termination calibration value, only used for DWC PHY
++ * @need_reset: Isys requires d0i0->i3 transition
++ * @ref_count: total number of callers fw open
++ * @mutex: serialise access isys video open/release related operations
++ * @stream_mutex: serialise stream start and stop, queueing requests
++ * @pdata: platform data pointer
++ * @csi2: CSI-2 receivers
++ */
++struct ipu6_isys {
++	struct media_device media_dev;
++	struct v4l2_device v4l2_dev;
++	struct ipu6_bus_device *adev;
++
++	int power;
++	spinlock_t power_lock;
++	u32 isr_csi2_bits;
++	u32 csi2_rx_ctrl_cached;
++	spinlock_t streams_lock;
++	struct ipu6_isys_stream streams[IPU6_ISYS_MAX_STREAMS];
++	int streams_ref_count[IPU6_ISYS_MAX_STREAMS];
++	void *fwcom;
++	unsigned int line_align;
++	u32 phy_termcal_val;
++	bool need_reset;
++	bool icache_prefetch;
++	bool csi2_cse_ipc_not_supported;
++	unsigned int ref_count;
++	unsigned int stream_opened;
++	unsigned int sensor_type;
++
++	struct mutex mutex;
++	struct mutex stream_mutex;
++
++	struct ipu6_isys_pdata *pdata;
++
++	int (*phy_set_power)(struct ipu6_isys *isys,
++			     struct ipu6_isys_csi2_config *cfg,
++			     const struct ipu6_isys_csi2_timing *timing,
++			     bool on);
++
++	struct ipu6_isys_csi2 *csi2;
++	struct ipu6_isys_video av[NR_OF_VIDEO_DEVICE];
++
++	struct pm_qos_request pm_qos;
++	spinlock_t listlock;	/* Protect framebuflist */
++	struct list_head framebuflist;
++	struct list_head framebuflist_fw;
++	struct v4l2_async_notifier notifier;
++	struct isys_iwake_watermark iwake_watermark;
++};
++
++struct isys_fw_msgs {
++	union {
++		u64 dummy;
++		struct ipu6_fw_isys_frame_buff_set_abi frame;
++		struct ipu6_fw_isys_stream_cfg_data_abi stream;
++	} fw_msg;
++	struct list_head head;
++	dma_addr_t dma_addr;
++};
++
++struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream);
++void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, u64 data);
++void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys);
++
++extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops;
++
++void isys_setup_hw(struct ipu6_isys *isys);
++irqreturn_t isys_isr(struct ipu6_bus_device *adev);
++void update_watermark_setting(struct ipu6_isys *isys);
++
++int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on);
++
++int ipu6_isys_dwc_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on);
++
++int ipu6_isys_jsl_phy_set_power(struct ipu6_isys *isys,
++				struct ipu6_isys_csi2_config *cfg,
++				const struct ipu6_isys_csi2_timing *timing,
++				bool on);
++#endif /* IPU6_ISYS_H */
+-- 
+2.43.2
+
+
+From 7cdb944c1cc8beb6f61268e2fe177d585fa5f415 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:25 +0800
+Subject: [PATCH 18/33] media: intel/ipu6: input system video capture nodes
+
+Register v4l2 video device and setup the vb2 queue to
+support basic video capture. Video streaming callback
+will trigger the input system driver to construct a
+input system stream configuration for firmware based on
+data type and stream ID and then queue buffers to firmware
+to do capture.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ .../media/pci/intel/ipu6/ipu6-isys-queue.c    |  825 +++++++++++
+ .../media/pci/intel/ipu6/ipu6-isys-queue.h    |   76 +
+ .../media/pci/intel/ipu6/ipu6-isys-video.c    | 1253 +++++++++++++++++
+ .../media/pci/intel/ipu6/ipu6-isys-video.h    |  136 ++
+ 4 files changed, 2290 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+ create mode 100644 drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+new file mode 100644
+index 000000000000..735d2d642d87
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+@@ -0,0 +1,825 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++#include <linux/atomic.h>
++#include <linux/bug.h>
++#include <linux/device.h>
++#include <linux/list.h>
++#include <linux/lockdep.h>
++#include <linux/mutex.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-subdev.h>
++#include <media/videobuf2-dma-contig.h>
++#include <media/videobuf2-v4l2.h>
++
++#include "ipu6-bus.h"
++#include "ipu6-fw-isys.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-video.h"
++
++static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
++		       unsigned int *num_planes, unsigned int sizes[],
++		       struct device *alloc_devs[])
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	bool use_fmt = false;
++	unsigned int i;
++	u32 size;
++
++	/* num_planes == 0: we're being called through VIDIOC_REQBUFS */
++	if (!*num_planes) {
++		use_fmt = true;
++		*num_planes = av->mpix.num_planes;
++	}
++
++	for (i = 0; i < *num_planes; i++) {
++		size = av->mpix.plane_fmt[i].sizeimage;
++		if (use_fmt) {
++			sizes[i] = size;
++		} else if (sizes[i] < size) {
++			dev_err(dev, "%s: queue setup: plane %d size %u < %u\n",
++				av->vdev.name, i, sizes[i], size);
++			return -EINVAL;
++		}
++
++		alloc_devs[i] = aq->dev;
++	}
++
++	return 0;
++}
++
++static int ipu6_isys_buf_prepare(struct vb2_buffer *vb)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++
++	dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n",
++		av->vdev.name, av->mpix.plane_fmt[0].sizeimage,
++		vb2_plane_size(vb, 0));
++
++	if (av->mpix.plane_fmt[0].sizeimage > vb2_plane_size(vb, 0))
++		return -EINVAL;
++
++	vb2_set_plane_payload(vb, 0, av->mpix.plane_fmt[0].bytesperline *
++			      av->mpix.height);
++	vb->planes[0].data_offset = 0;
++
++	return 0;
++}
++
++/*
++ * Queue a buffer list back to incoming or active queues. The buffers
++ * are removed from the buffer list.
++ */
++void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl,
++				 unsigned long op_flags,
++				 enum vb2_buffer_state state)
++{
++	struct ipu6_isys_buffer *ib, *ib_safe;
++	unsigned long flags;
++	bool first = true;
++
++	if (!bl)
++		return;
++
++	WARN_ON_ONCE(!bl->nbufs);
++	WARN_ON_ONCE(op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE &&
++		     op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING);
++
++	list_for_each_entry_safe(ib, ib_safe, &bl->head, head) {
++		struct ipu6_isys_video *av;
++		struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++		struct ipu6_isys_queue *aq =
++			vb2_queue_to_isys_queue(vb->vb2_queue);
++		struct device *dev;
++
++		av = ipu6_isys_queue_to_video(aq);
++		dev = &av->isys->adev->auxdev.dev;
++		spin_lock_irqsave(&aq->lock, flags);
++		list_del(&ib->head);
++		if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_ACTIVE)
++			list_add(&ib->head, &aq->active);
++		else if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_INCOMING)
++			list_add_tail(&ib->head, &aq->incoming);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		if (op_flags & IPU6_ISYS_BUFFER_LIST_FL_SET_STATE)
++			vb2_buffer_done(vb, state);
++
++		if (first) {
++			dev_dbg(dev,
++				"queue buf list %p flags %lx, s %d, %d bufs\n",
++				bl, op_flags, state, bl->nbufs);
++			first = false;
++		}
++
++		bl->nbufs--;
++	}
++
++	WARN_ON(bl->nbufs);
++}
++
++/*
++ * flush_firmware_streamon_fail() - Flush in cases where requests may
++ * have been queued to firmware and the *firmware streamon fails for a
++ * reason or another.
++ */
++static void flush_firmware_streamon_fail(struct ipu6_isys_stream *stream)
++{
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_queue *aq;
++	unsigned long flags;
++
++	lockdep_assert_held(&stream->mutex);
++
++	list_for_each_entry(aq, &stream->queues, node) {
++		struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++		struct ipu6_isys_buffer *ib, *ib_safe;
++
++		spin_lock_irqsave(&aq->lock, flags);
++		list_for_each_entry_safe(ib, ib_safe, &aq->active, head) {
++			struct vb2_buffer *vb =
++				ipu6_isys_buffer_to_vb2_buffer(ib);
++
++			list_del(&ib->head);
++			if (av->streaming) {
++				dev_dbg(dev,
++					"%s: queue buffer %u back to incoming\n",
++					av->vdev.name, vb->index);
++				/* Queue already streaming, return to driver. */
++				list_add(&ib->head, &aq->incoming);
++				continue;
++			}
++			/* Queue not yet streaming, return to user. */
++			dev_dbg(dev, "%s: return %u back to videobuf2\n",
++				av->vdev.name, vb->index);
++			vb2_buffer_done(ipu6_isys_buffer_to_vb2_buffer(ib),
++					VB2_BUF_STATE_QUEUED);
++		}
++		spin_unlock_irqrestore(&aq->lock, flags);
++	}
++}
++
++/*
++ * Attempt obtaining a buffer list from the incoming queues, a list of buffers
++ * that contains one entry from each video buffer queue. If a buffer can't be
++ * obtained from every queue, the buffers are returned back to the queue.
++ */
++static int buffer_list_get(struct ipu6_isys_stream *stream,
++			   struct ipu6_isys_buffer_list *bl)
++{
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_queue *aq;
++	unsigned long flags;
++	unsigned long buf_flag = IPU6_ISYS_BUFFER_LIST_FL_INCOMING;
++
++	bl->nbufs = 0;
++	INIT_LIST_HEAD(&bl->head);
++
++	list_for_each_entry(aq, &stream->queues, node) {
++		struct ipu6_isys_buffer *ib;
++
++		spin_lock_irqsave(&aq->lock, flags);
++		if (list_empty(&aq->incoming)) {
++			spin_unlock_irqrestore(&aq->lock, flags);
++			if (!list_empty(&bl->head))
++				ipu6_isys_buffer_list_queue(bl, buf_flag, 0);
++			return -ENODATA;
++		}
++
++		ib = list_last_entry(&aq->incoming,
++				     struct ipu6_isys_buffer, head);
++
++		dev_dbg(dev, "buffer: %s: buffer %u\n",
++			ipu6_isys_queue_to_video(aq)->vdev.name,
++			ipu6_isys_buffer_to_vb2_buffer(ib)->index);
++		list_del(&ib->head);
++		list_add(&ib->head, &bl->head);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		bl->nbufs++;
++	}
++
++	dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs);
++
++	return 0;
++}
++
++static void
++ipu6_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb,
++				  struct ipu6_fw_isys_frame_buff_set_abi *set)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
++
++	set->output_pins[aq->fw_output].addr =
++		vb2_dma_contig_plane_dma_addr(vb, 0);
++	set->output_pins[aq->fw_output].out_buf_id = vb->index + 1;
++}
++
++/*
++ * Convert a buffer list to a isys fw ABI framebuffer set. The
++ * buffer list is not modified.
++ */
++#define IPU6_ISYS_FRAME_NUM_THRESHOLD  (30)
++void
++ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set,
++			      struct ipu6_isys_stream *stream,
++			      struct ipu6_isys_buffer_list *bl)
++{
++	struct ipu6_isys_buffer *ib;
++
++	WARN_ON(!bl->nbufs);
++
++	set->send_irq_sof = 1;
++	set->send_resp_sof = 1;
++	set->send_irq_eof = 0;
++	set->send_resp_eof = 0;
++
++	if (stream->streaming)
++		set->send_irq_capture_ack = 0;
++	else
++		set->send_irq_capture_ack = 1;
++	set->send_irq_capture_done = 0;
++
++	set->send_resp_capture_ack = 1;
++	set->send_resp_capture_done = 1;
++	if (atomic_read(&stream->sequence) >= IPU6_ISYS_FRAME_NUM_THRESHOLD) {
++		set->send_resp_capture_ack = 0;
++		set->send_resp_capture_done = 0;
++	}
++
++	list_for_each_entry(ib, &bl->head, head) {
++		struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++
++		ipu6_isys_buf_to_fw_frame_buf_pin(vb, set);
++	}
++}
++
++/* Start streaming for real. The buffer list must be available. */
++static int ipu6_isys_stream_start(struct ipu6_isys_video *av,
++				  struct ipu6_isys_buffer_list *bl, bool error)
++{
++	struct ipu6_isys_stream *stream = av->stream;
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	struct ipu6_isys_buffer_list __bl;
++	int ret;
++
++	mutex_lock(&stream->isys->stream_mutex);
++	ret = ipu6_isys_video_set_streaming(av, 1, bl);
++	mutex_unlock(&stream->isys->stream_mutex);
++	if (ret)
++		goto out_requeue;
++
++	stream->streaming = 1;
++
++	bl = &__bl;
++
++	do {
++		struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
++		struct isys_fw_msgs *msg;
++		u16 send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE;
++
++		ret = buffer_list_get(stream, bl);
++		if (ret < 0)
++			break;
++
++		msg = ipu6_get_fw_msg_buf(stream);
++		if (!msg)
++			return -ENOMEM;
++
++		buf = &msg->fw_msg.frame;
++		ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);
++		ipu6_fw_isys_dump_frame_buff_set(dev, buf,
++						 stream->nr_output_pins);
++		ipu6_isys_buffer_list_queue(bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE,
++					    0);
++		ret = ipu6_fw_isys_complex_cmd(stream->isys,
++					       stream->stream_handle, buf,
++					       msg->dma_addr, sizeof(*buf),
++					       send_type);
++	} while (!WARN_ON(ret));
++
++	return 0;
++
++out_requeue:
++	if (bl && bl->nbufs)
++		ipu6_isys_buffer_list_queue(bl,
++					    (IPU6_ISYS_BUFFER_LIST_FL_INCOMING |
++					     error) ?
++					    IPU6_ISYS_BUFFER_LIST_FL_SET_STATE :
++					    0, error ? VB2_BUF_STATE_ERROR :
++					    VB2_BUF_STATE_QUEUED);
++	flush_firmware_streamon_fail(stream);
++
++	return ret;
++}
++
++static void buf_queue(struct vb2_buffer *vb)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
++	struct ipu6_isys_video_buffer *ivb =
++		vb2_buffer_to_ipu6_isys_video_buffer(vvb);
++	struct ipu6_isys_buffer *ib = &ivb->ib;
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct media_pipeline *media_pipe =
++		media_entity_pipeline(&av->vdev.entity);
++	struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
++	struct ipu6_isys_stream *stream = av->stream;
++	struct ipu6_isys_buffer_list bl;
++	struct isys_fw_msgs *msg;
++	unsigned long flags;
++	dma_addr_t dma;
++	unsigned int i;
++	int ret;
++
++	dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name);
++
++	for (i = 0; i < vb->num_planes; i++) {
++		dma = vb2_dma_contig_plane_dma_addr(vb, i);
++		dev_dbg(dev, "iova: plane %u iova %pad\n", i, &dma);
++	}
++
++	spin_lock_irqsave(&aq->lock, flags);
++	list_add(&ib->head, &aq->incoming);
++	spin_unlock_irqrestore(&aq->lock, flags);
++
++	if (!media_pipe || !vb->vb2_queue->start_streaming_called) {
++		dev_dbg(dev, "media pipeline is not ready for %s\n",
++			av->vdev.name);
++		return;
++	}
++
++	mutex_lock(&stream->mutex);
++
++	if (stream->nr_streaming != stream->nr_queues) {
++		dev_dbg(dev, "not streaming yet, adding to incoming\n");
++		goto out;
++	}
++
++	/*
++	 * We just put one buffer to the incoming list of this queue
++	 * (above). Let's see whether all queues in the pipeline would
++	 * have a buffer.
++	 */
++	ret = buffer_list_get(stream, &bl);
++	if (ret < 0) {
++		dev_warn(dev, "No buffers available\n");
++		goto out;
++	}
++
++	msg = ipu6_get_fw_msg_buf(stream);
++	if (!msg) {
++		ret = -ENOMEM;
++		goto out;
++	}
++
++	buf = &msg->fw_msg.frame;
++	ipu6_isys_buf_to_fw_frame_buf(buf, stream, &bl);
++	ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins);
++
++	if (!stream->streaming) {
++		ret = ipu6_isys_stream_start(av, &bl, true);
++		if (ret)
++			dev_err(dev, "stream start failed.\n");
++		goto out;
++	}
++
++	/*
++	 * We must queue the buffers in the buffer list to the
++	 * appropriate video buffer queues BEFORE passing them to the
++	 * firmware since we could get a buffer event back before we
++	 * have queued them ourselves to the active queue.
++	 */
++	ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
++
++	ret = ipu6_fw_isys_complex_cmd(stream->isys, stream->stream_handle,
++				       buf, msg->dma_addr, sizeof(*buf),
++				       IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE);
++	if (ret < 0)
++		dev_err(dev, "send stream capture failed\n");
++
++out:
++	mutex_unlock(&stream->mutex);
++}
++
++static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq)
++{
++	struct v4l2_mbus_framefmt format;
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct media_pad *remote_pad =
++		media_pad_remote_pad_first(av->vdev.entity.pads);
++	struct v4l2_subdev *sd;
++	u32 r_stream;
++	int ret;
++
++	if (!remote_pad)
++		return -ENOTCONN;
++
++	sd = media_entity_to_v4l2_subdev(remote_pad->entity);
++	r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, remote_pad->index);
++
++	ret = ipu6_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream,
++					   &format);
++
++	if (ret) {
++		dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n",
++			sd->entity.name, remote_pad->index, r_stream);
++		return ret;
++	}
++
++	if (format.width != av->mpix.width ||
++	    format.height != av->mpix.height) {
++		dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n",
++			av->mpix.width, av->mpix.height,
++			format.width, format.height);
++		return -EINVAL;
++	}
++
++	if (format.field != av->mpix.field) {
++		dev_dbg(dev, "wrong field value 0x%8.8x (0x%8.8x expected)\n",
++			av->mpix.field, format.field);
++		return -EINVAL;
++	}
++
++	if (format.code != av->pfmt->code) {
++		dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n",
++			av->pfmt->code, format.code);
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static void return_buffers(struct ipu6_isys_queue *aq,
++			   enum vb2_buffer_state state)
++{
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct ipu6_isys_buffer *ib;
++	bool need_reset = false;
++	unsigned long flags;
++
++	spin_lock_irqsave(&aq->lock, flags);
++	while (!list_empty(&aq->incoming)) {
++		struct vb2_buffer *vb;
++
++		ib = list_first_entry(&aq->incoming, struct ipu6_isys_buffer,
++				      head);
++		vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++		list_del(&ib->head);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		vb2_buffer_done(vb, state);
++
++		spin_lock_irqsave(&aq->lock, flags);
++	}
++
++	/*
++	 * Something went wrong (FW crash / HW hang / not all buffers
++	 * returned from isys) if there are still buffers queued in active
++	 * queue. We have to clean up places a bit.
++	 */
++	while (!list_empty(&aq->active)) {
++		struct vb2_buffer *vb;
++
++		ib = list_first_entry(&aq->active, struct ipu6_isys_buffer,
++				      head);
++		vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++
++		list_del(&ib->head);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		vb2_buffer_done(vb, state);
++
++		spin_lock_irqsave(&aq->lock, flags);
++		need_reset = true;
++	}
++
++	spin_unlock_irqrestore(&aq->lock, flags);
++
++	if (need_reset) {
++		mutex_lock(&av->isys->mutex);
++		av->isys->need_reset = true;
++		mutex_unlock(&av->isys->mutex);
++	}
++}
++
++static void ipu6_isys_stream_cleanup(struct ipu6_isys_video *av)
++{
++	video_device_pipeline_stop(&av->vdev);
++	ipu6_isys_put_stream(av->stream);
++	av->stream = NULL;
++}
++
++static int start_streaming(struct vb2_queue *q, unsigned int count)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct ipu6_isys_buffer_list __bl, *bl = NULL;
++	struct ipu6_isys_stream *stream;
++	struct media_entity *source_entity = NULL;
++	int nr_queues, ret;
++
++	dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
++		av->vdev.name, av->mpix.width, av->mpix.height,
++		av->pfmt->css_pixelformat);
++
++	ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues);
++	if (ret < 0) {
++		dev_err(dev, "failed to setup video\n");
++		goto out_return_buffers;
++	}
++
++	ret = ipu6_isys_link_fmt_validate(aq);
++	if (ret) {
++		dev_err(dev,
++			"%s: link format validation failed (%d)\n",
++			av->vdev.name, ret);
++		goto out_pipeline_stop;
++	}
++
++	ret = ipu6_isys_fw_open(av->isys);
++	if (ret)
++		goto out_pipeline_stop;
++
++	stream = av->stream;
++	mutex_lock(&stream->mutex);
++	if (!stream->nr_streaming) {
++		ret = ipu6_isys_video_prepare_stream(av, source_entity,
++						     nr_queues);
++		if (ret)
++			goto out_fw_close;
++	}
++
++	stream->nr_streaming++;
++	dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming,
++		stream->nr_queues);
++
++	list_add(&aq->node, &stream->queues);
++	ipu6_isys_set_csi2_streams_status(av, true);
++	ipu6_isys_configure_stream_watermark(av, true);
++	ipu6_isys_update_stream_watermark(av, true);
++
++	if (stream->nr_streaming != stream->nr_queues)
++		goto out;
++
++	bl = &__bl;
++	ret = buffer_list_get(stream, bl);
++	if (ret < 0) {
++		dev_dbg(dev,
++			"no buffer available, postponing streamon\n");
++		goto out;
++	}
++
++	ret = ipu6_isys_stream_start(av, bl, false);
++	if (ret)
++		goto out_stream_start;
++
++out:
++	mutex_unlock(&stream->mutex);
++
++	return 0;
++
++out_stream_start:
++	list_del(&aq->node);
++	stream->nr_streaming--;
++
++out_fw_close:
++	mutex_unlock(&stream->mutex);
++	ipu6_isys_fw_close(av->isys);
++
++out_pipeline_stop:
++	ipu6_isys_stream_cleanup(av);
++
++out_return_buffers:
++	return_buffers(aq, VB2_BUF_STATE_QUEUED);
++
++	return ret;
++}
++
++static void stop_streaming(struct vb2_queue *q)
++{
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct ipu6_isys_stream *stream = av->stream;
++
++	ipu6_isys_set_csi2_streams_status(av, false);
++
++	mutex_lock(&stream->mutex);
++
++	ipu6_isys_update_stream_watermark(av, false);
++
++	mutex_lock(&av->isys->stream_mutex);
++	if (stream->nr_streaming == stream->nr_queues && stream->streaming)
++		ipu6_isys_video_set_streaming(av, 0, NULL);
++	mutex_unlock(&av->isys->stream_mutex);
++
++	stream->nr_streaming--;
++	list_del(&aq->node);
++	stream->streaming = 0;
++	mutex_unlock(&stream->mutex);
++
++	ipu6_isys_stream_cleanup(av);
++
++	return_buffers(aq, VB2_BUF_STATE_ERROR);
++
++	ipu6_isys_fw_close(av->isys);
++}
++
++static unsigned int
++get_sof_sequence_by_timestamp(struct ipu6_isys_stream *stream,
++			      struct ipu6_fw_isys_resp_info_abi *info)
++{
++	u64 time = (u64)info->timestamp[1] << 32 | info->timestamp[0];
++	struct ipu6_isys *isys = stream->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	unsigned int i;
++
++	/*
++	 * The timestamp is invalid as no TSC in some FPGA platform,
++	 * so get the sequence from pipeline directly in this case.
++	 */
++	if (time == 0)
++		return atomic_read(&stream->sequence) - 1;
++
++	for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++)
++		if (time == stream->seq[i].timestamp) {
++			dev_dbg(dev, "sof: using seq nr %u for ts %llu\n",
++				stream->seq[i].sequence, time);
++			return stream->seq[i].sequence;
++		}
++
++	for (i = 0; i < IPU6_ISYS_MAX_PARALLEL_SOF; i++)
++		dev_dbg(dev, "sof: sequence %u, timestamp value %llu\n",
++			stream->seq[i].sequence, stream->seq[i].timestamp);
++
++	return 0;
++}
++
++static u64 get_sof_ns_delta(struct ipu6_isys_video *av,
++			    struct ipu6_fw_isys_resp_info_abi *info)
++{
++	struct ipu6_bus_device *adev = av->isys->adev;
++	struct ipu6_device *isp = adev->isp;
++	u64 delta, tsc_now;
++
++	ipu6_buttress_tsc_read(isp, &tsc_now);
++	if (!tsc_now)
++		return 0;
++
++	delta = tsc_now - ((u64)info->timestamp[1] << 32 | info->timestamp[0]);
++
++	return ipu6_buttress_tsc_ticks_to_ns(delta, isp);
++}
++
++void ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib,
++				      struct ipu6_fw_isys_resp_info_abi *info)
++{
++	struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
++	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct ipu6_isys_stream *stream = av->stream;
++	u64 ns;
++	u32 sequence;
++
++	ns = ktime_get_ns() - get_sof_ns_delta(av, info);
++	sequence = get_sof_sequence_by_timestamp(stream, info);
++
++	vbuf->vb2_buf.timestamp = ns;
++	vbuf->sequence = sequence;
++
++	dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n",
++		av->vdev.name, ktime_get_ns(), sequence);
++	dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index,
++		vbuf->vb2_buf.timestamp);
++}
++
++void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib)
++{
++	struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++
++	if (atomic_read(&ib->str2mmio_flag)) {
++		vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
++		/*
++		 * Operation on buffer is ended with error and will be reported
++		 * to the userspace when it is de-queued
++		 */
++		atomic_set(&ib->str2mmio_flag, 0);
++	} else {
++		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
++	}
++}
++
++void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream,
++			       struct ipu6_fw_isys_resp_info_abi *info)
++{
++	struct ipu6_isys_queue *aq = stream->output_pins[info->pin_id].aq;
++	struct ipu6_isys *isys = stream->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct ipu6_isys_buffer *ib;
++	struct vb2_buffer *vb;
++	unsigned long flags;
++	bool first = true;
++	struct vb2_v4l2_buffer *buf;
++
++	spin_lock_irqsave(&aq->lock, flags);
++	if (list_empty(&aq->active)) {
++		spin_unlock_irqrestore(&aq->lock, flags);
++		dev_err(dev, "active queue empty\n");
++		return;
++	}
++
++	list_for_each_entry_reverse(ib, &aq->active, head) {
++		dma_addr_t addr;
++
++		vb = ipu6_isys_buffer_to_vb2_buffer(ib);
++		addr = vb2_dma_contig_plane_dma_addr(vb, 0);
++
++		if (info->pin.addr != addr) {
++			if (first)
++				dev_err(dev, "Unexpected buffer address %pad\n",
++					&addr);
++			first = false;
++			continue;
++		}
++
++		if (info->error_info.error ==
++		    IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO) {
++			/*
++			 * Check for error message:
++			 * 'IPU6_FW_ISYS_ERROR_HW_REPORTED_STR2MMIO'
++			 */
++			atomic_set(&ib->str2mmio_flag, 1);
++		}
++		dev_dbg(dev, "buffer: found buffer %pad\n", &addr);
++
++		buf = to_vb2_v4l2_buffer(vb);
++		buf->field = V4L2_FIELD_NONE;
++
++		list_del(&ib->head);
++		spin_unlock_irqrestore(&aq->lock, flags);
++
++		ipu6_isys_buf_calc_sequence_time(ib, info);
++
++		ipu6_isys_queue_buf_done(ib);
++
++		return;
++	}
++
++	dev_err(dev, "Failed to find a matching video buffer");
++
++	spin_unlock_irqrestore(&aq->lock, flags);
++}
++
++static const struct vb2_ops ipu6_isys_queue_ops = {
++	.queue_setup = queue_setup,
++	.wait_prepare = vb2_ops_wait_prepare,
++	.wait_finish = vb2_ops_wait_finish,
++	.buf_prepare = ipu6_isys_buf_prepare,
++	.start_streaming = start_streaming,
++	.stop_streaming = stop_streaming,
++	.buf_queue = buf_queue,
++};
++
++int ipu6_isys_queue_init(struct ipu6_isys_queue *aq)
++{
++	struct ipu6_isys *isys = ipu6_isys_queue_to_video(aq)->isys;
++	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
++	int ret;
++
++	/* no support for userptr */
++	if (!aq->vbq.io_modes)
++		aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF;
++
++	aq->vbq.drv_priv = aq;
++	aq->vbq.ops = &ipu6_isys_queue_ops;
++	aq->vbq.lock = &av->mutex;
++	aq->vbq.mem_ops = &vb2_dma_contig_memops;
++	aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
++	aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++
++	ret = vb2_queue_init(&aq->vbq);
++	if (ret)
++		return ret;
++
++	aq->dev = &isys->adev->auxdev.dev;
++	aq->vbq.dev = &isys->adev->auxdev.dev;
++	spin_lock_init(&aq->lock);
++	INIT_LIST_HEAD(&aq->active);
++	INIT_LIST_HEAD(&aq->incoming);
++
++	return 0;
++}
++
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
+new file mode 100644
+index 000000000000..9fb454577bb5
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
+@@ -0,0 +1,76 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_QUEUE_H
++#define IPU6_ISYS_QUEUE_H
++
++#include <linux/container_of.h>
++#include <linux/atomic.h>
++#include <linux/device.h>
++#include <linux/list.h>
++#include <linux/spinlock_types.h>
++
++#include <media/videobuf2-v4l2.h>
++
++#include "ipu6-fw-isys.h"
++#include "ipu6-isys-video.h"
++
++struct ipu6_isys_queue {
++	struct vb2_queue vbq;
++	struct list_head node;
++	struct device *dev;
++	/*
++	 * @lock: serialise access to queued and pre_streamon_queued
++	 */
++	spinlock_t lock;
++	struct list_head active;
++	struct list_head incoming;
++	unsigned int fw_output;
++};
++
++struct ipu6_isys_buffer {
++	struct list_head head;
++	atomic_t str2mmio_flag;
++};
++
++struct ipu6_isys_video_buffer {
++	struct vb2_v4l2_buffer vb_v4l2;
++	struct ipu6_isys_buffer ib;
++};
++
++#define IPU6_ISYS_BUFFER_LIST_FL_INCOMING	BIT(0)
++#define IPU6_ISYS_BUFFER_LIST_FL_ACTIVE	BIT(1)
++#define IPU6_ISYS_BUFFER_LIST_FL_SET_STATE	BIT(2)
++
++struct ipu6_isys_buffer_list {
++	struct list_head head;
++	unsigned int nbufs;
++};
++
++#define vb2_queue_to_isys_queue(__vb2) \
++	container_of(__vb2, struct ipu6_isys_queue, vbq)
++
++#define ipu6_isys_to_isys_video_buffer(__ib) \
++	container_of(__ib, struct ipu6_isys_video_buffer, ib)
++
++#define vb2_buffer_to_ipu6_isys_video_buffer(__vvb) \
++	container_of(__vvb, struct ipu6_isys_video_buffer, vb_v4l2)
++
++#define ipu6_isys_buffer_to_vb2_buffer(__ib) \
++	(&ipu6_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf)
++
++void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl,
++				 unsigned long op_flags,
++				 enum vb2_buffer_state state);
++void
++ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set,
++			      struct ipu6_isys_stream *stream,
++			      struct ipu6_isys_buffer_list *bl);
++void
++ipu6_isys_buf_calc_sequence_time(struct ipu6_isys_buffer *ib,
++				 struct ipu6_fw_isys_resp_info_abi *info);
++void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib);
++void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream,
++			       struct ipu6_fw_isys_resp_info_abi *info);
++int ipu6_isys_queue_init(struct ipu6_isys_queue *aq);
++#endif /* IPU6_ISYS_QUEUE_H */
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+new file mode 100644
+index 000000000000..847eac26bcd6
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+@@ -0,0 +1,1253 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2013 - 2023 Intel Corporation
++ */
++
++#include <linux/align.h>
++#include <linux/bits.h>
++#include <linux/bug.h>
++#include <linux/completion.h>
++#include <linux/container_of.h>
++#include <linux/device.h>
++#include <linux/list.h>
++#include <linux/math64.h>
++#include <linux/minmax.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/pm_runtime.h>
++#include <linux/spinlock.h>
++#include <linux/string.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-dev.h>
++#include <media/v4l2-fh.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-subdev.h>
++#include <media/videobuf2-v4l2.h>
++
++#include "ipu6.h"
++#include "ipu6-bus.h"
++#include "ipu6-cpd.h"
++#include "ipu6-fw-isys.h"
++#include "ipu6-isys.h"
++#include "ipu6-isys-csi2.h"
++#include "ipu6-isys-queue.h"
++#include "ipu6-isys-video.h"
++#include "ipu6-platform-regs.h"
++
++const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
++	{V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW16},
++	{V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
++	{V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
++	{V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
++	{V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
++	{V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
++	{V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
++	{V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
++	{V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
++	{V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
++	{V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_YUYV},
++	{V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RGB565},
++	{V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RGBA888},
++};
++
++static int video_open(struct file *file)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++	struct ipu6_isys *isys = av->isys;
++	struct ipu6_bus_device *adev = isys->adev;
++
++	mutex_lock(&isys->mutex);
++	if (isys->need_reset) {
++		mutex_unlock(&isys->mutex);
++		dev_warn(&adev->auxdev.dev, "isys power cycle required\n");
++		return -EIO;
++	}
++	mutex_unlock(&isys->mutex);
++
++	return v4l2_fh_open(file);
++}
++
++static int video_release(struct file *file)
++{
++	return vb2_fop_release(file);
++}
++
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_get_pixelformat(u32 pixelformat)
++{
++	unsigned int i;
++
++	for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
++		const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i];
++
++		if (pfmt->pixelformat == pixelformat)
++			return pfmt;
++	}
++
++	return &ipu6_isys_pfmts[0];
++}
++
++int ipu6_isys_vidioc_querycap(struct file *file, void *fh,
++			      struct v4l2_capability *cap)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++
++	strscpy(cap->driver, IPU6_ISYS_NAME, sizeof(cap->driver));
++	strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card));
++
++	return 0;
++}
++
++int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,
++			      struct v4l2_fmtdesc *f)
++{
++	unsigned int i, found = 0;
++
++	if (f->index >= ARRAY_SIZE(ipu6_isys_pfmts))
++		return -EINVAL;
++
++	if (!f->mbus_code) {
++		f->flags = 0;
++		f->pixelformat = ipu6_isys_pfmts[f->index].pixelformat;
++		return 0;
++	}
++
++	for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
++		if (f->mbus_code != ipu6_isys_pfmts[i].code)
++			continue;
++
++		if (f->index == found) {
++			f->flags = 0;
++			f->pixelformat = ipu6_isys_pfmts[i].pixelformat;
++			return 0;
++		}
++		found++;
++	}
++
++	return -EINVAL;
++}
++
++static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh,
++					    struct v4l2_frmsizeenum *fsize)
++{
++	if (fsize->index > 0)
++		return -EINVAL;
++
++	fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
++	fsize->stepwise.min_width = IPU6_ISYS_MIN_WIDTH;
++	fsize->stepwise.max_width = IPU6_ISYS_MAX_WIDTH;
++	fsize->stepwise.min_height = IPU6_ISYS_MIN_HEIGHT;
++	fsize->stepwise.max_height = IPU6_ISYS_MAX_HEIGHT;
++	fsize->stepwise.step_width = 2;
++	fsize->stepwise.step_height = 2;
++
++	return 0;
++}
++
++static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *fh,
++				       struct v4l2_format *fmt)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++
++	fmt->fmt.pix_mp = av->mpix;
++
++	return 0;
++}
++
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_video_try_fmt_vid_mplane(struct ipu6_isys_video *av,
++				   struct v4l2_pix_format_mplane *mpix)
++{
++	const struct ipu6_isys_pixelformat *pfmt =
++		ipu6_isys_get_pixelformat(mpix->pixelformat);
++
++	mpix->pixelformat = pfmt->pixelformat;
++	mpix->num_planes = 1;
++
++	mpix->width = clamp(mpix->width, IPU6_ISYS_MIN_WIDTH,
++			    IPU6_ISYS_MAX_WIDTH);
++	mpix->height = clamp(mpix->height, IPU6_ISYS_MIN_HEIGHT,
++			     IPU6_ISYS_MAX_HEIGHT);
++
++	if (pfmt->bpp != pfmt->bpp_packed)
++		mpix->plane_fmt[0].bytesperline =
++			mpix->width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);
++	else
++		mpix->plane_fmt[0].bytesperline =
++			DIV_ROUND_UP((unsigned int)mpix->width * pfmt->bpp,
++				     BITS_PER_BYTE);
++
++	mpix->plane_fmt[0].bytesperline = ALIGN(mpix->plane_fmt[0].bytesperline,
++						av->isys->line_align);
++
++	/*
++	 * (height + 1) * bytesperline due to a hardware issue: the DMA unit
++	 * is a power of two, and a line should be transferred as few units
++	 * as possible. The result is that up to line length more data than
++	 * the image size may be transferred to memory after the image.
++	 * Another limitation is the GDA allocation unit size. For low
++	 * resolution it gives a bigger number. Use larger one to avoid
++	 * memory corruption.
++	 */
++	mpix->plane_fmt[0].sizeimage =
++		max(mpix->plane_fmt[0].sizeimage,
++		    mpix->plane_fmt[0].bytesperline * mpix->height +
++		    max(mpix->plane_fmt[0].bytesperline,
++			av->isys->pdata->ipdata->isys_dma_overshoot));
++
++	memset(mpix->plane_fmt[0].reserved, 0,
++	       sizeof(mpix->plane_fmt[0].reserved));
++
++	mpix->field = V4L2_FIELD_NONE;
++
++	mpix->colorspace = V4L2_COLORSPACE_RAW;
++	mpix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
++	mpix->quantization = V4L2_QUANTIZATION_DEFAULT;
++	mpix->xfer_func = V4L2_XFER_FUNC_DEFAULT;
++
++	return pfmt;
++}
++
++static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *fh,
++				       struct v4l2_format *f)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++
++	if (av->aq.vbq.streaming)
++		return -EBUSY;
++
++	av->pfmt = ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
++	av->mpix = f->fmt.pix_mp;
++
++	return 0;
++}
++
++static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *fh,
++					 struct v4l2_format *f)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++
++	ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
++
++	return 0;
++}
++
++static int link_validate(struct media_link *link)
++{
++	struct ipu6_isys_video *av =
++		container_of(link->sink, struct ipu6_isys_video, pad);
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct v4l2_subdev_state *s_state;
++	struct v4l2_subdev *s_sd;
++	struct v4l2_mbus_framefmt *s_fmt;
++	struct media_pad *s_pad;
++	u32 s_stream;
++	int ret = -EPIPE;
++
++	if (!link->source->entity)
++		return ret;
++
++	s_sd = media_entity_to_v4l2_subdev(link->source->entity);
++	s_state = v4l2_subdev_get_unlocked_active_state(s_sd);
++	if (!s_state)
++		return ret;
++
++	dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n",
++		link->source->entity->name, link->source->index,
++		link->sink->entity->name);
++
++	s_pad = media_pad_remote_pad_first(&av->pad);
++	s_stream = ipu6_isys_get_src_stream_by_src_pad(s_sd, s_pad->index);
++
++	v4l2_subdev_lock_state(s_state);
++
++	s_fmt = v4l2_subdev_state_get_stream_format(s_state, s_pad->index,
++						    s_stream);
++	if (!s_fmt) {
++		dev_err(dev, "failed to get source pad format\n");
++		goto unlock;
++	}
++
++	if (s_fmt->width != av->mpix.width ||
++	    s_fmt->height != av->mpix.height || s_fmt->code != av->pfmt->code) {
++		dev_err(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",
++			s_fmt->width, s_fmt->height, s_fmt->code,
++			av->mpix.width, av->mpix.height, av->pfmt->code);
++		goto unlock;
++	}
++
++	v4l2_subdev_unlock_state(s_state);
++
++	return 0;
++unlock:
++	v4l2_subdev_unlock_state(s_state);
++
++	return ret;
++}
++
++static void get_stream_opened(struct ipu6_isys_video *av)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&av->isys->streams_lock, flags);
++	av->isys->stream_opened++;
++	spin_unlock_irqrestore(&av->isys->streams_lock, flags);
++}
++
++static void put_stream_opened(struct ipu6_isys_video *av)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&av->isys->streams_lock, flags);
++	av->isys->stream_opened--;
++	spin_unlock_irqrestore(&av->isys->streams_lock, flags);
++}
++
++static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
++				struct ipu6_fw_isys_stream_cfg_data_abi *cfg)
++{
++	struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad);
++	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
++	struct ipu6_fw_isys_input_pin_info_abi *input_pin;
++	struct ipu6_fw_isys_output_pin_info_abi *output_pin;
++	struct ipu6_isys_stream *stream = av->stream;
++	struct ipu6_isys_queue *aq = &av->aq;
++	struct v4l2_mbus_framefmt fmt;
++	struct v4l2_rect v4l2_crop;
++	struct ipu6_isys *isys = av->isys;
++	struct device *dev = &isys->adev->auxdev.dev;
++	int input_pins = cfg->nof_input_pins++;
++	int output_pins;
++	u32 src_stream;
++	int ret;
++
++	src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index);
++	ret = ipu6_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream,
++					   &fmt);
++	if (ret < 0) {
++		dev_err(dev, "can't get stream format (%d)\n", ret);
++		return ret;
++	}
++
++	ret = ipu6_isys_get_stream_pad_crop(sd, src_pad->index, src_stream,
++					    &v4l2_crop);
++	if (ret < 0) {
++		dev_err(dev, "can't get stream crop (%d)\n", ret);
++		return ret;
++	}
++
++	input_pin = &cfg->input_pins[input_pins];
++	input_pin->input_res.width = fmt.width;
++	input_pin->input_res.height = fmt.height;
++	input_pin->dt = av->dt;
++	input_pin->bits_per_pix = av->pfmt->bpp_packed;
++	input_pin->mapped_dt = 0x40; /* invalid mipi data type */
++	input_pin->mipi_decompression = 0;
++	input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR;
++	input_pin->mipi_store_mode = av->pfmt->bpp == av->pfmt->bpp_packed ?
++		IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER :
++		IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL;
++	input_pin->crop_first_and_last_lines = v4l2_crop.top & 1;
++
++	output_pins = cfg->nof_output_pins++;
++	aq->fw_output = output_pins;
++	stream->output_pins[output_pins].pin_ready = ipu6_isys_queue_buf_ready;
++	stream->output_pins[output_pins].aq = aq;
++
++	output_pin = &cfg->output_pins[output_pins];
++	output_pin->input_pin_id = input_pins;
++	output_pin->output_res.width = av->mpix.width;
++	output_pin->output_res.height = av->mpix.height;
++
++	output_pin->stride = av->mpix.plane_fmt[0].bytesperline;
++	if (av->pfmt->bpp != av->pfmt->bpp_packed)
++		output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC;
++	else
++		output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI;
++	output_pin->ft = av->pfmt->css_pixelformat;
++	output_pin->send_irq = 1;
++	memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets));
++	output_pin->s2m_pixel_soc_pixel_remapping =
++		S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
++	output_pin->csi_be_soc_pixel_remapping =
++		CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
++
++	output_pin->snoopable = true;
++	output_pin->error_handling_enable = false;
++	output_pin->sensor_type = isys->sensor_type++;
++	if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end)
++		isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
++
++	return 0;
++}
++
++static int start_stream_firmware(struct ipu6_isys_video *av,
++				 struct ipu6_isys_buffer_list *bl)
++{
++	struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg;
++	struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
++	struct ipu6_isys_stream *stream = av->stream;
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct isys_fw_msgs *msg = NULL;
++	struct ipu6_isys_queue *aq;
++	int ret, retout, tout;
++	u16 send_type;
++
++	msg = ipu6_get_fw_msg_buf(stream);
++	if (!msg)
++		return -ENOMEM;
++
++	stream_cfg = &msg->fw_msg.stream;
++	stream_cfg->src = stream->stream_source;
++	stream_cfg->vc = stream->vc;
++	stream_cfg->isl_use = 0;
++	stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL;
++
++	list_for_each_entry(aq, &stream->queues, node) {
++		struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq);
++
++		ret = ipu6_isys_fw_pin_cfg(__av, stream_cfg);
++		if (ret < 0) {
++			ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
++			return ret;
++		}
++	}
++
++	ipu6_fw_isys_dump_stream_cfg(dev, stream_cfg);
++
++	stream->nr_output_pins = stream_cfg->nof_output_pins;
++
++	reinit_completion(&stream->stream_open_completion);
++
++	ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
++				       stream_cfg, msg->dma_addr,
++				       sizeof(*stream_cfg),
++				       IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN);
++	if (ret < 0) {
++		dev_err(dev, "can't open stream (%d)\n", ret);
++		ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
++		return ret;
++	}
++
++	get_stream_opened(av);
++
++	tout = wait_for_completion_timeout(&stream->stream_open_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++
++	ipu6_put_fw_msg_buf(av->isys, (u64)stream_cfg);
++
++	if (!tout) {
++		dev_err(dev, "stream open time out\n");
++		ret = -ETIMEDOUT;
++		goto out_put_stream_opened;
++	}
++	if (stream->error) {
++		dev_err(dev, "stream open error: %d\n", stream->error);
++		ret = -EIO;
++		goto out_put_stream_opened;
++	}
++	dev_dbg(dev, "start stream: open complete\n");
++
++	if (bl) {
++		msg = ipu6_get_fw_msg_buf(stream);
++		if (!msg) {
++			ret = -ENOMEM;
++			goto out_put_stream_opened;
++		}
++		buf = &msg->fw_msg.frame;
++		ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);
++		ipu6_isys_buffer_list_queue(bl,
++					    IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
++	}
++
++	reinit_completion(&stream->stream_start_completion);
++
++	if (bl) {
++		send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE;
++		ipu6_fw_isys_dump_frame_buff_set(dev, buf,
++						 stream_cfg->nof_output_pins);
++		ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
++					       buf, msg->dma_addr,
++					       sizeof(*buf), send_type);
++	} else {
++		send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START;
++		ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
++					      send_type);
++	}
++
++	if (ret < 0) {
++		dev_err(dev, "can't start streaming (%d)\n", ret);
++		goto out_stream_close;
++	}
++
++	tout = wait_for_completion_timeout(&stream->stream_start_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++	if (!tout) {
++		dev_err(dev, "stream start time out\n");
++		ret = -ETIMEDOUT;
++		goto out_stream_close;
++	}
++	if (stream->error) {
++		dev_err(dev, "stream start error: %d\n", stream->error);
++		ret = -EIO;
++		goto out_stream_close;
++	}
++	dev_dbg(dev, "start stream: complete\n");
++
++	return 0;
++
++out_stream_close:
++	reinit_completion(&stream->stream_close_completion);
++
++	retout = ipu6_fw_isys_simple_cmd(av->isys,
++					 stream->stream_handle,
++					 IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
++	if (retout < 0) {
++		dev_dbg(dev, "can't close stream (%d)\n", retout);
++		goto out_put_stream_opened;
++	}
++
++	tout = wait_for_completion_timeout(&stream->stream_close_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++	if (!tout)
++		dev_err(dev, "stream close time out\n");
++	else if (stream->error)
++		dev_err(dev, "stream close error: %d\n", stream->error);
++	else
++		dev_dbg(dev, "stream close complete\n");
++
++out_put_stream_opened:
++	put_stream_opened(av);
++
++	return ret;
++}
++
++static void stop_streaming_firmware(struct ipu6_isys_video *av)
++{
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct ipu6_isys_stream *stream = av->stream;
++	int ret, tout;
++
++	reinit_completion(&stream->stream_stop_completion);
++
++	ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
++				      IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH);
++
++	if (ret < 0) {
++		dev_err(dev, "can't stop stream (%d)\n", ret);
++		return;
++	}
++
++	tout = wait_for_completion_timeout(&stream->stream_stop_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++	if (!tout)
++		dev_warn(dev, "stream stop time out\n");
++	else if (stream->error)
++		dev_warn(dev, "stream stop error: %d\n", stream->error);
++	else
++		dev_dbg(dev, "stop stream: complete\n");
++}
++
++static void close_streaming_firmware(struct ipu6_isys_video *av)
++{
++	struct ipu6_isys_stream *stream = av->stream;
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	int ret, tout;
++
++	reinit_completion(&stream->stream_close_completion);
++
++	ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
++				      IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
++	if (ret < 0) {
++		dev_err(dev, "can't close stream (%d)\n", ret);
++		return;
++	}
++
++	tout = wait_for_completion_timeout(&stream->stream_close_completion,
++					   IPU6_FW_CALL_TIMEOUT_JIFFIES);
++	if (!tout)
++		dev_warn(dev, "stream close time out\n");
++	else if (stream->error)
++		dev_warn(dev, "stream close error: %d\n", stream->error);
++	else
++		dev_dbg(dev, "close stream: complete\n");
++
++	put_stream_opened(av);
++}
++
++int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
++				   struct media_entity *source_entity,
++				   int nr_queues)
++{
++	struct ipu6_isys_stream *stream = av->stream;
++	struct ipu6_isys_csi2 *csi2;
++
++	if (WARN_ON(stream->nr_streaming))
++		return -EINVAL;
++
++	stream->nr_queues = nr_queues;
++	atomic_set(&stream->sequence, 0);
++
++	stream->seq_index = 0;
++	memset(stream->seq, 0, sizeof(stream->seq));
++
++	if (WARN_ON(!list_empty(&stream->queues)))
++		return -EINVAL;
++
++	stream->stream_source = stream->asd->source;
++	csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
++	csi2->receiver_errors = 0;
++	stream->source_entity = source_entity;
++
++	dev_dbg(&av->isys->adev->auxdev.dev,
++		"prepare stream: external entity %s\n",
++		stream->source_entity->name);
++
++	return 0;
++}
++
++void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
++					  bool state)
++{
++	struct ipu6_isys *isys = av->isys;
++	struct ipu6_isys_csi2 *csi2 = NULL;
++	struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
++	struct device *dev = &isys->adev->auxdev.dev;
++	struct v4l2_mbus_framefmt format;
++	struct v4l2_subdev *esd;
++	struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 };
++	unsigned int bpp, lanes;
++	s64 link_freq = 0;
++	u64 pixel_rate = 0;
++	int ret;
++
++	if (!state)
++		return;
++
++	esd = media_entity_to_v4l2_subdev(av->stream->source_entity);
++
++	av->watermark.width = av->mpix.width;
++	av->watermark.height = av->mpix.height;
++	av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift;
++	av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size;
++
++	ret = v4l2_g_ctrl(esd->ctrl_handler, &hb);
++	if (!ret && hb.value >= 0)
++		av->watermark.hblank = hb.value;
++	else
++		av->watermark.hblank = 0;
++
++	csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd);
++	link_freq = ipu6_isys_csi2_get_link_freq(csi2);
++	if (link_freq > 0) {
++		lanes = csi2->nlanes;
++		ret = ipu6_isys_get_stream_pad_fmt(&csi2->asd.sd, 0,
++						   av->source_stream, &format);
++		if (!ret) {
++			bpp = ipu6_isys_mbus_code_to_bpp(format.code);
++			pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp);
++		}
++	}
++
++	av->watermark.pixel_rate = pixel_rate;
++
++	if (!pixel_rate) {
++		mutex_lock(&iwake_watermark->mutex);
++		iwake_watermark->force_iwake_disable = true;
++		mutex_unlock(&iwake_watermark->mutex);
++		dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n",
++			 av->stream->source_entity->name);
++	}
++}
++
++static void calculate_stream_datarate(struct ipu6_isys_video *av)
++{
++	struct video_stream_watermark *watermark = &av->watermark;
++	u32 bpp = av->pfmt->bpp;
++	u32 pages_per_line, pb_bytes_per_line, pixels_per_line, bytes_per_line;
++	u64 line_time_ns, stream_data_rate;
++	u16 shift, size;
++
++	shift = watermark->sram_gran_shift;
++	size = watermark->sram_gran_size;
++
++	pixels_per_line = watermark->width + watermark->hblank;
++	line_time_ns =  div_u64(pixels_per_line * NSEC_PER_SEC,
++				watermark->pixel_rate);
++	bytes_per_line = watermark->width * bpp / 8;
++	pages_per_line = DIV_ROUND_UP(bytes_per_line, size);
++	pb_bytes_per_line = pages_per_line << shift;
++	stream_data_rate = div64_u64(pb_bytes_per_line * 1000, line_time_ns);
++
++	watermark->stream_data_rate = stream_data_rate;
++}
++
++void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state)
++{
++	struct isys_iwake_watermark *iwake_watermark =
++		&av->isys->iwake_watermark;
++
++	if (!av->watermark.pixel_rate)
++		return;
++
++	if (state) {
++		calculate_stream_datarate(av);
++		mutex_lock(&iwake_watermark->mutex);
++		list_add(&av->watermark.stream_node,
++			 &iwake_watermark->video_list);
++		mutex_unlock(&iwake_watermark->mutex);
++	} else {
++		av->watermark.stream_data_rate = 0;
++		mutex_lock(&iwake_watermark->mutex);
++		list_del(&av->watermark.stream_node);
++		mutex_unlock(&iwake_watermark->mutex);
++	}
++
++	update_watermark_setting(av->isys);
++}
++
++void ipu6_isys_put_stream(struct ipu6_isys_stream *stream)
++{
++	struct device *dev = &stream->isys->adev->auxdev.dev;
++	unsigned int i;
++	unsigned long flags;
++
++	if (!stream) {
++		dev_err(dev, "no available stream\n");
++		return;
++	}
++
++	spin_lock_irqsave(&stream->isys->streams_lock, flags);
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++		if (&stream->isys->streams[i] == stream) {
++			if (stream->isys->streams_ref_count[i] > 0)
++				stream->isys->streams_ref_count[i]--;
++			else
++				dev_warn(dev, "invalid stream %d\n", i);
++
++			break;
++		}
++	}
++	spin_unlock_irqrestore(&stream->isys->streams_lock, flags);
++}
++
++static struct ipu6_isys_stream *
++ipu6_isys_get_stream(struct ipu6_isys_video *av, struct ipu6_isys_subdev *asd)
++{
++	struct ipu6_isys_stream *stream = NULL;
++	struct ipu6_isys *isys = av->isys;
++	unsigned long flags;
++	unsigned int i;
++	u8 vc = av->vc;
++
++	if (!isys)
++		return NULL;
++
++	spin_lock_irqsave(&isys->streams_lock, flags);
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++		if (isys->streams_ref_count[i] && isys->streams[i].vc == vc &&
++		    isys->streams[i].asd == asd) {
++			isys->streams_ref_count[i]++;
++			stream = &isys->streams[i];
++			break;
++		}
++	}
++
++	if (!stream) {
++		for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++			if (!isys->streams_ref_count[i]) {
++				isys->streams_ref_count[i]++;
++				stream = &isys->streams[i];
++				stream->vc = vc;
++				stream->asd = asd;
++				break;
++			}
++		}
++	}
++	spin_unlock_irqrestore(&isys->streams_lock, flags);
++
++	return stream;
++}
++
++struct ipu6_isys_stream *
++ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle)
++{
++	unsigned long flags;
++	struct ipu6_isys_stream *stream = NULL;
++
++	if (!isys)
++		return NULL;
++
++	if (stream_handle >= IPU6_ISYS_MAX_STREAMS) {
++		dev_err(&isys->adev->auxdev.dev,
++			"stream_handle %d is invalid\n", stream_handle);
++		return NULL;
++	}
++
++	spin_lock_irqsave(&isys->streams_lock, flags);
++	if (isys->streams_ref_count[stream_handle] > 0) {
++		isys->streams_ref_count[stream_handle]++;
++		stream = &isys->streams[stream_handle];
++	}
++	spin_unlock_irqrestore(&isys->streams_lock, flags);
++
++	return stream;
++}
++
++struct ipu6_isys_stream *
++ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc)
++{
++	struct ipu6_isys_stream *stream = NULL;
++	unsigned long flags;
++	unsigned int i;
++
++	if (!isys)
++		return NULL;
++
++	if (source < 0) {
++		dev_err(&stream->isys->adev->auxdev.dev,
++			"query stream with invalid port number\n");
++		return NULL;
++	}
++
++	spin_lock_irqsave(&isys->streams_lock, flags);
++	for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
++		if (!isys->streams_ref_count[i])
++			continue;
++
++		if (isys->streams[i].stream_source == source &&
++		    isys->streams[i].vc == vc) {
++			stream = &isys->streams[i];
++			isys->streams_ref_count[i]++;
++			break;
++		}
++	}
++	spin_unlock_irqrestore(&isys->streams_lock, flags);
++
++	return stream;
++}
++
++static u64 get_stream_mask_by_pipeline(struct ipu6_isys_video *av)
++{
++	struct media_pipeline *pipeline =
++		media_entity_pipeline(&av->vdev.entity);
++	struct media_entity *entity;
++	unsigned int i;
++	u64 stream_mask = 0;
++
++	for (i = 0; i < NR_OF_VIDEO_DEVICE; i++) {
++		entity = &av->isys->av[i].vdev.entity;
++		if (pipeline == media_entity_pipeline(entity))
++			stream_mask |= BIT_ULL(av->isys->av[i].source_stream);
++	}
++
++	return stream_mask;
++}
++
++int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
++				  struct ipu6_isys_buffer_list *bl)
++{
++	struct v4l2_subdev_krouting *routing;
++	struct ipu6_isys_stream *stream = av->stream;
++	struct v4l2_subdev_state *subdev_state;
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct v4l2_subdev *sd = NULL;
++	struct v4l2_subdev *ssd = NULL;
++	struct media_pad *r_pad;
++	struct media_pad *s_pad = NULL;
++	u32 sink_pad, sink_stream;
++	u64 r_stream;
++	u64 stream_mask = 0;
++	int ret = 0;
++
++	dev_dbg(dev, "set stream: %d\n", state);
++
++	if (WARN(!stream->source_entity, "No source entity for stream\n"))
++		return -ENODEV;
++
++	ssd = media_entity_to_v4l2_subdev(stream->source_entity);
++	sd = &stream->asd->sd;
++	r_pad = media_pad_remote_pad_first(&av->pad);
++	r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
++
++	subdev_state = v4l2_subdev_lock_and_get_active_state(sd);
++	routing = &subdev_state->routing;
++	ret = v4l2_subdev_routing_find_opposite_end(routing, r_pad->index,
++						    r_stream, &sink_pad,
++						    &sink_stream);
++	v4l2_subdev_unlock_state(subdev_state);
++	if (ret)
++		return ret;
++
++	s_pad = media_pad_remote_pad_first(&stream->asd->pad[sink_pad]);
++
++	stream_mask = get_stream_mask_by_pipeline(av);
++	if (!state) {
++		stop_streaming_firmware(av);
++
++		/* stop external sub-device now. */
++		dev_dbg(dev, "disable streams 0x%llx of %s\n", stream_mask,
++			ssd->name);
++		ret = v4l2_subdev_disable_streams(ssd, s_pad->index,
++						  stream_mask);
++		if (ret) {
++			dev_err(dev, "disable streams of %s failed with %d\n",
++				ssd->name, ret);
++			return ret;
++		}
++
++		/* stop sub-device which connects with video */
++		dev_dbg(dev, "stream off entity %s pad:%d\n", sd->name,
++			r_pad->index);
++		ret = v4l2_subdev_call(sd, video, s_stream, state);
++		if (ret) {
++			dev_err(dev, "stream off %s failed with %d\n", sd->name,
++				ret);
++			return ret;
++		}
++		close_streaming_firmware(av);
++	} else {
++		ret = start_stream_firmware(av, bl);
++		if (ret) {
++			dev_err(dev, "start stream of firmware failed\n");
++			goto out_clear_stream_watermark;
++		}
++
++		/* start sub-device which connects with video */
++		dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index);
++		ret = v4l2_subdev_call(sd, video, s_stream, state);
++		if (ret) {
++			dev_err(dev, "stream on %s failed with %d\n", sd->name,
++				ret);
++			goto out_media_entity_stop_streaming_firmware;
++		}
++
++		/* start external sub-device now. */
++		dev_dbg(dev, "enable streams 0x%llx of %s\n", stream_mask,
++			ssd->name);
++		ret = v4l2_subdev_enable_streams(ssd, s_pad->index,
++						 stream_mask);
++		if (ret) {
++			dev_err(dev,
++				"enable streams 0x%llx of %s failed with %d\n",
++				stream_mask, stream->source_entity->name, ret);
++			goto out_media_entity_stop_streaming;
++		}
++	}
++
++	av->streaming = state;
++
++	return 0;
++
++out_media_entity_stop_streaming:
++	v4l2_subdev_disable_streams(sd, r_pad->index, BIT(r_stream));
++
++out_media_entity_stop_streaming_firmware:
++	stop_streaming_firmware(av);
++
++out_clear_stream_watermark:
++	ipu6_isys_update_stream_watermark(av, 0);
++
++	return ret;
++}
++
++static const struct v4l2_ioctl_ops ioctl_ops_mplane = {
++	.vidioc_querycap = ipu6_isys_vidioc_querycap,
++	.vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt,
++	.vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes,
++	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane,
++	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane,
++	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane,
++	.vidioc_reqbufs = vb2_ioctl_reqbufs,
++	.vidioc_create_bufs = vb2_ioctl_create_bufs,
++	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++	.vidioc_querybuf = vb2_ioctl_querybuf,
++	.vidioc_qbuf = vb2_ioctl_qbuf,
++	.vidioc_dqbuf = vb2_ioctl_dqbuf,
++	.vidioc_streamon = vb2_ioctl_streamon,
++	.vidioc_streamoff = vb2_ioctl_streamoff,
++	.vidioc_expbuf = vb2_ioctl_expbuf,
++};
++
++static const struct media_entity_operations entity_ops = {
++	.link_validate = link_validate,
++};
++
++static const struct v4l2_file_operations isys_fops = {
++	.owner = THIS_MODULE,
++	.poll = vb2_fop_poll,
++	.unlocked_ioctl = video_ioctl2,
++	.mmap = vb2_fop_mmap,
++	.open = video_open,
++	.release = video_release,
++};
++
++int ipu6_isys_fw_open(struct ipu6_isys *isys)
++{
++	struct ipu6_bus_device *adev = isys->adev;
++	const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata;
++	int ret;
++
++	ret = pm_runtime_resume_and_get(&adev->auxdev.dev);
++	if (ret < 0)
++		return ret;
++
++	mutex_lock(&isys->mutex);
++
++	if (isys->ref_count++)
++		goto unlock;
++
++	ipu6_configure_spc(adev->isp, &ipdata->hw_variant,
++			   IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base,
++			   adev->pkg_dir, adev->pkg_dir_dma_addr);
++
++	/*
++	 * Buffers could have been left to wrong queue at last closure.
++	 * Move them now back to empty buffer queue.
++	 */
++	ipu6_cleanup_fw_msg_bufs(isys);
++
++	if (isys->fwcom) {
++		/*
++		 * Something went wrong in previous shutdown. As we are now
++		 * restarting isys we can safely delete old context.
++		 */
++		dev_warn(&adev->auxdev.dev, "clearing old context\n");
++		ipu6_fw_isys_cleanup(isys);
++	}
++
++	ret = ipu6_fw_isys_init(isys, ipdata->num_parallel_streams);
++	if (ret < 0)
++		goto out;
++
++unlock:
++	mutex_unlock(&isys->mutex);
++
++	return 0;
++
++out:
++	isys->ref_count--;
++	mutex_unlock(&isys->mutex);
++	pm_runtime_put(&adev->auxdev.dev);
++
++	return ret;
++}
++
++void ipu6_isys_fw_close(struct ipu6_isys *isys)
++{
++	mutex_lock(&isys->mutex);
++
++	isys->ref_count--;
++	if (!isys->ref_count) {
++		ipu6_fw_isys_close(isys);
++		if (isys->fwcom) {
++			isys->need_reset = true;
++			dev_warn(&isys->adev->auxdev.dev,
++				 "failed to close fw isys\n");
++		}
++	}
++
++	mutex_unlock(&isys->mutex);
++
++	if (isys->need_reset)
++		pm_runtime_put_sync(&isys->adev->auxdev.dev);
++	else
++		pm_runtime_put(&isys->adev->auxdev.dev);
++}
++
++int ipu6_isys_setup_video(struct ipu6_isys_video *av,
++			  struct media_entity **source_entity, int *nr_queues)
++{
++	struct device *dev = &av->isys->adev->auxdev.dev;
++	struct v4l2_mbus_frame_desc_entry entry;
++	struct v4l2_subdev_route *route = NULL;
++	struct v4l2_subdev_route *r;
++	struct v4l2_subdev_state *state;
++	struct ipu6_isys_subdev *asd;
++	struct v4l2_subdev *remote_sd;
++	struct media_pipeline *pipeline;
++	struct media_pad *source_pad, *remote_pad;
++	int ret = -EINVAL;
++
++	remote_pad = media_pad_remote_pad_first(&av->pad);
++	if (!remote_pad) {
++		dev_dbg(dev, "failed to get remote pad\n");
++		return -ENODEV;
++	}
++
++	remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
++	asd = to_ipu6_isys_subdev(remote_sd);
++	source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]);
++	if (!source_pad) {
++		dev_dbg(dev, "No external source entity\n");
++		return -ENODEV;
++	}
++
++	*source_entity = source_pad->entity;
++
++	/* Find the root */
++	state = v4l2_subdev_lock_and_get_active_state(remote_sd);
++	for_each_active_route(&state->routing, r) {
++		if (r->source_pad != remote_pad->index)
++			continue;
++
++		route = r;
++		break;
++	}
++
++	if (!route) {
++		v4l2_subdev_unlock_state(state);
++		dev_dbg(dev, "Failed to find route\n");
++		return -ENODEV;
++	}
++	v4l2_subdev_unlock_state(state);
++	av->source_stream = route->sink_stream;
++
++	ret = ipu6_isys_csi2_get_remote_desc(av->source_stream,
++					     to_ipu6_isys_csi2(asd),
++					     *source_entity, &entry,
++					     nr_queues);
++	if (ret == -ENOIOCTLCMD) {
++		av->vc = 0;
++		av->dt = ipu6_isys_mbus_code_to_mipi(av->pfmt->code);
++		*nr_queues = 1;
++	} else if (!ret) {
++		dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",
++			entry.stream, entry.length, entry.bus.csi2.vc,
++			entry.bus.csi2.dt);
++
++		av->vc = entry.bus.csi2.vc;
++		av->dt = entry.bus.csi2.dt;
++	} else {
++		dev_err(dev, "failed to get remote frame desc\n");
++		return ret;
++	}
++
++	pipeline = media_entity_pipeline(&av->vdev.entity);
++	if (!pipeline)
++		ret = video_device_pipeline_alloc_start(&av->vdev);
++	else
++		ret = video_device_pipeline_start(&av->vdev, pipeline);
++	if (ret < 0) {
++		dev_dbg(dev, "media pipeline start failed\n");
++		return ret;
++	}
++
++	av->stream = ipu6_isys_get_stream(av, asd);
++	if (!av->stream) {
++		video_device_pipeline_stop(&av->vdev);
++		dev_err(dev, "no available stream for firmware\n");
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++/*
++ * Do everything that's needed to initialise things related to video
++ * buffer queue, video node, and the related media entity. The caller
++ * is expected to assign isys field and set the name of the video
++ * device.
++ */
++int ipu6_isys_video_init(struct ipu6_isys_video *av)
++{
++	struct v4l2_format format = {
++		.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++		.fmt.pix_mp = {
++			.width = 1920,
++			.height = 1080,
++		},
++	};
++	int ret;
++
++	mutex_init(&av->mutex);
++	av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |
++			       V4L2_CAP_VIDEO_CAPTURE_MPLANE;
++	av->vdev.vfl_dir = VFL_DIR_RX;
++
++	ret = ipu6_isys_queue_init(&av->aq);
++	if (ret)
++		goto out_free_watermark;
++
++	av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
++	ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad);
++	if (ret)
++		goto out_vb2_queue_release;
++
++	av->vdev.entity.ops = &entity_ops;
++	av->vdev.release = video_device_release_empty;
++	av->vdev.fops = &isys_fops;
++	av->vdev.v4l2_dev = &av->isys->v4l2_dev;
++	if (!av->vdev.ioctl_ops)
++		av->vdev.ioctl_ops = &ioctl_ops_mplane;
++	av->vdev.queue = &av->aq.vbq;
++	av->vdev.lock = &av->mutex;
++
++	ipu6_isys_video_try_fmt_vid_mplane(av, &format.fmt.pix_mp);
++	av->mpix = format.fmt.pix_mp;
++
++	set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags);
++	video_set_drvdata(&av->vdev, av);
++
++	ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1);
++	if (ret)
++		goto out_media_entity_cleanup;
++
++	return ret;
++
++out_media_entity_cleanup:
++	vb2_video_unregister_device(&av->vdev);
++	media_entity_cleanup(&av->vdev.entity);
++
++out_vb2_queue_release:
++	vb2_queue_release(&av->aq.vbq);
++
++out_free_watermark:
++	mutex_destroy(&av->mutex);
++
++	return ret;
++}
++
++void ipu6_isys_video_cleanup(struct ipu6_isys_video *av)
++{
++	vb2_video_unregister_device(&av->vdev);
++	media_entity_cleanup(&av->vdev.entity);
++	mutex_destroy(&av->mutex);
++}
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+new file mode 100644
+index 000000000000..21cd33c7e277
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+@@ -0,0 +1,136 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/* Copyright (C) 2013 - 2023 Intel Corporation */
++
++#ifndef IPU6_ISYS_VIDEO_H
++#define IPU6_ISYS_VIDEO_H
++
++#include <linux/atomic.h>
++#include <linux/completion.h>
++#include <linux/container_of.h>
++#include <linux/list.h>
++#include <linux/mutex.h>
++
++#include <media/media-entity.h>
++#include <media/v4l2-dev.h>
++
++#include "ipu6-isys-queue.h"
++
++#define IPU6_ISYS_OUTPUT_PINS 11
++#define IPU6_ISYS_MAX_PARALLEL_SOF 2
++#define NR_OF_VIDEO_DEVICE 31
++
++struct file;
++struct ipu6_isys;
++struct ipu6_isys_subdev;
++
++struct ipu6_isys_pixelformat {
++	u32 pixelformat;
++	u32 bpp;
++	u32 bpp_packed;
++	u32 code;
++	u32 css_pixelformat;
++};
++
++struct sequence_info {
++	unsigned int sequence;
++	u64 timestamp;
++};
++
++struct output_pin_data {
++	void (*pin_ready)(struct ipu6_isys_stream *stream,
++			  struct ipu6_fw_isys_resp_info_abi *info);
++	struct ipu6_isys_queue *aq;
++};
++
++/*
++ * Align with firmware stream. Each stream represents a CSI virtual channel.
++ * May map to multiple video devices
++ */
++struct ipu6_isys_stream {
++	struct mutex mutex;
++	struct media_entity *source_entity;
++	atomic_t sequence;
++	unsigned int seq_index;
++	struct sequence_info seq[IPU6_ISYS_MAX_PARALLEL_SOF];
++	int stream_source;
++	int stream_handle;
++	unsigned int nr_output_pins;
++	struct ipu6_isys_subdev *asd;
++
++	int nr_queues;	/* Number of capture queues */
++	int nr_streaming;
++	int streaming;	/* Has streaming been really started? */
++	struct list_head queues;
++	struct completion stream_open_completion;
++	struct completion stream_close_completion;
++	struct completion stream_start_completion;
++	struct completion stream_stop_completion;
++	struct ipu6_isys *isys;
++
++	struct output_pin_data output_pins[IPU6_ISYS_OUTPUT_PINS];
++	int error;
++	u8 vc;
++};
++
++struct video_stream_watermark {
++	u32 width;
++	u32 height;
++	u32 hblank;
++	u32 frame_rate;
++	u64 pixel_rate;
++	u64 stream_data_rate;
++	u16 sram_gran_shift;
++	u16 sram_gran_size;
++	struct list_head stream_node;
++};
++
++struct ipu6_isys_video {
++	struct ipu6_isys_queue aq;
++	/* Serialise access to other fields in the struct. */
++	struct mutex mutex;
++	struct media_pad pad;
++	struct video_device vdev;
++	struct v4l2_pix_format_mplane mpix;
++	const struct ipu6_isys_pixelformat *pfmt;
++	struct ipu6_isys *isys;
++	struct ipu6_isys_stream *stream;
++	unsigned int streaming;
++	struct video_stream_watermark watermark;
++	u32 source_stream;
++	u8 vc;
++	u8 dt;
++};
++
++#define ipu6_isys_queue_to_video(__aq) \
++	container_of(__aq, struct ipu6_isys_video, aq)
++
++extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts[];
++extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[];
++
++int ipu6_isys_vidioc_querycap(struct file *file, void *fh,
++			      struct v4l2_capability *cap);
++
++int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,
++			      struct v4l2_fmtdesc *f);
++int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
++				   struct media_entity *source_entity,
++				   int nr_queues);
++int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
++				  struct ipu6_isys_buffer_list *bl);
++int ipu6_isys_fw_open(struct ipu6_isys *isys);
++void ipu6_isys_fw_close(struct ipu6_isys *isys);
++int ipu6_isys_setup_video(struct ipu6_isys_video *av,
++			  struct media_entity **source_entity, int *nr_queues);
++int ipu6_isys_video_init(struct ipu6_isys_video *av);
++void ipu6_isys_video_cleanup(struct ipu6_isys_video *av);
++void ipu6_isys_put_stream(struct ipu6_isys_stream *stream);
++struct ipu6_isys_stream *
++ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle);
++struct ipu6_isys_stream *
++ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc);
++
++void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
++					  bool state);
++void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state);
++
++#endif /* IPU6_ISYS_VIDEO_H */
+-- 
+2.43.2
+
+
+From cc79447bab87ce8c498b0e7a5f849c7d4f6262c0 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:26 +0800
+Subject: [PATCH 19/33] media: add Kconfig and Makefile for IPU6
+
+Add IPU6 support in Kconfig and Makefile, with this patch you can
+build the Intel IPU6 and input system modules by select the
+CONFIG_VIDEO_INTEL_IPU6 in config.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+Signed-off-by: Andreas Helbech Kleist <andreaskleist@gmail.com>
+---
+ drivers/media/pci/intel/Kconfig       |  1 +
+ drivers/media/pci/intel/Makefile      |  1 +
+ drivers/media/pci/intel/ipu6/Kconfig  | 17 +++++++++++++++++
+ drivers/media/pci/intel/ipu6/Makefile | 23 +++++++++++++++++++++++
+ 4 files changed, 42 insertions(+)
+ create mode 100644 drivers/media/pci/intel/ipu6/Kconfig
+ create mode 100644 drivers/media/pci/intel/ipu6/Makefile
+
+diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig
+index ee4684159d3d..04cb3d253486 100644
+--- a/drivers/media/pci/intel/Kconfig
++++ b/drivers/media/pci/intel/Kconfig
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+ 
+ source "drivers/media/pci/intel/ipu3/Kconfig"
++source "drivers/media/pci/intel/ipu6/Kconfig"
+ source "drivers/media/pci/intel/ivsc/Kconfig"
+ 
+ config IPU_BRIDGE
+diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile
+index f199a97e1d78..3a2cc6567159 100644
+--- a/drivers/media/pci/intel/Makefile
++++ b/drivers/media/pci/intel/Makefile
+@@ -5,3 +5,4 @@
+ obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o
+ obj-y	+= ipu3/
+ obj-y	+= ivsc/
++obj-$(CONFIG_VIDEO_INTEL_IPU6)	+= ipu6/
+diff --git a/drivers/media/pci/intel/ipu6/Kconfig b/drivers/media/pci/intel/ipu6/Kconfig
+new file mode 100644
+index 000000000000..5cb4f3c2d59f
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/Kconfig
+@@ -0,0 +1,17 @@
++config VIDEO_INTEL_IPU6
++	tristate "Intel IPU6 driver"
++	depends on ACPI || COMPILE_TEST
++	depends on MEDIA_SUPPORT
++	depends on MEDIA_PCI_SUPPORT
++	depends on X86 && X86_64
++	select IOMMU_IOVA
++	select VIDEO_V4L2_SUBDEV_API
++	select VIDEOBUF2_DMA_CONTIG
++	select V4L2_FWNODE
++	select IPU_BRIDGE
++	help
++	  This is the 6th Gen Intel Image Processing Unit, found in Intel SoCs
++	  and used for capturing images and video from camera sensors.
++
++	  To compile this driver, say Y here! It contains 2 modules -
++	  intel_ipu6 and intel_ipu6_isys.
+diff --git a/drivers/media/pci/intel/ipu6/Makefile b/drivers/media/pci/intel/ipu6/Makefile
+new file mode 100644
+index 000000000000..a821b0a1567f
+--- /dev/null
++++ b/drivers/media/pci/intel/ipu6/Makefile
+@@ -0,0 +1,23 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++intel-ipu6-y			:= ipu6.o \
++				ipu6-bus.o \
++				ipu6-dma.o \
++				ipu6-mmu.o \
++				ipu6-buttress.o \
++				ipu6-cpd.o \
++				ipu6-fw-com.o
++
++obj-$(CONFIG_VIDEO_INTEL_IPU6)	+= intel-ipu6.o
++
++intel-ipu6-isys-y		:= ipu6-isys.o \
++				ipu6-isys-csi2.o \
++				ipu6-fw-isys.o \
++				ipu6-isys-video.o \
++				ipu6-isys-queue.o \
++				ipu6-isys-subdev.o \
++				ipu6-isys-mcd-phy.o \
++				ipu6-isys-jsl-phy.o \
++				ipu6-isys-dwc-phy.o
++
++obj-$(CONFIG_VIDEO_INTEL_IPU6)	+= intel-ipu6-isys.o
+-- 
+2.43.2
+
+
+From edc6bed6991727e64f1eb60c0392403c39b96ba4 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:27 +0800
+Subject: [PATCH 20/33] MAINTAINERS: add maintainers for Intel IPU6 input
+ system driver
+
+Update MAINTAINERS file for Intel IPU6 input system driver.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ MAINTAINERS | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 1aabf1c15bb3..5346d472cb0f 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -10899,6 +10899,16 @@ F:	Documentation/admin-guide/media/ipu3_rcb.svg
+ F:	Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst
+ F:	drivers/staging/media/ipu3/
+ 
++INTEL IPU6 INPUT SYSTEM DRIVER
++M:	Sakari Ailus <sakari.ailus@linux.intel.com>
++M:	Bingbu Cao <bingbu.cao@intel.com>
++R:	Tianshu Qiu <tian.shu.qiu@intel.com>
++L:	linux-media@vger.kernel.org
++S:	Maintained
++T:	git git://linuxtv.org/media_tree.git
++F:	Documentation/admin-guide/media/ipu6-isys.rst
++F:	drivers/media/pci/intel/ipu6/
++
+ INTEL ISHTP ECLITE DRIVER
+ M:	Sumesh K Naduvalath <sumesh.k.naduvalath@intel.com>
+ L:	platform-driver-x86@vger.kernel.org
+-- 
+2.43.2
+
+
+From a12041e5f7fb32b93669f19b579bc1940a026bbe Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:28 +0800
+Subject: [PATCH 21/33] Documentation: add Intel IPU6 ISYS driver admin-guide
+ doc
+
+This document mainly describe the functionality of IPU6 and
+IPU6 isys driver, and gives an example that how user can do
+imaging capture with tools.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ Documentation/admin-guide/media/ipu6-isys.rst | 158 ++++++++++++++++
+ .../admin-guide/media/ipu6_isys_graph.svg     | 174 ++++++++++++++++++
+ .../admin-guide/media/v4l-drivers.rst         |   1 +
+ 3 files changed, 333 insertions(+)
+ create mode 100644 Documentation/admin-guide/media/ipu6-isys.rst
+ create mode 100644 Documentation/admin-guide/media/ipu6_isys_graph.svg
+
+diff --git a/Documentation/admin-guide/media/ipu6-isys.rst b/Documentation/admin-guide/media/ipu6-isys.rst
+new file mode 100644
+index 000000000000..5e78ab88c649
+--- /dev/null
++++ b/Documentation/admin-guide/media/ipu6-isys.rst
+@@ -0,0 +1,158 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++.. include:: <isonum.txt>
++
++========================================================
++Intel Image Processing Unit 6 (IPU6) Input System driver
++========================================================
++
++Copyright |copy| 2023 Intel Corporation
++
++Introduction
++============
++
++This file documents the Intel IPU6 (6th generation Image Processing Unit)
++Input System (MIPI CSI2 receiver) drivers located under
++drivers/media/pci/intel/ipu6.
++
++The Intel IPU6 can be found in certain Intel Chipsets but not in all SKUs:
++
++* TigerLake
++* JasperLake
++* AlderLake
++* RaptorLake
++* MeteorLake
++
++Intel IPU6 is made up of two components - Input System (ISYS) and Processing
++System (PSYS).
++
++The Input System mainly works as MIPI CSI2 receiver which receives and
++processes the imaging data from the sensors and outputs the frames to memory.
++
++There are 2 driver modules - intel_ipu6 and intel_ipu6_isys. intel_ipu6 is an
++IPU6 common driver which does PCI configuration, firmware loading and parsing,
++firmware authentication, DMA mapping and IPU-MMU (internal Memory mapping Unit)
++configuration. intel_ipu6_isys implements V4L2, Media Controller and V4L2
++sub-device interfaces. The IPU6 ISYS driver supports camera sensors connected
++to the IPU6 ISYS through V4L2 sub-device sensor drivers.
++
++.. Note:: See Documentation/driver-api/media/drivers/ipu6.rst for more
++	  information about the IPU6 hardware.
++
++
++Input system driver
++===================
++
++The input System driver mainly configures CSI2 DPHY, constructs the firmware
++stream configuration, sends commands to firmware, gets response from hardware
++and firmware and then returns buffers to user.
++The ISYS is represented as several V4L2 sub-devices - 'Intel IPU6 CSI2 $port',
++which provide V4L2 subdev interfaces to the user space, there are also several
++video nodes for each CSI-2 stream capture - 'Intel IPU6 ISYS capture $num' which
++provide interface to user to set formats, queue buffers and streaming.
++
++.. kernel-figure::  ipu6_isys_graph.svg
++   :alt: ipu6 isys media graph with multiple streams support
++
++   ipu6 isys media graph with multiple streams support
++
++Capturing frames by IPU6 ISYS
++-----------------------------
++
++IPU6 ISYS is used to capture frames from the camera sensors connected to the
++CSI2 ports. The supported input formats of ISYS are listed in table below:
++
++.. tabularcolumns:: |p{0.8cm}|p{4.0cm}|p{4.0cm}|
++
++.. flat-table::
++    :header-rows: 1
++
++    * - IPU6 ISYS supported input formats
++
++    * - RGB565, RGB888
++
++    * - UYVY8, YUYV8
++
++    * - RAW8, RAW10, RAW12
++
++.. _ipu6_isys_capture_examples:
++
++Examples
++~~~~~~~~
++Here is an example of IPU6 ISYS raw capture on Dell XPS 9315 laptop. On this
++machine, ov01a10 sensor is connected to IPU ISYS CSI2 port 2, which can
++generate images at sBGGR10 with resolution 1280x800.
++
++Using the media controller APIs, we can configure ov01a10 sensor by
++media-ctl [#f1]_ and yavta [#f2]_ to transmit frames to IPU6 ISYS.
++
++.. code-block:: none
++
++    # Example 1 capture frame from ov01a10 camera sensor
++    # This example assumes /dev/media0 as the IPU ISYS media device
++    export MDEV=/dev/media0
++
++    # Establish the link for the media devices using media-ctl
++    media-ctl -d $MDEV -l "\"ov01a10 3-0036\":0 -> \"Intel IPU6 CSI2 2\":0[1]"
++
++    # Set the format for the media devices
++    media-ctl -d $MDEV -V "ov01a10:0 [fmt:SBGGR10/1280x800]"
++    media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:0 [fmt:SBGGR10/1280x800]"
++    media-ctl -d $MDEV -V "Intel IPU6 CSI2 2:1 [fmt:SBGGR10/1280x800]"
++
++Once the media pipeline is configured, desired sensor specific settings
++(such as exposure and gain settings) can be set, using the yavta tool.
++
++e.g
++
++.. code-block:: none
++
++    # and that ov01a10 sensor is connected to i2c bus 3 with address 0x36
++    export SDEV=$(media-ctl -d $MDEV -e "ov01a10 3-0036")
++
++    yavta -w 0x009e0903 400 $SDEV
++    yavta -w 0x009e0913 1000 $SDEV
++    yavta -w 0x009e0911 2000 $SDEV
++
++Once the desired sensor settings are set, frame captures can be done as below.
++
++e.g
++
++.. code-block:: none
++
++    yavta --data-prefix -u -c10 -n5 -I -s 1280x800 --file=/tmp/frame-#.bin \
++          -f SBGGR10 $(media-ctl -d $MDEV -e "Intel IPU6 ISYS Capture 0")
++
++With the above command, 10 frames are captured at 1280x800 resolution with
++sBGGR10 format. The captured frames are available as /tmp/frame-#.bin files.
++
++Here is another example of IPU6 ISYS RAW and metadata capture from camera
++sensor ov2740 on Lenovo X1 Yoga laptop.
++
++.. code-block:: none
++
++    media-ctl -l "\"ov2740 14-0036\":0 -> \"Intel IPU6 CSI2 1\":0[1]"
++    media-ctl -l "\"Intel IPU6 CSI2 1\":1 -> \"Intel IPU6 ISYS Capture 0\":0[5]"
++    media-ctl -l "\"Intel IPU6 CSI2 1\":2 -> \"Intel IPU6 ISYS Capture 1\":0[5]"
++
++    # set routing
++    media-ctl -v -R "\"Intel IPU6 CSI2 1\" [0/0->1/0[1],0/1->2/1[1]]"
++
++    media-ctl -v "\"Intel IPU6 CSI2 1\":0/0 [fmt:SGRBG10/1932x1092]"
++    media-ctl -v "\"Intel IPU6 CSI2 1\":0/1 [fmt:GENERIC_8/97x1]"
++    media-ctl -v "\"Intel IPU6 CSI2 1\":1/0 [fmt:SGRBG10/1932x1092]"
++    media-ctl -v "\"Intel IPU6 CSI2 1\":2/1 [fmt:GENERIC_8/97x1]"
++
++    CAPTURE_DEV=$(media-ctl -e "Intel IPU6 ISYS Capture 0")
++    ./yavta --data-prefix -c100 -n5 -I -s1932x1092 --file=/tmp/frame-#.bin \
++    -f SGRBG10 ${CAPTURE_DEV}
++
++    CAPTURE_META=$(media-ctl -e "Intel IPU6 ISYS Capture 1")
++    ./yavta --data-prefix -c100 -n5 -I -s97x1 -B meta-capture \
++    --file=/tmp/meta-#.bin -f GENERIC_8 ${CAPTURE_META}
++
++References
++==========
++
++.. [#f1] https://git.ideasonboard.org/?p=media-ctl.git;a=summary
++.. [#f2] https://git.ideasonboard.org/yavta.git
+diff --git a/Documentation/admin-guide/media/ipu6_isys_graph.svg b/Documentation/admin-guide/media/ipu6_isys_graph.svg
+new file mode 100644
+index 000000000000..707747c75280
+--- /dev/null
++++ b/Documentation/admin-guide/media/ipu6_isys_graph.svg
+@@ -0,0 +1,174 @@
++<?xml version="1.0" encoding="UTF-8" standalone="no"?>
++<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
++ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
++<!-- Generated by graphviz version 2.38.0 (20140413.2041)
++ -->
++<!-- Title: board Pages: 1 -->
++<svg width="559pt" height="810pt"
++ viewBox="0.00 0.00 559.00 809.50" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
++<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 805.5)">
++<title>board</title>
++<polygon fill="white" stroke="none" points="-4,4 -4,-805.5 555,-805.5 555,4 -4,4"/>
++<!-- n00000001 -->
++<g id="node1" class="node"><title>n00000001</title>
++<polygon fill="#66cd00" stroke="black" points="551,-192.5 387,-192.5 387,-154.5 551,-154.5 551,-192.5"/>
++<text text-anchor="middle" x="469" y="-177.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 0</text>
++<text text-anchor="middle" x="469" y="-162.3" font-family="Times,serif" font-size="14.00">/dev/video0</text>
++</g>
++<!-- n00000002 -->
++<g id="node2" class="node"><title>n00000002</title>
++<polygon fill="#66cd00" stroke="black" points="551,-395.5 387,-395.5 387,-357.5 551,-357.5 551,-395.5"/>
++<text text-anchor="middle" x="469" y="-380.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 1</text>
++<text text-anchor="middle" x="469" y="-365.3" font-family="Times,serif" font-size="14.00">/dev/video1</text>
++</g>
++<!-- n00000003 -->
++<g id="node3" class="node"><title>n00000003</title>
++<polygon fill="#66cd00" stroke="black" points="551,-598.5 387,-598.5 387,-560.5 551,-560.5 551,-598.5"/>
++<text text-anchor="middle" x="469" y="-583.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 2</text>
++<text text-anchor="middle" x="469" y="-568.3" font-family="Times,serif" font-size="14.00">/dev/video2</text>
++</g>
++<!-- n00000004 -->
++<g id="node4" class="node"><title>n00000004</title>
++<polygon fill="#66cd00" stroke="black" points="551,-801.5 387,-801.5 387,-763.5 551,-763.5 551,-801.5"/>
++<text text-anchor="middle" x="469" y="-786.3" font-family="Times,serif" font-size="14.00">Intel IPU6 ISYS Capture 3</text>
++<text text-anchor="middle" x="469" y="-771.3" font-family="Times,serif" font-size="14.00">/dev/video3</text>
++</g>
++<!-- n0000007d -->
++<g id="node5" class="node"><title>n0000007d</title>
++<path fill="#ffb90f" stroke="black" d="M201,-0.5C201,-0.5 339,-0.5 339,-0.5 345,-0.5 351,-6.5 351,-12.5 351,-12.5 351,-172.5 351,-172.5 351,-178.5 345,-184.5 339,-184.5 339,-184.5 201,-184.5 201,-184.5 195,-184.5 189,-178.5 189,-172.5 189,-172.5 189,-12.5 189,-12.5 189,-6.5 195,-0.5 201,-0.5"/>
++<text text-anchor="middle" x="200.5" y="-88.8" font-family="Times,serif" font-size="14.00">0</text>
++<polyline fill="none" stroke="black" points="212,-0.5 212,-184.5 "/>
++<text text-anchor="middle" x="270" y="-96.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 0</text>
++<text text-anchor="middle" x="270" y="-81.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev0</text>
++<polyline fill="none" stroke="black" points="328,-0.5 328,-184.5 "/>
++<text text-anchor="middle" x="339.5" y="-169.3" font-family="Times,serif" font-size="14.00">1</text>
++<polyline fill="none" stroke="black" points="328,-161.5 351,-161.5 "/>
++<text text-anchor="middle" x="339.5" y="-146.3" font-family="Times,serif" font-size="14.00">2</text>
++<polyline fill="none" stroke="black" points="328,-138.5 351,-138.5 "/>
++<text text-anchor="middle" x="339.5" y="-123.3" font-family="Times,serif" font-size="14.00">3</text>
++<polyline fill="none" stroke="black" points="328,-115.5 351,-115.5 "/>
++<text text-anchor="middle" x="339.5" y="-100.3" font-family="Times,serif" font-size="14.00">4</text>
++<polyline fill="none" stroke="black" points="328,-92.5 351,-92.5 "/>
++<text text-anchor="middle" x="339.5" y="-77.3" font-family="Times,serif" font-size="14.00">5</text>
++<polyline fill="none" stroke="black" points="328,-69.5 351,-69.5 "/>
++<text text-anchor="middle" x="339.5" y="-54.3" font-family="Times,serif" font-size="14.00">6</text>
++<polyline fill="none" stroke="black" points="328,-46.5 351,-46.5 "/>
++<text text-anchor="middle" x="339.5" y="-31.3" font-family="Times,serif" font-size="14.00">7</text>
++<polyline fill="none" stroke="black" points="328,-23.5 351,-23.5 "/>
++<text text-anchor="middle" x="339.5" y="-8.3" font-family="Times,serif" font-size="14.00">8</text>
++</g>
++<!-- n0000007d&#45;&gt;n00000001 -->
++<g id="edge1" class="edge"><title>n0000007d:port1&#45;&gt;n00000001</title>
++<path fill="none" stroke="black" stroke-dasharray="5,2" d="M351,-173.5C359.322,-173.5 367.976,-173.5 376.644,-173.5"/>
++<polygon fill="black" stroke="black" points="376.807,-177 386.807,-173.5 376.807,-170 376.807,-177"/>
++</g>
++<!-- n00000087 -->
++<g id="node6" class="node"><title>n00000087</title>
++<path fill="#ffb90f" stroke="black" d="M201,-203.5C201,-203.5 339,-203.5 339,-203.5 345,-203.5 351,-209.5 351,-215.5 351,-215.5 351,-375.5 351,-375.5 351,-381.5 345,-387.5 339,-387.5 339,-387.5 201,-387.5 201,-387.5 195,-387.5 189,-381.5 189,-375.5 189,-375.5 189,-215.5 189,-215.5 189,-209.5 195,-203.5 201,-203.5"/>
++<text text-anchor="middle" x="200.5" y="-291.8" font-family="Times,serif" font-size="14.00">0</text>
++<polyline fill="none" stroke="black" points="212,-203.5 212,-387.5 "/>
++<text text-anchor="middle" x="270" y="-299.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 1</text>
++<text text-anchor="middle" x="270" y="-284.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev1</text>
++<polyline fill="none" stroke="black" points="328,-203.5 328,-387.5 "/>
++<text text-anchor="middle" x="339.5" y="-372.3" font-family="Times,serif" font-size="14.00">1</text>
++<polyline fill="none" stroke="black" points="328,-364.5 351,-364.5 "/>
++<text text-anchor="middle" x="339.5" y="-349.3" font-family="Times,serif" font-size="14.00">2</text>
++<polyline fill="none" stroke="black" points="328,-341.5 351,-341.5 "/>
++<text text-anchor="middle" x="339.5" y="-326.3" font-family="Times,serif" font-size="14.00">3</text>
++<polyline fill="none" stroke="black" points="328,-318.5 351,-318.5 "/>
++<text text-anchor="middle" x="339.5" y="-303.3" font-family="Times,serif" font-size="14.00">4</text>
++<polyline fill="none" stroke="black" points="328,-295.5 351,-295.5 "/>
++<text text-anchor="middle" x="339.5" y="-280.3" font-family="Times,serif" font-size="14.00">5</text>
++<polyline fill="none" stroke="black" points="328,-272.5 351,-272.5 "/>
++<text text-anchor="middle" x="339.5" y="-257.3" font-family="Times,serif" font-size="14.00">6</text>
++<polyline fill="none" stroke="black" points="328,-249.5 351,-249.5 "/>
++<text text-anchor="middle" x="339.5" y="-234.3" font-family="Times,serif" font-size="14.00">7</text>
++<polyline fill="none" stroke="black" points="328,-226.5 351,-226.5 "/>
++<text text-anchor="middle" x="339.5" y="-211.3" font-family="Times,serif" font-size="14.00">8</text>
++</g>
++<!-- n00000087&#45;&gt;n00000002 -->
++<g id="edge2" class="edge"><title>n00000087:port1&#45;&gt;n00000002</title>
++<path fill="none" stroke="black" stroke-dasharray="5,2" d="M351,-376.5C359.322,-376.5 367.976,-376.5 376.644,-376.5"/>
++<polygon fill="black" stroke="black" points="376.807,-380 386.807,-376.5 376.807,-373 376.807,-380"/>
++</g>
++<!-- n00000091 -->
++<g id="node7" class="node"><title>n00000091</title>
++<path fill="#ffb90f" stroke="black" d="M201,-406.5C201,-406.5 339,-406.5 339,-406.5 345,-406.5 351,-412.5 351,-418.5 351,-418.5 351,-578.5 351,-578.5 351,-584.5 345,-590.5 339,-590.5 339,-590.5 201,-590.5 201,-590.5 195,-590.5 189,-584.5 189,-578.5 189,-578.5 189,-418.5 189,-418.5 189,-412.5 195,-406.5 201,-406.5"/>
++<text text-anchor="middle" x="200.5" y="-494.8" font-family="Times,serif" font-size="14.00">0</text>
++<polyline fill="none" stroke="black" points="212,-406.5 212,-590.5 "/>
++<text text-anchor="middle" x="270" y="-502.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 2</text>
++<text text-anchor="middle" x="270" y="-487.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev2</text>
++<polyline fill="none" stroke="black" points="328,-406.5 328,-590.5 "/>
++<text text-anchor="middle" x="339.5" y="-575.3" font-family="Times,serif" font-size="14.00">1</text>
++<polyline fill="none" stroke="black" points="328,-567.5 351,-567.5 "/>
++<text text-anchor="middle" x="339.5" y="-552.3" font-family="Times,serif" font-size="14.00">2</text>
++<polyline fill="none" stroke="black" points="328,-544.5 351,-544.5 "/>
++<text text-anchor="middle" x="339.5" y="-529.3" font-family="Times,serif" font-size="14.00">3</text>
++<polyline fill="none" stroke="black" points="328,-521.5 351,-521.5 "/>
++<text text-anchor="middle" x="339.5" y="-506.3" font-family="Times,serif" font-size="14.00">4</text>
++<polyline fill="none" stroke="black" points="328,-498.5 351,-498.5 "/>
++<text text-anchor="middle" x="339.5" y="-483.3" font-family="Times,serif" font-size="14.00">5</text>
++<polyline fill="none" stroke="black" points="328,-475.5 351,-475.5 "/>
++<text text-anchor="middle" x="339.5" y="-460.3" font-family="Times,serif" font-size="14.00">6</text>
++<polyline fill="none" stroke="black" points="328,-452.5 351,-452.5 "/>
++<text text-anchor="middle" x="339.5" y="-437.3" font-family="Times,serif" font-size="14.00">7</text>
++<polyline fill="none" stroke="black" points="328,-429.5 351,-429.5 "/>
++<text text-anchor="middle" x="339.5" y="-414.3" font-family="Times,serif" font-size="14.00">8</text>
++</g>
++<!-- n00000091&#45;&gt;n00000003 -->
++<g id="edge3" class="edge"><title>n00000091:port1&#45;&gt;n00000003</title>
++<path fill="none" stroke="black" d="M351,-579.5C359.322,-579.5 367.976,-579.5 376.644,-579.5"/>
++<polygon fill="black" stroke="black" points="376.807,-583 386.807,-579.5 376.807,-576 376.807,-583"/>
++</g>
++<!-- n0000009b -->
++<g id="node8" class="node"><title>n0000009b</title>
++<path fill="#ffb90f" stroke="black" d="M201,-609.5C201,-609.5 339,-609.5 339,-609.5 345,-609.5 351,-615.5 351,-621.5 351,-621.5 351,-781.5 351,-781.5 351,-787.5 345,-793.5 339,-793.5 339,-793.5 201,-793.5 201,-793.5 195,-793.5 189,-787.5 189,-781.5 189,-781.5 189,-621.5 189,-621.5 189,-615.5 195,-609.5 201,-609.5"/>
++<text text-anchor="middle" x="200.5" y="-697.8" font-family="Times,serif" font-size="14.00">0</text>
++<polyline fill="none" stroke="black" points="212,-609.5 212,-793.5 "/>
++<text text-anchor="middle" x="270" y="-705.3" font-family="Times,serif" font-size="14.00">Intel IPU6 CSI2 3</text>
++<text text-anchor="middle" x="270" y="-690.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev3</text>
++<polyline fill="none" stroke="black" points="328,-609.5 328,-793.5 "/>
++<text text-anchor="middle" x="339.5" y="-778.3" font-family="Times,serif" font-size="14.00">1</text>
++<polyline fill="none" stroke="black" points="328,-770.5 351,-770.5 "/>
++<text text-anchor="middle" x="339.5" y="-755.3" font-family="Times,serif" font-size="14.00">2</text>
++<polyline fill="none" stroke="black" points="328,-747.5 351,-747.5 "/>
++<text text-anchor="middle" x="339.5" y="-732.3" font-family="Times,serif" font-size="14.00">3</text>
++<polyline fill="none" stroke="black" points="328,-724.5 351,-724.5 "/>
++<text text-anchor="middle" x="339.5" y="-709.3" font-family="Times,serif" font-size="14.00">4</text>
++<polyline fill="none" stroke="black" points="328,-701.5 351,-701.5 "/>
++<text text-anchor="middle" x="339.5" y="-686.3" font-family="Times,serif" font-size="14.00">5</text>
++<polyline fill="none" stroke="black" points="328,-678.5 351,-678.5 "/>
++<text text-anchor="middle" x="339.5" y="-663.3" font-family="Times,serif" font-size="14.00">6</text>
++<polyline fill="none" stroke="black" points="328,-655.5 351,-655.5 "/>
++<text text-anchor="middle" x="339.5" y="-640.3" font-family="Times,serif" font-size="14.00">7</text>
++<polyline fill="none" stroke="black" points="328,-632.5 351,-632.5 "/>
++<text text-anchor="middle" x="339.5" y="-617.3" font-family="Times,serif" font-size="14.00">8</text>
++</g>
++<!-- n0000009b&#45;&gt;n00000004 -->
++<g id="edge4" class="edge"><title>n0000009b:port1&#45;&gt;n00000004</title>
++<path fill="none" stroke="black" stroke-dasharray="5,2" d="M351,-782.5C359.322,-782.5 367.976,-782.5 376.644,-782.5"/>
++<polygon fill="black" stroke="black" points="376.807,-786 386.807,-782.5 376.807,-779 376.807,-786"/>
++</g>
++<!-- n00000865 -->
++<g id="node9" class="node"><title>n00000865</title>
++<path fill="cornflowerblue" stroke="black" d="M12,-479.5C12,-479.5 141,-479.5 141,-479.5 147,-479.5 153,-485.5 153,-491.5 153,-491.5 153,-505.5 153,-505.5 153,-511.5 147,-517.5 141,-517.5 141,-517.5 12,-517.5 12,-517.5 6,-517.5 0,-511.5 0,-505.5 0,-505.5 0,-491.5 0,-491.5 0,-485.5 6,-479.5 12,-479.5"/>
++<text text-anchor="middle" x="10" y="-494.8" font-family="Times,serif" font-size="14.00"> </text>
++<polyline fill="none" stroke="black" points="20,-479.5 20,-517.5 "/>
++<text text-anchor="middle" x="75" y="-502.3" font-family="Times,serif" font-size="14.00">ov01a10 3&#45;0036</text>
++<text text-anchor="middle" x="75" y="-487.3" font-family="Times,serif" font-size="14.00">/dev/v4l&#45;subdev4</text>
++<polyline fill="none" stroke="black" points="130,-479.5 130,-517.5 "/>
++<text text-anchor="middle" x="141.5" y="-494.8" font-family="Times,serif" font-size="14.00">0</text>
++</g>
++<!-- n00000865&#45;&gt;n00000091 -->
++<g id="edge5" class="edge"><title>n00000865:port0&#45;&gt;n00000091:port0</title>
++<path fill="none" stroke="black" d="M153,-498.5C165,-498.5 170.25,-498.5 178.875,-498.5"/>
++<polygon fill="black" stroke="black" points="179,-502 189,-498.5 179,-495 179,-502"/>
++</g>
++<!-- n00000866 -->
++<!-- n00000866&#45;&gt;n0000007d -->
++<!-- n00000867 -->
++<!-- n00000867&#45;&gt;n00000087 -->
++<!-- n00000868 -->
++<!-- n00000868&#45;&gt;n0000009b -->
++</g>
++</svg>
+diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst
+index f4bb2605f07e..4120eded9a13 100644
+--- a/Documentation/admin-guide/media/v4l-drivers.rst
++++ b/Documentation/admin-guide/media/v4l-drivers.rst
+@@ -16,6 +16,7 @@ Video4Linux (V4L) driver-specific documentation
+ 	imx
+ 	imx7
+ 	ipu3
++	ipu6-isys
+ 	ivtv
+ 	mgb4
+ 	omap3isp
+-- 
+2.43.2
+
+
+From 3e80683ecc9ffe38fdf6e6232089794b6019816b Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:29 +0800
+Subject: [PATCH 22/33] Documentation: add documentation of Intel IPU6 driver
+ and hardware overview
+
+Add a documentation for an overview of IPU6 hardware and describe the main
+the components of IPU6 driver.
+
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ .../driver-api/media/drivers/index.rst        |   1 +
+ .../driver-api/media/drivers/ipu6.rst         | 205 ++++++++++++++++++
+ 2 files changed, 206 insertions(+)
+ create mode 100644 Documentation/driver-api/media/drivers/ipu6.rst
+
+diff --git a/Documentation/driver-api/media/drivers/index.rst b/Documentation/driver-api/media/drivers/index.rst
+index c4123a16b5f9..7f6f3dcd5c90 100644
+--- a/Documentation/driver-api/media/drivers/index.rst
++++ b/Documentation/driver-api/media/drivers/index.rst
+@@ -26,6 +26,7 @@ Video4Linux (V4L) drivers
+ 	vimc-devel
+ 	zoran
+ 	ccs/ccs
++	ipu6
+ 
+ 
+ Digital TV drivers
+diff --git a/Documentation/driver-api/media/drivers/ipu6.rst b/Documentation/driver-api/media/drivers/ipu6.rst
+new file mode 100644
+index 000000000000..b6357155c13b
+--- /dev/null
++++ b/Documentation/driver-api/media/drivers/ipu6.rst
+@@ -0,0 +1,205 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++==================
++Intel IPU6 Driver
++==================
++
++Author: Bingbu Cao <bingbu.cao@intel.com>
++
++Overview
++=========
++
++Intel IPU6 is the sixth generation of Intel Image Processing Unit used in some
++Intel Chipsets such as Tiger Lake, Jasper Lake, Alder Lake, Raptor Lake and
++Meteor Lake. IPU6 consists of two major systems: Input System (IS) and
++Processing System (PS). IPU6 are visible on the PCI bus as a single device,
++it can be found by ``lspci``:
++
++``0000:00:05.0 Multimedia controller: Intel Corporation Device xxxx (rev xx)``
++
++IPU6 has a 16 MB BAR in PCI configuration Space for MMIO registers which is
++visible for driver.
++
++Buttress
++=========
++
++The IPU6 is connecting to the system fabric with ``Buttress`` which is enabling
++host driver to control the IPU6, it also allows IPU6 access the system memory to
++store and load frame pixel streams and any other metadata.
++
++``Buttress`` mainly manages several system functionalities - power management,
++interrupt handling, firmware authentication and global timer sync.
++
++IS and PS Power flow
++---------------------------
++
++IPU6 driver initialize the IS and PS power up or down request by setting the
++Buttress frequency control register for IS and PS -
++``IPU6_BUTTRESS_REG_IS_FREQ_CTL`` and ``IPU6_BUTTRESS_REG_PS_FREQ_CTL`` in
++function:
++
++.. c:function:: int ipu6_buttress_power(..., bool on)
++
++Buttress forwards the request to Punit, after Punit execute the power up flow,
++buttress indicates driver that IS or PS is powered up by updating the power
++status registers.
++
++.. Note:: IS power up needs take place prior to PS power up, IS power down needs
++	  take place after PS power down due to hardware limitation.
++
++
++Interrupt
++------------
++
++IPU6 interrupt can be generated as MSI or INTA, interrupt will be triggered
++when IS, PS, Buttress event or error happen, driver can get the interrupt
++cause by reading the interrupt status register ``BUTTRESS_REG_ISR_STATUS``,
++driver firstly clear the irq status and then call specific IS or PS irq handler.
++
++.. c:function:: irqreturn_t ipu6_buttress_isr(int irq, ...)
++
++Security and firmware authentication
++-------------------------------------
++To address the IPU6 firmware security concerns, the IPU6 firmware needs to
++undergo an authentication process before it is allowed to executed on the IPU6
++internal processors. Driver will work with Converged Security Engine (CSE) to
++complete authentication process. CSE is responsible of authenticating the
++IPU6 firmware, the authenticated firmware binary is copied into an isolated
++memory region. Firmware authentication process is implemented by CSE following
++an IPC handshake with driver. There are some Buttress registers used by CSE and
++driver to communicate with each other as IPC messages.
++
++.. c:function:: int ipu6_buttress_authenticate(...)
++
++Global timer sync
++------------------
++IPU driver initiates a Hammock Harbor synchronization flow each time it starts
++camera operation. IPU will synchronizes an internal counter in the Buttress
++with a copy of SoC time, this counter keeps the updated time until camera
++operation is stopped. Driver can use this time counter to calibrate the
++timestamp based on the timestamp in response event from firmware.
++
++.. c:function:: int ipu6_buttress_start_tsc_sync(...)
++
++
++DMA and MMU
++============
++
++IPU6 has its own scalar processor where the firmware run at, it has
++an internal 32-bits virtual address space. IPU6 has MMU address translation
++hardware to allow that scalar process access the internal memory and external
++system memory through IPU6 virtual address. The address translation is
++based on two levels of page lookup tables stored in system memory which are
++maintained by IPU6 driver. IPU6 driver sets the level-1 page table base address
++to MMU register and allow MMU to lookup the page table.
++
++IPU6 driver exports its own DMA operations. Driver will update the page table
++entries for each DMA operation and invalidate the MMU TLB after each unmap and
++free.
++
++.. code-block:: none
++
++    const struct dma_map_ops ipu6_dma_ops = {
++	   .alloc = ipu6_dma_alloc,
++	   .free = ipu6_dma_free,
++	   .mmap = ipu6_dma_mmap,
++	   .map_sg = ipu6_dma_map_sg,
++	   .unmap_sg = ipu6_dma_unmap_sg,
++	   ...
++    };
++
++.. Note:: IPU6 MMU works behind IOMMU, so for each IPU6 DMA ops, driver will
++	  call generic PCI DMA ops to ask IOMMU to do the additional mapping
++	  if VT-d enabled.
++
++
++Firmware file format
++=====================
++
++IPU6 release the firmware in Code Partition Directory (CPD) file format. The
++CPD firmware contains a CPD header, several CPD entries and CPD components.
++CPD component includes 3 entries - manifest, metadata and module data. Manifest
++and metadata are defined by CSE and used by CSE for authentication. Module data
++is defined by IPU6 which holds the binary data of firmware called package
++directory. IPU6 driver (``ipu6-cpd.c``) parses and validates the CPD firmware
++file and get the package directory binary data of IPU6 firmware, copy it to
++specific DMA buffer and sets its base address to Buttress ``FW_SOURCE_BASE``
++register, CSE will do authentication for this firmware binary.
++
++
++Syscom interface
++================
++
++IPU6 driver communicates with firmware via syscom ABI. Syscom is an
++inter-processor communication mechanism between IPU scalar processor and CPU.
++There are a number of resources shared between firmware and software.
++A system memory region where the message queues reside, firmware can access the
++memory region via IPU MMU. Syscom queues are FIFO fixed depth queues with
++configurable elements ``token`` (message). There is also a common IPU MMIO
++registers where the queue read and write indices reside. Software and firmware
++work as producer and consumer of tokens in queue, and update the write and read
++indices separately when sending or receiving each message.
++
++IPU6 driver must prepare and configure the number of input and output queues,
++configure the count of tokens per queue and the size of per token before
++initiate and start the communication with firmware, firmware and software must
++use same configurations. IPU6 Buttress has a number of firmware boot parameter
++registers which can be used to store the address of configuration and initiate
++the Syscom state, then driver can request firmware to start and run via setting
++the scalar processor control status register.
++
++
++Input System
++==============
++
++IPU6 input system consists of MIPI D-PHY and several CSI receiver controllers,
++it can capture image pixel data from camera sensors or other MIPI CSI output
++devices.
++
++D-PHYs and CSI-2 ports lane mapping
++-----------------------------------
++
++IPU6 integrates different D-PHY IPs on different SoCs, on Tiger Lake and Alder
++Lake, IPU6 integrates MCD10 D-PHY, IPU6SE on Jasper Lake integrates JSL D-PHY
++and IPU6EP on Meteor Lake integrates a Synopsys DWC D-PHY. There is an adaption
++layer between D-PHY and CSI receiver controller which includes port
++configuration, PHY wrapper or private test interfaces for D-PHY. There are 3
++D-PHY drivers ``ipu6-isys-mcd-phy.c``, ``ipu6-isys-jsl-phy.c`` and
++``ipu6-isys-dwc-phy.c`` program the above 3 D-PHYs in IPU6.
++
++Different IPU6 version has different D-PHY lanes mappings, On Tiger Lake, there
++are 12 data lanes and 8 clock lanes, IPU6 support maximum 8 CSI-2 ports, see
++the ppi mmapping in ``ipu6-isys-mcd-phy.c`` for more information. On Jasper Lake
++and Alder Lake, D-PHY has 8 data lanes and 4 clock lanes, IPU6 support maximum 4
++CSI-2 ports. For Meteor Lake, D-PHY has 12 data lanes and 6 clock lanes, IPU6
++support maximum 6 CSI-2 ports.
++
++.. Note:: Each adjacent CSI ports work as a pair and share the data lanes.
++	  For example, for CSI port 0 and 1, CSI port 0 support maximum 4
++	  data lanes, CSI port 1 support maximum 2 data lanes, CSI port 0
++	  with 2 data lanes can work together with CSI port 1 with 2 data lanes.
++	  If trying to use CSI port 0 with 4 lanes, CSI port 1 will not be
++	  available as the 4 data lanes are shared by CSI port 0 and 1. Same
++	  scenario is also applied for CSI port 2/3, 4/5 and 7/8.
++
++IS firmware ABIs
++----------------
++
++IPU6 firmware define a series of ABIs to software. In general, software firstly
++prepare the stream configuration ``struct ipu6_fw_isys_stream_cfg_data_abi``
++and send the configuration to firmware via sending ``STREAM_OPEN`` command.
++Stream configuration includes input pins and output pins, input pin
++``struct ipu6_fw_isys_input_pin_info_abi`` defines the resolution and data type
++of input source, output pin ``struct ipu6_fw_isys_output_pin_info_abi``
++defines the output resolution, stride and frame format, etc. Once driver get the
++interrupt from firmware that indicates stream open successfully, driver will
++send the ``STREAM_START`` and ``STREAM_CAPTURE`` command to request firmware to
++start capturing image frames. ``STREAM_CAPTURE`` command queues the buffers to
++firmware with ``struct ipu6_fw_isys_frame_buff_set``, software then wait the
++interrupt and response from firmware, ``PIN_DATA_READY`` means data ready
++on specific output pin and then software return the buffers to user.
++
++.. Note:: See :ref:`Examples<ipu6_isys_capture_examples>` about how to do
++	  capture by IPU6 IS driver.
++
++
+-- 
+2.43.2
+
+
+From d883f3386e7185d9404cb25e32df986656a4e82a Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:30 +0800
+Subject: [PATCH 23/33] media: ipu6/isys: support line-based metadata capture
+ support
+
+Some camera sensor can output the embedded data in specific
+data type.  This patch add the support for embedded data capture
+in IPU6 IS driver.
+
+It's based on Sakari's line-based metadata capture support change:
+<URL:https://git.linuxtv.org/sailus/media_tree.git/log/?h=metadata>
+
+Signed-off-by: Hongju Wang <hongju.wang@intel.com>
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c |   5 +
+ .../media/pci/intel/ipu6/ipu6-isys-queue.c    |  44 ++--
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.c   |   5 +
+ .../media/pci/intel/ipu6/ipu6-isys-video.c    | 201 +++++++++++++++---
+ .../media/pci/intel/ipu6/ipu6-isys-video.h    |   7 +-
+ 5 files changed, 216 insertions(+), 46 deletions(-)
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+index ac9fa3e0d7ab..a6430d531129 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+@@ -42,6 +42,11 @@ static const u32 csi2_supported_codes[] = {
+ 	MEDIA_BUS_FMT_SGBRG8_1X8,
+ 	MEDIA_BUS_FMT_SGRBG8_1X8,
+ 	MEDIA_BUS_FMT_SRGGB8_1X8,
++	MEDIA_BUS_FMT_META_8,
++	MEDIA_BUS_FMT_META_10,
++	MEDIA_BUS_FMT_META_12,
++	MEDIA_BUS_FMT_META_16,
++	MEDIA_BUS_FMT_META_24,
+ 	0
+ };
+ 
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+index 735d2d642d87..15fa7ed22b2f 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+@@ -35,11 +35,14 @@ static int queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ 	/* num_planes == 0: we're being called through VIDIOC_REQBUFS */
+ 	if (!*num_planes) {
+ 		use_fmt = true;
+-		*num_planes = av->mpix.num_planes;
++		if (av->vfmt.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++			*num_planes = av->vfmt.fmt.pix_mp.num_planes;
++		else if (av->vfmt.type == V4L2_BUF_TYPE_META_CAPTURE)
++			*num_planes = 1;
+ 	}
+ 
+ 	for (i = 0; i < *num_planes; i++) {
+-		size = av->mpix.plane_fmt[i].sizeimage;
++		size = ipu6_get_data_size(&av->vfmt, i);
+ 		if (use_fmt) {
+ 			sizes[i] = size;
+ 		} else if (sizes[i] < size) {
+@@ -59,16 +62,17 @@ static int ipu6_isys_buf_prepare(struct vb2_buffer *vb)
+ 	struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+ 	struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ 	struct device *dev = &av->isys->adev->auxdev.dev;
++	u32 bytesperline = ipu6_get_bytes_per_line(&av->vfmt);
++	u32 height = ipu6_get_frame_height(&av->vfmt);
++	u32 size = ipu6_get_data_size(&av->vfmt, 0);
+ 
+ 	dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n",
+-		av->vdev.name, av->mpix.plane_fmt[0].sizeimage,
+-		vb2_plane_size(vb, 0));
++		av->vdev.name, size, vb2_plane_size(vb, 0));
+ 
+-	if (av->mpix.plane_fmt[0].sizeimage > vb2_plane_size(vb, 0))
++	if (size > vb2_plane_size(vb, 0))
+ 		return -EINVAL;
+ 
+-	vb2_set_plane_payload(vb, 0, av->mpix.plane_fmt[0].bytesperline *
+-			      av->mpix.height);
++	vb2_set_plane_payload(vb, 0, bytesperline * height);
+ 	vb->planes[0].data_offset = 0;
+ 
+ 	return 0;
+@@ -437,18 +441,22 @@ static int ipu6_isys_link_fmt_validate(struct ipu6_isys_queue *aq)
+ 		return ret;
+ 	}
+ 
+-	if (format.width != av->mpix.width ||
+-	    format.height != av->mpix.height) {
+-		dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n",
+-			av->mpix.width, av->mpix.height,
+-			format.width, format.height);
++	if (format.width != ipu6_get_frame_width(&av->vfmt) ||
++	    format.height != ipu6_get_frame_height(&av->vfmt)) {
++		dev_err(dev, "wrong width or height %ux%u (%ux%u expected)\n",
++			ipu6_get_frame_width(&av->vfmt),
++			ipu6_get_frame_height(&av->vfmt), format.width,
++			format.height);
+ 		return -EINVAL;
+ 	}
+ 
+-	if (format.field != av->mpix.field) {
+-		dev_dbg(dev, "wrong field value 0x%8.8x (0x%8.8x expected)\n",
+-			av->mpix.field, format.field);
+-		return -EINVAL;
++	if (av->vfmt.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
++		if (format.field != av->vfmt.fmt.pix_mp.field) {
++			dev_dbg(dev,
++				"wrong field value 0x%8.8x (%8.8x expected)\n",
++				av->vfmt.fmt.pix_mp.field, format.field);
++			return -EINVAL;
++		}
+ 	}
+ 
+ 	if (format.code != av->pfmt->code) {
+@@ -531,8 +539,8 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
+ 	int nr_queues, ret;
+ 
+ 	dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
+-		av->vdev.name, av->mpix.width, av->mpix.height,
+-		av->pfmt->css_pixelformat);
++		av->vdev.name, ipu6_get_frame_width(&av->vfmt),
++		ipu6_get_frame_height(&av->vfmt), av->pfmt->css_pixelformat);
+ 
+ 	ret = ipu6_isys_setup_video(av, &source_entity, &nr_queues);
+ 	if (ret < 0) {
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+index 510c5ca34f9f..3c9263ac02a3 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+@@ -20,25 +20,30 @@ unsigned int ipu6_isys_mbus_code_to_bpp(u32 code)
+ {
+ 	switch (code) {
+ 	case MEDIA_BUS_FMT_RGB888_1X24:
++	case MEDIA_BUS_FMT_META_24:
+ 		return 24;
+ 	case MEDIA_BUS_FMT_RGB565_1X16:
+ 	case MEDIA_BUS_FMT_UYVY8_1X16:
+ 	case MEDIA_BUS_FMT_YUYV8_1X16:
++	case MEDIA_BUS_FMT_META_16:
+ 		return 16;
+ 	case MEDIA_BUS_FMT_SBGGR12_1X12:
+ 	case MEDIA_BUS_FMT_SGBRG12_1X12:
+ 	case MEDIA_BUS_FMT_SGRBG12_1X12:
+ 	case MEDIA_BUS_FMT_SRGGB12_1X12:
++	case MEDIA_BUS_FMT_META_12:
+ 		return 12;
+ 	case MEDIA_BUS_FMT_SBGGR10_1X10:
+ 	case MEDIA_BUS_FMT_SGBRG10_1X10:
+ 	case MEDIA_BUS_FMT_SGRBG10_1X10:
+ 	case MEDIA_BUS_FMT_SRGGB10_1X10:
++	case MEDIA_BUS_FMT_META_10:
+ 		return 10;
+ 	case MEDIA_BUS_FMT_SBGGR8_1X8:
+ 	case MEDIA_BUS_FMT_SGBRG8_1X8:
+ 	case MEDIA_BUS_FMT_SGRBG8_1X8:
+ 	case MEDIA_BUS_FMT_SRGGB8_1X8:
++	case MEDIA_BUS_FMT_META_8:
+ 		return 8;
+ 	default:
+ 		WARN_ON(1);
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+index 847eac26bcd6..1a023bf1e1a6 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+@@ -85,6 +85,11 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RGB565},
+ 	{V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RGBA888},
++	{V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8, 0},
++	{V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10, 0},
++	{V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12, 0},
++	{V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16, 0},
++	{V4L2_META_FMT_GENERIC_CSI2_24, 24, 24, MEDIA_BUS_FMT_META_24, 0},
+ };
+ 
+ static int video_open(struct file *file)
+@@ -181,12 +186,12 @@ static int ipu6_isys_vidioc_enum_framesizes(struct file *file, void *fh,
+ 	return 0;
+ }
+ 
+-static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+-				       struct v4l2_format *fmt)
++static int vidioc_get_format(struct file *file, void *fh,
++			     struct v4l2_format *fmt)
+ {
+ 	struct ipu6_isys_video *av = video_drvdata(file);
+ 
+-	fmt->fmt.pix_mp = av->mpix;
++	*fmt = av->vfmt;
+ 
+ 	return 0;
+ }
+@@ -245,30 +250,114 @@ ipu6_isys_video_try_fmt_vid_mplane(struct ipu6_isys_video *av,
+ 	return pfmt;
+ }
+ 
+-static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *fh,
+-				       struct v4l2_format *f)
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_video_try_fmt_meta(struct ipu6_isys_video *av,
++			     struct v4l2_meta_format *meta)
++{
++	const struct ipu6_isys_pixelformat *pfmt =
++		ipu6_isys_get_pixelformat(meta->dataformat);
++
++	memset(&av->vfmt, 0, sizeof(av->vfmt));
++	av->vfmt.type = V4L2_BUF_TYPE_META_CAPTURE;
++	av->pfmt = pfmt;
++
++	meta->dataformat = pfmt->pixelformat;
++	meta->width = clamp(meta->width, IPU6_ISYS_MIN_WIDTH,
++			    IPU6_ISYS_MAX_WIDTH);
++	meta->height = clamp(meta->height, IPU6_ISYS_MIN_HEIGHT,
++			     IPU6_ISYS_MAX_HEIGHT);
++
++	if (pfmt->bpp != pfmt->bpp_packed)
++		meta->bytesperline = meta->width *
++				     DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);
++	else
++		meta->bytesperline =
++			DIV_ROUND_UP(meta->width * pfmt->bpp, BITS_PER_BYTE);
++
++	meta->bytesperline = ALIGN(meta->bytesperline, av->isys->line_align);
++	meta->buffersize =
++		max(max(meta->buffersize, meta->bytesperline * meta->height +
++			max(meta->bytesperline,
++			    av->isys->pdata->ipdata->isys_dma_overshoot)), 1U);
++
++	return pfmt;
++}
++
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_video_try_fmt(struct ipu6_isys_video *av, struct v4l2_format *f)
++{
++	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
++	else if (f->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return ipu6_isys_video_try_fmt_meta(av, &f->fmt.meta);
++	else
++		return &ipu6_isys_pfmts[0];
++}
++
++static int vidioc_set_format(struct file *file, void *fh,
++			     struct v4l2_format *f)
+ {
+ 	struct ipu6_isys_video *av = video_drvdata(file);
+ 
+ 	if (av->aq.vbq.streaming)
+ 		return -EBUSY;
+ 
+-	av->pfmt = ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
+-	av->mpix = f->fmt.pix_mp;
++	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
++	    f->type != V4L2_BUF_TYPE_META_CAPTURE)
++		return -EINVAL;
++
++	av->pfmt = ipu6_isys_video_try_fmt(av, f);
++	av->vfmt = *f;
+ 
+ 	return 0;
+ }
+ 
+-static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *fh,
+-					 struct v4l2_format *f)
++static int vidioc_try_format(struct file *file, void *fh,
++			     struct v4l2_format *f)
+ {
+ 	struct ipu6_isys_video *av = video_drvdata(file);
+ 
+-	ipu6_isys_video_try_fmt_vid_mplane(av, &f->fmt.pix_mp);
++	if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
++	    f->type != V4L2_BUF_TYPE_META_CAPTURE)
++		return -EINVAL;
++
++	ipu6_isys_video_try_fmt(av, f);
+ 
+ 	return 0;
+ }
+ 
++static int vidioc_request_qbufs(struct file *file, void *priv,
++				struct v4l2_requestbuffers *p)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++	int ret;
++
++	av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type);
++	av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type);
++
++	ret = vb2_queue_change_type(&av->aq.vbq, p->type);
++	if (ret)
++		return ret;
++
++	return vb2_ioctl_reqbufs(file, priv, p);
++}
++
++static int vidioc_create_bufs(struct file *file, void *priv,
++			      struct v4l2_create_buffers *p)
++{
++	struct ipu6_isys_video *av = video_drvdata(file);
++	int ret;
++
++	av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type);
++	av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type);
++
++	ret = vb2_queue_change_type(&av->aq.vbq, p->format.type);
++	if (ret)
++		return ret;
++
++	return vb2_ioctl_create_bufs(file, priv, p);
++}
++
+ static int link_validate(struct media_link *link)
+ {
+ 	struct ipu6_isys_video *av =
+@@ -279,6 +368,8 @@ static int link_validate(struct media_link *link)
+ 	struct v4l2_mbus_framefmt *s_fmt;
+ 	struct media_pad *s_pad;
+ 	u32 s_stream;
++	u32 height;
++	u32 width;
+ 	int ret = -EPIPE;
+ 
+ 	if (!link->source->entity)
+@@ -305,11 +396,13 @@ static int link_validate(struct media_link *link)
+ 		goto unlock;
+ 	}
+ 
+-	if (s_fmt->width != av->mpix.width ||
+-	    s_fmt->height != av->mpix.height || s_fmt->code != av->pfmt->code) {
++	height = ipu6_get_frame_height(&av->vfmt);
++	width = ipu6_get_frame_width(&av->vfmt);
++	if (s_fmt->width != width || s_fmt->height != height ||
++	    s_fmt->code != av->pfmt->code) {
+ 		dev_err(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",
+-			s_fmt->width, s_fmt->height, s_fmt->code,
+-			av->mpix.width, av->mpix.height, av->pfmt->code);
++			s_fmt->width, s_fmt->height, s_fmt->code, width, height,
++			av->pfmt->code);
+ 		goto unlock;
+ 	}
+ 
+@@ -393,10 +486,10 @@ static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
+ 
+ 	output_pin = &cfg->output_pins[output_pins];
+ 	output_pin->input_pin_id = input_pins;
+-	output_pin->output_res.width = av->mpix.width;
+-	output_pin->output_res.height = av->mpix.height;
++	output_pin->output_res.width = ipu6_get_frame_width(&av->vfmt);
++	output_pin->output_res.height = ipu6_get_frame_height(&av->vfmt);
+ 
+-	output_pin->stride = av->mpix.plane_fmt[0].bytesperline;
++	output_pin->stride = ipu6_get_bytes_per_line(&av->vfmt);
+ 	if (av->pfmt->bpp != av->pfmt->bpp_packed)
+ 		output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC;
+ 	else
+@@ -663,8 +756,8 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
+ 
+ 	esd = media_entity_to_v4l2_subdev(av->stream->source_entity);
+ 
+-	av->watermark.width = av->mpix.width;
+-	av->watermark.height = av->mpix.height;
++	av->watermark.width = ipu6_get_frame_width(&av->vfmt);
++	av->watermark.height = ipu6_get_frame_height(&av->vfmt);
+ 	av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift;
+ 	av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size;
+ 
+@@ -992,11 +1085,15 @@ static const struct v4l2_ioctl_ops ioctl_ops_mplane = {
+ 	.vidioc_querycap = ipu6_isys_vidioc_querycap,
+ 	.vidioc_enum_fmt_vid_cap = ipu6_isys_vidioc_enum_fmt,
+ 	.vidioc_enum_framesizes = ipu6_isys_vidioc_enum_framesizes,
+-	.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane,
+-	.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane,
+-	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane,
+-	.vidioc_reqbufs = vb2_ioctl_reqbufs,
+-	.vidioc_create_bufs = vb2_ioctl_create_bufs,
++	.vidioc_g_fmt_vid_cap_mplane = vidioc_get_format,
++	.vidioc_s_fmt_vid_cap_mplane = vidioc_set_format,
++	.vidioc_try_fmt_vid_cap_mplane = vidioc_try_format,
++	.vidioc_enum_fmt_meta_cap = ipu6_isys_vidioc_enum_fmt,
++	.vidioc_g_fmt_meta_cap = vidioc_get_format,
++	.vidioc_s_fmt_meta_cap = vidioc_set_format,
++	.vidioc_try_fmt_meta_cap = vidioc_try_format,
++	.vidioc_reqbufs = vidioc_request_qbufs,
++	.vidioc_create_bufs = vidioc_create_bufs,
+ 	.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ 	.vidioc_querybuf = vb2_ioctl_querybuf,
+ 	.vidioc_qbuf = vb2_ioctl_qbuf,
+@@ -1199,7 +1296,8 @@ int ipu6_isys_video_init(struct ipu6_isys_video *av)
+ 
+ 	mutex_init(&av->mutex);
+ 	av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |
+-			       V4L2_CAP_VIDEO_CAPTURE_MPLANE;
++			       V4L2_CAP_VIDEO_CAPTURE_MPLANE |
++			       V4L2_CAP_META_CAPTURE;
+ 	av->vdev.vfl_dir = VFL_DIR_RX;
+ 
+ 	ret = ipu6_isys_queue_init(&av->aq);
+@@ -1220,8 +1318,8 @@ int ipu6_isys_video_init(struct ipu6_isys_video *av)
+ 	av->vdev.queue = &av->aq.vbq;
+ 	av->vdev.lock = &av->mutex;
+ 
+-	ipu6_isys_video_try_fmt_vid_mplane(av, &format.fmt.pix_mp);
+-	av->mpix = format.fmt.pix_mp;
++	ipu6_isys_video_try_fmt(av, &format);
++	av->vfmt = format;
+ 
+ 	set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags);
+ 	video_set_drvdata(&av->vdev, av);
+@@ -1251,3 +1349,52 @@ void ipu6_isys_video_cleanup(struct ipu6_isys_video *av)
+ 	media_entity_cleanup(&av->vdev.entity);
+ 	mutex_destroy(&av->mutex);
+ }
++
++u32 ipu6_get_data_size(struct v4l2_format *vfmt, int plane)
++{
++	if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return vfmt->fmt.pix_mp.plane_fmt[plane].sizeimage;
++	else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return vfmt->fmt.meta.buffersize;
++
++	WARN_ON_ONCE(1);
++
++	return 0;
++}
++
++u32 ipu6_get_bytes_per_line(struct v4l2_format *vfmt)
++{
++	if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return vfmt->fmt.pix_mp.plane_fmt[0].bytesperline;
++	else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return vfmt->fmt.meta.bytesperline;
++
++	WARN_ON_ONCE(1);
++
++	return 0;
++}
++
++u32 ipu6_get_frame_width(struct v4l2_format *vfmt)
++{
++	if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return vfmt->fmt.pix_mp.width;
++	else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return vfmt->fmt.meta.width;
++
++	WARN_ON_ONCE(1);
++
++	return 0;
++}
++
++u32 ipu6_get_frame_height(struct v4l2_format *vfmt)
++{
++	if (vfmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
++		return vfmt->fmt.pix_mp.height;
++	else if (vfmt->type == V4L2_BUF_TYPE_META_CAPTURE)
++		return vfmt->fmt.meta.height;
++
++	WARN_ON_ONCE(1);
++
++	return 0;
++}
++
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+index 21cd33c7e277..2634ec0fd68b 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+@@ -90,7 +90,7 @@ struct ipu6_isys_video {
+ 	struct mutex mutex;
+ 	struct media_pad pad;
+ 	struct video_device vdev;
+-	struct v4l2_pix_format_mplane mpix;
++	struct v4l2_format vfmt;
+ 	const struct ipu6_isys_pixelformat *pfmt;
+ 	struct ipu6_isys *isys;
+ 	struct ipu6_isys_stream *stream;
+@@ -133,4 +133,9 @@ void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
+ 					  bool state);
+ void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state);
+ 
++u32 ipu6_get_data_size(struct v4l2_format *vfmt, int plane);
++u32 ipu6_get_bytes_per_line(struct v4l2_format *vfmt);
++u32 ipu6_get_frame_width(struct v4l2_format *vfmt);
++u32 ipu6_get_frame_height(struct v4l2_format *vfmt);
++
+ #endif /* IPU6_ISYS_VIDEO_H */
+-- 
+2.43.2
+
+
+From 9a6fb311b81433ebbd8e0769bed19958a6a5a5f6 Mon Sep 17 00:00:00 2001
+From: Bingbu Cao <bingbu.cao@intel.com>
+Date: Thu, 11 Jan 2024 14:55:31 +0800
+Subject: [PATCH 24/33] media: ipu6/isys: support new v4l2 subdev state APIs
+
+Add support for the upcoming v4l2-subdev API changes in kernel 6.8.
+This patch is based on Sakari's branch:
+<URL:https://git.linuxtv.org/sailus/media_tree.git/log/?h=metadata>
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Bingbu Cao <bingbu.cao@intel.com>
+---
+ drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c |  8 +++-----
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.c   | 19 +++++++++++--------
+ .../media/pci/intel/ipu6/ipu6-isys-subdev.h   |  2 --
+ .../media/pci/intel/ipu6/ipu6-isys-video.c    |  3 +--
+ 4 files changed, 15 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+index a6430d531129..6f258cf92fc1 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+@@ -403,12 +403,11 @@ static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,
+ 	if (!sink_ffmt)
+ 		return -EINVAL;
+ 
+-	src_ffmt = v4l2_subdev_state_get_stream_format(state, sel->pad,
+-						       sel->stream);
++	src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
+ 	if (!src_ffmt)
+ 		return -EINVAL;
+ 
+-	crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream);
++	crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ 	if (!crop)
+ 		return -EINVAL;
+ 
+@@ -453,7 +452,7 @@ static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd,
+ 	if (!sink_ffmt)
+ 		return -EINVAL;
+ 
+-	crop = v4l2_subdev_state_get_stream_crop(state, sel->pad, sel->stream);
++	crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ 	if (!crop)
+ 		return -EINVAL;
+ 
+@@ -480,7 +479,6 @@ static const struct v4l2_subdev_video_ops csi2_sd_video_ops = {
+ };
+ 
+ static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
+-	.init_cfg = ipu6_isys_subdev_init_cfg,
+ 	.get_fmt = v4l2_subdev_get_fmt,
+ 	.set_fmt = ipu6_isys_subdev_set_fmt,
+ 	.get_selection = ipu6_isys_csi2_get_sel,
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+index 3c9263ac02a3..aeccd6f93986 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+@@ -156,8 +156,7 @@ int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ 	format->format.field = V4L2_FIELD_NONE;
+ 
+ 	/* Store the format and propagate it to the source pad. */
+-	fmt = v4l2_subdev_state_get_stream_format(state, format->pad,
+-						  format->stream);
++	fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ 	if (!fmt)
+ 		return -EINVAL;
+ 
+@@ -182,8 +181,7 @@ int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ 	if (ret)
+ 		return -EINVAL;
+ 
+-	crop = v4l2_subdev_state_get_stream_crop(state, other_pad,
+-						 other_stream);
++	crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
+ 	/* reset crop */
+ 	crop->left = 0;
+ 	crop->top = 0;
+@@ -241,7 +239,7 @@ int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ 		return -EINVAL;
+ 
+ 	state = v4l2_subdev_lock_and_get_active_state(sd);
+-	fmt = v4l2_subdev_state_get_stream_format(state, pad, stream);
++	fmt = v4l2_subdev_state_get_format(state, pad, stream);
+ 	if (fmt)
+ 		*format = *fmt;
+ 	v4l2_subdev_unlock_state(state);
+@@ -259,7 +257,7 @@ int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ 		return -EINVAL;
+ 
+ 	state = v4l2_subdev_lock_and_get_active_state(sd);
+-	rect = v4l2_subdev_state_get_stream_crop(state, pad, stream);
++	rect = v4l2_subdev_state_get_crop(state, pad, stream);
+ 	if (rect)
+ 		*crop = *rect;
+ 	v4l2_subdev_unlock_state(state);
+@@ -291,8 +289,8 @@ u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
+ 	return source_stream;
+ }
+ 
+-int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd,
+-			      struct v4l2_subdev_state *state)
++static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd,
++				       struct v4l2_subdev_state *state)
+ {
+ 	struct v4l2_subdev_route route = {
+ 		.sink_pad = 0,
+@@ -317,6 +315,10 @@ int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
+ 	return subdev_set_routing(sd, state, routing);
+ }
+ 
++static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = {
++	.init_state = ipu6_isys_subdev_init_state,
++};
++
+ int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
+ 			  const struct v4l2_subdev_ops *ops,
+ 			  unsigned int nr_ctrls,
+@@ -334,6 +336,7 @@ int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,
+ 			 V4L2_SUBDEV_FL_STREAMS;
+ 	asd->sd.owner = THIS_MODULE;
+ 	asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
++	asd->sd.internal_ops = &ipu6_isys_subdev_internal_ops;
+ 
+ 	asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
+ 				sizeof(*asd->pad), GFP_KERNEL);
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+index adea2a55761d..f4e32b094b5b 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+@@ -46,8 +46,6 @@ int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ 				 struct v4l2_mbus_framefmt *format);
+ int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ 				  struct v4l2_rect *crop);
+-int ipu6_isys_subdev_init_cfg(struct v4l2_subdev *sd,
+-			      struct v4l2_subdev_state *state);
+ int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
+ 				 struct v4l2_subdev_state *state,
+ 				 enum v4l2_subdev_format_whence which,
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+index 1a023bf1e1a6..62d4043fc2a1 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+@@ -389,8 +389,7 @@ static int link_validate(struct media_link *link)
+ 
+ 	v4l2_subdev_lock_state(s_state);
+ 
+-	s_fmt = v4l2_subdev_state_get_stream_format(s_state, s_pad->index,
+-						    s_stream);
++	s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream);
+ 	if (!s_fmt) {
+ 		dev_err(dev, "failed to get source pad format\n");
+ 		goto unlock;
+-- 
+2.43.2
+
+
+From 53ca77877d2cc7ecc39bb0ef26a1871a1c26afd1 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 15 Jan 2024 15:57:06 +0100
+Subject: [PATCH 25/33] media: intel/ipu6: Disable packed bayer v4l2-buffer
+ formats on TGL
+
+Using CSI2 packing to store 10bpp bayer data in the v4l2-buffers does not
+work on Tiger Lake when testing with an ov01a1s sensor.
+
+Disable packed bayer formats on Tiger Lake for now.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ .../media/pci/intel/ipu6/ipu6-isys-video.c    | 65 ++++++++++++-------
+ 1 file changed, 43 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+index 62d4043fc2a1..c971ffe0b948 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
++++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+@@ -61,6 +61,17 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
+ 	{V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW8},
++	{V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
++	{V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_YUYV},
++	{V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RGB565},
++	{V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
++	 IPU6_FW_ISYS_FRAME_FORMAT_RGBA888},
++};
++
++const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[] = {
+ 	{V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW12},
+ 	{V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
+@@ -77,19 +88,6 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
+ 	{V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ 	 IPU6_FW_ISYS_FRAME_FORMAT_RAW10},
+-	{V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
+-	 IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
+-	{V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
+-	 IPU6_FW_ISYS_FRAME_FORMAT_YUYV},
+-	{V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
+-	 IPU6_FW_ISYS_FRAME_FORMAT_RGB565},
+-	{V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
+-	 IPU6_FW_ISYS_FRAME_FORMAT_RGBA888},
+-	{V4L2_META_FMT_GENERIC_8, 8, 8, MEDIA_BUS_FMT_META_8, 0},
+-	{V4L2_META_FMT_GENERIC_CSI2_10, 10, 10, MEDIA_BUS_FMT_META_10, 0},
+-	{V4L2_META_FMT_GENERIC_CSI2_12, 12, 12, MEDIA_BUS_FMT_META_12, 0},
+-	{V4L2_META_FMT_GENERIC_CSI2_16, 16, 16, MEDIA_BUS_FMT_META_16, 0},
+-	{V4L2_META_FMT_GENERIC_CSI2_24, 24, 24, MEDIA_BUS_FMT_META_24, 0},
+ };
+ 
+ static int video_open(struct file *file)
+@@ -114,14 +112,27 @@ static int video_release(struct file *file)
+ 	return vb2_fop_release(file);
+ }
+ 
++static const struct ipu6_isys_pixelformat *
++ipu6_isys_get_pixelformat_by_idx(unsigned int idx)
++{
++	if (idx < ARRAY_SIZE(ipu6_isys_pfmts))
++		return &ipu6_isys_pfmts[idx];
++
++	idx -= ARRAY_SIZE(ipu6_isys_pfmts);
++
++	if (idx < ARRAY_SIZE(ipu6_isys_pfmts_packed))
++		return &ipu6_isys_pfmts_packed[idx];
++
++	return NULL;
++}
++
+ static const struct ipu6_isys_pixelformat *
+ ipu6_isys_get_pixelformat(u32 pixelformat)
+ {
++	const struct ipu6_isys_pixelformat *pfmt;
+ 	unsigned int i;
+ 
+-	for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
+-		const struct ipu6_isys_pixelformat *pfmt = &ipu6_isys_pfmts[i];
+-
++	for (i = 0; (pfmt = ipu6_isys_get_pixelformat_by_idx(i)); i++) {
+ 		if (pfmt->pixelformat == pixelformat)
+ 			return pfmt;
+ 	}
+@@ -143,24 +154,34 @@ int ipu6_isys_vidioc_querycap(struct file *file, void *fh,
+ int ipu6_isys_vidioc_enum_fmt(struct file *file, void *fh,
+ 			      struct v4l2_fmtdesc *f)
+ {
+-	unsigned int i, found = 0;
++	struct ipu6_isys_video *av = video_drvdata(file);
++	const struct ipu6_isys_pixelformat *fmt;
++	unsigned int i, nfmts, found = 0;
++
++	nfmts = ARRAY_SIZE(ipu6_isys_pfmts);
++	/* Disable packed formats on TGL for now, TGL has 8 CSI ports */
++	if (av->isys->pdata->ipdata->csi2.nports != 8)
++		nfmts += ARRAY_SIZE(ipu6_isys_pfmts_packed);
+ 
+-	if (f->index >= ARRAY_SIZE(ipu6_isys_pfmts))
++	if (f->index >= nfmts)
+ 		return -EINVAL;
+ 
+ 	if (!f->mbus_code) {
++		fmt = ipu6_isys_get_pixelformat_by_idx(f->index);
+ 		f->flags = 0;
+-		f->pixelformat = ipu6_isys_pfmts[f->index].pixelformat;
++		f->pixelformat = fmt->pixelformat;
+ 		return 0;
+ 	}
+ 
+-	for (i = 0; i < ARRAY_SIZE(ipu6_isys_pfmts); i++) {
+-		if (f->mbus_code != ipu6_isys_pfmts[i].code)
++	for (i = 0; i < nfmts; i++) {
++		fmt = ipu6_isys_get_pixelformat_by_idx(i);
++
++		if (f->mbus_code != fmt->code)
+ 			continue;
+ 
+ 		if (f->index == found) {
+ 			f->flags = 0;
+-			f->pixelformat = ipu6_isys_pfmts[i].pixelformat;
++			f->pixelformat = fmt->pixelformat;
+ 			return 0;
+ 		}
+ 		found++;
+-- 
+2.43.2
+
+
+From ed407043f03e9af2b09ab8ad449c2716ce7fde01 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 6 Nov 2023 12:13:42 +0100
+Subject: [PATCH 26/33] media: Add ov01a1s driver
+
+Add ov01a1s driver from:
+https://github.com/intel/ipu6-drivers/
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/Kconfig   |    9 +
+ drivers/media/i2c/Makefile  |    1 +
+ drivers/media/i2c/ov01a1s.c | 1191 +++++++++++++++++++++++++++++++++++
+ 3 files changed, 1201 insertions(+)
+ create mode 100644 drivers/media/i2c/ov01a1s.c
+
+diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
+index 4c3435921f19..08f934740980 100644
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -313,6 +313,15 @@ config VIDEO_OV01A10
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called ov01a10.
+ 
++config VIDEO_OV01A1S
++	tristate "OmniVision OV01A1S sensor support"
++	help
++	  This is a Video4Linux2 sensor driver for the OmniVision
++	  OV01A1S camera.
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called ov01a1s.
++
+ config VIDEO_OV02A10
+ 	tristate "OmniVision OV02A10 sensor support"
+ 	help
+diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
+index dfbe6448b549..53ea54c2cf01 100644
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -76,6 +76,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
+ obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
+ obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o
+ obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o
++obj-$(CONFIG_VIDEO_OV01A1S) += ov01a1s.o
+ obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o
+ obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o
+ obj-$(CONFIG_VIDEO_OV08X40) += ov08x40.o
+diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c
+new file mode 100644
+index 000000000000..0dcce8b492b4
+--- /dev/null
++++ b/drivers/media/i2c/ov01a1s.c
+@@ -0,0 +1,1191 @@
++// SPDX-License-Identifier: GPL-2.0
++// Copyright (c) 2020-2022 Intel Corporation.
++
++#include <asm/unaligned.h>
++#include <linux/acpi.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/version.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-fwnode.h>
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++#include <linux/clk.h>
++#include <linux/gpio/consumer.h>
++#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++#include "power_ctrl_logic.h"
++#endif
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++#include <linux/vsc.h>
++#endif
++
++#define OV01A1S_LINK_FREQ_400MHZ	400000000ULL
++#define OV01A1S_SCLK			40000000LL
++#define OV01A1S_MCLK			19200000
++#define OV01A1S_DATA_LANES		1
++#define OV01A1S_RGB_DEPTH		10
++
++#define OV01A1S_REG_CHIP_ID		0x300a
++#define OV01A1S_CHIP_ID			0x560141
++
++#define OV01A1S_REG_MODE_SELECT		0x0100
++#define OV01A1S_MODE_STANDBY		0x00
++#define OV01A1S_MODE_STREAMING		0x01
++
++/* vertical-timings from sensor */
++#define OV01A1S_REG_VTS			0x380e
++#define OV01A1S_VTS_DEF			0x0380
++#define OV01A1S_VTS_MIN			0x0380
++#define OV01A1S_VTS_MAX			0xffff
++
++/* Exposure controls from sensor */
++#define OV01A1S_REG_EXPOSURE		0x3501
++#define OV01A1S_EXPOSURE_MIN		4
++#define OV01A1S_EXPOSURE_MAX_MARGIN	8
++#define OV01A1S_EXPOSURE_STEP		1
++
++/* Analog gain controls from sensor */
++#define OV01A1S_REG_ANALOG_GAIN		0x3508
++#define OV01A1S_ANAL_GAIN_MIN		0x100
++#define OV01A1S_ANAL_GAIN_MAX		0xffff
++#define OV01A1S_ANAL_GAIN_STEP		1
++
++/* Digital gain controls from sensor */
++#define OV01A1S_REG_DIGILAL_GAIN_B	0x350A
++#define OV01A1S_REG_DIGITAL_GAIN_GB	0x3510
++#define OV01A1S_REG_DIGITAL_GAIN_GR	0x3513
++#define OV01A1S_REG_DIGITAL_GAIN_R	0x3516
++#define OV01A1S_DGTL_GAIN_MIN		0
++#define OV01A1S_DGTL_GAIN_MAX		0x3ffff
++#define OV01A1S_DGTL_GAIN_STEP		1
++#define OV01A1S_DGTL_GAIN_DEFAULT	1024
++
++/* Test Pattern Control */
++#define OV01A1S_REG_TEST_PATTERN		0x4503
++#define OV01A1S_TEST_PATTERN_ENABLE	BIT(7)
++#define OV01A1S_TEST_PATTERN_BAR_SHIFT	0
++
++enum {
++	OV01A1S_LINK_FREQ_400MHZ_INDEX,
++};
++
++struct ov01a1s_reg {
++	u16 address;
++	u8 val;
++};
++
++struct ov01a1s_reg_list {
++	u32 num_of_regs;
++	const struct ov01a1s_reg *regs;
++};
++
++struct ov01a1s_link_freq_config {
++	const struct ov01a1s_reg_list reg_list;
++};
++
++struct ov01a1s_mode {
++	/* Frame width in pixels */
++	u32 width;
++
++	/* Frame height in pixels */
++	u32 height;
++
++	/* Horizontal timining size */
++	u32 hts;
++
++	/* Default vertical timining size */
++	u32 vts_def;
++
++	/* Min vertical timining size */
++	u32 vts_min;
++
++	/* Link frequency needed for this resolution */
++	u32 link_freq_index;
++
++	/* Sensor register settings for this resolution */
++	const struct ov01a1s_reg_list reg_list;
++};
++
++static const struct ov01a1s_reg mipi_data_rate_720mbps[] = {
++};
++
++static const struct ov01a1s_reg sensor_1296x800_setting[] = {
++	{0x0103, 0x01},
++	{0x0302, 0x00},
++	{0x0303, 0x06},
++	{0x0304, 0x01},
++	{0x0305, 0x90},
++	{0x0306, 0x00},
++	{0x0308, 0x01},
++	{0x0309, 0x00},
++	{0x030c, 0x01},
++	{0x0322, 0x01},
++	{0x0323, 0x06},
++	{0x0324, 0x01},
++	{0x0325, 0x68},
++	{0x3002, 0xa1},
++	{0x301e, 0xf0},
++	{0x3022, 0x01},
++	{0x3501, 0x03},
++	{0x3502, 0x78},
++	{0x3504, 0x0c},
++	{0x3508, 0x01},
++	{0x3509, 0x00},
++	{0x3601, 0xc0},
++	{0x3603, 0x71},
++	{0x3610, 0x68},
++	{0x3611, 0x86},
++	{0x3640, 0x10},
++	{0x3641, 0x80},
++	{0x3642, 0xdc},
++	{0x3646, 0x55},
++	{0x3647, 0x57},
++	{0x364b, 0x00},
++	{0x3653, 0x10},
++	{0x3655, 0x00},
++	{0x3656, 0x00},
++	{0x365f, 0x0f},
++	{0x3661, 0x45},
++	{0x3662, 0x24},
++	{0x3663, 0x11},
++	{0x3664, 0x07},
++	{0x3709, 0x34},
++	{0x370b, 0x6f},
++	{0x3714, 0x22},
++	{0x371b, 0x27},
++	{0x371c, 0x67},
++	{0x371d, 0xa7},
++	{0x371e, 0xe7},
++	{0x3730, 0x81},
++	{0x3733, 0x10},
++	{0x3734, 0x40},
++	{0x3737, 0x04},
++	{0x3739, 0x1c},
++	{0x3767, 0x00},
++	{0x376c, 0x81},
++	{0x3772, 0x14},
++	{0x37c2, 0x04},
++	{0x37d8, 0x03},
++	{0x37d9, 0x0c},
++	{0x37e0, 0x00},
++	{0x37e1, 0x08},
++	{0x37e2, 0x10},
++	{0x37e3, 0x04},
++	{0x37e4, 0x04},
++	{0x37e5, 0x03},
++	{0x37e6, 0x04},
++	{0x3800, 0x00},
++	{0x3801, 0x00},
++	{0x3802, 0x00},
++	{0x3803, 0x00},
++	{0x3804, 0x05},
++	{0x3805, 0x0f},
++	{0x3806, 0x03},
++	{0x3807, 0x2f},
++	{0x3808, 0x05},
++	{0x3809, 0x00},
++	{0x380a, 0x03},
++	{0x380b, 0x1e},
++	{0x380c, 0x05},
++	{0x380d, 0xd0},
++	{0x380e, 0x03},
++	{0x380f, 0x80},
++	{0x3810, 0x00},
++	{0x3811, 0x09},
++	{0x3812, 0x00},
++	{0x3813, 0x08},
++	{0x3814, 0x01},
++	{0x3815, 0x01},
++	{0x3816, 0x01},
++	{0x3817, 0x01},
++	{0x3820, 0xa8},
++	{0x3822, 0x03},
++	{0x3832, 0x28},
++	{0x3833, 0x10},
++	{0x3b00, 0x00},
++	{0x3c80, 0x00},
++	{0x3c88, 0x02},
++	{0x3c8c, 0x07},
++	{0x3c8d, 0x40},
++	{0x3cc7, 0x80},
++	{0x4000, 0xc3},
++	{0x4001, 0xe0},
++	{0x4003, 0x40},
++	{0x4008, 0x02},
++	{0x4009, 0x19},
++	{0x400a, 0x01},
++	{0x400b, 0x6c},
++	{0x4011, 0x00},
++	{0x4041, 0x00},
++	{0x4300, 0xff},
++	{0x4301, 0x00},
++	{0x4302, 0x0f},
++	{0x4503, 0x00},
++	{0x4601, 0x50},
++	{0x481f, 0x34},
++	{0x4825, 0x33},
++	{0x4837, 0x14},
++	{0x4881, 0x40},
++	{0x4883, 0x01},
++	{0x4890, 0x00},
++	{0x4901, 0x00},
++	{0x4902, 0x00},
++	{0x4b00, 0x2a},
++	{0x4b0d, 0x00},
++	{0x450a, 0x04},
++	{0x450b, 0x00},
++	{0x5000, 0x65},
++	{0x5004, 0x00},
++	{0x5080, 0x40},
++	{0x5200, 0x18},
++	{0x4837, 0x14},
++	{0x0305, 0xf4},
++	{0x0325, 0xc2},
++	{0x3808, 0x05},
++	{0x3809, 0x10},
++	{0x380a, 0x03},
++	{0x380b, 0x1e},
++	{0x3810, 0x00},
++	{0x3811, 0x00},
++	{0x3812, 0x00},
++	{0x3813, 0x09},
++	{0x3820, 0x88},
++	{0x373d, 0x24},
++};
++
++static const char * const ov01a1s_test_pattern_menu[] = {
++	"Disabled",
++	"Color Bar",
++	"Top-Bottom Darker Color Bar",
++	"Right-Left Darker Color Bar",
++	"Color Bar type 4",
++};
++
++static const s64 link_freq_menu_items[] = {
++	OV01A1S_LINK_FREQ_400MHZ,
++};
++
++static const struct ov01a1s_link_freq_config link_freq_configs[] = {
++	[OV01A1S_LINK_FREQ_400MHZ_INDEX] = {
++		.reg_list = {
++			.num_of_regs = ARRAY_SIZE(mipi_data_rate_720mbps),
++			.regs = mipi_data_rate_720mbps,
++		}
++	},
++};
++
++static const struct ov01a1s_mode supported_modes[] = {
++	{
++		.width = 1296,
++		.height = 798,
++		.hts = 1488,
++		.vts_def = OV01A1S_VTS_DEF,
++		.vts_min = OV01A1S_VTS_MIN,
++		.reg_list = {
++			.num_of_regs = ARRAY_SIZE(sensor_1296x800_setting),
++			.regs = sensor_1296x800_setting,
++		},
++		.link_freq_index = OV01A1S_LINK_FREQ_400MHZ_INDEX,
++	},
++};
++
++struct ov01a1s {
++	struct v4l2_subdev sd;
++	struct media_pad pad;
++	struct v4l2_ctrl_handler ctrl_handler;
++
++	/* V4L2 Controls */
++	struct v4l2_ctrl *link_freq;
++	struct v4l2_ctrl *pixel_rate;
++	struct v4l2_ctrl *vblank;
++	struct v4l2_ctrl *hblank;
++	struct v4l2_ctrl *exposure;
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	struct v4l2_ctrl *privacy_status;
++
++	/* VSC settings */
++	struct vsc_mipi_config conf;
++	struct vsc_camera_status status;
++#endif
++
++	/* Current mode */
++	const struct ov01a1s_mode *cur_mode;
++
++	/* To serialize asynchronus callbacks */
++	struct mutex mutex;
++
++	/* i2c client */
++	struct i2c_client *client;
++
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	/* GPIO for reset */
++	struct gpio_desc *reset_gpio;
++	/* GPIO for powerdown */
++	struct gpio_desc *powerdown_gpio;
++	/* Power enable */
++	struct regulator *avdd;
++	/* Clock provider */
++	struct clk *clk;
++#endif
++
++	enum {
++		OV01A1S_USE_DEFAULT = 0,
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) || IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++		OV01A1S_USE_INT3472 = 1,
++#endif
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++		OV01A1S_USE_INTEL_VSC = 2,
++#endif
++	} power_type;
++
++	/* Streaming on/off */
++	bool streaming;
++};
++
++static inline struct ov01a1s *to_ov01a1s(struct v4l2_subdev *subdev)
++{
++	return container_of(subdev, struct ov01a1s, sd);
++}
++
++static int ov01a1s_read_reg(struct ov01a1s *ov01a1s, u16 reg, u16 len, u32 *val)
++{
++	struct i2c_client *client = ov01a1s->client;
++	struct i2c_msg msgs[2];
++	u8 addr_buf[2];
++	u8 data_buf[4] = {0};
++	int ret = 0;
++
++	if (len > sizeof(data_buf))
++		return -EINVAL;
++
++	put_unaligned_be16(reg, addr_buf);
++	msgs[0].addr = client->addr;
++	msgs[0].flags = 0;
++	msgs[0].len = sizeof(addr_buf);
++	msgs[0].buf = addr_buf;
++	msgs[1].addr = client->addr;
++	msgs[1].flags = I2C_M_RD;
++	msgs[1].len = len;
++	msgs[1].buf = &data_buf[sizeof(data_buf) - len];
++
++	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
++	if (ret != ARRAY_SIZE(msgs))
++		return ret < 0 ? ret : -EIO;
++
++	*val = get_unaligned_be32(data_buf);
++
++	return 0;
++}
++
++static int ov01a1s_write_reg(struct ov01a1s *ov01a1s, u16 reg, u16 len, u32 val)
++{
++	struct i2c_client *client = ov01a1s->client;
++	u8 buf[6];
++	int ret = 0;
++
++	if (len > 4)
++		return -EINVAL;
++
++	put_unaligned_be16(reg, buf);
++	put_unaligned_be32(val << 8 * (4 - len), buf + 2);
++
++	ret = i2c_master_send(client, buf, len + 2);
++	if (ret != len + 2)
++		return ret < 0 ? ret : -EIO;
++
++	return 0;
++}
++
++static int ov01a1s_write_reg_list(struct ov01a1s *ov01a1s,
++				  const struct ov01a1s_reg_list *r_list)
++{
++	struct i2c_client *client = ov01a1s->client;
++	unsigned int i;
++	int ret = 0;
++
++	for (i = 0; i < r_list->num_of_regs; i++) {
++		ret = ov01a1s_write_reg(ov01a1s, r_list->regs[i].address, 1,
++					r_list->regs[i].val);
++		if (ret) {
++			dev_err_ratelimited(&client->dev,
++					    "write reg 0x%4.4x return err = %d",
++					    r_list->regs[i].address, ret);
++			return ret;
++		}
++	}
++
++	return 0;
++}
++
++static int ov01a1s_update_digital_gain(struct ov01a1s *ov01a1s, u32 d_gain)
++{
++	struct i2c_client *client = ov01a1s->client;
++	u32 real = d_gain << 6;
++	int ret = 0;
++
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_DIGILAL_GAIN_B, 3, real);
++	if (ret) {
++		dev_err(&client->dev, "failed to set OV01A1S_REG_DIGITAL_GAIN_B");
++		return ret;
++	}
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_DIGITAL_GAIN_GB, 3, real);
++	if (ret) {
++		dev_err(&client->dev, "failed to set OV01A1S_REG_DIGITAL_GAIN_GB");
++		return ret;
++	}
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_DIGITAL_GAIN_GR, 3, real);
++	if (ret) {
++		dev_err(&client->dev, "failed to set OV01A1S_REG_DIGITAL_GAIN_GR");
++		return ret;
++	}
++
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_DIGITAL_GAIN_R, 3, real);
++	if (ret) {
++		dev_err(&client->dev, "failed to set OV01A1S_REG_DIGITAL_GAIN_R");
++		return ret;
++	}
++	return ret;
++}
++
++static int ov01a1s_test_pattern(struct ov01a1s *ov01a1s, u32 pattern)
++{
++	if (pattern)
++		pattern = (pattern - 1) << OV01A1S_TEST_PATTERN_BAR_SHIFT |
++			  OV01A1S_TEST_PATTERN_ENABLE;
++
++	return ov01a1s_write_reg(ov01a1s, OV01A1S_REG_TEST_PATTERN, 1, pattern);
++}
++
++static int ov01a1s_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++	struct ov01a1s *ov01a1s = container_of(ctrl->handler,
++					     struct ov01a1s, ctrl_handler);
++	struct i2c_client *client = ov01a1s->client;
++	s64 exposure_max;
++	int ret = 0;
++
++	/* Propagate change of current control to all related controls */
++	if (ctrl->id == V4L2_CID_VBLANK) {
++		/* Update max exposure while meeting expected vblanking */
++		exposure_max = ov01a1s->cur_mode->height + ctrl->val -
++			       OV01A1S_EXPOSURE_MAX_MARGIN;
++		__v4l2_ctrl_modify_range(ov01a1s->exposure,
++					 ov01a1s->exposure->minimum,
++					 exposure_max, ov01a1s->exposure->step,
++					 exposure_max);
++	}
++
++	/* V4L2 controls values will be applied only when power is already up */
++	if (!pm_runtime_get_if_in_use(&client->dev))
++		return 0;
++
++	switch (ctrl->id) {
++	case V4L2_CID_ANALOGUE_GAIN:
++		ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_ANALOG_GAIN, 2,
++					ctrl->val);
++		break;
++
++	case V4L2_CID_DIGITAL_GAIN:
++		ret = ov01a1s_update_digital_gain(ov01a1s, ctrl->val);
++		break;
++
++	case V4L2_CID_EXPOSURE:
++		ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_EXPOSURE, 2,
++					ctrl->val);
++		break;
++
++	case V4L2_CID_VBLANK:
++		ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_VTS, 2,
++					ov01a1s->cur_mode->height + ctrl->val);
++		break;
++
++	case V4L2_CID_TEST_PATTERN:
++		ret = ov01a1s_test_pattern(ov01a1s, ctrl->val);
++		break;
++
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	case V4L2_CID_PRIVACY:
++		dev_dbg(&client->dev, "set privacy to %d", ctrl->val);
++		break;
++
++#endif
++	default:
++		ret = -EINVAL;
++		break;
++	}
++
++	pm_runtime_put(&client->dev);
++
++	return ret;
++}
++
++static const struct v4l2_ctrl_ops ov01a1s_ctrl_ops = {
++	.s_ctrl = ov01a1s_set_ctrl,
++};
++
++static int ov01a1s_init_controls(struct ov01a1s *ov01a1s)
++{
++	struct v4l2_ctrl_handler *ctrl_hdlr;
++	const struct ov01a1s_mode *cur_mode;
++	s64 exposure_max, h_blank;
++	u32 vblank_min, vblank_max, vblank_default;
++	int size;
++	int ret = 0;
++
++	ctrl_hdlr = &ov01a1s->ctrl_handler;
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
++#else
++	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
++#endif
++	if (ret)
++		return ret;
++
++	ctrl_hdlr->lock = &ov01a1s->mutex;
++	cur_mode = ov01a1s->cur_mode;
++	size = ARRAY_SIZE(link_freq_menu_items);
++
++	ov01a1s->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
++						    &ov01a1s_ctrl_ops,
++						    V4L2_CID_LINK_FREQ,
++						    size - 1, 0,
++						    link_freq_menu_items);
++	if (ov01a1s->link_freq)
++		ov01a1s->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
++	ov01a1s->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops,
++						V4L2_CID_PIXEL_RATE, 0,
++						OV01A1S_SCLK, 1, OV01A1S_SCLK);
++
++	vblank_min = cur_mode->vts_min - cur_mode->height;
++	vblank_max = OV01A1S_VTS_MAX - cur_mode->height;
++	vblank_default = cur_mode->vts_def - cur_mode->height;
++	ov01a1s->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops,
++					    V4L2_CID_VBLANK, vblank_min,
++					    vblank_max, 1, vblank_default);
++
++	h_blank = cur_mode->hts - cur_mode->width;
++	ov01a1s->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops,
++					    V4L2_CID_HBLANK, h_blank, h_blank,
++					    1, h_blank);
++	if (ov01a1s->hblank)
++		ov01a1s->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	ov01a1s->privacy_status = v4l2_ctrl_new_std(ctrl_hdlr,
++						    &ov01a1s_ctrl_ops,
++						    V4L2_CID_PRIVACY,
++						    0, 1, 1, 0);
++#endif
++
++	v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++			  OV01A1S_ANAL_GAIN_MIN, OV01A1S_ANAL_GAIN_MAX,
++			  OV01A1S_ANAL_GAIN_STEP, OV01A1S_ANAL_GAIN_MIN);
++	v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
++			  OV01A1S_DGTL_GAIN_MIN, OV01A1S_DGTL_GAIN_MAX,
++			  OV01A1S_DGTL_GAIN_STEP, OV01A1S_DGTL_GAIN_DEFAULT);
++	exposure_max = cur_mode->vts_def - OV01A1S_EXPOSURE_MAX_MARGIN;
++	ov01a1s->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops,
++					      V4L2_CID_EXPOSURE,
++					      OV01A1S_EXPOSURE_MIN,
++					      exposure_max,
++					      OV01A1S_EXPOSURE_STEP,
++					      exposure_max);
++	v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov01a1s_ctrl_ops,
++				     V4L2_CID_TEST_PATTERN,
++				     ARRAY_SIZE(ov01a1s_test_pattern_menu) - 1,
++				     0, 0, ov01a1s_test_pattern_menu);
++	if (ctrl_hdlr->error)
++		return ctrl_hdlr->error;
++
++	ov01a1s->sd.ctrl_handler = ctrl_hdlr;
++
++	return 0;
++}
++
++static void ov01a1s_update_pad_format(const struct ov01a1s_mode *mode,
++				      struct v4l2_mbus_framefmt *fmt)
++{
++	fmt->width = mode->width;
++	fmt->height = mode->height;
++	fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10;
++	fmt->field = V4L2_FIELD_NONE;
++}
++
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++static void ov01a1s_vsc_privacy_callback(void *handle,
++				       enum vsc_privacy_status status)
++{
++	struct ov01a1s *ov01a1s = handle;
++
++	v4l2_ctrl_s_ctrl(ov01a1s->privacy_status, !status);
++}
++
++#endif
++static int ov01a1s_start_streaming(struct ov01a1s *ov01a1s)
++{
++	struct i2c_client *client = ov01a1s->client;
++	const struct ov01a1s_reg_list *reg_list;
++	int link_freq_index;
++	int ret = 0;
++
++	link_freq_index = ov01a1s->cur_mode->link_freq_index;
++	reg_list = &link_freq_configs[link_freq_index].reg_list;
++	ret = ov01a1s_write_reg_list(ov01a1s, reg_list);
++	if (ret) {
++		dev_err(&client->dev, "failed to set plls");
++		return ret;
++	}
++
++	reg_list = &ov01a1s->cur_mode->reg_list;
++	ret = ov01a1s_write_reg_list(ov01a1s, reg_list);
++	if (ret) {
++		dev_err(&client->dev, "failed to set mode");
++		return ret;
++	}
++
++	ret = __v4l2_ctrl_handler_setup(ov01a1s->sd.ctrl_handler);
++	if (ret)
++		return ret;
++
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_MODE_SELECT, 1,
++				OV01A1S_MODE_STREAMING);
++	if (ret)
++		dev_err(&client->dev, "failed to start streaming");
++
++	return ret;
++}
++
++static void ov01a1s_stop_streaming(struct ov01a1s *ov01a1s)
++{
++	struct i2c_client *client = ov01a1s->client;
++	int ret = 0;
++
++	ret = ov01a1s_write_reg(ov01a1s, OV01A1S_REG_MODE_SELECT, 1,
++				OV01A1S_MODE_STANDBY);
++	if (ret)
++		dev_err(&client->dev, "failed to stop streaming");
++}
++
++static int ov01a1s_set_stream(struct v4l2_subdev *sd, int enable)
++{
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	struct i2c_client *client = ov01a1s->client;
++	int ret = 0;
++
++	if (ov01a1s->streaming == enable)
++		return 0;
++
++	mutex_lock(&ov01a1s->mutex);
++	if (enable) {
++		ret = pm_runtime_get_sync(&client->dev);
++		if (ret < 0) {
++			pm_runtime_put_noidle(&client->dev);
++			mutex_unlock(&ov01a1s->mutex);
++			return ret;
++		}
++
++		ret = ov01a1s_start_streaming(ov01a1s);
++		if (ret) {
++			enable = 0;
++			ov01a1s_stop_streaming(ov01a1s);
++			pm_runtime_put(&client->dev);
++		}
++	} else {
++		ov01a1s_stop_streaming(ov01a1s);
++		pm_runtime_put(&client->dev);
++	}
++
++	ov01a1s->streaming = enable;
++	mutex_unlock(&ov01a1s->mutex);
++
++	return ret;
++}
++
++static int ov01a1s_power_off(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	int ret = 0;
++
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472) {
++		gpiod_set_value_cansleep(ov01a1s->reset_gpio, 1);
++		gpiod_set_value_cansleep(ov01a1s->powerdown_gpio, 1);
++		if (ov01a1s->avdd)
++			ret = regulator_disable(ov01a1s->avdd);
++		clk_disable_unprepare(ov01a1s->clk);
++		msleep(20);
++	}
++#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
++		ret = power_ctrl_logic_set_power(0);
++#endif
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) {
++		ret = vsc_release_camera_sensor(&ov01a1s->status);
++		if (ret && ret != -EAGAIN)
++			dev_err(dev, "Release VSC failed");
++	}
++#endif
++
++	return ret;
++}
++
++static int ov01a1s_power_on(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	int ret = 0;
++
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472) {
++		ret = clk_prepare_enable(ov01a1s->clk);
++		if (ret)
++			return ret;
++		if (ov01a1s->avdd)
++			ret = regulator_enable(ov01a1s->avdd);
++		if (ret)
++			return ret;
++		gpiod_set_value_cansleep(ov01a1s->powerdown_gpio, 0);
++		gpiod_set_value_cansleep(ov01a1s->reset_gpio, 0);
++		msleep(20);
++	}
++#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
++		ret = power_ctrl_logic_set_power(1);
++#endif
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) {
++		ret = vsc_acquire_camera_sensor(&ov01a1s->conf,
++						ov01a1s_vsc_privacy_callback,
++						ov01a1s, &ov01a1s->status);
++		if (ret && ret != -EAGAIN) {
++			dev_err(dev, "Acquire VSC failed");
++			return ret;
++		}
++		__v4l2_ctrl_s_ctrl(ov01a1s->privacy_status,
++				   !(ov01a1s->status.status));
++	}
++#endif
++
++	return ret;
++}
++
++static int __maybe_unused ov01a1s_suspend(struct device *dev)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct v4l2_subdev *sd = i2c_get_clientdata(client);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++
++	mutex_lock(&ov01a1s->mutex);
++	if (ov01a1s->streaming)
++		ov01a1s_stop_streaming(ov01a1s);
++
++	mutex_unlock(&ov01a1s->mutex);
++
++	return 0;
++}
++
++static int __maybe_unused ov01a1s_resume(struct device *dev)
++{
++	struct i2c_client *client = to_i2c_client(dev);
++	struct v4l2_subdev *sd = i2c_get_clientdata(client);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	int ret = 0;
++
++	mutex_lock(&ov01a1s->mutex);
++	if (!ov01a1s->streaming)
++		goto exit;
++
++	ret = ov01a1s_start_streaming(ov01a1s);
++	if (ret) {
++		ov01a1s->streaming = false;
++		ov01a1s_stop_streaming(ov01a1s);
++	}
++
++exit:
++	mutex_unlock(&ov01a1s->mutex);
++	return ret;
++}
++
++static int ov01a1s_set_format(struct v4l2_subdev *sd,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++			      struct v4l2_subdev_pad_config *cfg,
++#else
++			      struct v4l2_subdev_state *sd_state,
++#endif
++			      struct v4l2_subdev_format *fmt)
++{
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++	const struct ov01a1s_mode *mode;
++	s32 vblank_def, h_blank;
++
++	mode = v4l2_find_nearest_size(supported_modes,
++				      ARRAY_SIZE(supported_modes), width,
++				      height, fmt->format.width,
++				      fmt->format.height);
++
++	mutex_lock(&ov01a1s->mutex);
++	ov01a1s_update_pad_format(mode, &fmt->format);
++	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
++#else
++		*v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format;
++#endif
++	} else {
++		ov01a1s->cur_mode = mode;
++		__v4l2_ctrl_s_ctrl(ov01a1s->link_freq, mode->link_freq_index);
++		__v4l2_ctrl_s_ctrl_int64(ov01a1s->pixel_rate, OV01A1S_SCLK);
++
++		/* Update limits and set FPS to default */
++		vblank_def = mode->vts_def - mode->height;
++		__v4l2_ctrl_modify_range(ov01a1s->vblank,
++					 mode->vts_min - mode->height,
++					 OV01A1S_VTS_MAX - mode->height, 1,
++					 vblank_def);
++		__v4l2_ctrl_s_ctrl(ov01a1s->vblank, vblank_def);
++		h_blank = mode->hts - mode->width;
++		__v4l2_ctrl_modify_range(ov01a1s->hblank, h_blank, h_blank, 1,
++					 h_blank);
++	}
++	mutex_unlock(&ov01a1s->mutex);
++
++	return 0;
++}
++
++static int ov01a1s_get_format(struct v4l2_subdev *sd,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++			      struct v4l2_subdev_pad_config *cfg,
++#else
++			      struct v4l2_subdev_state *sd_state,
++#endif
++			      struct v4l2_subdev_format *fmt)
++{
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++
++	mutex_lock(&ov01a1s->mutex);
++	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++		fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd, cfg,
++							  fmt->pad);
++#else
++		fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd,
++							  sd_state, fmt->pad);
++#endif
++	else
++		ov01a1s_update_pad_format(ov01a1s->cur_mode, &fmt->format);
++
++	mutex_unlock(&ov01a1s->mutex);
++
++	return 0;
++}
++
++static int ov01a1s_enum_mbus_code(struct v4l2_subdev *sd,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++				  struct v4l2_subdev_pad_config *cfg,
++#else
++				  struct v4l2_subdev_state *sd_state,
++#endif
++				  struct v4l2_subdev_mbus_code_enum *code)
++{
++	if (code->index > 0)
++		return -EINVAL;
++
++	code->code = MEDIA_BUS_FMT_SGRBG10_1X10;
++
++	return 0;
++}
++
++static int ov01a1s_enum_frame_size(struct v4l2_subdev *sd,
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++				   struct v4l2_subdev_pad_config *cfg,
++#else
++				   struct v4l2_subdev_state *sd_state,
++#endif
++				   struct v4l2_subdev_frame_size_enum *fse)
++{
++	if (fse->index >= ARRAY_SIZE(supported_modes))
++		return -EINVAL;
++
++	if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
++		return -EINVAL;
++
++	fse->min_width = supported_modes[fse->index].width;
++	fse->max_width = fse->min_width;
++	fse->min_height = supported_modes[fse->index].height;
++	fse->max_height = fse->min_height;
++
++	return 0;
++}
++
++static int ov01a1s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++
++	mutex_lock(&ov01a1s->mutex);
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
++	ov01a1s_update_pad_format(&supported_modes[0],
++				  v4l2_subdev_get_try_format(sd, fh->pad, 0));
++#else
++	ov01a1s_update_pad_format(&supported_modes[0],
++				  v4l2_subdev_get_try_format(sd, fh->state, 0));
++#endif
++	mutex_unlock(&ov01a1s->mutex);
++
++	return 0;
++}
++
++static const struct v4l2_subdev_video_ops ov01a1s_video_ops = {
++	.s_stream = ov01a1s_set_stream,
++};
++
++static const struct v4l2_subdev_pad_ops ov01a1s_pad_ops = {
++	.set_fmt = ov01a1s_set_format,
++	.get_fmt = ov01a1s_get_format,
++	.enum_mbus_code = ov01a1s_enum_mbus_code,
++	.enum_frame_size = ov01a1s_enum_frame_size,
++};
++
++static const struct v4l2_subdev_ops ov01a1s_subdev_ops = {
++	.video = &ov01a1s_video_ops,
++	.pad = &ov01a1s_pad_ops,
++};
++
++static const struct media_entity_operations ov01a1s_subdev_entity_ops = {
++	.link_validate = v4l2_subdev_link_validate,
++};
++
++static const struct v4l2_subdev_internal_ops ov01a1s_internal_ops = {
++	.open = ov01a1s_open,
++};
++
++static int ov01a1s_identify_module(struct ov01a1s *ov01a1s)
++{
++	struct i2c_client *client = ov01a1s->client;
++	int ret;
++	u32 val;
++
++	ret = ov01a1s_read_reg(ov01a1s, OV01A1S_REG_CHIP_ID, 3, &val);
++	if (ret)
++		return ret;
++
++	if (val != OV01A1S_CHIP_ID) {
++		dev_err(&client->dev, "chip id mismatch: %x!=%x",
++			OV01A1S_CHIP_ID, val);
++		return -ENXIO;
++	}
++
++	return 0;
++}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
++static int ov01a1s_remove(struct i2c_client *client)
++#else
++static void ov01a1s_remove(struct i2c_client *client)
++#endif
++{
++	struct v4l2_subdev *sd = i2c_get_clientdata(client);
++	struct ov01a1s *ov01a1s = to_ov01a1s(sd);
++
++	v4l2_async_unregister_subdev(sd);
++	media_entity_cleanup(&sd->entity);
++	v4l2_ctrl_handler_free(sd->ctrl_handler);
++	pm_runtime_disable(&client->dev);
++	mutex_destroy(&ov01a1s->mutex);
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
++	return 0;
++#endif
++}
++
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++static int ov01a1s_parse_gpio(struct ov01a1s *ov01a1s)
++{
++	struct device *dev = &ov01a1s->client->dev;
++
++	ov01a1s->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
++	if (IS_ERR(ov01a1s->reset_gpio)) {
++		dev_warn(dev, "error while getting reset gpio: %ld\n",
++			 PTR_ERR(ov01a1s->reset_gpio));
++		ov01a1s->reset_gpio = NULL;
++		return -EPROBE_DEFER;
++	}
++
++	/* For optional, don't return or print warn if can't get it */
++	ov01a1s->powerdown_gpio =
++		devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW);
++	if (IS_ERR(ov01a1s->powerdown_gpio)) {
++		dev_dbg(dev, "no powerdown gpio: %ld\n",
++			PTR_ERR(ov01a1s->powerdown_gpio));
++		ov01a1s->powerdown_gpio = NULL;
++	}
++
++	ov01a1s->avdd = devm_regulator_get_optional(dev, "avdd");
++	if (IS_ERR(ov01a1s->avdd)) {
++		dev_dbg(dev, "no regulator avdd: %ld\n",
++			PTR_ERR(ov01a1s->avdd));
++		ov01a1s->avdd = NULL;
++	}
++
++	ov01a1s->clk = devm_clk_get_optional(dev, "clk");
++	if (IS_ERR(ov01a1s->clk)) {
++		dev_dbg(dev, "no clk: %ld\n", PTR_ERR(ov01a1s->clk));
++		ov01a1s->clk = NULL;
++	}
++
++	return 0;
++}
++#endif
++
++static int ov01a1s_parse_power(struct ov01a1s *ov01a1s)
++{
++	int ret = 0;
++
++#if IS_ENABLED(CONFIG_INTEL_VSC)
++	ov01a1s->conf.lane_num = OV01A1S_DATA_LANES;
++	/* frequency unit 100k */
++	ov01a1s->conf.freq = OV01A1S_LINK_FREQ_400MHZ / 100000;
++	ret = vsc_acquire_camera_sensor(&ov01a1s->conf, NULL, NULL, &ov01a1s->status);
++	if (!ret) {
++		ov01a1s->power_type = OV01A1S_USE_INTEL_VSC;
++		return 0;
++	} else if (ret != -EAGAIN) {
++		return ret;
++	}
++#endif
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	ret = ov01a1s_parse_gpio(ov01a1s);
++#elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++	ret = power_ctrl_logic_set_power(1);
++#endif
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) || IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
++	if (!ret) {
++		ov01a1s->power_type = OV01A1S_USE_INT3472;
++		return 0;
++	}
++#endif
++	if (ret == -EAGAIN)
++		return -EPROBE_DEFER;
++
++	return ret;
++}
++
++static int ov01a1s_probe(struct i2c_client *client)
++{
++	struct ov01a1s *ov01a1s;
++	int ret = 0;
++
++	ov01a1s = devm_kzalloc(&client->dev, sizeof(*ov01a1s), GFP_KERNEL);
++	if (!ov01a1s)
++		return -ENOMEM;
++
++	ov01a1s->client = client;
++	ret = ov01a1s_parse_power(ov01a1s);
++	if (ret)
++		return ret;
++
++	v4l2_i2c_subdev_init(&ov01a1s->sd, client, &ov01a1s_subdev_ops);
++#if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
++	/* In other cases, power is up in ov01a1s_parse_power */
++	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
++		ov01a1s_power_on(&client->dev);
++#endif
++	ret = ov01a1s_identify_module(ov01a1s);
++	if (ret) {
++		dev_err(&client->dev, "failed to find sensor: %d", ret);
++		goto probe_error_power_off;
++	}
++
++	mutex_init(&ov01a1s->mutex);
++	ov01a1s->cur_mode = &supported_modes[0];
++	ret = ov01a1s_init_controls(ov01a1s);
++	if (ret) {
++		dev_err(&client->dev, "failed to init controls: %d", ret);
++		goto probe_error_v4l2_ctrl_handler_free;
++	}
++
++	ov01a1s->sd.internal_ops = &ov01a1s_internal_ops;
++	ov01a1s->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++	ov01a1s->sd.entity.ops = &ov01a1s_subdev_entity_ops;
++	ov01a1s->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++	ov01a1s->pad.flags = MEDIA_PAD_FL_SOURCE;
++	ret = media_entity_pads_init(&ov01a1s->sd.entity, 1, &ov01a1s->pad);
++	if (ret) {
++		dev_err(&client->dev, "failed to init entity pads: %d", ret);
++		goto probe_error_v4l2_ctrl_handler_free;
++	}
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
++	ret = v4l2_async_register_subdev_sensor_common(&ov01a1s->sd);
++#else
++	ret = v4l2_async_register_subdev_sensor(&ov01a1s->sd);
++#endif
++	if (ret < 0) {
++		dev_err(&client->dev, "failed to register V4L2 subdev: %d",
++			ret);
++		goto probe_error_media_entity_cleanup;
++	}
++
++	/*
++	 * Device is already turned on by i2c-core with ACPI domain PM.
++	 * Enable runtime PM and turn off the device.
++	 */
++	pm_runtime_set_active(&client->dev);
++	pm_runtime_enable(&client->dev);
++	pm_runtime_idle(&client->dev);
++
++	return 0;
++
++probe_error_media_entity_cleanup:
++	media_entity_cleanup(&ov01a1s->sd.entity);
++
++probe_error_v4l2_ctrl_handler_free:
++	v4l2_ctrl_handler_free(ov01a1s->sd.ctrl_handler);
++	mutex_destroy(&ov01a1s->mutex);
++
++probe_error_power_off:
++	ov01a1s_power_off(&client->dev);
++
++	return ret;
++}
++
++static const struct dev_pm_ops ov01a1s_pm_ops = {
++	SET_SYSTEM_SLEEP_PM_OPS(ov01a1s_suspend, ov01a1s_resume)
++	SET_RUNTIME_PM_OPS(ov01a1s_power_off, ov01a1s_power_on, NULL)
++};
++
++#ifdef CONFIG_ACPI
++static const struct acpi_device_id ov01a1s_acpi_ids[] = {
++	{ "OVTI01AS" },
++	{}
++};
++
++MODULE_DEVICE_TABLE(acpi, ov01a1s_acpi_ids);
++#endif
++
++static struct i2c_driver ov01a1s_i2c_driver = {
++	.driver = {
++		.name = "ov01a1s",
++		.pm = &ov01a1s_pm_ops,
++		.acpi_match_table = ACPI_PTR(ov01a1s_acpi_ids),
++	},
++#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
++	.probe_new = ov01a1s_probe,
++#else
++	.probe = ov01a1s_probe,
++#endif
++	.remove = ov01a1s_remove,
++};
++
++module_i2c_driver(ov01a1s_i2c_driver);
++
++MODULE_AUTHOR("Xu, Chongyang <chongyang.xu@intel.com>");
++MODULE_AUTHOR("Lai, Jim <jim.lai@intel.com>");
++MODULE_AUTHOR("Qiu, Tianshu <tian.shu.qiu@intel.com>");
++MODULE_AUTHOR("Shawn Tu <shawnx.tu@intel.com>");
++MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
++MODULE_DESCRIPTION("OmniVision OV01A1S sensor driver");
++MODULE_LICENSE("GPL v2");
+-- 
+2.43.2
+
+
+From 9f58ae728245ad7ac604737ab16781d7ccb2006e Mon Sep 17 00:00:00 2001
+From: Florian Klink <flokli@flokli.de>
+Date: Sun, 17 Mar 2024 14:24:05 +0200
+Subject: [PATCH 27/33] ov01a1s.c: support Linux 6.8.0
+
+Used https://github.com/intel/ipu6-drivers/pull/213 as an inspiration.
+---
+ drivers/media/i2c/ov01a1s.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c
+index 0dcce8b492b4..923b12b2a948 100644
+--- a/drivers/media/i2c/ov01a1s.c
++++ b/drivers/media/i2c/ov01a1s.c
+@@ -832,8 +832,10 @@ static int ov01a1s_set_format(struct v4l2_subdev *sd,
+ 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
+ 		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+-#else
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0)
+ 		*v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format;
++#else
++		*v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
+ #endif
+ 	} else {
+ 		ov01a1s->cur_mode = mode;
+@@ -871,9 +873,11 @@ static int ov01a1s_get_format(struct v4l2_subdev *sd,
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
+ 		fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd, cfg,
+ 							  fmt->pad);
+-#else
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0)
+ 		fmt->format = *v4l2_subdev_get_try_format(&ov01a1s->sd,
+ 							  sd_state, fmt->pad);
++#else
++		fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad);
+ #endif
+ 	else
+ 		ov01a1s_update_pad_format(ov01a1s->cur_mode, &fmt->format);
+@@ -929,9 +933,12 @@ static int ov01a1s_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)
+ 	ov01a1s_update_pad_format(&supported_modes[0],
+ 				  v4l2_subdev_get_try_format(sd, fh->pad, 0));
+-#else
++#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 8, 0)
+ 	ov01a1s_update_pad_format(&supported_modes[0],
+ 				  v4l2_subdev_get_try_format(sd, fh->state, 0));
++#else
++	ov01a1s_update_pad_format(&supported_modes[0],
++				  v4l2_subdev_state_get_format(fh->state, 0));
+ #endif
+ 	mutex_unlock(&ov01a1s->mutex);
+ 
+-- 
+2.43.2
+
+
+From 80bee1ca899ebfa4126d1e69ea821a2c30aba00c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 6 Nov 2023 12:33:56 +0100
+Subject: [PATCH 28/33] media: ov01a1s: Remove non upstream iVSC support
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/ov01a1s.c | 71 -------------------------------------
+ 1 file changed, 71 deletions(-)
+
+diff --git a/drivers/media/i2c/ov01a1s.c b/drivers/media/i2c/ov01a1s.c
+index 923b12b2a948..22b406bdeae9 100644
+--- a/drivers/media/i2c/ov01a1s.c
++++ b/drivers/media/i2c/ov01a1s.c
+@@ -17,9 +17,6 @@
+ #elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
+ #include "power_ctrl_logic.h"
+ #endif
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-#include <linux/vsc.h>
+-#endif
+ 
+ #define OV01A1S_LINK_FREQ_400MHZ	400000000ULL
+ #define OV01A1S_SCLK			40000000LL
+@@ -302,13 +299,6 @@ struct ov01a1s {
+ 	struct v4l2_ctrl *vblank;
+ 	struct v4l2_ctrl *hblank;
+ 	struct v4l2_ctrl *exposure;
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	struct v4l2_ctrl *privacy_status;
+-
+-	/* VSC settings */
+-	struct vsc_mipi_config conf;
+-	struct vsc_camera_status status;
+-#endif
+ 
+ 	/* Current mode */
+ 	const struct ov01a1s_mode *cur_mode;
+@@ -334,9 +324,6 @@ struct ov01a1s {
+ 		OV01A1S_USE_DEFAULT = 0,
+ #if IS_ENABLED(CONFIG_INTEL_SKL_INT3472) || IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
+ 		OV01A1S_USE_INT3472 = 1,
+-#endif
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-		OV01A1S_USE_INTEL_VSC = 2,
+ #endif
+ 	} power_type;
+ 
+@@ -505,12 +492,6 @@ static int ov01a1s_set_ctrl(struct v4l2_ctrl *ctrl)
+ 		ret = ov01a1s_test_pattern(ov01a1s, ctrl->val);
+ 		break;
+ 
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	case V4L2_CID_PRIVACY:
+-		dev_dbg(&client->dev, "set privacy to %d", ctrl->val);
+-		break;
+-
+-#endif
+ 	default:
+ 		ret = -EINVAL;
+ 		break;
+@@ -535,11 +516,7 @@ static int ov01a1s_init_controls(struct ov01a1s *ov01a1s)
+ 	int ret = 0;
+ 
+ 	ctrl_hdlr = &ov01a1s->ctrl_handler;
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+-#else
+ 	ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8);
+-#endif
+ 	if (ret)
+ 		return ret;
+ 
+@@ -572,12 +549,6 @@ static int ov01a1s_init_controls(struct ov01a1s *ov01a1s)
+ 					    1, h_blank);
+ 	if (ov01a1s->hblank)
+ 		ov01a1s->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	ov01a1s->privacy_status = v4l2_ctrl_new_std(ctrl_hdlr,
+-						    &ov01a1s_ctrl_ops,
+-						    V4L2_CID_PRIVACY,
+-						    0, 1, 1, 0);
+-#endif
+ 
+ 	v4l2_ctrl_new_std(ctrl_hdlr, &ov01a1s_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ 			  OV01A1S_ANAL_GAIN_MIN, OV01A1S_ANAL_GAIN_MAX,
+@@ -613,16 +584,6 @@ static void ov01a1s_update_pad_format(const struct ov01a1s_mode *mode,
+ 	fmt->field = V4L2_FIELD_NONE;
+ }
+ 
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-static void ov01a1s_vsc_privacy_callback(void *handle,
+-				       enum vsc_privacy_status status)
+-{
+-	struct ov01a1s *ov01a1s = handle;
+-
+-	v4l2_ctrl_s_ctrl(ov01a1s->privacy_status, !status);
+-}
+-
+-#endif
+ static int ov01a1s_start_streaming(struct ov01a1s *ov01a1s)
+ {
+ 	struct i2c_client *client = ov01a1s->client;
+@@ -722,13 +683,6 @@ static int ov01a1s_power_off(struct device *dev)
+ 	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
+ 		ret = power_ctrl_logic_set_power(0);
+ #endif
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) {
+-		ret = vsc_release_camera_sensor(&ov01a1s->status);
+-		if (ret && ret != -EAGAIN)
+-			dev_err(dev, "Release VSC failed");
+-	}
+-#endif
+ 
+ 	return ret;
+ }
+@@ -756,19 +710,6 @@ static int ov01a1s_power_on(struct device *dev)
+ 	if (ov01a1s->power_type == OV01A1S_USE_INT3472)
+ 		ret = power_ctrl_logic_set_power(1);
+ #endif
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	if (ov01a1s->power_type == OV01A1S_USE_INTEL_VSC) {
+-		ret = vsc_acquire_camera_sensor(&ov01a1s->conf,
+-						ov01a1s_vsc_privacy_callback,
+-						ov01a1s, &ov01a1s->status);
+-		if (ret && ret != -EAGAIN) {
+-			dev_err(dev, "Acquire VSC failed");
+-			return ret;
+-		}
+-		__v4l2_ctrl_s_ctrl(ov01a1s->privacy_status,
+-				   !(ov01a1s->status.status));
+-	}
+-#endif
+ 
+ 	return ret;
+ }
+@@ -1051,18 +992,6 @@ static int ov01a1s_parse_power(struct ov01a1s *ov01a1s)
+ {
+ 	int ret = 0;
+ 
+-#if IS_ENABLED(CONFIG_INTEL_VSC)
+-	ov01a1s->conf.lane_num = OV01A1S_DATA_LANES;
+-	/* frequency unit 100k */
+-	ov01a1s->conf.freq = OV01A1S_LINK_FREQ_400MHZ / 100000;
+-	ret = vsc_acquire_camera_sensor(&ov01a1s->conf, NULL, NULL, &ov01a1s->status);
+-	if (!ret) {
+-		ov01a1s->power_type = OV01A1S_USE_INTEL_VSC;
+-		return 0;
+-	} else if (ret != -EAGAIN) {
+-		return ret;
+-	}
+-#endif
+ #if IS_ENABLED(CONFIG_INTEL_SKL_INT3472)
+ 	ret = ov01a1s_parse_gpio(ov01a1s);
+ #elif IS_ENABLED(CONFIG_POWER_CTRL_LOGIC)
+-- 
+2.43.2
+
+
+From e624515c64d782b452a4676c1e117815267559ae Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 23 Jan 2024 14:58:35 +0100
+Subject: [PATCH 29/33] media: hi556: Return -EPROBE_DEFER if no endpoint is
+ found
+
+With ipu bridge, endpoints may only be created when ipu bridge has
+initialised. This may happen after the sensor driver has first probed.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/hi556.c | 13 +++++++------
+ 1 file changed, 7 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
+index 38c77d515786..96bae9914d52 100644
+--- a/drivers/media/i2c/hi556.c
++++ b/drivers/media/i2c/hi556.c
+@@ -1206,8 +1206,13 @@ static int hi556_check_hwcfg(struct device *dev)
+ 	int ret = 0;
+ 	unsigned int i, j;
+ 
+-	if (!fwnode)
+-		return -ENXIO;
++	/*
++	 * Sometimes the fwnode graph is initialized by the bridge driver,
++	 * wait for this.
++	 */
++	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
++	if (!ep)
++		return -EPROBE_DEFER;
+ 
+ 	ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk);
+ 	if (ret) {
+@@ -1220,10 +1225,6 @@ static int hi556_check_hwcfg(struct device *dev)
+ 		return -EINVAL;
+ 	}
+ 
+-	ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+-	if (!ep)
+-		return -ENXIO;
+-
+ 	ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ 	fwnode_handle_put(ep);
+ 	if (ret)
+-- 
+2.43.2
+
+
+From b127d1003050fb894ea764b600d5f399af413b68 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 23 Jan 2024 14:48:26 +0100
+Subject: [PATCH 30/33] media: hi556: Add support for reset GPIO
+
+On some ACPI platforms, such as Chromebooks the ACPI methods to
+change the power-state (_PS0 and _PS3) fully take care of powering
+on/off the sensor.
+
+On other ACPI platforms, such as e.g. various HP models with IPU6 +
+hi556 sensor, the sensor driver must control the reset GPIO itself.
+
+Add support for having the driver control an optional reset GPIO.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/hi556.c | 45 ++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 44 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
+index 96bae9914d52..f5a39b83598b 100644
+--- a/drivers/media/i2c/hi556.c
++++ b/drivers/media/i2c/hi556.c
+@@ -4,6 +4,7 @@
+ #include <asm/unaligned.h>
+ #include <linux/acpi.h>
+ #include <linux/delay.h>
++#include <linux/gpio/consumer.h>
+ #include <linux/i2c.h>
+ #include <linux/module.h>
+ #include <linux/pm_runtime.h>
+@@ -633,6 +634,9 @@ struct hi556 {
+ 	struct v4l2_ctrl *hblank;
+ 	struct v4l2_ctrl *exposure;
+ 
++	/* GPIOs, clocks, etc. */
++	struct gpio_desc *reset_gpio;
++
+ 	/* Current mode */
+ 	const struct hi556_mode *cur_mode;
+ 
+@@ -1276,6 +1280,25 @@ static void hi556_remove(struct i2c_client *client)
+ 	mutex_destroy(&hi556->mutex);
+ }
+ 
++static int hi556_suspend(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct hi556 *hi556 = to_hi556(sd);
++
++	gpiod_set_value_cansleep(hi556->reset_gpio, 1);
++	return 0;
++}
++
++static int hi556_resume(struct device *dev)
++{
++	struct v4l2_subdev *sd = dev_get_drvdata(dev);
++	struct hi556 *hi556 = to_hi556(sd);
++
++	gpiod_set_value_cansleep(hi556->reset_gpio, 0);
++	usleep_range(5000, 5500);
++	return 0;
++}
++
+ static int hi556_probe(struct i2c_client *client)
+ {
+ 	struct hi556 *hi556;
+@@ -1295,12 +1318,24 @@ static int hi556_probe(struct i2c_client *client)
+ 
+ 	v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops);
+ 
++	hi556->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
++						    GPIOD_OUT_HIGH);
++	if (IS_ERR(hi556->reset_gpio))
++		return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio),
++				     "failed to get reset GPIO\n");
++
+ 	full_power = acpi_dev_state_d0(&client->dev);
+ 	if (full_power) {
++		/* Ensure non ACPI managed resources are enabled */
++		ret = hi556_resume(&client->dev);
++		if (ret)
++			return dev_err_probe(&client->dev, ret,
++					     "failed to power on sensor\n");
++
+ 		ret = hi556_identify_module(hi556);
+ 		if (ret) {
+ 			dev_err(&client->dev, "failed to find sensor: %d", ret);
+-			return ret;
++			goto probe_error_power_off;
+ 		}
+ 	}
+ 
+@@ -1345,9 +1380,16 @@ static int hi556_probe(struct i2c_client *client)
+ 	v4l2_ctrl_handler_free(hi556->sd.ctrl_handler);
+ 	mutex_destroy(&hi556->mutex);
+ 
++probe_error_power_off:
++	if (full_power)
++		hi556_suspend(&client->dev);
++
+ 	return ret;
+ }
+ 
++static DEFINE_RUNTIME_DEV_PM_OPS(hi556_pm_ops, hi556_suspend, hi556_resume,
++				 NULL);
++
+ #ifdef CONFIG_ACPI
+ static const struct acpi_device_id hi556_acpi_ids[] = {
+ 	{"INT3537"},
+@@ -1361,6 +1403,7 @@ static struct i2c_driver hi556_i2c_driver = {
+ 	.driver = {
+ 		.name = "hi556",
+ 		.acpi_match_table = ACPI_PTR(hi556_acpi_ids),
++		.pm = pm_sleep_ptr(&hi556_pm_ops),
+ 	},
+ 	.probe = hi556_probe,
+ 	.remove = hi556_remove,
+-- 
+2.43.2
+
+
+From ee651202ba2ca38da067b5379edd7b4f339cf7a8 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 23 Jan 2024 14:54:22 +0100
+Subject: [PATCH 31/33] media: hi556: Add support for external clock
+
+On some ACPI platforms, such as Chromebooks the ACPI methods to
+change the power-state (_PS0 and _PS3) fully take care of powering
+on/off the sensor.
+
+On other ACPI platforms, such as e.g. various HP models with IPU6 +
+hi556 sensor, the sensor driver must control the sensor's clock itself.
+
+Add support for having the driver control an optional clock.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/hi556.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
+index f5a39b83598b..b783e0f56687 100644
+--- a/drivers/media/i2c/hi556.c
++++ b/drivers/media/i2c/hi556.c
+@@ -3,6 +3,7 @@
+ 
+ #include <asm/unaligned.h>
+ #include <linux/acpi.h>
++#include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/gpio/consumer.h>
+ #include <linux/i2c.h>
+@@ -636,6 +637,7 @@ struct hi556 {
+ 
+ 	/* GPIOs, clocks, etc. */
+ 	struct gpio_desc *reset_gpio;
++	struct clk *clk;
+ 
+ 	/* Current mode */
+ 	const struct hi556_mode *cur_mode;
+@@ -1286,6 +1288,7 @@ static int hi556_suspend(struct device *dev)
+ 	struct hi556 *hi556 = to_hi556(sd);
+ 
+ 	gpiod_set_value_cansleep(hi556->reset_gpio, 1);
++	clk_disable_unprepare(hi556->clk);
+ 	return 0;
+ }
+ 
+@@ -1293,6 +1296,11 @@ static int hi556_resume(struct device *dev)
+ {
+ 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ 	struct hi556 *hi556 = to_hi556(sd);
++	int ret;
++
++	ret = clk_prepare_enable(hi556->clk);
++	if (ret)
++		return ret;
+ 
+ 	gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ 	usleep_range(5000, 5500);
+@@ -1324,6 +1332,11 @@ static int hi556_probe(struct i2c_client *client)
+ 		return dev_err_probe(&client->dev, PTR_ERR(hi556->reset_gpio),
+ 				     "failed to get reset GPIO\n");
+ 
++	hi556->clk = devm_clk_get_optional(&client->dev, "clk");
++	if (IS_ERR(hi556->clk))
++		return dev_err_probe(&client->dev, PTR_ERR(hi556->clk),
++				     "failed to get clock\n");
++
+ 	full_power = acpi_dev_state_d0(&client->dev);
+ 	if (full_power) {
+ 		/* Ensure non ACPI managed resources are enabled */
+-- 
+2.43.2
+
+
+From 16be71996d451b8137ba63070e760448814c11a1 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 24 Jan 2024 18:45:02 +0100
+Subject: [PATCH 32/33] media: hi556: Add support for avdd regulator
+
+On some ACPI platforms, such as Chromebooks the ACPI methods to
+change the power-state (_PS0 and _PS3) fully take care of powering
+on/off the sensor.
+
+On other ACPI platforms, such as e.g. various HP models with IPU6 +
+hi556 sensor, the sensor driver must control the avdd regulator itself.
+
+Add support for having the driver control the sensor's avdd regulator.
+Note this relies on the regulator-core providing a dummy regulator
+(which it does by default) on platforms where Linux is not aware of
+the avdd regulator.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/media/i2c/hi556.c | 24 ++++++++++++++++++++++++
+ 1 file changed, 24 insertions(+)
+
+diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
+index b783e0f56687..5641c249d4b1 100644
+--- a/drivers/media/i2c/hi556.c
++++ b/drivers/media/i2c/hi556.c
+@@ -9,6 +9,7 @@
+ #include <linux/i2c.h>
+ #include <linux/module.h>
+ #include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
+ #include <media/v4l2-ctrls.h>
+ #include <media/v4l2-device.h>
+ #include <media/v4l2-fwnode.h>
+@@ -638,6 +639,7 @@ struct hi556 {
+ 	/* GPIOs, clocks, etc. */
+ 	struct gpio_desc *reset_gpio;
+ 	struct clk *clk;
++	struct regulator *avdd;
+ 
+ 	/* Current mode */
+ 	const struct hi556_mode *cur_mode;
+@@ -1286,8 +1288,17 @@ static int hi556_suspend(struct device *dev)
+ {
+ 	struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ 	struct hi556 *hi556 = to_hi556(sd);
++	int ret;
+ 
+ 	gpiod_set_value_cansleep(hi556->reset_gpio, 1);
++
++	ret = regulator_disable(hi556->avdd);
++	if (ret) {
++		dev_err(dev, "failed to disable avdd: %d\n", ret);
++		gpiod_set_value_cansleep(hi556->reset_gpio, 0);
++		return ret;
++	}
++
+ 	clk_disable_unprepare(hi556->clk);
+ 	return 0;
+ }
+@@ -1302,6 +1313,13 @@ static int hi556_resume(struct device *dev)
+ 	if (ret)
+ 		return ret;
+ 
++	ret = regulator_enable(hi556->avdd);
++	if (ret) {
++		dev_err(dev, "failed to enable avdd: %d\n", ret);
++		clk_disable_unprepare(hi556->clk);
++		return ret;
++	}
++
+ 	gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ 	usleep_range(5000, 5500);
+ 	return 0;
+@@ -1337,6 +1355,12 @@ static int hi556_probe(struct i2c_client *client)
+ 		return dev_err_probe(&client->dev, PTR_ERR(hi556->clk),
+ 				     "failed to get clock\n");
+ 
++	/* The regulator core will provide a "dummy" regulator if necessary */
++	hi556->avdd = devm_regulator_get(&client->dev, "avdd");
++	if (IS_ERR(hi556->avdd))
++		return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd),
++				     "failed to get avdd regulator\n");
++
+ 	full_power = acpi_dev_state_d0(&client->dev);
+ 	if (full_power) {
+ 		/* Ensure non ACPI managed resources are enabled */
+-- 
+2.43.2
+
+
+From 6bd6e73829cf264120f629c88c552c4eb59c7eee Mon Sep 17 00:00:00 2001
+From: Florian Klink <flokli@flokli.de>
+Date: Sun, 17 Mar 2024 17:07:53 +0200
+Subject: [PATCH 33/33] media: intel/ipu6: fix firmware paths
+
+linux-firmware ships them in intel/ipu, not intel/.
+---
+ drivers/media/pci/intel/ipu6/ipu6.h | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/media/pci/intel/ipu6/ipu6.h b/drivers/media/pci/intel/ipu6/ipu6.h
+index 04e7e7e61ca5..da8a95a9edf8 100644
+--- a/drivers/media/pci/intel/ipu6/ipu6.h
++++ b/drivers/media/pci/intel/ipu6/ipu6.h
+@@ -24,10 +24,10 @@ struct ipu6_bus_device;
+ #define IPU6_NAME			"intel-ipu6"
+ #define IPU6_MEDIA_DEV_MODEL_NAME	"ipu6"
+ 
+-#define IPU6SE_FIRMWARE_NAME		"intel/ipu6se_fw.bin"
+-#define IPU6EP_FIRMWARE_NAME		"intel/ipu6ep_fw.bin"
+-#define IPU6_FIRMWARE_NAME		"intel/ipu6_fw.bin"
+-#define IPU6EPMTL_FIRMWARE_NAME		"intel/ipu6epmtl_fw.bin"
++#define IPU6SE_FIRMWARE_NAME		"intel/ipu/ipu6se_fw.bin"
++#define IPU6EP_FIRMWARE_NAME		"intel/ipu/ipu6ep_fw.bin"
++#define IPU6_FIRMWARE_NAME		"intel/ipu/ipu6_fw.bin"
++#define IPU6EPMTL_FIRMWARE_NAME		"intel/ipu/ipu6epmtl_fw.bin"
+ 
+ enum ipu6_version {
+ 	IPU6_VER_INVALID = 0,
+-- 
+2.43.2
+
diff --git a/users/riking/dotfiles/regolith/flags/show-shortcuts b/users/flokli/ipu6-softisp/libcamera/.skip-tree
index e69de29bb2..e69de29bb2 100644
--- a/users/riking/dotfiles/regolith/flags/show-shortcuts
+++ b/users/flokli/ipu6-softisp/libcamera/.skip-tree
diff --git a/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch b/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch
new file mode 100644
index 0000000000..b640ddaa24
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0001-libcamera-pipeline-simple-fix-size-adjustment-in-val.patch
@@ -0,0 +1,82 @@
+From d86746fc1739f678e4bafe43f5047cba9b6b053e Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:05 +0100
+Subject: [PATCH 01/21] libcamera: pipeline: simple: fix size adjustment in
+ validate()
+
+SimpleCameraConfiguration::validate() adjusts the configuration of its
+streams (if the size is not in the outputSizes) to the captureSize. But
+the captureSize itself can be not in the outputSizes, and then the
+adjusted configuration won't be valid resulting in camera configuration
+failure.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/pipeline/simple/simple.cpp | 37 ++++++++++++++++++++++--
+ 1 file changed, 35 insertions(+), 2 deletions(-)
+
+diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
+index 911051b2..a84f6760 100644
+--- a/src/libcamera/pipeline/simple/simple.cpp
++++ b/src/libcamera/pipeline/simple/simple.cpp
+@@ -881,6 +881,30 @@ SimpleCameraConfiguration::SimpleCameraConfiguration(Camera *camera,
+ {
+ }
+ 
++namespace {
++
++static Size adjustSize(const Size &requestedSize, const SizeRange &supportedSizes)
++{
++	ASSERT(supportedSizes.min <= supportedSizes.max);
++
++	if (supportedSizes.min == supportedSizes.max)
++		return supportedSizes.max;
++
++	unsigned int hStep = supportedSizes.hStep;
++	unsigned int vStep = supportedSizes.vStep;
++
++	if (hStep == 0)
++		hStep = supportedSizes.max.width - supportedSizes.min.width;
++	if (vStep == 0)
++		vStep = supportedSizes.max.height - supportedSizes.min.height;
++
++	Size adjusted = requestedSize.boundedTo(supportedSizes.max).expandedTo(supportedSizes.min);
++
++	return adjusted.shrunkBy(supportedSizes.min).alignedDownTo(hStep, vStep).grownBy(supportedSizes.min);
++}
++
++} /* namespace */
++
+ CameraConfiguration::Status SimpleCameraConfiguration::validate()
+ {
+ 	const CameraSensor *sensor = data_->sensor_.get();
+@@ -997,10 +1021,19 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
+ 		}
+ 
+ 		if (!pipeConfig_->outputSizes.contains(cfg.size)) {
++			Size adjustedSize = pipeConfig_->captureSize;
++			/*
++			 * The converter (when present) may not be able to output
++			 * a size identical to its input size. The capture size is thus
++			 * not guaranteed to be a valid output size. In such cases, use
++			 * the smaller valid output size closest to the requested.
++			 */
++			if (!pipeConfig_->outputSizes.contains(adjustedSize))
++				adjustedSize = adjustSize(cfg.size, pipeConfig_->outputSizes);
+ 			LOG(SimplePipeline, Debug)
+ 				<< "Adjusting size from " << cfg.size
+-				<< " to " << pipeConfig_->captureSize;
+-			cfg.size = pipeConfig_->captureSize;
++				<< " to " << adjustedSize;
++			cfg.size = adjustedSize;
+ 			status = Adjusted;
+ 		}
+ 
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch b/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch
new file mode 100644
index 0000000000..450a0a21f1
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0002-libcamera-internal-Move-dma_heaps.-h-cpp-to-common-d.patch
@@ -0,0 +1,350 @@
+From 96e50c6a43352a9cb81d558fea27e580f2b26585 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:06 +0100
+Subject: [PATCH 02/21] libcamera: internal: Move dma_heaps.[h, cpp] to common
+ directories
+
+DmaHeap class is useful outside the RPi pipeline handler too.
+
+Move dma_heaps.h and dma_heaps.cpp to common directories. Update
+the build files and RPi vc4 pipeline handler accordingly.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ .../libcamera/internal}/dma_heaps.h           |   4 -
+ include/libcamera/internal/meson.build        |   1 +
+ src/libcamera/dma_heaps.cpp                   | 127 ++++++++++++++++++
+ src/libcamera/meson.build                     |   1 +
+ src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp  |  90 -------------
+ src/libcamera/pipeline/rpi/vc4/meson.build    |   1 -
+ src/libcamera/pipeline/rpi/vc4/vc4.cpp        |   5 +-
+ 7 files changed, 131 insertions(+), 98 deletions(-)
+ rename {src/libcamera/pipeline/rpi/vc4 => include/libcamera/internal}/dma_heaps.h (92%)
+ create mode 100644 src/libcamera/dma_heaps.cpp
+ delete mode 100644 src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
+
+diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
+similarity index 92%
+rename from src/libcamera/pipeline/rpi/vc4/dma_heaps.h
+rename to include/libcamera/internal/dma_heaps.h
+index 0a4a8d86..cff8f140 100644
+--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h
++++ b/include/libcamera/internal/dma_heaps.h
+@@ -13,8 +13,6 @@
+ 
+ namespace libcamera {
+ 
+-namespace RPi {
+-
+ class DmaHeap
+ {
+ public:
+@@ -27,6 +25,4 @@ private:
+ 	UniqueFD dmaHeapHandle_;
+ };
+ 
+-} /* namespace RPi */
+-
+ } /* namespace libcamera */
+diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
+index 7f1f3440..33eb0fb3 100644
+--- a/include/libcamera/internal/meson.build
++++ b/include/libcamera/internal/meson.build
+@@ -25,6 +25,7 @@ libcamera_internal_headers = files([
+     'device_enumerator.h',
+     'device_enumerator_sysfs.h',
+     'device_enumerator_udev.h',
++    'dma_heaps.h',
+     'formats.h',
+     'framebuffer.h',
+     'ipa_manager.h',
+diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
+new file mode 100644
+index 00000000..38ef175a
+--- /dev/null
++++ b/src/libcamera/dma_heaps.cpp
+@@ -0,0 +1,127 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2020, Raspberry Pi Ltd
++ *
++ * dma_heaps.h - Helper class for dma-heap allocations.
++ */
++
++#include "libcamera/internal/dma_heaps.h"
++
++#include <array>
++#include <fcntl.h>
++#include <sys/ioctl.h>
++#include <unistd.h>
++
++#include <linux/dma-buf.h>
++#include <linux/dma-heap.h>
++
++#include <libcamera/base/log.h>
++
++/**
++ * \file dma_heaps.cpp
++ * \brief CMA dma-heap allocator
++ */
++
++/*
++ * /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
++ * to only have to worry about importing.
++ *
++ * Annoyingly, should the cma heap size be specified on the kernel command line
++ * instead of DT, the heap gets named "reserved" instead.
++ */
++static constexpr std::array<const char *, 2> heapNames = {
++	"/dev/dma_heap/linux,cma",
++	"/dev/dma_heap/reserved"
++};
++
++namespace libcamera {
++
++LOG_DEFINE_CATEGORY(DmaHeap)
++
++/**
++ * \class DmaHeap
++ * \brief Helper class for CMA dma-heap allocations
++ */
++
++/**
++ * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor
++ *
++ * Goes through the internal list of possible names of the CMA dma-heap devices
++ * until a CMA dma-heap device is successfully opened. If it fails to open any
++ * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap
++ * object owns a wrapped dma-heap file descriptor.
++ *
++ * Please check the new DmaHeap object with \ref DmaHeap::isValid before using it.
++ */
++DmaHeap::DmaHeap()
++{
++	for (const char *name : heapNames) {
++		int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
++		if (ret < 0) {
++			ret = errno;
++			LOG(DmaHeap, Debug)
++				<< "Failed to open " << name << ": "
++				<< strerror(ret);
++			continue;
++		}
++
++		dmaHeapHandle_ = UniqueFD(ret);
++		break;
++	}
++
++	if (!dmaHeapHandle_.isValid())
++		LOG(DmaHeap, Error) << "Could not open any dmaHeap device";
++}
++
++/**
++ * \brief Destroy the DmaHeap instance
++ *
++ * Destroying a DmaHeap instance which owns a wrapped dma-heap file descriptor
++ * closes the descriptor automatically.
++ */
++DmaHeap::~DmaHeap() = default;
++
++/**
++ * \fn DmaHeap::isValid()
++ * \brief Check if the DmaHeap instance is valid
++ * \return True if the DmaHeap is valid, false otherwise
++ */
++
++/**
++ * \brief Allocate a dma-buf from the DmaHeap
++ * \param [in] name The name to set for the allocated buffer
++ * \param [in] size The size of the buffer to allocate
++ * \return The \ref UniqueFD of the allocated buffer
++ *
++ * Allocates a dma-buf with read/write access.
++ * If the allocation fails returns invalid UniqueFD.
++ */
++UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
++{
++	int ret;
++
++	if (!name)
++		return {};
++
++	struct dma_heap_allocation_data alloc = {};
++
++	alloc.len = size;
++	alloc.fd_flags = O_CLOEXEC | O_RDWR;
++
++	ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
++	if (ret < 0) {
++		LOG(DmaHeap, Error) << "dmaHeap allocation failure for " << name;
++		return {};
++	}
++
++	UniqueFD allocFd(alloc.fd);
++	ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
++	if (ret < 0) {
++		LOG(DmaHeap, Error) << "dmaHeap naming failure for " << name;
++		return {};
++	}
++
++	return allocFd;
++}
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
+index 45f63e93..3c5e43df 100644
+--- a/src/libcamera/meson.build
++++ b/src/libcamera/meson.build
+@@ -17,6 +17,7 @@ libcamera_sources = files([
+     'delayed_controls.cpp',
+     'device_enumerator.cpp',
+     'device_enumerator_sysfs.cpp',
++    'dma_heaps.cpp',
+     'fence.cpp',
+     'formats.cpp',
+     'framebuffer.cpp',
+diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
+deleted file mode 100644
+index 317b1fc1..00000000
+--- a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp
++++ /dev/null
+@@ -1,90 +0,0 @@
+-/* SPDX-License-Identifier: LGPL-2.1-or-later */
+-/*
+- * Copyright (C) 2020, Raspberry Pi Ltd
+- *
+- * dma_heaps.h - Helper class for dma-heap allocations.
+- */
+-
+-#include "dma_heaps.h"
+-
+-#include <array>
+-#include <fcntl.h>
+-#include <linux/dma-buf.h>
+-#include <linux/dma-heap.h>
+-#include <sys/ioctl.h>
+-#include <unistd.h>
+-
+-#include <libcamera/base/log.h>
+-
+-/*
+- * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
+- * to only have to worry about importing.
+- *
+- * Annoyingly, should the cma heap size be specified on the kernel command line
+- * instead of DT, the heap gets named "reserved" instead.
+- */
+-static constexpr std::array<const char *, 2> heapNames = {
+-	"/dev/dma_heap/linux,cma",
+-	"/dev/dma_heap/reserved"
+-};
+-
+-namespace libcamera {
+-
+-LOG_DECLARE_CATEGORY(RPI)
+-
+-namespace RPi {
+-
+-DmaHeap::DmaHeap()
+-{
+-	for (const char *name : heapNames) {
+-		int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
+-		if (ret < 0) {
+-			ret = errno;
+-			LOG(RPI, Debug) << "Failed to open " << name << ": "
+-					<< strerror(ret);
+-			continue;
+-		}
+-
+-		dmaHeapHandle_ = UniqueFD(ret);
+-		break;
+-	}
+-
+-	if (!dmaHeapHandle_.isValid())
+-		LOG(RPI, Error) << "Could not open any dmaHeap device";
+-}
+-
+-DmaHeap::~DmaHeap() = default;
+-
+-UniqueFD DmaHeap::alloc(const char *name, std::size_t size)
+-{
+-	int ret;
+-
+-	if (!name)
+-		return {};
+-
+-	struct dma_heap_allocation_data alloc = {};
+-
+-	alloc.len = size;
+-	alloc.fd_flags = O_CLOEXEC | O_RDWR;
+-
+-	ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc);
+-	if (ret < 0) {
+-		LOG(RPI, Error) << "dmaHeap allocation failure for "
+-				<< name;
+-		return {};
+-	}
+-
+-	UniqueFD allocFd(alloc.fd);
+-	ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name);
+-	if (ret < 0) {
+-		LOG(RPI, Error) << "dmaHeap naming failure for "
+-				<< name;
+-		return {};
+-	}
+-
+-	return allocFd;
+-}
+-
+-} /* namespace RPi */
+-
+-} /* namespace libcamera */
+diff --git a/src/libcamera/pipeline/rpi/vc4/meson.build b/src/libcamera/pipeline/rpi/vc4/meson.build
+index cdb049c5..386e2296 100644
+--- a/src/libcamera/pipeline/rpi/vc4/meson.build
++++ b/src/libcamera/pipeline/rpi/vc4/meson.build
+@@ -1,7 +1,6 @@
+ # SPDX-License-Identifier: CC0-1.0
+ 
+ libcamera_sources += files([
+-    'dma_heaps.cpp',
+     'vc4.cpp',
+ ])
+ 
+diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+index 26102ea7..3a42e75e 100644
+--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
++++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+@@ -12,12 +12,11 @@
+ #include <libcamera/formats.h>
+ 
+ #include "libcamera/internal/device_enumerator.h"
++#include "libcamera/internal/dma_heaps.h"
+ 
+ #include "../common/pipeline_base.h"
+ #include "../common/rpi_stream.h"
+ 
+-#include "dma_heaps.h"
+-
+ using namespace std::chrono_literals;
+ 
+ namespace libcamera {
+@@ -87,7 +86,7 @@ public:
+ 	RPi::Device<Isp, 4> isp_;
+ 
+ 	/* DMAHEAP allocation helper. */
+-	RPi::DmaHeap dmaHeap_;
++	DmaHeap dmaHeap_;
+ 	SharedFD lsTable_;
+ 
+ 	struct Config {
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch b/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch
new file mode 100644
index 0000000000..6e5ef9445a
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0003-libcamera-dma_heaps-extend-DmaHeap-class-to-support-.patch
@@ -0,0 +1,169 @@
+From 5df9bc3b2a3d86bcc8504896cc87d7fcb5aea3a4 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:07 +0100
+Subject: [PATCH 03/21] libcamera: dma_heaps: extend DmaHeap class to support
+ system heap
+
+Add an argument to the constructor to specify dma heaps type(s)
+to use. Can be DmaHeapFlag::Cma and/or DmaHeapFlag::System.
+By default DmaHeapFlag::Cma is used. If both DmaHeapFlag::Cma and
+DmaHeapFlag::System are set, CMA heap is tried first.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ include/libcamera/internal/dma_heaps.h | 12 ++++-
+ src/libcamera/dma_heaps.cpp            | 67 ++++++++++++++++++++------
+ 2 files changed, 63 insertions(+), 16 deletions(-)
+
+diff --git a/include/libcamera/internal/dma_heaps.h b/include/libcamera/internal/dma_heaps.h
+index cff8f140..80bf29e7 100644
+--- a/include/libcamera/internal/dma_heaps.h
++++ b/include/libcamera/internal/dma_heaps.h
+@@ -9,6 +9,7 @@
+ 
+ #include <stddef.h>
+ 
++#include <libcamera/base/flags.h>
+ #include <libcamera/base/unique_fd.h>
+ 
+ namespace libcamera {
+@@ -16,7 +17,14 @@ namespace libcamera {
+ class DmaHeap
+ {
+ public:
+-	DmaHeap();
++	enum class DmaHeapFlag {
++		Cma = 1 << 0,
++		System = 1 << 1,
++	};
++
++	using DmaHeapFlags = Flags<DmaHeapFlag>;
++
++	DmaHeap(DmaHeapFlags flags = DmaHeapFlag::Cma);
+ 	~DmaHeap();
+ 	bool isValid() const { return dmaHeapHandle_.isValid(); }
+ 	UniqueFD alloc(const char *name, std::size_t size);
+@@ -25,4 +33,6 @@ private:
+ 	UniqueFD dmaHeapHandle_;
+ };
+ 
++LIBCAMERA_FLAGS_ENABLE_OPERATORS(DmaHeap::DmaHeapFlag)
++
+ } /* namespace libcamera */
+diff --git a/src/libcamera/dma_heaps.cpp b/src/libcamera/dma_heaps.cpp
+index 38ef175a..d0e33ce6 100644
+--- a/src/libcamera/dma_heaps.cpp
++++ b/src/libcamera/dma_heaps.cpp
+@@ -19,9 +19,11 @@
+ 
+ /**
+  * \file dma_heaps.cpp
+- * \brief CMA dma-heap allocator
++ * \brief dma-heap allocator
+  */
+ 
++namespace libcamera {
++
+ /*
+  * /dev/dma_heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma
+  * to only have to worry about importing.
+@@ -29,42 +31,77 @@
+  * Annoyingly, should the cma heap size be specified on the kernel command line
+  * instead of DT, the heap gets named "reserved" instead.
+  */
+-static constexpr std::array<const char *, 2> heapNames = {
+-	"/dev/dma_heap/linux,cma",
+-	"/dev/dma_heap/reserved"
++
++/**
++ * \struct DmaHeapInfo
++ * \brief Tells what type of dma-heap the dma-heap represented by the device node name is
++ * \var DmaHeapInfo::flag
++ * \brief The type of the dma-heap
++ * \var DmaHeapInfo::name
++ * \brief The dma-heap's device node name
++ */
++struct DmaHeapInfo {
++	DmaHeap::DmaHeapFlag flag;
++	const char *name;
+ };
+ 
+-namespace libcamera {
++static constexpr std::array<DmaHeapInfo, 3> heapInfos = {
++	{ /* CMA heap names first */
++	  { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/linux,cma" },
++	  { DmaHeap::DmaHeapFlag::Cma, "/dev/dma_heap/reserved" },
++	  { DmaHeap::DmaHeapFlag::System, "/dev/dma_heap/system" } }
++};
+ 
+ LOG_DEFINE_CATEGORY(DmaHeap)
+ 
+ /**
+  * \class DmaHeap
+- * \brief Helper class for CMA dma-heap allocations
++ * \brief Helper class for dma-heap allocations
+  */
+ 
+ /**
+- * \brief Construct a DmaHeap that owns a CMA dma-heap file descriptor
++ * \enum DmaHeap::DmaHeapFlag
++ * \brief Type of the dma-heap
++ * \var DmaHeap::Cma
++ * \brief Allocate from a CMA dma-heap
++ * \var DmaHeap::System
++ * \brief Allocate from the system dma-heap
++ */
++
++/**
++ * \typedef DmaHeap::DmaHeapFlags
++ * \brief A bitwise combination of DmaHeap::DmaHeapFlag values
++ */
++
++/**
++ * \brief Construct a DmaHeap that owns a CMA or system dma-heap file descriptor
++ * \param [in] flags The type(s) of the dma-heap(s) to allocate from
+  *
+- * Goes through the internal list of possible names of the CMA dma-heap devices
+- * until a CMA dma-heap device is successfully opened. If it fails to open any
+- * dma-heap device, an invalid DmaHeap object is constructed. A valid DmaHeap
+- * object owns a wrapped dma-heap file descriptor.
++ * By default \a flags are set to DmaHeap::DmaHeapFlag::Cma. The constructor goes
++ * through the internal list of possible names of the CMA and system dma-heap devices
++ * until the dma-heap device of the requested type is successfully opened. If more
++ * than one dma-heap type is specified in flags the CMA heap is tried first. If it
++ * fails to open any dma-heap device an invalid DmaHeap object is constructed.
++ * A valid DmaHeap object owns a wrapped dma-heap file descriptor.
+  *
+  * Please check the new DmaHeap object with \ref DmaHeap::isValid before using it.
+  */
+-DmaHeap::DmaHeap()
++DmaHeap::DmaHeap(DmaHeapFlags flags)
+ {
+-	for (const char *name : heapNames) {
+-		int ret = ::open(name, O_RDWR | O_CLOEXEC, 0);
++	for (const auto &info : heapInfos) {
++		if (!(flags & info.flag))
++			continue;
++
++		int ret = ::open(info.name, O_RDWR | O_CLOEXEC, 0);
+ 		if (ret < 0) {
+ 			ret = errno;
+ 			LOG(DmaHeap, Debug)
+-				<< "Failed to open " << name << ": "
++				<< "Failed to open " << info.name << ": "
+ 				<< strerror(ret);
+ 			continue;
+ 		}
+ 
++		LOG(DmaHeap, Debug) << "Using " << info.name;
+ 		dmaHeapHandle_ = UniqueFD(ret);
+ 		break;
+ 	}
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch b/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch
new file mode 100644
index 0000000000..48f10aa47a
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0004-libcamera-internal-Move-SharedMemObject-class-to-a-c.patch
@@ -0,0 +1,69 @@
+From a6777760a2121f02808baecea504ac0e242f860b Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:08 +0100
+Subject: [PATCH 04/21] libcamera: internal: Move SharedMemObject class to a
+ common directory
+
+Move SharedMemObject class out of RPi namespace and put it into
+include/libcamera/internal so that everyone could use it.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ include/libcamera/internal/meson.build                      | 1 +
+ .../libcamera/internal}/shared_mem_object.h                 | 6 +-----
+ 2 files changed, 2 insertions(+), 5 deletions(-)
+ rename {src/libcamera/pipeline/rpi/common => include/libcamera/internal}/shared_mem_object.h (97%)
+
+diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
+index 33eb0fb3..5807dfd9 100644
+--- a/include/libcamera/internal/meson.build
++++ b/include/libcamera/internal/meson.build
+@@ -39,6 +39,7 @@ libcamera_internal_headers = files([
+     'process.h',
+     'pub_key.h',
+     'request.h',
++    'shared_mem_object.h',
+     'source_paths.h',
+     'sysfs.h',
+     'v4l2_device.h',
+diff --git a/src/libcamera/pipeline/rpi/common/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
+similarity index 97%
+rename from src/libcamera/pipeline/rpi/common/shared_mem_object.h
+rename to include/libcamera/internal/shared_mem_object.h
+index aa56c220..98636b44 100644
+--- a/src/libcamera/pipeline/rpi/common/shared_mem_object.h
++++ b/include/libcamera/internal/shared_mem_object.h
+@@ -6,8 +6,8 @@
+  */
+ #pragma once
+ 
+-#include <cstddef>
+ #include <fcntl.h>
++#include <stddef.h>
+ #include <string>
+ #include <sys/mman.h>
+ #include <sys/stat.h>
+@@ -19,8 +19,6 @@
+ 
+ namespace libcamera {
+ 
+-namespace RPi {
+-
+ template<class T>
+ class SharedMemObject
+ {
+@@ -123,6 +121,4 @@ private:
+ 	T *obj_;
+ };
+ 
+-} /* namespace RPi */
+-
+ } /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch b/users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch
new file mode 100644
index 0000000000..d2143febf7
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0005-libcamera-shared_mem_object-reorganize-the-code-and-.patch
@@ -0,0 +1,403 @@
+From f94af21adc1889706127d07c5425f44c9cec9a95 Mon Sep 17 00:00:00 2001
+From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
+Date: Mon, 11 Mar 2024 15:15:09 +0100
+Subject: [PATCH 05/21] libcamera: shared_mem_object: reorganize the code and
+ document the SharedMemObject class
+
+Split the parts which doesn't otherwise depend on the type T or
+arguments Args out of the SharedMemObject class into a new
+SharedMem class.
+
+Doxygen documentation by Dennis Bonke and Andrei Konovalov.
+
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ .../libcamera/internal/shared_mem_object.h    | 101 ++++++----
+ src/libcamera/meson.build                     |   1 +
+ src/libcamera/shared_mem_object.cpp           | 190 ++++++++++++++++++
+ 3 files changed, 253 insertions(+), 39 deletions(-)
+ create mode 100644 src/libcamera/shared_mem_object.cpp
+
+diff --git a/include/libcamera/internal/shared_mem_object.h b/include/libcamera/internal/shared_mem_object.h
+index 98636b44..43b07c9d 100644
+--- a/include/libcamera/internal/shared_mem_object.h
++++ b/include/libcamera/internal/shared_mem_object.h
+@@ -6,12 +6,9 @@
+  */
+ #pragma once
+ 
+-#include <fcntl.h>
+ #include <stddef.h>
+ #include <string>
+ #include <sys/mman.h>
+-#include <sys/stat.h>
+-#include <unistd.h>
+ #include <utility>
+ 
+ #include <libcamera/base/class.h>
+@@ -19,58 +16,92 @@
+ 
+ namespace libcamera {
+ 
++class SharedMem
++{
++public:
++	SharedMem()
++		: mem_(nullptr)
++	{
++	}
++
++	SharedMem(const std::string &name, std::size_t size);
++
++	SharedMem(SharedMem &&rhs)
++	{
++		this->name_ = std::move(rhs.name_);
++		this->fd_ = std::move(rhs.fd_);
++		this->mem_ = rhs.mem_;
++		rhs.mem_ = nullptr;
++	}
++
++	virtual ~SharedMem()
++	{
++		if (mem_)
++			munmap(mem_, size_);
++	}
++
++	/* Make SharedMem non-copyable for now. */
++	LIBCAMERA_DISABLE_COPY(SharedMem)
++
++	SharedMem &operator=(SharedMem &&rhs)
++	{
++		this->name_ = std::move(rhs.name_);
++		this->fd_ = std::move(rhs.fd_);
++		this->mem_ = rhs.mem_;
++		rhs.mem_ = nullptr;
++		return *this;
++	}
++
++	const SharedFD &fd() const
++	{
++		return fd_;
++	}
++
++	void *mem() const
++	{
++		return mem_;
++	}
++
++private:
++	std::string name_;
++	SharedFD fd_;
++	size_t size_;
++protected:
++	void *mem_;
++};
++
+ template<class T>
+-class SharedMemObject
++class SharedMemObject : public SharedMem
+ {
+ public:
+ 	static constexpr std::size_t SIZE = sizeof(T);
+ 
+ 	SharedMemObject()
+-		: obj_(nullptr)
++		: SharedMem(), obj_(nullptr)
+ 	{
+ 	}
+ 
+ 	template<class... Args>
+ 	SharedMemObject(const std::string &name, Args &&...args)
+-		: name_(name), obj_(nullptr)
++		: SharedMem(name, SIZE), obj_(nullptr)
+ 	{
+-		void *mem;
+-		int ret;
+-
+-		ret = memfd_create(name_.c_str(), MFD_CLOEXEC);
+-		if (ret < 0)
+-			return;
+-
+-		fd_ = SharedFD(std::move(ret));
+-		if (!fd_.isValid())
+-			return;
+-
+-		ret = ftruncate(fd_.get(), SIZE);
+-		if (ret < 0)
++		if (mem_ == nullptr)
+ 			return;
+ 
+-		mem = mmap(nullptr, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+-			   fd_.get(), 0);
+-		if (mem == MAP_FAILED)
+-			return;
+-
+-		obj_ = new (mem) T(std::forward<Args>(args)...);
++		obj_ = new (mem_) T(std::forward<Args>(args)...);
+ 	}
+ 
+ 	SharedMemObject(SharedMemObject<T> &&rhs)
++		: SharedMem(std::move(rhs))
+ 	{
+-		this->name_ = std::move(rhs.name_);
+-		this->fd_ = std::move(rhs.fd_);
+ 		this->obj_ = rhs.obj_;
+ 		rhs.obj_ = nullptr;
+ 	}
+ 
+ 	~SharedMemObject()
+ 	{
+-		if (obj_) {
++		if (obj_)
+ 			obj_->~T();
+-			munmap(obj_, SIZE);
+-		}
+ 	}
+ 
+ 	/* Make SharedMemObject non-copyable for now. */
+@@ -78,8 +109,7 @@ public:
+ 
+ 	SharedMemObject<T> &operator=(SharedMemObject<T> &&rhs)
+ 	{
+-		this->name_ = std::move(rhs.name_);
+-		this->fd_ = std::move(rhs.fd_);
++		SharedMem::operator=(std::move(rhs));
+ 		this->obj_ = rhs.obj_;
+ 		rhs.obj_ = nullptr;
+ 		return *this;
+@@ -105,19 +135,12 @@ public:
+ 		return *obj_;
+ 	}
+ 
+-	const SharedFD &fd() const
+-	{
+-		return fd_;
+-	}
+-
+ 	explicit operator bool() const
+ 	{
+ 		return !!obj_;
+ 	}
+ 
+ private:
+-	std::string name_;
+-	SharedFD fd_;
+ 	T *obj_;
+ };
+ 
+diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
+index 3c5e43df..94a95ae3 100644
+--- a/src/libcamera/meson.build
++++ b/src/libcamera/meson.build
+@@ -41,6 +41,7 @@ libcamera_sources = files([
+     'process.cpp',
+     'pub_key.cpp',
+     'request.cpp',
++    'shared_mem_object.cpp',
+     'source_paths.cpp',
+     'stream.cpp',
+     'sysfs.cpp',
+diff --git a/src/libcamera/shared_mem_object.cpp b/src/libcamera/shared_mem_object.cpp
+new file mode 100644
+index 00000000..44fe74c2
+--- /dev/null
++++ b/src/libcamera/shared_mem_object.cpp
+@@ -0,0 +1,190 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Raspberry Pi Ltd
++ *
++ * shared_mem_object.cpp - Helper class for shared memory allocations
++ */
++
++#include "libcamera/internal/shared_mem_object.h"
++
++#include <sys/types.h>
++#include <unistd.h>
++
++/**
++ * \file shared_mem_object.cpp
++ * \brief Helper class for shared memory allocations
++ */
++
++namespace libcamera {
++
++/**
++ * \class SharedMem
++ * \brief Helper class for allocating shared memory
++ *
++ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries.
++ *
++ * SharedMem allocates the shared memory of the given size and maps it.
++ * To check that the shared memory was allocated and mapped successfully, one
++ * needs to verify that the pointer to the shared memory returned by SharedMem::mem()
++ * is not nullptr.
++ *
++ * To access the shared memory from another process the SharedFD should be passed
++ * to that process, and then the shared memory should be mapped into that process
++ * address space by calling mmap().
++ *
++ * A single memfd is created for every SharedMem. If there is a need to allocate
++ * a large number of objects in shared memory, these objects should be grouped
++ * together and use the shared memory allocated by a single SharedMem object if
++ * possible. This will help to minimize the number of created memfd's.
++ */
++
++/**
++ * \fn SharedMem::SharedMem(const std::string &name, std::size_t size)
++ * \brief Constructor for the SharedMem
++ * \param[in] name Name of the SharedMem
++ * \param[in] size Size of the shared memory to allocate and map
++ */
++
++/**
++ * \fn SharedMem::SharedMem(SharedMem &&rhs)
++ * \brief Move constructor for SharedMem
++ * \param[in] rhs The object to move
++ */
++
++/**
++ * \fn SharedMem::~SharedMem()
++ * \brief SharedMem destructor
++ *
++ * Unmaps the allocated shared memory. Decrements the shared memory descriptor use
++ * count.
++ */
++
++/**
++ * \fn SharedMem &SharedMem::operator=(SharedMem &&rhs)
++ * \brief Move constructor for SharedMem
++ * \param[in] rhs The object to move
++ */
++
++/**
++ * \fn const SharedFD &SharedMem::fd() const
++ * \brief Gets the file descriptor for the underlying shared memory
++ * \return The file descriptor
++ */
++
++/**
++ * \fn void *SharedMem::mem() const
++ * \brief Gets the pointer to the underlying shared memory
++ * \return The pointer to the shared memory
++ */
++
++SharedMem::SharedMem(const std::string &name, std::size_t size)
++	: name_(name), size_(size), mem_(nullptr)
++{
++	int fd = memfd_create(name_.c_str(), MFD_CLOEXEC);
++	if (fd < 0)
++		return;
++
++	fd_ = SharedFD(std::move(fd));
++	if (!fd_.isValid())
++		return;
++
++	if (ftruncate(fd_.get(), size_) < 0)
++		return;
++
++	mem_ = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED,
++		    fd_.get(), 0);
++	if (mem_ == MAP_FAILED)
++		mem_ = nullptr;
++}
++
++/**
++ * \var SharedMem::mem_
++ * \brief Pointer to the shared memory allocated
++ */
++
++/**
++ * \class SharedMemObject
++ * \brief Helper class for allocating objects in shared memory
++ *
++ * Memory is allocated and exposed as a SharedFD for use across IPC boundaries.
++ *
++ * Given the type of the object to be created in shared memory and the arguments
++ * to pass to this object's constructor, SharedMemObject allocates the shared memory
++ * of the size of the object and constructs the object in this memory. To ensure
++ * that the SharedMemObject was created successfully, one needs to verify that the
++ * overloaded bool() operator returns true. The object created in the shared memory
++ * can be accessed using the SharedMemObject::operator*() indirection operator. Its
++ * members can be accessed with the SharedMemObject::operator->() member of pointer
++ * operator.
++ *
++ * To access the object from another process the SharedFD should be passed to that
++ * process, and the shared memory should be mapped by calling mmap().
++ *
++ * A single memfd is created for every SharedMemObject. If there is a need to allocate
++ * a large number of objects in shared memory, these objects should be grouped into a
++ * single large object to keep the number of created memfd's reasonably small.
++ */
++
++/**
++ * \var SharedMemObject::SIZE
++ * \brief The size of the object that is going to be stored here
++ */
++
++/**
++ * \fn SharedMemObject< T >::SharedMemObject(const std::string &name, Args &&...args)
++ * \brief Constructor for the SharedMemObject
++ * \param[in] name Name of the SharedMemObject
++ * \param[in] args Args to pass to the constructor of the object in shared memory
++ */
++
++/**
++ * \fn SharedMemObject::SharedMemObject(SharedMemObject<T> &&rhs)
++ * \brief Move constructor for SharedMemObject
++ * \param[in] rhs The object to move
++ */
++
++/**
++ * \fn SharedMemObject::~SharedMemObject()
++ * \brief SharedMemObject destructor
++ *
++ * Destroys the object created in the shared memory and then unmaps the shared memory.
++ * Decrements the shared memory descriptor use count.
++ */
++
++/**
++ * \fn SharedMemObject::operator=(SharedMemObject<T> &&rhs)
++ * \brief Operator= for SharedMemObject
++ * \param[in] rhs The SharedMemObject object to take the data from
++ */
++
++/**
++ * \fn SharedMemObject::operator->()
++ * \brief Operator-> for SharedMemObject
++ * \return The pointer to the object
++ */
++
++/**
++ * \fn const T *SharedMemObject::operator->() const
++ * \brief Operator-> for SharedMemObject
++ * \return The pointer to the const object
++ */
++
++/**
++ * \fn SharedMemObject::operator*()
++ * \brief Operator* for SharedMemObject
++ * \return The reference to the object
++ */
++
++/**
++ * \fn const T &SharedMemObject::operator*() const
++ * \brief Operator* for SharedMemObject
++ * \return Const reference to the object
++ */
++
++/**
++ * \fn SharedMemObject::operator bool()
++ * \brief Operator bool() for SharedMemObject
++ * \return True if the object was created OK in the shared memory, false otherwise
++ */
++
++} // namespace libcamera
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch
new file mode 100644
index 0000000000..9f80b69f16
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0006-libcamera-software_isp-Add-SwStatsCpu-class.patch
@@ -0,0 +1,523 @@
+From 4259b01930333c6666a185d923e6e68ec915a4fd Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:10 +0100
+Subject: [PATCH 06/21] libcamera: software_isp: Add SwStatsCpu class
+
+Add a CPU based SwStats implementation for SoftwareISP / SoftIPA use.
+
+This implementation offers a configure function + functions to gather
+statistics on a line by line basis. This allows CPU based software
+debayering to call into interlace debayering and statistics gathering
+on a line by line bases while the input data is still hot in the cache.
+
+This implementation also allows specifying a window over which to gather
+statistics instead of processing the whole frame.
+
+Doxygen documentation by Dennis Bonke.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Co-developed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Co-developed-by: Marttico <g.martti@gmail.com>
+Signed-off-by: Marttico <g.martti@gmail.com>
+Co-developed-by: Toon Langendam <t.langendam@gmail.com>
+Signed-off-by: Toon Langendam <t.langendam@gmail.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ include/libcamera/internal/meson.build        |   1 +
+ .../internal/software_isp/meson.build         |   5 +
+ .../internal/software_isp/swisp_stats.h       |  38 ++++
+ src/libcamera/meson.build                     |   1 +
+ src/libcamera/software_isp/meson.build        |  12 +
+ src/libcamera/software_isp/swstats_cpu.cpp    | 208 ++++++++++++++++++
+ src/libcamera/software_isp/swstats_cpu.h      | 159 +++++++++++++
+ 7 files changed, 424 insertions(+)
+ create mode 100644 include/libcamera/internal/software_isp/meson.build
+ create mode 100644 include/libcamera/internal/software_isp/swisp_stats.h
+ create mode 100644 src/libcamera/software_isp/meson.build
+ create mode 100644 src/libcamera/software_isp/swstats_cpu.cpp
+ create mode 100644 src/libcamera/software_isp/swstats_cpu.h
+
+diff --git a/include/libcamera/internal/meson.build b/include/libcamera/internal/meson.build
+index 5807dfd9..160fdc37 100644
+--- a/include/libcamera/internal/meson.build
++++ b/include/libcamera/internal/meson.build
+@@ -50,3 +50,4 @@ libcamera_internal_headers = files([
+ ])
+ 
+ subdir('converter')
++subdir('software_isp')
+diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
+new file mode 100644
+index 00000000..66c9c3fb
+--- /dev/null
++++ b/include/libcamera/internal/software_isp/meson.build
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: CC0-1.0
++
++libcamera_internal_headers += files([
++    'swisp_stats.h',
++])
+diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h
+new file mode 100644
+index 00000000..afe42c9a
+--- /dev/null
++++ b/include/libcamera/internal/software_isp/swisp_stats.h
+@@ -0,0 +1,38 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ *
++ * swisp_stats.h - Statistics data format used by the software ISP and software IPA
++ */
++
++#pragma once
++
++namespace libcamera {
++
++/**
++ * \brief Struct that holds the statistics for the Software ISP.
++ */
++struct SwIspStats {
++	/**
++	 * \brief Holds the sum of all sampled red pixels.
++	 */
++	unsigned long sumR_;
++	/**
++	 * \brief Holds the sum of all sampled green pixels.
++	 */
++	unsigned long sumG_;
++	/**
++	 * \brief Holds the sum of all sampled blue pixels.
++	 */
++	unsigned long sumB_;
++	/**
++	 * \brief Number of bins in the yHistogram.
++	 */
++	static constexpr unsigned int kYHistogramSize = 16;
++	/**
++	 * \brief A histogram of luminance values.
++	 */
++	std::array<unsigned int, kYHistogramSize> yHistogram;
++};
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/meson.build b/src/libcamera/meson.build
+index 94a95ae3..91e4cc60 100644
+--- a/src/libcamera/meson.build
++++ b/src/libcamera/meson.build
+@@ -71,6 +71,7 @@ subdir('converter')
+ subdir('ipa')
+ subdir('pipeline')
+ subdir('proxy')
++subdir('software_isp')
+ 
+ null_dep = dependency('', required : false)
+ 
+diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
+new file mode 100644
+index 00000000..fcfff74a
+--- /dev/null
++++ b/src/libcamera/software_isp/meson.build
+@@ -0,0 +1,12 @@
++# SPDX-License-Identifier: CC0-1.0
++
++softisp_enabled = pipelines.contains('simple')
++summary({'SoftISP support' : softisp_enabled}, section : 'Configuration')
++
++if not (softisp_enabled)
++    subdir_done()
++endif
++
++libcamera_sources += files([
++    'swstats_cpu.cpp',
++])
+diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
+new file mode 100644
+index 00000000..448d0e4c
+--- /dev/null
++++ b/src/libcamera/software_isp/swstats_cpu.cpp
+@@ -0,0 +1,208 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * swstats_cpu.cpp - CPU based software statistics implementation
++ */
++
++#include "swstats_cpu.h"
++
++#include <libcamera/base/log.h>
++
++#include <libcamera/stream.h>
++
++#include "libcamera/internal/bayer_format.h"
++
++namespace libcamera {
++
++/**
++ * \class SwStatsCpu
++ * \brief Class for gathering statistics on the CPU
++ *
++ * CPU based software ISP statistics implementation.
++ *
++ * This class offers a configure function + functions to gather statistics
++ * on a line by line basis. This allows CPU based software debayering to
++ * interlace debayering and statistics gathering on a line by line basis
++ * while the input data is still hot in the cache.
++ *
++ * It is also possible to specify a window over which to gather
++ * statistics instead of processing the whole frame.
++ */
++
++LOG_DEFINE_CATEGORY(SwStatsCpu)
++
++SwStatsCpu::SwStatsCpu()
++{
++	sharedStats_ = SharedMemObject<SwIspStats>("softIsp_stats");
++	if (!sharedStats_.fd().isValid())
++		LOG(SwStatsCpu, Error)
++			<< "Failed to create shared memory for statistics";
++}
++
++static const unsigned int kRedYMul = 77; /* 0.299 * 256 */
++static const unsigned int kGreenYMul = 150; /* 0.587 * 256 */
++static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */
++
++#define SWSTATS_START_LINE_STATS(pixel_t) \
++	pixel_t r, g, g2, b;              \
++	unsigned int yVal;                \
++                                          \
++	unsigned int sumR = 0;            \
++	unsigned int sumG = 0;            \
++	unsigned int sumB = 0;
++
++#define SWSTATS_ACCUMULATE_LINE_STATS(div) \
++	sumR += r;                         \
++	sumG += g;                         \
++	sumB += b;                         \
++                                           \
++	yVal = r * kRedYMul;               \
++	yVal += g * kGreenYMul;            \
++	yVal += b * kBlueYMul;             \
++	stats_.yHistogram[yVal * SwIspStats::kYHistogramSize / (256 * 256 * (div))]++;
++
++#define SWSTATS_FINISH_LINE_STATS() \
++	stats_.sumR_ += sumR;       \
++	stats_.sumG_ += sumG;       \
++	stats_.sumB_ += sumB;
++
++void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
++{
++	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
++	const uint8_t *src1 = src[2] + window_.x * 5 / 4;
++	const int widthInBytes = window_.width * 5 / 4;
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	SWSTATS_START_LINE_STATS(uint8_t)
++
++	/* x += 5 sample every other 2x2 block */
++	for (int x = 0; x < widthInBytes; x += 5) {
++		/* BGGR */
++		b = src0[x];
++		g = src0[x + 1];
++		g2 = src1[x];
++		r = src1[x + 1];
++		g = (g + g2) / 2;
++		/* Data is already 8 bits, divide by 1 */
++		SWSTATS_ACCUMULATE_LINE_STATS(1)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
++void SwStatsCpu::statsGBRG10PLine0(const uint8_t *src[])
++{
++	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
++	const uint8_t *src1 = src[2] + window_.x * 5 / 4;
++	const int widthInBytes = window_.width * 5 / 4;
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	SWSTATS_START_LINE_STATS(uint8_t)
++
++	/* x += 5 sample every other 2x2 block */
++	for (int x = 0; x < widthInBytes; x += 5) {
++		/* GBRG */
++		g = src0[x];
++		b = src0[x + 1];
++		r = src1[x];
++		g2 = src1[x + 1];
++		g = (g + g2) / 2;
++		/* Data is already 8 bits, divide by 1 */
++		SWSTATS_ACCUMULATE_LINE_STATS(1)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
++/**
++ * \brief Reset state to start statistics gathering for a new frame.
++ *
++ * This may only be called after a successful setWindow() call.
++ */
++void SwStatsCpu::startFrame(void)
++{
++	stats_.sumR_ = 0;
++	stats_.sumB_ = 0;
++	stats_.sumG_ = 0;
++	stats_.yHistogram.fill(0);
++}
++
++/**
++ * \brief Finish statistics calculation for the current frame.
++ *
++ * This may only be called after a successful setWindow() call.
++ */
++void SwStatsCpu::finishFrame(void)
++{
++	*sharedStats_ = stats_;
++	statsReady.emit(0);
++}
++
++/**
++ * \brief Configure the statistics object for the passed in input format.
++ * \param[in] inputCfg The input format
++ *
++ * \return 0 on success, a negative errno value on failure
++ */
++int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
++{
++	BayerFormat bayerFormat =
++		BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
++
++	if (bayerFormat.bitDepth == 10 &&
++	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
++		patternSize_.height = 2;
++		patternSize_.width = 4; /* 5 bytes per *4* pixels */
++		/* Skip every 3th and 4th line, sample every other 2x2 block */
++		ySkipMask_ = 0x02;
++		xShift_ = 0;
++
++		switch (bayerFormat.order) {
++		case BayerFormat::BGGR:
++		case BayerFormat::GRBG:
++			stats0_ = &SwStatsCpu::statsBGGR10PLine0;
++			swapLines_ = bayerFormat.order == BayerFormat::GRBG;
++			return 0;
++		case BayerFormat::GBRG:
++		case BayerFormat::RGGB:
++			stats0_ = &SwStatsCpu::statsGBRG10PLine0;
++			swapLines_ = bayerFormat.order == BayerFormat::RGGB;
++			return 0;
++		default:
++			break;
++		}
++	}
++
++	LOG(SwStatsCpu, Info)
++		<< "Unsupported input format " << inputCfg.pixelFormat.toString();
++	return -EINVAL;
++}
++
++/**
++ * \brief Specify window coordinates over which to gather statistics.
++ * \param[in] window The window object.
++ */
++void SwStatsCpu::setWindow(Rectangle window)
++{
++	window_ = window;
++
++	window_.x &= ~(patternSize_.width - 1);
++	window_.x += xShift_;
++	window_.y &= ~(patternSize_.height - 1);
++
++	/* width_ - xShift_ to make sure the window fits */
++	window_.width -= xShift_;
++	window_.width &= ~(patternSize_.width - 1);
++	window_.height &= ~(patternSize_.height - 1);
++}
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
+new file mode 100644
+index 00000000..0ac9ae71
+--- /dev/null
++++ b/src/libcamera/software_isp/swstats_cpu.h
+@@ -0,0 +1,159 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * swstats_cpu.h - CPU based software statistics implementation
++ */
++
++#pragma once
++
++#include <stdint.h>
++
++#include <libcamera/base/signal.h>
++
++#include <libcamera/geometry.h>
++
++#include "libcamera/internal/shared_mem_object.h"
++#include "libcamera/internal/software_isp/swisp_stats.h"
++
++namespace libcamera {
++
++class PixelFormat;
++struct StreamConfiguration;
++
++class SwStatsCpu
++{
++public:
++	SwStatsCpu();
++	~SwStatsCpu() = default;
++
++	/**
++	 * \brief Gets whether the statistics object is valid.
++	 *
++	 * \return true if it's valid, false otherwise
++	 */
++	bool isValid() const { return sharedStats_.fd().isValid(); }
++
++	/**
++	 * \brief Get the file descriptor for the statistics.
++	 *
++	 * \return the file descriptor
++	 */
++	const SharedFD &getStatsFD() { return sharedStats_.fd(); }
++
++	/**
++	 * \brief Get the pattern size.
++	 *
++	 * For some input-formats, e.g. Bayer data, processing is done multiple lines
++	 * and/or columns at a time. Get width and height at which the (bayer) pattern
++	 * repeats. Window values are rounded down to a multiple of this and the height
++	 * also indicates if processLine2() should be called or not.
++	 * This may only be called after a successful configure() call.
++	 *
++	 * \return the pattern size
++	 */
++	const Size &patternSize() { return patternSize_; }
++
++	int configure(const StreamConfiguration &inputCfg);
++	void setWindow(Rectangle window);
++	void startFrame();
++	void finishFrame();
++
++	/**
++	 * \brief Process line 0.
++	 * \param[in] y The y coordinate.
++	 * \param[in] src The input data.
++	 *
++	 * This function processes line 0 for input formats with patternSize height == 1.
++	 * It'll process line 0 and 1 for input formats with patternSize height >= 2.
++	 * This function may only be called after a successful setWindow() call.
++	 */
++	void processLine0(unsigned int y, const uint8_t *src[])
++	{
++		if ((y & ySkipMask_) || y < (unsigned int)window_.y ||
++		    y >= (window_.y + window_.height))
++			return;
++
++		(this->*stats0_)(src);
++	}
++
++	/**
++	 * \brief Process line 2 and 3.
++	 * \param[in] y The y coordinate.
++	 * \param[in] src The input data.
++	 *
++	 * This function processes line 2 and 3 for input formats with patternSize height == 4.
++	 * This function may only be called after a successful setWindow() call.
++	 */
++	void processLine2(unsigned int y, const uint8_t *src[])
++	{
++		if ((y & ySkipMask_) || y < (unsigned int)window_.y ||
++		    y >= (window_.y + window_.height))
++			return;
++
++		(this->*stats2_)(src);
++	}
++
++	/**
++	 * \brief Signals that the statistics are ready.
++	 *
++	 * The int parameter isn't actually used.
++	 */
++	Signal<int> statsReady;
++
++private:
++	/**
++	 * \brief Called when there is data to get statistics from.
++	 * \param[in] src The input data
++	 *
++	 * These functions take an array of (patternSize_.height + 1) src
++	 * pointers each pointing to a line in the source image. The middle
++	 * element of the array will point to the actual line being processed.
++	 * Earlier element(s) will point to the previous line(s) and later
++	 * element(s) to the next line(s).
++	 *
++	 * See the documentation of DebayerCpu::debayerFn for more details.
++	 */
++	using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
++
++	void statsBGGR10PLine0(const uint8_t *src[]);
++	void statsGBRG10PLine0(const uint8_t *src[]);
++
++	/* Variables set by configure(), used every line */
++	statsProcessFn stats0_;
++	statsProcessFn stats2_;
++	bool swapLines_;
++
++	/**
++	 * \brief Skip lines where this bitmask is set in y.
++	 */
++	unsigned int ySkipMask_;
++
++	/**
++	 * \brief Statistics window, set by setWindow(), used ever line.
++	 */
++	Rectangle window_;
++
++	/**
++	 * \brief The size of the bayer pattern.
++	 *
++	 * Valid sizes are: 2x2, 4x2 or 4x4.
++	 */
++	Size patternSize_;
++
++	/**
++	 * \brief The offset of x, applied to window_.x for bayer variants.
++	 *
++	 * This can either be 0 or 1.
++	 */
++	unsigned int xShift_;
++
++	SharedMemObject<SwIspStats> sharedStats_;
++	SwIspStats stats_;
++};
++
++} /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch b/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch
new file mode 100644
index 0000000000..7c71709896
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0007-libcamera-software_isp-Add-Debayer-base-class.patch
@@ -0,0 +1,255 @@
+From 25e6893e46bd2174f6913eea79817988d9280706 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:11 +0100
+Subject: [PATCH 07/21] libcamera: software_isp: Add Debayer base class
+
+Add a base class for debayer implementations. This is intended to be
+suitable for both GPU (or otherwise) accelerated debayer implementations
+as well as CPU based debayering.
+
+Doxygen documentation by Dennis Bonke.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ .../internal/software_isp/debayer_params.h    |  48 ++++++++
+ .../internal/software_isp/meson.build         |   1 +
+ src/libcamera/software_isp/debayer.cpp        |  29 +++++
+ src/libcamera/software_isp/debayer.h          | 104 ++++++++++++++++++
+ src/libcamera/software_isp/meson.build        |   1 +
+ 5 files changed, 183 insertions(+)
+ create mode 100644 include/libcamera/internal/software_isp/debayer_params.h
+ create mode 100644 src/libcamera/software_isp/debayer.cpp
+ create mode 100644 src/libcamera/software_isp/debayer.h
+
+diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
+new file mode 100644
+index 00000000..98965fa1
+--- /dev/null
++++ b/include/libcamera/internal/software_isp/debayer_params.h
+@@ -0,0 +1,48 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer_params.h - DebayerParams header
++ */
++
++#pragma once
++
++namespace libcamera {
++
++/**
++ * \brief Struct to hold the debayer parameters.
++ */
++struct DebayerParams {
++	/**
++	 * \brief const value for 1.0 gain
++	 */
++	static constexpr unsigned int kGain10 = 256;
++
++	/**
++	 * \brief Red Gain
++	 *
++	 * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
++	 */
++	unsigned int gainR;
++	/**
++	 * \brief Green Gain
++	 *
++	 * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
++	 */
++	unsigned int gainG;
++	/**
++	 * \brief Blue Gain
++	 *
++	 * 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
++	 */
++	unsigned int gainB;
++	/**
++	 * \brief Gamma correction, 1.0 is no correction
++	 */
++	float gamma;
++};
++
++} /* namespace libcamera */
+diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
+index 66c9c3fb..a620e16d 100644
+--- a/include/libcamera/internal/software_isp/meson.build
++++ b/include/libcamera/internal/software_isp/meson.build
+@@ -1,5 +1,6 @@
+ # SPDX-License-Identifier: CC0-1.0
+ 
+ libcamera_internal_headers += files([
++    'debayer_params.h',
+     'swisp_stats.h',
+ ])
+diff --git a/src/libcamera/software_isp/debayer.cpp b/src/libcamera/software_isp/debayer.cpp
+new file mode 100644
+index 00000000..64f0b5a0
+--- /dev/null
++++ b/src/libcamera/software_isp/debayer.cpp
+@@ -0,0 +1,29 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer.cpp - debayer base class
++ */
++
++#include "debayer.h"
++
++namespace libcamera {
++
++/**
++ * \class Debayer
++ * \brief Base debayering class
++ *
++ * Base class that provides functions for setting up the debayering process.
++ */
++
++LOG_DEFINE_CATEGORY(Debayer)
++
++Debayer::~Debayer()
++{
++}
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/debayer.h b/src/libcamera/software_isp/debayer.h
+new file mode 100644
+index 00000000..8880ff99
+--- /dev/null
++++ b/src/libcamera/software_isp/debayer.h
+@@ -0,0 +1,104 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer.h - debayering base class
++ */
++
++#pragma once
++
++#include <stdint.h>
++
++#include <libcamera/base/log.h>
++#include <libcamera/base/signal.h>
++
++#include <libcamera/geometry.h>
++#include <libcamera/stream.h>
++
++#include "libcamera/internal/software_isp/debayer_params.h"
++
++namespace libcamera {
++
++class FrameBuffer;
++
++LOG_DECLARE_CATEGORY(Debayer)
++
++class Debayer
++{
++public:
++	virtual ~Debayer() = 0;
++
++	/**
++	 * \brief Configure the debayer object according to the passed in parameters.
++	 * \param[in] inputCfg The input configuration.
++	 * \param[in] outputCfgs The output configurations.
++	 *
++	 * \return 0 on success, a negative errno on failure.
++	 */
++	virtual int configure(const StreamConfiguration &inputCfg,
++			      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs) = 0;
++
++	/**
++	 * \brief Get the width and height at which the bayer pattern repeats.
++	 * \param[in] inputFormat The input format.
++	 *
++	 * Valid sizes are: 2x2, 4x2 or 4x4.
++	 *
++	 * \return pattern size or an empty size for unsupported inputFormats.
++	 */
++	virtual Size patternSize(PixelFormat inputFormat) = 0;
++
++	/**
++	 * \brief Get the supported output formats.
++	 * \param[in] inputFormat The input format.
++	 *
++	 * \return all supported output formats or an empty vector if there are none.
++	 */
++	virtual std::vector<PixelFormat> formats(PixelFormat inputFormat) = 0;
++
++	/**
++	 * \brief Get the stride and the frame size.
++	 * \param[in] outputFormat The output format.
++	 * \param[in] size The output size.
++	 *
++	 * \return a tuple of the stride and the frame size, or a tuple with 0,0 if there is no valid output config.
++	 */
++	virtual std::tuple<unsigned int, unsigned int>
++	strideAndFrameSize(const PixelFormat &outputFormat, const Size &size) = 0;
++
++	/**
++	 * \brief Process the bayer data into the requested format.
++	 * \param[in] input The input buffer.
++	 * \param[in] output The output buffer.
++	 * \param[in] params The parameters to be used in debayering.
++	 *
++	 * \note DebayerParams is passed by value deliberately so that a copy is passed
++	 * when this is run in another thread by invokeMethod().
++	 */
++	virtual void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params) = 0;
++
++	/**
++	 * \brief Get the supported output sizes for the given input format and size.
++	 * \param[in] inputFormat The input format.
++	 * \param[in] inputSize The input size.
++	 *
++	 * \return The valid size ranges or an empty range if there are none.
++	 */
++	virtual SizeRange sizes(PixelFormat inputFormat, const Size &inputSize) = 0;
++
++	/**
++	 * \brief Signals when the input buffer is ready.
++	 */
++	Signal<FrameBuffer *> inputBufferReady;
++
++	/**
++	 * \brief Signals when the output buffer is ready.
++	 */
++	Signal<FrameBuffer *> outputBufferReady;
++};
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
+index fcfff74a..62095f61 100644
+--- a/src/libcamera/software_isp/meson.build
++++ b/src/libcamera/software_isp/meson.build
+@@ -8,5 +8,6 @@ if not (softisp_enabled)
+ endif
+ 
+ libcamera_sources += files([
++    'debayer.cpp',
+     'swstats_cpu.cpp',
+ ])
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch
new file mode 100644
index 0000000000..f549769f2f
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0008-libcamera-software_isp-Add-DebayerCpu-class.patch
@@ -0,0 +1,825 @@
+From 5f57a52ea1054cac73344d83ff605cba0df0d279 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:12 +0100
+Subject: [PATCH 08/21] libcamera: software_isp: Add DebayerCpu class
+
+Add CPU based debayering implementation. This initial implementation
+only supports debayering packed 10 bits per pixel bayer data in
+the 4 standard bayer orders.
+
+Doxygen documentation by Dennis Bonke.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Co-developed-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Co-developed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ src/libcamera/software_isp/debayer_cpu.cpp | 626 +++++++++++++++++++++
+ src/libcamera/software_isp/debayer_cpu.h   | 143 +++++
+ src/libcamera/software_isp/meson.build     |   1 +
+ 3 files changed, 770 insertions(+)
+ create mode 100644 src/libcamera/software_isp/debayer_cpu.cpp
+ create mode 100644 src/libcamera/software_isp/debayer_cpu.h
+
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+new file mode 100644
+index 00000000..f932362c
+--- /dev/null
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -0,0 +1,626 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer_cpu.cpp - CPU based debayering class
++ */
++
++#include "debayer_cpu.h"
++
++#include <math.h>
++#include <stdlib.h>
++#include <time.h>
++
++#include <libcamera/formats.h>
++
++#include "libcamera/internal/bayer_format.h"
++#include "libcamera/internal/framebuffer.h"
++#include "libcamera/internal/mapped_framebuffer.h"
++
++namespace libcamera {
++
++/**
++ * \class DebayerCpu
++ * \brief Class for debayering on the CPU
++ *
++ * Implementation for CPU based debayering
++ */
++
++/**
++ * \brief Constructs a DebayerCpu object.
++ * \param[in] stats Pointer to the stats object to use.
++ */
++DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
++	: stats_(std::move(stats)), gamma_correction_(1.0)
++{
++#ifdef __x86_64__
++	enableInputMemcpy_ = false;
++#else
++	enableInputMemcpy_ = true;
++#endif
++	/* Initialize gamma to 1.0 curve */
++	for (unsigned int i = 0; i < kGammaLookupSize; i++)
++		gamma_[i] = i / (kGammaLookupSize / kRGBLookupSize);
++
++	for (unsigned int i = 0; i < kMaxLineBuffers; i++)
++		lineBuffers_[i] = nullptr;
++}
++
++DebayerCpu::~DebayerCpu()
++{
++	for (unsigned int i = 0; i < kMaxLineBuffers; i++)
++		free(lineBuffers_[i]);
++}
++
++// RGR
++// GBG
++// RGR
++#define BGGR_BGR888(p, n, div)                                                                \
++	*dst++ = blue_[curr[x] / (div)];                                                      \
++	*dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))];       \
++	*dst++ = red_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \
++	x++;
++
++// GBG
++// RGR
++// GBG
++#define GRBG_BGR888(p, n, div)                                    \
++	*dst++ = blue_[(prev[x] + next[x]) / (2 * (div))];        \
++	*dst++ = green_[curr[x] / (div)];                         \
++	*dst++ = red_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \
++	x++;
++
++// GRG
++// BGB
++// GRG
++#define GBRG_BGR888(p, n, div)                                     \
++	*dst++ = blue_[(curr[x - p] + curr[x + n]) / (2 * (div))]; \
++	*dst++ = green_[curr[x] / (div)];                          \
++	*dst++ = red_[(prev[x] + next[x]) / (2 * (div))];          \
++	x++;
++
++// BGB
++// GRG
++// BGB
++#define RGGB_BGR888(p, n, div)                                                                 \
++	*dst++ = blue_[(prev[x - p] + prev[x + n] + next[x - p] + next[x + n]) / (4 * (div))]; \
++	*dst++ = green_[(prev[x] + curr[x - p] + curr[x + n] + next[x]) / (4 * (div))];        \
++	*dst++ = red_[curr[x] / (div)];                                                        \
++	x++;
++
++void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	const int width_in_bytes = window_.width * 5 / 4;
++	const uint8_t *prev = (const uint8_t *)src[0];
++	const uint8_t *curr = (const uint8_t *)src[1];
++	const uint8_t *next = (const uint8_t *)src[2];
++
++	/*
++	 * For the first pixel getting a pixel from the previous column uses
++	 * x - 2 to skip the 5th byte with least-significant bits for 4 pixels.
++	 * Same for last pixel (uses x + 2) and looking at the next column.
++	 */
++	for (int x = 0; x < width_in_bytes;) {
++		/* First pixel */
++		BGGR_BGR888(2, 1, 1)
++		/* Second pixel BGGR -> GBRG */
++		GBRG_BGR888(1, 1, 1)
++		/* Same thing for third and fourth pixels */
++		BGGR_BGR888(1, 1, 1)
++		GBRG_BGR888(1, 2, 1)
++		/* Skip 5th src byte with 4 x 2 least-significant-bits */
++		x++;
++	}
++}
++
++void DebayerCpu::debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	const int width_in_bytes = window_.width * 5 / 4;
++	const uint8_t *prev = (const uint8_t *)src[0];
++	const uint8_t *curr = (const uint8_t *)src[1];
++	const uint8_t *next = (const uint8_t *)src[2];
++
++	for (int x = 0; x < width_in_bytes;) {
++		/* First pixel */
++		GRBG_BGR888(2, 1, 1)
++		/* Second pixel GRBG -> RGGB */
++		RGGB_BGR888(1, 1, 1)
++		/* Same thing for third and fourth pixels */
++		GRBG_BGR888(1, 1, 1)
++		RGGB_BGR888(1, 2, 1)
++		/* Skip 5th src byte with 4 x 2 least-significant-bits */
++		x++;
++	}
++}
++
++void DebayerCpu::debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	const int width_in_bytes = window_.width * 5 / 4;
++	const uint8_t *prev = (const uint8_t *)src[0];
++	const uint8_t *curr = (const uint8_t *)src[1];
++	const uint8_t *next = (const uint8_t *)src[2];
++
++	for (int x = 0; x < width_in_bytes;) {
++		/* Even pixel */
++		GBRG_BGR888(2, 1, 1)
++		/* Odd pixel GBGR -> BGGR */
++		BGGR_BGR888(1, 1, 1)
++		/* Same thing for next 2 pixels */
++		GBRG_BGR888(1, 1, 1)
++		BGGR_BGR888(1, 2, 1)
++		/* Skip 5th src byte with 4 x 2 least-significant-bits */
++		x++;
++	}
++}
++
++void DebayerCpu::debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	const int width_in_bytes = window_.width * 5 / 4;
++	const uint8_t *prev = (const uint8_t *)src[0];
++	const uint8_t *curr = (const uint8_t *)src[1];
++	const uint8_t *next = (const uint8_t *)src[2];
++
++	for (int x = 0; x < width_in_bytes;) {
++		/* Even pixel */
++		RGGB_BGR888(2, 1, 1)
++		/* Odd pixel RGGB -> GRBG */
++		GRBG_BGR888(1, 1, 1)
++		/* Same thing for next 2 pixels */
++		RGGB_BGR888(1, 1, 1)
++		GRBG_BGR888(1, 2, 1)
++		/* Skip 5th src byte with 4 x 2 least-significant-bits */
++		x++;
++	}
++}
++
++static bool isStandardBayerOrder(BayerFormat::Order order)
++{
++	return order == BayerFormat::BGGR || order == BayerFormat::GBRG ||
++	       order == BayerFormat::GRBG || order == BayerFormat::RGGB;
++}
++
++/*
++ * Setup the Debayer object according to the passed in parameters.
++ * Return 0 on success, a negative errno value on failure
++ * (unsupported parameters).
++ */
++int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config)
++{
++	BayerFormat bayerFormat =
++		BayerFormat::fromPixelFormat(inputFormat);
++
++	if (bayerFormat.bitDepth == 10 &&
++	    bayerFormat.packing == BayerFormat::Packing::CSI2 &&
++	    isStandardBayerOrder(bayerFormat.order)) {
++		config.bpp = 10;
++		config.patternSize.width = 4; /* 5 bytes per *4* pixels */
++		config.patternSize.height = 2;
++		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
++		return 0;
++	}
++
++	LOG(Debayer, Info)
++		<< "Unsupported input format " << inputFormat.toString();
++	return -EINVAL;
++}
++
++int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
++{
++	if (outputFormat == formats::RGB888) {
++		config.bpp = 24;
++		return 0;
++	}
++
++	LOG(Debayer, Info)
++		<< "Unsupported output format " << outputFormat.toString();
++	return -EINVAL;
++}
++
++/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */
++int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat)
++{
++	BayerFormat bayerFormat =
++		BayerFormat::fromPixelFormat(inputFormat);
++
++	if (bayerFormat.bitDepth == 10 &&
++	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
++		switch (bayerFormat.order) {
++		case BayerFormat::BGGR:
++			debayer0_ = &DebayerCpu::debayer10P_BGBG_BGR888;
++			debayer1_ = &DebayerCpu::debayer10P_GRGR_BGR888;
++			return 0;
++		case BayerFormat::GBRG:
++			debayer0_ = &DebayerCpu::debayer10P_GBGB_BGR888;
++			debayer1_ = &DebayerCpu::debayer10P_RGRG_BGR888;
++			return 0;
++		case BayerFormat::GRBG:
++			debayer0_ = &DebayerCpu::debayer10P_GRGR_BGR888;
++			debayer1_ = &DebayerCpu::debayer10P_BGBG_BGR888;
++			return 0;
++		case BayerFormat::RGGB:
++			debayer0_ = &DebayerCpu::debayer10P_RGRG_BGR888;
++			debayer1_ = &DebayerCpu::debayer10P_GBGB_BGR888;
++			return 0;
++		default:
++			break;
++		}
++	}
++
++	LOG(Debayer, Error) << "Unsupported input output format combination";
++	return -EINVAL;
++}
++
++int DebayerCpu::configure(const StreamConfiguration &inputCfg,
++			  const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs)
++{
++	if (getInputConfig(inputCfg.pixelFormat, inputConfig_) != 0)
++		return -EINVAL;
++
++	if (stats_->configure(inputCfg) != 0)
++		return -EINVAL;
++
++	const Size &stats_pattern_size = stats_->patternSize();
++	if (inputConfig_.patternSize.width != stats_pattern_size.width ||
++	    inputConfig_.patternSize.height != stats_pattern_size.height) {
++		LOG(Debayer, Error)
++			<< "mismatching stats and debayer pattern sizes for "
++			<< inputCfg.pixelFormat.toString();
++		return -EINVAL;
++	}
++
++	inputConfig_.stride = inputCfg.stride;
++
++	if (outputCfgs.size() != 1) {
++		LOG(Debayer, Error)
++			<< "Unsupported number of output streams: "
++			<< outputCfgs.size();
++		return -EINVAL;
++	}
++
++	const StreamConfiguration &outputCfg = outputCfgs[0];
++	SizeRange outSizeRange = sizes(inputCfg.pixelFormat, inputCfg.size);
++	std::tie(outputConfig_.stride, outputConfig_.frameSize) =
++		strideAndFrameSize(outputCfg.pixelFormat, outputCfg.size);
++
++	if (!outSizeRange.contains(outputCfg.size) || outputConfig_.stride != outputCfg.stride) {
++		LOG(Debayer, Error)
++			<< "Invalid output size/stride: "
++			<< "\n  " << outputCfg.size << " (" << outSizeRange << ")"
++			<< "\n  " << outputCfg.stride << " (" << outputConfig_.stride << ")";
++		return -EINVAL;
++	}
++
++	if (setDebayerFunctions(inputCfg.pixelFormat, outputCfg.pixelFormat) != 0)
++		return -EINVAL;
++
++	window_.x = ((inputCfg.size.width - outputCfg.size.width) / 2) &
++		    ~(inputConfig_.patternSize.width - 1);
++	window_.y = ((inputCfg.size.height - outputCfg.size.height) / 2) &
++		    ~(inputConfig_.patternSize.height - 1);
++	window_.width = outputCfg.size.width;
++	window_.height = outputCfg.size.height;
++
++	/* Don't pass x,y since process() already adjusts src before passing it */
++	stats_->setWindow(Rectangle(window_.size()));
++
++	/* pad with patternSize.Width on both left and right side */
++	lineBufferPadding_ = inputConfig_.patternSize.width * inputConfig_.bpp / 8;
++	lineBufferLength_ = window_.width * inputConfig_.bpp / 8 +
++			    2 * lineBufferPadding_;
++	for (unsigned int i = 0;
++	     i < (inputConfig_.patternSize.height + 1) && enableInputMemcpy_;
++	     i++) {
++		free(lineBuffers_[i]);
++		lineBuffers_[i] = (uint8_t *)malloc(lineBufferLength_);
++		if (!lineBuffers_[i])
++			return -ENOMEM;
++	}
++
++	measuredFrames_ = 0;
++	frameProcessTime_ = 0;
++
++	return 0;
++}
++
++/*
++ * Get width and height at which the bayer-pattern repeats.
++ * Return pattern-size or an empty Size for an unsupported inputFormat.
++ */
++Size DebayerCpu::patternSize(PixelFormat inputFormat)
++{
++	DebayerCpu::DebayerInputConfig config;
++
++	if (getInputConfig(inputFormat, config) != 0)
++		return {};
++
++	return config.patternSize;
++}
++
++std::vector<PixelFormat> DebayerCpu::formats(PixelFormat inputFormat)
++{
++	DebayerCpu::DebayerInputConfig config;
++
++	if (getInputConfig(inputFormat, config) != 0)
++		return std::vector<PixelFormat>();
++
++	return config.outputFormats;
++}
++
++std::tuple<unsigned int, unsigned int>
++DebayerCpu::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
++{
++	DebayerCpu::DebayerOutputConfig config;
++
++	if (getOutputConfig(outputFormat, config) != 0)
++		return std::make_tuple(0, 0);
++
++	/* round up to multiple of 8 for 64 bits alignment */
++	unsigned int stride = (size.width * config.bpp / 8 + 7) & ~7;
++
++	return std::make_tuple(stride, stride * size.height);
++}
++
++void DebayerCpu::setupInputMemcpy(const uint8_t *linePointers[])
++{
++	const unsigned int patternHeight = inputConfig_.patternSize.height;
++
++	if (!enableInputMemcpy_)
++		return;
++
++	for (unsigned int i = 0; i < patternHeight; i++) {
++		memcpy(lineBuffers_[i], linePointers[i + 1] - lineBufferPadding_,
++		       lineBufferLength_);
++		linePointers[i + 1] = lineBuffers_[i] + lineBufferPadding_;
++	}
++
++	/* Point lineBufferIndex_ to first unused lineBuffer */
++	lineBufferIndex_ = patternHeight;
++}
++
++void DebayerCpu::shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src)
++{
++	const unsigned int patternHeight = inputConfig_.patternSize.height;
++
++	for (unsigned int i = 0; i < patternHeight; i++)
++		linePointers[i] = linePointers[i + 1];
++
++	linePointers[patternHeight] = src +
++				      (patternHeight / 2) * (int)inputConfig_.stride;
++}
++
++void DebayerCpu::memcpyNextLine(const uint8_t *linePointers[])
++{
++	const unsigned int patternHeight = inputConfig_.patternSize.height;
++
++	if (!enableInputMemcpy_)
++		return;
++
++	memcpy(lineBuffers_[lineBufferIndex_], linePointers[patternHeight] - lineBufferPadding_,
++	       lineBufferLength_);
++	linePointers[patternHeight] = lineBuffers_[lineBufferIndex_] + lineBufferPadding_;
++
++	lineBufferIndex_ = (lineBufferIndex_ + 1) % (patternHeight + 1);
++}
++
++void DebayerCpu::process2(const uint8_t *src, uint8_t *dst)
++{
++	unsigned int y_end = window_.y + window_.height;
++	/* Holds [0] previous- [1] current- [2] next-line */
++	const uint8_t *linePointers[3];
++
++	/* Adjust src to top left corner of the window */
++	src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
++
++	/* [x] becomes [x - 1] after initial shiftLinePointers() call */
++	if (window_.y) {
++		linePointers[1] = src - inputConfig_.stride; /* previous-line */
++		linePointers[2] = src;
++	} else {
++		/* window_.y == 0, use the next line as prev line */
++		linePointers[1] = src + inputConfig_.stride;
++		linePointers[2] = src;
++		/* Last 2 lines also need special handling */
++		y_end -= 2;
++	}
++
++	setupInputMemcpy(linePointers);
++
++	for (unsigned int y = window_.y; y < y_end; y += 2) {
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		stats_->processLine0(y, linePointers);
++		(this->*debayer0_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		(this->*debayer1_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++	}
++
++	if (window_.y == 0) {
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		stats_->processLine0(y_end, linePointers);
++		(this->*debayer0_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		/* next line may point outside of src, use prev. */
++		linePointers[2] = linePointers[0];
++		(this->*debayer1_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++	}
++}
++
++void DebayerCpu::process4(const uint8_t *src, uint8_t *dst)
++{
++	const unsigned int y_end = window_.y + window_.height;
++	/*
++	 * This holds pointers to [0] 2-lines-up [1] 1-line-up [2] current-line
++	 * [3] 1-line-down [4] 2-lines-down.
++	 */
++	const uint8_t *linePointers[5];
++
++	/* Adjust src to top left corner of the window */
++	src += window_.y * inputConfig_.stride + window_.x * inputConfig_.bpp / 8;
++
++	/* [x] becomes [x - 1] after initial shiftLinePointers() call */
++	linePointers[1] = src - 2 * inputConfig_.stride;
++	linePointers[2] = src - inputConfig_.stride;
++	linePointers[3] = src;
++	linePointers[4] = src + inputConfig_.stride;
++
++	setupInputMemcpy(linePointers);
++
++	for (unsigned int y = window_.y; y < y_end; y += 4) {
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		stats_->processLine0(y, linePointers);
++		(this->*debayer0_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		(this->*debayer1_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		stats_->processLine2(y, linePointers);
++		(this->*debayer2_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++
++		shiftLinePointers(linePointers, src);
++		memcpyNextLine(linePointers);
++		(this->*debayer3_)(dst, linePointers);
++		src += inputConfig_.stride;
++		dst += outputConfig_.stride;
++	}
++}
++
++static inline int64_t timeDiff(timespec &after, timespec &before)
++{
++	return (after.tv_sec - before.tv_sec) * 1000000000LL +
++	       (int64_t)after.tv_nsec - (int64_t)before.tv_nsec;
++}
++
++void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams params)
++{
++	timespec frameStartTime;
++
++	if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure) {
++		frameStartTime = {};
++		clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime);
++	}
++
++	/* Apply DebayerParams */
++	if (params.gamma != gamma_correction_) {
++		for (unsigned int i = 0; i < kGammaLookupSize; i++)
++			gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma);
++
++		gamma_correction_ = params.gamma;
++	}
++
++	for (unsigned int i = 0; i < kRGBLookupSize; i++) {
++		constexpr unsigned int div =
++			kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize;
++		unsigned int idx;
++
++		/* Apply gamma after gain! */
++		idx = std::min({ i * params.gainR / div, (kGammaLookupSize - 1) });
++		red_[i] = gamma_[idx];
++
++		idx = std::min({ i * params.gainG / div, (kGammaLookupSize - 1) });
++		green_[i] = gamma_[idx];
++
++		idx = std::min({ i * params.gainB / div, (kGammaLookupSize - 1) });
++		blue_[i] = gamma_[idx];
++	}
++
++	/* Copy metadata from the input buffer */
++	FrameMetadata &metadata = output->_d()->metadata();
++	metadata.status = input->metadata().status;
++	metadata.sequence = input->metadata().sequence;
++	metadata.timestamp = input->metadata().timestamp;
++
++	MappedFrameBuffer in(input, MappedFrameBuffer::MapFlag::Read);
++	MappedFrameBuffer out(output, MappedFrameBuffer::MapFlag::Write);
++	if (!in.isValid() || !out.isValid()) {
++		LOG(Debayer, Error) << "mmap-ing buffer(s) failed";
++		metadata.status = FrameMetadata::FrameError;
++		return;
++	}
++
++	stats_->startFrame();
++
++	if (inputConfig_.patternSize.height == 2)
++		process2(in.planes()[0].data(), out.planes()[0].data());
++	else
++		process4(in.planes()[0].data(), out.planes()[0].data());
++
++	metadata.planes()[0].bytesused = out.planes()[0].size();
++
++	/* Measure before emitting signals */
++	if (measuredFrames_ < DebayerCpu::kLastFrameToMeasure &&
++	    ++measuredFrames_ > DebayerCpu::kFramesToSkip) {
++		timespec frameEndTime = {};
++		clock_gettime(CLOCK_MONOTONIC_RAW, &frameEndTime);
++		frameProcessTime_ += timeDiff(frameEndTime, frameStartTime);
++		if (measuredFrames_ == DebayerCpu::kLastFrameToMeasure) {
++			const unsigned int measuredFrames = DebayerCpu::kLastFrameToMeasure -
++							    DebayerCpu::kFramesToSkip;
++			LOG(Debayer, Info)
++				<< "Processed " << measuredFrames
++				<< " frames in " << frameProcessTime_ / 1000 << "us, "
++				<< frameProcessTime_ / (1000 * measuredFrames)
++				<< " us/frame";
++		}
++	}
++
++	stats_->finishFrame();
++	outputBufferReady.emit(output);
++	inputBufferReady.emit(input);
++}
++
++SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
++{
++	Size pattern_size = patternSize(inputFormat);
++	unsigned int border_height = pattern_size.height;
++
++	if (pattern_size.isNull())
++		return {};
++
++	/* No need for top/bottom border with a pattern height of 2 */
++	if (pattern_size.height == 2)
++		border_height = 0;
++
++	/*
++	 * For debayer interpolation a border is kept around the entire image
++	 * and the minimum output size is pattern-height x pattern-width.
++	 */
++	if (inputSize.width < (3 * pattern_size.width) ||
++	    inputSize.height < (2 * border_height + pattern_size.height)) {
++		LOG(Debayer, Warning)
++			<< "Input format size too small: " << inputSize.toString();
++		return {};
++	}
++
++	return SizeRange(Size(pattern_size.width, pattern_size.height),
++			 Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
++			      (inputSize.height - 2 * border_height) & ~(pattern_size.height - 1)),
++			 pattern_size.width, pattern_size.height);
++}
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
+new file mode 100644
+index 00000000..8a51ed85
+--- /dev/null
++++ b/src/libcamera/software_isp/debayer_cpu.h
+@@ -0,0 +1,143 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ * Copyright (C) 2023, Red Hat Inc.
++ *
++ * Authors:
++ * Hans de Goede <hdegoede@redhat.com>
++ *
++ * debayer_cpu.h - CPU based debayering header
++ */
++
++#pragma once
++
++#include <memory>
++#include <stdint.h>
++#include <vector>
++
++#include <libcamera/base/object.h>
++
++#include "debayer.h"
++#include "swstats_cpu.h"
++
++namespace libcamera {
++
++class DebayerCpu : public Debayer, public Object
++{
++public:
++	DebayerCpu(std::unique_ptr<SwStatsCpu> stats);
++	~DebayerCpu();
++
++	int configure(const StreamConfiguration &inputCfg,
++		      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs);
++	Size patternSize(PixelFormat inputFormat);
++	std::vector<PixelFormat> formats(PixelFormat input);
++	std::tuple<unsigned int, unsigned int>
++	strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
++	void process(FrameBuffer *input, FrameBuffer *output, DebayerParams params);
++	SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
++
++	/**
++	 * \brief Get the file descriptor for the statistics.
++	 *
++	 * \return the file descriptor pointing to the statistics.
++	 */
++	const SharedFD &getStatsFD() { return stats_->getStatsFD(); }
++
++	/**
++	 * \brief Get the output frame size.
++	 *
++	 * \return The output frame size.
++	 */
++	unsigned int frameSize() { return outputConfig_.frameSize; }
++
++private:
++	/**
++	 * \brief Called to debayer 1 line of Bayer input data to output format
++	 * \param[out] dst Pointer to the start of the output line to write
++	 * \param[in] src The input data
++	 *
++	 * Input data is an array of (patternSize_.height + 1) src
++	 * pointers each pointing to a line in the Bayer source. The middle
++	 * element of the array will point to the actual line being processed.
++	 * Earlier element(s) will point to the previous line(s) and later
++	 * element(s) to the next line(s).
++	 *
++	 * These functions take an array of src pointers, rather than
++	 * a single src pointer + a stride for the source, so that when the src
++	 * is slow uncached memory it can be copied to faster memory before
++	 * debayering. Debayering a standard 2x2 Bayer pattern requires access
++	 * to the previous and next src lines for interpolating the missing
++	 * colors. To allow copying the src lines only once 3 temporary buffers
++	 * each holding a single line are used, re-using the oldest buffer for
++	 * the next line and the pointers are swizzled so that:
++	 * src[0] = previous-line, src[1] = currrent-line, src[2] = next-line.
++	 * This way the 3 pointers passed to the debayer functions form
++	 * a sliding window over the src avoiding the need to copy each
++	 * line more than once.
++	 *
++	 * Similarly for bayer patterns which repeat every 4 lines, 5 src
++	 * pointers are passed holding: src[0] = 2-lines-up, src[1] = 1-line-up
++	 * src[2] = current-line, src[3] = 1-line-down, src[4] = 2-lines-down.
++	 */
++	using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]);
++
++	/* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
++	void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer10P_GBGB_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer10P_RGRG_BGR888(uint8_t *dst, const uint8_t *src[]);
++
++	struct DebayerInputConfig {
++		Size patternSize;
++		unsigned int bpp; /* Memory used per pixel, not precision */
++		unsigned int stride;
++		std::vector<PixelFormat> outputFormats;
++	};
++
++	struct DebayerOutputConfig {
++		unsigned int bpp; /* Memory used per pixel, not precision */
++		unsigned int stride;
++		unsigned int frameSize;
++	};
++
++	int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
++	int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
++	int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
++	void setupInputMemcpy(const uint8_t *linePointers[]);
++	void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
++	void memcpyNextLine(const uint8_t *linePointers[]);
++	void process2(const uint8_t *src, uint8_t *dst);
++	void process4(const uint8_t *src, uint8_t *dst);
++
++	static constexpr unsigned int kGammaLookupSize = 1024;
++	static constexpr unsigned int kRGBLookupSize = 256;
++	/* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */
++	static constexpr unsigned int kMaxLineBuffers = 5;
++
++	std::array<uint8_t, kGammaLookupSize> gamma_;
++	std::array<uint8_t, kRGBLookupSize> red_;
++	std::array<uint8_t, kRGBLookupSize> green_;
++	std::array<uint8_t, kRGBLookupSize> blue_;
++	debayerFn debayer0_;
++	debayerFn debayer1_;
++	debayerFn debayer2_;
++	debayerFn debayer3_;
++	Rectangle window_;
++	DebayerInputConfig inputConfig_;
++	DebayerOutputConfig outputConfig_;
++	std::unique_ptr<SwStatsCpu> stats_;
++	uint8_t *lineBuffers_[kMaxLineBuffers];
++	unsigned int lineBufferLength_;
++	unsigned int lineBufferPadding_;
++	unsigned int lineBufferIndex_;
++	bool enableInputMemcpy_;
++	float gamma_correction_;
++	unsigned int measuredFrames_;
++	int64_t frameProcessTime_;
++	/* Skip 30 frames for things to stabilize then measure 30 frames */
++	static constexpr unsigned int kFramesToSkip = 30;
++	static constexpr unsigned int kLastFrameToMeasure = 60;
++};
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
+index 62095f61..71b46539 100644
+--- a/src/libcamera/software_isp/meson.build
++++ b/src/libcamera/software_isp/meson.build
+@@ -9,5 +9,6 @@ endif
+ 
+ libcamera_sources += files([
+     'debayer.cpp',
++    'debayer_cpu.cpp',
+     'swstats_cpu.cpp',
+ ])
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch b/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch
new file mode 100644
index 0000000000..40f9403ba9
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0009-libcamera-ipa-add-Soft-IPA.patch
@@ -0,0 +1,506 @@
+From 5261c801d8425fa82bcbd3da0199d06153eb5bd7 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:13 +0100
+Subject: [PATCH 09/21] libcamera: ipa: add Soft IPA
+
+Define the Soft IPA main and event interfaces, add the Soft IPA
+implementation.
+
+The current src/ipa/meson.build assumes the IPA name to match the
+pipeline name. For this reason "-Dipas=simple" is used for the
+Soft IPA module.
+
+Auto exposure/gain and AWB implementation by Dennis, Toon and Martti.
+
+Auto exposure/gain targets a Mean Sample Value of 2.5 following
+the MSV calculation algorithm from:
+https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Co-developed-by: Marttico <g.martti@gmail.com>
+Signed-off-by: Marttico <g.martti@gmail.com>
+Co-developed-by: Toon Langendam <t.langendam@gmail.com>
+Signed-off-by: Toon Langendam <t.langendam@gmail.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ Documentation/Doxyfile.in         |   1 +
+ include/libcamera/ipa/meson.build |   1 +
+ include/libcamera/ipa/soft.mojom  |  28 +++
+ meson_options.txt                 |   2 +-
+ src/ipa/simple/data/meson.build   |   9 +
+ src/ipa/simple/data/soft.conf     |   3 +
+ src/ipa/simple/meson.build        |  25 +++
+ src/ipa/simple/soft_simple.cpp    | 326 ++++++++++++++++++++++++++++++
+ 8 files changed, 394 insertions(+), 1 deletion(-)
+ create mode 100644 include/libcamera/ipa/soft.mojom
+ create mode 100644 src/ipa/simple/data/meson.build
+ create mode 100644 src/ipa/simple/data/soft.conf
+ create mode 100644 src/ipa/simple/meson.build
+ create mode 100644 src/ipa/simple/soft_simple.cpp
+
+diff --git a/Documentation/Doxyfile.in b/Documentation/Doxyfile.in
+index a86ea6c1..2be8d47b 100644
+--- a/Documentation/Doxyfile.in
++++ b/Documentation/Doxyfile.in
+@@ -44,6 +44,7 @@ EXCLUDE                = @TOP_SRCDIR@/include/libcamera/base/span.h \
+                          @TOP_SRCDIR@/src/libcamera/pipeline/ \
+                          @TOP_SRCDIR@/src/libcamera/tracepoints.cpp \
+                          @TOP_BUILDDIR@/include/libcamera/internal/tracepoints.h \
++                         @TOP_BUILDDIR@/include/libcamera/ipa/soft_ipa_interface.h \
+                          @TOP_BUILDDIR@/src/libcamera/proxy/
+ 
+ EXCLUDE_PATTERNS       = @TOP_BUILDDIR@/include/libcamera/ipa/*_serializer.h \
+diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build
+index f3b4881c..3352d08f 100644
+--- a/include/libcamera/ipa/meson.build
++++ b/include/libcamera/ipa/meson.build
+@@ -65,6 +65,7 @@ pipeline_ipa_mojom_mapping = {
+     'ipu3': 'ipu3.mojom',
+     'rkisp1': 'rkisp1.mojom',
+     'rpi/vc4': 'raspberrypi.mojom',
++    'simple': 'soft.mojom',
+     'vimc': 'vimc.mojom',
+ }
+ 
+diff --git a/include/libcamera/ipa/soft.mojom b/include/libcamera/ipa/soft.mojom
+new file mode 100644
+index 00000000..c249bd75
+--- /dev/null
++++ b/include/libcamera/ipa/soft.mojom
+@@ -0,0 +1,28 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++
++/*
++ * \todo Document the interface and remove the related EXCLUDE_PATTERNS entry.
++ */
++
++module ipa.soft;
++
++import "include/libcamera/ipa/core.mojom";
++
++interface IPASoftInterface {
++	init(libcamera.IPASettings settings,
++	     libcamera.SharedFD fdStats,
++	     libcamera.SharedFD fdParams,
++	     libcamera.ControlInfoMap sensorCtrlInfoMap)
++		=> (int32 ret);
++	start() => (int32 ret);
++	stop();
++	configure(libcamera.ControlInfoMap sensorCtrlInfoMap)
++		=> (int32 ret);
++
++	[async] processStats(libcamera.ControlList sensorControls);
++};
++
++interface IPASoftEventInterface {
++	setSensorControls(libcamera.ControlList sensorControls);
++	setIspParams(int32 dummy);
++};
+diff --git a/meson_options.txt b/meson_options.txt
+index 5fdc7be8..94372e47 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -27,7 +27,7 @@ option('gstreamer',
+ 
+ option('ipas',
+         type : 'array',
+-        choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'],
++        choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'vimc'],
+         description : 'Select which IPA modules to build')
+ 
+ option('lc-compliance',
+diff --git a/src/ipa/simple/data/meson.build b/src/ipa/simple/data/meson.build
+new file mode 100644
+index 00000000..33548cc6
+--- /dev/null
++++ b/src/ipa/simple/data/meson.build
+@@ -0,0 +1,9 @@
++# SPDX-License-Identifier: CC0-1.0
++
++conf_files = files([
++    'soft.conf',
++])
++
++install_data(conf_files,
++             install_dir : ipa_data_dir / 'soft',
++             install_tag : 'runtime')
+diff --git a/src/ipa/simple/data/soft.conf b/src/ipa/simple/data/soft.conf
+new file mode 100644
+index 00000000..0c70e7c0
+--- /dev/null
++++ b/src/ipa/simple/data/soft.conf
+@@ -0,0 +1,3 @@
++# SPDX-License-Identifier: LGPL-2.1-or-later
++#
++# Dummy configuration file for the soft IPA.
+diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
+new file mode 100644
+index 00000000..3e863db7
+--- /dev/null
++++ b/src/ipa/simple/meson.build
+@@ -0,0 +1,25 @@
++# SPDX-License-Identifier: CC0-1.0
++
++ipa_name = 'ipa_soft_simple'
++
++mod = shared_module(ipa_name,
++                    ['soft_simple.cpp', libcamera_generated_ipa_headers],
++                    name_prefix : '',
++                    include_directories : [ipa_includes, libipa_includes],
++                    dependencies : libcamera_private,
++                    link_with : libipa,
++                    install : true,
++                    install_dir : ipa_install_dir)
++
++if ipa_sign_module
++    custom_target(ipa_name + '.so.sign',
++                  input : mod,
++                  output : ipa_name + '.so.sign',
++                  command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'],
++                  install : false,
++                  build_by_default : true)
++endif
++
++subdir('data')
++
++ipa_names += ipa_name
+diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
+new file mode 100644
+index 00000000..312df4ba
+--- /dev/null
++++ b/src/ipa/simple/soft_simple.cpp
+@@ -0,0 +1,326 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ *
++ * soft_simple.cpp - Simple Software Image Processing Algorithm module
++ */
++
++#include <sys/mman.h>
++
++#include <libcamera/base/file.h>
++#include <libcamera/base/log.h>
++#include <libcamera/base/shared_fd.h>
++
++#include <libcamera/control_ids.h>
++#include <libcamera/controls.h>
++
++#include <libcamera/ipa/ipa_interface.h>
++#include <libcamera/ipa/ipa_module_info.h>
++#include <libcamera/ipa/soft_ipa_interface.h>
++
++#include "libcamera/internal/camera_sensor.h"
++#include "libcamera/internal/software_isp/debayer_params.h"
++#include "libcamera/internal/software_isp/swisp_stats.h"
++
++namespace libcamera {
++
++LOG_DEFINE_CATEGORY(IPASoft)
++
++namespace ipa::soft {
++
++class IPASoftSimple : public ipa::soft::IPASoftInterface
++{
++public:
++	IPASoftSimple()
++		: params_(static_cast<DebayerParams *>(MAP_FAILED)),
++		  stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0)
++	{
++	}
++
++	~IPASoftSimple()
++	{
++		if (stats_ != MAP_FAILED)
++			munmap(stats_, sizeof(SwIspStats));
++		if (params_ != MAP_FAILED)
++			munmap(params_, sizeof(DebayerParams));
++	}
++
++	int init(const IPASettings &settings,
++		 const SharedFD &fdStats,
++		 const SharedFD &fdParams,
++		 const ControlInfoMap &sensorInfoMap) override;
++	int configure(const ControlInfoMap &sensorInfoMap) override;
++
++	int start() override;
++	void stop() override;
++
++	void processStats(const ControlList &sensorControls) override;
++
++private:
++	void updateExposure(double exposureMSV);
++
++	SharedFD fdStats_;
++	SharedFD fdParams_;
++	DebayerParams *params_;
++	SwIspStats *stats_;
++
++	int32_t exposure_min_, exposure_max_;
++	int32_t again_min_, again_max_;
++	int32_t again_, exposure_;
++	unsigned int ignore_updates_;
++};
++
++int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,
++			const SharedFD &fdStats,
++			const SharedFD &fdParams,
++			const ControlInfoMap &sensorInfoMap)
++{
++	fdStats_ = fdStats;
++	if (!fdStats_.isValid()) {
++		LOG(IPASoft, Error) << "Invalid Statistics handle";
++		return -ENODEV;
++	}
++
++	fdParams_ = fdParams;
++	if (!fdParams_.isValid()) {
++		LOG(IPASoft, Error) << "Invalid Parameters handle";
++		return -ENODEV;
++	}
++
++	params_ = static_cast<DebayerParams *>(mmap(nullptr, sizeof(DebayerParams),
++						    PROT_WRITE, MAP_SHARED,
++						    fdParams_.get(), 0));
++	if (params_ == MAP_FAILED) {
++		LOG(IPASoft, Error) << "Unable to map Parameters";
++		return -errno;
++	}
++
++	stats_ = static_cast<SwIspStats *>(mmap(nullptr, sizeof(SwIspStats),
++						PROT_READ, MAP_SHARED,
++						fdStats_.get(), 0));
++	if (stats_ == MAP_FAILED) {
++		LOG(IPASoft, Error) << "Unable to map Statistics";
++		return -errno;
++	}
++
++	if (sensorInfoMap.find(V4L2_CID_EXPOSURE) == sensorInfoMap.end()) {
++		LOG(IPASoft, Error) << "Don't have exposure control";
++		return -EINVAL;
++	}
++
++	if (sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN) == sensorInfoMap.end()) {
++		LOG(IPASoft, Error) << "Don't have gain control";
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap)
++{
++	const ControlInfo &exposure_info = sensorInfoMap.find(V4L2_CID_EXPOSURE)->second;
++	const ControlInfo &gain_info = sensorInfoMap.find(V4L2_CID_ANALOGUE_GAIN)->second;
++
++	exposure_min_ = exposure_info.min().get<int32_t>();
++	exposure_max_ = exposure_info.max().get<int32_t>();
++	if (!exposure_min_) {
++		LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
++		exposure_min_ = 1;
++	}
++
++	again_min_ = gain_info.min().get<int32_t>();
++	again_max_ = gain_info.max().get<int32_t>();
++	/*
++	 * The camera sensor gain (g) is usually not equal to the value written
++	 * into the gain register (x). But the way how the AGC algorithm changes
++	 * the gain value to make the total exposure closer to the optimum assumes
++	 * that g(x) is not too far from linear function. If the minimal gain is 0,
++	 * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
++	 * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
++	 * one edge, and very small near the other) we limit the range of the gain
++	 * values used.
++	 */
++	if (!again_min_) {
++		LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
++		again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);
++	}
++
++	LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
++			   << ", gain " << again_min_ << "-" << again_max_;
++
++	return 0;
++}
++
++int IPASoftSimple::start()
++{
++	return 0;
++}
++
++void IPASoftSimple::stop()
++{
++}
++
++/*
++ * The number of bins to use for the optimal exposure calculations.
++ */
++static constexpr unsigned int kExposureBinsCount = 5;
++/*
++ * The exposure is optimal when the mean sample value of the histogram is
++ * in the middle of the range.
++ */
++static constexpr float kExposureOptimal = kExposureBinsCount / 2.0;
++/*
++ * The below value implements the hysteresis for the exposure adjustment.
++ * It is small enough to have the exposure close to the optimal, and is big
++ * enough to prevent the exposure from wobbling around the optimal value.
++ */
++static constexpr float kExposureSatisfactory = 0.2;
++
++void IPASoftSimple::processStats(const ControlList &sensorControls)
++{
++	/*
++	 * Calculate red and blue gains for AWB.
++	 * Clamp max gain at 4.0, this also avoids 0 division.
++	 */
++	if (stats_->sumR_ <= stats_->sumG_ / 4)
++		params_->gainR = 1024;
++	else
++		params_->gainR = 256 * stats_->sumG_ / stats_->sumR_;
++
++	if (stats_->sumB_ <= stats_->sumG_ / 4)
++		params_->gainB = 1024;
++	else
++		params_->gainB = 256 * stats_->sumG_ / stats_->sumB_;
++
++	/* Green gain and gamma values are fixed */
++	params_->gainG = 256;
++	params_->gamma = 0.5;
++
++	setIspParams.emit(0);
++
++	/*
++	 * AE / AGC, use 2 frames delay to make sure that the exposure and
++	 * the gain set have applied to the camera sensor.
++	 */
++	if (ignore_updates_ > 0) {
++		--ignore_updates_;
++		return;
++	}
++
++	/*
++	 * Calculate Mean Sample Value (MSV) according to formula from:
++	 * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
++	 */
++	constexpr unsigned int yHistValsPerBin =
++		SwIspStats::kYHistogramSize / kExposureBinsCount;
++	constexpr unsigned int yHistValsPerBinMod =
++		SwIspStats::kYHistogramSize /
++		(SwIspStats::kYHistogramSize % kExposureBinsCount + 1);
++	int ExposureBins[kExposureBinsCount] = {};
++	unsigned int denom = 0;
++	unsigned int num = 0;
++
++	for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) {
++		unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;
++		ExposureBins[idx] += stats_->yHistogram[i];
++	}
++
++	for (unsigned int i = 0; i < kExposureBinsCount; i++) {
++		LOG(IPASoft, Debug) << i << ": " << ExposureBins[i];
++		denom += ExposureBins[i];
++		num += ExposureBins[i] * (i + 1);
++	}
++
++	float exposureMSV = (float)num / denom;
++
++	/* sanity check */
++	if (!sensorControls.contains(V4L2_CID_EXPOSURE) ||
++	    !sensorControls.contains(V4L2_CID_ANALOGUE_GAIN)) {
++		LOG(IPASoft, Error) << "Control(s) missing";
++		return;
++	}
++
++	ControlList ctrls(sensorControls);
++
++	exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>();
++	again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
++
++	updateExposure(exposureMSV);
++
++	ctrls.set(V4L2_CID_EXPOSURE, exposure_);
++	ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
++
++	ignore_updates_ = 2;
++
++	setSensorControls.emit(ctrls);
++
++	LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV
++			    << " exp " << exposure_ << " again " << again_
++			    << " gain R/B " << params_->gainR << "/" << params_->gainB;
++}
++
++void IPASoftSimple::updateExposure(double exposureMSV)
++{
++	/* DENOMINATOR of 10 gives ~10% increment/decrement; DENOMINATOR of 5 - about ~20% */
++	static constexpr uint8_t kExpDenominator = 10;
++	static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
++	static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
++
++	int next;
++
++	if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
++		next = exposure_ * kExpNumeratorUp / kExpDenominator;
++		if (next - exposure_ < 1)
++			exposure_ += 1;
++		else
++			exposure_ = next;
++		if (exposure_ >= exposure_max_) {
++			next = again_ * kExpNumeratorUp / kExpDenominator;
++			if (next - again_ < 1)
++				again_ += 1;
++			else
++				again_ = next;
++		}
++	}
++
++	if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
++		if (exposure_ == exposure_max_ && again_ != again_min_) {
++			next = again_ * kExpNumeratorDown / kExpDenominator;
++			if (again_ - next < 1)
++				again_ -= 1;
++			else
++				again_ = next;
++		} else {
++			next = exposure_ * kExpNumeratorDown / kExpDenominator;
++			if (exposure_ - next < 1)
++				exposure_ -= 1;
++			else
++				exposure_ = next;
++		}
++	}
++
++	exposure_ = std::clamp(exposure_, exposure_min_, exposure_max_);
++	again_ = std::clamp(again_, again_min_, again_max_);
++}
++
++} /* namespace ipa::soft */
++
++/*
++ * External IPA module interface
++ */
++extern "C" {
++const struct IPAModuleInfo ipaModuleInfo = {
++	IPA_MODULE_API_VERSION,
++	0,
++	"SimplePipelineHandler",
++	"simple",
++};
++
++IPAInterface *ipaCreate()
++{
++	return new ipa::soft::IPASoftSimple();
++}
++
++} /* extern "C" */
++
++} /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch b/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch
new file mode 100644
index 0000000000..9f2d66c2f8
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0010-libcamera-introduce-SoftwareIsp.patch
@@ -0,0 +1,507 @@
+From ad41ea12fe4b8ca0ace20781c775a63ed0d66f4c Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:14 +0100
+Subject: [PATCH 10/21] libcamera: introduce SoftwareIsp
+
+Doxygen documentation by Dennis Bonke.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Co-developed-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Dennis Bonke <admin@dennisbonke.com>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ .../internal/software_isp/meson.build         |   1 +
+ .../internal/software_isp/software_isp.h      |  98 +++++
+ src/libcamera/software_isp/meson.build        |   1 +
+ src/libcamera/software_isp/software_isp.cpp   | 349 ++++++++++++++++++
+ 4 files changed, 449 insertions(+)
+ create mode 100644 include/libcamera/internal/software_isp/software_isp.h
+ create mode 100644 src/libcamera/software_isp/software_isp.cpp
+
+diff --git a/include/libcamera/internal/software_isp/meson.build b/include/libcamera/internal/software_isp/meson.build
+index a620e16d..508ddddc 100644
+--- a/include/libcamera/internal/software_isp/meson.build
++++ b/include/libcamera/internal/software_isp/meson.build
+@@ -2,5 +2,6 @@
+ 
+ libcamera_internal_headers += files([
+     'debayer_params.h',
++    'software_isp.h',
+     'swisp_stats.h',
+ ])
+diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
+new file mode 100644
+index 00000000..8d25e979
+--- /dev/null
++++ b/include/libcamera/internal/software_isp/software_isp.h
+@@ -0,0 +1,98 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ *
++ * software_isp.h - Simple software ISP implementation
++ */
++
++#pragma once
++
++#include <functional>
++#include <initializer_list>
++#include <map>
++#include <memory>
++#include <string>
++#include <tuple>
++#include <vector>
++
++#include <libcamera/base/class.h>
++#include <libcamera/base/log.h>
++#include <libcamera/base/signal.h>
++#include <libcamera/base/thread.h>
++
++#include <libcamera/geometry.h>
++#include <libcamera/pixel_format.h>
++
++#include <libcamera/ipa/soft_ipa_interface.h>
++#include <libcamera/ipa/soft_ipa_proxy.h>
++
++#include "libcamera/internal/dma_heaps.h"
++#include "libcamera/internal/pipeline_handler.h"
++#include "libcamera/internal/shared_mem_object.h"
++#include "libcamera/internal/software_isp/debayer_params.h"
++
++namespace libcamera {
++
++class DebayerCpu;
++class FrameBuffer;
++class PixelFormat;
++struct StreamConfiguration;
++
++LOG_DECLARE_CATEGORY(SoftwareIsp)
++
++class SoftwareIsp
++{
++public:
++	SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
++	~SoftwareIsp();
++
++	int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
++
++	bool isValid() const;
++
++	std::vector<PixelFormat> formats(PixelFormat input);
++
++	SizeRange sizes(PixelFormat inputFormat, const Size &inputSize);
++
++	std::tuple<unsigned int, unsigned int>
++	strideAndFrameSize(const PixelFormat &outputFormat, const Size &size);
++
++	int configure(const StreamConfiguration &inputCfg,
++		      const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
++		      const ControlInfoMap &sensorControls);
++
++	int exportBuffers(unsigned int output, unsigned int count,
++			  std::vector<std::unique_ptr<FrameBuffer>> *buffers);
++
++	void processStats(const ControlList &sensorControls);
++
++	int start();
++	void stop();
++
++	int queueBuffers(FrameBuffer *input,
++			 const std::map<unsigned int, FrameBuffer *> &outputs);
++
++	void process(FrameBuffer *input, FrameBuffer *output);
++
++	Signal<FrameBuffer *> inputBufferReady;
++	Signal<FrameBuffer *> outputBufferReady;
++	Signal<int> ispStatsReady;
++	Signal<const ControlList &> setSensorControls;
++
++private:
++	void saveIspParams(int dummy);
++	void setSensorCtrls(const ControlList &sensorControls);
++	void statsReady(int dummy);
++	void inputReady(FrameBuffer *input);
++	void outputReady(FrameBuffer *output);
++
++	std::unique_ptr<DebayerCpu> debayer_;
++	Thread ispWorkerThread_;
++	SharedMemObject<DebayerParams> sharedParams_;
++	DebayerParams debayerParams_;
++	DmaHeap dmaHeap_;
++
++	std::unique_ptr<ipa::soft::IPAProxySoft> ipa_;
++};
++
++} /* namespace libcamera */
+diff --git a/src/libcamera/software_isp/meson.build b/src/libcamera/software_isp/meson.build
+index 71b46539..e9266e54 100644
+--- a/src/libcamera/software_isp/meson.build
++++ b/src/libcamera/software_isp/meson.build
+@@ -10,5 +10,6 @@ endif
+ libcamera_sources += files([
+     'debayer.cpp',
+     'debayer_cpu.cpp',
++    'software_isp.cpp',
+     'swstats_cpu.cpp',
+ ])
+diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
+new file mode 100644
+index 00000000..388b4496
+--- /dev/null
++++ b/src/libcamera/software_isp/software_isp.cpp
+@@ -0,0 +1,349 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2023, Linaro Ltd
++ *
++ * software_isp.cpp - Simple software ISP implementation
++ */
++
++#include "libcamera/internal/software_isp/software_isp.h"
++
++#include <sys/mman.h>
++#include <sys/types.h>
++#include <unistd.h>
++
++#include <libcamera/formats.h>
++#include <libcamera/stream.h>
++
++#include "libcamera/internal/bayer_format.h"
++#include "libcamera/internal/framebuffer.h"
++#include "libcamera/internal/ipa_manager.h"
++#include "libcamera/internal/mapped_framebuffer.h"
++
++#include "debayer_cpu.h"
++
++/**
++ * \file software_isp.cpp
++ * \brief Simple software ISP implementation
++ */
++
++namespace libcamera {
++
++LOG_DEFINE_CATEGORY(SoftwareIsp)
++
++/**
++ * \class SoftwareIsp
++ * \brief Class for the Software ISP
++ */
++
++/**
++ * \var SoftwareIsp::inputBufferReady
++ * \brief A signal emitted when the input frame buffer completes
++ */
++
++/**
++ * \var SoftwareIsp::outputBufferReady
++ * \brief A signal emitted when the output frame buffer completes
++ */
++
++/**
++ * \var SoftwareIsp::ispStatsReady
++ * \brief A signal emitted when the statistics for IPA are ready
++ *
++ * The int parameter isn't actually used.
++ */
++
++/**
++ * \var SoftwareIsp::setSensorControls
++ * \brief A signal emitted when the values to write to the sensor controls are ready
++ */
++
++/**
++ * \brief Constructs SoftwareIsp object
++ * \param[in] pipe The pipeline handler in use
++ * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor
++ */
++SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
++	: debayer_(nullptr),
++	  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f },
++	  dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
++{
++	if (!dmaHeap_.isValid()) {
++		LOG(SoftwareIsp, Error) << "Failed to create DmaHeap object";
++		return;
++	}
++
++	sharedParams_ = SharedMemObject<DebayerParams>("softIsp_params");
++	if (!sharedParams_) {
++		LOG(SoftwareIsp, Error) << "Failed to create shared memory for parameters";
++		return;
++	}
++
++	auto stats = std::make_unique<SwStatsCpu>();
++	if (!stats->isValid()) {
++		LOG(SoftwareIsp, Error) << "Failed to create SwStatsCpu object";
++		return;
++	}
++	stats->statsReady.connect(this, &SoftwareIsp::statsReady);
++
++	debayer_ = std::make_unique<DebayerCpu>(std::move(stats));
++	debayer_->inputBufferReady.connect(this, &SoftwareIsp::inputReady);
++	debayer_->outputBufferReady.connect(this, &SoftwareIsp::outputReady);
++
++	ipa_ = IPAManager::createIPA<ipa::soft::IPAProxySoft>(pipe, 0, 0);
++	if (!ipa_) {
++		LOG(SoftwareIsp, Error)
++			<< "Creating IPA for software ISP failed";
++		debayer_.reset();
++		return;
++	}
++
++	int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
++			     debayer_->getStatsFD(),
++			     sharedParams_.fd(),
++			     sensorControls);
++	if (ret) {
++		LOG(SoftwareIsp, Error) << "IPA init failed";
++		debayer_.reset();
++		return;
++	}
++
++	ipa_->setIspParams.connect(this, &SoftwareIsp::saveIspParams);
++	ipa_->setSensorControls.connect(this, &SoftwareIsp::setSensorCtrls);
++
++	debayer_->moveToThread(&ispWorkerThread_);
++}
++
++SoftwareIsp::~SoftwareIsp()
++{
++	/* make sure to destroy the DebayerCpu before the ispWorkerThread_ is gone */
++	debayer_.reset();
++}
++
++/**
++ * \fn int SoftwareIsp::loadConfiguration([[maybe_unused]] const std::string &filename)
++ * \brief Load a configuration from a file
++ * \param[in] filename The file to load the configuration data from
++ *
++ * Currently is a stub doing nothing and always returning "success".
++ *
++ * \return 0 on success
++ */
++
++/**
++ * \brief Process the statistics gathered
++ * \param[in] sensorControls The sensor controls
++ *
++ * Requests the IPA to calculate new parameters for ISP and new control
++ * values for the sensor.
++ */
++void SoftwareIsp::processStats(const ControlList &sensorControls)
++{
++	ASSERT(ipa_);
++	ipa_->processStats(sensorControls);
++}
++
++/**
++ * \brief Check the validity of Software Isp object
++ * \return True if Software Isp is valid, false otherwise
++ */
++bool SoftwareIsp::isValid() const
++{
++	return !!debayer_;
++}
++
++/**
++  * \brief Get the output formats supported for the given input format
++  * \param[in] inputFormat The input format
++  * \return All the supported output formats or an empty vector if there are none
++  */
++std::vector<PixelFormat> SoftwareIsp::formats(PixelFormat inputFormat)
++{
++	ASSERT(debayer_ != nullptr);
++
++	return debayer_->formats(inputFormat);
++}
++
++/**
++ * \brief Get the supported output sizes for the given input format and size
++ * \param[in] inputFormat The input format
++ * \param[in] inputSize The input frame size
++ * \return The valid size range or an empty range if there are none
++ */
++SizeRange SoftwareIsp::sizes(PixelFormat inputFormat, const Size &inputSize)
++{
++	ASSERT(debayer_ != nullptr);
++
++	return debayer_->sizes(inputFormat, inputSize);
++}
++
++/**
++ * Get the output stride and the frame size in bytes for the given output format and size
++ * \param[in] outputFormat The output format
++ * \param[in] size The output size (width and height in pixels)
++ * \return A tuple of the stride and the frame size in bytes, or a tuple of 0,0
++ * if there is no valid output config
++ */
++std::tuple<unsigned int, unsigned int>
++SoftwareIsp::strideAndFrameSize(const PixelFormat &outputFormat, const Size &size)
++{
++	ASSERT(debayer_ != nullptr);
++
++	return debayer_->strideAndFrameSize(outputFormat, size);
++}
++
++/**
++ * \brief Configure the SoftwareIsp object according to the passed in parameters
++ * \param[in] inputCfg The input configuration
++ * \param[in] outputCfgs The output configurations
++ * \param[in] sensorControls ControlInfoMap of the controls supported by the sensor
++ * \return 0 on success, a negative errno on failure
++ */
++int SoftwareIsp::configure(const StreamConfiguration &inputCfg,
++			   const std::vector<std::reference_wrapper<StreamConfiguration>> &outputCfgs,
++			   const ControlInfoMap &sensorControls)
++{
++	ASSERT(ipa_ != nullptr && debayer_ != nullptr);
++
++	int ret = ipa_->configure(sensorControls);
++	if (ret < 0)
++		return ret;
++
++	return debayer_->configure(inputCfg, outputCfgs);
++}
++
++/**
++ * \brief Export the buffers from the Software ISP
++ * \param[in] output Output stream index exporting the buffers
++ * \param[in] count Number of buffers to allocate
++ * \param[out] buffers Vector to store the allocated buffers
++ * \return The number of allocated buffers on success or a negative error code
++ * otherwise
++ */
++int SoftwareIsp::exportBuffers(unsigned int output, unsigned int count,
++			       std::vector<std::unique_ptr<FrameBuffer>> *buffers)
++{
++	ASSERT(debayer_ != nullptr);
++
++	/* single output for now */
++	if (output >= 1)
++		return -EINVAL;
++
++	for (unsigned int i = 0; i < count; i++) {
++		const std::string name = "frame-" + std::to_string(i);
++		const size_t frameSize = debayer_->frameSize();
++
++		FrameBuffer::Plane outPlane;
++		outPlane.fd = SharedFD(dmaHeap_.alloc(name.c_str(), frameSize));
++		if (!outPlane.fd.isValid()) {
++			LOG(SoftwareIsp, Error)
++				<< "failed to allocate a dma_buf";
++			return -ENOMEM;
++		}
++		outPlane.offset = 0;
++		outPlane.length = frameSize;
++
++		std::vector<FrameBuffer::Plane> planes{ outPlane };
++		buffers->emplace_back(std::make_unique<FrameBuffer>(std::move(planes)));
++	}
++
++	return count;
++}
++
++/**
++ * \brief Queue buffers to Software ISP
++ * \param[in] input The input framebuffer
++ * \param[in] outputs The container holding the output stream indexes and
++ * their respective frame buffer outputs
++ * \return 0 on success, a negative errno on failure
++ */
++int SoftwareIsp::queueBuffers(FrameBuffer *input,
++			      const std::map<unsigned int, FrameBuffer *> &outputs)
++{
++	unsigned int mask = 0;
++
++	/*
++	 * Validate the outputs as a sanity check: at least one output is
++	 * required, all outputs must reference a valid stream and no two
++	 * outputs can reference the same stream.
++	 */
++	if (outputs.empty())
++		return -EINVAL;
++
++	for (auto [index, buffer] : outputs) {
++		if (!buffer)
++			return -EINVAL;
++		if (index >= 1) /* only single stream atm */
++			return -EINVAL;
++		if (mask & (1 << index))
++			return -EINVAL;
++
++		mask |= 1 << index;
++	}
++
++	process(input, outputs.at(0));
++
++	return 0;
++}
++
++/**
++ * \brief Starts the Software ISP streaming operation
++ * \return 0 on success, any other value indicates an error
++ */
++int SoftwareIsp::start()
++{
++	int ret = ipa_->start();
++	if (ret)
++		return ret;
++
++	ispWorkerThread_.start();
++	return 0;
++}
++
++/**
++ * \brief Stops the Software ISP streaming operation
++ */
++void SoftwareIsp::stop()
++{
++	ispWorkerThread_.exit();
++	ispWorkerThread_.wait();
++
++	ipa_->stop();
++}
++
++/**
++ * \brief Passes the input framebuffer to the ISP worker to process
++ * \param[in] input The input framebuffer
++ * \param[out] output The framebuffer to write the processed frame to
++ */
++void SoftwareIsp::process(FrameBuffer *input, FrameBuffer *output)
++{
++	debayer_->invokeMethod(&DebayerCpu::process,
++			       ConnectionTypeQueued, input, output, debayerParams_);
++}
++
++void SoftwareIsp::saveIspParams([[maybe_unused]] int dummy)
++{
++	debayerParams_ = *sharedParams_;
++}
++
++void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls)
++{
++	setSensorControls.emit(sensorControls);
++}
++
++void SoftwareIsp::statsReady(int dummy)
++{
++	ispStatsReady.emit(dummy);
++}
++
++void SoftwareIsp::inputReady(FrameBuffer *input)
++{
++	inputBufferReady.emit(input);
++}
++
++void SoftwareIsp::outputReady(FrameBuffer *output)
++{
++	outputBufferReady.emit(output);
++}
++
++} /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch b/users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
new file mode 100644
index 0000000000..5c2237a8eb
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0011-libcamera-pipeline-simple-rename-converterBuffers_-a.patch
@@ -0,0 +1,240 @@
+From 050440eed6ab90686df217f5ff7dea0b241e3898 Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:15 +0100
+Subject: [PATCH 11/21] libcamera: pipeline: simple: rename converterBuffers_
+ and related vars
+
+The converterBuffers_ and the converterQueue_ are not that specific
+to the Converter, and could be used by another entity doing the format
+conversion.
+
+Rename converterBuffers_, converterQueue_, and useConverter_ to
+conversionBuffers_, conversionQueue_ and useConversion_ to
+disassociate them from the Converter.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/pipeline/simple/simple.cpp | 63 ++++++++++++------------
+ 1 file changed, 32 insertions(+), 31 deletions(-)
+
+diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
+index a84f6760..78854ef8 100644
+--- a/src/libcamera/pipeline/simple/simple.cpp
++++ b/src/libcamera/pipeline/simple/simple.cpp
+@@ -269,17 +269,18 @@ public:
+ 	std::vector<Configuration> configs_;
+ 	std::map<PixelFormat, std::vector<const Configuration *>> formats_;
+ 
++	std::vector<std::unique_ptr<FrameBuffer>> conversionBuffers_;
++	std::queue<std::map<unsigned int, FrameBuffer *>> conversionQueue_;
++	bool useConversion_;
++
+ 	std::unique_ptr<Converter> converter_;
+-	std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;
+-	bool useConverter_;
+-	std::queue<std::map<unsigned int, FrameBuffer *>> converterQueue_;
+ 
+ private:
+ 	void tryPipeline(unsigned int code, const Size &size);
+ 	static std::vector<const MediaPad *> routedSourcePads(MediaPad *sink);
+ 
+-	void converterInputDone(FrameBuffer *buffer);
+-	void converterOutputDone(FrameBuffer *buffer);
++	void conversionInputDone(FrameBuffer *buffer);
++	void conversionOutputDone(FrameBuffer *buffer);
+ };
+ 
+ class SimpleCameraConfiguration : public CameraConfiguration
+@@ -503,8 +504,8 @@ int SimpleCameraData::init()
+ 				<< "Failed to create converter, disabling format conversion";
+ 			converter_.reset();
+ 		} else {
+-			converter_->inputBufferReady.connect(this, &SimpleCameraData::converterInputDone);
+-			converter_->outputBufferReady.connect(this, &SimpleCameraData::converterOutputDone);
++			converter_->inputBufferReady.connect(this, &SimpleCameraData::conversionInputDone);
++			converter_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
+ 		}
+ 	}
+ 
+@@ -740,7 +741,7 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 	 * point converting an erroneous buffer.
+ 	 */
+ 	if (buffer->metadata().status != FrameMetadata::FrameSuccess) {
+-		if (!useConverter_) {
++		if (!useConversion_) {
+ 			/* No conversion, just complete the request. */
+ 			Request *request = buffer->request();
+ 			pipe->completeBuffer(request, buffer);
+@@ -756,16 +757,16 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 		if (buffer->metadata().status != FrameMetadata::FrameCancelled)
+ 			video_->queueBuffer(buffer);
+ 
+-		if (converterQueue_.empty())
++		if (conversionQueue_.empty())
+ 			return;
+ 
+ 		Request *request = nullptr;
+-		for (auto &item : converterQueue_.front()) {
++		for (auto &item : conversionQueue_.front()) {
+ 			FrameBuffer *outputBuffer = item.second;
+ 			request = outputBuffer->request();
+ 			pipe->completeBuffer(request, outputBuffer);
+ 		}
+-		converterQueue_.pop();
++		conversionQueue_.pop();
+ 
+ 		if (request)
+ 			pipe->completeRequest(request);
+@@ -782,9 +783,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 	 */
+ 	Request *request = buffer->request();
+ 
+-	if (useConverter_ && !converterQueue_.empty()) {
++	if (useConversion_ && !conversionQueue_.empty()) {
+ 		const std::map<unsigned int, FrameBuffer *> &outputs =
+-			converterQueue_.front();
++			conversionQueue_.front();
+ 		if (!outputs.empty()) {
+ 			FrameBuffer *outputBuffer = outputs.begin()->second;
+ 			if (outputBuffer)
+@@ -801,14 +802,14 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 	 * conversion is needed. If there's no queued request, just requeue the
+ 	 * captured buffer for capture.
+ 	 */
+-	if (useConverter_) {
+-		if (converterQueue_.empty()) {
++	if (useConversion_) {
++		if (conversionQueue_.empty()) {
+ 			video_->queueBuffer(buffer);
+ 			return;
+ 		}
+ 
+-		converter_->queueBuffers(buffer, converterQueue_.front());
+-		converterQueue_.pop();
++		converter_->queueBuffers(buffer, conversionQueue_.front());
++		conversionQueue_.pop();
+ 		return;
+ 	}
+ 
+@@ -817,13 +818,13 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 	pipe->completeRequest(request);
+ }
+ 
+-void SimpleCameraData::converterInputDone(FrameBuffer *buffer)
++void SimpleCameraData::conversionInputDone(FrameBuffer *buffer)
+ {
+ 	/* Queue the input buffer back for capture. */
+ 	video_->queueBuffer(buffer);
+ }
+ 
+-void SimpleCameraData::converterOutputDone(FrameBuffer *buffer)
++void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
+ {
+ 	SimplePipelineHandler *pipe = SimpleCameraData::pipe();
+ 
+@@ -1189,14 +1190,14 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
+ 
+ 	/* Configure the converter if needed. */
+ 	std::vector<std::reference_wrapper<StreamConfiguration>> outputCfgs;
+-	data->useConverter_ = config->needConversion();
++	data->useConversion_ = config->needConversion();
+ 
+ 	for (unsigned int i = 0; i < config->size(); ++i) {
+ 		StreamConfiguration &cfg = config->at(i);
+ 
+ 		cfg.setStream(&data->streams_[i]);
+ 
+-		if (data->useConverter_)
++		if (data->useConversion_)
+ 			outputCfgs.push_back(cfg);
+ 	}
+ 
+@@ -1222,7 +1223,7 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
+ 	 * Export buffers on the converter or capture video node, depending on
+ 	 * whether the converter is used or not.
+ 	 */
+-	if (data->useConverter_)
++	if (data->useConversion_)
+ 		return data->converter_->exportBuffers(data->streamIndex(stream),
+ 						       count, buffers);
+ 	else
+@@ -1243,13 +1244,13 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
+ 		return -EBUSY;
+ 	}
+ 
+-	if (data->useConverter_) {
++	if (data->useConversion_) {
+ 		/*
+ 		 * When using the converter allocate a fixed number of internal
+ 		 * buffers.
+ 		 */
+ 		ret = video->allocateBuffers(kNumInternalBuffers,
+-					     &data->converterBuffers_);
++					     &data->conversionBuffers_);
+ 	} else {
+ 		/* Otherwise, prepare for using buffers from the only stream. */
+ 		Stream *stream = &data->streams_[0];
+@@ -1268,7 +1269,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
+ 		return ret;
+ 	}
+ 
+-	if (data->useConverter_) {
++	if (data->useConversion_) {
+ 		ret = data->converter_->start();
+ 		if (ret < 0) {
+ 			stop(camera);
+@@ -1276,7 +1277,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
+ 		}
+ 
+ 		/* Queue all internal buffers for capture. */
+-		for (std::unique_ptr<FrameBuffer> &buffer : data->converterBuffers_)
++		for (std::unique_ptr<FrameBuffer> &buffer : data->conversionBuffers_)
+ 			video->queueBuffer(buffer.get());
+ 	}
+ 
+@@ -1288,7 +1289,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
+ 	SimpleCameraData *data = cameraData(camera);
+ 	V4L2VideoDevice *video = data->video_;
+ 
+-	if (data->useConverter_)
++	if (data->useConversion_)
+ 		data->converter_->stop();
+ 
+ 	video->streamOff();
+@@ -1296,7 +1297,7 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
+ 
+ 	video->bufferReady.disconnect(data, &SimpleCameraData::bufferReady);
+ 
+-	data->converterBuffers_.clear();
++	data->conversionBuffers_.clear();
+ 
+ 	releasePipeline(data);
+ }
+@@ -1314,7 +1315,7 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
+ 		 * queue, it will be handed to the converter in the capture
+ 		 * completion handler.
+ 		 */
+-		if (data->useConverter_) {
++		if (data->useConversion_) {
+ 			buffers.emplace(data->streamIndex(stream), buffer);
+ 		} else {
+ 			ret = data->video_->queueBuffer(buffer);
+@@ -1323,8 +1324,8 @@ int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
+ 		}
+ 	}
+ 
+-	if (data->useConverter_)
+-		data->converterQueue_.push(std::move(buffers));
++	if (data->useConversion_)
++		data->conversionQueue_.push(std::move(buffers));
+ 
+ 	return 0;
+ }
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch b/users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
new file mode 100644
index 0000000000..378a43604f
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0012-libcamera-pipeline-simple-enable-use-of-Soft-ISP-and.patch
@@ -0,0 +1,302 @@
+From d64b0fca22ef25b8a14d7fc97dfab64eb1c4f21a Mon Sep 17 00:00:00 2001
+From: Andrey Konovalov <andrey.konovalov@linaro.org>
+Date: Mon, 11 Mar 2024 15:15:16 +0100
+Subject: [PATCH 12/21] libcamera: pipeline: simple: enable use of Soft ISP and
+ Soft IPA
+
+To enable the Simple Soft ISP and Soft IPA for simple pipeline handler
+configure the build with:
+  -Dpipelines=simple -Dipas=simple
+
+Also using the Soft ISP for the particular hardware platform must
+be enabled in the supportedDevices[] table.
+
+If the pipeline uses Converter, Soft ISP and Soft IPA aren't
+available.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Andrey Konovalov <andrey.konovalov@linaro.org>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/pipeline/simple/simple.cpp | 137 ++++++++++++++++++-----
+ 1 file changed, 109 insertions(+), 28 deletions(-)
+
+diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
+index 78854ef8..c3ebb7b7 100644
+--- a/src/libcamera/pipeline/simple/simple.cpp
++++ b/src/libcamera/pipeline/simple/simple.cpp
+@@ -34,6 +34,7 @@
+ #include "libcamera/internal/device_enumerator.h"
+ #include "libcamera/internal/media_device.h"
+ #include "libcamera/internal/pipeline_handler.h"
++#include "libcamera/internal/software_isp/software_isp.h"
+ #include "libcamera/internal/v4l2_subdevice.h"
+ #include "libcamera/internal/v4l2_videodevice.h"
+ 
+@@ -185,17 +186,22 @@ struct SimplePipelineInfo {
+ 	 * and the number of streams it supports.
+ 	 */
+ 	std::vector<std::pair<const char *, unsigned int>> converters;
++	/*
++	 * Using Software ISP is to be enabled per driver.
++	 * The Software ISP can't be used together with the converters.
++	 */
++	bool swIspEnabled;
+ };
+ 
+ namespace {
+ 
+ static const SimplePipelineInfo supportedDevices[] = {
+-	{ "dcmipp", {} },
+-	{ "imx7-csi", { { "pxp", 1 } } },
+-	{ "j721e-csi2rx", {} },
+-	{ "mxc-isi", {} },
+-	{ "qcom-camss", {} },
+-	{ "sun6i-csi", {} },
++	{ "dcmipp", {}, false },
++	{ "imx7-csi", { { "pxp", 1 } }, false },
++	{ "j721e-csi2rx", {}, false },
++	{ "mxc-isi", {}, false },
++	{ "qcom-camss", {}, true },
++	{ "sun6i-csi", {}, false },
+ };
+ 
+ } /* namespace */
+@@ -274,6 +280,7 @@ public:
+ 	bool useConversion_;
+ 
+ 	std::unique_ptr<Converter> converter_;
++	std::unique_ptr<SoftwareIsp> swIsp_;
+ 
+ private:
+ 	void tryPipeline(unsigned int code, const Size &size);
+@@ -281,6 +288,9 @@ private:
+ 
+ 	void conversionInputDone(FrameBuffer *buffer);
+ 	void conversionOutputDone(FrameBuffer *buffer);
++
++	void ispStatsReady(int dummy);
++	void setSensorControls(const ControlList &sensorControls);
+ };
+ 
+ class SimpleCameraConfiguration : public CameraConfiguration
+@@ -332,6 +342,7 @@ public:
+ 	V4L2VideoDevice *video(const MediaEntity *entity);
+ 	V4L2Subdevice *subdev(const MediaEntity *entity);
+ 	MediaDevice *converter() { return converter_; }
++	bool swIspEnabled() { return swIspEnabled_; }
+ 
+ protected:
+ 	int queueRequestDevice(Camera *camera, Request *request) override;
+@@ -360,6 +371,7 @@ private:
+ 	std::map<const MediaEntity *, EntityData> entities_;
+ 
+ 	MediaDevice *converter_;
++	bool swIspEnabled_;
+ };
+ 
+ /* -----------------------------------------------------------------------------
+@@ -509,6 +521,29 @@ int SimpleCameraData::init()
+ 		}
+ 	}
+ 
++	/*
++	 * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
++	 */
++	if (!converter_ && pipe->swIspEnabled()) {
++		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());
++		if (!swIsp_->isValid()) {
++			LOG(SimplePipeline, Warning)
++				<< "Failed to create software ISP, disabling software debayering";
++			swIsp_.reset();
++		} else {
++			/*
++			 * \todo explain why SimpleCameraData::conversionInputDone() can't be directly
++			 * connected to inputBufferReady signal.
++			 */
++			swIsp_->inputBufferReady.connect(pipe, [this](FrameBuffer *buffer) {
++				this->conversionInputDone(buffer);
++			});
++			swIsp_->outputBufferReady.connect(this, &SimpleCameraData::conversionOutputDone);
++			swIsp_->ispStatsReady.connect(this, &SimpleCameraData::ispStatsReady);
++			swIsp_->setSensorControls.connect(this, &SimpleCameraData::setSensorControls);
++		}
++	}
++
+ 	video_ = pipe->video(entities_.back().entity);
+ 	ASSERT(video_);
+ 
+@@ -599,12 +634,21 @@ void SimpleCameraData::tryPipeline(unsigned int code, const Size &size)
+ 		config.captureFormat = pixelFormat;
+ 		config.captureSize = format.size;
+ 
+-		if (!converter_) {
++
++		if (converter_) {
++ 			config.outputFormats = converter_->formats(pixelFormat);
++ 			config.outputSizes = converter_->sizes(format.size);
++		} else if (swIsp_) {
++			config.outputFormats = swIsp_->formats(pixelFormat);
++			config.outputSizes = swIsp_->sizes(pixelFormat, format.size);
++			if (config.outputFormats.empty()) {
++				/* Do not use swIsp for unsupported pixelFormat's: */
++				config.outputFormats = { pixelFormat };
++				config.outputSizes = config.captureSize;
++			}
++		} else {
+ 			config.outputFormats = { pixelFormat };
+ 			config.outputSizes = config.captureSize;
+-		} else {
+-			config.outputFormats = converter_->formats(pixelFormat);
+-			config.outputSizes = converter_->sizes(format.size);
+ 		}
+ 
+ 		configs_.push_back(config);
+@@ -750,9 +794,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 		}
+ 
+ 		/*
+-		 * The converter is in use. Requeue the internal buffer for
+-		 * capture (unless the stream is being stopped), and complete
+-		 * the request with all the user-facing buffers.
++		 * The converter or Software ISP is in use. Requeue the internal
++		 * buffer for capture (unless the stream is being stopped), and
++		 * complete the request with all the user-facing buffers.
+ 		 */
+ 		if (buffer->metadata().status != FrameMetadata::FrameCancelled)
+ 			video_->queueBuffer(buffer);
+@@ -798,9 +842,9 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 					buffer->metadata().timestamp);
+ 
+ 	/*
+-	 * Queue the captured and the request buffer to the converter if format
+-	 * conversion is needed. If there's no queued request, just requeue the
+-	 * captured buffer for capture.
++	 * Queue the captured and the request buffer to the converter or Software
++	 * ISP if format conversion is needed. If there's no queued request, just
++	 * requeue the captured buffer for capture.
+ 	 */
+ 	if (useConversion_) {
+ 		if (conversionQueue_.empty()) {
+@@ -808,7 +852,11 @@ void SimpleCameraData::bufferReady(FrameBuffer *buffer)
+ 			return;
+ 		}
+ 
+-		converter_->queueBuffers(buffer, conversionQueue_.front());
++		if (converter_)
++			converter_->queueBuffers(buffer, conversionQueue_.front());
++		else
++			swIsp_->queueBuffers(buffer, conversionQueue_.front());
++
+ 		conversionQueue_.pop();
+ 		return;
+ 	}
+@@ -834,6 +882,18 @@ void SimpleCameraData::conversionOutputDone(FrameBuffer *buffer)
+ 		pipe->completeRequest(request);
+ }
+ 
++void SimpleCameraData::ispStatsReady([[maybe_unused]] int dummy)
++{
++	swIsp_->processStats(sensor_->getControls({ V4L2_CID_ANALOGUE_GAIN,
++						    V4L2_CID_EXPOSURE }));
++}
++
++void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
++{
++	ControlList ctrls(sensorControls);
++	sensor_->setControls(&ctrls);
++}
++
+ /* Retrieve all source pads connected to a sink pad through active routes. */
+ std::vector<const MediaPad *> SimpleCameraData::routedSourcePads(MediaPad *sink)
+ {
+@@ -1046,8 +1106,10 @@ CameraConfiguration::Status SimpleCameraConfiguration::validate()
+ 		/* Set the stride, frameSize and bufferCount. */
+ 		if (needConversion_) {
+ 			std::tie(cfg.stride, cfg.frameSize) =
+-				data_->converter_->strideAndFrameSize(cfg.pixelFormat,
+-								      cfg.size);
++				(data_->converter_) ? data_->converter_->strideAndFrameSize(cfg.pixelFormat,
++											    cfg.size)
++						    : data_->swIsp_->strideAndFrameSize(cfg.pixelFormat,
++											cfg.size);
+ 			if (cfg.stride == 0)
+ 				return Invalid;
+ 		} else {
+@@ -1210,7 +1272,9 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
+ 	inputCfg.stride = captureFormat.planes[0].bpl;
+ 	inputCfg.bufferCount = kNumInternalBuffers;
+ 
+-	return data->converter_->configure(inputCfg, outputCfgs);
++	return (data->converter_) ? data->converter_->configure(inputCfg, outputCfgs)
++				  : data->swIsp_->configure(inputCfg, outputCfgs,
++							    data->sensor_->controls());
+ }
+ 
+ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
+@@ -1224,8 +1288,10 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
+ 	 * whether the converter is used or not.
+ 	 */
+ 	if (data->useConversion_)
+-		return data->converter_->exportBuffers(data->streamIndex(stream),
+-						       count, buffers);
++		return (data->converter_) ? data->converter_->exportBuffers(data->streamIndex(stream),
++									    count, buffers)
++					  : data->swIsp_->exportBuffers(data->streamIndex(stream),
++									count, buffers);
+ 	else
+ 		return data->video_->exportBuffers(count, buffers);
+ }
+@@ -1270,10 +1336,18 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
+ 	}
+ 
+ 	if (data->useConversion_) {
+-		ret = data->converter_->start();
+-		if (ret < 0) {
+-			stop(camera);
+-			return ret;
++		if (data->converter_) {
++			ret = data->converter_->start();
++			if (ret < 0) {
++				stop(camera);
++				return ret;
++			}
++		} else if (data->swIsp_) {
++			ret = data->swIsp_->start();
++			if (ret < 0) {
++				stop(camera);
++				return ret;
++			}
+ 		}
+ 
+ 		/* Queue all internal buffers for capture. */
+@@ -1289,8 +1363,13 @@ void SimplePipelineHandler::stopDevice(Camera *camera)
+ 	SimpleCameraData *data = cameraData(camera);
+ 	V4L2VideoDevice *video = data->video_;
+ 
+-	if (data->useConversion_)
+-		data->converter_->stop();
++	if (data->useConversion_) {
++		if (data->converter_)
++			data->converter_->stop();
++		else if (data->swIsp_) {
++			data->swIsp_->stop();
++		}
++	}
+ 
+ 	video->streamOff();
+ 	video->releaseBuffers();
+@@ -1452,6 +1531,8 @@ bool SimplePipelineHandler::match(DeviceEnumerator *enumerator)
+ 		}
+ 	}
+ 
++  swIspEnabled_ = info->swIspEnabled;
++
+ 	/* Locate the sensors. */
+ 	std::vector<MediaEntity *> sensors = locateSensors();
+ 	if (sensors.empty()) {
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch b/users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
new file mode 100644
index 0000000000..1a57d690ff
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0013-libcamera-swstats_cpu-Add-support-for-8-10-and-12-bp.patch
@@ -0,0 +1,203 @@
+From aabc53453d542495d9da25411f57308c01f2bc28 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:17 +0100
+Subject: [PATCH 13/21] libcamera: swstats_cpu: Add support for 8, 10 and 12
+ bpp unpacked bayer input
+
+Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard
+bayer orders.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/software_isp/swstats_cpu.cpp | 128 +++++++++++++++++++++
+ src/libcamera/software_isp/swstats_cpu.h   |   9 ++
+ 2 files changed, 137 insertions(+)
+
+diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
+index 448d0e4c..be310f56 100644
+--- a/src/libcamera/software_isp/swstats_cpu.cpp
++++ b/src/libcamera/software_isp/swstats_cpu.cpp
+@@ -71,6 +71,83 @@ static const unsigned int kBlueYMul = 29; /* 0.114 * 256 */
+ 	stats_.sumG_ += sumG;       \
+ 	stats_.sumB_ += sumB;
+ 
++void SwStatsCpu::statsBGGR8Line0(const uint8_t *src[])
++{
++	const uint8_t *src0 = src[1] + window_.x;
++	const uint8_t *src1 = src[2] + window_.x;
++
++	SWSTATS_START_LINE_STATS(uint8_t)
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	/* x += 4 sample every other 2x2 block */
++	for (int x = 0; x < (int)window_.width; x += 4) {
++		b = src0[x];
++		g = src0[x + 1];
++		g2 = src1[x];
++		r = src1[x + 1];
++
++		g = (g + g2) / 2;
++
++		SWSTATS_ACCUMULATE_LINE_STATS(1)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
++void SwStatsCpu::statsBGGR10Line0(const uint8_t *src[])
++{
++	const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
++	const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
++
++	SWSTATS_START_LINE_STATS(uint16_t)
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	/* x += 4 sample every other 2x2 block */
++	for (int x = 0; x < (int)window_.width; x += 4) {
++		b = src0[x];
++		g = src0[x + 1];
++		g2 = src1[x];
++		r = src1[x + 1];
++
++		g = (g + g2) / 2;
++
++		/* divide Y by 4 for 10 -> 8 bpp value */
++		SWSTATS_ACCUMULATE_LINE_STATS(4)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
++void SwStatsCpu::statsBGGR12Line0(const uint8_t *src[])
++{
++	const uint16_t *src0 = (const uint16_t *)src[1] + window_.x;
++	const uint16_t *src1 = (const uint16_t *)src[2] + window_.x;
++
++	SWSTATS_START_LINE_STATS(uint16_t)
++
++	if (swapLines_)
++		std::swap(src0, src1);
++
++	/* x += 4 sample every other 2x2 block */
++	for (int x = 0; x < (int)window_.width; x += 4) {
++		b = src0[x];
++		g = src0[x + 1];
++		g2 = src1[x];
++		r = src1[x + 1];
++
++		g = (g + g2) / 2;
++
++		/* divide Y by 16 for 12 -> 8 bpp value */
++		SWSTATS_ACCUMULATE_LINE_STATS(16)
++	}
++
++	SWSTATS_FINISH_LINE_STATS()
++}
++
+ void SwStatsCpu::statsBGGR10PLine0(const uint8_t *src[])
+ {
+ 	const uint8_t *src0 = src[1] + window_.x * 5 / 4;
+@@ -147,6 +224,42 @@ void SwStatsCpu::finishFrame(void)
+ 	statsReady.emit(0);
+ }
+ 
++/**
++ * \brief Setup SwStatsCpu object for standard Bayer orders
++ * \param[in] order The Bayer order
++ *
++ * Check if order is a standard Bayer order and setup xShift_ and swapLines_
++ * so that a single BGGR stats function can be used for all 4 standard orders.
++ */
++int SwStatsCpu::setupStandardBayerOrder(BayerFormat::Order order)
++{
++	switch (order) {
++	case BayerFormat::BGGR:
++		xShift_ = 0;
++		swapLines_ = false;
++		break;
++	case BayerFormat::GBRG:
++		xShift_ = 1; /* BGGR -> GBRG */
++		swapLines_ = false;
++		break;
++	case BayerFormat::GRBG:
++		xShift_ = 0;
++		swapLines_ = true; /* BGGR -> GRBG */
++		break;
++	case BayerFormat::RGGB:
++		xShift_ = 1; /* BGGR -> GBRG */
++		swapLines_ = true; /* GBRG -> RGGB */
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	patternSize_.height = 2;
++	patternSize_.width = 2;
++	ySkipMask_ = 0x02; /* Skip every 3th and 4th line */
++	return 0;
++}
++
+ /**
+  * \brief Configure the statistics object for the passed in input format.
+  * \param[in] inputCfg The input format
+@@ -158,6 +271,21 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
+ 
++	if (bayerFormat.packing == BayerFormat::Packing::None &&
++	    setupStandardBayerOrder(bayerFormat.order) == 0) {
++		switch (bayerFormat.bitDepth) {
++		case 8:
++			stats0_ = &SwStatsCpu::statsBGGR8Line0;
++			return 0;
++		case 10:
++			stats0_ = &SwStatsCpu::statsBGGR10Line0;
++			return 0;
++		case 12:
++			stats0_ = &SwStatsCpu::statsBGGR12Line0;
++			return 0;
++		}
++	}
++
+ 	if (bayerFormat.bitDepth == 10 &&
+ 	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ 		patternSize_.height = 2;
+diff --git a/src/libcamera/software_isp/swstats_cpu.h b/src/libcamera/software_isp/swstats_cpu.h
+index 0ac9ae71..bbbcf69b 100644
+--- a/src/libcamera/software_isp/swstats_cpu.h
++++ b/src/libcamera/software_isp/swstats_cpu.h
+@@ -17,6 +17,7 @@
+ 
+ #include <libcamera/geometry.h>
+ 
++#include "libcamera/internal/bayer_format.h"
+ #include "libcamera/internal/shared_mem_object.h"
+ #include "libcamera/internal/software_isp/swisp_stats.h"
+ 
+@@ -120,6 +121,14 @@ private:
+ 	 */
+ 	using statsProcessFn = void (SwStatsCpu::*)(const uint8_t *src[]);
+ 
++	int setupStandardBayerOrder(BayerFormat::Order order);
++	/* Bayer 8 bpp unpacked */
++	void statsBGGR8Line0(const uint8_t *src[]);
++	/* Bayer 10 bpp unpacked */
++	void statsBGGR10Line0(const uint8_t *src[]);
++	/* Bayer 12 bpp unpacked */
++	void statsBGGR12Line0(const uint8_t *src[]);
++	/* Bayer 10 bpp packed */
+ 	void statsBGGR10PLine0(const uint8_t *src[]);
+ 	void statsGBRG10PLine0(const uint8_t *src[]);
+ 
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch b/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
new file mode 100644
index 0000000000..c7edf49828
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0014-libcamera-debayer_cpu-Add-support-for-8-10-and-12-bp.patch
@@ -0,0 +1,234 @@
+From 5f3647bd4f12dd62256a425c49fd18a0f5990930 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:18 +0100
+Subject: [PATCH 14/21] libcamera: debayer_cpu: Add support for 8, 10 and 12
+ bpp unpacked bayer input
+
+Add support for 8, 10 and 12 bpp unpacked bayer input for all 4 standard
+bayer orders.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/software_isp/debayer_cpu.cpp | 128 +++++++++++++++++++++
+ src/libcamera/software_isp/debayer_cpu.h   |  13 +++
+ 2 files changed, 141 insertions(+)
+
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index f932362c..eb1c2718 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -56,6 +56,11 @@ DebayerCpu::~DebayerCpu()
+ 		free(lineBuffers_[i]);
+ }
+ 
++#define DECLARE_SRC_POINTERS(pixel_t)                            \
++	const pixel_t *prev = (const pixel_t *)src[0] + xShift_; \
++	const pixel_t *curr = (const pixel_t *)src[1] + xShift_; \
++	const pixel_t *next = (const pixel_t *)src[2] + xShift_;
++
+ // RGR
+ // GBG
+ // RGR
+@@ -92,6 +97,70 @@ DebayerCpu::~DebayerCpu()
+ 	*dst++ = red_[curr[x] / (div)];                                                        \
+ 	x++;
+ 
++void DebayerCpu::debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint8_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		BGGR_BGR888(1, 1, 1)
++		GBRG_BGR888(1, 1, 1)
++	}
++}
++
++void DebayerCpu::debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint8_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		GRBG_BGR888(1, 1, 1)
++		RGGB_BGR888(1, 1, 1)
++	}
++}
++
++void DebayerCpu::debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint16_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		/* divide values by 4 for 10 -> 8 bpp value */
++		BGGR_BGR888(1, 1, 4)
++		GBRG_BGR888(1, 1, 4)
++	}
++}
++
++void DebayerCpu::debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint16_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		/* divide values by 4 for 10 -> 8 bpp value */
++		GRBG_BGR888(1, 1, 4)
++		RGGB_BGR888(1, 1, 4)
++	}
++}
++
++void DebayerCpu::debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint16_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		/* divide values by 16 for 12 -> 8 bpp value */
++		BGGR_BGR888(1, 1, 16)
++		GBRG_BGR888(1, 1, 16)
++	}
++}
++
++void DebayerCpu::debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[])
++{
++	DECLARE_SRC_POINTERS(uint16_t)
++
++	for (int x = 0; x < (int)window_.width;) {
++		/* divide values by 16 for 12 -> 8 bpp value */
++		GRBG_BGR888(1, 1, 16)
++		RGGB_BGR888(1, 1, 16)
++	}
++}
++
+ void DebayerCpu::debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[])
+ {
+ 	const int width_in_bytes = window_.width * 5 / 4;
+@@ -193,6 +262,16 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
++	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
++	    bayerFormat.packing == BayerFormat::Packing::None &&
++	    isStandardBayerOrder(bayerFormat.order)) {
++		config.bpp = (bayerFormat.bitDepth + 7) & ~7;
++		config.patternSize.width = 2;
++		config.patternSize.height = 2;
++		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
++		return 0;
++	}
++
+ 	if (bayerFormat.bitDepth == 10 &&
+ 	    bayerFormat.packing == BayerFormat::Packing::CSI2 &&
+ 	    isStandardBayerOrder(bayerFormat.order)) {
+@@ -220,12 +299,61 @@ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &c
+ 	return -EINVAL;
+ }
+ 
++/*
++ * Check for standard Bayer orders and set xShift_ and swap debayer0/1, so that
++ * a single pair of BGGR debayer functions can be used for all 4 standard orders.
++ */
++int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
++{
++	switch (order) {
++	case BayerFormat::BGGR:
++		break;
++	case BayerFormat::GBRG:
++		xShift_ = 1; /* BGGR -> GBRG */
++		break;
++	case BayerFormat::GRBG:
++		std::swap(debayer0_, debayer1_); /* BGGR -> GRBG */
++		break;
++	case BayerFormat::RGGB:
++		xShift_ = 1; /* BGGR -> GBRG */
++		std::swap(debayer0_, debayer1_); /* GBRG -> RGGB */
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
+ /* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */
+ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat)
+ {
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
++	xShift_ = 0;
++
++	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
++	    bayerFormat.packing == BayerFormat::Packing::None &&
++	    isStandardBayerOrder(bayerFormat.order)) {
++		switch (bayerFormat.bitDepth) {
++		case 8:
++			debayer0_ = &DebayerCpu::debayer8_BGBG_BGR888;
++			debayer1_ = &DebayerCpu::debayer8_GRGR_BGR888;
++			break;
++		case 10:
++			debayer0_ = &DebayerCpu::debayer10_BGBG_BGR888;
++			debayer1_ = &DebayerCpu::debayer10_GRGR_BGR888;
++			break;
++		case 12:
++			debayer0_ = &DebayerCpu::debayer12_BGBG_BGR888;
++			debayer1_ = &DebayerCpu::debayer12_GRGR_BGR888;
++			break;
++		}
++		setupStandardBayerOrder(bayerFormat.order);
++		return 0;
++	}
++
+ 	if (bayerFormat.bitDepth == 10 &&
+ 	    bayerFormat.packing == BayerFormat::Packing::CSI2) {
+ 		switch (bayerFormat.order) {
+diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
+index 8a51ed85..fd1fa180 100644
+--- a/src/libcamera/software_isp/debayer_cpu.h
++++ b/src/libcamera/software_isp/debayer_cpu.h
+@@ -17,6 +17,8 @@
+ 
+ #include <libcamera/base/object.h>
+ 
++#include "libcamera/internal/bayer_format.h"
++
+ #include "debayer.h"
+ #include "swstats_cpu.h"
+ 
+@@ -82,6 +84,15 @@ private:
+ 	 */
+ 	using debayerFn = void (DebayerCpu::*)(uint8_t *dst, const uint8_t *src[]);
+ 
++	/* 8-bit raw bayer format */
++	void debayer8_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer8_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
++	/* unpacked 10-bit raw bayer format */
++	void debayer10_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer10_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
++	/* unpacked 12-bit raw bayer format */
++	void debayer12_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
++	void debayer12_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
+ 	/* CSI-2 packed 10-bit raw bayer format (all the 4 orders) */
+ 	void debayer10P_BGBG_BGR888(uint8_t *dst, const uint8_t *src[]);
+ 	void debayer10P_GRGR_BGR888(uint8_t *dst, const uint8_t *src[]);
+@@ -103,6 +114,7 @@ private:
+ 
+ 	int getInputConfig(PixelFormat inputFormat, DebayerInputConfig &config);
+ 	int getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config);
++	int setupStandardBayerOrder(BayerFormat::Order order);
+ 	int setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat);
+ 	void setupInputMemcpy(const uint8_t *linePointers[]);
+ 	void shiftLinePointers(const uint8_t *linePointers[], const uint8_t *src);
+@@ -131,6 +143,7 @@ private:
+ 	unsigned int lineBufferLength_;
+ 	unsigned int lineBufferPadding_;
+ 	unsigned int lineBufferIndex_;
++	unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
+ 	bool enableInputMemcpy_;
+ 	float gamma_correction_;
+ 	unsigned int measuredFrames_;
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch b/users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch
new file mode 100644
index 0000000000..0abca2ea82
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0015-libcamera-debayer_cpu-Add-BGR888-output-support.patch
@@ -0,0 +1,127 @@
+From 186db51d54bcbd4d5096bea1e4396966c2dad001 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:19 +0100
+Subject: [PATCH 15/21] libcamera: debayer_cpu: Add BGR888 output support
+
+BGR888 is RGB888 with the red and blue pixels swapped, adjust
+the debayering to swap the red and blue pixels in the bayer pattern
+to add support for writing formats::BGR888.
+
+Tested-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> # sc8280xp Lenovo x13s
+Tested-by: Pavel Machek <pavel@ucw.cz>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
+---
+ src/libcamera/software_isp/debayer_cpu.cpp | 42 +++++++++++++++++++---
+ src/libcamera/software_isp/debayer_cpu.h   |  1 +
+ 2 files changed, 38 insertions(+), 5 deletions(-)
+
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index eb1c2718..a1692693 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -268,7 +268,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 		config.bpp = (bayerFormat.bitDepth + 7) & ~7;
+ 		config.patternSize.width = 2;
+ 		config.patternSize.height = 2;
+-		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
++		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888, formats::BGR888 });
+ 		return 0;
+ 	}
+ 
+@@ -278,7 +278,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 		config.bpp = 10;
+ 		config.patternSize.width = 4; /* 5 bytes per *4* pixels */
+ 		config.patternSize.height = 2;
+-		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888 });
++		config.outputFormats = std::vector<PixelFormat>({ formats::RGB888, formats::BGR888 });
+ 		return 0;
+ 	}
+ 
+@@ -289,7 +289,7 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 
+ int DebayerCpu::getOutputConfig(PixelFormat outputFormat, DebayerOutputConfig &config)
+ {
+-	if (outputFormat == formats::RGB888) {
++	if (outputFormat == formats::RGB888 || outputFormat == formats::BGR888) {
+ 		config.bpp = 24;
+ 		return 0;
+ 	}
+@@ -325,13 +325,41 @@ int DebayerCpu::setupStandardBayerOrder(BayerFormat::Order order)
+ 	return 0;
+ }
+ 
+-/* TODO: this ignores outputFormat since there is only 1 supported outputFormat for now */
+-int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] PixelFormat outputFormat)
++int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputFormat)
+ {
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
+ 	xShift_ = 0;
++	swapRedBlueGains_ = false;
++
++	switch (outputFormat) {
++	case formats::RGB888:
++		break;
++	case formats::BGR888:
++		/* Swap R and B in bayer order to generate BGR888 instead of RGB888 */
++		swapRedBlueGains_ = true;
++
++		switch (bayerFormat.order) {
++		case BayerFormat::BGGR:
++			bayerFormat.order = BayerFormat::RGGB;
++			break;
++		case BayerFormat::GBRG:
++			bayerFormat.order = BayerFormat::GRBG;
++			break;
++		case BayerFormat::GRBG:
++			bayerFormat.order = BayerFormat::GBRG;
++			break;
++		case BayerFormat::RGGB:
++			bayerFormat.order = BayerFormat::BGGR;
++			break;
++		default:
++			goto invalid_fmt;
++		}
++		break;
++	default:
++		goto invalid_fmt;
++	}
+ 
+ 	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
+ 	    bayerFormat.packing == BayerFormat::Packing::None &&
+@@ -378,6 +406,7 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, [[maybe_unused]] Pi
+ 		}
+ 	}
+ 
++invalid_fmt:
+ 	LOG(Debayer, Error) << "Unsupported input output format combination";
+ 	return -EINVAL;
+ }
+@@ -661,6 +690,9 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
+ 		gamma_correction_ = params.gamma;
+ 	}
+ 
++	if (swapRedBlueGains_)
++		std::swap(params.gainR, params.gainB);
++
+ 	for (unsigned int i = 0; i < kRGBLookupSize; i++) {
+ 		constexpr unsigned int div =
+ 			kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize;
+diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
+index fd1fa180..5f44fc65 100644
+--- a/src/libcamera/software_isp/debayer_cpu.h
++++ b/src/libcamera/software_isp/debayer_cpu.h
+@@ -145,6 +145,7 @@ private:
+ 	unsigned int lineBufferIndex_;
+ 	unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
+ 	bool enableInputMemcpy_;
++	bool swapRedBlueGains_;
+ 	float gamma_correction_;
+ 	unsigned int measuredFrames_;
+ 	int64_t frameProcessTime_;
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch b/users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
new file mode 100644
index 0000000000..724b67033f
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0016-libcamera-Add-support-for-IGIG_GBGR_IGIG_GRGB-bayer-.patch
@@ -0,0 +1,237 @@
+From e9580d30a1a79fce1ebd72ae74ceb4a3d1cf8fbb Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 19 Dec 2023 11:16:26 +0100
+Subject: [PATCH 16/21] libcamera: Add support for IGIG_GBGR_IGIG_GRGB bayer
+ order DNU
+
+The ov01a1s sensor has the following bayer pattern (4x4 tile repeating):
+
+IGIG
+GBGR
+IGIG
+GRGB
+
+Add support for this PixelFormat to libcamera.
+
+Do Not Upstream, first the include/linux/media-bus-format.h and
+include/linux/videodev2.h changes need to land in the upstream kernel.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ include/libcamera/internal/bayer_format.h |  3 ++-
+ include/linux/drm_fourcc.h                |  2 ++
+ include/linux/media-bus-format.h          |  4 +++-
+ include/linux/videodev2.h                 |  3 +++
+ src/libcamera/bayer_format.cpp            |  5 +++++
+ src/libcamera/camera_sensor.cpp           |  3 +++
+ src/libcamera/formats.cpp                 | 20 ++++++++++++++++++++
+ src/libcamera/formats.yaml                |  5 +++++
+ src/libcamera/v4l2_pixelformat.cpp        |  4 ++++
+ src/libcamera/v4l2_subdevice.cpp          |  1 +
+ 10 files changed, 48 insertions(+), 2 deletions(-)
+
+diff --git a/include/libcamera/internal/bayer_format.h b/include/libcamera/internal/bayer_format.h
+index 78ba3969..e77106c3 100644
+--- a/include/libcamera/internal/bayer_format.h
++++ b/include/libcamera/internal/bayer_format.h
+@@ -27,7 +27,8 @@ public:
+ 		GBRG = 1,
+ 		GRBG = 2,
+ 		RGGB = 3,
+-		MONO = 4
++		MONO = 4,
++		IGIG_GBGR_IGIG_GRGB = 5,
+ 	};
+ 
+ 	enum class Packing : uint16_t {
+diff --git a/include/linux/drm_fourcc.h b/include/linux/drm_fourcc.h
+index 1496e097..750ae8c9 100644
+--- a/include/linux/drm_fourcc.h
++++ b/include/linux/drm_fourcc.h
+@@ -405,6 +405,8 @@ extern "C" {
+ #define DRM_FORMAT_SGRBG10	fourcc_code('B', 'A', '1', '0')
+ #define DRM_FORMAT_SGBRG10	fourcc_code('G', 'B', '1', '0')
+ #define DRM_FORMAT_SBGGR10	fourcc_code('B', 'G', '1', '0')
++/* Mixed 10 bit bayer + ir pixel pattern found on Omnivision ov01a1s */
++#define DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10 fourcc_code('O', 'V', '1', 'S')
+ 
+ /* 12-bit Bayer formats */
+ #define DRM_FORMAT_SRGGB12	fourcc_code('R', 'G', '1', '2')
+diff --git a/include/linux/media-bus-format.h b/include/linux/media-bus-format.h
+index 0dfc11ee..c5fbda0e 100644
+--- a/include/linux/media-bus-format.h
++++ b/include/linux/media-bus-format.h
+@@ -112,7 +112,7 @@
+ #define MEDIA_BUS_FMT_YUV16_1X48		0x202a
+ #define MEDIA_BUS_FMT_UYYVYY16_0_5X48		0x202b
+ 
+-/* Bayer - next is	0x3021 */
++/* Bayer - next is 0x3022 */
+ #define MEDIA_BUS_FMT_SBGGR8_1X8		0x3001
+ #define MEDIA_BUS_FMT_SGBRG8_1X8		0x3013
+ #define MEDIA_BUS_FMT_SGRBG8_1X8		0x3002
+@@ -145,6 +145,8 @@
+ #define MEDIA_BUS_FMT_SGBRG16_1X16		0x301e
+ #define MEDIA_BUS_FMT_SGRBG16_1X16		0x301f
+ #define MEDIA_BUS_FMT_SRGGB16_1X16		0x3020
++/* Mixed bayer + ir pixel pattern found on ov01a1s */
++#define MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10 0x3021
+ 
+ /* JPEG compressed formats - next is	0x4002 */
+ #define MEDIA_BUS_FMT_JPEG_1X8			0x4001
+diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
+index bfb315d6..13c6c9d3 100644
+--- a/include/linux/videodev2.h
++++ b/include/linux/videodev2.h
+@@ -678,6 +678,9 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_SGBRG16 v4l2_fourcc('G', 'B', '1', '6') /* 16  GBGB.. RGRG.. */
+ #define V4L2_PIX_FMT_SGRBG16 v4l2_fourcc('G', 'R', '1', '6') /* 16  GRGR.. BGBG.. */
+ #define V4L2_PIX_FMT_SRGGB16 v4l2_fourcc('R', 'G', '1', '6') /* 16  RGRG.. GBGB.. */
++	/* 10bit mixed bayer + ir pixel pattern found on ov01a1s */
++#define V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10  v4l2_fourcc('O', 'V', '1', 'S') /* unpacked */
++#define V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P v4l2_fourcc('O', 'V', '1', 'P') /* packed */
+ 
+ /* HSV formats */
+ #define V4L2_PIX_FMT_HSV24 v4l2_fourcc('H', 'S', 'V', '3')
+diff --git a/src/libcamera/bayer_format.cpp b/src/libcamera/bayer_format.cpp
+index 3bf15fb4..ae227540 100644
+--- a/src/libcamera/bayer_format.cpp
++++ b/src/libcamera/bayer_format.cpp
+@@ -108,6 +108,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{
+ 		{ formats::SGRBG10, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10) } },
+ 	{ { BayerFormat::RGGB, 10, BayerFormat::Packing::None },
+ 		{ formats::SRGGB10, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10) } },
++	{ { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::None },
++		{ formats::SIGIG_GBGR_IGIG_GRGB10, V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10) } },
+ 	{ { BayerFormat::BGGR, 10, BayerFormat::Packing::CSI2 },
+ 		{ formats::SBGGR10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P) } },
+ 	{ { BayerFormat::GBRG, 10, BayerFormat::Packing::CSI2 },
+@@ -116,6 +118,8 @@ const std::map<BayerFormat, Formats, BayerFormatComparator> bayerToFormat{
+ 		{ formats::SGRBG10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SGRBG10P) } },
+ 	{ { BayerFormat::RGGB, 10, BayerFormat::Packing::CSI2 },
+ 		{ formats::SRGGB10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P) } },
++	{ { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::CSI2 },
++		{ formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P) } },
+ 	{ { BayerFormat::BGGR, 10, BayerFormat::Packing::IPU3 },
+ 		{ formats::SBGGR10_IPU3, V4L2PixelFormat(V4L2_PIX_FMT_IPU3_SBGGR10) } },
+ 	{ { BayerFormat::GBRG, 10, BayerFormat::Packing::IPU3 },
+@@ -193,6 +197,7 @@ const std::unordered_map<unsigned int, BayerFormat> mbusCodeToBayer{
+ 	{ MEDIA_BUS_FMT_SGBRG10_1X10, { BayerFormat::GBRG, 10, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SGRBG10_1X10, { BayerFormat::GRBG, 10, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SRGGB10_1X10, { BayerFormat::RGGB, 10, BayerFormat::Packing::None } },
++	{ MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { BayerFormat::IGIG_GBGR_IGIG_GRGB, 10, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SBGGR12_1X12, { BayerFormat::BGGR, 12, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SGBRG12_1X12, { BayerFormat::GBRG, 12, BayerFormat::Packing::None } },
+ 	{ MEDIA_BUS_FMT_SGRBG12_1X12, { BayerFormat::GRBG, 12, BayerFormat::Packing::None } },
+diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
+index 0ef78d9c..f19f72ea 100644
+--- a/src/libcamera/camera_sensor.cpp
++++ b/src/libcamera/camera_sensor.cpp
+@@ -510,6 +510,9 @@ int CameraSensor::initProperties()
+ 		case BayerFormat::MONO:
+ 			cfa = properties::draft::MONO;
+ 			break;
++		case BayerFormat::IGIG_GBGR_IGIG_GRGB:
++			cfa = properties::draft::RGB;
++			break;
+ 		}
+ 
+ 		properties_.set(properties::draft::ColorFilterArrangement, cfa);
+diff --git a/src/libcamera/formats.cpp b/src/libcamera/formats.cpp
+index 447e6238..aef7d598 100644
+--- a/src/libcamera/formats.cpp
++++ b/src/libcamera/formats.cpp
+@@ -599,6 +599,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
+ 		.pixelsPerGroup = 2,
+ 		.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
+ 	} },
++	{ formats::SIGIG_GBGR_IGIG_GRGB10, {
++		.name = "SIGIG_GBGR_IGIG_GRGB10",
++		.format = formats::SIGIG_GBGR_IGIG_GRGB10,
++		.v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10), },
++		.bitsPerPixel = 10,
++		.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
++		.packed = false,
++		.pixelsPerGroup = 4,
++		.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
++	} },
+ 	{ formats::SBGGR10_CSI2P, {
+ 		.name = "SBGGR10_CSI2P",
+ 		.format = formats::SBGGR10_CSI2P,
+@@ -639,6 +649,16 @@ const std::map<PixelFormat, PixelFormatInfo> pixelFormatInfo{
+ 		.pixelsPerGroup = 4,
+ 		.planes = {{ { 5, 1 }, { 0, 0 }, { 0, 0 } }},
+ 	} },
++	{ formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, {
++		.name = "SIGIG_GBGR_IGIG_GRGB10_CSI2P",
++		.format = formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P,
++		.v4l2Formats = { V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P), },
++		.bitsPerPixel = 10,
++		.colourEncoding = PixelFormatInfo::ColourEncodingRAW,
++		.packed = true,
++		.pixelsPerGroup = 4,
++		.planes = {{ { 4, 1 }, { 0, 0 }, { 0, 0 } }},
++	} },
+ 	{ formats::SBGGR12, {
+ 		.name = "SBGGR12",
+ 		.format = formats::SBGGR12,
+diff --git a/src/libcamera/formats.yaml b/src/libcamera/formats.yaml
+index 539ac0b3..0786a900 100644
+--- a/src/libcamera/formats.yaml
++++ b/src/libcamera/formats.yaml
+@@ -100,6 +100,8 @@ formats:
+       fourcc: DRM_FORMAT_SGBRG10
+   - SBGGR10:
+       fourcc: DRM_FORMAT_SBGGR10
++  - SIGIG_GBGR_IGIG_GRGB10:
++      fourcc: DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10
+ 
+   - SRGGB12:
+       fourcc: DRM_FORMAT_SRGGB12
+@@ -144,6 +146,9 @@ formats:
+   - SBGGR10_CSI2P:
+       fourcc: DRM_FORMAT_SBGGR10
+       mod: MIPI_FORMAT_MOD_CSI2_PACKED
++  - SIGIG_GBGR_IGIG_GRGB10_CSI2P:
++      fourcc: DRM_FORMAT_SIGIG_GBGR_IGIG_GRGB10
++      mod: MIPI_FORMAT_MOD_CSI2_PACKED
+ 
+   - SRGGB12_CSI2P:
+       fourcc: DRM_FORMAT_SRGGB12
+diff --git a/src/libcamera/v4l2_pixelformat.cpp b/src/libcamera/v4l2_pixelformat.cpp
+index 5551c62e..53078d99 100644
+--- a/src/libcamera/v4l2_pixelformat.cpp
++++ b/src/libcamera/v4l2_pixelformat.cpp
+@@ -153,6 +153,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{
+ 		{ formats::SGRBG10, "10-bit Bayer GRGR/BGBG" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10),
+ 		{ formats::SRGGB10, "10-bit Bayer RGRG/GBGB" } },
++	{ V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10),
++		{ formats::SIGIG_GBGR_IGIG_GRGB10, "10-bit Bayer GRGB/IGIG/GBGR/IGIG" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR10P),
+ 		{ formats::SBGGR10_CSI2P, "10-bit Bayer BGBG/GRGR Packed" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG10P),
+@@ -161,6 +163,8 @@ const std::map<V4L2PixelFormat, V4L2PixelFormat::Info> vpf2pf{
+ 		{ formats::SGRBG10_CSI2P, "10-bit Bayer GRGR/BGBG Packed" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SRGGB10P),
+ 		{ formats::SRGGB10_CSI2P, "10-bit Bayer RGRG/GBGB Packed" } },
++	{ V4L2PixelFormat(V4L2_PIX_FMT_SIGIG_GBGR_IGIG_GRGB10P),
++		{ formats::SIGIG_GBGR_IGIG_GRGB10_CSI2P, "10-bit Bayer GRGB/IGIG/GBGR/IGIG Packed" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SBGGR12),
+ 		{ formats::SBGGR12, "12-bit Bayer BGBG/GRGR" } },
+ 	{ V4L2PixelFormat(V4L2_PIX_FMT_SGBRG12),
+diff --git a/src/libcamera/v4l2_subdevice.cpp b/src/libcamera/v4l2_subdevice.cpp
+index 15e8206a..4ad37aaf 100644
+--- a/src/libcamera/v4l2_subdevice.cpp
++++ b/src/libcamera/v4l2_subdevice.cpp
+@@ -128,6 +128,7 @@ const std::map<uint32_t, V4L2SubdeviceFormatInfo> formatInfoMap = {
+ 	{ MEDIA_BUS_FMT_SGBRG10_1X10, { 10, "SGBRG10_1X10", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SGRBG10_1X10, { 10, "SGRBG10_1X10", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SRGGB10_1X10, { 10, "SRGGB10_1X10", PixelFormatInfo::ColourEncodingRAW } },
++	{ MEDIA_BUS_FMT_SIGIG_GBGR_IGIG_GRGB10_1X10, { 10, "SIGIG_GBGR_IGIG_GRGB10_1X10", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SBGGR12_1X12, { 12, "SBGGR12_1X12", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SGBRG12_1X12, { 12, "SGBRG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
+ 	{ MEDIA_BUS_FMT_SGRBG12_1X12, { 12, "SGRBG12_1X12", PixelFormatInfo::ColourEncodingRAW } },
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch b/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
new file mode 100644
index 0000000000..2343e9c46f
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0017-libcamera-Add-Software-ISP-benchmarking-documentatio.patch
@@ -0,0 +1,132 @@
+From 6c509a3d144d46a11454d32d128d16e16602b50f Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:20 +0100
+Subject: [PATCH 17/21] libcamera: Add "Software ISP benchmarking"
+ documentation
+
+Add a "Software ISP benchmarking" documentation section which describes
+the performance/power consumption measurements used during
+the Software ISP's development.
+
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>
+---
+ Documentation/index.rst                     |  1 +
+ Documentation/meson.build                   |  1 +
+ Documentation/software-isp-benchmarking.rst | 82 +++++++++++++++++++++
+ 3 files changed, 84 insertions(+)
+ create mode 100644 Documentation/software-isp-benchmarking.rst
+
+diff --git a/Documentation/index.rst b/Documentation/index.rst
+index 63fac72d..5442ae75 100644
+--- a/Documentation/index.rst
++++ b/Documentation/index.rst
+@@ -24,3 +24,4 @@
+    Lens driver requirements <lens_driver_requirements>
+    Python Bindings <python-bindings>
+    Camera Sensor Model <camera-sensor-model>
++   SoftwareISP Benchmarking <software-isp-benchmarking>
+diff --git a/Documentation/meson.build b/Documentation/meson.build
+index 7a58fec8..3872e0a8 100644
+--- a/Documentation/meson.build
++++ b/Documentation/meson.build
+@@ -80,6 +80,7 @@ if sphinx.found()
+         'lens_driver_requirements.rst',
+         'python-bindings.rst',
+         'sensor_driver_requirements.rst',
++        'software-isp-benchmarking.rst',
+        '../README.rst',
+     ]
+ 
+diff --git a/Documentation/software-isp-benchmarking.rst b/Documentation/software-isp-benchmarking.rst
+new file mode 100644
+index 00000000..b2803953
+--- /dev/null
++++ b/Documentation/software-isp-benchmarking.rst
+@@ -0,0 +1,82 @@
++.. SPDX-License-Identifier: CC-BY-SA-4.0
++
++.. _software-isp-benchmarking:
++
++Software ISP benchmarking
++=========================
++
++The Software ISP is particularly sensitive to performance regressions
++therefore it is a good idea to always benchmark the Software ISP
++before and after making changes to it and ensure that there are
++no performance regressions.
++
++DebayerCpu class builtin benchmark
++----------------------------------
++
++The DebayerCpu class has a builtin benchmark. This benchmark
++measures the time spent on processing (collecting statistics
++and debayering) only, it does not measure the time spent on
++capturing or outputting the frames.
++
++The builtin benchmark always runs. So this can be used by simply
++running "cam" or "qcam" with a pipeline using the Software ISP.
++
++When it runs it will skip measuring the first 30 frames to
++allow the caches and the CPU temperature (turbo-ing) to warm-up
++and then it measures 30 fps and shows the total and per frame
++processing time using an info level log message:
++
++.. code-block:: text
++
++   INFO Debayer debayer_cpu.cpp:907 Processed 30 frames in 244317us, 8143 us/frame
++
++To get stable measurements it is advised to disable any other processes which
++may cause significant CPU usage (e.g. disable wifi, bluetooth and browsers).
++When possible it is also advisable to disable CPU turbo-ing and
++frequency-scaling.
++
++For example when benchmarking on a Lenovo ThinkPad X1 Yoga Gen 8, with
++the charger plugged in, the CPU can be fixed to run at 2 GHz using:
++
++.. code-block:: shell
++
++   sudo x86_energy_perf_policy --turbo-enable 0
++   sudo cpupower frequency-set -d 2GHz -u 2GHz
++
++with these settings the builtin bench reports a processing time of ~7.8ms/frame
++on this laptop for FHD SGRBG10 (unpacked) bayer data.
++
++Measuring power consumption
++---------------------------
++
++Since the Software ISP is often used on mobile devices it is also
++important to measure power consumption and ensure that that does
++not regress.
++
++For example to measure power consumption on a Lenovo ThinkPad X1 Yoga Gen 8
++it needs to be running on battery and it should be configured with its
++platform-profile (/sys/firmware/acpi/platform_profile) set to balanced and
++with its default turbo and frequency-scaling behavior to match real world usage.
++
++Then start qcam to capture a FHD picture at 30 fps and position the qcam window
++so that it is fully visible. After this run the following command to monitor
++the power consumption:
++
++.. code-block:: shell
++
++   watch -n 10 cat /sys/class/power_supply/BAT0/power_now /sys/class/hwmon/hwmon6/fan?_input
++
++Note this not only measures the power consumption in ยตW it also monitors
++the speed of this laptop's 2 fans. This is important because depending on
++the ambient temperature the 2 fans may spin up while testing and this
++will cause an additional power consumption of approx. 0.5 W messing up
++the measurement.
++
++After starting qcam + the watch command let the laptop sit without using
++it for 2 minutes for the readings to stabilize. Then check that the fans
++have not turned on and manually take a couple of consecutive power readings
++and avarage these.
++
++On the example Lenovo ThinkPad X1 Yoga Gen 8 laptop this results in
++a measured power consumption of approx. 13 W while running qcam versus
++approx. 4-5 W while setting idle with its OLED panel on.
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch b/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch
new file mode 100644
index 0000000000..c746b74dba
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0018-libcamera-software_isp-Apply-black-level-compensatio.patch
@@ -0,0 +1,396 @@
+From bb608d177135d74e3c98b8a61fb459ebe254bca5 Mon Sep 17 00:00:00 2001
+From: Milan Zamazal <mzamazal@redhat.com>
+Date: Mon, 11 Mar 2024 15:15:21 +0100
+Subject: [PATCH 18/21] libcamera: software_isp: Apply black level compensation
+
+Black may not be represented as 0 pixel value for given hardware, it may be
+higher.  If this is not compensated then various problems may occur such as low
+contrast or suboptimal exposure.
+
+The black pixel value can be either retrieved from a tuning file for the given
+hardware, or automatically on fly.  The former is the right and correct method,
+while the latter can be used when a tuning file is not available for the given
+hardware.  Since there is currently no support for tuning files in software ISP,
+the automatic, hardware independent way, is always used.  Support for tuning
+files should be added in future but it will require more work than this patch.
+
+The patch looks at the image histogram and assumes that black starts when pixel
+values start occurring on the left.  A certain amount of the darkest pixels is
+ignored; it doesn't matter whether they represent various kinds of noise or are
+real, they are better to omit in any case to make the image looking better.  It
+also doesn't matter whether the darkest pixels occur around the supposed black
+level or are spread between 0 and the black level, the difference is not
+important.
+
+An arbitrary threshold of 2% darkest pixels is applied; there is no magic about
+that value.
+
+The patch assumes that the black values for different colors are the same and
+doesn't attempt any other non-primitive enhancements.  It cannot completely
+replace tuning files and simplicity, while providing visible benefit, is its
+goal.  Anything more sophisticated is left for future patches.
+
+A possible cheap enhancement, if needed, could be setting exposure + gain to
+minimum values temporarily, before setting the black level.  In theory, the
+black level should be fixed but it may not be reached in all images.  For this
+reason, the patch updates black level only if the observed value is lower than
+the current one; it should be never increased.
+
+The purpose of the patch is to compensate for hardware properties.  General
+image contrast enhancements are out of scope of this patch.
+
+Stats are still gathered as an uncorrected histogram, to avoid any confusion and
+to represent the raw image data.  Exposure must be determined after the black
+level correction -- it has no influence on the sub-black area and must be
+correct after applying the black level correction.  The granularity of the
+histogram is increased from 16 to 64 to provide a better precision (there is no
+theory behind either of those numbers).
+
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Milan Zamazal <mzamazal@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ .../internal/software_isp/debayer_params.h    |  4 +
+ .../internal/software_isp/swisp_stats.h       | 10 ++-
+ src/ipa/simple/black_level.cpp                | 86 +++++++++++++++++++
+ src/ipa/simple/black_level.h                  | 28 ++++++
+ src/ipa/simple/meson.build                    |  7 +-
+ src/ipa/simple/soft_simple.cpp                | 28 ++++--
+ src/libcamera/software_isp/debayer_cpu.cpp    | 13 ++-
+ src/libcamera/software_isp/debayer_cpu.h      |  1 +
+ src/libcamera/software_isp/software_isp.cpp   |  2 +-
+ 9 files changed, 162 insertions(+), 17 deletions(-)
+ create mode 100644 src/ipa/simple/black_level.cpp
+ create mode 100644 src/ipa/simple/black_level.h
+
+diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h
+index 98965fa1..5e38e08b 100644
+--- a/include/libcamera/internal/software_isp/debayer_params.h
++++ b/include/libcamera/internal/software_isp/debayer_params.h
+@@ -43,6 +43,10 @@ struct DebayerParams {
+ 	 * \brief Gamma correction, 1.0 is no correction
+ 	 */
+ 	float gamma;
++	/**
++	 * \brief Level of the black point, 0..255, 0 is no correction.
++	 */
++	unsigned int blackLevel;
+ };
+ 
+ } /* namespace libcamera */
+diff --git a/include/libcamera/internal/software_isp/swisp_stats.h b/include/libcamera/internal/software_isp/swisp_stats.h
+index afe42c9a..25cd5abd 100644
+--- a/include/libcamera/internal/software_isp/swisp_stats.h
++++ b/include/libcamera/internal/software_isp/swisp_stats.h
+@@ -7,6 +7,8 @@
+ 
+ #pragma once
+ 
++#include <array>
++
+ namespace libcamera {
+ 
+ /**
+@@ -28,11 +30,15 @@ struct SwIspStats {
+ 	/**
+ 	 * \brief Number of bins in the yHistogram.
+ 	 */
+-	static constexpr unsigned int kYHistogramSize = 16;
++	static constexpr unsigned int kYHistogramSize = 64;
++	/**
++	 * \brief Type of the histogram.
++	 */
++	using histogram = std::array<unsigned int, kYHistogramSize>;
+ 	/**
+ 	 * \brief A histogram of luminance values.
+ 	 */
+-	std::array<unsigned int, kYHistogramSize> yHistogram;
++	histogram yHistogram;
+ };
+ 
+ } /* namespace libcamera */
+diff --git a/src/ipa/simple/black_level.cpp b/src/ipa/simple/black_level.cpp
+new file mode 100644
+index 00000000..8d52201b
+--- /dev/null
++++ b/src/ipa/simple/black_level.cpp
+@@ -0,0 +1,86 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2024, Red Hat Inc.
++ *
++ * black_level.cpp - black level handling
++ */
++
++#include "black_level.h"
++
++#include <numeric>
++
++#include <libcamera/base/log.h>
++
++namespace libcamera {
++
++LOG_DEFINE_CATEGORY(IPASoftBL)
++
++/**
++ * \class BlackLevel
++ * \brief Object providing black point level for software ISP
++ *
++ * Black level can be provided in hardware tuning files or, if no tuning file is
++ * available for the given hardware, guessed automatically, with less accuracy.
++ * As tuning files are not yet implemented for software ISP, BlackLevel
++ * currently provides only guessed black levels.
++ *
++ * This class serves for tracking black level as a property of the underlying
++ * hardware, not as means of enhancing a particular scene or image.
++ *
++ * The class is supposed to be instantiated for the given camera stream.
++ * The black level can be retrieved using BlackLevel::get() method. It is
++ * initially 0 and may change when updated using BlackLevel::update() method.
++ */
++
++BlackLevel::BlackLevel()
++	: blackLevel_(255), blackLevelSet_(false)
++{
++}
++
++/**
++ * \brief Return the current black level
++ *
++ * \return The black level, in the range from 0 (minimum) to 255 (maximum).
++ * If the black level couldn't be determined yet, return 0.
++ */
++unsigned int BlackLevel::get() const
++{
++	return blackLevelSet_ ? blackLevel_ : 0;
++}
++
++/**
++ * \brief Update black level from the provided histogram
++ * \param[in] yHistogram The histogram to be used for updating black level
++ *
++ * The black level is property of the given hardware, not image. It is updated
++ * only if it has not been yet set or if it is lower than the lowest value seen
++ * so far.
++ */
++void BlackLevel::update(SwIspStats::histogram &yHistogram)
++{
++	// The constant is selected to be "good enough", not overly conservative or
++	// aggressive. There is no magic about the given value.
++	constexpr float ignoredPercentage_ = 0.02;
++	const unsigned int total =
++		std::accumulate(begin(yHistogram), end(yHistogram), 0);
++	const unsigned int pixelThreshold = ignoredPercentage_ * total;
++	const unsigned int currentBlackIdx =
++		blackLevel_ / (256 / SwIspStats::kYHistogramSize);
++
++	for (unsigned int i = 0, seen = 0;
++	     i < currentBlackIdx && i < SwIspStats::kYHistogramSize;
++	     i++) {
++		seen += yHistogram[i];
++		if (seen >= pixelThreshold) {
++			blackLevel_ = i * (256 / SwIspStats::kYHistogramSize);
++			blackLevelSet_ = true;
++			LOG(IPASoftBL, Debug)
++				<< "Auto-set black level: "
++				<< i << "/" << SwIspStats::kYHistogramSize
++				<< " (" << 100 * (seen - yHistogram[i]) / total << "% below, "
++				<< 100 * seen / total << "% at or below)";
++			break;
++		}
++	};
++}
++} // namespace libcamera
+diff --git a/src/ipa/simple/black_level.h b/src/ipa/simple/black_level.h
+new file mode 100644
+index 00000000..b3785db0
+--- /dev/null
++++ b/src/ipa/simple/black_level.h
+@@ -0,0 +1,28 @@
++/* SPDX-License-Identifier: LGPL-2.1-or-later */
++/*
++ * Copyright (C) 2024, Red Hat Inc.
++ *
++ * black_level.h - black level handling
++ */
++
++#pragma once
++
++#include <array>
++
++#include "libcamera/internal/software_isp/swisp_stats.h"
++
++namespace libcamera {
++
++class BlackLevel
++{
++public:
++	BlackLevel();
++	unsigned int get() const;
++	void update(std::array<unsigned int, SwIspStats::kYHistogramSize> &yHistogram);
++
++private:
++	unsigned int blackLevel_;
++	bool blackLevelSet_;
++};
++
++} // namespace libcamera
+diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build
+index 3e863db7..44b5f1d7 100644
+--- a/src/ipa/simple/meson.build
++++ b/src/ipa/simple/meson.build
+@@ -2,8 +2,13 @@
+ 
+ ipa_name = 'ipa_soft_simple'
+ 
++soft_simple_sources = files([
++    'soft_simple.cpp',
++    'black_level.cpp',
++])
++
+ mod = shared_module(ipa_name,
+-                    ['soft_simple.cpp', libcamera_generated_ipa_headers],
++                    [soft_simple_sources, libcamera_generated_ipa_headers],
+                     name_prefix : '',
+                     include_directories : [ipa_includes, libipa_includes],
+                     dependencies : libcamera_private,
+diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
+index 312df4ba..ac027568 100644
+--- a/src/ipa/simple/soft_simple.cpp
++++ b/src/ipa/simple/soft_simple.cpp
+@@ -22,6 +22,8 @@
+ #include "libcamera/internal/software_isp/debayer_params.h"
+ #include "libcamera/internal/software_isp/swisp_stats.h"
+ 
++#include "black_level.h"
++
+ namespace libcamera {
+ 
+ LOG_DEFINE_CATEGORY(IPASoft)
+@@ -33,7 +35,8 @@ class IPASoftSimple : public ipa::soft::IPASoftInterface
+ public:
+ 	IPASoftSimple()
+ 		: params_(static_cast<DebayerParams *>(MAP_FAILED)),
+-		  stats_(static_cast<SwIspStats *>(MAP_FAILED)), ignore_updates_(0)
++		  stats_(static_cast<SwIspStats *>(MAP_FAILED)),
++		  blackLevel_(BlackLevel()), ignore_updates_(0)
+ 	{
+ 	}
+ 
+@@ -63,6 +66,7 @@ private:
+ 	SharedFD fdParams_;
+ 	DebayerParams *params_;
+ 	SwIspStats *stats_;
++	BlackLevel blackLevel_;
+ 
+ 	int32_t exposure_min_, exposure_max_;
+ 	int32_t again_min_, again_max_;
+@@ -196,6 +200,10 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
+ 	params_->gainG = 256;
+ 	params_->gamma = 0.5;
+ 
++	if (ignore_updates_ > 0)
++		blackLevel_.update(stats_->yHistogram);
++	params_->blackLevel = blackLevel_.get();
++
+ 	setIspParams.emit(0);
+ 
+ 	/*
+@@ -211,18 +219,19 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
+ 	 * Calculate Mean Sample Value (MSV) according to formula from:
+ 	 * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
+ 	 */
+-	constexpr unsigned int yHistValsPerBin =
+-		SwIspStats::kYHistogramSize / kExposureBinsCount;
+-	constexpr unsigned int yHistValsPerBinMod =
+-		SwIspStats::kYHistogramSize /
+-		(SwIspStats::kYHistogramSize % kExposureBinsCount + 1);
++	const unsigned int blackLevelHistIdx =
++		params_->blackLevel / (256 / SwIspStats::kYHistogramSize);
++	const unsigned int histogramSize = SwIspStats::kYHistogramSize - blackLevelHistIdx;
++	const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount;
++	const unsigned int yHistValsPerBinMod =
++		histogramSize / (histogramSize % kExposureBinsCount + 1);
+ 	int ExposureBins[kExposureBinsCount] = {};
+ 	unsigned int denom = 0;
+ 	unsigned int num = 0;
+ 
+-	for (unsigned int i = 0; i < SwIspStats::kYHistogramSize; i++) {
++	for (unsigned int i = 0; i < histogramSize; i++) {
+ 		unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin;
+-		ExposureBins[idx] += stats_->yHistogram[i];
++		ExposureBins[idx] += stats_->yHistogram[blackLevelHistIdx + i];
+ 	}
+ 
+ 	for (unsigned int i = 0; i < kExposureBinsCount; i++) {
+@@ -256,7 +265,8 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
+ 
+ 	LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV
+ 			    << " exp " << exposure_ << " again " << again_
+-			    << " gain R/B " << params_->gainR << "/" << params_->gainB;
++			    << " gain R/B " << params_->gainR << "/" << params_->gainB
++			    << " black level " << params_->blackLevel;
+ }
+ 
+ void IPASoftSimple::updateExposure(double exposureMSV)
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index a1692693..3be3cdfe 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -35,7 +35,7 @@ namespace libcamera {
+  * \param[in] stats Pointer to the stats object to use.
+  */
+ DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
+-	: stats_(std::move(stats)), gamma_correction_(1.0)
++	: stats_(std::move(stats)), gamma_correction_(1.0), blackLevel_(0)
+ {
+ #ifdef __x86_64__
+ 	enableInputMemcpy_ = false;
+@@ -683,11 +683,16 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
+ 	}
+ 
+ 	/* Apply DebayerParams */
+-	if (params.gamma != gamma_correction_) {
+-		for (unsigned int i = 0; i < kGammaLookupSize; i++)
+-			gamma_[i] = UINT8_MAX * powf(i / (kGammaLookupSize - 1.0), params.gamma);
++	if (params.gamma != gamma_correction_ || params.blackLevel != blackLevel_) {
++		const unsigned int blackIndex =
++			params.blackLevel * kGammaLookupSize / 256;
++		std::fill(gamma_.begin(), gamma_.begin() + blackIndex, 0);
++		const float divisor = kGammaLookupSize - blackIndex - 1.0;
++		for (unsigned int i = blackIndex; i < kGammaLookupSize; i++)
++			gamma_[i] = UINT8_MAX * powf((i - blackIndex) / divisor, params.gamma);
+ 
+ 		gamma_correction_ = params.gamma;
++		blackLevel_ = params.blackLevel;
+ 	}
+ 
+ 	if (swapRedBlueGains_)
+diff --git a/src/libcamera/software_isp/debayer_cpu.h b/src/libcamera/software_isp/debayer_cpu.h
+index 5f44fc65..ea02f909 100644
+--- a/src/libcamera/software_isp/debayer_cpu.h
++++ b/src/libcamera/software_isp/debayer_cpu.h
+@@ -147,6 +147,7 @@ private:
+ 	bool enableInputMemcpy_;
+ 	bool swapRedBlueGains_;
+ 	float gamma_correction_;
++	unsigned int blackLevel_;
+ 	unsigned int measuredFrames_;
+ 	int64_t frameProcessTime_;
+ 	/* Skip 30 frames for things to stabilize then measure 30 frames */
+diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
+index 388b4496..9b49be41 100644
+--- a/src/libcamera/software_isp/software_isp.cpp
++++ b/src/libcamera/software_isp/software_isp.cpp
+@@ -64,7 +64,7 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
+  */
+ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
+ 	: debayer_(nullptr),
+-	  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f },
++	  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },
+ 	  dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
+ {
+ 	if (!dmaHeap_.isValid()) {
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch b/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch
new file mode 100644
index 0000000000..5b562c603c
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0019-libcamera-Soft-IPA-use-CameraSensorHelper-for-analog.patch
@@ -0,0 +1,239 @@
+From b0c07674abecb05dc0af93a4b749971f057bc3c6 Mon Sep 17 00:00:00 2001
+From: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
+Date: Mon, 11 Mar 2024 15:15:22 +0100
+Subject: [PATCH 19/21] libcamera: Soft IPA: use CameraSensorHelper for
+ analogue gain
+
+Use CameraSensorHelper to convert the analogue gain code read from the
+camera sensor into real analogue gain value. In the future this makes
+it possible to use faster AE/AGC algorithm. For now the same AE/AGC
+algorithm is used, but even then the CameraSensorHelper lets us use the
+full range of analogue gain values.
+
+If there is no CameraSensorHelper for the camera sensor in use, a
+warning log message is printed, and the AE/AGC works exactly as before
+this change.
+
+Signed-off-by: Andrei Konovalov <andrey.konovalov.ynk@gmail.com>
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
+---
+ .../internal/software_isp/software_isp.h      |  3 +-
+ src/ipa/simple/soft_simple.cpp                | 77 ++++++++++++-------
+ src/libcamera/pipeline/simple/simple.cpp      |  2 +-
+ src/libcamera/software_isp/software_isp.cpp   |  8 +-
+ 4 files changed, 57 insertions(+), 33 deletions(-)
+
+diff --git a/include/libcamera/internal/software_isp/software_isp.h b/include/libcamera/internal/software_isp/software_isp.h
+index 8d25e979..2a6db7ba 100644
+--- a/include/libcamera/internal/software_isp/software_isp.h
++++ b/include/libcamera/internal/software_isp/software_isp.h
+@@ -26,6 +26,7 @@
+ #include <libcamera/ipa/soft_ipa_interface.h>
+ #include <libcamera/ipa/soft_ipa_proxy.h>
+ 
++#include "libcamera/internal/camera_sensor.h"
+ #include "libcamera/internal/dma_heaps.h"
+ #include "libcamera/internal/pipeline_handler.h"
+ #include "libcamera/internal/shared_mem_object.h"
+@@ -43,7 +44,7 @@ LOG_DECLARE_CATEGORY(SoftwareIsp)
+ class SoftwareIsp
+ {
+ public:
+-	SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls);
++	SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor);
+ 	~SoftwareIsp();
+ 
+ 	int loadConfiguration([[maybe_unused]] const std::string &filename) { return 0; }
+diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp
+index ac027568..e4d64762 100644
+--- a/src/ipa/simple/soft_simple.cpp
++++ b/src/ipa/simple/soft_simple.cpp
+@@ -22,6 +22,8 @@
+ #include "libcamera/internal/software_isp/debayer_params.h"
+ #include "libcamera/internal/software_isp/swisp_stats.h"
+ 
++#include "libipa/camera_sensor_helper.h"
++
+ #include "black_level.h"
+ 
+ namespace libcamera {
+@@ -67,18 +69,27 @@ private:
+ 	DebayerParams *params_;
+ 	SwIspStats *stats_;
+ 	BlackLevel blackLevel_;
++	std::unique_ptr<CameraSensorHelper> camHelper_;
+ 
+ 	int32_t exposure_min_, exposure_max_;
+-	int32_t again_min_, again_max_;
+-	int32_t again_, exposure_;
++	int32_t exposure_;
++	double again_min_, again_max_, againMinStep_;
++	double again_;
+ 	unsigned int ignore_updates_;
+ };
+ 
+-int IPASoftSimple::init([[maybe_unused]] const IPASettings &settings,
++int IPASoftSimple::init(const IPASettings &settings,
+ 			const SharedFD &fdStats,
+ 			const SharedFD &fdParams,
+ 			const ControlInfoMap &sensorInfoMap)
+ {
++	camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel);
++	if (camHelper_ == nullptr) {
++		LOG(IPASoft, Warning)
++			<< "Failed to create camera sensor helper for "
++			<< settings.sensorModel;
++	}
++
+ 	fdStats_ = fdStats;
+ 	if (!fdStats_.isValid()) {
+ 		LOG(IPASoft, Error) << "Invalid Statistics handle";
+@@ -132,25 +143,35 @@ int IPASoftSimple::configure(const ControlInfoMap &sensorInfoMap)
+ 		exposure_min_ = 1;
+ 	}
+ 
+-	again_min_ = gain_info.min().get<int32_t>();
+-	again_max_ = gain_info.max().get<int32_t>();
+-	/*
+-	 * The camera sensor gain (g) is usually not equal to the value written
+-	 * into the gain register (x). But the way how the AGC algorithm changes
+-	 * the gain value to make the total exposure closer to the optimum assumes
+-	 * that g(x) is not too far from linear function. If the minimal gain is 0,
+-	 * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
+-	 * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
+-	 * one edge, and very small near the other) we limit the range of the gain
+-	 * values used.
+-	 */
+-	if (!again_min_) {
+-		LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
+-		again_min_ = std::min(100, again_min_ / 2 + again_max_ / 2);
++	int32_t again_min = gain_info.min().get<int32_t>();
++	int32_t again_max = gain_info.max().get<int32_t>();
++
++	if (camHelper_) {
++		again_min_ = camHelper_->gain(again_min);
++		again_max_ = camHelper_->gain(again_max);
++		againMinStep_ = (again_max_ - again_min_) / 100.0;
++	} else {
++		/*
++		 * The camera sensor gain (g) is usually not equal to the value written
++		 * into the gain register (x). But the way how the AGC algorithm changes
++		 * the gain value to make the total exposure closer to the optimum assumes
++		 * that g(x) is not too far from linear function. If the minimal gain is 0,
++		 * the g(x) is likely to be far from the linear, like g(x) = a / (b * x + c).
++		 * To avoid unexpected changes to the gain by the AGC algorithm (abrupt near
++		 * one edge, and very small near the other) we limit the range of the gain
++		 * values used.
++		 */
++		again_max_ = again_max;
++		if (!again_min) {
++			LOG(IPASoft, Warning) << "Minimum gain is zero, that can't be linear";
++			again_min_ = std::min(100, again_min / 2 + again_max / 2);
++		}
++		againMinStep_ = 1.0;
+ 	}
+ 
+ 	LOG(IPASoft, Info) << "Exposure " << exposure_min_ << "-" << exposure_max_
+-			   << ", gain " << again_min_ << "-" << again_max_;
++			   << ", gain " << again_min_ << "-" << again_max_
++			   << " (" << againMinStep_ << ")";
+ 
+ 	return 0;
+ }
+@@ -252,12 +273,14 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
+ 	ControlList ctrls(sensorControls);
+ 
+ 	exposure_ = ctrls.get(V4L2_CID_EXPOSURE).get<int32_t>();
+-	again_ = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
++	int32_t again = ctrls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();
++	again_ = camHelper_ ? camHelper_->gain(again) : again;
+ 
+ 	updateExposure(exposureMSV);
+ 
+ 	ctrls.set(V4L2_CID_EXPOSURE, exposure_);
+-	ctrls.set(V4L2_CID_ANALOGUE_GAIN, again_);
++	ctrls.set(V4L2_CID_ANALOGUE_GAIN,
++		  static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(again_) : again_));
+ 
+ 	ignore_updates_ = 2;
+ 
+@@ -276,7 +299,7 @@ void IPASoftSimple::updateExposure(double exposureMSV)
+ 	static constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;
+ 	static constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;
+ 
+-	int next;
++	double next;
+ 
+ 	if (exposureMSV < kExposureOptimal - kExposureSatisfactory) {
+ 		next = exposure_ * kExpNumeratorUp / kExpDenominator;
+@@ -286,18 +309,18 @@ void IPASoftSimple::updateExposure(double exposureMSV)
+ 			exposure_ = next;
+ 		if (exposure_ >= exposure_max_) {
+ 			next = again_ * kExpNumeratorUp / kExpDenominator;
+-			if (next - again_ < 1)
+-				again_ += 1;
++			if (next - again_ < againMinStep_)
++				again_ += againMinStep_;
+ 			else
+ 				again_ = next;
+ 		}
+ 	}
+ 
+ 	if (exposureMSV > kExposureOptimal + kExposureSatisfactory) {
+-		if (exposure_ == exposure_max_ && again_ != again_min_) {
++		if (exposure_ == exposure_max_ && again_ > again_min_) {
+ 			next = again_ * kExpNumeratorDown / kExpDenominator;
+-			if (again_ - next < 1)
+-				again_ -= 1;
++			if (again_ - next < againMinStep_)
++				again_ -= againMinStep_;
+ 			else
+ 				again_ = next;
+ 		} else {
+diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
+index c3ebb7b7..7e932a14 100644
+--- a/src/libcamera/pipeline/simple/simple.cpp
++++ b/src/libcamera/pipeline/simple/simple.cpp
+@@ -525,7 +525,7 @@ int SimpleCameraData::init()
+ 	 * Instantiate Soft ISP if this is enabled for the given driver and no converter is used.
+ 	 */
+ 	if (!converter_ && pipe->swIspEnabled()) {
+-		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_->controls());
++		swIsp_ = std::make_unique<SoftwareIsp>(pipe, sensor_.get());
+ 		if (!swIsp_->isValid()) {
+ 			LOG(SimplePipeline, Warning)
+ 				<< "Failed to create software ISP, disabling software debayering";
+diff --git a/src/libcamera/software_isp/software_isp.cpp b/src/libcamera/software_isp/software_isp.cpp
+index 9b49be41..ea4d96e4 100644
+--- a/src/libcamera/software_isp/software_isp.cpp
++++ b/src/libcamera/software_isp/software_isp.cpp
+@@ -60,9 +60,9 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
+ /**
+  * \brief Constructs SoftwareIsp object
+  * \param[in] pipe The pipeline handler in use
+- * \param[in] sensorControls ControlInfoMap describing the controls supported by the sensor
++ * \param[in] sensor Pointer to the CameraSensor instance owned by the pipeline handler
+  */
+-SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorControls)
++SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)
+ 	: debayer_(nullptr),
+ 	  debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10, DebayerParams::kGain10, 0.5f, 0 },
+ 	  dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
+@@ -97,10 +97,10 @@ SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const ControlInfoMap &sensorCont
+ 		return;
+ 	}
+ 
+-	int ret = ipa_->init(IPASettings{ "No cfg file", "No sensor model" },
++	int ret = ipa_->init(IPASettings{ "No cfg file", sensor->model() },
+ 			     debayer_->getStatsFD(),
+ 			     sharedParams_.fd(),
+-			     sensorControls);
++			     sensor->controls());
+ 	if (ret) {
+ 		LOG(SoftwareIsp, Error) << "IPA init failed";
+ 		debayer_.reset();
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch b/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch
new file mode 100644
index 0000000000..343f04c850
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0020-ov01a1s-HACK.patch
@@ -0,0 +1,95 @@
+From 2bde6e420571c6dc0ff25246620b4c987987f6be Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 19 Dec 2023 15:45:51 +0100
+Subject: [PATCH 20/21] ov01a1s HACK
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/camera_sensor.cpp            | 6 ++++++
+ src/libcamera/software_isp/debayer_cpu.cpp | 8 ++++++++
+ src/libcamera/software_isp/swstats_cpu.cpp | 4 ++++
+ 3 files changed, 18 insertions(+)
+
+diff --git a/src/libcamera/camera_sensor.cpp b/src/libcamera/camera_sensor.cpp
+index f19f72ea..7ad4b9ef 100644
+--- a/src/libcamera/camera_sensor.cpp
++++ b/src/libcamera/camera_sensor.cpp
+@@ -34,6 +34,9 @@
+ 
+ namespace libcamera {
+ 
++// HACK HACK
++bool is_ov01a1s = false;
++
+ LOG_DEFINE_CATEGORY(CameraSensor)
+ 
+ /**
+@@ -426,6 +429,9 @@ int CameraSensor::initProperties()
+ 	model_ = subdev_->model();
+ 	properties_.set(properties::Model, utils::toAscii(model_));
+ 
++	if (model_ == "ov01a1s")
++		is_ov01a1s = true;
++
+ 	/* Generate a unique ID for the sensor. */
+ 	int ret = generateId();
+ 	if (ret)
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index 3be3cdfe..d6599805 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -23,6 +23,7 @@
+ 
+ namespace libcamera {
+ 
++extern bool is_ov01a1s;
+ /**
+  * \class DebayerCpu
+  * \brief Class for debayering on the CPU
+@@ -262,6 +263,9 @@ int DebayerCpu::getInputConfig(PixelFormat inputFormat, DebayerInputConfig &conf
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
++	if (is_ov01a1s)
++		bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
++
+ 	if ((bayerFormat.bitDepth == 8 || bayerFormat.bitDepth == 10 || bayerFormat.bitDepth == 12) &&
+ 	    bayerFormat.packing == BayerFormat::Packing::None &&
+ 	    isStandardBayerOrder(bayerFormat.order)) {
+@@ -330,7 +334,11 @@ int DebayerCpu::setDebayerFunctions(PixelFormat inputFormat, PixelFormat outputF
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputFormat);
+ 
++	if (is_ov01a1s)
++		bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
++
+ 	xShift_ = 0;
++
+ 	swapRedBlueGains_ = false;
+ 
+ 	switch (outputFormat) {
+diff --git a/src/libcamera/software_isp/swstats_cpu.cpp b/src/libcamera/software_isp/swstats_cpu.cpp
+index be310f56..cda1894a 100644
+--- a/src/libcamera/software_isp/swstats_cpu.cpp
++++ b/src/libcamera/software_isp/swstats_cpu.cpp
+@@ -19,6 +19,7 @@
+ 
+ namespace libcamera {
+ 
++extern bool is_ov01a1s;
+ /**
+  * \class SwStatsCpu
+  * \brief Class for gathering statistics on the CPU
+@@ -271,6 +272,9 @@ int SwStatsCpu::configure(const StreamConfiguration &inputCfg)
+ 	BayerFormat bayerFormat =
+ 		BayerFormat::fromPixelFormat(inputCfg.pixelFormat);
+ 
++	if (is_ov01a1s)
++		bayerFormat.order = BayerFormat::IGIG_GBGR_IGIG_GRGB;
++
+ 	if (bayerFormat.packing == BayerFormat::Packing::None &&
+ 	    setupStandardBayerOrder(bayerFormat.order) == 0) {
+ 		switch (bayerFormat.bitDepth) {
+-- 
+2.43.2
+
diff --git a/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch b/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
new file mode 100644
index 0000000000..a3af38c93c
--- /dev/null
+++ b/users/flokli/ipu6-softisp/libcamera/0021-libcamera-debayer_cpu-Make-the-minimum-size-1280x720.patch
@@ -0,0 +1,42 @@
+From a21bb26dcfcc00425f031421b87576f9c81e4824 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 24 Jan 2024 20:44:29 +0100
+Subject: [PATCH 21/21] libcamera: debayer_cpu: Make the minimum size 1280x720
+
+pipewire + firefox default to what looks like 640x480 if we export
+the entire supported cropping range. Hardcode 720p as minsize for now.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ src/libcamera/software_isp/debayer_cpu.cpp | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp
+index d6599805..5a06b191 100644
+--- a/src/libcamera/software_isp/debayer_cpu.cpp
++++ b/src/libcamera/software_isp/debayer_cpu.cpp
+@@ -790,10 +790,17 @@ SizeRange DebayerCpu::sizes(PixelFormat inputFormat, const Size &inputSize)
+ 		return {};
+ 	}
+ 
+-	return SizeRange(Size(pattern_size.width, pattern_size.height),
+-			 Size((inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1),
+-			      (inputSize.height - 2 * border_height) & ~(pattern_size.height - 1)),
+-			 pattern_size.width, pattern_size.height);
++	/*
++	 * pipewire + firefox default to what looks like 640x480
++	 * if we export the entire supported cropping range.
++	 * Hardcode 720p as minsize for now. Minsize should be
++	 * Size(pattern_size.width, pattern_size.height)
++	 */
++	unsigned int w = (inputSize.width - 2 * pattern_size.width) & ~(pattern_size.width - 1);
++	unsigned int h = (inputSize.height - 2 * pattern_size.height) & ~(pattern_size.height - 1);
++	return SizeRange(Size(std::min(w, 1280u), std::min(h, 720u)),
++	                 Size(w, h),
++	                 pattern_size.width, pattern_size.height);
+ }
+ 
+ } /* namespace libcamera */
+-- 
+2.43.2
+
diff --git a/users/flokli/keyboards/dilemma/default.nix b/users/flokli/keyboards/dilemma/default.nix
new file mode 100644
index 0000000000..265f8e56db
--- /dev/null
+++ b/users/flokli/keyboards/dilemma/default.nix
@@ -0,0 +1,45 @@
+{ depot, pkgs, ... }:
+
+rec {
+  firmware = pkgs.stdenv.mkDerivation {
+    name = "keychron-bastardkb-dilemma-firmware";
+
+    src = pkgs.fetchFromGitHub {
+      owner = "qmk";
+      repo = "qmk_firmware";
+      rev = "728aa576b0cd65c6fb7cf77132fdcd06fcedb643"; # develop branch
+      hash = "sha256-YmdX8nEsB1R8d265HAmvwejPjEHJdoTnm4QNigzrcyw=";
+      fetchSubmodules = true;
+    };
+
+    patches = [ ./enable-taps.patch ];
+
+    postPatch = ''
+      patchShebangs util/uf2conv.py
+    '';
+
+    nativeBuildInputs = [
+      pkgs.python3
+      pkgs.qmk
+    ];
+
+    buildPhase = ''
+      mkdir -p keyboards/bastardkb/dilemma/3x5_3/keymaps/flokli
+      cp ${./keymap.c} keyboards/bastardkb/dilemma/3x5_3/keymaps/flokli/keymap.c
+      cp ${./rules.mk} keyboards/bastardkb/dilemma/3x5_3/keymaps/flokli/rules.mk
+
+      make bastardkb/dilemma/3x5_3:flokli
+    '';
+
+    installPhase = ''
+      mkdir -p $out
+      cp bastardkb_dilemma_3x5_3_flokli.uf2 $out/
+    '';
+  };
+
+  flash = pkgs.writeShellScript "flash.sh" ''
+    ${pkgs.qmk}/bin/qmk flash ${firmware}/bastardkb_dilemma_3x5_3_flokli.uf2
+  '';
+
+  meta.ci.targets = [ "firmware" ];
+}
diff --git a/users/flokli/keyboards/dilemma/enable-taps.patch b/users/flokli/keyboards/dilemma/enable-taps.patch
new file mode 100644
index 0000000000..afded85492
--- /dev/null
+++ b/users/flokli/keyboards/dilemma/enable-taps.patch
@@ -0,0 +1,24 @@
+From 32a1b9a189c13bec03c6b0f258121c47185db0ad Mon Sep 17 00:00:00 2001
+From: Florian Klink <flokli@flokli.de>
+Date: Tue, 23 Jan 2024 11:26:10 +0200
+Subject: [PATCH] bastardkb dilemma: enable taps
+
+---
+ keyboards/bastardkb/dilemma/3x5_3/config.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/keyboards/bastardkb/dilemma/3x5_3/config.h b/keyboards/bastardkb/dilemma/3x5_3/config.h
+index ccbc4e2f58..bf17dc7e02 100644
+--- a/keyboards/bastardkb/dilemma/3x5_3/config.h
++++ b/keyboards/bastardkb/dilemma/3x5_3/config.h
+@@ -42,6 +42,7 @@
+ #define POINTING_DEVICE_CS_PIN GP21
+ #undef CIRQUE_PINNACLE_DIAMETER_MM
+ #define CIRQUE_PINNACLE_DIAMETER_MM 40
++#define CIRQUE_PINNACLE_TAP_ENABLE 1
+ 
+ /* Reset. */
+ #define RP2040_BOOTLOADER_DOUBLE_TAP_RESET
+-- 
+2.43.0
+
diff --git a/users/flokli/keyboards/dilemma/keymap.c b/users/flokli/keyboards/dilemma/keymap.c
new file mode 100644
index 0000000000..2c21ef6c9e
--- /dev/null
+++ b/users/flokli/keyboards/dilemma/keymap.c
@@ -0,0 +1,220 @@
+/**
+ * Copyright 2022 Charly Delay <charly@codesink.dev> (@0xcharly)
+ * Copyright 2023 casuanoob <casuanoob@hotmail.com> (@casuanoob)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include QMK_KEYBOARD_H
+
+enum dilemma_keymap_layers {
+    LAYER_BASE = 0,
+    LAYER_NAVIGATION,
+    LAYER_MOUSE,
+    LAYER_MEDIA,
+    LAYER_NUMERAL,
+    LAYER_SYMBOLS,
+    LAYER_FUNCTION,
+};
+
+// Automatically enable sniping-mode on the pointer layer.
+#define DILEMMA_AUTO_SNIPING_ON_LAYER LAYER_MOUSE
+#define ESC_MED LT(LAYER_MEDIA, KC_ESC)
+#define SPC_NAV LT(LAYER_NAVIGATION, KC_SPC)
+#define TAB_MOU LT(LAYER_MOUSE, KC_TAB)
+#define ENT_SYM LT(LAYER_SYMBOLS, KC_ENT)
+#define BSP_NUM LT(LAYER_NUMERAL, KC_BSPC)
+#define DEL_FUN LT(LAYER_FUNCTION, KC_DEL)
+
+#ifndef POINTING_DEVICE_ENABLE
+#    define DRGSCRL KC_NO
+#    define DPI_MOD KC_NO
+#    define S_D_MOD KC_NO
+#    define SNIPING KC_NO
+#endif // !POINTING_DEVICE_ENABLE
+
+// clang-format off
+/** \brief COLEMAK-DH layout (3 rows, 10 columns). */
+#define LAYOUT_LAYER_BASE                                                                     \
+       KC_Q,    KC_W,    KC_F,    KC_P,    KC_B,    KC_J,    KC_L,    KC_U,    KC_Y, KC_QUOT, \
+       KC_A,    KC_R,    KC_S,    KC_T,    KC_G,    KC_M,    KC_N,    KC_E,    KC_I,    KC_O, \
+       KC_Z,    KC_X,    KC_C,    KC_D,    KC_V,    KC_K,    KC_H, KC_COMMA, KC_DOT, KC_SLSH, \
+                      ESC_MED, SPC_NAV, TAB_MOU, ENT_SYM, BSP_NUM, DEL_FUN
+
+/** Convenience row shorthands. */
+#define _______________DEAD_HALF_ROW_______________ XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX
+#define ______________HOME_ROW_GACS_L______________ KC_LGUI, KC_LALT, KC_LCTL, KC_LSFT, XXXXXXX
+#define ______________HOME_ROW_GACS_R______________ XXXXXXX, KC_LSFT, KC_LCTL, KC_RALT, KC_LGUI
+
+/*
+ * Layers used on the Dilemma.
+ *
+ * These layers started off heavily inspired by the Miryoku layout, but trimmed
+ * down and tailored for a stock experience that is meant to be fundation for
+ * further personalization.
+ *
+ * See https://github.com/manna-harbour/miryoku for the original layout.
+ */
+
+/**
+ * \brief Navigation layer.
+ *
+ * Primary left-hand layer (left home thumb) is navigation and editing. Cursor
+ * keys are on the home position, line and page movement below, clipboard
+ * above, caps lock and insert on the inner column. Thumb keys are duplicated
+ * from the base layer to avoid having to layer change mid edit and to enable
+ * auto-repeat.
+*/
+#define LAYOUT_LAYER_NAVIGATION                                                               \
+    _______________DEAD_HALF_ROW_______________, KC_AGAIN,LCTL(KC_V), LCTL(KC_C),  KC_CUT, KC_UNDO, \
+    ______________HOME_ROW_GACS_L______________, KC_CAPS, KC_LEFT,    KC_DOWN,   KC_UP, KC_RGHT, \
+    _______________DEAD_HALF_ROW_______________,  KC_INS, KC_HOME,    KC_PGDN, KC_PGUP,  KC_END, \
+                      XXXXXXX, _______, XXXXXXX,  KC_ENT, KC_BSPC,    KC_DEL
+
+/**
+ * \brief Mouse layer
+ *
+ * Secondary left-hand layer is mouse emulation. Mouse movement mirrors cursor
+ * navigation on home and wheel mirrors line / page movement below. Mouse
+ * buttons are on the thumbs. Left, right, and middle mouse buttons are on the
+ * primary, secondary, and tertiary thumb keys, respectively. Mouse movement,
+ * click, and drag, with modifiers, can be performed from the home position.
+ * Clipboard keys are duplicated from the Nav layer.
+*/
+#define LAYOUT_LAYER_MOUSE                                                                    \
+    _______________DEAD_HALF_ROW_______________, KC_AGAIN,KC_PSTE, KC_COPY,  KC_CUT, KC_UNDO, \
+    ______________HOME_ROW_GACS_L______________, _______, KC_MS_L, KC_MS_D, KC_MS_U, KC_MS_R, \
+    _______________DEAD_HALF_ROW_______________, _______, KC_WH_L, KC_WH_D, KC_WH_U, KC_WH_R, \
+                      XXXXXXX, XXXXXXX, _______, KC_BTN2, KC_BTN1, KC_BTN3
+
+/**
+ * \brief Media layer
+ *
+ * Tertiary left-hand layer is media control, with volume up / volume down and
+ * next / prev mirroring the navigation keys. Pause, stop and mute are on the
+ * primary, secondary, and tertiary thumbs, respectively.
+ *
+ * Keyboard hardware controls are also present, and depend on hardware and
+ * firmware support.
+ *
+ * RGB control is on the top row. RGB Toggle is on the inner index column key.
+ * Combine with Shift for RGB Off. RGB Mode, RGB Hue, RGB Saturation, and RGB
+ * Value are on index, middle, ring, and pinkie column keys, respectively.
+ * Tapping will increase the corresponding value. Combine with Shift to
+ * decrease.
+*/
+#define LAYOUT_LAYER_MEDIA                                                                    \
+    _______________DEAD_HALF_ROW_______________, RGB_TOG, RGB_MOD, RGB_HUI, RGB_SAI, RGB_VAI, \
+    ______________HOME_ROW_GACS_L______________, _______, KC_MPRV, KC_VOLD, KC_VOLU, KC_MNXT, \
+    _______________DEAD_HALF_ROW_______________, _______, _______, _______, _______, _______, \
+                      _______, XXXXXXX, XXXXXXX, KC_MSTP, KC_MPLY, KC_MUTE
+
+/**
+ * \brief Numeral layout.
+ *
+ * Primary right-hand layer (right home thumb) is numerals and symbols. Numerals
+ * are in the standard numpad locations with symbols in the remaining positions.
+ */
+#define LAYOUT_LAYER_NUMERAL                                                                  \
+    KC_LBRC,    KC_7,    KC_8,    KC_9, KC_RBRC, _______________DEAD_HALF_ROW_______________, \
+    KC_SCLN,    KC_4,    KC_5,    KC_6,  KC_EQL, ______________HOME_ROW_GACS_R______________, \
+    KC_GRAVE,   KC_1,    KC_2,    KC_3, KC_BSLS, _______________DEAD_HALF_ROW_______________, \
+                       KC_DOT, KC_0, KC_MINS, XXXXXXX, _______, XXXXXXX
+
+/**
+ * \brief Symbols layer.
+ *
+ * Secondary right-hand layer has shifted symbols in the same locations to reduce
+ * chording when using mods with shifted symbols. `KC_LPRN` is duplicated next to
+ * `KC_RPRN`.
+ */
+#define LAYOUT_LAYER_SYMBOLS                                                                  \
+    KC_LCBR, KC_AMPR, KC_ASTR, KC_LPRN, KC_RCBR, _______________DEAD_HALF_ROW_______________, \
+    KC_COLN,  KC_DLR, KC_PERC, KC_CIRC, KC_PLUS, ______________HOME_ROW_GACS_R______________, \
+    KC_TILD, KC_EXLM,   KC_AT, KC_HASH, KC_PIPE, _______________DEAD_HALF_ROW_______________, \
+                      KC_LPRN, KC_RPRN, KC_UNDS, _______, XXXXXXX, XXXXXXX
+
+/**
+ * \brief Function layer.
+ *
+ * Tertiary right-hand layer has function keys mirroring the numerals on the
+ * primary layer with system keys on the inner column. App is on the tertiary
+ * thumb key and other thumb keys are duplicated from the base layer to enable
+ * auto-repeat.
+ */
+#define LAYOUT_LAYER_FUNCTION                                                                 \
+     KC_F12,   KC_F7,   KC_F8,   KC_F9, KC_PSCR, _______________DEAD_HALF_ROW_______________, \
+     KC_F11,   KC_F4,   KC_F5,   KC_F6, KC_SCRL, ______________HOME_ROW_GACS_R______________, \
+     KC_F10,   KC_F1,   KC_F2,   KC_F3, KC_PAUS, _______________DEAD_HALF_ROW_______________, \
+                      KC_APP, KC_SPC, KC_TAB, XXXXXXX, XXXXXXX, _______
+
+/**
+ * \brief Add Home Row mod to a layout.
+ *
+ * Expects a 10-key per row layout.  Adds support for GACS (Gui, Alt, Ctl, Shift)
+ * home row.  The layout passed in parameter must contain at least 20 keycodes.
+ *
+ * This is meant to be used with `LAYER_BASE` defined above, eg.:
+ *
+ *     HOME_ROW_MOD_GACS(LAYER_BASE)
+ */
+#define _HOME_ROW_MOD_GACS(                                            \
+    L00, L01, L02, L03, L04, R05, R06, R07, R08, R09,                  \
+    L10, L11, L12, L13, L14, R15, R16, R17, R18, R19,                  \
+    ...)                                                               \
+             L00,         L01,         L02,         L03,         L04,  \
+             R05,         R06,         R07,         R08,         R09,  \
+      LGUI_T(L10), LALT_T(L11), LCTL_T(L12), LSFT_T(L13),        L14,  \
+             R15,  RSFT_T(R16), RCTL_T(R17), RALT_T(R18), RGUI_T(R19), \
+      __VA_ARGS__
+#define HOME_ROW_MOD_GACS(...) _HOME_ROW_MOD_GACS(__VA_ARGS__)
+
+
+#define LAYOUT_wrapper(...) LAYOUT_split_3x5_3(__VA_ARGS__)
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+  [LAYER_BASE] = LAYOUT_wrapper(
+    HOME_ROW_MOD_GACS(LAYOUT_LAYER_BASE)
+  ),
+  [LAYER_NAVIGATION] = LAYOUT_wrapper(LAYOUT_LAYER_NAVIGATION),
+  [LAYER_MOUSE] = LAYOUT_wrapper(LAYOUT_LAYER_MOUSE),
+  [LAYER_MEDIA] = LAYOUT_wrapper(LAYOUT_LAYER_MEDIA),
+  [LAYER_NUMERAL] = LAYOUT_wrapper(LAYOUT_LAYER_NUMERAL),
+  [LAYER_SYMBOLS] = LAYOUT_wrapper(LAYOUT_LAYER_SYMBOLS),
+  [LAYER_FUNCTION] = LAYOUT_wrapper(LAYOUT_LAYER_FUNCTION),
+};
+// clang-format on
+
+#ifdef POINTING_DEVICE_ENABLE
+#    ifdef DILEMMA_AUTO_SNIPING_ON_LAYER
+layer_state_t layer_state_set_user(layer_state_t state) {
+    dilemma_set_pointer_sniping_enabled(layer_state_cmp(state, DILEMMA_AUTO_SNIPING_ON_LAYER));
+    return state;
+}
+#    endif // DILEMMA_AUTO_SNIPING_ON_LAYER
+#endif     // POINTING_DEVICE_ENABLE
+
+#ifdef ENCODER_MAP_ENABLE
+// clang-format off
+const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
+    [LAYER_BASE]       = {ENCODER_CCW_CW(KC_WH_D, KC_WH_U),  ENCODER_CCW_CW(KC_VOLD, KC_VOLU)},
+    [LAYER_NAVIGATION] = {ENCODER_CCW_CW(KC_PGDN, KC_PGUP),  ENCODER_CCW_CW(KC_VOLU, KC_VOLD)},
+    [LAYER_MOUSE]      = {ENCODER_CCW_CW(RGB_HUD, RGB_HUI),  ENCODER_CCW_CW(RGB_SAD, RGB_SAI)},
+    [LAYER_MEDIA]      = {ENCODER_CCW_CW(KC_PGDN, KC_PGUP),  ENCODER_CCW_CW(KC_VOLU, KC_VOLD)},
+    [LAYER_NUMERAL]    = {ENCODER_CCW_CW(RGB_VAD, RGB_VAI),  ENCODER_CCW_CW(RGB_SPD, RGB_SPI)},
+    [LAYER_SYMBOLS]    = {ENCODER_CCW_CW(RGB_RMOD, RGB_MOD), ENCODER_CCW_CW(KC_LEFT, KC_RGHT)},
+    [LAYER_FUNCTION]   = {ENCODER_CCW_CW(KC_DOWN, KC_UP),    ENCODER_CCW_CW(KC_LEFT, KC_RGHT)},
+};
+// clang-format on
+#endif // ENCODER_MAP_ENABLE
diff --git a/users/flokli/keyboards/dilemma/rules.mk b/users/flokli/keyboards/dilemma/rules.mk
new file mode 100644
index 0000000000..5a090013dc
--- /dev/null
+++ b/users/flokli/keyboards/dilemma/rules.mk
@@ -0,0 +1,2 @@
+ENCODER_MAP_ENABLE = yes
+OPT_DEFS += -DMK_KINETIC_SPEED=1
diff --git a/users/flokli/keyboards/k6_pro/default.nix b/users/flokli/keyboards/k6_pro/default.nix
new file mode 100644
index 0000000000..708bec7313
--- /dev/null
+++ b/users/flokli/keyboards/k6_pro/default.nix
@@ -0,0 +1,39 @@
+{ depot, pkgs, ... }:
+
+rec {
+  firmware = pkgs.stdenv.mkDerivation {
+    name = "keychron-k6_pro-firmware";
+
+    src = pkgs.fetchFromGitHub {
+      owner = "Keychron"; # the Keychron fork of qmk/qmk_firmware
+      repo = "qmk_firmware";
+      rev = "e0a48783e7cde92d1edfc53a8fff511c45e869d4"; # bluetooth_playground branch
+      hash = "sha256-Pk9kXktmej9JyvSt7UMEW2FDrBg7k1lOssh6HjrP5ro=";
+      fetchSubmodules = true;
+    };
+
+    nativeBuildInputs = [
+      pkgs.qmk
+    ];
+
+    buildPhase = ''
+      mkdir -p keyboards/keychron/k6_pro/ansi/rgb/keymaps/flokli
+      cp ${./keymap.c} keyboards/keychron/k6_pro/ansi/rgb/keymaps/flokli/keymap.c
+      cp ${./rules.mk} keyboards/keychron/k6_pro/ansi/rgb/keymaps/flokli/rules.mk
+
+      make keychron/k6_pro/ansi/rgb:flokli
+    '';
+
+    installPhase = ''
+      mkdir -p $out
+
+      cp keychron_k6_pro_ansi_rgb_flokli.bin $out/
+    '';
+  };
+
+  flash = pkgs.writeShellScript "flash.sh" ''
+    ${pkgs.qmk}/bin/qmk flash ${firmware}/keychron_k6_pro_ansi_rgb_flokli.bin
+  '';
+
+  meta.ci.targets = [ "firmware" ];
+}
diff --git a/users/flokli/keyboards/k6_pro/keymap.c b/users/flokli/keyboards/k6_pro/keymap.c
new file mode 100644
index 0000000000..1aa406eeac
--- /dev/null
+++ b/users/flokli/keyboards/k6_pro/keymap.c
@@ -0,0 +1,76 @@
+/* Copyright 2021 @ Keychron (https://www.keychron.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include QMK_KEYBOARD_H
+
+// clang-format off
+enum layers{
+  MAC_BASE,
+  WIN_BASE,
+  MAC_FN1,
+  WIN_FN1,
+  FN2
+};
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+[MAC_BASE] = LAYOUT_ansi_68(
+     KC_ESC,   KC_1,     KC_2,     KC_3,     KC_4,     KC_5,     KC_6,     KC_7,     KC_8,     KC_9,     KC_0,     KC_MINS,  KC_EQL,   KC_BSPC, KC_DEL,
+     KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_LBRC,  KC_RBRC,  KC_BSLS, KC_HOME,
+     KC_CAPS,  KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_QUOT,            KC_ENT,  KC_PGUP,
+     KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,   KC_SLSH,  KC_RSFT,  KC_UP,    KC_PGDN,
+     KC_LCTL,  KC_LOPTN, KC_LCMMD,                               KC_SPC,                       KC_RCMMD,MO(MAC_FN1),MO(FN2), KC_LEFT,  KC_DOWN, KC_RGHT),
+
+[WIN_BASE] = LAYOUT_ansi_68(
+     KC_ESC,   KC_1,     KC_2,     KC_3,     KC_4,     KC_5,     KC_6,     KC_7,     KC_8,     KC_9,     KC_0,     KC_MINS,  KC_EQL,   KC_BSPC, KC_DEL,
+     KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_LBRC,  KC_RBRC,  KC_BSLS, KC_HOME,
+     KC_CAPS,  KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_QUOT,            KC_ENT,  KC_PGUP,
+     KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,   KC_SLSH,  KC_RSFT,            KC_UP,   KC_PGDN,
+     KC_LCTL,  KC_LGUI,  KC_LALT,                                KC_SPC,                       KC_RALT, MO(WIN_FN1),MO(FN2), KC_LEFT,  KC_DOWN, KC_RGHT),
+
+[MAC_FN1] = LAYOUT_ansi_68(
+     KC_GRV,   KC_BRID,  KC_BRIU,  KC_MCTL,  KC_LPAD,  RGB_VAD,  RGB_VAI,  KC_MPRV,  KC_MPLY,  KC_MNXT,  KC_MUTE,  KC_VOLD,  KC_VOLU,  _______,  RGB_TOG,
+     _______,  BT_HST1,  BT_HST2,  BT_HST3,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     RGB_TOG,  RGB_MOD,  RGB_VAI,  RGB_HUI,  RGB_SAI,  RGB_SPI,  _______,  _______,  _______,  _______,  _______,  _______,            _______,  _______,
+     _______,  RGB_RMOD, RGB_VAD,  RGB_HUD,  RGB_SAD,  RGB_SPD,  NK_TOGG,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     _______,  _______,  _______,                                _______,                      _______,  _______,  _______,  _______,  _______,  _______),
+
+[WIN_FN1] = LAYOUT_ansi_68(
+//                                           mic mute                      webcam    wifi
+     KC_GRV,   KC_MUTE,  KC_VOLD,  KC_VOLU,  _______,  KC_BRID,  KC_BRIU,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  RGB_TOG,
+     _______,  BT_HST1,  BT_HST2,  BT_HST3,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     RGB_TOG,  RGB_MOD,  RGB_VAI,  RGB_HUI,  RGB_SAI,  RGB_SPI,  _______,  _______,  _______,  _______,  _______,  _______,            _______,  _______,
+     _______,  RGB_RMOD, RGB_VAD,  RGB_HUD,  RGB_SAD,  RGB_SPD,  NK_TOGG,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     _______,  _______,  _______,                                _______,                      KC_PSCR,  _______,  _______,  _______,  _______,  _______),
+
+[FN2] = LAYOUT_ansi_68(
+     KC_TILD,  KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_F9,    KC_F10,   KC_F11,   KC_F12,   _______,  KC_SLEP,
+     _______,  KC_BTN1,  KC_MS_U,  KC_BTN2,  KC_WH_U,  KC_VOLU,  KC_MUTE,  KC_MPLY,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     _______,  KC_MS_L,  KC_MS_D,  KC_MS_R,  KC_WH_D,  KC_VOLD,  KC_MPRV,  KC_MNXT,  _______,  _______,  _______,  _______,            _______,  _______,
+     _______,  _______,  _______,  _______,  _______,  BAT_LVL,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
+     _______,  _______,  _______,                                _______,                      _______,  _______,  _______,  _______,  _______,  _______),
+};
+
+// Shift+Del -> middle mouse button
+const key_override_t insert_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_DEL, KC_BTN3);
+// Shift+Home -> End
+const key_override_t end_key_override = ko_make_basic(MOD_MASK_SHIFT, KC_HOME, KC_END);
+
+// This globally defines all key overrides to be used
+const key_override_t **key_overrides = (const key_override_t *[]) {
+     &insert_key_override,
+     &end_key_override,
+     NULL // end of array
+};
diff --git a/users/flokli/keyboards/k6_pro/rules.mk b/users/flokli/keyboards/k6_pro/rules.mk
new file mode 100644
index 0000000000..35725756d4
--- /dev/null
+++ b/users/flokli/keyboards/k6_pro/rules.mk
@@ -0,0 +1,2 @@
+KEY_OVERRIDE_ENABLE = yes
+OPT_DEFS += -DDYNAMIC_KEYMAP_LAYER_COUNT=5 -DMK_KINETIC_SPEED=1
diff --git a/users/flokli/nixos/.envrc b/users/flokli/nixos/.envrc
new file mode 100644
index 0000000000..ccf3cb847a
--- /dev/null
+++ b/users/flokli/nixos/.envrc
@@ -0,0 +1 @@
+PATH_add $(nix-build ../../.. -A users.flokli.nixos.deps --no-out-link)/bin
diff --git a/users/riking/dotfiles/regolith/flags/term-profile b/users/flokli/nixos/.skip-subtree
index e69de29bb2..e69de29bb2 100644
--- a/users/riking/dotfiles/regolith/flags/term-profile
+++ b/users/flokli/nixos/.skip-subtree
diff --git a/users/flokli/nixos/archeology-ec2/OWNERS b/users/flokli/nixos/archeology-ec2/OWNERS
new file mode 100644
index 0000000000..b9bc074a80
--- /dev/null
+++ b/users/flokli/nixos/archeology-ec2/OWNERS
@@ -0,0 +1 @@
+edef
diff --git a/users/flokli/nixos/archeology-ec2/configuration.nix b/users/flokli/nixos/archeology-ec2/configuration.nix
new file mode 100644
index 0000000000..f0fc0c5d09
--- /dev/null
+++ b/users/flokli/nixos/archeology-ec2/configuration.nix
@@ -0,0 +1,35 @@
+{ depot, pkgs, modulesPath, ... }:
+
+{
+  imports = [
+    "${modulesPath}/virtualisation/amazon-image.nix"
+    ../profiles/archeology.nix
+  ];
+
+  systemd.timers.parse-bucket-logs = {
+    wantedBy = [ "multi-user.target" ];
+    timerConfig.OnCalendar = "*-*-* 03:00:00 UTC";
+  };
+
+  systemd.services.parse-bucket-logs = {
+    path = [ depot.users.flokli.archeology.parse-bucket-logs ];
+    serviceConfig = {
+      Type = "oneshot";
+      ExecStart = (pkgs.writers.writePython3 "parse-bucket-logs-continuously"
+        {
+          libraries = [ pkgs.python3Packages.boto3 ];
+        } ./parse-bucket-logs-continuously.py);
+      DynamicUser = "yes";
+      StateDirectory = "parse-bucket-logs";
+    };
+  };
+
+  environment.systemPackages = [
+    depot.users.flokli.archeology.parse-bucket-logs
+  ];
+
+  networking.hostName = "archeology-ec2";
+
+  system.stateVersion = "23.05"; # Did you read the comment?
+}
+
diff --git a/users/flokli/nixos/archeology-ec2/hardware-configuration.nix b/users/flokli/nixos/archeology-ec2/hardware-configuration.nix
new file mode 100644
index 0000000000..7b3d79d70a
--- /dev/null
+++ b/users/flokli/nixos/archeology-ec2/hardware-configuration.nix
@@ -0,0 +1,36 @@
+{ lib, modulesPath, ... }:
+
+{
+  imports =
+    [
+      (modulesPath + "/profiles/qemu-guest.nix")
+    ];
+
+  boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "sr_mod" "virtio_blk" ];
+  boot.initrd.kernelModules = [ ];
+  boot.kernelModules = [ "kvm-amd" ];
+  boot.extraModulePackages = [ ];
+
+  fileSystems."/" =
+    {
+      device = "/dev/disk/by-partlabel/root";
+      fsType = "xfs";
+    };
+
+  fileSystems."/boot" =
+    {
+      device = "/dev/disk/by-partlabel/boot";
+      fsType = "vfat";
+    };
+
+  swapDevices = [ ];
+
+  # Enables DHCP on each ethernet and wireless interface. In case of scripted networking
+  # (the default) this is the recommended approach. When using systemd-networkd it's
+  # still possible to use this option, but it's recommended to use it in conjunction
+  # with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
+  networking.useDHCP = lib.mkDefault true;
+  # networking.interfaces.enp1s0.useDHCP = lib.mkDefault true;
+
+  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+}
diff --git a/users/flokli/nixos/archeology-ec2/parse-bucket-logs-continuously.py b/users/flokli/nixos/archeology-ec2/parse-bucket-logs-continuously.py
new file mode 100644
index 0000000000..f6ec8fb77c
--- /dev/null
+++ b/users/flokli/nixos/archeology-ec2/parse-bucket-logs-continuously.py
@@ -0,0 +1,62 @@
+import boto3
+import datetime
+import os
+import re
+import subprocess
+import tempfile
+
+s3 = boto3.resource('s3')
+bucket_name = "nix-archeologist"
+prefix = "nix-cache-bucket-logs/"
+
+bucket = s3.Bucket(bucket_name)
+
+key_pattern = re.compile(r'.*\/(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})\.parquet$')  # noqa: E501
+
+# get a listing (which is sorted), grab the most recent key
+last_elem = list(
+    o for o in bucket.objects.filter(Prefix=prefix)
+    if key_pattern.match(o.key)
+).pop()
+
+# extract the date of that key.
+m = key_pattern.search(last_elem.key)
+last_elem_date = datetime.date(int(m.group("y")), int(m.group("m")), int(m.group("d")))  # noqa: E501
+
+# get the current date (UTC)
+now = datetime.datetime.now(tz=datetime.UTC)
+now_date = datetime.date(now.year, now.month, now.day)
+
+while True:
+    # Calculate what date would be processed next.
+    next_elem_date = last_elem_date + datetime.timedelta(days=1)
+
+    # If that's today, we don't want to process it.
+    if next_elem_date == now_date:
+        print("Caught up, would process data from today.")
+        break
+
+    # If we'd be processing data from yesterday, but it's right after midnight,
+    # also don't process - data might still be flushed.
+    if (next_elem_date + datetime.timedelta(days=1) == now_date) and now.hour == 0:  # noqa: E501
+        print("Not processing data from previous day right after midnight")
+        break
+
+    src = f"http://nix-cache-log.s3.amazonaws.com/log/{next_elem_date.isoformat()}-*"  # noqa: E501
+
+    # Invoke parse-bucket-logs script inside a tempdir and upload on success.
+    with tempfile.TemporaryDirectory() as td:
+        work_file_name = os.path.join(td, "output.parquet")
+        args = ["archeology-parse-bucket-logs", src, work_file_name]
+        subprocess.run(
+            args,
+            check=True  # throw exception if nonzero exit code
+        )
+
+        dest_key = f"{prefix}{next_elem_date.isoformat()}.parquet"
+
+        # Upload the file
+        print(f"uploading to s3://{bucket_name}{dest_key}")
+        bucket.upload_file(work_file_name, dest_key)
+
+    last_elem_date = next_elem_date
diff --git a/users/flokli/nixos/default.nix b/users/flokli/nixos/default.nix
new file mode 100644
index 0000000000..9ed223a908
--- /dev/null
+++ b/users/flokli/nixos/default.nix
@@ -0,0 +1,32 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
+
+  # assumes `name` is configured appropriately in your .ssh/config
+  deployScript = name: sys: pkgs.writeShellScriptBin "deploy-${name}" ''
+    set -eo pipefail
+    nix-copy-closure --to ${name} --gzip --use-substitutes ${sys}
+    ssh ${name} nix-env --profile /nix/var/nix/profiles/system --set ${sys}
+    ssh ${name} ${sys}/bin/switch-to-configuration switch
+  '';
+
+in
+depot.nix.readTree.drvTargets rec {
+  archeologyEc2System = (depot.ops.nixos.nixosFor ({ ... }: {
+    imports = [
+      ./archeology-ec2/configuration.nix
+    ];
+  })).config.system.build.toplevel;
+
+  deploy-archeology-ec2 = (deployScript "archeology-ec2" archeologyEc2System);
+
+  deps = (depot.nix.lazy-deps {
+    deploy-archeology-ec2.attr = "users.flokli.nixos.deploy-archeology-ec2";
+  });
+
+  shell = pkgs.mkShell {
+    name = "flokli-nixos-shell";
+    packages = [ deps ];
+  };
+}
diff --git a/users/flokli/nixos/profiles/archeology.nix b/users/flokli/nixos/profiles/archeology.nix
new file mode 100644
index 0000000000..c87d6bcf30
--- /dev/null
+++ b/users/flokli/nixos/profiles/archeology.nix
@@ -0,0 +1,37 @@
+# Set of unconditional config options applicable to all archeology machines.
+
+{ depot, pkgs, ... }:
+
+{
+  # Use the TVL binary cache
+  tvl.cache.enable = true;
+
+  # Start clickhose as a system service.
+  services.clickhouse.enable = true;
+
+  # for ClickHouse
+  # We're keeping this here rather than in the NixOS module, because I suspect
+  # this opens up timing side channels. This is a single-user, single-purpose
+  # machine, so that isn't a concern here.
+  boot.kernel.sysctl."kernel.task_delayacct" = 1;
+
+  # Enable SSH and let edef and flokli in
+  services.openssh.enable = true;
+
+  users.users.root.openssh.authorizedKeys.keys = [
+    "cert-authority ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCvb/7ojfcbKvHIyjnrNUOOgzy44tCkgXY9HLuyFta1jQOE9pFIK19B4dR9bOglPKf145CCL0mSFJNNqmNwwavU2uRn+TQrW+U1dQAk8Gt+gh3O49YE854hwwyMU+xD6bIuUdfxPr+r5al/Ov5Km28ZMlHOs3FoAP0hInK+eAibioxL5rVJOtgicrOVCkGoXEgnuG+LRbOYTwzdClhRUxiPjK8alCbcJQ53AeZHO4G6w9wTr+W5ILCfvW4OmUXCX01sKzaBiQuuFCF6M/H4LlnsPWLMra2twXxkOIhZblwC+lncps9lQaUgiD4koZeOCORvHW00G0L39ilFbbnVcL6Itp/m8RRWm/xRxS4RMnsdV/AhvpRLrhL3lfQ7E2oCeSM36v1S9rdg6a47zcnpL+ahG76Gz39Y7KmVRQciNx7ezbwxj3Q5lZtFykgdfGIAN+bT8ijXMO6m68g60i9Bz4IoMZGkiJGqMYLTxMQ+oRgR3Ro5lbj7E11YBHyeimoBYXYGHMkiuxopQZ7lIj3plxIzhmUlXJBA4jMw9KGHdYaLhaicIYhvQmCTAjrkt2HvxEe6lU8iws2Qv+pB6tAGundN36RVVWAckeQPZ4ZsgDP8V2FfibZ1nsrQ+zBKqaslYMAHs01Cf0Hm0PnCqagf230xaobu0iooNuXx44QKoDnB+w== edef"
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPTVTXOutUZZjXLB0lUSgeKcSY/8mxKkC0ingGK1whD2 flokli"
+  ];
+
+  # Get a bunch of text editors and CLI tools.
+  environment.systemPackages = [
+    pkgs.awscli
+    pkgs.duckdb
+    pkgs.parquet-tools
+    pkgs.helix
+    pkgs.htop
+    pkgs.kakoune
+    pkgs.kitty.terminfo
+    pkgs.tmux
+  ];
+}
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/.gitignore b/users/flokli/presentations/2023-09-09-nixcon-tvix/.gitignore
new file mode 100644
index 0000000000..397b4a7624
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/.gitignore
@@ -0,0 +1 @@
+*.log
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/architecture.dot b/users/flokli/presentations/2023-09-09-nixcon-tvix/architecture.dot
new file mode 100644
index 0000000000..a6ea0460ef
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/architecture.dot
@@ -0,0 +1,5 @@
+digraph {
+    Builder
+    Store
+    Evaluator
+}
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/cppnix-example-lexer.cpp b/users/flokli/presentations/2023-09-09-nixcon-tvix/cppnix-example-lexer.cpp
new file mode 100644
index 0000000000..7c52bce8b6
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/cppnix-example-lexer.cpp
@@ -0,0 +1,13 @@
+attrpath
+  : attrpath '.' attr {
+    $$ = $1; $1->push_back(AttrName(data->symbols.create($3)));
+  }
+  | attrpath '.' string_attr
+    { $$ = $1;
+      ExprString * str = dynamic_cast<ExprString *>($3);
+      if (str) {
+          $$->push_back(AttrName(data->symbols.create(str->s)));
+          delete str;
+      } else
+          $$->push_back(AttrName($3));
+    }
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/crate-deps.dot b/users/flokli/presentations/2023-09-09-nixcon-tvix/crate-deps.dot
new file mode 100644
index 0000000000..66ead74b1e
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/crate-deps.dot
@@ -0,0 +1,19 @@
+digraph {
+    bgcolor="transparent"
+    node [fillcolor="lightgrey",style="filled"]
+
+    tvix_cli
+    tvix_eval
+    nix_compat
+    tvix_serde
+    tvix_store
+
+    tvix_cli -> tvix_store
+    tvix_cli -> nix_compat
+    tvix_cli -> tvix_eval
+
+    tvix_store -> nix_compat
+    tvix_eval -> nix_compat
+
+    tvix_serde -> tvix_eval
+}
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/default.nix b/users/flokli/presentations/2023-09-09-nixcon-tvix/default.nix
new file mode 100644
index 0000000000..1ec0a0bd0e
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/default.nix
@@ -0,0 +1,37 @@
+{ depot, pkgs, ... }:
+
+let
+  inherit (pkgs)
+    fontconfig qrencode runCommand stdenv;
+  mkQr = url: runCommand "qrcode.png" { } ''
+    ${qrencode}/bin/qrencode -o $out -t SVG -s 5 \
+      --background=fafafa \
+      --foreground=000000 \
+      ${url}
+  '';
+in
+stdenv.mkDerivation {
+  name = "2023-nixcon-tvix";
+  src = ./.;
+
+  FONTCONFIG_FILE = pkgs.makeFontsConf {
+    fontDirectories = with pkgs; [ jetbrains-mono fira fira-code fira-mono lato ];
+  };
+
+  PUPPETEER_EXECUTABLE_PATH = "${pkgs.chromium}/bin/chromium";
+  PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "1";
+
+  nativeBuildInputs = [ pkgs.reveal-md pkgs.graphviz ];
+
+  buildPhase = ''
+    cp ${depot.tvix.logo}/logo.png tvix-logo.png
+    dot -Tsvg crate-deps.dot > crate-deps.svg
+    cp ${mkQr "https://flokli.de"} qrcode-flokli.svg
+    cp ${mkQr "https://tvix.dev"} qrcode-tvix.svg
+
+    mkdir -p $out
+    reveal-md --static $out presentation.md
+    reveal-md --print $out/slides.pdf presentation.md
+    cp tvixbolt.webm $out
+  '';
+}
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/presentation.md b/users/flokli/presentations/2023-09-09-nixcon-tvix/presentation.md
new file mode 100644
index 0000000000..b37554e188
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/presentation.md
@@ -0,0 +1,294 @@
+---
+author:
+- Florian Klink
+date: 2023-09-09
+title: "Tvix: Status update"
+theme: moon
+revealOptions:
+  transition: 'fade'
+---
+
+# Tvix: Status update
+
+![Tvix Logo](tvix-logo.png)
+
+2023-09-09
+
+Florian Klink
+
+---
+
+## Whoami
+
+- flokli
+- nixpkgs contributor since 2018, maintaining systemd, nsncd and some
+  more stuff
+- Freelance Nix/DevOps consultant
+- I spend too much time on computers :-)
+
+---
+
+## What is Tvix?
+
+- A new implementation of Nix
+- modular
+- written in Rust
+- developed in the [TVL](https://tvl.fyi) monorepo
+- subtree exported to [github:tvlfyi/tvix](https://github.com/tvlfyi/tvix)
+
+---
+
+## Structure
+
+- strong separation between **Evaluator**, **Store** and **Builder**
+- Defined interfaces between different components (Protobuf/gRPC) <!-- .element: class="fragment" -->
+  - Allows adding to/combining with your own components <!-- .element: class="fragment" -->
+- <!-- .element: class="fragment" --> A lot of helper code for some of the Nix internals in the `nix-compat` crate
+
+Note: Derivation types, serializers. NAR writers, nixbase32 enc/dec, Nix Hash function, stringparsing etc.
+
+----
+
+![crate-deps.svg](crate-deps.svg)
+
+---
+
+## Evaluator: Design
+
+- <!-- .element: class="fragment" --> 
+  Nix code is parsed via [rnix](https://github.com/nix-community/rnix-parser)
+- <!-- .element: class="fragment" -->
+  AST traversal, generate bytecode (with some transformations)
+- <!-- .element: class="fragment" -->
+  Bytecode is executed by an abstract machine with a Nix-specific instruction set
+
+----
+
+## Evaluator: Design
+
+- <!-- .element: class="fragment" -->
+  Builtins are separate from the "evaluator core"
+  - <!-- .element: class="fragment" -->
+    inject your own builtins
+  - <!-- .element: class="fragment" -->
+    this includes `builtins.derivation`!
+- <!-- .element: class="fragment" -->
+  IO is nicely abstracted away
+  - <!-- .element: class="fragment" -->
+    We can run a Nixlang subset without IO in wasm (see [tvixbolt](https://bolt.tvix.dev/)),
+    or parse Nix into a config struct with `tvix-serde`
+
+----
+
+<!-- <video class="r-stretch" src="./tvixbolt.webm"></video> -->
+<a href="./tvixbolt.webm">Tvixbolt Demo</a>
+
+----
+
+### Evaluator: Current Work
+
+- <!-- .element: class="fragment" -->
+  Current goal: **evaluate nixpkgs the same way as Nix does**
+- <!-- .element: class="fragment" -->
+  Checked by comparing the calculated output paths, which checks correctness of all \"parent\" output paths too.
+- <!-- .element: class="fragment" -->
+  Required implementing a lot of Nix internals in `nix-compat`, and `tvix-store` (A-Term, hash modulo, NAR writer/hasher)
+
+Note: Getting output hashing correct, and exposing this in a re-usable fashion took quite some iterations to get right.
+
+----
+
+### Evaluator: Current Work (cont.)
+- <!-- .element: class="fragment" -->
+  ๐ŸŽ‰ Already correct for (and continously checked by CI on every commit):
+  - <!-- .element: class="fragment" -->
+  `stdenv`, `hello`
+  - <!-- .element: class="fragment" -->
+  `pkgsCross.aarch64-multiplatform.stdenv`, `pkgsCross.aarch64-multiplatform.hello`
+- <!-- .element: class="fragment" -->
+  Some work left for more complicated expressions
+  - <!-- .element: class="fragment" -->
+    infinite recursion [when inheriting from a `builtins.tryEval` multiple times](https://b.tvl.fyi/281)
+  - <!-- .element: class="fragment" -->
+    small details around file imports
+- <!-- .element: class="fragment" -->
+  Not too much performance finetuning until we're correct first.
+
+----
+
+### Evaluator: Demo
+
+[![asciicast](https://asciinema.org/a/MH4tuVPLsKewJSGJUYEyIKUpj.svg)](https://asciinema.org/a/MH4tuVPLsKewJSGJUYEyIKUpj)
+
+---
+
+## Store: Design
+
+- <!-- .element: class="fragment" -->
+  Uses a very different underlying data model:
+  - <!-- .element: class="fragment" -->
+    Nix stores on a per- `StorePath` granularity
+  - <!-- .element: class="fragment" -->
+    tvix-store uses a Merkle DAG of directories, similar to git trees, but with [BLAKE3](https://github.com/BLAKE3-team/BLAKE3) digests as pointers.
+  - <!-- .element: class="fragment" -->
+    Compat layer in front to still render/calculate NAR on demand where needed
+  - <!-- .element: class="fragment" -->
+    Substitution, caching, ... possible to describe via composition/layering
+
+----
+
+![tvix-store graph](tvix-store-graph.svg)
+
+----
+
+### Store: Advantages
+
+- <!-- .element: class="fragment" -->
+  Less downloading of data that didn't change
+- <!-- .element: class="fragment" -->
+  Less duplicate data on disk/storage
+- <!-- .element: class="fragment" -->
+  Inherently content-addressed, so P2P substitution possible
+- <!-- .element: class="fragment" -->
+  Allows doing verified blob streaming ([BAO](https://github.com/oconnor663/bao), [bao-tree](https://github.com/n0-computer/bao-tree))
+- <!-- .element: class="fragment" -->
+  Protocol has some \"smarter\" methods to avoid roundtrips, but could all be statically pre-rendered
+- <!-- .element: class="fragment" -->
+  Very little data that needs to be fetched from a trusted party (or be signed)
+
+Note: Our way of addressing blobs by their raw blake3 digest is natively compatible with iroh, the IPFS Re-implementation in Rust
+
+----
+
+### Store: Status
+
+- <!-- .element: class="fragment" -->
+  Whole Merkle-based store implementation (and Nix NAR compat layer) is there
+  - <!-- .element: class="fragment" -->
+    exercised by the output path CI tests, and a test suite.
+  - <!-- .element: class="fragment" -->
+    three backends (Sled, in-memory, gRPC client)
+  - <!-- .element: class="fragment" -->
+    more backends and more test suites planned.
+- <!-- .element: class="fragment" -->
+  FUSE filesystem to expose the store (to Tvix Builders, \"appliances\") <!-- .element: class="fragment" -->
+
+Note: backends: RocksDB, sqlite, s3. fuse: lazy fetching of build input files | think about a minimal initrd to bring up network and mount the store, then run any closure.
+
+----
+
+### Store: Demo
+
+[![asciicast](https://asciinema.org/a/YFB9yycHdx0OUH9N0WdAkIYua.svg)](https://asciinema.org/a/YFB9yycHdx0OUH9N0WdAkIYua)
+
+----
+
+### Store: Status (cont.)
+- <!-- .element: class="fragment" -->
+  More work on store composition needed (necessary for substition and caching)
+- <!-- .element: class="fragment" -->
+  More work on more granular blob substititon needed.
+- <!-- .element: class="fragment" -->
+  More work on bridges with Nix needed
+  - <!-- .element: class="fragment" -->
+    Get Nix to talk to a tvix-store
+  - <!-- .element: class="fragment" -->
+    Expose existing binary caches to tvix-store
+
+---
+
+### Builder: Design
+
+- <!-- .element: class="fragment" -->
+  Build requests/Build protocol is less Nix-specific
+  - <!-- .element: class="fragment" -->
+    allows reusing builders for other usages (non-sandboxed builds, other build systems, playing with other addressing mechanisms)
+- <!-- .element: class="fragment" -->
+  Distinction between a **specific build attempt** and the **general build recipe**
+  - <!-- .element: class="fragment" -->
+    allows keeping metadata about failed builds
+  - <!-- .element: class="fragment" -->
+    stats (memory/cpu consumption)
+  - <!-- .element: class="fragment" -->
+    comparing different produced binary outputs (r11y)
+
+----
+
+### Builder: Design
+
+- <!-- .element: class="fragment" -->
+  Invididual builds can be run in your desired container/virt engine/scheduler, as long as it speaks the same Build API
+- <!-- .element: class="fragment" -->
+  Build API composition/proxying, similar to Store composition
+- <!-- .element: class="fragment" -->
+  allows "unattended building" (evaluate nixpkgs locally and send all build requests to a remote builder)
+- <!-- .element: class="fragment" -->
+  allows tailing logs from currently/already running builds
+
+----
+
+### Builder: Status
+
+- <!-- .element: class="fragment" -->
+  Dummy Builder implementation in `go-nix` (using OCI)
+- <!-- .element: class="fragment" -->
+  Some scribble notes on the Build Protocol
+- <!-- .element: class="fragment" -->
+  Glue code to trigger builds from inside `builtins.derivation` needs to be written
+- <!-- .element: class="fragment" -->
+  Builder implementation (using `systemd-nspwan` or some container engine needs to be written.
+- <!-- .element: class="fragment" -->
+  Web interface to visualize store contents and build graphs/builds/logs
+
+---
+
+## Contributing
+
+- <!-- .element: class="fragment" -->
+  Join the IRC channel (`#tvl` on `hackint`), bridged to Matrix and XMPP
+- <!-- .element: class="fragment" -->
+  Check our issue tracker
+- <!-- .element: class="fragment" -->
+  Try to use it and tell us how you broke it!
+- <!-- .element: class="fragment" -->
+  Add various Nix bits to `nix-compat`
+
+Note: or if you like what you seeing and want to sponsor parts, that's also cool :-)
+
+---
+
+# Thanks to...
+
+- <!-- .element: class="fragment" -->
+  all TVL contributors (some drive-by, some long-term contributors)
+- <!-- .element: class="fragment" -->
+  countless Nix community members for their input on the architecture and rubberducking
+- <!-- .element: class="fragment" -->
+  NLNET and others to sponsor parts of this
+
+----
+
+# Questions?
+
+<style>
+.container{
+    display: flex;
+}
+.col{
+    flex: 1;
+}
+</style>
+
+<div class="container">
+
+<div class="col">
+Florian Klink / <a href="https://flokli.de">flokli.de</a><br />
+<img src="qrcode-flokli.svg" />
+</div>
+
+<div class="col">
+Tvix / <a href="https://tvix.dev">tvix.dev</a><br />
+<img src="qrcode-tvix.svg" />
+</div>
+
+</div>
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/tvix-store-graph.svg b/users/flokli/presentations/2023-09-09-nixcon-tvix/tvix-store-graph.svg
new file mode 100644
index 0000000000..56338b587e
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/tvix-store-graph.svg
@@ -0,0 +1,17 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1297.1484375 836.19140625" width="1297.1484375" height="836.19140625">
+  <!-- svg-source:excalidraw -->
+  
+  <defs>
+    <style class="style-fonts">
+      @font-face {
+        font-family: "Virgil";
+        src: url("https://excalidraw.com/Virgil.woff2");
+      }
+      @font-face {
+        font-family: "Cascadia";
+        src: url("https://excalidraw.com/Cascadia.woff2");
+      }
+    </style>
+    
+  </defs>
+  <rect x="0" y="0" width="1297.1484375" height="836.19140625" fill="#ffffff"></rect><g stroke-linecap="round" transform="translate(10 64.05078125) rotate(0 71.10546875 23.685546875)"><path d="M3.15 2.74 C3.15 2.74, 3.15 2.74, 3.15 2.74 M3.15 2.74 C3.15 2.74, 3.15 2.74, 3.15 2.74 M1.58 10.65 C3.55 6.56, 7.08 5.83, 10.11 0.84 M1.58 10.65 C4.54 7.52, 5.83 5.33, 10.11 0.84 M1.97 16.29 C5.97 12.52, 7.97 9.89, 14.44 1.95 M1.97 16.29 C5.39 12.84, 8.96 8.01, 14.44 1.95 M1.71 22.69 C5.44 18.07, 11.5 12.49, 19.43 2.31 M1.71 22.69 C6.47 16.78, 11.69 12.31, 19.43 2.31 M1.45 29.09 C9.97 18.06, 21.19 8.77, 25.07 1.92 M1.45 29.09 C9.61 17.76, 19.35 7.56, 25.07 1.92 M1.85 34.73 C6.66 29.67, 14.83 20.48, 30.06 2.28 M1.85 34.73 C8.29 27.25, 17.05 18.03, 30.06 2.28 M2.24 40.37 C10.77 28.82, 20.92 18.16, 35.7 1.88 M2.24 40.37 C14.65 26.46, 25.75 13.13, 35.7 1.88 M3.29 45.26 C19.71 28.3, 32.79 9.79, 40.69 2.24 M3.29 45.26 C14.19 32.96, 23.82 22.41, 40.69 2.24 M6.97 47.13 C21.76 30.34, 38.17 12.66, 45.67 2.6 M6.97 47.13 C19.37 32.8, 32.58 18.66, 45.67 2.6 M12.61 46.74 C23.7 34.53, 32.71 23.85, 51.32 2.21 M12.61 46.74 C23.04 33.52, 34.64 21.81, 51.32 2.21 M17.6 47.1 C32.92 32.2, 44.82 17.61, 56.3 2.57 M17.6 47.1 C26.86 36.54, 34.43 27.02, 56.3 2.57 M22.58 47.46 C36.55 31.33, 51.38 11.55, 61.95 2.17 M22.58 47.46 C35.33 33.95, 46.66 20.15, 61.95 2.17 M28.23 47.06 C40.8 33.63, 55.34 16.98, 66.93 2.53 M28.23 47.06 C36.54 38.37, 43.25 29.02, 66.93 2.53 M33.21 47.42 C48.21 29.57, 61.74 14.8, 72.58 2.14 M33.21 47.42 C44.03 35.16, 55.8 21.76, 72.58 2.14 M38.86 47.03 C49.09 34.45, 58.15 26.51, 77.56 2.5 M38.86 47.03 C53.73 30.24, 67.18 15.29, 77.56 2.5 M43.84 47.39 C57.75 33, 73.31 14.12, 82.55 2.86 M43.84 47.39 C53.3 37.24, 61.17 27.55, 82.55 2.86 M49.49 46.99 C62.68 35.47, 70.45 21.6, 88.19 2.46 M49.49 46.99 C61.84 34.86, 73.11 21.46, 88.19 2.46 M54.47 47.35 C61.1 38.29, 69.25 27.83, 93.18 2.82 M54.47 47.35 C66.26 32.61, 76.99 20.02, 93.18 2.82 M60.12 46.96 C73.27 30.81, 84.64 16.32, 98.82 2.43 M60.12 46.96 C69.94 34.83, 80.66 21.92, 98.82 2.43 M65.1 47.32 C73.48 35.83, 86.65 26.47, 103.81 2.79 M65.1 47.32 C78.64 32.14, 91.97 18.02, 103.81 2.79 M70.09 47.68 C85.6 30.67, 98.3 12.05, 109.45 2.4 M70.09 47.68 C81.34 34.02, 94.32 21.42, 109.45 2.4 M75.73 47.28 C89.43 31.61, 102.84 18.22, 114.44 2.76 M75.73 47.28 C85.67 35.55, 96.35 22.65, 114.44 2.76 M80.72 47.64 C91.93 34.98, 103.63 17.52, 119.43 3.12 M80.72 47.64 C90.36 36.8, 98.88 24.6, 119.43 3.12 M86.36 47.25 C101.83 29.55, 117.32 12.7, 125.07 2.72 M86.36 47.25 C99.66 32.13, 111.08 17.18, 125.07 2.72 M91.35 47.61 C104.35 33.22, 117.8 19.95, 130.06 3.08 M91.35 47.61 C103.56 34.45, 114.2 20.27, 130.06 3.08 M96.99 47.21 C103.59 38.71, 113.94 28.96, 136.36 1.93 M96.99 47.21 C106.26 36.64, 117.24 24.44, 136.36 1.93 M101.98 47.57 C111.9 34.49, 126.09 20.88, 140.03 3.8 M101.98 47.57 C114.1 33.1, 128.09 16.65, 140.03 3.8 M106.97 47.93 C114.85 39.44, 121.62 28.8, 141.74 7.93 M106.97 47.93 C117.52 37.38, 125.94 26.56, 141.74 7.93 M112.61 47.54 C120.79 38.54, 126.18 28.87, 144.1 11.31 M112.61 47.54 C125.62 33.32, 137.05 19.84, 144.1 11.31 M117.6 47.9 C122.32 42.89, 130.44 32.85, 144.5 16.96 M117.6 47.9 C126.99 36.5, 135.5 27.33, 144.5 16.96 M123.24 47.51 C127.95 43.64, 132.17 36.33, 144.23 23.35 M123.24 47.51 C129.47 41.35, 136.09 32.69, 144.23 23.35 M128.23 47.87 C131.28 40.54, 137.28 36.59, 143.97 29.75 M128.23 47.87 C132.3 43.29, 136.94 37.23, 143.97 29.75 M132.56 48.98 C136.69 46.03, 136.69 42.77, 143.71 36.15 M132.56 48.98 C136.02 46.4, 138.73 42.29, 143.71 36.15" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M11.84 0 M11.84 0 C38.02 2.65, 61.84 0.28, 130.37 0 M11.84 0 C38.21 1.94, 65.76 1.5, 130.37 0 M130.37 0 C139.47 1, 143.75 5.88, 142.21 11.84 M130.37 0 C137.21 0.92, 139.99 4.87, 142.21 11.84 M142.21 11.84 C140.42 17.68, 141.72 25.2, 142.21 35.53 M142.21 11.84 C142.2 20.55, 141.47 28.9, 142.21 35.53 M142.21 35.53 C143.14 42.65, 138.92 45.76, 130.37 47.37 M142.21 35.53 C141.26 45.41, 136.07 48.58, 130.37 47.37 M130.37 47.37 C100.03 46.91, 70.33 46.17, 11.84 47.37 M130.37 47.37 C93.27 45.68, 57.65 45.47, 11.84 47.37 M11.84 47.37 C3.83 47.21, 0.04 43.58, 0 35.53 M11.84 47.37 C3.58 49.37, 2.09 44.62, 0 35.53 M0 35.53 C-0.11 25.56, 1.74 17.16, 0 11.84 M0 35.53 C-0.73 27.14, 0.25 16.41, 0 11.84 M0 11.84 C1.26 4.93, 5.18 0.97, 11.84 0 M0 11.84 C0.62 1.9, 3.42 2.05, 11.84 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(15 78.13632812499998) rotate(0 65.625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">0x01 0x02 0x03</text></g><g stroke-linecap="round" transform="translate(11.80078125 149.404296875) rotate(0 51.5 24.5)"><path d="M3.26 2.83 C3.26 2.83, 3.26 2.83, 3.26 2.83 M3.26 2.83 C3.26 2.83, 3.26 2.83, 3.26 2.83 M1.69 10.74 C4.79 9.22, 6.14 6.4, 10.22 0.93 M1.69 10.74 C4.82 7.44, 8.36 3.46, 10.22 0.93 M1.43 17.14 C5.08 10.94, 12.94 4.63, 14.55 2.05 M1.43 17.14 C7.42 12.34, 11.48 5.83, 14.55 2.05 M1.82 22.78 C7.79 17.76, 9.92 11.78, 20.19 1.65 M1.82 22.78 C6.91 17.54, 10.69 12.89, 20.19 1.65 M1.56 29.18 C10.68 19.58, 16.05 8.86, 25.18 2.01 M1.56 29.18 C6.66 24.1, 10.7 18.88, 25.18 2.01 M1.95 34.82 C14.06 20.7, 22.56 11.87, 30.16 2.37 M1.95 34.82 C12.05 23.95, 21.94 11.12, 30.16 2.37 M1.69 41.22 C7.98 31.97, 17.66 24.23, 35.81 1.98 M1.69 41.22 C14.67 26.87, 25.96 11.6, 35.81 1.98 M3.4 45.35 C12.42 31.5, 21.88 20.01, 40.79 2.34 M3.4 45.35 C14.79 32.81, 25.98 18.39, 40.79 2.34 M5.76 48.73 C19.03 33.54, 30.13 20.75, 46.44 1.94 M5.76 48.73 C15.95 35.19, 26.77 22.13, 46.44 1.94 M10.75 49.09 C25.08 35.85, 34.98 19.45, 51.42 2.3 M10.75 49.09 C25.36 34.29, 38.07 17.14, 51.42 2.3 M14.42 50.96 C25.24 34.64, 40.8 20.23, 57.07 1.91 M14.42 50.96 C31.56 31.52, 47.54 13.07, 57.07 1.91 M20.07 50.57 C36.58 31.78, 51.41 11.57, 62.05 2.27 M20.07 50.57 C35.27 32.37, 49.62 15.57, 62.05 2.27 M25.05 50.93 C33.69 40.39, 45.49 30.76, 67.7 1.87 M25.05 50.93 C39.57 35.58, 52.89 19.97, 67.7 1.87 M30.04 51.29 C42.51 36.46, 56.98 20.61, 72.68 2.23 M30.04 51.29 C39.08 40.34, 49.2 29.9, 72.68 2.23 M35.68 50.89 C47.44 39.9, 56.68 27.08, 78.33 1.84 M35.68 50.89 C52.05 33.69, 66.26 15.86, 78.33 1.84 M40.67 51.25 C53.32 36.02, 69.16 19.85, 83.31 2.2 M40.67 51.25 C53.19 37.93, 64.14 24.78, 83.31 2.2 M46.31 50.86 C62.11 32.31, 78.52 15.58, 88.96 1.8 M46.31 50.86 C57.68 37.06, 69.81 23.8, 88.96 1.8 M51.3 51.22 C59.29 38.98, 72.37 29.04, 93.94 2.16 M51.3 51.22 C64.73 35.51, 79.44 17.72, 93.94 2.16 M56.94 50.83 C69.81 34.76, 86.63 19.59, 98.93 2.52 M56.94 50.83 C72.69 32.66, 90.36 14.37, 98.93 2.52 M61.93 51.19 C77.07 35.49, 89.58 18.3, 101.95 5.15 M61.93 51.19 C76.05 33.49, 92.37 16.74, 101.95 5.15 M67.57 50.79 C76.42 41.59, 82.64 33.63, 103 10.04 M67.57 50.79 C76.25 40.25, 84.99 30.22, 103 10.04 M72.56 51.15 C79.09 44.86, 86.76 36.01, 103.4 15.68 M72.56 51.15 C78.4 43.41, 84.37 36.12, 103.4 15.68 M78.2 50.76 C84.15 41.95, 92.56 33.07, 103.13 22.08 M78.2 50.76 C88.29 39.34, 96.97 28.67, 103.13 22.08 M83.19 51.12 C88.53 45.31, 96.1 36.68, 103.53 27.72 M83.19 51.12 C91.24 41.6, 98.32 33.94, 103.53 27.72 M88.83 50.72 C90.5 47.29, 97.87 43.07, 103.27 34.12 M88.83 50.72 C94.68 44.06, 98.18 39.73, 103.27 34.12 M93.82 51.08 C96.56 47.12, 100.38 44.41, 104.97 38.25 M93.82 51.08 C96.61 47.17, 101.29 42.48, 104.97 38.25" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M12.25 0 M12.25 0 C28.51 0, 42.5 2.25, 90.75 0 M12.25 0 C32.14 -0.62, 52.39 1.04, 90.75 0 M90.75 0 C100.71 0.09, 101.8 3.77, 103 12.25 M90.75 0 C97.64 -0.83, 104 5.94, 103 12.25 M103 12.25 C101.33 19.82, 101.63 32.15, 103 36.75 M103 12.25 C102.35 21.13, 102.15 31.06, 103 36.75 M103 36.75 C104.56 46.87, 99.61 50.21, 90.75 49 M103 36.75 C104.15 46.69, 101.13 47.94, 90.75 49 M90.75 49 C62.94 48.31, 35.39 47.65, 12.25 49 M90.75 49 C67.73 49.76, 43.51 49.69, 12.25 49 M12.25 49 C3.6 47.48, -1.71 45.84, 0 36.75 M12.25 49 C3.19 49.75, -1.85 43.96, 0 36.75 M0 36.75 C-0.6 25.33, 0.5 18.11, 0 12.25 M0 36.75 C-0.07 30.3, -1.01 22.9, 0 12.25 M0 12.25 C-1.34 4.43, 2.32 -0.12, 12.25 0 M0 12.25 C-0.18 4.12, 4.26 -0.37, 12.25 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(16.80078125 164.30429687499998) rotate(0 42.1875 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">0x04 0x05</text></g><g stroke-linecap="round" transform="translate(14.93359375 236.67578125) rotate(0 4.138671875 25.552734375)"><path d="M-0.29 2.4 C-0.29 2.4, -0.29 2.4, -0.29 2.4 M-0.29 2.4 C-0.29 2.4, -0.29 2.4, -0.29 2.4 M0.11 8.04 C1.82 6.13, 2.96 4.61, 4.7 2.76 M0.11 8.04 C1.05 6.92, 2.71 5.33, 4.7 2.76 M-0.15 14.44 C2.61 10.36, 4.52 9.27, 9.69 3.12 M-0.15 14.44 C3 10.45, 7.79 5.81, 9.69 3.12 M0.24 20.08 C1.78 17.33, 6.21 13.86, 9.43 9.52 M0.24 20.08 C3.9 16.52, 6.5 12.39, 9.43 9.52 M-0.02 26.48 C2.46 23.23, 7.19 20.21, 9.82 15.16 M-0.02 26.48 C2.52 23.58, 5.92 19.96, 9.82 15.16 M-0.28 32.88 C3.43 29.78, 4.96 26.51, 9.56 21.56 M-0.28 32.88 C1.68 30.37, 4.75 26.47, 9.56 21.56 M0.11 38.52 C2.43 35.55, 6.84 32.06, 9.3 27.96 M0.11 38.52 C3.34 34.69, 7.79 30.27, 9.3 27.96 M-0.15 44.92 C4.41 39.72, 7.29 36.44, 9.69 33.6 M-0.15 44.92 C2.92 41.4, 5.54 37.99, 9.69 33.6 M0.9 49.81 C2.58 47.91, 5.61 43.76, 9.43 40 M0.9 49.81 C3.14 46.67, 5.91 43.78, 9.43 40 M3.92 52.43 C5.01 50.49, 6.18 49.02, 9.82 45.64 M3.92 52.43 C5.89 50.77, 7.52 48.28, 9.82 45.64" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M2.07 0 M2.07 0 C2.97 -0.21, 4.11 0.12, 6.21 0 M2.07 0 C3.19 -0.16, 4.23 0.11, 6.21 0 M6.21 0 C8.97 0.45, 9.01 1.39, 8.28 2.07 M6.21 0 C9.57 -1.98, 9.89 2.6, 8.28 2.07 M8.28 2.07 C8.66 12.58, 9.73 22.31, 8.28 49.04 M8.28 2.07 C8.53 15.26, 7.49 28.86, 8.28 49.04 M8.28 49.04 C6.95 49.38, 7.58 52.75, 6.21 51.11 M8.28 49.04 C10.35 52.7, 9.64 53.24, 6.21 51.11 M6.21 51.11 C5.12 51.08, 3.76 50.9, 2.07 51.11 M6.21 51.11 C5.14 51.07, 4.25 51.01, 2.07 51.11 M2.07 51.11 C2.45 52.5, -1.17 52.21, 0 49.04 M2.07 51.11 C-1.47 50.3, 2.23 51.76, 0 49.04 M0 49.04 C-0.13 38.39, -0.48 26.48, 0 2.07 M0 49.04 C-1.31 32.15, -0.35 13.45, 0 2.07 M0 2.07 C0.41 0.36, -0.67 -0.56, 2.07 0 M0 2.07 C-0.35 -0.58, -1.03 -1.54, 2.07 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(10.23046875 10) rotate(0 23.4375 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">Blobs</text></g><g transform="translate(279.12890625 12.759374999999977) rotate(0 51.5625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">Directories</text></g><g stroke-linecap="round" transform="translate(283.875 68.3828125) rotate(0 197 72.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C8.28 14.49, 8.49 11.04, 18.1 2.48 M3.66 19.09 C7.7 14.57, 11.52 11.66, 18.1 2.48 M2.75 26.24 C11.51 19.05, 17.92 11.98, 24.4 1.33 M2.75 26.24 C7.23 20.55, 12.55 15.89, 24.4 1.33 M2.48 32.64 C12.12 24.05, 19.28 13.72, 30.7 0.18 M2.48 32.64 C9.16 24.56, 14.83 17.39, 30.7 0.18 M2.88 38.28 C11.43 28.28, 22.31 16.44, 34.37 2.05 M2.88 38.28 C14.6 24.25, 27.89 9.94, 34.37 2.05 M2.62 44.68 C9.32 34.89, 17.39 26.88, 39.36 2.41 M2.62 44.68 C11.95 33.82, 21.66 21.02, 39.36 2.41 M2.36 51.07 C12.81 41.21, 19.43 27.76, 45 2.02 M2.36 51.07 C16.68 33.29, 30.46 16.46, 45 2.02 M2.75 56.72 C16.64 41.98, 28.98 26.21, 49.99 2.38 M2.75 56.72 C21 35.95, 39.52 14.81, 49.99 2.38 M2.49 63.11 C19.63 44.26, 39.08 21.21, 55.63 1.98 M2.49 63.11 C17.01 46.73, 31.61 30.35, 55.63 1.98 M2.88 68.76 C22.9 46.33, 43.08 21.26, 60.62 2.34 M2.88 68.76 C23.19 45.39, 41.88 24.57, 60.62 2.34 M2.62 75.16 C23.83 54.32, 39.89 29.17, 66.26 1.95 M2.62 75.16 C18.47 56.13, 34.46 38.2, 66.26 1.95 M2.36 81.55 C29.87 50.88, 54.51 22.49, 71.25 2.31 M2.36 81.55 C26.5 54.38, 49.03 26.57, 71.25 2.31 M2.76 87.2 C25.76 61.24, 52.69 29.56, 76.89 1.91 M2.76 87.2 C22.68 64.01, 42.85 40.22, 76.89 1.91 M2.49 93.59 C30.49 61.54, 57.87 26.06, 81.88 2.27 M2.49 93.59 C31.33 60.52, 59.32 27.03, 81.88 2.27 M2.89 99.24 C28.16 70.88, 49.29 44.38, 87.52 1.88 M2.89 99.24 C22.19 77.54, 40.48 55.68, 87.52 1.88 M2.63 105.64 C27.42 78.52, 50.89 50.01, 92.51 2.24 M2.63 105.64 C38.6 64.43, 74.48 23.22, 92.51 2.24 M2.37 112.03 C35.69 71.87, 67.49 34.66, 98.15 1.85 M2.37 112.03 C29.87 80.37, 57.31 48.8, 98.15 1.85 M2.76 117.68 C35.35 78.95, 70.62 41.89, 103.14 2.21 M2.76 117.68 C24.16 94.43, 44.43 69.26, 103.14 2.21 M3.16 123.32 C27.31 95.67, 53.64 64.97, 108.78 1.81 M3.16 123.32 C40.3 79.27, 78.36 37.25, 108.78 1.81 M4.86 127.45 C35.22 90.31, 66.42 52.47, 113.77 2.17 M4.86 127.45 C27.89 100.07, 53.15 74.47, 113.77 2.17 M5.26 133.1 C41.54 89.35, 82.58 48.1, 119.41 1.78 M5.26 133.1 C50.78 80.9, 96.57 28.36, 119.41 1.78 M7.62 136.47 C50.54 85.68, 96.45 32.28, 124.4 2.14 M7.62 136.47 C50.54 87.26, 95.13 37.29, 124.4 2.14 M10.64 139.1 C44.47 96.28, 83.89 53.02, 130.04 1.74 M10.64 139.1 C49.89 96.71, 86.98 53.36, 130.04 1.74 M14.31 140.97 C55.74 92.08, 97.64 42.48, 135.03 2.1 M14.31 140.97 C49.36 97.94, 86.03 55.71, 135.03 2.1 M17.33 143.59 C62.88 90.12, 107.49 38.88, 140.67 1.71 M17.33 143.59 C52.29 102.09, 87.68 60.93, 140.67 1.71 M21.01 145.46 C48.28 114.13, 76.95 81.48, 145.66 2.07 M21.01 145.46 C64.61 95.84, 107.71 47.87, 145.66 2.07 M25.34 146.58 C73.23 89.86, 125.31 35.24, 150.65 2.43 M25.34 146.58 C51.9 117.94, 78.32 86.29, 150.65 2.43 M32.29 144.67 C78.07 95.21, 118.24 44.56, 156.29 2.03 M32.29 144.67 C74.54 98.13, 114.59 49.94, 156.29 2.03 M37.28 145.03 C85.09 88.58, 133.69 36.74, 161.28 2.39 M37.28 145.03 C69.44 108.12, 100.26 71.2, 161.28 2.39 M42.92 144.64 C90.72 91.41, 136.95 40.49, 166.92 2 M42.92 144.64 C66.95 114.58, 93.7 85.42, 166.92 2 M47.91 145 C75.02 114.35, 102.25 80.22, 171.91 2.36 M47.91 145 C87.4 99.39, 126.35 54.03, 171.91 2.36 M52.9 145.36 C79.04 113.6, 107.32 79.81, 177.55 1.96 M52.9 145.36 C77.61 115.94, 103.48 86, 177.55 1.96 M58.54 144.96 C92.12 105.3, 128.8 63.9, 182.54 2.32 M58.54 144.96 C106.61 90.51, 152.9 36.35, 182.54 2.32 M63.53 145.32 C107.47 99.27, 149.03 48.65, 188.18 1.93 M63.53 145.32 C96.13 109.61, 126.87 72.99, 188.18 1.93 M69.17 144.93 C102.47 104.11, 137.86 61.81, 193.17 2.29 M69.17 144.93 C110.04 98.38, 152.23 51.84, 193.17 2.29 M74.16 145.29 C104.78 108.19, 138.63 71.58, 198.81 1.9 M74.16 145.29 C99.31 117.03, 123.84 87.71, 198.81 1.9 M79.8 144.9 C130.71 87.32, 178.54 30.53, 203.8 2.26 M79.8 144.9 C124.29 94.42, 167.51 43.91, 203.8 2.26 M84.79 145.26 C111.41 111.48, 143.37 80.45, 209.44 1.86 M84.79 145.26 C130.36 93.05, 176.97 39.33, 209.44 1.86 M90.43 144.86 C128.08 97.62, 168.49 56.08, 214.43 2.22 M90.43 144.86 C130.35 96.04, 171.08 48.72, 214.43 2.22 M95.42 145.22 C146.19 90.39, 192.4 31.56, 220.07 1.83 M95.42 145.22 C120.72 114.85, 146.9 84.4, 220.07 1.83 M101.06 144.83 C127.22 111.52, 152.8 83.04, 225.06 2.19 M101.06 144.83 C132.86 108.13, 163.34 71.68, 225.06 2.19 M106.05 145.19 C154.17 92.56, 198.8 39.31, 230.7 1.79 M106.05 145.19 C136.53 110.59, 166.08 76.75, 230.7 1.79 M111.69 144.79 C138.65 117.15, 162.06 85.81, 235.69 2.15 M111.69 144.79 C137.61 113.65, 164.22 81.98, 235.69 2.15 M116.68 145.15 C157.06 99.25, 194.58 56.02, 241.33 1.76 M116.68 145.15 C155.1 99.61, 194.87 53.74, 241.33 1.76 M122.32 144.76 C155.38 104.12, 188.9 66.56, 246.32 2.12 M122.32 144.76 C171.19 89.94, 218.89 34.94, 246.32 2.12 M127.31 145.12 C160.63 107.75, 195.41 68.39, 251.96 1.72 M127.31 145.12 C171.79 94.11, 216.13 43.25, 251.96 1.72 M132.95 144.72 C167.14 104.52, 206.3 64.21, 256.95 2.08 M132.95 144.72 C164.99 105.52, 198.48 68.92, 256.95 2.08 M137.94 145.08 C183.71 92.31, 226.45 45.7, 262.59 1.69 M137.94 145.08 C163.89 115.91, 190.22 85.14, 262.59 1.69 M143.58 144.69 C174.92 109.48, 206.89 70.46, 267.58 2.05 M143.58 144.69 C170.4 113.21, 199.74 82.17, 267.58 2.05 M148.57 145.05 C178.08 111.76, 203.33 81.73, 272.56 2.41 M148.57 145.05 C173.64 115.53, 198.59 86.39, 272.56 2.41 M154.21 144.65 C187.55 108.07, 219.55 68.66, 278.21 2.01 M154.21 144.65 C200.06 91.97, 243.47 39.89, 278.21 2.01 M159.2 145.01 C186.66 112.8, 212.53 85.21, 283.19 2.37 M159.2 145.01 C207.12 89.92, 255.48 37.14, 283.19 2.37 M164.19 145.37 C202.12 104.92, 237.2 59.79, 288.84 1.98 M164.19 145.37 C194.82 106.77, 226.84 70.45, 288.84 1.98 M169.83 144.98 C212.12 93.05, 258.35 43.46, 293.82 2.34 M169.83 144.98 C201.44 106.46, 235.81 67.61, 293.82 2.34 M174.82 145.34 C207.54 111.65, 235.58 74.76, 299.47 1.94 M174.82 145.34 C199.92 118.44, 225.68 88.79, 299.47 1.94 M180.46 144.94 C208.5 116.42, 230.68 85.13, 304.45 2.3 M180.46 144.94 C218.97 100.78, 259.51 55.75, 304.45 2.3 M185.45 145.31 C221.24 102.72, 256.86 59.74, 310.1 1.91 M185.45 145.31 C232.8 92.21, 280.33 37.72, 310.1 1.91 M191.09 144.91 C238.27 88.19, 288.2 35.33, 315.08 2.27 M191.09 144.91 C220.59 109.55, 252.22 74.41, 315.08 2.27 M196.08 145.27 C239.76 94.85, 284.75 46.35, 320.73 1.88 M196.08 145.27 C230.49 104.94, 265.25 68.33, 320.73 1.88 M201.72 144.88 C234.66 108.79, 263.22 71.79, 325.71 2.24 M201.72 144.88 C237.32 101.42, 273.7 59.07, 325.71 2.24 M206.71 145.24 C253.21 90.64, 300.99 37.76, 331.36 1.84 M206.71 145.24 C240.65 108.22, 273.71 69.33, 331.36 1.84 M212.35 144.84 C242.19 114.5, 267.67 79.17, 336.34 2.2 M212.35 144.84 C236.6 115.07, 262.16 86.57, 336.34 2.2 M217.34 145.2 C251.25 105.29, 286.42 64.08, 341.99 1.81 M217.34 145.2 C249.04 108.05, 278.75 71.6, 341.99 1.81 M222.98 144.81 C254.78 109.71, 284.95 73.51, 346.97 2.17 M222.98 144.81 C251.44 112.17, 277.38 81.44, 346.97 2.17 M227.97 145.17 C258.67 109.25, 290.39 71.64, 352.62 1.77 M227.97 145.17 C253.46 115.85, 279.91 85.09, 352.62 1.77 M233.61 144.77 C279.92 90.22, 328.36 36.09, 357.6 2.13 M233.61 144.77 C263.54 109.84, 296.58 72.44, 357.6 2.13 M238.6 145.13 C284.33 94.02, 329.86 38.18, 363.25 1.74 M238.6 145.13 C270.6 110.97, 300.1 77.3, 363.25 1.74 M244.24 144.74 C285.03 98.47, 325.99 49.52, 368.23 2.1 M244.24 144.74 C288.81 94.28, 333.65 43.13, 368.23 2.1 M249.23 145.1 C274.07 113.24, 304.32 83.42, 373.22 2.46 M249.23 145.1 C288.32 102.57, 326.71 58.15, 373.22 2.46 M254.87 144.7 C297.9 94.29, 343.78 43.11, 377.55 3.57 M254.87 144.7 C286.66 106.11, 320.39 66.72, 377.55 3.57 M259.86 145.06 C295.8 103.74, 331.06 61.72, 381.23 5.44 M259.86 145.06 C298.82 101.67, 336.82 56.01, 381.23 5.44 M265.5 144.67 C313.19 90.52, 361.44 33.85, 384.9 7.31 M265.5 144.67 C292.92 114.87, 319.46 83.51, 384.9 7.31 M270.49 145.03 C293.76 118.67, 317.19 90.91, 387.92 9.94 M270.49 145.03 C298.6 112.33, 327.42 78.58, 387.92 9.94 M276.13 144.63 C309.18 107.62, 339.56 70.71, 390.28 13.32 M276.13 144.63 C314.84 98.86, 353.06 54.55, 390.28 13.32 M281.12 144.99 C305.94 116.7, 328.37 88.76, 391.99 17.45 M281.12 144.99 C321.03 97.29, 362.22 51.33, 391.99 17.45 M286.1 145.35 C324.81 103.13, 365.35 57.02, 393.04 22.34 M286.1 145.35 C313.97 112.26, 343.79 77.31, 393.04 22.34 M291.75 144.96 C324.16 107.97, 358.78 71.44, 395.4 25.72 M291.75 144.96 C322.28 110.33, 352.3 74.13, 395.4 25.72 M296.73 145.32 C322.53 113.82, 350.61 86.17, 395.14 32.11 M296.73 145.32 C330.9 104.68, 367.32 64.68, 395.14 32.11 M302.38 144.93 C320.38 122.95, 340.8 101.95, 395.54 37.76 M302.38 144.93 C328.33 112.21, 356.43 82.25, 395.54 37.76 M307.36 145.29 C337.92 110.64, 366.55 76.31, 395.28 44.15 M307.36 145.29 C325.37 123.43, 344.42 100.44, 395.28 44.15 M313.01 144.89 C335.45 120.95, 355.71 93.18, 395.01 50.55 M313.01 144.89 C342.22 110.33, 372.53 76.59, 395.01 50.55 M317.99 145.25 C335.46 125.76, 350.14 107.98, 395.41 56.2 M317.99 145.25 C334.45 126.54, 349.07 108.69, 395.41 56.2 M323.64 144.86 C350.88 112.04, 380.82 81.41, 395.15 62.59 M323.64 144.86 C349.73 114.44, 376.12 84.65, 395.15 62.59 M328.62 145.22 C342.07 126.15, 358.11 110, 395.54 68.24 M328.62 145.22 C352.38 118.21, 373.64 91.69, 395.54 68.24 M334.27 144.82 C356.58 116.53, 380.06 91.2, 395.28 74.63 M334.27 144.82 C352.85 123.85, 373.24 101.61, 395.28 74.63 M339.25 145.18 C350.58 130.51, 366.65 115.79, 395.67 80.28 M339.25 145.18 C360.84 120.94, 381.57 97.17, 395.67 80.28 M344.9 144.79 C359.95 129.93, 374.97 110.51, 395.41 86.68 M344.9 144.79 C359.66 128.95, 373.84 112.99, 395.41 86.68 M349.88 145.15 C365.74 124.35, 384.39 105.92, 395.81 92.32 M349.88 145.15 C365.01 128.41, 378.61 111.44, 395.81 92.32 M355.53 144.75 C366.83 128.33, 381.85 115.58, 395.55 98.72 M355.53 144.75 C366.51 134.09, 376.57 121.06, 395.55 98.72 M360.51 145.11 C371.57 133.46, 379.69 124.34, 395.94 104.36 M360.51 145.11 C371.82 131.23, 384.33 117.41, 395.94 104.36 M364.19 146.98 C372.36 139.23, 375.92 131.75, 395.68 110.76 M364.19 146.98 C375.25 134.47, 383.71 124.14, 395.68 110.76 M370.49 145.83 C377.43 139.68, 383.18 129.21, 396.73 115.65 M370.49 145.83 C377.33 137.24, 382.58 130.01, 396.73 115.65 M377.44 143.93 C380.98 139.22, 385.26 134.38, 391.88 127.33 M377.44 143.93 C381.01 139.93, 385.77 134.2, 391.88 127.33" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C158.41 -0.65, 283.84 1.37, 362 0 M32 0 C99.31 -2.94, 165.01 -1.66, 362 0 M362 0 C382.37 -1.19, 392.06 11.27, 394 32 M362 0 C383.53 0.55, 395.84 12.5, 394 32 M394 32 C396.17 49.29, 394.95 72.1, 394 113 M394 32 C394.74 57.51, 393.79 80.56, 394 113 M394 113 C393.49 135.83, 381.87 143.9, 362 145 M394 113 C391.98 135.04, 383.6 143.19, 362 145 M362 145 C291.31 144.27, 221.15 142.78, 32 145 M362 145 C249.93 143.31, 138.97 142.96, 32 145 M32 145 C8.71 146.69, -1.86 135.95, 0 113 M32 145 C10.21 143.8, 1.65 132.18, 0 113 M0 113 C0.55 94.72, -1.35 75.26, 0 32 M0 113 C-0.88 90.36, -1.5 66.27, 0 32 M0 32 C1.9 12.2, 9.12 -1.48, 32 0 M0 32 C0.22 11.69, 10.1 1.96, 32 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(288.875 73.3828125) rotate(0 140.625 67.19999999999999)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories: []</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: .keep</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;empty-blob-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g stroke-linecap="round" transform="translate(279.59193843887 271.159696266393) rotate(0 198.99999999999994 130)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C7.06 13.81, 9.24 9.34, 18.1 2.48 M3.66 19.09 C8.44 11.73, 13.76 5.27, 18.1 2.48 M2.75 26.24 C10 16.18, 18.61 6.13, 24.4 1.33 M2.75 26.24 C9.44 19.31, 14.57 12.4, 24.4 1.33 M2.48 32.64 C10.34 21.03, 17.74 12.3, 30.7 0.18 M2.48 32.64 C13.49 21.44, 21.83 9.48, 30.7 0.18 M2.88 38.28 C12.91 25, 26.86 13.76, 34.37 2.05 M2.88 38.28 C12.64 28.15, 21.63 16.63, 34.37 2.05 M2.62 44.68 C14.17 33.27, 22.89 18.23, 39.36 2.41 M2.62 44.68 C12.44 34.48, 21.24 23.55, 39.36 2.41 M2.36 51.07 C18.27 36.19, 31.27 19.8, 45 2.02 M2.36 51.07 C15.34 37.29, 27.12 23.41, 45 2.02 M2.75 56.72 C18.22 37.56, 35.93 17.94, 49.99 2.38 M2.75 56.72 C20.57 34.56, 37.8 14.24, 49.99 2.38 M2.49 63.11 C18.51 41.78, 36.66 23.69, 55.63 1.98 M2.49 63.11 C16.06 46.84, 30.13 32.58, 55.63 1.98 M2.23 69.51 C14.08 54.4, 27.81 42.3, 60.62 2.34 M2.23 69.51 C14.8 54.54, 28.11 38.47, 60.62 2.34 M2.62 75.16 C23.22 53.14, 45.11 28.68, 66.26 1.95 M2.62 75.16 C18.68 55.49, 35.76 36.87, 66.26 1.95 M2.36 81.55 C29.49 51.06, 52.67 20.07, 71.25 2.31 M2.36 81.55 C23.35 59.17, 43.86 35.46, 71.25 2.31 M2.76 87.2 C29.08 59.14, 54.26 30.38, 76.89 1.91 M2.76 87.2 C25.2 61.93, 47.39 35.45, 76.89 1.91 M2.49 93.59 C22.97 73.16, 41.24 50.49, 81.88 2.27 M2.49 93.59 C22.85 70.13, 41.2 48.51, 81.88 2.27 M2.23 99.99 C26.73 70.85, 53 42.34, 87.52 1.88 M2.23 99.99 C26.11 70.65, 52.18 43.37, 87.52 1.88 M2.63 105.64 C29.25 73.43, 57.28 44.11, 92.51 2.24 M2.63 105.64 C22.21 84.02, 40.09 61.9, 92.51 2.24 M2.37 112.03 C24.78 84.92, 46.32 60.61, 98.15 1.85 M2.37 112.03 C40.71 68.73, 77.43 25.35, 98.15 1.85 M2.1 118.43 C32.45 81.76, 64.34 45.79, 103.14 2.21 M2.1 118.43 C26.04 90.13, 50.16 63.23, 103.14 2.21 M2.5 124.07 C34.35 88.85, 66.58 53.31, 108.78 1.81 M2.5 124.07 C35.8 86.3, 67.38 48.42, 108.78 1.81 M2.24 130.47 C44.13 79.64, 85.07 32.68, 113.77 2.17 M2.24 130.47 C28.26 100.65, 54.41 68.41, 113.77 2.17 M2.63 136.11 C39.83 96.08, 73.86 52.5, 119.41 1.78 M2.63 136.11 C27.92 105.97, 55.2 76.2, 119.41 1.78 M2.37 142.51 C38.64 103.62, 72.54 62.27, 124.4 2.14 M2.37 142.51 C38.27 104.7, 71.42 64.68, 124.4 2.14 M2.11 148.91 C31.62 115.14, 60.21 82.46, 130.04 1.74 M2.11 148.91 C30.64 115.22, 60.37 82.49, 130.04 1.74 M2.5 154.55 C45.43 105.72, 87.93 57.79, 135.03 2.1 M2.5 154.55 C39.77 111.59, 78.21 68.92, 135.03 2.1 M2.24 160.95 C40.01 120.72, 75.93 77.72, 140.67 1.71 M2.24 160.95 C44.66 112.93, 87.03 64.56, 140.67 1.71 M1.98 167.35 C39.14 124.53, 74.75 81.57, 145.66 2.07 M1.98 167.35 C47.85 117.91, 91.41 66.21, 145.66 2.07 M2.38 172.99 C58.38 106.95, 115.31 43.43, 150.65 2.43 M2.38 172.99 C53.11 115.34, 104.81 56.32, 150.65 2.43 M2.11 179.39 C64.27 108.42, 124.58 39.52, 156.29 2.03 M2.11 179.39 C42.26 131.15, 83.61 83.43, 156.29 2.03 M2.51 185.03 C62.42 116.85, 122.68 43.77, 161.28 2.39 M2.51 185.03 C56.66 122.43, 112.32 58.31, 161.28 2.39 M2.25 191.43 C51.58 133.03, 102.03 75.72, 166.92 2 M2.25 191.43 C38.3 149.45, 75.45 107.32, 166.92 2 M1.99 197.83 C42.68 152.88, 80.66 108.12, 171.91 2.36 M1.99 197.83 C66.15 123.85, 132.83 47.33, 171.91 2.36 M2.38 203.47 C55.91 143.98, 107.7 83.76, 177.55 1.96 M2.38 203.47 C56.81 142.73, 110.46 81.58, 177.55 1.96 M2.12 209.87 C66.55 137, 131.82 62.14, 182.54 2.32 M2.12 209.87 C50.61 157.03, 98.73 101.44, 182.54 2.32 M1.86 216.27 C63.05 142.96, 126.25 69.69, 188.18 1.93 M1.86 216.27 C65.44 140.95, 131.03 66.87, 188.18 1.93 M2.25 221.91 C59.05 161.57, 111.38 97.22, 193.17 2.29 M2.25 221.91 C52.49 164.23, 101.5 106.36, 193.17 2.29 M1.33 229.06 C64.26 156.57, 124.34 84.79, 198.81 1.9 M1.33 229.06 C60.97 162.1, 120.98 94.62, 198.81 1.9 M1.73 234.71 C54.24 174.7, 106.54 116.97, 203.8 2.26 M1.73 234.71 C76.5 149.79, 152.59 63.83, 203.8 2.26 M2.78 239.59 C49.77 186.82, 94.95 134.94, 209.44 1.86 M2.78 239.59 C83.35 146.44, 163.63 53.44, 209.44 1.86 M4.49 243.73 C48.55 190.47, 93.86 139.53, 214.43 2.22 M4.49 243.73 C63.53 174.61, 122.23 106.2, 214.43 2.22 M6.19 247.86 C77.48 165.73, 149.04 81.52, 220.07 1.83 M6.19 247.86 C85.41 157.55, 164.08 66.86, 220.07 1.83 M8.56 251.24 C90.22 155.68, 175.16 56.51, 225.06 2.19 M8.56 251.24 C89.12 157.29, 172.43 62.13, 225.06 2.19 M10.92 254.62 C69.41 191.54, 124.13 126, 230.7 1.79 M10.92 254.62 C69.41 187.27, 127.03 120.42, 230.7 1.79 M13.94 257.24 C82.45 176.73, 153.61 97.39, 235.69 2.15 M13.94 257.24 C93.45 166.82, 173.91 75.69, 235.69 2.15 M18.27 258.36 C76.98 194.1, 131.53 129.14, 241.33 1.76 M18.27 258.36 C66.67 201.61, 115.46 145.37, 241.33 1.76 M22.6 259.47 C75.95 201, 128.47 140.06, 246.32 2.12 M22.6 259.47 C70.35 207.82, 116.17 155.22, 246.32 2.12 M26.27 261.34 C95.93 180.59, 169.95 95.39, 251.96 1.72 M26.27 261.34 C89.37 190.53, 152.1 118.22, 251.96 1.72 M32.57 260.19 C96.06 189.84, 155.98 120.97, 256.95 2.08 M32.57 260.19 C78.18 205.29, 124.7 151.48, 256.95 2.08 M37.56 260.55 C117 171.22, 192.42 83.99, 262.59 1.69 M37.56 260.55 C121.96 163.6, 206.2 67.19, 262.59 1.69 M43.2 260.16 C105.28 189.09, 169.45 117.92, 267.58 2.05 M43.2 260.16 C123.84 168.01, 203.45 76.43, 267.58 2.05 M48.19 260.52 C117.81 178.99, 186.9 100.6, 272.56 2.41 M48.19 260.52 C125.53 171.84, 202.4 82.45, 272.56 2.41 M53.83 260.12 C138.08 162.28, 222.76 67.81, 278.21 2.01 M53.83 260.12 C128.55 171.85, 204.35 84.41, 278.21 2.01 M58.82 260.48 C127.06 184.42, 193 106.58, 283.19 2.37 M58.82 260.48 C135.91 173.81, 211.1 87.23, 283.19 2.37 M64.46 260.09 C109.71 206.88, 156.75 154.77, 288.84 1.98 M64.46 260.09 C142.84 169.53, 220.7 80.3, 288.84 1.98 M69.45 260.45 C138.09 183.73, 208 105.35, 293.82 2.34 M69.45 260.45 C156.6 162.77, 241.99 64.65, 293.82 2.34 M75.09 260.06 C143.01 182.6, 211.21 103.49, 299.47 1.94 M75.09 260.06 C136 192, 195.87 123.37, 299.47 1.94 M80.08 260.42 C126.17 206.79, 172.95 151.57, 304.45 2.3 M80.08 260.42 C129.25 204.69, 179.83 146.37, 304.45 2.3 M85.72 260.02 C157.76 178.28, 226.22 97.47, 310.1 1.91 M85.72 260.02 C149.05 184.68, 213.67 111.24, 310.1 1.91 M90.71 260.38 C181.48 157.9, 269.25 55.91, 315.08 2.27 M90.71 260.38 C157.59 183.34, 224.55 106.5, 315.08 2.27 M95.7 260.74 C174.5 168.64, 255.03 76.7, 320.73 1.88 M95.7 260.74 C164.52 183.36, 232.23 105.12, 320.73 1.88 M101.34 260.35 C181.6 167.21, 261.15 73.9, 325.71 2.24 M101.34 260.35 C177.77 172.29, 255.39 82.95, 325.71 2.24 M106.33 260.71 C168.93 189.98, 230.06 115.81, 331.36 1.84 M106.33 260.71 C170.53 184.03, 237.17 107.92, 331.36 1.84 M111.97 260.31 C176.35 190.63, 236.85 119.43, 336.34 2.2 M111.97 260.31 C157.57 208.21, 204.38 154.93, 336.34 2.2 M116.96 260.67 C164.3 205.68, 212.63 148.72, 341.99 1.81 M116.96 260.67 C197.94 169.46, 278.58 77.5, 341.99 1.81 M122.6 260.28 C175.27 200.79, 229.84 138.31, 346.97 2.17 M122.6 260.28 C172.5 202.83, 220.76 146.35, 346.97 2.17 M127.59 260.64 C215.15 158.17, 304.42 54.77, 352.62 1.77 M127.59 260.64 C188.96 189.82, 250.22 118.28, 352.62 1.77 M133.23 260.24 C183.32 201.93, 235.94 141.72, 357.6 2.13 M133.23 260.24 C184.28 203.09, 233.15 145.34, 357.6 2.13 M138.22 260.6 C220.44 165.6, 301.95 72.93, 363.25 1.74 M138.22 260.6 C215.16 172.58, 291.69 83.91, 363.25 1.74 M143.86 260.21 C216.13 172.96, 292.05 87.04, 368.23 2.1 M143.86 260.21 C223.54 169.38, 302.36 80.03, 368.23 2.1 M148.85 260.57 C228.87 169.81, 309.22 79.04, 374.53 0.95 M148.85 260.57 C225.85 169.3, 304.04 78.63, 374.53 0.95 M154.49 260.17 C230.89 172.65, 306.45 86.57, 378.21 2.82 M154.49 260.17 C210.94 195.8, 265.6 130.92, 378.21 2.82 M159.48 260.53 C223.94 184.22, 290.75 110.57, 382.54 3.93 M159.48 260.53 C244.05 163.85, 329.27 67.22, 382.54 3.93 M165.12 260.14 C236.95 176.83, 309.56 92.2, 386.87 5.05 M165.12 260.14 C221.23 195.95, 279.16 130.62, 386.87 5.05 M170.11 260.5 C221.21 201.49, 273.24 142.28, 389.23 8.43 M170.11 260.5 C218.37 205.87, 266.44 150.41, 389.23 8.43 M175.75 260.11 C241.58 187.82, 304.45 113.61, 392.25 11.05 M175.75 260.11 C222.69 207.98, 267.39 155.95, 392.25 11.05 M180.74 260.47 C245.33 188.58, 307.54 115.78, 395.27 13.68 M180.74 260.47 C261.6 168.2, 340.34 76.88, 395.27 13.68 M186.38 260.07 C251.57 186.65, 319.28 107.6, 396.32 18.56 M186.38 260.07 C236.65 200.09, 289.45 140.81, 396.32 18.56 M191.37 260.43 C255.8 183.29, 323.42 107.08, 397.37 23.45 M191.37 260.43 C233.86 209.7, 277.09 160.88, 397.37 23.45 M197.01 260.04 C273.9 173.76, 350.14 83.39, 397.77 29.09 M197.01 260.04 C238.49 210.28, 280.91 161.3, 397.77 29.09 M202 260.4 C249.95 204.58, 301.21 145.35, 399.47 33.23 M202 260.4 C246.27 207.11, 292.1 154.9, 399.47 33.23 M207.64 260 C282.04 175.37, 353.03 93.88, 399.21 39.63 M207.64 260 C252.23 206.89, 298.22 154.54, 399.21 39.63 M212.63 260.36 C270.79 190.87, 332.59 125.04, 399.61 45.27 M212.63 260.36 C255.76 212.2, 297.66 164.88, 399.61 45.27 M217.62 260.72 C270.87 200.01, 324.39 137.32, 399.34 51.67 M217.62 260.72 C286.2 182.97, 352.23 105.9, 399.34 51.67 M223.26 260.33 C266.88 209.47, 311.98 157.05, 399.74 57.31 M223.26 260.33 C270.48 203.33, 319.36 147.73, 399.74 57.31 M228.25 260.69 C264.48 217.85, 298.81 176.68, 399.48 63.71 M228.25 260.69 C287.06 192.75, 345.09 124.31, 399.48 63.71 M233.89 260.29 C283.82 202.81, 333.35 147.36, 399.22 70.11 M233.89 260.29 C296.32 189.36, 357.95 117.97, 399.22 70.11 M238.88 260.65 C288.24 201.51, 340.4 144.75, 399.61 75.75 M238.88 260.65 C302.98 187.65, 365.83 115.51, 399.61 75.75 M244.52 260.26 C301.41 192.57, 362.82 123.97, 399.35 82.15 M244.52 260.26 C276.87 221.94, 309.71 183.55, 399.35 82.15 M249.51 260.62 C294.86 210.74, 338.19 159.76, 399.74 87.79 M249.51 260.62 C288.68 213.68, 329.48 168.33, 399.74 87.79 M255.15 260.22 C306.36 203.12, 357.73 144.26, 399.48 94.19 M255.15 260.22 C310.1 197.2, 364.53 133.07, 399.48 94.19 M260.14 260.58 C306.6 208.36, 353.31 154.47, 399.22 100.59 M260.14 260.58 C314.84 198.11, 370.53 134.35, 399.22 100.59 M265.78 260.19 C310.07 209.18, 353.85 155.07, 399.62 106.23 M265.78 260.19 C313.52 203.42, 361.67 148.41, 399.62 106.23 M270.77 260.55 C312.94 210.71, 350.92 165.03, 399.35 112.63 M270.77 260.55 C313.74 212.09, 354.22 165.25, 399.35 112.63 M276.41 260.15 C322.47 205.94, 367.79 155.3, 399.75 118.27 M276.41 260.15 C319.24 210.97, 362.85 162.61, 399.75 118.27 M281.4 260.51 C309.82 228.04, 337.57 195, 399.49 124.67 M281.4 260.51 C315.37 218.59, 351.47 180.05, 399.49 124.67 M287.04 260.12 C329.88 210.1, 376.19 158.03, 399.23 131.06 M287.04 260.12 C314.45 229.24, 339.01 200.91, 399.23 131.06 M292.03 260.48 C332.66 211.87, 372.61 167.01, 399.62 136.71 M292.03 260.48 C317.01 231.17, 342.37 202.7, 399.62 136.71 M297.67 260.09 C320.79 233.82, 342.65 206.24, 399.36 143.11 M297.67 260.09 C337.01 212.55, 377.78 167.34, 399.36 143.11 M302.66 260.45 C335.5 224.45, 365.99 190.46, 399.75 148.75 M302.66 260.45 C341.03 214.95, 379.75 172.24, 399.75 148.75 M308.3 260.05 C331.34 234.81, 350.27 209.28, 399.49 155.15 M308.3 260.05 C337.96 226.28, 369 190.93, 399.49 155.15 M313.29 260.41 C344.16 226.42, 373.26 191.65, 399.23 161.54 M313.29 260.41 C340.87 229.56, 366.67 199.76, 399.23 161.54 M318.93 260.02 C339.08 235.8, 357.34 215.88, 399.63 167.19 M318.93 260.02 C338.18 238.19, 356.44 217.12, 399.63 167.19 M323.92 260.38 C343.38 240.84, 359.55 216.05, 399.36 173.59 M323.92 260.38 C339.58 242.35, 357.05 223.74, 399.36 173.59 M328.9 260.74 C356.6 230.93, 381.38 201.75, 399.76 179.23 M328.9 260.74 C355.35 229.48, 382.07 199.6, 399.76 179.23 M334.55 260.34 C353.55 235.84, 374.53 215.15, 399.5 185.63 M334.55 260.34 C350.96 240.58, 366.19 222.61, 399.5 185.63 M339.53 260.7 C355.36 243.43, 370.27 221.73, 399.24 192.02 M339.53 260.7 C361.66 234.82, 383.39 210.67, 399.24 192.02 M345.18 260.31 C362.54 238.6, 380.33 220.43, 399.63 197.67 M345.18 260.31 C359.44 243.81, 374.08 228.32, 399.63 197.67 M350.16 260.67 C364.5 243.76, 376.71 230.97, 399.37 204.06 M350.16 260.67 C363.93 245.32, 377.91 229.24, 399.37 204.06 M355.81 260.27 C372.91 242.23, 387.92 223.73, 399.76 209.71 M355.81 260.27 C373.85 240.57, 389.06 220.82, 399.76 209.71 M360.79 260.63 C368.09 252.39, 377.96 242.27, 399.5 216.11 M360.79 260.63 C372.53 247.08, 385.83 232.19, 399.5 216.11 M366.44 260.24 C372.92 252.64, 384.19 241.59, 399.24 222.5 M366.44 260.24 C374.31 251.44, 384.03 240.28, 399.24 222.5 M372.74 259.09 C379.45 251.45, 383.63 244.65, 398.98 228.9 M372.74 259.09 C383.24 247.41, 391.87 236.99, 398.98 228.9 M378.38 258.69 C382.28 255.84, 387.86 247.21, 398.06 236.05 M378.38 258.69 C382.66 254.99, 386.79 248.97, 398.06 236.05 M385.99 256.04 C389.48 251.84, 392.18 249.55, 394.52 246.23 M385.99 256.04 C389.44 252.25, 392.29 247.67, 394.52 246.23" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C123.14 1.36, 217.94 1.27, 366 0 M32 0 C131.29 -1.79, 231.25 -1.6, 366 0 M366 0 C387.8 0.82, 396.14 9, 398 32 M366 0 C388.4 -1.28, 396.3 11.05, 398 32 M398 32 C396.68 77, 397.3 120.08, 398 228 M398 32 C397.91 99.78, 396.94 166.68, 398 228 M398 228 C399.84 247.36, 385.48 260.38, 366 260 M398 228 C399.48 250.74, 389.16 259.77, 366 260 M366 260 C278.41 260.83, 191.33 259.2, 32 260 M366 260 C254.85 261.48, 142.72 261.51, 32 260 M32 260 C9.06 261.46, -1.27 247.64, 0 228 M32 260 C8.96 260.25, 0.79 249.13, 0 228 M0 228 C-2.62 187.23, -0.98 150.02, 0 32 M0 228 C-1.12 169.92, -1.74 111.79, 0 32 M0 32 C-0.23 11.83, 11.85 0.53, 32 0 M0 32 C1.74 9.13, 11.06 1.81, 32 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(284.59193843887 276.159696266393) rotate(0 182.81249999999994 124.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: keep</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;directory-with-keep-digest&gt;</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 1</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files:</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: .keep</text><text x="0" y="134.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;empty-blob-digest&gt;</text><text x="0" y="153.6" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="172.79999999999998" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="192" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks:</text><text x="0" y="211.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: aa</text><text x="0" y="230.39999999999998" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   target: /nix/store/somewhereelse</text></g><g stroke-linecap="round" transform="translate(292.58984375 581.578125) rotate(0 192 36.5)"><path d="M4.86 4.22 C4.86 4.22, 4.86 4.22, 4.86 4.22 M4.86 4.22 C4.86 4.22, 4.86 4.22, 4.86 4.22 M3.28 12.13 C5.02 8.82, 8.25 4.62, 11.81 2.32 M3.28 12.13 C6.19 8.3, 9.57 5.6, 11.81 2.32 M1.71 20.04 C8.34 16.13, 10.93 7.61, 18.77 0.41 M1.71 20.04 C6.51 15.46, 9.85 9.62, 18.77 0.41 M2.11 25.68 C10.79 16.53, 14.3 8.85, 23.76 0.77 M2.11 25.68 C8.12 18.52, 15.95 11.63, 23.76 0.77 M1.84 32.08 C8.84 22.24, 16.07 17.29, 29.4 0.38 M1.84 32.08 C11.36 20.47, 23.16 9.07, 29.4 0.38 M2.24 37.72 C9.27 29.77, 15.4 22.85, 34.39 0.74 M2.24 37.72 C15.42 23.1, 27.12 8.74, 34.39 0.74 M2.63 43.36 C8.76 33.58, 17 25.79, 40.03 0.35 M2.63 43.36 C13.14 31.06, 23.17 18.26, 40.03 0.35 M2.37 49.76 C16.36 34.07, 29.4 16.32, 45.02 0.71 M2.37 49.76 C17.93 32.43, 33.93 13.74, 45.02 0.71 M2.77 55.41 C19.05 35.42, 38.7 10.68, 50.66 0.31 M2.77 55.41 C19.96 34.91, 39.43 13.51, 50.66 0.31 M1.19 63.31 C17.21 49, 29.42 31.12, 55.65 0.67 M1.19 63.31 C15.9 46.95, 29.88 30.05, 55.65 0.67 M4.21 65.94 C21.41 44.81, 41.02 26.02, 61.29 0.28 M4.21 65.94 C24.28 42.91, 45.96 20.08, 61.29 0.28 M6.57 69.32 C23.78 51.2, 36.27 33.53, 66.28 0.64 M6.57 69.32 C19.35 53.63, 31.93 38.99, 66.28 0.64 M9.59 71.94 C25.17 56.48, 40.49 38.06, 71.92 0.24 M9.59 71.94 C23.84 58.2, 36.53 43.78, 71.92 0.24 M13.92 73.05 C31.98 52.05, 54.85 25.68, 76.91 0.6 M13.92 73.05 C30.95 53.92, 48.62 33.36, 76.91 0.6 M19.57 72.66 C38.35 51.77, 53.43 34.49, 82.55 0.21 M19.57 72.66 C32.02 56.9, 44.97 41.55, 82.55 0.21 M24.55 73.02 C48.11 46.73, 66.96 24.34, 87.54 0.57 M24.55 73.02 C47.62 46.37, 71.59 19.54, 87.54 0.57 M30.2 72.63 C46.25 51.67, 67.22 31.55, 93.18 0.17 M30.2 72.63 C53.38 46.94, 75.41 21.61, 93.18 0.17 M35.18 72.99 C55.38 48.59, 74.75 27.94, 98.17 0.53 M35.18 72.99 C57.1 47.59, 77.7 22.46, 98.17 0.53 M40.83 72.59 C64.45 43.72, 89.12 19.76, 103.81 0.14 M40.83 72.59 C61.26 47.84, 82.21 23.37, 103.81 0.14 M45.81 72.95 C66.24 52.79, 83.08 30.43, 108.8 0.5 M45.81 72.95 C67.8 48.81, 88.58 24.77, 108.8 0.5 M51.46 72.56 C64.09 57.9, 78.25 44.63, 113.78 0.86 M51.46 72.56 C72.83 47.42, 94.74 22.72, 113.78 0.86 M56.44 72.92 C74.65 52.58, 96.04 31.05, 119.43 0.46 M56.44 72.92 C81.73 45.16, 105.23 18.3, 119.43 0.46 M62.09 72.52 C80.07 51.93, 98.45 29.82, 124.41 0.82 M62.09 72.52 C79.04 54.05, 95.96 34.96, 124.41 0.82 M67.07 72.88 C80.12 59.08, 92.94 42.21, 130.06 0.43 M67.07 72.88 C80.66 58.14, 95.45 40.91, 130.06 0.43 M72.06 73.24 C93.79 49.66, 111.38 26.32, 135.04 0.79 M72.06 73.24 C89.58 51.72, 107.82 32.09, 135.04 0.79 M77.7 72.85 C104.72 44.35, 127.17 17, 140.69 0.4 M77.7 72.85 C96.13 51.38, 114.98 29.95, 140.69 0.4 M82.69 73.21 C104.71 47.57, 127.59 22.31, 145.67 0.76 M82.69 73.21 C101.71 51.47, 120.77 29.01, 145.67 0.76 M88.33 72.81 C110.76 47.24, 131.64 20.5, 151.32 0.36 M88.33 72.81 C109 48.14, 130.72 23.09, 151.32 0.36 M93.32 73.17 C111.15 55.01, 126.29 31.78, 156.3 0.72 M93.32 73.17 C110.49 51.39, 129.9 29.91, 156.3 0.72 M98.96 72.78 C118.26 54.28, 133.1 34.79, 161.95 0.33 M98.96 72.78 C111.06 59.03, 124.81 44.07, 161.95 0.33 M103.95 73.14 C115.72 58.92, 129.64 40.85, 166.93 0.69 M103.95 73.14 C126.22 47.2, 149.58 21.56, 166.93 0.69 M109.59 72.74 C123.31 56.85, 140.06 38.01, 172.58 0.29 M109.59 72.74 C124.38 56.56, 137.2 40.37, 172.58 0.29 M114.58 73.1 C138.07 45.66, 163.14 15.75, 177.56 0.65 M114.58 73.1 C131.86 52.94, 147.99 32.77, 177.56 0.65 M120.22 72.71 C133.34 57.46, 150.05 38.7, 183.21 0.26 M120.22 72.71 C135.3 57.12, 148.59 39.51, 183.21 0.26 M125.21 73.07 C147.35 45.64, 169.73 21.54, 188.19 0.62 M125.21 73.07 C146.83 48.56, 167.28 24.07, 188.19 0.62 M130.85 72.68 C149.9 47.27, 173.04 22.78, 193.84 0.22 M130.85 72.68 C153.31 46.45, 175.71 22.68, 193.84 0.22 M135.84 73.04 C157.75 47.97, 181.46 23.22, 198.82 0.58 M135.84 73.04 C157.01 47.37, 178.52 21.52, 198.82 0.58 M141.49 72.64 C163.6 47.54, 184.92 24.28, 203.81 0.94 M141.49 72.64 C157.97 55.11, 171.75 36.25, 203.81 0.94 M146.47 73 C164.74 50.07, 184.75 31.86, 209.45 0.55 M146.47 73 C169.77 45.07, 195.2 17.9, 209.45 0.55 M152.12 72.61 C170.98 50.38, 191.11 25.57, 214.44 0.91 M152.12 72.61 C167.16 54.48, 184.74 36.2, 214.44 0.91 M157.1 72.97 C171.7 55.53, 188.24 37.45, 220.08 0.51 M157.1 72.97 C171.53 57.7, 184.96 42.02, 220.08 0.51 M162.09 73.33 C182.79 52.34, 200.46 29.24, 225.07 0.87 M162.09 73.33 C176.54 57.94, 188.74 43.02, 225.07 0.87 M167.73 72.93 C187.78 52.57, 204.67 31.34, 230.71 0.48 M167.73 72.93 C192.31 45.48, 215.11 18.16, 230.71 0.48 M172.72 73.29 C192.03 53.03, 213.93 26.16, 235.7 0.84 M172.72 73.29 C187.14 54.62, 203.69 37.67, 235.7 0.84 M178.36 72.9 C196.89 49.98, 218.6 27.24, 241.34 0.45 M178.36 72.9 C191.33 57.21, 204.9 42.89, 241.34 0.45 M183.35 73.26 C207.11 47.48, 230.29 17.13, 246.33 0.81 M183.35 73.26 C195.69 56.95, 208.85 41.55, 246.33 0.81 M188.99 72.86 C203.94 55.74, 221.89 34.74, 251.97 0.41 M188.99 72.86 C203.18 55.08, 218.22 38.46, 251.97 0.41 M193.98 73.22 C219.55 44.07, 240.87 19.79, 256.96 0.77 M193.98 73.22 C208.25 55.94, 223.54 39.09, 256.96 0.77 M199.62 72.83 C217.99 48.42, 241.49 28.33, 262.6 0.38 M199.62 72.83 C214.61 56.41, 229.03 40.98, 262.6 0.38 M204.61 73.19 C223.33 53.31, 241.74 30.68, 267.59 0.74 M204.61 73.19 C229.17 46.22, 251.07 19.59, 267.59 0.74 M210.25 72.79 C224.49 54.46, 241.18 34.58, 273.23 0.34 M210.25 72.79 C227.21 52.37, 245.03 32.68, 273.23 0.34 M215.24 73.15 C229.43 57.1, 240.31 42.47, 278.22 0.7 M215.24 73.15 C237.32 48.52, 258.37 22.2, 278.22 0.7 M220.88 72.76 C240.5 49.7, 259.92 29.24, 283.86 0.31 M220.88 72.76 C245.23 46.31, 268.29 19.17, 283.86 0.31 M225.87 73.12 C244.42 48.72, 265.88 27.95, 288.85 0.67 M225.87 73.12 C251.66 44.25, 275.97 16.49, 288.85 0.67 M231.51 72.72 C253.24 45.13, 279.63 17.21, 293.84 1.03 M231.51 72.72 C244.39 57.29, 257.3 41.73, 293.84 1.03 M236.5 73.08 C256.68 52.52, 274.01 31.26, 299.48 0.63 M236.5 73.08 C252.49 52.88, 270.22 34.34, 299.48 0.63 M242.14 72.69 C264.56 48.95, 287.34 23.02, 304.47 0.99 M242.14 72.69 C265.99 45.72, 289.13 17.36, 304.47 0.99 M247.13 73.05 C268.76 49.79, 290 25.19, 310.11 0.6 M247.13 73.05 C271.93 45.31, 297.4 16.35, 310.11 0.6 M252.77 72.66 C272.81 49.72, 292.69 22.65, 315.1 0.96 M252.77 72.66 C274.82 46.14, 296.99 21.06, 315.1 0.96 M257.76 73.02 C279.06 47.65, 296.04 26.13, 320.74 0.56 M257.76 73.02 C279.13 48.78, 298.14 26.64, 320.74 0.56 M262.75 73.38 C286.03 44.74, 309.2 19.59, 325.73 0.92 M262.75 73.38 C284.29 48.03, 306.79 23.96, 325.73 0.92 M268.39 72.98 C284.12 55.13, 298.79 37.14, 331.37 0.53 M268.39 72.98 C286.16 50.17, 306.05 30.27, 331.37 0.53 M273.38 73.34 C296.56 45.22, 323.75 15.16, 336.36 0.89 M273.38 73.34 C289.13 55.59, 302.31 40.33, 336.36 0.89 M279.02 72.95 C303.53 43.87, 326.85 18.13, 342 0.49 M279.02 72.95 C293.37 55.49, 308.15 39.18, 342 0.49 M284.01 73.31 C298.42 57.71, 311.23 40.55, 346.99 0.85 M284.01 73.31 C308.02 43.63, 333.49 16.03, 346.99 0.85 M289.65 72.91 C311.51 49.12, 331.37 27.36, 352.63 0.46 M289.65 72.91 C314.28 43.1, 339.52 15.91, 352.63 0.46 M294.64 73.27 C311.15 55.94, 323.35 38.14, 357.62 0.82 M294.64 73.27 C315.12 50.08, 336.79 25.51, 357.62 0.82 M300.28 72.88 C323.32 47.78, 344.6 22, 363.26 0.43 M300.28 72.88 C320.59 50.02, 339.32 28.35, 363.26 0.43 M305.27 73.24 C321.19 53.97, 335.23 38.9, 368.25 0.79 M305.27 73.24 C320.38 55.99, 334.56 39.62, 368.25 0.79 M310.91 72.84 C326.89 57.23, 339.69 36.32, 372.58 1.9 M310.91 72.84 C323.53 58.18, 338.06 42.96, 372.58 1.9 M315.9 73.2 C339.86 47.6, 361.01 22.6, 376.91 3.02 M315.9 73.2 C338.69 46.17, 361.68 20.58, 376.91 3.02 M321.54 72.81 C338.51 50.61, 357.46 32.27, 379.93 5.64 M321.54 72.81 C336.35 54.99, 349.93 38.93, 379.93 5.64 M326.53 73.17 C341.29 57.14, 355.14 36.64, 382.29 9.02 M326.53 73.17 C347.2 48.97, 367.48 26.48, 382.29 9.02 M332.17 72.77 C348.91 51.78, 366.05 34.35, 384.66 12.4 M332.17 72.77 C345.89 56.87, 360.02 41.97, 384.66 12.4 M337.16 73.13 C350.96 56.83, 362.64 44.64, 384.39 18.8 M337.16 73.13 C350.38 58.41, 363.8 42.97, 384.39 18.8 M342.8 72.74 C358.9 55.85, 372.92 38.47, 384.13 25.19 M342.8 72.74 C359.81 54.2, 374.02 35.6, 384.13 25.19 M347.79 73.1 C354.66 65.33, 364.11 55.68, 384.53 30.84 M347.79 73.1 C358.88 60.26, 371.55 46.08, 384.53 30.84 M352.77 73.46 C358.94 66.21, 369.89 55.52, 384.27 37.23 M352.77 73.46 C360.31 65.04, 369.69 54.26, 384.27 37.23 M358.42 73.07 C364.98 65.59, 369.02 58.96, 384 43.63 M358.42 73.07 C368.68 61.66, 377.08 51.51, 384 43.63 M363.4 73.43 C367.59 70.26, 373.45 61.3, 384.4 49.27 M363.4 73.43 C367.97 69.41, 372.38 63.07, 384.4 49.27 M367.74 74.54 C374.28 66.78, 379.58 61.94, 384.14 55.67 M367.74 74.54 C374.31 67.19, 380 58.66, 384.14 55.67 M374.69 72.64 C376.19 69.99, 382.01 66.54, 385.19 60.56 M374.69 72.64 C377.37 68.95, 380.91 65.58, 385.19 60.56" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M18.25 0 M18.25 0 C129.54 2.72, 242.28 1.14, 365.75 0 M18.25 0 C127.66 -1.41, 238.58 -1.21, 365.75 0 M365.75 0 C378.27 0.9, 382.95 5.36, 384 18.25 M365.75 0 C380.07 0.47, 383.93 4.97, 384 18.25 M384 18.25 C384.02 33.84, 384.87 48.45, 384 54.75 M384 18.25 C384.01 28.7, 384.22 39.53, 384 54.75 M384 54.75 C382.32 67.61, 378.7 73.98, 365.75 73 M384 54.75 C385.93 68.19, 378.42 74.36, 365.75 73 M365.75 73 C288.24 70.59, 213.1 72.01, 18.25 73 M365.75 73 C265.08 74.84, 163.23 75.38, 18.25 73 M18.25 73 C7.8 72.9, 1.58 68.54, 0 54.75 M18.25 73 C7.06 72.31, -0.46 65.42, 0 54.75 M0 54.75 C0.31 45.73, -1.55 34.03, 0 18.25 M0 54.75 C0.72 40.09, -0.05 27.49, 0 18.25 M0 18.25 C1.55 7.28, 6.06 -1.74, 18.25 0 M0 18.25 C1.91 4.6, 5.29 1.6, 18.25 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(297.58984375 586.578125) rotate(0 70.3125 28.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories: []</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files: []</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g stroke-linecap="round" transform="translate(297.01171875 700.19140625) rotate(0 192 63)"><path d="M4.68 12.02 C4.68 12.02, 4.68 12.02, 4.68 12.02 M4.68 12.02 C4.68 12.02, 4.68 12.02, 4.68 12.02 M1.14 22.19 C5.68 16.12, 10.34 11.35, 18.19 2.57 M1.14 22.19 C5.63 15.12, 11.63 10.54, 18.19 2.57 M2.84 26.32 C9.4 19.34, 14.38 14.47, 24.49 1.42 M2.84 26.32 C7.02 21.88, 12.43 16.24, 24.49 1.42 M3.24 31.97 C12.37 22.32, 23.31 9.97, 30.14 1.02 M3.24 31.97 C8.75 26.69, 14.07 19.43, 30.14 1.02 M2.98 38.36 C11.61 29.09, 22.34 16.73, 34.47 2.14 M2.98 38.36 C14.61 26.7, 24.62 13.57, 34.47 2.14 M2.72 44.76 C8.44 35.48, 20.15 28.58, 39.46 2.5 M2.72 44.76 C9.81 35.38, 16.68 27.81, 39.46 2.5 M3.11 50.4 C17.54 33.89, 29.94 20.31, 45.1 2.1 M3.11 50.4 C20.74 31.63, 36.06 13.37, 45.1 2.1 M2.85 56.8 C18.58 35.97, 39.38 14.61, 50.09 2.46 M2.85 56.8 C15.02 42.55, 25.62 29.03, 50.09 2.46 M3.24 62.45 C13.28 49.17, 26.13 38.7, 55.73 2.07 M3.24 62.45 C14.41 50.21, 23.72 38.66, 55.73 2.07 M2.98 68.84 C13.36 56.63, 24.49 40.18, 60.72 2.43 M2.98 68.84 C25.38 43.36, 46.78 18.82, 60.72 2.43 M2.72 75.24 C27.68 45.78, 49.87 21.46, 66.36 2.03 M2.72 75.24 C27.36 48.58, 50.99 21.41, 66.36 2.03 M3.12 80.88 C26.98 53.98, 51.79 26.74, 71.35 2.39 M3.12 80.88 C24.82 57.11, 46.21 30.7, 71.35 2.39 M2.85 87.28 C25.83 58.82, 49.6 32.81, 76.33 2.75 M2.85 87.28 C18.86 69.84, 32.85 53.27, 76.33 2.75 M3.25 92.92 C31.02 64.11, 55.21 32.05, 81.98 2.36 M3.25 92.92 C29.09 64.51, 54.01 35.08, 81.98 2.36 M0.36 102.34 C18.06 82.88, 34.35 63.19, 86.96 2.72 M0.36 102.34 C22.5 76.81, 45.99 50.02, 86.96 2.72 M2.07 106.47 C35.94 64.04, 71.16 24.59, 92.61 2.33 M2.07 106.47 C29.3 74.98, 57.21 42, 92.61 2.33 M3.78 110.61 C32.64 72.16, 65.08 38.52, 97.59 2.69 M3.78 110.61 C27.71 83.59, 50.59 56.73, 97.59 2.69 M5.48 114.74 C31.81 84.38, 61.24 51.3, 103.24 2.29 M5.48 114.74 C38.37 76.78, 69.65 40.61, 103.24 2.29 M7.85 118.12 C37.24 84.78, 67.86 51.75, 108.22 2.65 M7.85 118.12 C40.51 79.72, 74.23 40.65, 108.22 2.65 M10.86 120.75 C33.15 93.28, 59.27 65.67, 113.87 2.26 M10.86 120.75 C41.44 83.49, 74.67 47.06, 113.87 2.26 M14.54 122.62 C54.9 77.33, 93.45 31.72, 118.85 2.62 M14.54 122.62 C36.96 96.13, 58.59 69.9, 118.85 2.62 M18.21 124.48 C60.03 78.24, 98.36 27.73, 124.5 2.22 M18.21 124.48 C47.99 89.82, 78.58 52.73, 124.5 2.22 M23.2 124.84 C60.93 81.34, 96.9 37.41, 129.48 2.58 M23.2 124.84 C51.15 90.76, 80.7 57.55, 129.48 2.58 M27.53 125.96 C67.64 79.48, 108.95 32.16, 135.13 2.19 M27.53 125.96 C50.35 100, 70.45 76.16, 135.13 2.19 M32.52 126.32 C67.96 82.8, 105.29 42.95, 140.11 2.55 M32.52 126.32 C55.09 102.84, 75.65 76.61, 140.11 2.55 M37.51 126.68 C76.58 83.49, 111.39 39.46, 145.76 2.15 M37.51 126.68 C68.68 90.35, 99.78 54.4, 145.76 2.15 M43.15 126.29 C79.05 86.81, 113.32 46.01, 150.74 2.51 M43.15 126.29 C83.16 78.74, 122.47 32.63, 150.74 2.51 M48.14 126.65 C84.95 86.46, 121.01 43.96, 156.39 2.12 M48.14 126.65 C77.47 92.07, 106.15 57.86, 156.39 2.12 M53.12 127.01 C94.06 83.46, 132.13 35.83, 161.37 2.48 M53.12 127.01 C83.35 90.25, 115.88 54.9, 161.37 2.48 M58.77 126.61 C88.9 91.94, 120.62 55.5, 167.02 2.08 M58.77 126.61 C80.71 101.71, 103.08 74.94, 167.02 2.08 M63.75 126.97 C94.83 94.26, 121.35 59.65, 172 2.44 M63.75 126.97 C99.19 87.16, 134.08 46.33, 172 2.44 M69.4 126.58 C96.06 98.29, 123.08 66.58, 177.65 2.05 M69.4 126.58 C94.05 98.86, 118.43 72.82, 177.65 2.05 M74.38 126.94 C110.22 87.09, 146.32 44.12, 182.63 2.41 M74.38 126.94 C112.7 80.36, 152.52 34.24, 182.63 2.41 M80.03 126.54 C112.63 85.84, 146.64 47.21, 188.28 2.01 M80.03 126.54 C118.52 83.47, 154.62 39.47, 188.28 2.01 M85.01 126.9 C123.66 84.49, 160.64 41.03, 193.26 2.37 M85.01 126.9 C107.24 101.3, 128.84 75.24, 193.26 2.37 M90.66 126.51 C128.32 79.98, 168.35 34.11, 198.25 2.73 M90.66 126.51 C125.23 87.51, 159.47 48.47, 198.25 2.73 M95.64 126.87 C136.32 80.89, 176.13 32.73, 203.89 2.34 M95.64 126.87 C130.67 85.7, 167.03 45.03, 203.89 2.34 M101.29 126.47 C138.72 85.68, 174.7 42.24, 208.88 2.7 M101.29 126.47 C141.27 81.26, 181.48 32.8, 208.88 2.7 M106.27 126.83 C133.13 96.99, 159.87 69.21, 214.52 2.31 M106.27 126.83 C139.22 87.92, 173.99 50.04, 214.52 2.31 M111.92 126.44 C139.17 92.95, 170.03 64.03, 219.51 2.67 M111.92 126.44 C140.35 94.34, 169.24 63.52, 219.51 2.67 M116.9 126.8 C139.41 102.01, 161.49 74.65, 225.15 2.27 M116.9 126.8 C143.96 97.47, 169.6 65.58, 225.15 2.27 M122.55 126.4 C151.81 92.7, 184.46 60.16, 230.14 2.63 M122.55 126.4 C144.55 101.98, 167.71 77.15, 230.14 2.63 M127.53 126.76 C155.24 93.9, 184.87 59.39, 235.78 2.24 M127.53 126.76 C162.55 85.69, 197.14 44.32, 235.78 2.24 M133.18 126.37 C156.24 97.59, 181.25 74.22, 240.77 2.6 M133.18 126.37 C169.91 83.87, 205.04 43.06, 240.77 2.6 M138.16 126.73 C171.62 83.67, 210.01 43.26, 246.41 2.2 M138.16 126.73 C162.75 98.55, 185.88 71.86, 246.41 2.2 M143.81 126.33 C167.53 96.38, 195.44 68.77, 251.4 2.56 M143.81 126.33 C175.38 87.65, 208.52 52.45, 251.4 2.56 M148.79 126.69 C186.15 81.29, 226.32 36.57, 257.04 2.17 M148.79 126.69 C185.38 86.13, 221.92 44.72, 257.04 2.17 M154.44 126.3 C190.78 83.15, 229.34 36.03, 262.03 2.53 M154.44 126.3 C178.51 97.01, 203.83 70.69, 262.03 2.53 M159.42 126.66 C189.83 93.77, 217.8 63.16, 267.67 2.13 M159.42 126.66 C196.22 84.11, 234.53 42.17, 267.67 2.13 M165.07 126.27 C199.58 88.17, 233.46 45.58, 272.66 2.49 M165.07 126.27 C204.81 83.18, 243.36 38.31, 272.66 2.49 M170.05 126.63 C191.4 102.17, 214.65 75.55, 278.3 2.1 M170.05 126.63 C194.79 98.87, 219.21 68.85, 278.3 2.1 M175.04 126.99 C218.62 76.89, 259.45 31.99, 283.29 2.46 M175.04 126.99 C200.78 100.7, 225.33 71.6, 283.29 2.46 M180.68 126.59 C214.02 88.6, 246.67 51.74, 288.93 2.06 M180.68 126.59 C212.29 91.19, 241.62 56.43, 288.93 2.06 M185.67 126.95 C215.71 95.07, 243.49 60.99, 293.92 2.42 M185.67 126.95 C216.01 94.84, 243.45 62.69, 293.92 2.42 M191.31 126.56 C229.63 85.76, 263.97 41.71, 299.56 2.03 M191.31 126.56 C222.59 88.19, 255.96 51.19, 299.56 2.03 M196.3 126.92 C217.19 100.03, 243 74.87, 304.55 2.39 M196.3 126.92 C218.88 99.48, 243.28 72.58, 304.55 2.39 M201.94 126.52 C245.64 78.21, 284.33 30.24, 309.54 2.75 M201.94 126.52 C240.53 84.15, 277.64 41.48, 309.54 2.75 M206.93 126.88 C248.75 80.42, 287.71 35.97, 315.18 2.36 M206.93 126.88 C240.59 87.82, 273.05 49.59, 315.18 2.36 M212.57 126.49 C242.06 93.98, 270.55 61.9, 320.17 2.72 M212.57 126.49 C238.53 99.42, 261.94 72.17, 320.17 2.72 M217.56 126.85 C255.21 80.1, 296.4 36.1, 325.81 2.32 M217.56 126.85 C248.46 91.27, 280.97 56.5, 325.81 2.32 M223.2 126.45 C245.32 96.89, 272.92 68.02, 330.8 2.68 M223.2 126.45 C246.93 99.22, 269.26 73.84, 330.8 2.68 M228.19 126.81 C266.27 81.56, 302.8 39.44, 336.44 2.29 M228.19 126.81 C268.68 80.87, 310.31 31.49, 336.44 2.29 M233.83 126.42 C261.99 92.9, 293.86 58.89, 341.43 2.65 M233.83 126.42 C265.72 90.09, 297.62 53.24, 341.43 2.65 M238.82 126.78 C276.56 81.01, 314.76 40.31, 347.07 2.25 M238.82 126.78 C275.07 83.85, 309.78 43.16, 347.07 2.25 M244.47 126.38 C281.08 86.31, 311.83 47.08, 353.37 1.1 M244.47 126.38 C284.42 79.98, 324.2 34.41, 353.37 1.1 M249.45 126.74 C283.83 89.61, 316.66 48.25, 358.36 1.46 M249.45 126.74 C288.06 81.49, 329.09 35.62, 358.36 1.46 M255.1 126.35 C286.95 88.5, 321.78 49.74, 362.69 2.58 M255.1 126.35 C278.54 101.51, 299.57 76.6, 362.69 2.58 M260.08 126.71 C289.9 92.16, 318.76 59.09, 367.68 2.94 M260.08 126.71 C286.6 97.68, 309.24 69.16, 367.68 2.94 M265.73 126.32 C304.67 81.34, 343.89 36.13, 371.35 4.81 M265.73 126.32 C296.05 89.95, 328.32 54.46, 371.35 4.81 M270.71 126.68 C312.11 79.85, 350.1 38.08, 375.03 6.68 M270.71 126.68 C308.29 85.27, 344.32 43.1, 375.03 6.68 M276.36 126.28 C302.37 95.05, 332.55 61.6, 377.39 10.06 M276.36 126.28 C300.17 99.46, 322.39 72.19, 377.39 10.06 M281.34 126.64 C304.76 96.82, 332.3 67.75, 380.41 12.68 M281.34 126.64 C307.49 95.18, 332.91 65.5, 380.41 12.68 M286.33 127 C314.42 93.01, 345.57 57.56, 381.46 17.57 M286.33 127 C313.51 94.85, 341.77 62.68, 381.46 17.57 M291.97 126.61 C313.48 100.45, 339.17 75.97, 383.16 21.7 M291.97 126.61 C311.58 103.09, 333.11 79.56, 383.16 21.7 M296.96 126.97 C320.59 100.34, 342.62 72.36, 384.87 25.84 M296.96 126.97 C321.66 99.71, 344.27 72.56, 384.87 25.84 M302.6 126.57 C334.11 86.35, 368.78 51.97, 385.27 31.48 M302.6 126.57 C330.75 94.29, 357.85 62.13, 385.27 31.48 M307.59 126.93 C326.81 105.47, 343.03 83.78, 385 37.88 M307.59 126.93 C327.54 105.51, 344.6 84.21, 385 37.88 M313.23 126.54 C332.05 106.16, 349.05 86.64, 385.4 43.52 M313.23 126.54 C332.89 103.96, 352.85 83.32, 385.4 43.52 M318.22 126.9 C331.98 110.58, 346.57 93.14, 385.14 49.92 M318.22 126.9 C331.03 111.1, 344.19 94.95, 385.14 49.92 M323.86 126.5 C335.72 108.89, 350.56 93.22, 385.53 55.56 M323.86 126.5 C345.11 101.47, 364.04 79.57, 385.53 55.56 M328.85 126.86 C346.1 108.02, 361.21 90.1, 385.27 61.96 M328.85 126.86 C342.2 112.06, 355.94 96.74, 385.27 61.96 M334.49 126.47 C350.84 109.97, 364.71 94.6, 385.67 67.6 M334.49 126.47 C353.49 104.81, 372.17 82.14, 385.67 67.6 M339.48 126.83 C348.96 114.71, 363.17 101.01, 385.4 74 M339.48 126.83 C354.75 110.09, 368.15 93.09, 385.4 74 M345.12 126.43 C354.84 117.22, 365.07 103.77, 385.8 79.64 M345.12 126.43 C358.46 110.23, 371.31 95.91, 385.8 79.64 M350.11 126.79 C364.3 111.08, 375.22 97.18, 385.54 86.04 M350.11 126.79 C363.14 112.28, 374.94 98.31, 385.54 86.04 M354.44 127.91 C362.81 117.31, 370.87 110.18, 385.28 92.44 M354.44 127.91 C362.9 118.85, 369.83 111.91, 385.28 92.44 M360.74 126.76 C367.63 119.85, 374.61 113.88, 386.98 96.57 M360.74 126.76 C369 116.72, 377.31 108.85, 386.98 96.57 M367.7 124.86 C371.04 118.25, 376.15 112.61, 382.13 108.25 M367.7 124.86 C371.11 120.48, 374.22 117.39, 382.13 108.25" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M31.5 0 M31.5 0 C144.73 2.16, 257.89 1.55, 352.5 0 M31.5 0 C123.21 -0.03, 215.81 -0.04, 352.5 0 M352.5 0 C373.48 -1.54, 384.54 11.54, 384 31.5 M352.5 0 C371.52 1.89, 382.06 11.18, 384 31.5 M384 31.5 C384.2 57.65, 382.83 81.45, 384 94.5 M384 31.5 C384.94 50.55, 383.94 67.7, 384 94.5 M384 94.5 C385.07 116.86, 374.35 127.03, 352.5 126 M384 94.5 C385.17 117.04, 373.57 125.11, 352.5 126 M352.5 126 C247.11 128.47, 140.42 127.09, 31.5 126 M352.5 126 C271.97 126.76, 193.29 127.03, 31.5 126 M31.5 126 C11.34 124.76, -1.26 114.07, 0 94.5 M31.5 126 C9.77 126.57, -1.64 113.31, 0 94.5 M0 94.5 C0.36 74.76, 1.16 53.5, 0 31.5 M0 94.5 C-0.19 81.51, -0.09 68.38, 0 31.5 M0 31.5 C-0.91 8.69, 10.9 0.79, 31.5 0 M0 31.5 C2.16 12.64, 11.39 -0.47, 31.5 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(302.01171875 705.19140625) rotate(0 145.3125 57.60000000000002)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: a</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;directory-a-digest&gt;</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files: []</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g transform="translate(303.75390625 45.62890625) rotate(0 89.0625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_WITH_KEEP</text></g><g transform="translate(308.25390625 240.4765625) rotate(0 98.4375 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_COMPLICATED</text></g><g transform="translate(307.28125 554.6148437500001) rotate(0 51.5625 9.600000000000023)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_A</text></g><g transform="translate(310.6875 674.4585937500001) rotate(0 51.5625 9.600000000000023)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_B</text></g><g transform="translate(18.95703125 42.53671874999998) rotate(0 28.125 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_A</text></g><g transform="translate(22.55078125 130.24374999999998) rotate(0 28.125 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_B</text></g><g transform="translate(13.5546875 210.27109374999998) rotate(0 46.875 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_EMPTY</text></g><g stroke-linecap="round"><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-0.52 1.13 C-60.08 19.9, -297.46 94.83, -357.2 113.61 M1.4 0.68 C-58.25 18.98, -298.08 93.09, -357.74 111.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-331.96 94.73 C-340.78 97.36, -347.89 102.93, -357.37 112.7 M-333.95 93.43 C-339.75 97.47, -345.42 102.99, -358.69 111" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-325.89 114.33 C-336.76 110.86, -345.74 110.35, -357.37 112.7 M-327.89 113.04 C-335.08 112.25, -342.26 112.89, -358.69 111" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M-0.42 1.04 C43.76 -38.95, 220.7 -199.19, 264.79 -239.05 M1.55 0.54 C45.63 -39.42, 220.36 -198.5, 263.94 -238.07" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M251.72 -211.19 C251.81 -219.53, 257.18 -225.36, 265.07 -236.65 M250.73 -212.1 C253.06 -218.02, 257.3 -223.31, 264.08 -238.4" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M237.91 -226.37 C241.88 -230.52, 251.02 -232.22, 265.07 -236.65 M236.92 -227.29 C242.47 -229.5, 250.03 -231.14, 264.08 -238.4" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M0.21 0.21 C-60.17 -24.98, -301.37 -126.03, -361.5 -151.72 M-1.14 -0.73 C-61.8 -26.18, -302.73 -127.84, -362.74 -153.25" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M-332.43 -152.64 C-341.83 -151.66, -356.48 -151.02, -362.88 -151.77 M-332.23 -151.12 C-344.25 -151.99, -356 -152.45, -362.93 -152.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M-340.42 -133.74 C-346.81 -139.68, -358.54 -145.95, -362.88 -151.77 M-340.22 -132.22 C-349.22 -140.26, -357.91 -147.96, -362.93 -152.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M-0.84 0.01 C13.92 -16, 74.3 -80.87, 89.45 -97.18 M0.92 -1.04 C15.95 -16.73, 76.9 -79.62, 91.58 -95.58" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M79.65 -69.91 C84.59 -79.16, 87.99 -87.88, 90.6 -97.23 M78.91 -67.92 C83.62 -76.08, 87.25 -85.83, 90.62 -96.45" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M64.75 -84.03 C75.18 -87.85, 84.2 -91.25, 90.6 -97.23 M64.01 -82.04 C73.25 -85.89, 81.47 -91.29, 90.62 -96.45" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g transform="translate(796.08984375 10.884374999999977) rotate(0 37.5 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">PathInfo</text></g><g stroke-linecap="round" transform="translate(800.94921875 70.3515625) rotate(0 238.5 66.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C10.39 14.59, 12.36 7.63, 18.1 2.48 M3.66 19.09 C7.72 12.99, 12.69 8.45, 18.1 2.48 M2.75 26.24 C9.54 18.05, 20.15 8.22, 24.4 1.33 M2.75 26.24 C10.63 16.6, 19.07 7.71, 24.4 1.33 M2.48 32.64 C10.84 25.1, 18.62 16.99, 30.7 0.18 M2.48 32.64 C10.3 22, 19.4 12.2, 30.7 0.18 M2.88 38.28 C13.73 28.82, 21.58 17.53, 34.37 2.05 M2.88 38.28 C11.28 29.81, 18.49 19.2, 34.37 2.05 M2.62 44.68 C15.27 32.46, 26.63 18.87, 39.36 2.41 M2.62 44.68 C12.58 32.64, 23.98 18.88, 39.36 2.41 M2.36 51.07 C15.78 39.12, 25.44 23.33, 45 2.02 M2.36 51.07 C15.86 35.07, 30.03 18.81, 45 2.02 M2.75 56.72 C21.32 37.62, 39.51 16.97, 49.99 2.38 M2.75 56.72 C15.6 42.48, 27.62 27.63, 49.99 2.38 M2.49 63.11 C19.73 43.61, 35.49 24.47, 55.63 1.98 M2.49 63.11 C14.04 50.89, 25.8 36.73, 55.63 1.98 M2.88 68.76 C20.08 47.93, 38.2 25.58, 60.62 2.34 M2.88 68.76 C19.66 48.91, 37.22 28.6, 60.62 2.34 M2.62 75.16 C19.27 53.2, 38.94 35.17, 66.26 1.95 M2.62 75.16 C17.89 55.5, 34.42 36.25, 66.26 1.95 M2.36 81.55 C30.5 51, 58.03 17.88, 71.25 2.31 M2.36 81.55 C16.96 64.32, 31.61 47.37, 71.25 2.31 M2.76 87.2 C31.87 55.75, 56.06 22.7, 76.89 1.91 M2.76 87.2 C24.31 61.65, 46.29 35.63, 76.89 1.91 M2.49 93.59 C32.62 61.39, 61.22 28.82, 81.88 2.27 M2.49 93.59 C23.69 67.97, 45.72 43.7, 81.88 2.27 M2.89 99.24 C23.68 73.49, 45.29 48.44, 87.52 1.88 M2.89 99.24 C26.99 71.79, 50.36 44.89, 87.52 1.88 M2.63 105.64 C23.82 79.99, 47.73 56.76, 92.51 2.24 M2.63 105.64 C29.41 76.09, 53.71 47.33, 92.51 2.24 M3.02 111.28 C35.76 74.46, 69.37 34.13, 98.15 1.85 M3.02 111.28 C24.1 86.82, 46.96 62.48, 98.15 1.85 M4.73 115.41 C41.64 74.79, 76.39 31.84, 103.14 2.21 M4.73 115.41 C35.15 80.63, 65.61 44.22, 103.14 2.21 M5.12 121.06 C38.67 80.99, 74.17 42.93, 108.78 1.81 M5.12 121.06 C37.52 87.2, 67.81 51.07, 108.78 1.81 M8.14 123.68 C45.61 75.63, 85.4 30.08, 113.77 2.17 M8.14 123.68 C46.84 78.16, 85.53 34.79, 113.77 2.17 M10.51 127.06 C51.38 83, 90.78 34.09, 119.41 1.78 M10.51 127.06 C44.83 86.58, 79.34 47.77, 119.41 1.78 M13.52 129.68 C38.07 99.79, 58.32 73.05, 124.4 2.14 M13.52 129.68 C50.9 87.61, 88.06 45.68, 124.4 2.14 M17.2 131.55 C58.64 84.2, 95.67 41.29, 130.04 1.74 M17.2 131.55 C41.93 103.22, 66.7 72.66, 130.04 1.74 M20.87 133.42 C57.62 92.2, 92.65 51.53, 135.03 2.1 M20.87 133.42 C65.94 80.91, 110.67 28.91, 135.03 2.1 M25.86 133.78 C57.72 93.6, 90.77 57.22, 140.67 1.71 M25.86 133.78 C56.65 99.16, 86.75 62.82, 140.67 1.71 M30.19 134.9 C76.39 84.32, 123.04 30.78, 145.66 2.07 M30.19 134.9 C59.94 102.4, 88.51 68.91, 145.66 2.07 M35.18 135.26 C63.65 103.83, 88.19 71.92, 150.65 2.43 M35.18 135.26 C77.05 84.73, 121.83 35.2, 150.65 2.43 M40.17 135.62 C76.62 93.15, 111.75 54.6, 156.29 2.03 M40.17 135.62 C72.44 97.03, 106.71 60.31, 156.29 2.03 M45.81 135.22 C68.12 108.89, 93.36 81.85, 161.28 2.39 M45.81 135.22 C93.12 81.98, 137.9 28.67, 161.28 2.39 M50.8 135.58 C99.08 85.21, 141.4 29.85, 166.92 2 M50.8 135.58 C95.54 85.04, 137.27 35.33, 166.92 2 M56.44 135.19 C86.16 100.81, 118.44 64.39, 171.91 2.36 M56.44 135.19 C85.59 100.01, 117.34 65.71, 171.91 2.36 M61.43 135.55 C93.02 99.32, 126.23 60.67, 177.55 1.96 M61.43 135.55 C98.63 92.46, 135.75 49.47, 177.55 1.96 M66.41 135.91 C88.8 108.25, 112.86 81.48, 182.54 2.32 M66.41 135.91 C91.41 106.52, 117.33 75.61, 182.54 2.32 M72.06 135.51 C104.73 95.48, 142.51 55.2, 188.18 1.93 M72.06 135.51 C99.45 104.83, 124.74 75.89, 188.18 1.93 M77.04 135.87 C115.11 94.08, 147.51 55.24, 193.17 2.29 M77.04 135.87 C100.11 110.76, 122.58 83.57, 193.17 2.29 M82.69 135.48 C116.4 97.33, 152.41 55.98, 198.81 1.9 M82.69 135.48 C110.99 102.8, 139.68 69.49, 198.81 1.9 M87.67 135.84 C132 84.75, 175.61 37.52, 203.8 2.26 M87.67 135.84 C116.95 102.95, 145.57 69.87, 203.8 2.26 M93.32 135.44 C137.75 81.91, 184.41 30.9, 209.44 1.86 M93.32 135.44 C125.76 94, 160.68 54.19, 209.44 1.86 M98.3 135.8 C129.81 99.97, 161.28 60.26, 214.43 2.22 M98.3 135.8 C133.08 96.91, 166.85 57.98, 214.43 2.22 M103.95 135.41 C130.43 101.26, 160.91 69.64, 220.07 1.83 M103.95 135.41 C145.52 86.13, 188.77 38.68, 220.07 1.83 M108.93 135.77 C131.74 110.52, 156.29 82.4, 225.06 2.19 M108.93 135.77 C155.58 84.93, 200.54 32.47, 225.06 2.19 M114.58 135.38 C157.28 86.99, 201.96 37.19, 230.7 1.79 M114.58 135.38 C152.78 93.11, 190.04 49.2, 230.7 1.79 M119.56 135.74 C147.35 100.73, 175.19 69.8, 235.69 2.15 M119.56 135.74 C152.35 99.22, 182.76 63.91, 235.69 2.15 M125.21 135.34 C151.37 104.17, 174.15 75, 241.33 1.76 M125.21 135.34 C170.53 85.3, 211.95 36.33, 241.33 1.76 M130.19 135.7 C173.94 86.44, 220.64 33.18, 246.32 2.12 M130.19 135.7 C172.93 86.98, 216.51 38.35, 246.32 2.12 M135.84 135.31 C174.21 90.08, 208.21 49.75, 251.96 1.72 M135.84 135.31 C158.72 109.66, 182.85 82.09, 251.96 1.72 M140.82 135.67 C177.31 93.41, 212.97 53.24, 256.95 2.08 M140.82 135.67 C166.68 105.14, 194.13 75.79, 256.95 2.08 M146.47 135.27 C182.12 93.41, 217.19 51.75, 262.59 1.69 M146.47 135.27 C192.8 83.11, 239.07 30.19, 262.59 1.69 M151.45 135.63 C195.86 84.04, 242.39 29.83, 267.58 2.05 M151.45 135.63 C182.45 96.24, 215.98 59.61, 267.58 2.05 M157.1 135.24 C193.93 90.8, 230.49 48.73, 272.56 2.41 M157.1 135.24 C186.1 101.92, 213.74 67.58, 272.56 2.41 M162.08 135.6 C204.71 88.09, 242.57 43.67, 278.21 2.01 M162.08 135.6 C195.51 97.13, 227.49 59.04, 278.21 2.01 M167.73 135.2 C191.02 109.31, 213.95 82.1, 283.19 2.37 M167.73 135.2 C208.32 87.02, 247.72 39.69, 283.19 2.37 M172.71 135.56 C211.05 92.35, 249.27 45.71, 288.84 1.98 M172.71 135.56 C204.27 99.8, 232.12 66.33, 288.84 1.98 M178.36 135.17 C208.57 98.05, 240.33 61.56, 293.82 2.34 M178.36 135.17 C224.01 83.75, 267.9 32.95, 293.82 2.34 M183.34 135.53 C211.9 102.21, 241.28 69.57, 299.47 1.94 M183.34 135.53 C225.07 89.34, 264.88 43.51, 299.47 1.94 M188.33 135.89 C232.49 86.72, 275.85 35.32, 304.45 2.3 M188.33 135.89 C226.55 88.72, 267.12 42.68, 304.45 2.3 M193.97 135.49 C226.44 96.44, 258.36 62.18, 310.1 1.91 M193.97 135.49 C239.79 82.46, 285.44 30.27, 310.1 1.91 M198.96 135.85 C228.28 100.09, 257.95 68.9, 315.08 2.27 M198.96 135.85 C235.09 93.52, 272.84 52.67, 315.08 2.27 M204.6 135.46 C239.68 94.74, 275.29 55.5, 320.73 1.88 M204.6 135.46 C244.23 91.28, 283.02 46.4, 320.73 1.88 M209.59 135.82 C250.03 93.16, 286.49 47.86, 325.71 2.24 M209.59 135.82 C241.67 97.03, 276.6 58.48, 325.71 2.24 M215.23 135.42 C252.93 93.98, 290.73 49.25, 331.36 1.84 M215.23 135.42 C239.46 107.48, 265.75 77.82, 331.36 1.84 M220.22 135.78 C258.31 87.12, 298.41 41.67, 336.34 2.2 M220.22 135.78 C261.35 88.79, 302.28 41.23, 336.34 2.2 M225.86 135.39 C247.19 109.85, 271.45 83.37, 341.99 1.81 M225.86 135.39 C269.09 87.75, 311.09 39.46, 341.99 1.81 M230.85 135.75 C271.9 89.87, 311.65 42.41, 346.97 2.17 M230.85 135.75 C266.69 94.9, 300.46 56.72, 346.97 2.17 M236.49 135.36 C279.54 90.41, 318.34 43.37, 352.62 1.77 M236.49 135.36 C262.51 106.22, 289.8 75.87, 352.62 1.77 M241.48 135.72 C270.62 102.2, 297.59 68.43, 357.6 2.13 M241.48 135.72 C267.4 106.57, 291.68 77.77, 357.6 2.13 M247.12 135.32 C272.55 104.17, 301.27 75.42, 363.25 1.74 M247.12 135.32 C286.54 88.24, 325.79 43.89, 363.25 1.74 M252.11 135.68 C285.8 94.95, 321.96 52.06, 368.23 2.1 M252.11 135.68 C286.48 98.48, 319.42 59.27, 368.23 2.1 M257.75 135.29 C287.3 103.83, 313.13 73.67, 373.88 1.7 M257.75 135.29 C296.47 90.19, 336.96 43.61, 373.88 1.7 M262.74 135.65 C294.04 101.96, 321.68 67.42, 378.86 2.06 M262.74 135.65 C285.88 109.26, 307.99 80.92, 378.86 2.06 M268.38 135.25 C301.94 102.32, 329.68 63.48, 383.85 2.42 M268.38 135.25 C312.43 84.8, 355.81 34.99, 383.85 2.42 M273.37 135.61 C307.98 93.43, 343.82 50.19, 389.49 2.03 M273.37 135.61 C314.49 86.55, 355.98 39.87, 389.49 2.03 M279.01 135.22 C315.47 96.77, 347.58 56.5, 394.48 2.39 M279.01 135.22 C310.17 98.46, 342.48 62.75, 394.48 2.39 M284 135.58 C309.56 103.84, 338.63 75.13, 400.12 1.99 M284 135.58 C310.36 104.37, 338.59 71.89, 400.12 1.99 M289.64 135.18 C312.79 106.98, 337.43 80.83, 405.11 2.35 M289.64 135.18 C334.14 86.06, 378.85 34.51, 405.11 2.35 M294.63 135.54 C322.11 103.4, 351.85 69.91, 410.75 1.96 M294.63 135.54 C321.72 103.68, 349.75 71.49, 410.75 1.96 M299.62 135.9 C347.67 83.43, 392.82 28.41, 415.74 2.32 M299.62 135.9 C340.13 91.24, 379.75 47.11, 415.74 2.32 M305.26 135.51 C337.44 97.55, 370.85 61.54, 421.38 1.93 M305.26 135.51 C331.72 105.39, 358.21 73.88, 421.38 1.93 M310.25 135.87 C347.77 87.67, 388.48 42.7, 426.37 2.29 M310.25 135.87 C343.24 98.26, 374.33 62.37, 426.37 2.29 M315.89 135.47 C358.33 83.2, 405.58 35.13, 432.01 1.89 M315.89 135.47 C342.14 103.92, 367.01 74.44, 432.01 1.89 M320.88 135.83 C349.09 105.42, 377.17 73.43, 437 2.25 M320.88 135.83 C348.8 103.87, 377.25 71.06, 437 2.25 M326.52 135.44 C357.37 98.83, 392.96 61.44, 442.64 1.86 M326.52 135.44 C359.38 95.45, 395.35 54.96, 442.64 1.86 M331.51 135.8 C371.1 90.29, 414.52 41.02, 447.63 2.22 M331.51 135.8 C375.19 84.86, 419.5 34.87, 447.63 2.22 M337.15 135.41 C360.41 108.49, 389.11 78.17, 452.62 2.58 M337.15 135.41 C365.83 101.31, 393.5 68.62, 452.62 2.58 M342.14 135.77 C374.92 95.35, 408.22 55.28, 456.95 3.69 M342.14 135.77 C375.97 95.74, 410.85 54.51, 456.95 3.69 M347.78 135.37 C386.27 93.45, 423.41 49.67, 461.94 4.05 M347.78 135.37 C388.88 89.16, 428.69 44.23, 461.94 4.05 M352.77 135.73 C391.81 93.38, 429.55 47.99, 464.96 6.68 M352.77 135.73 C395.76 88.88, 437.51 38.86, 464.96 6.68 M358.41 135.34 C384.9 101.59, 410.76 73.18, 469.29 7.79 M358.41 135.34 C381.72 110.3, 403.42 84.85, 469.29 7.79 M363.4 135.7 C391.38 101.39, 419.01 68.91, 471.65 11.17 M363.4 135.7 C388 108.1, 412.92 77.82, 471.65 11.17 M369.04 135.3 C402.56 98.71, 434.28 59.98, 474.67 13.79 M369.04 135.3 C400.06 99.18, 430.91 65.19, 474.67 13.79 M374.03 135.66 C409.08 94.39, 445.61 50.9, 475.72 18.68 M374.03 135.66 C395.27 111.63, 415.98 87.61, 475.72 18.68 M379.67 135.27 C413.73 94.24, 448.47 58.31, 477.43 22.82 M379.67 135.27 C399.49 110.9, 420.37 87.43, 477.43 22.82 M384.66 135.63 C405.61 110.26, 426.94 85.23, 479.13 26.95 M384.66 135.63 C415.84 98.51, 447.61 62.33, 479.13 26.95 M390.3 135.23 C419.31 104.53, 444.38 75.12, 479.53 32.59 M390.3 135.23 C411.06 109.85, 433.81 86.46, 479.53 32.59 M395.29 135.59 C423.34 101.52, 451.07 71.77, 479.27 38.99 M395.29 135.59 C425.27 99.57, 456.57 63.69, 479.27 38.99 M400.93 135.2 C416.38 116.25, 434.8 96.63, 479 45.39 M400.93 135.2 C430.56 100.88, 460.84 68, 479 45.39 M405.92 135.56 C424.18 114.12, 445.92 87.99, 479.4 51.03 M405.92 135.56 C430.48 106.84, 457.04 77.45, 479.4 51.03 M410.91 135.92 C425.56 119.11, 439.25 103.89, 479.14 57.43 M410.91 135.92 C432.34 111.55, 454.95 85.96, 479.14 57.43 M416.55 135.52 C437.03 112.31, 458.69 88.33, 479.53 63.07 M416.55 135.52 C441.28 108.64, 464.07 81.07, 479.53 63.07 M421.54 135.88 C439.7 114.89, 456.47 92.93, 479.27 69.47 M421.54 135.88 C433.45 119.98, 448.34 104.83, 479.27 69.47 M427.18 135.49 C444.19 117.51, 459.72 100.42, 479.66 75.11 M427.18 135.49 C444.66 114.13, 462.3 93.68, 479.66 75.11 M432.17 135.85 C446.83 119.86, 461.29 103.69, 479.4 81.51 M432.17 135.85 C442.13 123.55, 452.74 111.76, 479.4 81.51 M437.81 135.46 C448.92 124.72, 457.92 112.62, 479.8 87.15 M437.81 135.46 C453.55 117.76, 467.68 101.47, 479.8 87.15 M445.42 132.8 C459.39 120.23, 469.27 105.41, 479.54 93.55 M445.42 132.8 C454.06 123.31, 462.94 111.17, 479.54 93.55 M451.06 132.4 C463.15 119.27, 472.64 110.16, 477.96 101.46 M451.06 132.4 C458.03 124.07, 464.8 115.65, 477.96 101.46 M457.36 131.25 C462.65 126.55, 467.85 119.82, 477.05 108.61 M457.36 131.25 C462.56 125.74, 468.27 120.23, 477.05 108.61 M464.97 128.59 C469.23 123.21, 474.5 119.94, 476.13 115.76 M464.97 128.59 C466.4 126.62, 470.09 122.46, 476.13 115.76" stroke="#b2f2bb" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C134.76 -0.82, 237.15 -2.84, 445 0 M32 0 C117.14 0.77, 203.3 0.82, 445 0 M445 0 C464.46 1.31, 477.82 10, 477 32 M445 0 C468.12 -1.15, 476.89 8.73, 477 32 M477 32 C477.29 57.75, 476.77 81.02, 477 101 M477 32 C477.02 52.99, 476.55 76.44, 477 101 M477 101 C476.76 123.18, 464.89 132.41, 445 133 M477 101 C479.01 122.87, 468.12 132.27, 445 133 M445 133 C321.26 133.45, 195.94 134.33, 32 133 M445 133 C294.67 133.48, 144.05 133.68, 32 133 M32 133 C9.9 132.01, -1.58 121.23, 0 101 M32 133 C9.79 134.41, -1.71 120.88, 0 101 M0 101 C-1.18 83.33, -0.4 65.06, 0 32 M0 101 C0 80.69, 0.41 59.87, 0 32 M0 32 C1.82 10.89, 11.97 0.59, 32 0 M0 32 C-1.7 8.91, 9.45 -0.01, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(805.94921875 75.3515625) rotate(0 220.3125 57.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">node:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">  symlink:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    name: 00000000000000000000000000000000-test</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    target: /foo</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">references: []</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">narinfo: โ€ฆ</text></g><g stroke-linecap="round" transform="translate(806.46875 225.2578125) rotate(0 238.5 72.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C8.68 15.22, 9.53 10.88, 18.1 2.48 M3.66 19.09 C8.81 14.12, 11.51 9.97, 18.1 2.48 M2.75 26.24 C6.81 18.96, 11.51 16.19, 24.4 1.33 M2.75 26.24 C8.1 20.93, 12.69 14.7, 24.4 1.33 M2.48 32.64 C12.09 22.75, 21.05 8.83, 30.7 0.18 M2.48 32.64 C12.24 21.36, 23.11 8.33, 30.7 0.18 M2.88 38.28 C14.38 23.94, 25.95 11.59, 34.37 2.05 M2.88 38.28 C9.83 28.87, 18.57 19.97, 34.37 2.05 M2.62 44.68 C11.2 34.98, 16.8 26.09, 39.36 2.41 M2.62 44.68 C9.89 35.67, 19.19 25.97, 39.36 2.41 M2.36 51.07 C16.6 33.87, 30.06 19.6, 45 2.02 M2.36 51.07 C14.11 36.43, 26.4 24.25, 45 2.02 M2.75 56.72 C11.38 44.5, 23.52 31.72, 49.99 2.38 M2.75 56.72 C12.11 44.16, 21.93 34.17, 49.99 2.38 M2.49 63.11 C22.69 39.21, 44.13 13.8, 55.63 1.98 M2.49 63.11 C24.61 38.46, 45.21 14.7, 55.63 1.98 M2.88 68.76 C22.62 46.66, 38.87 25.27, 60.62 2.34 M2.88 68.76 C15.25 52.49, 29.63 37.02, 60.62 2.34 M2.62 75.16 C19.95 55.49, 39.17 32.32, 66.26 1.95 M2.62 75.16 C24.54 50.58, 44.69 26.49, 66.26 1.95 M2.36 81.55 C27.08 53.26, 56.1 20.67, 71.25 2.31 M2.36 81.55 C22.38 59.12, 42.38 37.91, 71.25 2.31 M2.76 87.2 C16.85 69.7, 34.99 53.54, 76.89 1.91 M2.76 87.2 C24.69 60.73, 48.08 33.35, 76.89 1.91 M2.49 93.59 C26.83 67.24, 48.57 42.72, 81.88 2.27 M2.49 93.59 C22.01 71.75, 40.83 49.63, 81.88 2.27 M2.89 99.24 C21.26 75.44, 42.16 55.2, 87.52 1.88 M2.89 99.24 C27.49 71.35, 50.92 41.12, 87.52 1.88 M2.63 105.64 C30.05 76.71, 56.77 44.63, 92.51 2.24 M2.63 105.64 C29.93 73.84, 55.66 42.59, 92.51 2.24 M2.37 112.03 C31.19 75.8, 63.69 43.11, 98.15 1.85 M2.37 112.03 C33.09 77.93, 61.62 44.45, 98.15 1.85 M2.76 117.68 C39.28 75.49, 75.08 36.84, 103.14 2.21 M2.76 117.68 C38.87 78.04, 74.17 36.06, 103.14 2.21 M3.16 123.32 C44.92 78.9, 86.85 29.88, 108.78 1.81 M3.16 123.32 C33.5 86.75, 64.72 49.98, 108.78 1.81 M4.86 127.45 C37.1 91.22, 67.62 56.08, 113.77 2.17 M4.86 127.45 C48.91 78.36, 90.69 30.13, 113.77 2.17 M5.26 133.1 C28.92 105.27, 51.55 78.45, 119.41 1.78 M5.26 133.1 C39.84 93.11, 73.53 54.29, 119.41 1.78 M7.62 136.47 C35.56 105.24, 62.69 73.44, 124.4 2.14 M7.62 136.47 C41.94 98.75, 76.48 57.38, 124.4 2.14 M10.64 139.1 C45.2 102.3, 79.7 60.38, 130.04 1.74 M10.64 139.1 C39.87 104.89, 70.76 71.45, 130.04 1.74 M14.31 140.97 C43.51 110.97, 68.26 78.62, 135.03 2.1 M14.31 140.97 C45.94 106.35, 77.72 71.79, 135.03 2.1 M17.33 143.59 C63.36 91.09, 109.79 35.17, 140.67 1.71 M17.33 143.59 C67.02 87.83, 115.79 32.77, 140.67 1.71 M21.01 145.46 C69.52 85.94, 122.53 32.68, 145.66 2.07 M21.01 145.46 C65.52 93.39, 110.85 41.72, 145.66 2.07 M25.34 146.58 C73.82 90.55, 124.58 31.62, 150.65 2.43 M25.34 146.58 C57.27 108.71, 90.6 71.59, 150.65 2.43 M32.29 144.67 C82.37 88.85, 130.86 32.77, 156.29 2.03 M32.29 144.67 C58.07 117.01, 83.71 85.72, 156.29 2.03 M37.28 145.03 C76.55 100.57, 116.26 57.79, 161.28 2.39 M37.28 145.03 C75.27 101.67, 112.08 57.69, 161.28 2.39 M42.92 144.64 C87.59 96.54, 128.11 43.6, 166.92 2 M42.92 144.64 C76.77 106.18, 112.16 66.71, 166.92 2 M47.91 145 C74.27 114.89, 99.04 86.08, 171.91 2.36 M47.91 145 C80.84 110.75, 111.51 74.33, 171.91 2.36 M52.9 145.36 C91.05 102.58, 127.57 62.7, 177.55 1.96 M52.9 145.36 C99.51 92.64, 144.44 40.01, 177.55 1.96 M58.54 144.96 C96.62 103.36, 132.1 61.4, 182.54 2.32 M58.54 144.96 C102.3 95.31, 145 44.66, 182.54 2.32 M63.53 145.32 C109.82 92.88, 153.81 37.96, 188.18 1.93 M63.53 145.32 C106.42 95.64, 150.95 44.77, 188.18 1.93 M69.17 144.93 C96.18 113.75, 124.05 81.34, 193.17 2.29 M69.17 144.93 C108.27 96.79, 149.34 50.87, 193.17 2.29 M74.16 145.29 C97.88 116.26, 126.66 87.16, 198.81 1.9 M74.16 145.29 C103.75 113.96, 133.25 80.61, 198.81 1.9 M79.8 144.9 C128.87 85.82, 177.7 31.59, 203.8 2.26 M79.8 144.9 C108.17 113.17, 135.25 78.83, 203.8 2.26 M84.79 145.26 C128.13 96.77, 168.82 46.48, 209.44 1.86 M84.79 145.26 C120.57 103.55, 156.93 61.55, 209.44 1.86 M90.43 144.86 C131.03 103.87, 164.62 58.8, 214.43 2.22 M90.43 144.86 C139.02 86.88, 188.33 30.13, 214.43 2.22 M95.42 145.22 C122.03 112.94, 150.45 84.35, 220.07 1.83 M95.42 145.22 C135.79 98.82, 175.14 53.71, 220.07 1.83 M101.06 144.83 C140.89 97.19, 181.91 54.17, 225.06 2.19 M101.06 144.83 C134.98 105.95, 167.66 68.29, 225.06 2.19 M106.05 145.19 C152.49 92.82, 198.26 40.96, 230.7 1.79 M106.05 145.19 C132.37 114.56, 159.47 83.34, 230.7 1.79 M111.69 144.79 C153.52 93.02, 197.5 42.26, 235.69 2.15 M111.69 144.79 C159.86 91.23, 206.41 36.81, 235.69 2.15 M116.68 145.15 C142.1 113.42, 167.54 83.21, 241.33 1.76 M116.68 145.15 C161.14 92.66, 207.79 41.99, 241.33 1.76 M122.32 144.76 C166.13 93.13, 209.82 39.8, 246.32 2.12 M122.32 144.76 C166.64 93.28, 213.29 39.18, 246.32 2.12 M127.31 145.12 C165.51 100.3, 206.72 56.81, 251.96 1.72 M127.31 145.12 C156.34 112.26, 187.1 77.6, 251.96 1.72 M132.95 144.72 C166.91 109.34, 195.98 71.92, 256.95 2.08 M132.95 144.72 C180.38 89.53, 229.12 34.54, 256.95 2.08 M137.94 145.08 C175.38 101.81, 210.1 63.31, 262.59 1.69 M137.94 145.08 C169.66 108.24, 200.14 72.75, 262.59 1.69 M143.58 144.69 C168.18 114.88, 194.29 86.3, 267.58 2.05 M143.58 144.69 C176.83 108.94, 208.19 70.29, 267.58 2.05 M148.57 145.05 C182.56 103.55, 216.29 66.03, 272.56 2.41 M148.57 145.05 C181.02 105.11, 215.28 67.56, 272.56 2.41 M154.21 144.65 C189.78 103.53, 226.3 57.46, 278.21 2.01 M154.21 144.65 C194.19 98.45, 235.22 50.5, 278.21 2.01 M159.2 145.01 C201.39 97.64, 239.44 52.54, 283.19 2.37 M159.2 145.01 C196.58 101.2, 234.19 59.04, 283.19 2.37 M164.19 145.37 C204.83 100.56, 245.77 53.99, 288.84 1.98 M164.19 145.37 C194.6 111.64, 224.28 77.56, 288.84 1.98 M169.83 144.98 C216.09 96.8, 258.65 45.93, 293.82 2.34 M169.83 144.98 C198.36 111.93, 225.45 82.13, 293.82 2.34 M174.82 145.34 C222.13 92.22, 266.76 39.3, 299.47 1.94 M174.82 145.34 C214.43 98.2, 254.01 52.59, 299.47 1.94 M180.46 144.94 C206.13 115.61, 232.02 85.17, 304.45 2.3 M180.46 144.94 C208.14 111.98, 236.78 80.02, 304.45 2.3 M185.45 145.31 C211.73 114.99, 239.84 81.36, 310.1 1.91 M185.45 145.31 C211.68 114.43, 238.14 82.89, 310.1 1.91 M191.09 144.91 C218.08 111.04, 246.8 80.32, 315.08 2.27 M191.09 144.91 C214.94 114.57, 239.96 87.29, 315.08 2.27 M196.08 145.27 C239.46 94.21, 286.41 44.83, 320.73 1.88 M196.08 145.27 C229.6 106.98, 264.69 68.01, 320.73 1.88 M201.72 144.88 C228.54 113.1, 258.25 77.79, 325.71 2.24 M201.72 144.88 C243.4 96.39, 285.56 47.13, 325.71 2.24 M206.71 145.24 C253.76 92.02, 298.3 40.23, 331.36 1.84 M206.71 145.24 C244.96 99.96, 284.86 52.62, 331.36 1.84 M212.35 144.84 C256.46 90.35, 305.16 39.74, 336.34 2.2 M212.35 144.84 C254.88 97.2, 294.9 51.16, 336.34 2.2 M217.34 145.2 C260.85 95.12, 303.44 46.91, 341.99 1.81 M217.34 145.2 C254.9 102.38, 292.7 59.98, 341.99 1.81 M222.98 144.81 C255.34 105.33, 290.89 65.64, 346.97 2.17 M222.98 144.81 C266.33 93.19, 310.85 42.12, 346.97 2.17 M227.97 145.17 C263.66 108.29, 298 68.71, 352.62 1.77 M227.97 145.17 C258.23 109.67, 288.39 75.4, 352.62 1.77 M233.61 144.77 C270.05 104.52, 304.97 63.07, 357.6 2.13 M233.61 144.77 C274.47 98.07, 312.89 55.02, 357.6 2.13 M238.6 145.13 C266.67 111.74, 296.18 82.47, 363.25 1.74 M238.6 145.13 C277.04 101.17, 315.84 55.8, 363.25 1.74 M244.24 144.74 C274.9 111.46, 304.37 75.97, 368.23 2.1 M244.24 144.74 C293.37 88.52, 341.04 33.09, 368.23 2.1 M249.23 145.1 C276.79 116.75, 302.14 86.51, 373.88 1.7 M249.23 145.1 C290.94 99.98, 331.11 52.85, 373.88 1.7 M254.87 144.7 C287.13 105.19, 322.18 68.96, 378.86 2.06 M254.87 144.7 C288.12 105.83, 322.32 65.46, 378.86 2.06 M259.86 145.06 C297.18 103.73, 332.56 62.14, 383.85 2.42 M259.86 145.06 C285.47 114.28, 312.5 83.14, 383.85 2.42 M265.5 144.67 C289.04 113.22, 315.35 85.99, 389.49 2.03 M265.5 144.67 C309.3 94.62, 352.05 44.75, 389.49 2.03 M270.49 145.03 C300.51 108.19, 332.82 72.1, 394.48 2.39 M270.49 145.03 C300.97 111.01, 330.93 74.5, 394.48 2.39 M276.13 144.63 C324.36 91.02, 372.28 35.59, 400.12 1.99 M276.13 144.63 C314.28 100.64, 353.55 56.58, 400.12 1.99 M281.12 144.99 C306.5 114.55, 332.17 85.71, 405.11 2.35 M281.12 144.99 C323.82 96.38, 365.49 47.59, 405.11 2.35 M286.1 145.35 C317.3 110.79, 350.4 75.24, 410.75 1.96 M286.1 145.35 C329.26 94.11, 373.2 45.44, 410.75 1.96 M291.75 144.96 C331.46 97.55, 372.96 52.2, 415.74 2.32 M291.75 144.96 C333.75 97.37, 374.48 47.84, 415.74 2.32 M296.73 145.32 C339.74 99.77, 378.75 49.03, 421.38 1.93 M296.73 145.32 C339.27 96.73, 382.31 46.86, 421.38 1.93 M302.38 144.93 C334.79 106.51, 370.65 62.67, 426.37 2.29 M302.38 144.93 C351.86 88.93, 398.72 33.88, 426.37 2.29 M307.36 145.29 C340.53 106.77, 376.08 66.7, 432.01 1.89 M307.36 145.29 C343.7 102.77, 379.62 59.95, 432.01 1.89 M313.01 144.89 C349.35 105.7, 384.17 63.87, 437 2.25 M313.01 144.89 C353.04 98.04, 391.67 55.11, 437 2.25 M317.99 145.25 C339.84 114.7, 366.5 88.66, 442.64 1.86 M317.99 145.25 C351.57 103.84, 387.73 63.05, 442.64 1.86 M323.64 144.86 C348.5 114.38, 375.72 88.33, 447.63 2.22 M323.64 144.86 C355.53 106.62, 389.28 69.15, 447.63 2.22 M328.62 145.22 C372.38 92.62, 416.87 43.42, 452.62 2.58 M328.62 145.22 C366.51 102.9, 405.23 58.43, 452.62 2.58 M334.27 144.82 C363.07 113.17, 389.9 79.51, 456.95 3.69 M334.27 144.82 C367.21 104.1, 402.44 65.76, 456.95 3.69 M339.25 145.18 C372.8 105.31, 407.19 67.1, 461.94 4.05 M339.25 145.18 C370.71 111.13, 401.08 74.54, 461.94 4.05 M344.9 144.79 C373.24 109.42, 403.66 74.09, 464.96 6.68 M344.9 144.79 C380.58 103.48, 416.5 62.22, 464.96 6.68 M349.88 145.15 C396.98 89.22, 441.52 35.96, 469.29 7.79 M349.88 145.15 C384.63 103.87, 421.1 63.92, 469.29 7.79 M355.53 144.75 C384.31 114.2, 411.66 82.06, 471.65 11.17 M355.53 144.75 C395.88 99.08, 436.7 51.19, 471.65 11.17 M360.51 145.11 C388.54 111.66, 414.89 82.27, 474.67 13.79 M360.51 145.11 C398.58 98.72, 438.1 53.79, 474.67 13.79 M366.16 144.72 C399.89 104.57, 433.88 66.68, 475.72 18.68 M366.16 144.72 C408.72 95.09, 451.17 47.53, 475.72 18.68 M371.14 145.08 C397.64 114.94, 427.94 82.11, 477.43 22.82 M371.14 145.08 C410.48 97.21, 450.83 51.08, 477.43 22.82 M376.79 144.68 C397.46 116.7, 419.98 91.38, 479.13 26.95 M376.79 144.68 C400.93 117.38, 426.7 88.5, 479.13 26.95 M381.77 145.04 C417.98 100.88, 455.83 57.26, 479.53 32.59 M381.77 145.04 C410.94 114.25, 438.41 81.7, 479.53 32.59 M387.42 144.65 C412.93 114.6, 438.57 87, 479.27 38.99 M387.42 144.65 C411.38 118.3, 434.26 90.98, 479.27 38.99 M392.4 145.01 C416.3 120.47, 436.57 92.84, 479 45.39 M392.4 145.01 C415.9 120.64, 438.88 93.42, 479 45.39 M397.39 145.37 C415.88 120.12, 437.69 100.01, 479.4 51.03 M397.39 145.37 C419.06 121.26, 438.71 97.02, 479.4 51.03 M403.03 144.98 C418.65 128.5, 434.02 108.59, 479.14 57.43 M403.03 144.98 C431.82 109.79, 463.03 76.44, 479.14 57.43 M408.02 145.34 C423.72 128.07, 441.93 108.83, 479.53 63.07 M408.02 145.34 C423.27 129.37, 437.27 112.48, 479.53 63.07 M413.66 144.94 C430.89 123.63, 448.67 104.59, 479.27 69.47 M413.66 144.94 C429.3 124.99, 447.94 106.36, 479.27 69.47 M418.65 145.3 C429.88 130.69, 442.79 115.76, 479.66 75.11 M418.65 145.3 C438.44 123.35, 457.66 100.29, 479.66 75.11 M424.29 144.91 C443.94 120.3, 467.31 96.47, 479.4 81.51 M424.29 144.91 C443.54 122.53, 464.39 98.64, 479.4 81.51 M429.28 145.27 C440.46 128.53, 453.86 117.18, 479.8 87.15 M429.28 145.27 C446.57 125.43, 465.7 103.67, 479.8 87.15 M434.92 144.87 C452.62 126.78, 470.9 107.72, 479.54 93.55 M434.92 144.87 C446.21 130.49, 458.92 116.32, 479.54 93.55 M439.91 145.23 C454.92 126.59, 470.52 110.64, 479.93 99.19 M439.91 145.23 C454.48 130.43, 466.98 114.01, 479.93 99.19 M445.55 144.84 C459.7 129.63, 473.4 112.45, 479.67 105.59 M445.55 144.84 C456.2 132.48, 466.43 120.71, 479.67 105.59 M451.2 144.44 C457.4 137.27, 463.87 127.4, 478.1 113.5 M451.2 144.44 C457.13 137.53, 462.11 130.98, 478.1 113.5 M457.5 143.29 C464.19 138.07, 467.02 131.58, 476.52 121.41 M457.5 143.29 C461.1 138.38, 465.47 132.12, 476.52 121.41 M465.11 140.63 C469.09 135.97, 469.57 134.23, 476.26 127.8 M465.11 140.63 C467.57 137.52, 470.45 135.62, 476.26 127.8" stroke="#b2f2bb" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C176.04 -2.14, 317.57 -1.78, 445 0 M32 0 C179.36 0.19, 326.13 -0.1, 445 0 M445 0 C466.49 -1.62, 476.74 11.76, 477 32 M445 0 C465.33 0.95, 478.9 10.46, 477 32 M477 32 C478.38 59.54, 477.89 83.51, 477 113 M477 32 C477.4 63.12, 476.78 93.95, 477 113 M477 113 C476.1 134.31, 466.7 143.02, 445 145 M477 113 C478.22 135.1, 467.61 144.46, 445 145 M445 145 C356.12 145.98, 265.85 145.1, 32 145 M445 145 C280.4 145.67, 115.39 145.75, 32 145 M32 145 C8.74 143.12, -0.92 132.86, 0 113 M32 145 C12.94 142.98, -0.76 132.33, 0 113 M0 113 C-0.07 82.2, 0.07 49.78, 0 32 M0 113 C0.34 82.21, 0.32 51.58, 0 32 M0 32 C1.6 12.66, 10.66 -1.83, 32 0 M0 32 C1.88 10.07, 10.34 0.34, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(811.46875 230.2578125) rotate(0 220.3125 67.19999999999999)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">node:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">  directory:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    name: 11111111111111111111111111111111-test</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    digest: &lt;directory-complicated-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    size: 4</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">references: []</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">narinfo: โ€ฆ</text></g><g stroke-linecap="round" transform="translate(810.1484375 394.59375) rotate(0 238.5 72.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C8.91 13.37, 11.17 9.44, 18.1 2.48 M3.66 19.09 C9.16 12.46, 14.4 7.69, 18.1 2.48 M2.75 26.24 C9.7 18.96, 16.37 8.53, 24.4 1.33 M2.75 26.24 C11.39 17.63, 20.3 6.99, 24.4 1.33 M2.48 32.64 C10.71 24.03, 18.59 13.26, 30.7 0.18 M2.48 32.64 C11.25 23.35, 19.02 14.4, 30.7 0.18 M2.88 38.28 C16.41 22.39, 27.21 9.65, 34.37 2.05 M2.88 38.28 C9.5 30.63, 15.35 23.52, 34.37 2.05 M2.62 44.68 C14.02 30.56, 24.17 18.77, 39.36 2.41 M2.62 44.68 C11.44 34.89, 20.03 24.71, 39.36 2.41 M2.36 51.07 C14.17 38.91, 27.26 20.6, 45 2.02 M2.36 51.07 C14.79 38.21, 27.2 22.82, 45 2.02 M2.75 56.72 C14.28 42.93, 28 31.31, 49.99 2.38 M2.75 56.72 C14.37 45.04, 23.83 32.21, 49.99 2.38 M2.49 63.11 C15.91 47.14, 31.6 33.09, 55.63 1.98 M2.49 63.11 C22.3 40.56, 42.25 16.35, 55.63 1.98 M2.88 68.76 C25.94 42.37, 49.23 17.68, 60.62 2.34 M2.88 68.76 C25.29 41.12, 49.97 16.59, 60.62 2.34 M2.62 75.16 C26.36 49.35, 49.71 23.45, 66.26 1.95 M2.62 75.16 C27.26 46.7, 53.03 16.79, 66.26 1.95 M2.36 81.55 C18.75 60.1, 37.31 41.18, 71.25 2.31 M2.36 81.55 C30.19 50.57, 57.16 19.4, 71.25 2.31 M2.76 87.2 C17.36 71.88, 32.62 50.73, 76.89 1.91 M2.76 87.2 C26.3 60.71, 49.92 34.99, 76.89 1.91 M2.49 93.59 C27.25 66.48, 49.52 37.6, 81.88 2.27 M2.49 93.59 C30.85 62.58, 57.18 29.13, 81.88 2.27 M2.89 99.24 C25.68 73.59, 51.19 46.74, 87.52 1.88 M2.89 99.24 C20.96 78.77, 38.14 58.85, 87.52 1.88 M2.63 105.64 C27.26 82.03, 48.77 55.01, 92.51 2.24 M2.63 105.64 C29.71 75, 56.08 45.73, 92.51 2.24 M2.37 112.03 C38.89 71.73, 73.23 30.36, 98.15 1.85 M2.37 112.03 C31.31 79.87, 58.69 47.79, 98.15 1.85 M2.76 117.68 C38.65 77.93, 73.02 35.37, 103.14 2.21 M2.76 117.68 C39.96 75.26, 75.95 31.69, 103.14 2.21 M3.16 123.32 C39.16 81.11, 77.97 37.18, 108.78 1.81 M3.16 123.32 C26.62 96.63, 50.07 69.49, 108.78 1.81 M4.86 127.45 C38.16 84.21, 75.04 44.41, 113.77 2.17 M4.86 127.45 C26.16 101.72, 50.12 76.16, 113.77 2.17 M5.26 133.1 C32.36 105, 60.22 74.13, 119.41 1.78 M5.26 133.1 C49.95 79.53, 94.95 28.74, 119.41 1.78 M7.62 136.47 C35.36 107.28, 59.43 73.22, 124.4 2.14 M7.62 136.47 C48.07 90.66, 87.09 44.03, 124.4 2.14 M10.64 139.1 C44.23 99.15, 78.94 58.88, 130.04 1.74 M10.64 139.1 C48.85 99.23, 82.98 56.76, 130.04 1.74 M14.31 140.97 C61.07 83.76, 109.13 28.41, 135.03 2.1 M14.31 140.97 C40.23 110.15, 67.08 81.31, 135.03 2.1 M17.33 143.59 C57.88 96.8, 95.53 53.8, 140.67 1.71 M17.33 143.59 C57.08 96.93, 97.56 52.45, 140.67 1.71 M21.01 145.46 C56.01 106.76, 88.35 69.45, 145.66 2.07 M21.01 145.46 C67.32 93, 112.76 41.13, 145.66 2.07 M25.34 146.58 C51.39 115.65, 78.49 84.38, 150.65 2.43 M25.34 146.58 C67.89 94.87, 112.13 43.89, 150.65 2.43 M32.29 144.67 C81.15 91.53, 127.11 36.9, 156.29 2.03 M32.29 144.67 C57.68 113.6, 83.65 83.25, 156.29 2.03 M37.28 145.03 C80.72 91.85, 128.85 42.46, 161.28 2.39 M37.28 145.03 C81.17 93.73, 125.7 40.99, 161.28 2.39 M42.92 144.64 C86.35 93.9, 133.36 38.98, 166.92 2 M42.92 144.64 C81.83 99.95, 121.66 56.09, 166.92 2 M47.91 145 C76.08 112.91, 107.55 78.18, 171.91 2.36 M47.91 145 C81.06 109.66, 111.35 72.82, 171.91 2.36 M52.9 145.36 C100.16 89.62, 149.95 34.51, 177.55 1.96 M52.9 145.36 C89.79 102.8, 125.11 62.89, 177.55 1.96 M58.54 144.96 C90.83 107.72, 120.89 72.28, 182.54 2.32 M58.54 144.96 C83.32 116.05, 108.95 87.3, 182.54 2.32 M63.53 145.32 C97.39 110.22, 128.17 69.65, 188.18 1.93 M63.53 145.32 C97.52 104.23, 131.94 65.28, 188.18 1.93 M69.17 144.93 C100.97 104.36, 136.13 67.64, 193.17 2.29 M69.17 144.93 C105.44 102.82, 142.12 58.58, 193.17 2.29 M74.16 145.29 C113.39 99.23, 154.87 50.02, 198.81 1.9 M74.16 145.29 C115.68 97.91, 154.99 52.02, 198.81 1.9 M79.8 144.9 C117.22 100.44, 155.63 58.46, 203.8 2.26 M79.8 144.9 C119.55 99.87, 159.99 53.61, 203.8 2.26 M84.79 145.26 C114.96 111.31, 144.84 77.07, 209.44 1.86 M84.79 145.26 C130.74 96.04, 174.26 45.02, 209.44 1.86 M90.43 144.86 C119.59 111.08, 146.64 82.67, 214.43 2.22 M90.43 144.86 C137.14 92.65, 182.18 40.05, 214.43 2.22 M95.42 145.22 C134.97 97.52, 174.23 52.21, 220.07 1.83 M95.42 145.22 C121.16 115.94, 147.23 85.62, 220.07 1.83 M101.06 144.83 C128.73 111.86, 158.26 79.88, 225.06 2.19 M101.06 144.83 C127.52 114.11, 154.95 81.91, 225.06 2.19 M106.05 145.19 C132.23 115.04, 157.86 83.38, 230.7 1.79 M106.05 145.19 C133.88 111.98, 162.23 80.52, 230.7 1.79 M111.69 144.79 C135.52 113.89, 160.61 88.04, 235.69 2.15 M111.69 144.79 C155.76 94.05, 201.14 44.16, 235.69 2.15 M116.68 145.15 C149.35 106.9, 185.6 68, 241.33 1.76 M116.68 145.15 C144.46 112.95, 173.88 78.54, 241.33 1.76 M122.32 144.76 C163.47 96.75, 205.64 46.73, 246.32 2.12 M122.32 144.76 C169.21 91.48, 214.25 39.39, 246.32 2.12 M127.31 145.12 C165.46 100.62, 205.52 51.66, 251.96 1.72 M127.31 145.12 C172.07 90.92, 219.78 38.74, 251.96 1.72 M132.95 144.72 C175.8 96.08, 215.01 50.98, 256.95 2.08 M132.95 144.72 C175.65 95.27, 218.21 46.7, 256.95 2.08 M137.94 145.08 C175.05 101.58, 213.52 59.48, 262.59 1.69 M137.94 145.08 C171.08 105.35, 206.42 65.3, 262.59 1.69 M143.58 144.69 C186.3 92.98, 230.97 41.88, 267.58 2.05 M143.58 144.69 C178.59 107.36, 212.3 68.54, 267.58 2.05 M148.57 145.05 C179.15 108.79, 208.33 76.07, 272.56 2.41 M148.57 145.05 C184.5 104.35, 220.01 62.87, 272.56 2.41 M154.21 144.65 C195.56 97.04, 233.34 55.88, 278.21 2.01 M154.21 144.65 C182.1 112.15, 210.74 81.54, 278.21 2.01 M159.2 145.01 C197.45 101.68, 236.26 55.56, 283.19 2.37 M159.2 145.01 C190.06 111.73, 219.71 76.82, 283.19 2.37 M164.19 145.37 C214.12 88.29, 261.36 32.74, 288.84 1.98 M164.19 145.37 C191.17 116.65, 216.4 87.09, 288.84 1.98 M169.83 144.98 C211.86 100.8, 252.07 52.72, 293.82 2.34 M169.83 144.98 C202.9 106.38, 236.88 69.34, 293.82 2.34 M174.82 145.34 C207.63 107.06, 242.55 64.81, 299.47 1.94 M174.82 145.34 C211.8 104.01, 247.25 62.79, 299.47 1.94 M180.46 144.94 C205.47 114.55, 232.3 83.6, 304.45 2.3 M180.46 144.94 C204.45 114.46, 230.06 86.52, 304.45 2.3 M185.45 145.31 C229.35 95.12, 271.86 44.84, 310.1 1.91 M185.45 145.31 C216.37 107.9, 248.61 71.36, 310.1 1.91 M191.09 144.91 C222.55 111.72, 251.85 73.92, 315.08 2.27 M191.09 144.91 C238.55 91.08, 286.27 36.02, 315.08 2.27 M196.08 145.27 C234.29 100.34, 274.54 56.28, 320.73 1.88 M196.08 145.27 C222.42 114.67, 248.24 85.32, 320.73 1.88 M201.72 144.88 C245.02 96.69, 286.1 47.75, 325.71 2.24 M201.72 144.88 C233.01 110.5, 264.89 75.09, 325.71 2.24 M206.71 145.24 C250.09 93.17, 294.8 45.48, 331.36 1.84 M206.71 145.24 C246.95 98.07, 287.81 52.25, 331.36 1.84 M212.35 144.84 C255.02 97.82, 294.21 47.38, 336.34 2.2 M212.35 144.84 C254.78 98.81, 294.78 49.85, 336.34 2.2 M217.34 145.2 C260.68 96.29, 303.76 46.02, 341.99 1.81 M217.34 145.2 C250.78 105.78, 286.51 63.39, 341.99 1.81 M222.98 144.81 C272.96 88.46, 318.86 33.36, 346.97 2.17 M222.98 144.81 C256.46 106.72, 290.95 67.45, 346.97 2.17 M227.97 145.17 C264.79 102.33, 299.75 59.1, 352.62 1.77 M227.97 145.17 C263.52 105.07, 298.61 63.81, 352.62 1.77 M233.61 144.77 C274.12 96.97, 312.37 55.98, 357.6 2.13 M233.61 144.77 C256.24 114.88, 281.92 87.66, 357.6 2.13 M238.6 145.13 C271.28 102.8, 307.76 62.45, 363.25 1.74 M238.6 145.13 C264.06 115.44, 290.29 87.89, 363.25 1.74 M244.24 144.74 C276.08 105.66, 310.55 68.73, 368.23 2.1 M244.24 144.74 C288.01 92.91, 332.32 42.92, 368.23 2.1 M249.23 145.1 C286.85 103.31, 326.12 58.27, 373.88 1.7 M249.23 145.1 C278.25 112.14, 306.08 78.73, 373.88 1.7 M254.87 144.7 C287.46 102.93, 323.86 65.43, 378.86 2.06 M254.87 144.7 C288.68 105.04, 323.05 66.18, 378.86 2.06 M259.86 145.06 C291.28 111.25, 321.61 73.04, 383.85 2.42 M259.86 145.06 C289.7 108.87, 320.7 73.05, 383.85 2.42 M265.5 144.67 C302.2 102.1, 339.7 59.09, 389.49 2.03 M265.5 144.67 C314.09 87.39, 361.48 31.86, 389.49 2.03 M270.49 145.03 C306.79 101.72, 345.32 61.4, 394.48 2.39 M270.49 145.03 C300.53 111.68, 330.35 77.04, 394.48 2.39 M276.13 144.63 C319.2 96.52, 362.81 44.49, 400.12 1.99 M276.13 144.63 C306.01 109.16, 335.36 75.86, 400.12 1.99 M281.12 144.99 C321.76 94.08, 364.67 45.75, 405.11 2.35 M281.12 144.99 C319.22 100.17, 357.51 56.73, 405.11 2.35 M286.1 145.35 C333.71 88.53, 382.13 35.38, 410.75 1.96 M286.1 145.35 C317.78 109.04, 351.73 70.98, 410.75 1.96 M291.75 144.96 C337.19 88.4, 384.3 34.77, 415.74 2.32 M291.75 144.96 C317.91 112.26, 345.28 81.05, 415.74 2.32 M296.73 145.32 C325.68 112.17, 358.06 76.44, 421.38 1.93 M296.73 145.32 C343.84 88.98, 392.35 33.13, 421.38 1.93 M302.38 144.93 C339.95 106.31, 374.33 64.87, 426.37 2.29 M302.38 144.93 C336.03 105.32, 370.06 67.12, 426.37 2.29 M307.36 145.29 C340.16 110, 371.15 72.35, 432.01 1.89 M307.36 145.29 C340.08 109.03, 371.38 70.88, 432.01 1.89 M313.01 144.89 C346.7 110.4, 379.86 70.69, 437 2.25 M313.01 144.89 C342.6 108.31, 373.8 74.9, 437 2.25 M317.99 145.25 C351.16 108.99, 380.33 72.15, 442.64 1.86 M317.99 145.25 C343.7 117.08, 369.38 86.42, 442.64 1.86 M323.64 144.86 C370.25 87.28, 421.75 33.13, 447.63 2.22 M323.64 144.86 C351.37 114.11, 380.44 81.53, 447.63 2.22 M328.62 145.22 C355.29 117.71, 379.5 88.31, 452.62 2.58 M328.62 145.22 C362.76 106.45, 396.24 68.64, 452.62 2.58 M334.27 144.82 C363.38 107.43, 398.48 72.68, 456.95 3.69 M334.27 144.82 C357.91 116.46, 383 87.56, 456.95 3.69 M339.25 145.18 C379.04 101.04, 417.7 54.68, 461.94 4.05 M339.25 145.18 C385.54 91.6, 433.51 37.95, 461.94 4.05 M344.9 144.79 C386.94 95.92, 432.15 44.1, 464.96 6.68 M344.9 144.79 C374.61 108.52, 405.83 74.64, 464.96 6.68 M349.88 145.15 C391 98.05, 435.67 47.17, 469.29 7.79 M349.88 145.15 C395.91 94.65, 441.86 42.78, 469.29 7.79 M355.53 144.75 C385.45 107.18, 418.33 70.28, 471.65 11.17 M355.53 144.75 C400.44 93.54, 444.91 43.39, 471.65 11.17 M360.51 145.11 C401.24 102.6, 437.59 56.72, 474.67 13.79 M360.51 145.11 C405.18 92.61, 450.73 39.5, 474.67 13.79 M366.16 144.72 C400.27 105.26, 433.48 67.04, 475.72 18.68 M366.16 144.72 C393.53 111.8, 421.43 78.48, 475.72 18.68 M371.14 145.08 C393.65 118.74, 414.53 93.08, 477.43 22.82 M371.14 145.08 C402.07 111.49, 430.19 77.53, 477.43 22.82 M376.79 144.68 C398.96 116.96, 423.02 86.8, 479.13 26.95 M376.79 144.68 C405.71 112.24, 431.87 81.46, 479.13 26.95 M381.77 145.04 C402.53 120.66, 424.65 98.55, 479.53 32.59 M381.77 145.04 C415.6 104.73, 447.91 66.04, 479.53 32.59 M387.42 144.65 C421.09 106.74, 452.24 68, 479.27 38.99 M387.42 144.65 C417.09 111.82, 445.22 80.57, 479.27 38.99 M392.4 145.01 C416.08 117.03, 443.83 87.54, 479 45.39 M392.4 145.01 C412.54 120.34, 434.35 95.05, 479 45.39 M397.39 145.37 C418.8 119.7, 440.1 98.63, 479.4 51.03 M397.39 145.37 C425.95 112.73, 456.43 79.45, 479.4 51.03 M403.03 144.98 C425.28 120.65, 446.29 95.66, 479.14 57.43 M403.03 144.98 C425.92 117.76, 450.02 91.73, 479.14 57.43 M408.02 145.34 C425.07 128.2, 439.73 110.85, 479.53 63.07 M408.02 145.34 C430.26 120.38, 450.08 96.27, 479.53 63.07 M413.66 144.94 C435.93 123.3, 454.67 96.07, 479.27 69.47 M413.66 144.94 C439.06 116.88, 463.51 87.09, 479.27 69.47 M418.65 145.3 C434.28 127.3, 448 113.58, 479.66 75.11 M418.65 145.3 C439.53 121.35, 457.99 99.06, 479.66 75.11 M424.29 144.91 C438.64 130.23, 450.19 116.95, 479.4 81.51 M424.29 144.91 C446.16 119.97, 466.98 96.04, 479.4 81.51 M429.28 145.27 C449.48 121.21, 467.73 98.87, 479.8 87.15 M429.28 145.27 C445.11 126.77, 460.67 109.04, 479.8 87.15 M434.92 144.87 C451.5 125.37, 470.64 107.76, 479.54 93.55 M434.92 144.87 C450.33 126.77, 466.57 106.69, 479.54 93.55 M439.91 145.23 C452.13 132.68, 464.37 120.59, 479.93 99.19 M439.91 145.23 C451.04 133.55, 462.09 121.11, 479.93 99.19 M445.55 144.84 C458.79 129.03, 473.86 114.16, 479.67 105.59 M445.55 144.84 C454.8 134.14, 463.69 123.97, 479.67 105.59 M451.2 144.44 C462.63 132.85, 473.14 119.83, 478.1 113.5 M451.2 144.44 C458.17 135.43, 463.94 127.69, 478.1 113.5 M457.5 143.29 C463.11 139.03, 465.29 134.84, 476.52 121.41 M457.5 143.29 C464 136.22, 469.37 130.32, 476.52 121.41 M465.11 140.63 C467.6 137.97, 471.38 132.46, 476.26 127.8 M465.11 140.63 C467.66 137.17, 471.15 133.88, 476.26 127.8" stroke="#b2f2bb" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C142.9 2.12, 251.08 2.36, 445 0 M32 0 C115.5 -0.6, 199.1 -1.14, 445 0 M445 0 C467.37 0.9, 475.89 9.1, 477 32 M445 0 C464.69 1.75, 476.83 12.79, 477 32 M477 32 C475.75 51.18, 476.7 69.4, 477 113 M477 32 C477.02 52.65, 476.92 71.83, 477 113 M477 113 C475.11 133.14, 465.24 146.78, 445 145 M477 113 C478.86 132.08, 465.62 144.87, 445 145 M445 145 C290.97 146.31, 137.09 146.13, 32 145 M445 145 C337.93 143.89, 230.99 144.81, 32 145 M32 145 C9.61 145.84, -0.8 134.99, 0 113 M32 145 C11.35 145.42, 1.86 135.37, 0 113 M0 113 C1.15 86.4, 0.56 57.51, 0 32 M0 113 C-0.77 88.92, -1.37 66.73, 0 32 M0 32 C1.21 9.29, 10.51 -1.67, 32 0 M0 32 C2.23 9.98, 9.94 -0.68, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(815.1484375 399.59375) rotate(0 220.3125 67.19999999999999)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">node:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">  directory:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    name: 22222222222222222222222222222222-test</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    digest: &lt;directory-with-keep-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    size: 1</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">references: []</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">narinfo: โ€ฆ</text></g><g stroke-linecap="round"><g transform="translate(935.83984375 297.9609375) rotate(0 -127.17742444525584 -6.3835650656164376)"><path d="M-0.07 0.59 C-42.41 -1.51, -211.36 -11.21, -253.47 -13.35 M-1.57 -0.15 C-44.1 -1.99, -212.39 -9.54, -254.28 -11.88" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(935.83984375 297.9609375) rotate(0 -127.17742444525584 -6.3835650656164376)"><path d="M-224.64 -20.31 C-235.83 -15.98, -244.05 -15.65, -255.88 -12.33 M-225.16 -19.94 C-234.34 -18.84, -244.43 -15.58, -254.5 -12.11" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(935.83984375 297.9609375) rotate(0 -127.17742444525584 -6.3835650656164376)"><path d="M-225.65 0.18 C-236.36 -2.61, -244.23 -9.41, -255.88 -12.33 M-226.18 0.55 C-234.92 -4.92, -244.69 -8.23, -254.5 -12.11" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(934.54296875 464.29296875) rotate(0 -122.96248760393826 -133.71583716869355)"><path d="M-0.84 -0.49 C-41.65 -45.05, -205 -222.75, -245.8 -266.94 M0.91 -1.8 C-39.98 -46.25, -205.61 -221.44, -246.84 -265.75" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(934.54296875 464.29296875) rotate(0 -122.96248760393826 -133.71583716869355)"><path d="M-220.88 -252.52 C-227.08 -255.42, -230.79 -256.62, -246.15 -266.29 M-219.26 -252.47 C-226.67 -255.03, -232.47 -258.86, -246.62 -266.61" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(934.54296875 464.29296875) rotate(0 -122.96248760393826 -133.71583716869355)"><path d="M-235.85 -238.47 C-238.83 -244.34, -239.26 -248.61, -246.15 -266.29 M-234.23 -238.42 C-238.04 -244.21, -240.25 -251.41, -246.62 -266.61" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round" transform="translate(808.1090530561523 569.6183182417908) rotate(0 238.5 82)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C6.81 13.38, 14.57 7.61, 18.1 2.48 M3.66 19.09 C7.86 14.77, 10.94 9.38, 18.1 2.48 M2.75 26.24 C8.42 17.74, 14.61 10.75, 24.4 1.33 M2.75 26.24 C7.35 20.79, 11.06 16.63, 24.4 1.33 M2.48 32.64 C8.41 23.44, 17.32 15.14, 30.7 0.18 M2.48 32.64 C8.74 25.77, 13.03 20.51, 30.7 0.18 M2.88 38.28 C8.15 29.11, 19.49 21.57, 34.37 2.05 M2.88 38.28 C13.52 25.23, 25.55 13.16, 34.37 2.05 M2.62 44.68 C16 33.11, 26.07 16.01, 39.36 2.41 M2.62 44.68 C14.25 32.19, 24.76 18.26, 39.36 2.41 M2.36 51.07 C18.74 34.45, 31.43 18.56, 45 2.02 M2.36 51.07 C17.04 34.98, 31.27 17.88, 45 2.02 M2.75 56.72 C12.45 43.26, 26.24 32.38, 49.99 2.38 M2.75 56.72 C13.08 44.38, 23.25 35.02, 49.99 2.38 M2.49 63.11 C13.22 50.48, 25.3 38, 55.63 1.98 M2.49 63.11 C20.59 44.59, 36.16 24.18, 55.63 1.98 M2.88 68.76 C22.9 48.96, 39.83 27.4, 60.62 2.34 M2.88 68.76 C24.33 43.11, 46.62 19.91, 60.62 2.34 M2.62 75.16 C24.58 48.11, 49.17 22.73, 66.26 1.95 M2.62 75.16 C17.85 56.46, 36.34 37, 66.26 1.95 M2.36 81.55 C22.67 56.14, 44.58 32.75, 71.25 2.31 M2.36 81.55 C28.06 52.26, 52.4 23.27, 71.25 2.31 M2.76 87.2 C23.08 61.22, 45.46 37.01, 76.89 1.91 M2.76 87.2 C19.7 68.52, 35.9 49.32, 76.89 1.91 M2.49 93.59 C20.89 69.28, 40.59 49.24, 81.88 2.27 M2.49 93.59 C22.48 70.61, 42.67 48.73, 81.88 2.27 M2.89 99.24 C34.82 63.55, 63.23 31.57, 87.52 1.88 M2.89 99.24 C20.63 78.39, 39.49 56.84, 87.52 1.88 M2.63 105.64 C35.22 68.03, 69.69 27.76, 92.51 2.24 M2.63 105.64 C33.18 70.17, 62.24 35.99, 92.51 2.24 M2.37 112.03 C26.3 85.12, 50.21 57.23, 98.15 1.85 M2.37 112.03 C36.74 71.78, 71.12 32.15, 98.15 1.85 M2.76 117.68 C37.79 74.87, 77.39 32.27, 103.14 2.21 M2.76 117.68 C28.95 87.13, 55.3 54.97, 103.14 2.21 M2.5 124.07 C29.71 93.79, 56.39 67.33, 108.78 1.81 M2.5 124.07 C39.16 80.77, 76.22 37.93, 108.78 1.81 M2.89 129.72 C46.1 83.46, 86.14 32.26, 113.77 2.17 M2.89 129.72 C38.33 90.23, 73.3 50.35, 113.77 2.17 M2.63 136.11 C32.42 101.51, 64.36 66.19, 119.41 1.78 M2.63 136.11 C42.9 88.38, 84.51 40.74, 119.41 1.78 M3.03 141.76 C47.01 92.79, 88.14 44.94, 124.4 2.14 M3.03 141.76 C36.31 103.57, 71.35 65.73, 124.4 2.14 M4.73 145.89 C51.61 90.8, 99.17 38.12, 130.04 1.74 M4.73 145.89 C47.88 98.67, 90.06 49.91, 130.04 1.74 M5.13 151.53 C50.72 98.34, 95.33 50.22, 135.03 2.1 M5.13 151.53 C54.14 93.61, 105.46 35.23, 135.03 2.1 M7.49 154.91 C46.02 110.99, 81.47 70.85, 140.67 1.71 M7.49 154.91 C52.42 103.68, 96.67 51.91, 140.67 1.71 M10.51 157.54 C60.56 98.74, 112.31 43.05, 145.66 2.07 M10.51 157.54 C41.53 122.75, 73.01 84.79, 145.66 2.07 M13.53 160.16 C39.75 129.1, 69.49 95.86, 150.65 2.43 M13.53 160.16 C66.46 100.75, 117.42 41, 150.65 2.43 M17.2 162.03 C70.82 99.54, 124.51 39.14, 156.29 2.03 M17.2 162.03 C45.6 127.78, 75.59 94.57, 156.29 2.03 M20.88 163.9 C56.65 126.92, 91.29 87.42, 161.28 2.39 M20.88 163.9 C71.96 105.59, 125.36 44.09, 161.28 2.39 M25.21 165.02 C65.92 117.85, 106.95 72.45, 166.92 2 M25.21 165.02 C60.62 122.61, 96.68 82.03, 166.92 2 M30.2 165.38 C61.83 128.51, 95.39 92.16, 171.91 2.36 M30.2 165.38 C61.34 132.24, 90.7 98.68, 171.91 2.36 M34.53 166.49 C75.36 119.62, 116.91 72.06, 177.55 1.96 M34.53 166.49 C82.24 112.82, 128.51 59.11, 177.55 1.96 M40.17 166.1 C71.9 131.05, 100.75 95.32, 182.54 2.32 M40.17 166.1 C75.59 123.88, 112.97 81.96, 182.54 2.32 M45.16 166.46 C101.91 101.55, 158.7 35.39, 188.18 1.93 M45.16 166.46 C92.93 108.21, 142.16 51.42, 188.18 1.93 M50.8 166.06 C83.66 126.11, 118.37 86.9, 193.17 2.29 M50.8 166.06 C90.57 121.95, 130.52 76.87, 193.17 2.29 M55.79 166.42 C98.61 114.56, 145.49 65.61, 198.81 1.9 M55.79 166.42 C89.92 128.89, 122.15 90.21, 198.81 1.9 M61.43 166.03 C108.14 112.25, 154.28 57.71, 203.8 2.26 M61.43 166.03 C102.94 116.29, 146.47 66.47, 203.8 2.26 M66.42 166.39 C110.46 115.14, 155.39 64.89, 209.44 1.86 M66.42 166.39 C103.96 123.93, 141.14 82.62, 209.44 1.86 M72.06 165.99 C116.55 115.54, 157.91 65.86, 214.43 2.22 M72.06 165.99 C122.43 107.95, 174.9 47.56, 214.43 2.22 M77.05 166.35 C130.59 105.53, 183.67 43.12, 220.07 1.83 M77.05 166.35 C125.26 111.87, 172.24 58.31, 220.07 1.83 M82.69 165.96 C137.84 105.94, 191.52 41.79, 225.06 2.19 M82.69 165.96 C133.74 107.43, 183.36 50.36, 225.06 2.19 M87.68 166.32 C128.09 119.78, 167.53 73.55, 230.7 1.79 M87.68 166.32 C125.69 123.08, 162.79 79.78, 230.7 1.79 M92.67 166.68 C142.83 109.77, 193.44 51.39, 235.69 2.15 M92.67 166.68 C144.44 108.24, 195.97 47.04, 235.69 2.15 M98.31 166.28 C138.71 115.99, 180.28 69.93, 241.33 1.76 M98.31 166.28 C147.53 110.62, 196.35 53.63, 241.33 1.76 M103.3 166.64 C137.9 125.59, 173.89 87.87, 246.32 2.12 M103.3 166.64 C156.08 106.69, 208.6 46.04, 246.32 2.12 M108.94 166.25 C148.42 122.93, 186.31 81.16, 251.96 1.72 M108.94 166.25 C161 105.15, 212.81 45.73, 251.96 1.72 M113.93 166.61 C141.79 133.21, 172.21 98.82, 256.95 2.08 M113.93 166.61 C156.91 116.88, 202.51 64.75, 256.95 2.08 M119.57 166.21 C168.54 109.97, 219.75 52.97, 262.59 1.69 M119.57 166.21 C167.8 108.87, 219.02 51.26, 262.59 1.69 M124.56 166.57 C161.75 123.88, 199.53 80.79, 267.58 2.05 M124.56 166.57 C156.23 128.01, 190.3 88.85, 267.58 2.05 M130.2 166.18 C169.64 122.3, 211.11 75.14, 272.56 2.41 M130.2 166.18 C168.9 120.31, 208.25 76.02, 272.56 2.41 M135.19 166.54 C184.84 111.09, 234.19 52.53, 278.21 2.01 M135.19 166.54 C187.36 108.27, 238.69 50.75, 278.21 2.01 M140.83 166.15 C183.77 117.65, 225.92 69.25, 283.19 2.37 M140.83 166.15 C187.53 113.68, 233.77 60.45, 283.19 2.37 M145.82 166.51 C182.78 122.72, 219.21 80.97, 288.84 1.98 M145.82 166.51 C200.67 105.62, 254.4 42.05, 288.84 1.98 M151.46 166.11 C182.26 131.38, 212.83 96.05, 293.82 2.34 M151.46 166.11 C186.45 126.29, 219.87 88.44, 293.82 2.34 M156.45 166.47 C204.97 108.71, 255.45 52.85, 299.47 1.94 M156.45 166.47 C211.25 105.36, 265.8 42.21, 299.47 1.94 M162.09 166.08 C213.47 106.17, 266.91 43.59, 304.45 2.3 M162.09 166.08 C216.39 105.62, 269.56 44.48, 304.45 2.3 M167.08 166.44 C219.65 106.64, 271.52 49.11, 310.1 1.91 M167.08 166.44 C214.13 111.77, 261.4 58.06, 310.1 1.91 M172.72 166.04 C208.84 124.53, 247 83.41, 315.08 2.27 M172.72 166.04 C205.15 129.49, 236.42 90.2, 315.08 2.27 M177.71 166.4 C209.21 130.02, 243.57 90.18, 320.73 1.88 M177.71 166.4 C231.87 105.57, 287.31 42.39, 320.73 1.88 M183.35 166.01 C220.85 120.46, 259.07 75.83, 325.71 2.24 M183.35 166.01 C230.79 111.91, 277 57.33, 325.71 2.24 M188.34 166.37 C233.45 112.01, 280.56 61.66, 331.36 1.84 M188.34 166.37 C240.12 108.18, 290.83 49.09, 331.36 1.84 M193.98 165.97 C226.25 131.55, 256.45 92.17, 336.34 2.2 M193.98 165.97 C244.44 109.17, 294.38 52.76, 336.34 2.2 M198.97 166.33 C247.81 112.21, 292.15 60.33, 341.99 1.81 M198.97 166.33 C239.08 119.2, 279.09 71.05, 341.99 1.81 M203.95 166.69 C258.46 101.41, 315.11 40.21, 346.97 2.17 M203.95 166.69 C257.24 106.78, 308.75 46.24, 346.97 2.17 M209.6 166.3 C264.45 102.42, 321.03 37.17, 352.62 1.77 M209.6 166.3 C247.96 124.81, 283.55 84.13, 352.62 1.77 M214.58 166.66 C251.99 125.65, 288.37 82.49, 357.6 2.13 M214.58 166.66 C245.51 132.68, 277.5 95.94, 357.6 2.13 M220.23 166.26 C250.91 130.54, 281.47 95.54, 363.25 1.74 M220.23 166.26 C270.46 109.08, 319.88 50.52, 363.25 1.74 M225.21 166.62 C276.61 104.17, 331.06 44.88, 368.23 2.1 M225.21 166.62 C275.98 109.32, 326.13 50.94, 368.23 2.1 M230.86 166.23 C272.47 114.76, 315.56 64.33, 373.88 1.7 M230.86 166.23 C278.29 112.46, 325.09 57.98, 373.88 1.7 M235.84 166.59 C282.63 113.09, 329.86 58.03, 378.86 2.06 M235.84 166.59 C289.28 107.3, 342.41 45.74, 378.86 2.06 M241.49 166.2 C289.5 107.98, 340.13 49.49, 383.85 2.42 M241.49 166.2 C291 108.5, 340.73 52, 383.85 2.42 M246.47 166.56 C299.38 107.83, 352.11 46.7, 389.49 2.03 M246.47 166.56 C290.21 118.16, 331.91 67.65, 389.49 2.03 M252.12 166.16 C287.75 124.26, 324.97 81.73, 394.48 2.39 M252.12 166.16 C285.46 128.24, 318.01 91.66, 394.48 2.39 M257.1 166.52 C303.62 111.42, 349.89 60.83, 400.12 1.99 M257.1 166.52 C297.83 119.09, 339.11 70.77, 400.12 1.99 M262.75 166.13 C302.79 119.22, 343.84 71.97, 405.11 2.35 M262.75 166.13 C318.14 102.81, 374.01 39.26, 405.11 2.35 M267.73 166.49 C316.44 106.41, 370.98 48.7, 410.75 1.96 M267.73 166.49 C314.68 110.39, 365.02 54.21, 410.75 1.96 M273.38 166.09 C319.98 112.15, 368.66 57.51, 415.74 2.32 M273.38 166.09 C318.87 114.27, 364 62.75, 415.74 2.32 M278.36 166.45 C334.32 101.65, 390.55 37.39, 421.38 1.93 M278.36 166.45 C333.03 105.94, 385.98 44.74, 421.38 1.93 M284.01 166.06 C317.55 130.16, 348.17 93.82, 426.37 2.29 M284.01 166.06 C326.17 119.01, 367.32 70.57, 426.37 2.29 M288.99 166.42 C322.48 126.3, 360.56 85.92, 432.01 1.89 M288.99 166.42 C335.34 112.09, 384.1 55.77, 432.01 1.89 M294.64 166.02 C329.61 122.3, 366.47 79.8, 437 2.25 M294.64 166.02 C335.35 119.83, 373.64 74.32, 437 2.25 M299.62 166.38 C347.99 112.19, 397.82 52.47, 442.64 1.86 M299.62 166.38 C346.57 111.86, 394.7 58.56, 442.64 1.86 M305.27 165.99 C340.79 125.39, 378.2 81.4, 447.63 2.22 M305.27 165.99 C336.56 132.13, 367.18 97.05, 447.63 2.22 M310.25 166.35 C364.09 102.48, 420.3 41.58, 452.62 2.58 M310.25 166.35 C350.18 121.57, 389.7 76.26, 452.62 2.58 M315.9 165.95 C355.72 120.42, 398.24 73.94, 456.95 3.69 M315.9 165.95 C361.7 110.74, 408.98 56.77, 456.95 3.69 M320.88 166.31 C359.3 121.21, 397.93 79.49, 461.94 4.05 M320.88 166.31 C351.82 129.41, 382.91 93.48, 461.94 4.05 M325.87 166.67 C377.05 109.95, 427.19 53.77, 464.96 6.68 M325.87 166.67 C359.4 128.68, 392.04 90.92, 464.96 6.68 M331.51 166.28 C388.57 102.78, 442.05 38.29, 469.29 7.79 M331.51 166.28 C365.47 127.01, 402.68 86.23, 469.29 7.79 M336.5 166.64 C378.68 118.85, 420.7 69.51, 471.65 11.17 M336.5 166.64 C367.77 133.94, 395.41 99.18, 471.65 11.17 M342.14 166.25 C389.11 113.09, 435.96 59.54, 474.67 13.79 M342.14 166.25 C370.01 136.08, 397.01 102.54, 474.67 13.79 M347.13 166.61 C393.05 113.37, 435.84 64.96, 475.72 18.68 M347.13 166.61 C393.9 112.62, 438.68 61.59, 475.72 18.68 M352.77 166.21 C380.68 136.97, 404.58 109.35, 477.43 22.82 M352.77 166.21 C393.66 118.4, 432.23 72.18, 477.43 22.82 M357.76 166.57 C393.89 123.75, 430.73 84.47, 479.13 26.95 M357.76 166.57 C407.4 110.77, 454.79 55.34, 479.13 26.95 M363.4 166.18 C388.23 138.89, 413.94 108.18, 479.53 32.59 M363.4 166.18 C409.58 114.51, 453.68 63.43, 479.53 32.59 M368.39 166.54 C408.72 121.43, 446.29 77.48, 479.27 38.99 M368.39 166.54 C401.91 127.96, 434.91 89.62, 479.27 38.99 M374.03 166.14 C412.5 122.98, 449.36 75.75, 479 45.39 M374.03 166.14 C396.44 139.53, 422.02 110.62, 479 45.39 M379.02 166.5 C409.99 128.35, 442.35 96.32, 479.4 51.03 M379.02 166.5 C406.86 135.7, 432.68 106.1, 479.4 51.03 M384.66 166.11 C402.24 145.5, 422.81 122.74, 479.14 57.43 M384.66 166.11 C422.18 124.54, 458.93 82.58, 479.14 57.43 M389.65 166.47 C419.28 131.51, 452.05 96.59, 479.53 63.07 M389.65 166.47 C419.89 132.79, 448.41 99.82, 479.53 63.07 M395.29 166.07 C426.82 132.48, 455.45 96.46, 479.27 69.47 M395.29 166.07 C416.92 140.62, 438.2 116.72, 479.27 69.47 M400.28 166.43 C420.47 143.05, 442.61 120.52, 479.66 75.11 M400.28 166.43 C426.13 136.5, 452.84 104.75, 479.66 75.11 M405.92 166.04 C427.91 140.97, 448.1 117.36, 479.4 81.51 M405.92 166.04 C431.73 136.16, 458.33 107.47, 479.4 81.51 M410.91 166.4 C439.08 133.08, 467.49 104.88, 479.8 87.15 M410.91 166.4 C439.78 134.31, 465.58 102.73, 479.8 87.15 M416.55 166 C435.71 144.16, 456.89 119.23, 479.54 93.55 M416.55 166 C429.32 151.02, 443.37 135.34, 479.54 93.55 M421.54 166.36 C441.2 143.55, 460.71 119.18, 479.28 99.95 M421.54 166.36 C436.83 150.09, 450.82 132.4, 479.28 99.95 M427.18 165.97 C444.74 144.68, 466.03 122.82, 479.67 105.59 M427.18 165.97 C445.95 144.28, 463.23 123.49, 479.67 105.59 M432.17 166.33 C443.24 153.78, 457.62 140.63, 479.41 111.99 M432.17 166.33 C444.12 153.05, 454.84 140.71, 479.41 111.99 M437.16 166.69 C445.39 157.04, 457.46 143.05, 479.8 117.63 M437.16 166.69 C448.39 153.72, 458.12 143.15, 479.8 117.63 M442.8 166.29 C452.3 154.17, 462.42 144.1, 479.54 124.03 M442.8 166.29 C455.94 150.05, 469.26 134.36, 479.54 124.03 M447.79 166.65 C456.94 155.72, 466.27 142.82, 477.31 132.69 M447.79 166.65 C458.81 155.08, 470.45 141.43, 477.31 132.69 M454.09 165.51 C459.1 157.63, 466.97 149.2, 476.39 139.85 M454.09 165.51 C460.38 158.66, 465.45 152.71, 476.39 139.85 M464.32 159.83 C469.65 156.05, 473.41 148.37, 475.48 147 M464.32 159.83 C466.17 157.25, 468.66 154.88, 475.48 147" stroke="#b2f2bb" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C179.89 -1.74, 328.07 0.08, 445 0 M32 0 C192.02 0.21, 350.62 -0.17, 445 0 M445 0 C465.52 0.25, 477.14 11.05, 477 32 M445 0 C467.31 -0.47, 475.37 11.81, 477 32 M477 32 C475.83 66.77, 477.32 98.5, 477 132 M477 32 C477.31 66.84, 477.55 102.41, 477 132 M477 132 C477.4 152.02, 464.97 162.99, 445 164 M477 132 C478.29 151.95, 467.37 166.29, 445 164 M445 164 C309.65 164.67, 175.86 163.51, 32 164 M445 164 C339.17 163.73, 232.87 164.46, 32 164 M32 164 C9.7 164.07, -1.59 152.38, 0 132 M32 164 C9.56 164.33, 1.77 155.2, 0 132 M0 132 C1.13 92.98, -0.18 57.12, 0 32 M0 132 C0.33 111.2, 1.53 90.02, 0 32 M0 32 C-1.62 11.04, 10.67 1.54, 32 0 M0 32 C0.37 11.97, 12.23 -2.15, 32 0" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(813.1090530561523 574.6183182417908) rotate(0 220.3125 76.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">node:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">  file:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    name: 33333333333333333333333333333333-test</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    digest: &lt;blob-a-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    size: 3</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">    executable: false</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">references: []</text><text x="0" y="134.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1e1e1e" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">narinfo: โ€ฆ</text></g><g stroke-linecap="round"><g transform="translate(942.4901398439579 641.0079592022902) rotate(0 -391.38489601802087 -282.46266052120785)"><path d="M-0.12 0.36 C-130.24 -93.43, -651.31 -469.22, -781.67 -563.32 M-1.64 -0.49 C-131.84 -94.62, -652.36 -471.5, -782.65 -565.29" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(942.4901398439579 641.0079592022902) rotate(0 -391.38489601802087 -282.46266052120785)"><path d="M-753.18 -557.22 C-759.58 -560.05, -767.44 -560.17, -782.06 -566.37 M-754.28 -557.06 C-760.35 -558.69, -767.09 -560.3, -781.67 -564.75" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(942.4901398439579 641.0079592022902) rotate(0 -391.38489601802087 -282.46266052120785)"><path d="M-765.2 -540.58 C-768.89 -547.05, -774.11 -550.81, -782.06 -566.37 M-766.3 -540.42 C-769.51 -545.96, -773.48 -551.41, -781.67 -564.75" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask></svg>
\ No newline at end of file
diff --git a/users/flokli/presentations/2023-09-09-nixcon-tvix/tvixbolt.webm b/users/flokli/presentations/2023-09-09-nixcon-tvix/tvixbolt.webm
new file mode 100644
index 0000000000..69bd20f193
--- /dev/null
+++ b/users/flokli/presentations/2023-09-09-nixcon-tvix/tvixbolt.webm
Binary files differdiff --git a/users/flokli/presentations/2023-09-13-asg-tvix-store/default.nix b/users/flokli/presentations/2023-09-13-asg-tvix-store/default.nix
new file mode 100644
index 0000000000..840f21de81
--- /dev/null
+++ b/users/flokli/presentations/2023-09-13-asg-tvix-store/default.nix
@@ -0,0 +1,32 @@
+{ depot, pkgs, ... }:
+
+let
+  inherit (pkgs)
+    fontconfig qrencode runCommand stdenv;
+  mkQr = url: runCommand "qrcode.png" { } ''
+    ${qrencode}/bin/qrencode -o $out -t SVG -s 5 \
+      --background=fafafa \
+      --foreground=000000 \
+      ${url}
+  '';
+in
+stdenv.mkDerivation {
+  name = "2023-asg-tvix-store";
+  src = ./.;
+
+  FONTCONFIG_FILE = pkgs.makeFontsConf {
+    fontDirectories = with pkgs; [ jetbrains-mono fira fira-code fira-mono lato ];
+  };
+
+  nativeBuildInputs = [ pkgs.reveal-md pkgs.graphviz ];
+
+  buildPhase = ''
+    cp ${depot.tvix.logo}/logo.png tvix-logo.png
+    cp ${mkQr "https://flokli.de"} qrcode-flokli.svg
+    cp ${mkQr "https://tvix.dev"} qrcode-tvix.svg
+
+    mkdir -p $out
+    cp tvix-store-graph-blob-directory.svg $out/
+    reveal-md --static $out presentation.md
+  '';
+}
diff --git a/users/flokli/presentations/2023-09-13-asg-tvix-store/presentation.md b/users/flokli/presentations/2023-09-13-asg-tvix-store/presentation.md
new file mode 100644
index 0000000000..978934f9a4
--- /dev/null
+++ b/users/flokli/presentations/2023-09-13-asg-tvix-store/presentation.md
@@ -0,0 +1,138 @@
+---
+author:
+- Florian Klink
+date: 2023-09-09
+title: "tvix-store: A content-addressed file system and sync protocol"
+theme: moon
+revealOptions:
+  transition: 'fade'
+---
+
+## tvix-store
+### A content-addressed file system and sync protocol
+
+2023-09-13
+
+Florian Klink / flokli
+
+---
+
+## Whoami
+
+- <!-- .element: class="fragment" -->
+  flokli
+- <!-- .element: class="fragment" -->
+  Nix/NixOS contributor
+  - maintain systemd, nss and more low-level stuff there
+- <!-- .element: class="fragment" -->
+  Freelance Nix/DevOps consultant
+
+Note: more Kubernetes/DevOps exposure with work
+
+---
+
+## What is tvix-store?
+- <!-- .element: class="fragment" -->
+  A new implementation of a content-addressed "storage system"
+  - <!-- .element: class="fragment" -->
+    part of the Tvix Project, a (WIP) reimplementation of Nix and auxillary components in Rust
+  - <!-- .element: class="fragment" -->
+    Storage model: think about git trees and its Merkle DAGโ€ฆ
+  - <!-- .element: class="fragment" -->
+    โ€ฆ but with nicer wire format (`.proto`) and hash function (blake3)
+
+---
+
+## Storage model
+- <!-- .element: class="fragment" -->
+  Once you know the root: everything else is content-addressed
+   - <!-- .element: class="fragment" -->
+     No timestamps, no uid/gid, no xattrs, only one way to represent the same tree
+- <!-- .element: class="fragment" -->
+  Automatic dedup of identical subtrees in different file system trees
+- <!-- .element: class="fragment" -->
+  Automatic dedup of identical blobs (and you can do more chunking underneath too)
+
+---
+
+## Storage model (cont.)
+- <!-- .element: class="fragment" -->
+  Granular seekable access into blobs
+- <!-- .element: class="fragment" -->
+  verified streaming thanks to BLAKE3 and Bao, faulty data is detected early on
+- <!-- .element: class="fragment" -->
+  Everything below can be retrieved from anyone without having to trust (P2P substitution, CDNs, โ€ฆ)
+
+---
+
+## Usecases
+- <!-- .element: class="fragment" -->
+  File system tree delivery
+- <!-- .element: class="fragment" -->
+  Container image delivery
+- <!-- .element: class="fragment" -->
+  Backing store for VCS
+- <!-- .element: class="fragment" -->
+  Granular access into large datasets
+
+---
+
+## Status
+- <!-- .element: class="fragment" -->
+  In-memory backend, a local K/V backend (Sled)
+- <!-- .element: class="fragment" -->
+  FUSE filesystem
+- <!-- .element: class="fragment" -->
+  A gRPC API to transfer things, bindings for golang and rust
+- <!-- .element: class="fragment" -->
+  some object storage backends in development (GCS, NATS)
+- <!-- .element: class="fragment" -->
+  FUTUREWORK: more storage backends / store composition / in-kernel module?
+
+Notes: of course you can use your own network protocol too, like HTTP CAS or iroh....plug different stores together to represent caches, blobfs
+
+---
+
+## Contributing
+
+- <!-- .element: class="fragment" -->
+  Join the IRC channel (`#tvl` on `hackint`), bridged to Matrix and XMPP
+- <!-- .element: class="fragment" -->
+  Check our issue tracker
+- <!-- .element: class="fragment" -->
+  Try to use it and tell us how you broke it!
+
+Note: if this sounds useful to you, reach out!
+
+---
+
+# Thanks!
+
+<style>
+.container{
+    display: flex;
+}
+.col{
+    flex: 1;
+}
+</style>
+
+<div class="container">
+
+<div class="col">
+Florian Klink / <a href="https://flokli.de">flokli.de</a><br />
+<img src="qrcode-flokli.svg" />
+</div>
+
+<div class="col">
+Tvix / <a href="https://tvix.dev">tvix.dev</a><br />
+<img src="qrcode-tvix.svg" />
+</div>
+
+</div>
+
+---
+
+## Structure
+
+[tvix-store graph](tvix-store-graph-blob-directory.svg)
diff --git a/users/flokli/presentations/2023-09-13-asg-tvix-store/tvix-store-graph-blob-directory.svg b/users/flokli/presentations/2023-09-13-asg-tvix-store/tvix-store-graph-blob-directory.svg
new file mode 100644
index 0000000000..2c87350d5b
--- /dev/null
+++ b/users/flokli/presentations/2023-09-13-asg-tvix-store/tvix-store-graph-blob-directory.svg
@@ -0,0 +1,17 @@
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 691.01171875 836.19140625" width="691.01171875" height="836.19140625">
+  <!-- svg-source:excalidraw -->
+  
+  <defs>
+    <style class="style-fonts">
+      @font-face {
+        font-family: "Virgil";
+        src: url("https://excalidraw.com/Virgil.woff2");
+      }
+      @font-face {
+        font-family: "Cascadia";
+        src: url("https://excalidraw.com/Cascadia.woff2");
+      }
+    </style>
+    
+  </defs>
+  <rect x="0" y="0" width="691.01171875" height="836.19140625" fill="#ffffff"></rect><g stroke-linecap="round" transform="translate(10 64.05078125) rotate(0 71.10546875 23.685546875)"><path d="M3.15 2.74 C3.15 2.74, 3.15 2.74, 3.15 2.74 M3.15 2.74 C3.15 2.74, 3.15 2.74, 3.15 2.74 M1.58 10.65 C3.55 6.56, 7.08 5.83, 10.11 0.84 M1.58 10.65 C4.54 7.52, 5.83 5.33, 10.11 0.84 M1.97 16.29 C5.97 12.52, 7.97 9.89, 14.44 1.95 M1.97 16.29 C5.39 12.84, 8.96 8.01, 14.44 1.95 M1.71 22.69 C5.44 18.07, 11.5 12.49, 19.43 2.31 M1.71 22.69 C6.47 16.78, 11.69 12.31, 19.43 2.31 M1.45 29.09 C9.97 18.06, 21.19 8.77, 25.07 1.92 M1.45 29.09 C9.61 17.76, 19.35 7.56, 25.07 1.92 M1.85 34.73 C6.66 29.67, 14.83 20.48, 30.06 2.28 M1.85 34.73 C8.29 27.25, 17.05 18.03, 30.06 2.28 M2.24 40.37 C10.77 28.82, 20.92 18.16, 35.7 1.88 M2.24 40.37 C14.65 26.46, 25.75 13.13, 35.7 1.88 M3.29 45.26 C19.71 28.3, 32.79 9.79, 40.69 2.24 M3.29 45.26 C14.19 32.96, 23.82 22.41, 40.69 2.24 M6.97 47.13 C21.76 30.34, 38.17 12.66, 45.67 2.6 M6.97 47.13 C19.37 32.8, 32.58 18.66, 45.67 2.6 M12.61 46.74 C23.7 34.53, 32.71 23.85, 51.32 2.21 M12.61 46.74 C23.04 33.52, 34.64 21.81, 51.32 2.21 M17.6 47.1 C32.92 32.2, 44.82 17.61, 56.3 2.57 M17.6 47.1 C26.86 36.54, 34.43 27.02, 56.3 2.57 M22.58 47.46 C36.55 31.33, 51.38 11.55, 61.95 2.17 M22.58 47.46 C35.33 33.95, 46.66 20.15, 61.95 2.17 M28.23 47.06 C40.8 33.63, 55.34 16.98, 66.93 2.53 M28.23 47.06 C36.54 38.37, 43.25 29.02, 66.93 2.53 M33.21 47.42 C48.21 29.57, 61.74 14.8, 72.58 2.14 M33.21 47.42 C44.03 35.16, 55.8 21.76, 72.58 2.14 M38.86 47.03 C49.09 34.45, 58.15 26.51, 77.56 2.5 M38.86 47.03 C53.73 30.24, 67.18 15.29, 77.56 2.5 M43.84 47.39 C57.75 33, 73.31 14.12, 82.55 2.86 M43.84 47.39 C53.3 37.24, 61.17 27.55, 82.55 2.86 M49.49 46.99 C62.68 35.47, 70.45 21.6, 88.19 2.46 M49.49 46.99 C61.84 34.86, 73.11 21.46, 88.19 2.46 M54.47 47.35 C61.1 38.29, 69.25 27.83, 93.18 2.82 M54.47 47.35 C66.26 32.61, 76.99 20.02, 93.18 2.82 M60.12 46.96 C73.27 30.81, 84.64 16.32, 98.82 2.43 M60.12 46.96 C69.94 34.83, 80.66 21.92, 98.82 2.43 M65.1 47.32 C73.48 35.83, 86.65 26.47, 103.81 2.79 M65.1 47.32 C78.64 32.14, 91.97 18.02, 103.81 2.79 M70.09 47.68 C85.6 30.67, 98.3 12.05, 109.45 2.4 M70.09 47.68 C81.34 34.02, 94.32 21.42, 109.45 2.4 M75.73 47.28 C89.43 31.61, 102.84 18.22, 114.44 2.76 M75.73 47.28 C85.67 35.55, 96.35 22.65, 114.44 2.76 M80.72 47.64 C91.93 34.98, 103.63 17.52, 119.43 3.12 M80.72 47.64 C90.36 36.8, 98.88 24.6, 119.43 3.12 M86.36 47.25 C101.83 29.55, 117.32 12.7, 125.07 2.72 M86.36 47.25 C99.66 32.13, 111.08 17.18, 125.07 2.72 M91.35 47.61 C104.35 33.22, 117.8 19.95, 130.06 3.08 M91.35 47.61 C103.56 34.45, 114.2 20.27, 130.06 3.08 M96.99 47.21 C103.59 38.71, 113.94 28.96, 136.36 1.93 M96.99 47.21 C106.26 36.64, 117.24 24.44, 136.36 1.93 M101.98 47.57 C111.9 34.49, 126.09 20.88, 140.03 3.8 M101.98 47.57 C114.1 33.1, 128.09 16.65, 140.03 3.8 M106.97 47.93 C114.85 39.44, 121.62 28.8, 141.74 7.93 M106.97 47.93 C117.52 37.38, 125.94 26.56, 141.74 7.93 M112.61 47.54 C120.79 38.54, 126.18 28.87, 144.1 11.31 M112.61 47.54 C125.62 33.32, 137.05 19.84, 144.1 11.31 M117.6 47.9 C122.32 42.89, 130.44 32.85, 144.5 16.96 M117.6 47.9 C126.99 36.5, 135.5 27.33, 144.5 16.96 M123.24 47.51 C127.95 43.64, 132.17 36.33, 144.23 23.35 M123.24 47.51 C129.47 41.35, 136.09 32.69, 144.23 23.35 M128.23 47.87 C131.28 40.54, 137.28 36.59, 143.97 29.75 M128.23 47.87 C132.3 43.29, 136.94 37.23, 143.97 29.75 M132.56 48.98 C136.69 46.03, 136.69 42.77, 143.71 36.15 M132.56 48.98 C136.02 46.4, 138.73 42.29, 143.71 36.15" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M11.84 0 M11.84 0 C38.02 2.65, 61.84 0.28, 130.37 0 M11.84 0 C38.21 1.94, 65.76 1.5, 130.37 0 M130.37 0 C139.47 1, 143.75 5.88, 142.21 11.84 M130.37 0 C137.21 0.92, 139.99 4.87, 142.21 11.84 M142.21 11.84 C140.42 17.68, 141.72 25.2, 142.21 35.53 M142.21 11.84 C142.2 20.55, 141.47 28.9, 142.21 35.53 M142.21 35.53 C143.14 42.65, 138.92 45.76, 130.37 47.37 M142.21 35.53 C141.26 45.41, 136.07 48.58, 130.37 47.37 M130.37 47.37 C100.03 46.91, 70.33 46.17, 11.84 47.37 M130.37 47.37 C93.27 45.68, 57.65 45.47, 11.84 47.37 M11.84 47.37 C3.83 47.21, 0.04 43.58, 0 35.53 M11.84 47.37 C3.58 49.37, 2.09 44.62, 0 35.53 M0 35.53 C-0.11 25.56, 1.74 17.16, 0 11.84 M0 35.53 C-0.73 27.14, 0.25 16.41, 0 11.84 M0 11.84 C1.26 4.93, 5.18 0.97, 11.84 0 M0 11.84 C0.62 1.9, 3.42 2.05, 11.84 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(15 78.13632812499998) rotate(0 65.625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">0x01 0x02 0x03</text></g><g stroke-linecap="round" transform="translate(11.80078125 149.404296875) rotate(0 51.5 24.5)"><path d="M3.26 2.83 C3.26 2.83, 3.26 2.83, 3.26 2.83 M3.26 2.83 C3.26 2.83, 3.26 2.83, 3.26 2.83 M1.69 10.74 C4.79 9.22, 6.14 6.4, 10.22 0.93 M1.69 10.74 C4.82 7.44, 8.36 3.46, 10.22 0.93 M1.43 17.14 C5.08 10.94, 12.94 4.63, 14.55 2.05 M1.43 17.14 C7.42 12.34, 11.48 5.83, 14.55 2.05 M1.82 22.78 C7.79 17.76, 9.92 11.78, 20.19 1.65 M1.82 22.78 C6.91 17.54, 10.69 12.89, 20.19 1.65 M1.56 29.18 C10.68 19.58, 16.05 8.86, 25.18 2.01 M1.56 29.18 C6.66 24.1, 10.7 18.88, 25.18 2.01 M1.95 34.82 C14.06 20.7, 22.56 11.87, 30.16 2.37 M1.95 34.82 C12.05 23.95, 21.94 11.12, 30.16 2.37 M1.69 41.22 C7.98 31.97, 17.66 24.23, 35.81 1.98 M1.69 41.22 C14.67 26.87, 25.96 11.6, 35.81 1.98 M3.4 45.35 C12.42 31.5, 21.88 20.01, 40.79 2.34 M3.4 45.35 C14.79 32.81, 25.98 18.39, 40.79 2.34 M5.76 48.73 C19.03 33.54, 30.13 20.75, 46.44 1.94 M5.76 48.73 C15.95 35.19, 26.77 22.13, 46.44 1.94 M10.75 49.09 C25.08 35.85, 34.98 19.45, 51.42 2.3 M10.75 49.09 C25.36 34.29, 38.07 17.14, 51.42 2.3 M14.42 50.96 C25.24 34.64, 40.8 20.23, 57.07 1.91 M14.42 50.96 C31.56 31.52, 47.54 13.07, 57.07 1.91 M20.07 50.57 C36.58 31.78, 51.41 11.57, 62.05 2.27 M20.07 50.57 C35.27 32.37, 49.62 15.57, 62.05 2.27 M25.05 50.93 C33.69 40.39, 45.49 30.76, 67.7 1.87 M25.05 50.93 C39.57 35.58, 52.89 19.97, 67.7 1.87 M30.04 51.29 C42.51 36.46, 56.98 20.61, 72.68 2.23 M30.04 51.29 C39.08 40.34, 49.2 29.9, 72.68 2.23 M35.68 50.89 C47.44 39.9, 56.68 27.08, 78.33 1.84 M35.68 50.89 C52.05 33.69, 66.26 15.86, 78.33 1.84 M40.67 51.25 C53.32 36.02, 69.16 19.85, 83.31 2.2 M40.67 51.25 C53.19 37.93, 64.14 24.78, 83.31 2.2 M46.31 50.86 C62.11 32.31, 78.52 15.58, 88.96 1.8 M46.31 50.86 C57.68 37.06, 69.81 23.8, 88.96 1.8 M51.3 51.22 C59.29 38.98, 72.37 29.04, 93.94 2.16 M51.3 51.22 C64.73 35.51, 79.44 17.72, 93.94 2.16 M56.94 50.83 C69.81 34.76, 86.63 19.59, 98.93 2.52 M56.94 50.83 C72.69 32.66, 90.36 14.37, 98.93 2.52 M61.93 51.19 C77.07 35.49, 89.58 18.3, 101.95 5.15 M61.93 51.19 C76.05 33.49, 92.37 16.74, 101.95 5.15 M67.57 50.79 C76.42 41.59, 82.64 33.63, 103 10.04 M67.57 50.79 C76.25 40.25, 84.99 30.22, 103 10.04 M72.56 51.15 C79.09 44.86, 86.76 36.01, 103.4 15.68 M72.56 51.15 C78.4 43.41, 84.37 36.12, 103.4 15.68 M78.2 50.76 C84.15 41.95, 92.56 33.07, 103.13 22.08 M78.2 50.76 C88.29 39.34, 96.97 28.67, 103.13 22.08 M83.19 51.12 C88.53 45.31, 96.1 36.68, 103.53 27.72 M83.19 51.12 C91.24 41.6, 98.32 33.94, 103.53 27.72 M88.83 50.72 C90.5 47.29, 97.87 43.07, 103.27 34.12 M88.83 50.72 C94.68 44.06, 98.18 39.73, 103.27 34.12 M93.82 51.08 C96.56 47.12, 100.38 44.41, 104.97 38.25 M93.82 51.08 C96.61 47.17, 101.29 42.48, 104.97 38.25" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M12.25 0 M12.25 0 C28.51 0, 42.5 2.25, 90.75 0 M12.25 0 C32.14 -0.62, 52.39 1.04, 90.75 0 M90.75 0 C100.71 0.09, 101.8 3.77, 103 12.25 M90.75 0 C97.64 -0.83, 104 5.94, 103 12.25 M103 12.25 C101.33 19.82, 101.63 32.15, 103 36.75 M103 12.25 C102.35 21.13, 102.15 31.06, 103 36.75 M103 36.75 C104.56 46.87, 99.61 50.21, 90.75 49 M103 36.75 C104.15 46.69, 101.13 47.94, 90.75 49 M90.75 49 C62.94 48.31, 35.39 47.65, 12.25 49 M90.75 49 C67.73 49.76, 43.51 49.69, 12.25 49 M12.25 49 C3.6 47.48, -1.71 45.84, 0 36.75 M12.25 49 C3.19 49.75, -1.85 43.96, 0 36.75 M0 36.75 C-0.6 25.33, 0.5 18.11, 0 12.25 M0 36.75 C-0.07 30.3, -1.01 22.9, 0 12.25 M0 12.25 C-1.34 4.43, 2.32 -0.12, 12.25 0 M0 12.25 C-0.18 4.12, 4.26 -0.37, 12.25 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(16.80078125 164.30429687499998) rotate(0 42.1875 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">0x04 0x05</text></g><g stroke-linecap="round" transform="translate(14.93359375 236.67578125) rotate(0 4.138671875 25.552734375)"><path d="M-0.29 2.4 C-0.29 2.4, -0.29 2.4, -0.29 2.4 M-0.29 2.4 C-0.29 2.4, -0.29 2.4, -0.29 2.4 M0.11 8.04 C1.82 6.13, 2.96 4.61, 4.7 2.76 M0.11 8.04 C1.05 6.92, 2.71 5.33, 4.7 2.76 M-0.15 14.44 C2.61 10.36, 4.52 9.27, 9.69 3.12 M-0.15 14.44 C3 10.45, 7.79 5.81, 9.69 3.12 M0.24 20.08 C1.78 17.33, 6.21 13.86, 9.43 9.52 M0.24 20.08 C3.9 16.52, 6.5 12.39, 9.43 9.52 M-0.02 26.48 C2.46 23.23, 7.19 20.21, 9.82 15.16 M-0.02 26.48 C2.52 23.58, 5.92 19.96, 9.82 15.16 M-0.28 32.88 C3.43 29.78, 4.96 26.51, 9.56 21.56 M-0.28 32.88 C1.68 30.37, 4.75 26.47, 9.56 21.56 M0.11 38.52 C2.43 35.55, 6.84 32.06, 9.3 27.96 M0.11 38.52 C3.34 34.69, 7.79 30.27, 9.3 27.96 M-0.15 44.92 C4.41 39.72, 7.29 36.44, 9.69 33.6 M-0.15 44.92 C2.92 41.4, 5.54 37.99, 9.69 33.6 M0.9 49.81 C2.58 47.91, 5.61 43.76, 9.43 40 M0.9 49.81 C3.14 46.67, 5.91 43.78, 9.43 40 M3.92 52.43 C5.01 50.49, 6.18 49.02, 9.82 45.64 M3.92 52.43 C5.89 50.77, 7.52 48.28, 9.82 45.64" stroke="#a5d8ff" stroke-width="0.5" fill="none"></path><path d="M2.07 0 M2.07 0 C2.97 -0.21, 4.11 0.12, 6.21 0 M2.07 0 C3.19 -0.16, 4.23 0.11, 6.21 0 M6.21 0 C8.97 0.45, 9.01 1.39, 8.28 2.07 M6.21 0 C9.57 -1.98, 9.89 2.6, 8.28 2.07 M8.28 2.07 C8.66 12.58, 9.73 22.31, 8.28 49.04 M8.28 2.07 C8.53 15.26, 7.49 28.86, 8.28 49.04 M8.28 49.04 C6.95 49.38, 7.58 52.75, 6.21 51.11 M8.28 49.04 C10.35 52.7, 9.64 53.24, 6.21 51.11 M6.21 51.11 C5.12 51.08, 3.76 50.9, 2.07 51.11 M6.21 51.11 C5.14 51.07, 4.25 51.01, 2.07 51.11 M2.07 51.11 C2.45 52.5, -1.17 52.21, 0 49.04 M2.07 51.11 C-1.47 50.3, 2.23 51.76, 0 49.04 M0 49.04 C-0.13 38.39, -0.48 26.48, 0 2.07 M0 49.04 C-1.31 32.15, -0.35 13.45, 0 2.07 M0 2.07 C0.41 0.36, -0.67 -0.56, 2.07 0 M0 2.07 C-0.35 -0.58, -1.03 -1.54, 2.07 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(10.23046875 10) rotate(0 23.4375 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">Blobs</text></g><g transform="translate(279.12890625 12.759374999999977) rotate(0 51.5625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">Directories</text></g><g stroke-linecap="round" transform="translate(283.875 68.3828125) rotate(0 197 72.5)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C8.28 14.49, 8.49 11.04, 18.1 2.48 M3.66 19.09 C7.7 14.57, 11.52 11.66, 18.1 2.48 M2.75 26.24 C11.51 19.05, 17.92 11.98, 24.4 1.33 M2.75 26.24 C7.23 20.55, 12.55 15.89, 24.4 1.33 M2.48 32.64 C12.12 24.05, 19.28 13.72, 30.7 0.18 M2.48 32.64 C9.16 24.56, 14.83 17.39, 30.7 0.18 M2.88 38.28 C11.43 28.28, 22.31 16.44, 34.37 2.05 M2.88 38.28 C14.6 24.25, 27.89 9.94, 34.37 2.05 M2.62 44.68 C9.32 34.89, 17.39 26.88, 39.36 2.41 M2.62 44.68 C11.95 33.82, 21.66 21.02, 39.36 2.41 M2.36 51.07 C12.81 41.21, 19.43 27.76, 45 2.02 M2.36 51.07 C16.68 33.29, 30.46 16.46, 45 2.02 M2.75 56.72 C16.64 41.98, 28.98 26.21, 49.99 2.38 M2.75 56.72 C21 35.95, 39.52 14.81, 49.99 2.38 M2.49 63.11 C19.63 44.26, 39.08 21.21, 55.63 1.98 M2.49 63.11 C17.01 46.73, 31.61 30.35, 55.63 1.98 M2.88 68.76 C22.9 46.33, 43.08 21.26, 60.62 2.34 M2.88 68.76 C23.19 45.39, 41.88 24.57, 60.62 2.34 M2.62 75.16 C23.83 54.32, 39.89 29.17, 66.26 1.95 M2.62 75.16 C18.47 56.13, 34.46 38.2, 66.26 1.95 M2.36 81.55 C29.87 50.88, 54.51 22.49, 71.25 2.31 M2.36 81.55 C26.5 54.38, 49.03 26.57, 71.25 2.31 M2.76 87.2 C25.76 61.24, 52.69 29.56, 76.89 1.91 M2.76 87.2 C22.68 64.01, 42.85 40.22, 76.89 1.91 M2.49 93.59 C30.49 61.54, 57.87 26.06, 81.88 2.27 M2.49 93.59 C31.33 60.52, 59.32 27.03, 81.88 2.27 M2.89 99.24 C28.16 70.88, 49.29 44.38, 87.52 1.88 M2.89 99.24 C22.19 77.54, 40.48 55.68, 87.52 1.88 M2.63 105.64 C27.42 78.52, 50.89 50.01, 92.51 2.24 M2.63 105.64 C38.6 64.43, 74.48 23.22, 92.51 2.24 M2.37 112.03 C35.69 71.87, 67.49 34.66, 98.15 1.85 M2.37 112.03 C29.87 80.37, 57.31 48.8, 98.15 1.85 M2.76 117.68 C35.35 78.95, 70.62 41.89, 103.14 2.21 M2.76 117.68 C24.16 94.43, 44.43 69.26, 103.14 2.21 M3.16 123.32 C27.31 95.67, 53.64 64.97, 108.78 1.81 M3.16 123.32 C40.3 79.27, 78.36 37.25, 108.78 1.81 M4.86 127.45 C35.22 90.31, 66.42 52.47, 113.77 2.17 M4.86 127.45 C27.89 100.07, 53.15 74.47, 113.77 2.17 M5.26 133.1 C41.54 89.35, 82.58 48.1, 119.41 1.78 M5.26 133.1 C50.78 80.9, 96.57 28.36, 119.41 1.78 M7.62 136.47 C50.54 85.68, 96.45 32.28, 124.4 2.14 M7.62 136.47 C50.54 87.26, 95.13 37.29, 124.4 2.14 M10.64 139.1 C44.47 96.28, 83.89 53.02, 130.04 1.74 M10.64 139.1 C49.89 96.71, 86.98 53.36, 130.04 1.74 M14.31 140.97 C55.74 92.08, 97.64 42.48, 135.03 2.1 M14.31 140.97 C49.36 97.94, 86.03 55.71, 135.03 2.1 M17.33 143.59 C62.88 90.12, 107.49 38.88, 140.67 1.71 M17.33 143.59 C52.29 102.09, 87.68 60.93, 140.67 1.71 M21.01 145.46 C48.28 114.13, 76.95 81.48, 145.66 2.07 M21.01 145.46 C64.61 95.84, 107.71 47.87, 145.66 2.07 M25.34 146.58 C73.23 89.86, 125.31 35.24, 150.65 2.43 M25.34 146.58 C51.9 117.94, 78.32 86.29, 150.65 2.43 M32.29 144.67 C78.07 95.21, 118.24 44.56, 156.29 2.03 M32.29 144.67 C74.54 98.13, 114.59 49.94, 156.29 2.03 M37.28 145.03 C85.09 88.58, 133.69 36.74, 161.28 2.39 M37.28 145.03 C69.44 108.12, 100.26 71.2, 161.28 2.39 M42.92 144.64 C90.72 91.41, 136.95 40.49, 166.92 2 M42.92 144.64 C66.95 114.58, 93.7 85.42, 166.92 2 M47.91 145 C75.02 114.35, 102.25 80.22, 171.91 2.36 M47.91 145 C87.4 99.39, 126.35 54.03, 171.91 2.36 M52.9 145.36 C79.04 113.6, 107.32 79.81, 177.55 1.96 M52.9 145.36 C77.61 115.94, 103.48 86, 177.55 1.96 M58.54 144.96 C92.12 105.3, 128.8 63.9, 182.54 2.32 M58.54 144.96 C106.61 90.51, 152.9 36.35, 182.54 2.32 M63.53 145.32 C107.47 99.27, 149.03 48.65, 188.18 1.93 M63.53 145.32 C96.13 109.61, 126.87 72.99, 188.18 1.93 M69.17 144.93 C102.47 104.11, 137.86 61.81, 193.17 2.29 M69.17 144.93 C110.04 98.38, 152.23 51.84, 193.17 2.29 M74.16 145.29 C104.78 108.19, 138.63 71.58, 198.81 1.9 M74.16 145.29 C99.31 117.03, 123.84 87.71, 198.81 1.9 M79.8 144.9 C130.71 87.32, 178.54 30.53, 203.8 2.26 M79.8 144.9 C124.29 94.42, 167.51 43.91, 203.8 2.26 M84.79 145.26 C111.41 111.48, 143.37 80.45, 209.44 1.86 M84.79 145.26 C130.36 93.05, 176.97 39.33, 209.44 1.86 M90.43 144.86 C128.08 97.62, 168.49 56.08, 214.43 2.22 M90.43 144.86 C130.35 96.04, 171.08 48.72, 214.43 2.22 M95.42 145.22 C146.19 90.39, 192.4 31.56, 220.07 1.83 M95.42 145.22 C120.72 114.85, 146.9 84.4, 220.07 1.83 M101.06 144.83 C127.22 111.52, 152.8 83.04, 225.06 2.19 M101.06 144.83 C132.86 108.13, 163.34 71.68, 225.06 2.19 M106.05 145.19 C154.17 92.56, 198.8 39.31, 230.7 1.79 M106.05 145.19 C136.53 110.59, 166.08 76.75, 230.7 1.79 M111.69 144.79 C138.65 117.15, 162.06 85.81, 235.69 2.15 M111.69 144.79 C137.61 113.65, 164.22 81.98, 235.69 2.15 M116.68 145.15 C157.06 99.25, 194.58 56.02, 241.33 1.76 M116.68 145.15 C155.1 99.61, 194.87 53.74, 241.33 1.76 M122.32 144.76 C155.38 104.12, 188.9 66.56, 246.32 2.12 M122.32 144.76 C171.19 89.94, 218.89 34.94, 246.32 2.12 M127.31 145.12 C160.63 107.75, 195.41 68.39, 251.96 1.72 M127.31 145.12 C171.79 94.11, 216.13 43.25, 251.96 1.72 M132.95 144.72 C167.14 104.52, 206.3 64.21, 256.95 2.08 M132.95 144.72 C164.99 105.52, 198.48 68.92, 256.95 2.08 M137.94 145.08 C183.71 92.31, 226.45 45.7, 262.59 1.69 M137.94 145.08 C163.89 115.91, 190.22 85.14, 262.59 1.69 M143.58 144.69 C174.92 109.48, 206.89 70.46, 267.58 2.05 M143.58 144.69 C170.4 113.21, 199.74 82.17, 267.58 2.05 M148.57 145.05 C178.08 111.76, 203.33 81.73, 272.56 2.41 M148.57 145.05 C173.64 115.53, 198.59 86.39, 272.56 2.41 M154.21 144.65 C187.55 108.07, 219.55 68.66, 278.21 2.01 M154.21 144.65 C200.06 91.97, 243.47 39.89, 278.21 2.01 M159.2 145.01 C186.66 112.8, 212.53 85.21, 283.19 2.37 M159.2 145.01 C207.12 89.92, 255.48 37.14, 283.19 2.37 M164.19 145.37 C202.12 104.92, 237.2 59.79, 288.84 1.98 M164.19 145.37 C194.82 106.77, 226.84 70.45, 288.84 1.98 M169.83 144.98 C212.12 93.05, 258.35 43.46, 293.82 2.34 M169.83 144.98 C201.44 106.46, 235.81 67.61, 293.82 2.34 M174.82 145.34 C207.54 111.65, 235.58 74.76, 299.47 1.94 M174.82 145.34 C199.92 118.44, 225.68 88.79, 299.47 1.94 M180.46 144.94 C208.5 116.42, 230.68 85.13, 304.45 2.3 M180.46 144.94 C218.97 100.78, 259.51 55.75, 304.45 2.3 M185.45 145.31 C221.24 102.72, 256.86 59.74, 310.1 1.91 M185.45 145.31 C232.8 92.21, 280.33 37.72, 310.1 1.91 M191.09 144.91 C238.27 88.19, 288.2 35.33, 315.08 2.27 M191.09 144.91 C220.59 109.55, 252.22 74.41, 315.08 2.27 M196.08 145.27 C239.76 94.85, 284.75 46.35, 320.73 1.88 M196.08 145.27 C230.49 104.94, 265.25 68.33, 320.73 1.88 M201.72 144.88 C234.66 108.79, 263.22 71.79, 325.71 2.24 M201.72 144.88 C237.32 101.42, 273.7 59.07, 325.71 2.24 M206.71 145.24 C253.21 90.64, 300.99 37.76, 331.36 1.84 M206.71 145.24 C240.65 108.22, 273.71 69.33, 331.36 1.84 M212.35 144.84 C242.19 114.5, 267.67 79.17, 336.34 2.2 M212.35 144.84 C236.6 115.07, 262.16 86.57, 336.34 2.2 M217.34 145.2 C251.25 105.29, 286.42 64.08, 341.99 1.81 M217.34 145.2 C249.04 108.05, 278.75 71.6, 341.99 1.81 M222.98 144.81 C254.78 109.71, 284.95 73.51, 346.97 2.17 M222.98 144.81 C251.44 112.17, 277.38 81.44, 346.97 2.17 M227.97 145.17 C258.67 109.25, 290.39 71.64, 352.62 1.77 M227.97 145.17 C253.46 115.85, 279.91 85.09, 352.62 1.77 M233.61 144.77 C279.92 90.22, 328.36 36.09, 357.6 2.13 M233.61 144.77 C263.54 109.84, 296.58 72.44, 357.6 2.13 M238.6 145.13 C284.33 94.02, 329.86 38.18, 363.25 1.74 M238.6 145.13 C270.6 110.97, 300.1 77.3, 363.25 1.74 M244.24 144.74 C285.03 98.47, 325.99 49.52, 368.23 2.1 M244.24 144.74 C288.81 94.28, 333.65 43.13, 368.23 2.1 M249.23 145.1 C274.07 113.24, 304.32 83.42, 373.22 2.46 M249.23 145.1 C288.32 102.57, 326.71 58.15, 373.22 2.46 M254.87 144.7 C297.9 94.29, 343.78 43.11, 377.55 3.57 M254.87 144.7 C286.66 106.11, 320.39 66.72, 377.55 3.57 M259.86 145.06 C295.8 103.74, 331.06 61.72, 381.23 5.44 M259.86 145.06 C298.82 101.67, 336.82 56.01, 381.23 5.44 M265.5 144.67 C313.19 90.52, 361.44 33.85, 384.9 7.31 M265.5 144.67 C292.92 114.87, 319.46 83.51, 384.9 7.31 M270.49 145.03 C293.76 118.67, 317.19 90.91, 387.92 9.94 M270.49 145.03 C298.6 112.33, 327.42 78.58, 387.92 9.94 M276.13 144.63 C309.18 107.62, 339.56 70.71, 390.28 13.32 M276.13 144.63 C314.84 98.86, 353.06 54.55, 390.28 13.32 M281.12 144.99 C305.94 116.7, 328.37 88.76, 391.99 17.45 M281.12 144.99 C321.03 97.29, 362.22 51.33, 391.99 17.45 M286.1 145.35 C324.81 103.13, 365.35 57.02, 393.04 22.34 M286.1 145.35 C313.97 112.26, 343.79 77.31, 393.04 22.34 M291.75 144.96 C324.16 107.97, 358.78 71.44, 395.4 25.72 M291.75 144.96 C322.28 110.33, 352.3 74.13, 395.4 25.72 M296.73 145.32 C322.53 113.82, 350.61 86.17, 395.14 32.11 M296.73 145.32 C330.9 104.68, 367.32 64.68, 395.14 32.11 M302.38 144.93 C320.38 122.95, 340.8 101.95, 395.54 37.76 M302.38 144.93 C328.33 112.21, 356.43 82.25, 395.54 37.76 M307.36 145.29 C337.92 110.64, 366.55 76.31, 395.28 44.15 M307.36 145.29 C325.37 123.43, 344.42 100.44, 395.28 44.15 M313.01 144.89 C335.45 120.95, 355.71 93.18, 395.01 50.55 M313.01 144.89 C342.22 110.33, 372.53 76.59, 395.01 50.55 M317.99 145.25 C335.46 125.76, 350.14 107.98, 395.41 56.2 M317.99 145.25 C334.45 126.54, 349.07 108.69, 395.41 56.2 M323.64 144.86 C350.88 112.04, 380.82 81.41, 395.15 62.59 M323.64 144.86 C349.73 114.44, 376.12 84.65, 395.15 62.59 M328.62 145.22 C342.07 126.15, 358.11 110, 395.54 68.24 M328.62 145.22 C352.38 118.21, 373.64 91.69, 395.54 68.24 M334.27 144.82 C356.58 116.53, 380.06 91.2, 395.28 74.63 M334.27 144.82 C352.85 123.85, 373.24 101.61, 395.28 74.63 M339.25 145.18 C350.58 130.51, 366.65 115.79, 395.67 80.28 M339.25 145.18 C360.84 120.94, 381.57 97.17, 395.67 80.28 M344.9 144.79 C359.95 129.93, 374.97 110.51, 395.41 86.68 M344.9 144.79 C359.66 128.95, 373.84 112.99, 395.41 86.68 M349.88 145.15 C365.74 124.35, 384.39 105.92, 395.81 92.32 M349.88 145.15 C365.01 128.41, 378.61 111.44, 395.81 92.32 M355.53 144.75 C366.83 128.33, 381.85 115.58, 395.55 98.72 M355.53 144.75 C366.51 134.09, 376.57 121.06, 395.55 98.72 M360.51 145.11 C371.57 133.46, 379.69 124.34, 395.94 104.36 M360.51 145.11 C371.82 131.23, 384.33 117.41, 395.94 104.36 M364.19 146.98 C372.36 139.23, 375.92 131.75, 395.68 110.76 M364.19 146.98 C375.25 134.47, 383.71 124.14, 395.68 110.76 M370.49 145.83 C377.43 139.68, 383.18 129.21, 396.73 115.65 M370.49 145.83 C377.33 137.24, 382.58 130.01, 396.73 115.65 M377.44 143.93 C380.98 139.22, 385.26 134.38, 391.88 127.33 M377.44 143.93 C381.01 139.93, 385.77 134.2, 391.88 127.33" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C158.41 -0.65, 283.84 1.37, 362 0 M32 0 C99.31 -2.94, 165.01 -1.66, 362 0 M362 0 C382.37 -1.19, 392.06 11.27, 394 32 M362 0 C383.53 0.55, 395.84 12.5, 394 32 M394 32 C396.17 49.29, 394.95 72.1, 394 113 M394 32 C394.74 57.51, 393.79 80.56, 394 113 M394 113 C393.49 135.83, 381.87 143.9, 362 145 M394 113 C391.98 135.04, 383.6 143.19, 362 145 M362 145 C291.31 144.27, 221.15 142.78, 32 145 M362 145 C249.93 143.31, 138.97 142.96, 32 145 M32 145 C8.71 146.69, -1.86 135.95, 0 113 M32 145 C10.21 143.8, 1.65 132.18, 0 113 M0 113 C0.55 94.72, -1.35 75.26, 0 32 M0 113 C-0.88 90.36, -1.5 66.27, 0 32 M0 32 C1.9 12.2, 9.12 -1.48, 32 0 M0 32 C0.22 11.69, 10.1 1.96, 32 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(288.875 73.3828125) rotate(0 140.625 67.19999999999999)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories: []</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files:</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: .keep</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;empty-blob-digest&gt;</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g stroke-linecap="round" transform="translate(279.59193843887 271.159696266393) rotate(0 198.99999999999994 130)"><path d="M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M7.86 8.16 C7.86 8.16, 7.86 8.16, 7.86 8.16 M3.66 19.09 C7.06 13.81, 9.24 9.34, 18.1 2.48 M3.66 19.09 C8.44 11.73, 13.76 5.27, 18.1 2.48 M2.75 26.24 C10 16.18, 18.61 6.13, 24.4 1.33 M2.75 26.24 C9.44 19.31, 14.57 12.4, 24.4 1.33 M2.48 32.64 C10.34 21.03, 17.74 12.3, 30.7 0.18 M2.48 32.64 C13.49 21.44, 21.83 9.48, 30.7 0.18 M2.88 38.28 C12.91 25, 26.86 13.76, 34.37 2.05 M2.88 38.28 C12.64 28.15, 21.63 16.63, 34.37 2.05 M2.62 44.68 C14.17 33.27, 22.89 18.23, 39.36 2.41 M2.62 44.68 C12.44 34.48, 21.24 23.55, 39.36 2.41 M2.36 51.07 C18.27 36.19, 31.27 19.8, 45 2.02 M2.36 51.07 C15.34 37.29, 27.12 23.41, 45 2.02 M2.75 56.72 C18.22 37.56, 35.93 17.94, 49.99 2.38 M2.75 56.72 C20.57 34.56, 37.8 14.24, 49.99 2.38 M2.49 63.11 C18.51 41.78, 36.66 23.69, 55.63 1.98 M2.49 63.11 C16.06 46.84, 30.13 32.58, 55.63 1.98 M2.23 69.51 C14.08 54.4, 27.81 42.3, 60.62 2.34 M2.23 69.51 C14.8 54.54, 28.11 38.47, 60.62 2.34 M2.62 75.16 C23.22 53.14, 45.11 28.68, 66.26 1.95 M2.62 75.16 C18.68 55.49, 35.76 36.87, 66.26 1.95 M2.36 81.55 C29.49 51.06, 52.67 20.07, 71.25 2.31 M2.36 81.55 C23.35 59.17, 43.86 35.46, 71.25 2.31 M2.76 87.2 C29.08 59.14, 54.26 30.38, 76.89 1.91 M2.76 87.2 C25.2 61.93, 47.39 35.45, 76.89 1.91 M2.49 93.59 C22.97 73.16, 41.24 50.49, 81.88 2.27 M2.49 93.59 C22.85 70.13, 41.2 48.51, 81.88 2.27 M2.23 99.99 C26.73 70.85, 53 42.34, 87.52 1.88 M2.23 99.99 C26.11 70.65, 52.18 43.37, 87.52 1.88 M2.63 105.64 C29.25 73.43, 57.28 44.11, 92.51 2.24 M2.63 105.64 C22.21 84.02, 40.09 61.9, 92.51 2.24 M2.37 112.03 C24.78 84.92, 46.32 60.61, 98.15 1.85 M2.37 112.03 C40.71 68.73, 77.43 25.35, 98.15 1.85 M2.1 118.43 C32.45 81.76, 64.34 45.79, 103.14 2.21 M2.1 118.43 C26.04 90.13, 50.16 63.23, 103.14 2.21 M2.5 124.07 C34.35 88.85, 66.58 53.31, 108.78 1.81 M2.5 124.07 C35.8 86.3, 67.38 48.42, 108.78 1.81 M2.24 130.47 C44.13 79.64, 85.07 32.68, 113.77 2.17 M2.24 130.47 C28.26 100.65, 54.41 68.41, 113.77 2.17 M2.63 136.11 C39.83 96.08, 73.86 52.5, 119.41 1.78 M2.63 136.11 C27.92 105.97, 55.2 76.2, 119.41 1.78 M2.37 142.51 C38.64 103.62, 72.54 62.27, 124.4 2.14 M2.37 142.51 C38.27 104.7, 71.42 64.68, 124.4 2.14 M2.11 148.91 C31.62 115.14, 60.21 82.46, 130.04 1.74 M2.11 148.91 C30.64 115.22, 60.37 82.49, 130.04 1.74 M2.5 154.55 C45.43 105.72, 87.93 57.79, 135.03 2.1 M2.5 154.55 C39.77 111.59, 78.21 68.92, 135.03 2.1 M2.24 160.95 C40.01 120.72, 75.93 77.72, 140.67 1.71 M2.24 160.95 C44.66 112.93, 87.03 64.56, 140.67 1.71 M1.98 167.35 C39.14 124.53, 74.75 81.57, 145.66 2.07 M1.98 167.35 C47.85 117.91, 91.41 66.21, 145.66 2.07 M2.38 172.99 C58.38 106.95, 115.31 43.43, 150.65 2.43 M2.38 172.99 C53.11 115.34, 104.81 56.32, 150.65 2.43 M2.11 179.39 C64.27 108.42, 124.58 39.52, 156.29 2.03 M2.11 179.39 C42.26 131.15, 83.61 83.43, 156.29 2.03 M2.51 185.03 C62.42 116.85, 122.68 43.77, 161.28 2.39 M2.51 185.03 C56.66 122.43, 112.32 58.31, 161.28 2.39 M2.25 191.43 C51.58 133.03, 102.03 75.72, 166.92 2 M2.25 191.43 C38.3 149.45, 75.45 107.32, 166.92 2 M1.99 197.83 C42.68 152.88, 80.66 108.12, 171.91 2.36 M1.99 197.83 C66.15 123.85, 132.83 47.33, 171.91 2.36 M2.38 203.47 C55.91 143.98, 107.7 83.76, 177.55 1.96 M2.38 203.47 C56.81 142.73, 110.46 81.58, 177.55 1.96 M2.12 209.87 C66.55 137, 131.82 62.14, 182.54 2.32 M2.12 209.87 C50.61 157.03, 98.73 101.44, 182.54 2.32 M1.86 216.27 C63.05 142.96, 126.25 69.69, 188.18 1.93 M1.86 216.27 C65.44 140.95, 131.03 66.87, 188.18 1.93 M2.25 221.91 C59.05 161.57, 111.38 97.22, 193.17 2.29 M2.25 221.91 C52.49 164.23, 101.5 106.36, 193.17 2.29 M1.33 229.06 C64.26 156.57, 124.34 84.79, 198.81 1.9 M1.33 229.06 C60.97 162.1, 120.98 94.62, 198.81 1.9 M1.73 234.71 C54.24 174.7, 106.54 116.97, 203.8 2.26 M1.73 234.71 C76.5 149.79, 152.59 63.83, 203.8 2.26 M2.78 239.59 C49.77 186.82, 94.95 134.94, 209.44 1.86 M2.78 239.59 C83.35 146.44, 163.63 53.44, 209.44 1.86 M4.49 243.73 C48.55 190.47, 93.86 139.53, 214.43 2.22 M4.49 243.73 C63.53 174.61, 122.23 106.2, 214.43 2.22 M6.19 247.86 C77.48 165.73, 149.04 81.52, 220.07 1.83 M6.19 247.86 C85.41 157.55, 164.08 66.86, 220.07 1.83 M8.56 251.24 C90.22 155.68, 175.16 56.51, 225.06 2.19 M8.56 251.24 C89.12 157.29, 172.43 62.13, 225.06 2.19 M10.92 254.62 C69.41 191.54, 124.13 126, 230.7 1.79 M10.92 254.62 C69.41 187.27, 127.03 120.42, 230.7 1.79 M13.94 257.24 C82.45 176.73, 153.61 97.39, 235.69 2.15 M13.94 257.24 C93.45 166.82, 173.91 75.69, 235.69 2.15 M18.27 258.36 C76.98 194.1, 131.53 129.14, 241.33 1.76 M18.27 258.36 C66.67 201.61, 115.46 145.37, 241.33 1.76 M22.6 259.47 C75.95 201, 128.47 140.06, 246.32 2.12 M22.6 259.47 C70.35 207.82, 116.17 155.22, 246.32 2.12 M26.27 261.34 C95.93 180.59, 169.95 95.39, 251.96 1.72 M26.27 261.34 C89.37 190.53, 152.1 118.22, 251.96 1.72 M32.57 260.19 C96.06 189.84, 155.98 120.97, 256.95 2.08 M32.57 260.19 C78.18 205.29, 124.7 151.48, 256.95 2.08 M37.56 260.55 C117 171.22, 192.42 83.99, 262.59 1.69 M37.56 260.55 C121.96 163.6, 206.2 67.19, 262.59 1.69 M43.2 260.16 C105.28 189.09, 169.45 117.92, 267.58 2.05 M43.2 260.16 C123.84 168.01, 203.45 76.43, 267.58 2.05 M48.19 260.52 C117.81 178.99, 186.9 100.6, 272.56 2.41 M48.19 260.52 C125.53 171.84, 202.4 82.45, 272.56 2.41 M53.83 260.12 C138.08 162.28, 222.76 67.81, 278.21 2.01 M53.83 260.12 C128.55 171.85, 204.35 84.41, 278.21 2.01 M58.82 260.48 C127.06 184.42, 193 106.58, 283.19 2.37 M58.82 260.48 C135.91 173.81, 211.1 87.23, 283.19 2.37 M64.46 260.09 C109.71 206.88, 156.75 154.77, 288.84 1.98 M64.46 260.09 C142.84 169.53, 220.7 80.3, 288.84 1.98 M69.45 260.45 C138.09 183.73, 208 105.35, 293.82 2.34 M69.45 260.45 C156.6 162.77, 241.99 64.65, 293.82 2.34 M75.09 260.06 C143.01 182.6, 211.21 103.49, 299.47 1.94 M75.09 260.06 C136 192, 195.87 123.37, 299.47 1.94 M80.08 260.42 C126.17 206.79, 172.95 151.57, 304.45 2.3 M80.08 260.42 C129.25 204.69, 179.83 146.37, 304.45 2.3 M85.72 260.02 C157.76 178.28, 226.22 97.47, 310.1 1.91 M85.72 260.02 C149.05 184.68, 213.67 111.24, 310.1 1.91 M90.71 260.38 C181.48 157.9, 269.25 55.91, 315.08 2.27 M90.71 260.38 C157.59 183.34, 224.55 106.5, 315.08 2.27 M95.7 260.74 C174.5 168.64, 255.03 76.7, 320.73 1.88 M95.7 260.74 C164.52 183.36, 232.23 105.12, 320.73 1.88 M101.34 260.35 C181.6 167.21, 261.15 73.9, 325.71 2.24 M101.34 260.35 C177.77 172.29, 255.39 82.95, 325.71 2.24 M106.33 260.71 C168.93 189.98, 230.06 115.81, 331.36 1.84 M106.33 260.71 C170.53 184.03, 237.17 107.92, 331.36 1.84 M111.97 260.31 C176.35 190.63, 236.85 119.43, 336.34 2.2 M111.97 260.31 C157.57 208.21, 204.38 154.93, 336.34 2.2 M116.96 260.67 C164.3 205.68, 212.63 148.72, 341.99 1.81 M116.96 260.67 C197.94 169.46, 278.58 77.5, 341.99 1.81 M122.6 260.28 C175.27 200.79, 229.84 138.31, 346.97 2.17 M122.6 260.28 C172.5 202.83, 220.76 146.35, 346.97 2.17 M127.59 260.64 C215.15 158.17, 304.42 54.77, 352.62 1.77 M127.59 260.64 C188.96 189.82, 250.22 118.28, 352.62 1.77 M133.23 260.24 C183.32 201.93, 235.94 141.72, 357.6 2.13 M133.23 260.24 C184.28 203.09, 233.15 145.34, 357.6 2.13 M138.22 260.6 C220.44 165.6, 301.95 72.93, 363.25 1.74 M138.22 260.6 C215.16 172.58, 291.69 83.91, 363.25 1.74 M143.86 260.21 C216.13 172.96, 292.05 87.04, 368.23 2.1 M143.86 260.21 C223.54 169.38, 302.36 80.03, 368.23 2.1 M148.85 260.57 C228.87 169.81, 309.22 79.04, 374.53 0.95 M148.85 260.57 C225.85 169.3, 304.04 78.63, 374.53 0.95 M154.49 260.17 C230.89 172.65, 306.45 86.57, 378.21 2.82 M154.49 260.17 C210.94 195.8, 265.6 130.92, 378.21 2.82 M159.48 260.53 C223.94 184.22, 290.75 110.57, 382.54 3.93 M159.48 260.53 C244.05 163.85, 329.27 67.22, 382.54 3.93 M165.12 260.14 C236.95 176.83, 309.56 92.2, 386.87 5.05 M165.12 260.14 C221.23 195.95, 279.16 130.62, 386.87 5.05 M170.11 260.5 C221.21 201.49, 273.24 142.28, 389.23 8.43 M170.11 260.5 C218.37 205.87, 266.44 150.41, 389.23 8.43 M175.75 260.11 C241.58 187.82, 304.45 113.61, 392.25 11.05 M175.75 260.11 C222.69 207.98, 267.39 155.95, 392.25 11.05 M180.74 260.47 C245.33 188.58, 307.54 115.78, 395.27 13.68 M180.74 260.47 C261.6 168.2, 340.34 76.88, 395.27 13.68 M186.38 260.07 C251.57 186.65, 319.28 107.6, 396.32 18.56 M186.38 260.07 C236.65 200.09, 289.45 140.81, 396.32 18.56 M191.37 260.43 C255.8 183.29, 323.42 107.08, 397.37 23.45 M191.37 260.43 C233.86 209.7, 277.09 160.88, 397.37 23.45 M197.01 260.04 C273.9 173.76, 350.14 83.39, 397.77 29.09 M197.01 260.04 C238.49 210.28, 280.91 161.3, 397.77 29.09 M202 260.4 C249.95 204.58, 301.21 145.35, 399.47 33.23 M202 260.4 C246.27 207.11, 292.1 154.9, 399.47 33.23 M207.64 260 C282.04 175.37, 353.03 93.88, 399.21 39.63 M207.64 260 C252.23 206.89, 298.22 154.54, 399.21 39.63 M212.63 260.36 C270.79 190.87, 332.59 125.04, 399.61 45.27 M212.63 260.36 C255.76 212.2, 297.66 164.88, 399.61 45.27 M217.62 260.72 C270.87 200.01, 324.39 137.32, 399.34 51.67 M217.62 260.72 C286.2 182.97, 352.23 105.9, 399.34 51.67 M223.26 260.33 C266.88 209.47, 311.98 157.05, 399.74 57.31 M223.26 260.33 C270.48 203.33, 319.36 147.73, 399.74 57.31 M228.25 260.69 C264.48 217.85, 298.81 176.68, 399.48 63.71 M228.25 260.69 C287.06 192.75, 345.09 124.31, 399.48 63.71 M233.89 260.29 C283.82 202.81, 333.35 147.36, 399.22 70.11 M233.89 260.29 C296.32 189.36, 357.95 117.97, 399.22 70.11 M238.88 260.65 C288.24 201.51, 340.4 144.75, 399.61 75.75 M238.88 260.65 C302.98 187.65, 365.83 115.51, 399.61 75.75 M244.52 260.26 C301.41 192.57, 362.82 123.97, 399.35 82.15 M244.52 260.26 C276.87 221.94, 309.71 183.55, 399.35 82.15 M249.51 260.62 C294.86 210.74, 338.19 159.76, 399.74 87.79 M249.51 260.62 C288.68 213.68, 329.48 168.33, 399.74 87.79 M255.15 260.22 C306.36 203.12, 357.73 144.26, 399.48 94.19 M255.15 260.22 C310.1 197.2, 364.53 133.07, 399.48 94.19 M260.14 260.58 C306.6 208.36, 353.31 154.47, 399.22 100.59 M260.14 260.58 C314.84 198.11, 370.53 134.35, 399.22 100.59 M265.78 260.19 C310.07 209.18, 353.85 155.07, 399.62 106.23 M265.78 260.19 C313.52 203.42, 361.67 148.41, 399.62 106.23 M270.77 260.55 C312.94 210.71, 350.92 165.03, 399.35 112.63 M270.77 260.55 C313.74 212.09, 354.22 165.25, 399.35 112.63 M276.41 260.15 C322.47 205.94, 367.79 155.3, 399.75 118.27 M276.41 260.15 C319.24 210.97, 362.85 162.61, 399.75 118.27 M281.4 260.51 C309.82 228.04, 337.57 195, 399.49 124.67 M281.4 260.51 C315.37 218.59, 351.47 180.05, 399.49 124.67 M287.04 260.12 C329.88 210.1, 376.19 158.03, 399.23 131.06 M287.04 260.12 C314.45 229.24, 339.01 200.91, 399.23 131.06 M292.03 260.48 C332.66 211.87, 372.61 167.01, 399.62 136.71 M292.03 260.48 C317.01 231.17, 342.37 202.7, 399.62 136.71 M297.67 260.09 C320.79 233.82, 342.65 206.24, 399.36 143.11 M297.67 260.09 C337.01 212.55, 377.78 167.34, 399.36 143.11 M302.66 260.45 C335.5 224.45, 365.99 190.46, 399.75 148.75 M302.66 260.45 C341.03 214.95, 379.75 172.24, 399.75 148.75 M308.3 260.05 C331.34 234.81, 350.27 209.28, 399.49 155.15 M308.3 260.05 C337.96 226.28, 369 190.93, 399.49 155.15 M313.29 260.41 C344.16 226.42, 373.26 191.65, 399.23 161.54 M313.29 260.41 C340.87 229.56, 366.67 199.76, 399.23 161.54 M318.93 260.02 C339.08 235.8, 357.34 215.88, 399.63 167.19 M318.93 260.02 C338.18 238.19, 356.44 217.12, 399.63 167.19 M323.92 260.38 C343.38 240.84, 359.55 216.05, 399.36 173.59 M323.92 260.38 C339.58 242.35, 357.05 223.74, 399.36 173.59 M328.9 260.74 C356.6 230.93, 381.38 201.75, 399.76 179.23 M328.9 260.74 C355.35 229.48, 382.07 199.6, 399.76 179.23 M334.55 260.34 C353.55 235.84, 374.53 215.15, 399.5 185.63 M334.55 260.34 C350.96 240.58, 366.19 222.61, 399.5 185.63 M339.53 260.7 C355.36 243.43, 370.27 221.73, 399.24 192.02 M339.53 260.7 C361.66 234.82, 383.39 210.67, 399.24 192.02 M345.18 260.31 C362.54 238.6, 380.33 220.43, 399.63 197.67 M345.18 260.31 C359.44 243.81, 374.08 228.32, 399.63 197.67 M350.16 260.67 C364.5 243.76, 376.71 230.97, 399.37 204.06 M350.16 260.67 C363.93 245.32, 377.91 229.24, 399.37 204.06 M355.81 260.27 C372.91 242.23, 387.92 223.73, 399.76 209.71 M355.81 260.27 C373.85 240.57, 389.06 220.82, 399.76 209.71 M360.79 260.63 C368.09 252.39, 377.96 242.27, 399.5 216.11 M360.79 260.63 C372.53 247.08, 385.83 232.19, 399.5 216.11 M366.44 260.24 C372.92 252.64, 384.19 241.59, 399.24 222.5 M366.44 260.24 C374.31 251.44, 384.03 240.28, 399.24 222.5 M372.74 259.09 C379.45 251.45, 383.63 244.65, 398.98 228.9 M372.74 259.09 C383.24 247.41, 391.87 236.99, 398.98 228.9 M378.38 258.69 C382.28 255.84, 387.86 247.21, 398.06 236.05 M378.38 258.69 C382.66 254.99, 386.79 248.97, 398.06 236.05 M385.99 256.04 C389.48 251.84, 392.18 249.55, 394.52 246.23 M385.99 256.04 C389.44 252.25, 392.29 247.67, 394.52 246.23" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M32 0 M32 0 C123.14 1.36, 217.94 1.27, 366 0 M32 0 C131.29 -1.79, 231.25 -1.6, 366 0 M366 0 C387.8 0.82, 396.14 9, 398 32 M366 0 C388.4 -1.28, 396.3 11.05, 398 32 M398 32 C396.68 77, 397.3 120.08, 398 228 M398 32 C397.91 99.78, 396.94 166.68, 398 228 M398 228 C399.84 247.36, 385.48 260.38, 366 260 M398 228 C399.48 250.74, 389.16 259.77, 366 260 M366 260 C278.41 260.83, 191.33 259.2, 32 260 M366 260 C254.85 261.48, 142.72 261.51, 32 260 M32 260 C9.06 261.46, -1.27 247.64, 0 228 M32 260 C8.96 260.25, 0.79 249.13, 0 228 M0 228 C-2.62 187.23, -0.98 150.02, 0 32 M0 228 C-1.12 169.92, -1.74 111.79, 0 32 M0 32 C-0.23 11.83, 11.85 0.53, 32 0 M0 32 C1.74 9.13, 11.06 1.81, 32 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(284.59193843887 276.159696266393) rotate(0 182.81249999999994 124.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: keep</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;directory-with-keep-digest&gt;</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 1</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files:</text><text x="0" y="115.19999999999999" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: .keep</text><text x="0" y="134.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;empty-blob-digest&gt;</text><text x="0" y="153.6" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="172.79999999999998" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   executable: false</text><text x="0" y="192" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks:</text><text x="0" y="211.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: aa</text><text x="0" y="230.39999999999998" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   target: /nix/store/somewhereelse</text></g><g stroke-linecap="round" transform="translate(292.58984375 581.578125) rotate(0 192 36.5)"><path d="M4.86 4.22 C4.86 4.22, 4.86 4.22, 4.86 4.22 M4.86 4.22 C4.86 4.22, 4.86 4.22, 4.86 4.22 M3.28 12.13 C5.02 8.82, 8.25 4.62, 11.81 2.32 M3.28 12.13 C6.19 8.3, 9.57 5.6, 11.81 2.32 M1.71 20.04 C8.34 16.13, 10.93 7.61, 18.77 0.41 M1.71 20.04 C6.51 15.46, 9.85 9.62, 18.77 0.41 M2.11 25.68 C10.79 16.53, 14.3 8.85, 23.76 0.77 M2.11 25.68 C8.12 18.52, 15.95 11.63, 23.76 0.77 M1.84 32.08 C8.84 22.24, 16.07 17.29, 29.4 0.38 M1.84 32.08 C11.36 20.47, 23.16 9.07, 29.4 0.38 M2.24 37.72 C9.27 29.77, 15.4 22.85, 34.39 0.74 M2.24 37.72 C15.42 23.1, 27.12 8.74, 34.39 0.74 M2.63 43.36 C8.76 33.58, 17 25.79, 40.03 0.35 M2.63 43.36 C13.14 31.06, 23.17 18.26, 40.03 0.35 M2.37 49.76 C16.36 34.07, 29.4 16.32, 45.02 0.71 M2.37 49.76 C17.93 32.43, 33.93 13.74, 45.02 0.71 M2.77 55.41 C19.05 35.42, 38.7 10.68, 50.66 0.31 M2.77 55.41 C19.96 34.91, 39.43 13.51, 50.66 0.31 M1.19 63.31 C17.21 49, 29.42 31.12, 55.65 0.67 M1.19 63.31 C15.9 46.95, 29.88 30.05, 55.65 0.67 M4.21 65.94 C21.41 44.81, 41.02 26.02, 61.29 0.28 M4.21 65.94 C24.28 42.91, 45.96 20.08, 61.29 0.28 M6.57 69.32 C23.78 51.2, 36.27 33.53, 66.28 0.64 M6.57 69.32 C19.35 53.63, 31.93 38.99, 66.28 0.64 M9.59 71.94 C25.17 56.48, 40.49 38.06, 71.92 0.24 M9.59 71.94 C23.84 58.2, 36.53 43.78, 71.92 0.24 M13.92 73.05 C31.98 52.05, 54.85 25.68, 76.91 0.6 M13.92 73.05 C30.95 53.92, 48.62 33.36, 76.91 0.6 M19.57 72.66 C38.35 51.77, 53.43 34.49, 82.55 0.21 M19.57 72.66 C32.02 56.9, 44.97 41.55, 82.55 0.21 M24.55 73.02 C48.11 46.73, 66.96 24.34, 87.54 0.57 M24.55 73.02 C47.62 46.37, 71.59 19.54, 87.54 0.57 M30.2 72.63 C46.25 51.67, 67.22 31.55, 93.18 0.17 M30.2 72.63 C53.38 46.94, 75.41 21.61, 93.18 0.17 M35.18 72.99 C55.38 48.59, 74.75 27.94, 98.17 0.53 M35.18 72.99 C57.1 47.59, 77.7 22.46, 98.17 0.53 M40.83 72.59 C64.45 43.72, 89.12 19.76, 103.81 0.14 M40.83 72.59 C61.26 47.84, 82.21 23.37, 103.81 0.14 M45.81 72.95 C66.24 52.79, 83.08 30.43, 108.8 0.5 M45.81 72.95 C67.8 48.81, 88.58 24.77, 108.8 0.5 M51.46 72.56 C64.09 57.9, 78.25 44.63, 113.78 0.86 M51.46 72.56 C72.83 47.42, 94.74 22.72, 113.78 0.86 M56.44 72.92 C74.65 52.58, 96.04 31.05, 119.43 0.46 M56.44 72.92 C81.73 45.16, 105.23 18.3, 119.43 0.46 M62.09 72.52 C80.07 51.93, 98.45 29.82, 124.41 0.82 M62.09 72.52 C79.04 54.05, 95.96 34.96, 124.41 0.82 M67.07 72.88 C80.12 59.08, 92.94 42.21, 130.06 0.43 M67.07 72.88 C80.66 58.14, 95.45 40.91, 130.06 0.43 M72.06 73.24 C93.79 49.66, 111.38 26.32, 135.04 0.79 M72.06 73.24 C89.58 51.72, 107.82 32.09, 135.04 0.79 M77.7 72.85 C104.72 44.35, 127.17 17, 140.69 0.4 M77.7 72.85 C96.13 51.38, 114.98 29.95, 140.69 0.4 M82.69 73.21 C104.71 47.57, 127.59 22.31, 145.67 0.76 M82.69 73.21 C101.71 51.47, 120.77 29.01, 145.67 0.76 M88.33 72.81 C110.76 47.24, 131.64 20.5, 151.32 0.36 M88.33 72.81 C109 48.14, 130.72 23.09, 151.32 0.36 M93.32 73.17 C111.15 55.01, 126.29 31.78, 156.3 0.72 M93.32 73.17 C110.49 51.39, 129.9 29.91, 156.3 0.72 M98.96 72.78 C118.26 54.28, 133.1 34.79, 161.95 0.33 M98.96 72.78 C111.06 59.03, 124.81 44.07, 161.95 0.33 M103.95 73.14 C115.72 58.92, 129.64 40.85, 166.93 0.69 M103.95 73.14 C126.22 47.2, 149.58 21.56, 166.93 0.69 M109.59 72.74 C123.31 56.85, 140.06 38.01, 172.58 0.29 M109.59 72.74 C124.38 56.56, 137.2 40.37, 172.58 0.29 M114.58 73.1 C138.07 45.66, 163.14 15.75, 177.56 0.65 M114.58 73.1 C131.86 52.94, 147.99 32.77, 177.56 0.65 M120.22 72.71 C133.34 57.46, 150.05 38.7, 183.21 0.26 M120.22 72.71 C135.3 57.12, 148.59 39.51, 183.21 0.26 M125.21 73.07 C147.35 45.64, 169.73 21.54, 188.19 0.62 M125.21 73.07 C146.83 48.56, 167.28 24.07, 188.19 0.62 M130.85 72.68 C149.9 47.27, 173.04 22.78, 193.84 0.22 M130.85 72.68 C153.31 46.45, 175.71 22.68, 193.84 0.22 M135.84 73.04 C157.75 47.97, 181.46 23.22, 198.82 0.58 M135.84 73.04 C157.01 47.37, 178.52 21.52, 198.82 0.58 M141.49 72.64 C163.6 47.54, 184.92 24.28, 203.81 0.94 M141.49 72.64 C157.97 55.11, 171.75 36.25, 203.81 0.94 M146.47 73 C164.74 50.07, 184.75 31.86, 209.45 0.55 M146.47 73 C169.77 45.07, 195.2 17.9, 209.45 0.55 M152.12 72.61 C170.98 50.38, 191.11 25.57, 214.44 0.91 M152.12 72.61 C167.16 54.48, 184.74 36.2, 214.44 0.91 M157.1 72.97 C171.7 55.53, 188.24 37.45, 220.08 0.51 M157.1 72.97 C171.53 57.7, 184.96 42.02, 220.08 0.51 M162.09 73.33 C182.79 52.34, 200.46 29.24, 225.07 0.87 M162.09 73.33 C176.54 57.94, 188.74 43.02, 225.07 0.87 M167.73 72.93 C187.78 52.57, 204.67 31.34, 230.71 0.48 M167.73 72.93 C192.31 45.48, 215.11 18.16, 230.71 0.48 M172.72 73.29 C192.03 53.03, 213.93 26.16, 235.7 0.84 M172.72 73.29 C187.14 54.62, 203.69 37.67, 235.7 0.84 M178.36 72.9 C196.89 49.98, 218.6 27.24, 241.34 0.45 M178.36 72.9 C191.33 57.21, 204.9 42.89, 241.34 0.45 M183.35 73.26 C207.11 47.48, 230.29 17.13, 246.33 0.81 M183.35 73.26 C195.69 56.95, 208.85 41.55, 246.33 0.81 M188.99 72.86 C203.94 55.74, 221.89 34.74, 251.97 0.41 M188.99 72.86 C203.18 55.08, 218.22 38.46, 251.97 0.41 M193.98 73.22 C219.55 44.07, 240.87 19.79, 256.96 0.77 M193.98 73.22 C208.25 55.94, 223.54 39.09, 256.96 0.77 M199.62 72.83 C217.99 48.42, 241.49 28.33, 262.6 0.38 M199.62 72.83 C214.61 56.41, 229.03 40.98, 262.6 0.38 M204.61 73.19 C223.33 53.31, 241.74 30.68, 267.59 0.74 M204.61 73.19 C229.17 46.22, 251.07 19.59, 267.59 0.74 M210.25 72.79 C224.49 54.46, 241.18 34.58, 273.23 0.34 M210.25 72.79 C227.21 52.37, 245.03 32.68, 273.23 0.34 M215.24 73.15 C229.43 57.1, 240.31 42.47, 278.22 0.7 M215.24 73.15 C237.32 48.52, 258.37 22.2, 278.22 0.7 M220.88 72.76 C240.5 49.7, 259.92 29.24, 283.86 0.31 M220.88 72.76 C245.23 46.31, 268.29 19.17, 283.86 0.31 M225.87 73.12 C244.42 48.72, 265.88 27.95, 288.85 0.67 M225.87 73.12 C251.66 44.25, 275.97 16.49, 288.85 0.67 M231.51 72.72 C253.24 45.13, 279.63 17.21, 293.84 1.03 M231.51 72.72 C244.39 57.29, 257.3 41.73, 293.84 1.03 M236.5 73.08 C256.68 52.52, 274.01 31.26, 299.48 0.63 M236.5 73.08 C252.49 52.88, 270.22 34.34, 299.48 0.63 M242.14 72.69 C264.56 48.95, 287.34 23.02, 304.47 0.99 M242.14 72.69 C265.99 45.72, 289.13 17.36, 304.47 0.99 M247.13 73.05 C268.76 49.79, 290 25.19, 310.11 0.6 M247.13 73.05 C271.93 45.31, 297.4 16.35, 310.11 0.6 M252.77 72.66 C272.81 49.72, 292.69 22.65, 315.1 0.96 M252.77 72.66 C274.82 46.14, 296.99 21.06, 315.1 0.96 M257.76 73.02 C279.06 47.65, 296.04 26.13, 320.74 0.56 M257.76 73.02 C279.13 48.78, 298.14 26.64, 320.74 0.56 M262.75 73.38 C286.03 44.74, 309.2 19.59, 325.73 0.92 M262.75 73.38 C284.29 48.03, 306.79 23.96, 325.73 0.92 M268.39 72.98 C284.12 55.13, 298.79 37.14, 331.37 0.53 M268.39 72.98 C286.16 50.17, 306.05 30.27, 331.37 0.53 M273.38 73.34 C296.56 45.22, 323.75 15.16, 336.36 0.89 M273.38 73.34 C289.13 55.59, 302.31 40.33, 336.36 0.89 M279.02 72.95 C303.53 43.87, 326.85 18.13, 342 0.49 M279.02 72.95 C293.37 55.49, 308.15 39.18, 342 0.49 M284.01 73.31 C298.42 57.71, 311.23 40.55, 346.99 0.85 M284.01 73.31 C308.02 43.63, 333.49 16.03, 346.99 0.85 M289.65 72.91 C311.51 49.12, 331.37 27.36, 352.63 0.46 M289.65 72.91 C314.28 43.1, 339.52 15.91, 352.63 0.46 M294.64 73.27 C311.15 55.94, 323.35 38.14, 357.62 0.82 M294.64 73.27 C315.12 50.08, 336.79 25.51, 357.62 0.82 M300.28 72.88 C323.32 47.78, 344.6 22, 363.26 0.43 M300.28 72.88 C320.59 50.02, 339.32 28.35, 363.26 0.43 M305.27 73.24 C321.19 53.97, 335.23 38.9, 368.25 0.79 M305.27 73.24 C320.38 55.99, 334.56 39.62, 368.25 0.79 M310.91 72.84 C326.89 57.23, 339.69 36.32, 372.58 1.9 M310.91 72.84 C323.53 58.18, 338.06 42.96, 372.58 1.9 M315.9 73.2 C339.86 47.6, 361.01 22.6, 376.91 3.02 M315.9 73.2 C338.69 46.17, 361.68 20.58, 376.91 3.02 M321.54 72.81 C338.51 50.61, 357.46 32.27, 379.93 5.64 M321.54 72.81 C336.35 54.99, 349.93 38.93, 379.93 5.64 M326.53 73.17 C341.29 57.14, 355.14 36.64, 382.29 9.02 M326.53 73.17 C347.2 48.97, 367.48 26.48, 382.29 9.02 M332.17 72.77 C348.91 51.78, 366.05 34.35, 384.66 12.4 M332.17 72.77 C345.89 56.87, 360.02 41.97, 384.66 12.4 M337.16 73.13 C350.96 56.83, 362.64 44.64, 384.39 18.8 M337.16 73.13 C350.38 58.41, 363.8 42.97, 384.39 18.8 M342.8 72.74 C358.9 55.85, 372.92 38.47, 384.13 25.19 M342.8 72.74 C359.81 54.2, 374.02 35.6, 384.13 25.19 M347.79 73.1 C354.66 65.33, 364.11 55.68, 384.53 30.84 M347.79 73.1 C358.88 60.26, 371.55 46.08, 384.53 30.84 M352.77 73.46 C358.94 66.21, 369.89 55.52, 384.27 37.23 M352.77 73.46 C360.31 65.04, 369.69 54.26, 384.27 37.23 M358.42 73.07 C364.98 65.59, 369.02 58.96, 384 43.63 M358.42 73.07 C368.68 61.66, 377.08 51.51, 384 43.63 M363.4 73.43 C367.59 70.26, 373.45 61.3, 384.4 49.27 M363.4 73.43 C367.97 69.41, 372.38 63.07, 384.4 49.27 M367.74 74.54 C374.28 66.78, 379.58 61.94, 384.14 55.67 M367.74 74.54 C374.31 67.19, 380 58.66, 384.14 55.67 M374.69 72.64 C376.19 69.99, 382.01 66.54, 385.19 60.56 M374.69 72.64 C377.37 68.95, 380.91 65.58, 385.19 60.56" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M18.25 0 M18.25 0 C129.54 2.72, 242.28 1.14, 365.75 0 M18.25 0 C127.66 -1.41, 238.58 -1.21, 365.75 0 M365.75 0 C378.27 0.9, 382.95 5.36, 384 18.25 M365.75 0 C380.07 0.47, 383.93 4.97, 384 18.25 M384 18.25 C384.02 33.84, 384.87 48.45, 384 54.75 M384 18.25 C384.01 28.7, 384.22 39.53, 384 54.75 M384 54.75 C382.32 67.61, 378.7 73.98, 365.75 73 M384 54.75 C385.93 68.19, 378.42 74.36, 365.75 73 M365.75 73 C288.24 70.59, 213.1 72.01, 18.25 73 M365.75 73 C265.08 74.84, 163.23 75.38, 18.25 73 M18.25 73 C7.8 72.9, 1.58 68.54, 0 54.75 M18.25 73 C7.06 72.31, -0.46 65.42, 0 54.75 M0 54.75 C0.31 45.73, -1.55 34.03, 0 18.25 M0 54.75 C0.72 40.09, -0.05 27.49, 0 18.25 M0 18.25 C1.55 7.28, 6.06 -1.74, 18.25 0 M0 18.25 C1.91 4.6, 5.29 1.6, 18.25 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(297.58984375 586.578125) rotate(0 70.3125 28.80000000000001)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories: []</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files: []</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g stroke-linecap="round" transform="translate(297.01171875 700.19140625) rotate(0 192 63)"><path d="M4.68 12.02 C4.68 12.02, 4.68 12.02, 4.68 12.02 M4.68 12.02 C4.68 12.02, 4.68 12.02, 4.68 12.02 M1.14 22.19 C5.68 16.12, 10.34 11.35, 18.19 2.57 M1.14 22.19 C5.63 15.12, 11.63 10.54, 18.19 2.57 M2.84 26.32 C9.4 19.34, 14.38 14.47, 24.49 1.42 M2.84 26.32 C7.02 21.88, 12.43 16.24, 24.49 1.42 M3.24 31.97 C12.37 22.32, 23.31 9.97, 30.14 1.02 M3.24 31.97 C8.75 26.69, 14.07 19.43, 30.14 1.02 M2.98 38.36 C11.61 29.09, 22.34 16.73, 34.47 2.14 M2.98 38.36 C14.61 26.7, 24.62 13.57, 34.47 2.14 M2.72 44.76 C8.44 35.48, 20.15 28.58, 39.46 2.5 M2.72 44.76 C9.81 35.38, 16.68 27.81, 39.46 2.5 M3.11 50.4 C17.54 33.89, 29.94 20.31, 45.1 2.1 M3.11 50.4 C20.74 31.63, 36.06 13.37, 45.1 2.1 M2.85 56.8 C18.58 35.97, 39.38 14.61, 50.09 2.46 M2.85 56.8 C15.02 42.55, 25.62 29.03, 50.09 2.46 M3.24 62.45 C13.28 49.17, 26.13 38.7, 55.73 2.07 M3.24 62.45 C14.41 50.21, 23.72 38.66, 55.73 2.07 M2.98 68.84 C13.36 56.63, 24.49 40.18, 60.72 2.43 M2.98 68.84 C25.38 43.36, 46.78 18.82, 60.72 2.43 M2.72 75.24 C27.68 45.78, 49.87 21.46, 66.36 2.03 M2.72 75.24 C27.36 48.58, 50.99 21.41, 66.36 2.03 M3.12 80.88 C26.98 53.98, 51.79 26.74, 71.35 2.39 M3.12 80.88 C24.82 57.11, 46.21 30.7, 71.35 2.39 M2.85 87.28 C25.83 58.82, 49.6 32.81, 76.33 2.75 M2.85 87.28 C18.86 69.84, 32.85 53.27, 76.33 2.75 M3.25 92.92 C31.02 64.11, 55.21 32.05, 81.98 2.36 M3.25 92.92 C29.09 64.51, 54.01 35.08, 81.98 2.36 M0.36 102.34 C18.06 82.88, 34.35 63.19, 86.96 2.72 M0.36 102.34 C22.5 76.81, 45.99 50.02, 86.96 2.72 M2.07 106.47 C35.94 64.04, 71.16 24.59, 92.61 2.33 M2.07 106.47 C29.3 74.98, 57.21 42, 92.61 2.33 M3.78 110.61 C32.64 72.16, 65.08 38.52, 97.59 2.69 M3.78 110.61 C27.71 83.59, 50.59 56.73, 97.59 2.69 M5.48 114.74 C31.81 84.38, 61.24 51.3, 103.24 2.29 M5.48 114.74 C38.37 76.78, 69.65 40.61, 103.24 2.29 M7.85 118.12 C37.24 84.78, 67.86 51.75, 108.22 2.65 M7.85 118.12 C40.51 79.72, 74.23 40.65, 108.22 2.65 M10.86 120.75 C33.15 93.28, 59.27 65.67, 113.87 2.26 M10.86 120.75 C41.44 83.49, 74.67 47.06, 113.87 2.26 M14.54 122.62 C54.9 77.33, 93.45 31.72, 118.85 2.62 M14.54 122.62 C36.96 96.13, 58.59 69.9, 118.85 2.62 M18.21 124.48 C60.03 78.24, 98.36 27.73, 124.5 2.22 M18.21 124.48 C47.99 89.82, 78.58 52.73, 124.5 2.22 M23.2 124.84 C60.93 81.34, 96.9 37.41, 129.48 2.58 M23.2 124.84 C51.15 90.76, 80.7 57.55, 129.48 2.58 M27.53 125.96 C67.64 79.48, 108.95 32.16, 135.13 2.19 M27.53 125.96 C50.35 100, 70.45 76.16, 135.13 2.19 M32.52 126.32 C67.96 82.8, 105.29 42.95, 140.11 2.55 M32.52 126.32 C55.09 102.84, 75.65 76.61, 140.11 2.55 M37.51 126.68 C76.58 83.49, 111.39 39.46, 145.76 2.15 M37.51 126.68 C68.68 90.35, 99.78 54.4, 145.76 2.15 M43.15 126.29 C79.05 86.81, 113.32 46.01, 150.74 2.51 M43.15 126.29 C83.16 78.74, 122.47 32.63, 150.74 2.51 M48.14 126.65 C84.95 86.46, 121.01 43.96, 156.39 2.12 M48.14 126.65 C77.47 92.07, 106.15 57.86, 156.39 2.12 M53.12 127.01 C94.06 83.46, 132.13 35.83, 161.37 2.48 M53.12 127.01 C83.35 90.25, 115.88 54.9, 161.37 2.48 M58.77 126.61 C88.9 91.94, 120.62 55.5, 167.02 2.08 M58.77 126.61 C80.71 101.71, 103.08 74.94, 167.02 2.08 M63.75 126.97 C94.83 94.26, 121.35 59.65, 172 2.44 M63.75 126.97 C99.19 87.16, 134.08 46.33, 172 2.44 M69.4 126.58 C96.06 98.29, 123.08 66.58, 177.65 2.05 M69.4 126.58 C94.05 98.86, 118.43 72.82, 177.65 2.05 M74.38 126.94 C110.22 87.09, 146.32 44.12, 182.63 2.41 M74.38 126.94 C112.7 80.36, 152.52 34.24, 182.63 2.41 M80.03 126.54 C112.63 85.84, 146.64 47.21, 188.28 2.01 M80.03 126.54 C118.52 83.47, 154.62 39.47, 188.28 2.01 M85.01 126.9 C123.66 84.49, 160.64 41.03, 193.26 2.37 M85.01 126.9 C107.24 101.3, 128.84 75.24, 193.26 2.37 M90.66 126.51 C128.32 79.98, 168.35 34.11, 198.25 2.73 M90.66 126.51 C125.23 87.51, 159.47 48.47, 198.25 2.73 M95.64 126.87 C136.32 80.89, 176.13 32.73, 203.89 2.34 M95.64 126.87 C130.67 85.7, 167.03 45.03, 203.89 2.34 M101.29 126.47 C138.72 85.68, 174.7 42.24, 208.88 2.7 M101.29 126.47 C141.27 81.26, 181.48 32.8, 208.88 2.7 M106.27 126.83 C133.13 96.99, 159.87 69.21, 214.52 2.31 M106.27 126.83 C139.22 87.92, 173.99 50.04, 214.52 2.31 M111.92 126.44 C139.17 92.95, 170.03 64.03, 219.51 2.67 M111.92 126.44 C140.35 94.34, 169.24 63.52, 219.51 2.67 M116.9 126.8 C139.41 102.01, 161.49 74.65, 225.15 2.27 M116.9 126.8 C143.96 97.47, 169.6 65.58, 225.15 2.27 M122.55 126.4 C151.81 92.7, 184.46 60.16, 230.14 2.63 M122.55 126.4 C144.55 101.98, 167.71 77.15, 230.14 2.63 M127.53 126.76 C155.24 93.9, 184.87 59.39, 235.78 2.24 M127.53 126.76 C162.55 85.69, 197.14 44.32, 235.78 2.24 M133.18 126.37 C156.24 97.59, 181.25 74.22, 240.77 2.6 M133.18 126.37 C169.91 83.87, 205.04 43.06, 240.77 2.6 M138.16 126.73 C171.62 83.67, 210.01 43.26, 246.41 2.2 M138.16 126.73 C162.75 98.55, 185.88 71.86, 246.41 2.2 M143.81 126.33 C167.53 96.38, 195.44 68.77, 251.4 2.56 M143.81 126.33 C175.38 87.65, 208.52 52.45, 251.4 2.56 M148.79 126.69 C186.15 81.29, 226.32 36.57, 257.04 2.17 M148.79 126.69 C185.38 86.13, 221.92 44.72, 257.04 2.17 M154.44 126.3 C190.78 83.15, 229.34 36.03, 262.03 2.53 M154.44 126.3 C178.51 97.01, 203.83 70.69, 262.03 2.53 M159.42 126.66 C189.83 93.77, 217.8 63.16, 267.67 2.13 M159.42 126.66 C196.22 84.11, 234.53 42.17, 267.67 2.13 M165.07 126.27 C199.58 88.17, 233.46 45.58, 272.66 2.49 M165.07 126.27 C204.81 83.18, 243.36 38.31, 272.66 2.49 M170.05 126.63 C191.4 102.17, 214.65 75.55, 278.3 2.1 M170.05 126.63 C194.79 98.87, 219.21 68.85, 278.3 2.1 M175.04 126.99 C218.62 76.89, 259.45 31.99, 283.29 2.46 M175.04 126.99 C200.78 100.7, 225.33 71.6, 283.29 2.46 M180.68 126.59 C214.02 88.6, 246.67 51.74, 288.93 2.06 M180.68 126.59 C212.29 91.19, 241.62 56.43, 288.93 2.06 M185.67 126.95 C215.71 95.07, 243.49 60.99, 293.92 2.42 M185.67 126.95 C216.01 94.84, 243.45 62.69, 293.92 2.42 M191.31 126.56 C229.63 85.76, 263.97 41.71, 299.56 2.03 M191.31 126.56 C222.59 88.19, 255.96 51.19, 299.56 2.03 M196.3 126.92 C217.19 100.03, 243 74.87, 304.55 2.39 M196.3 126.92 C218.88 99.48, 243.28 72.58, 304.55 2.39 M201.94 126.52 C245.64 78.21, 284.33 30.24, 309.54 2.75 M201.94 126.52 C240.53 84.15, 277.64 41.48, 309.54 2.75 M206.93 126.88 C248.75 80.42, 287.71 35.97, 315.18 2.36 M206.93 126.88 C240.59 87.82, 273.05 49.59, 315.18 2.36 M212.57 126.49 C242.06 93.98, 270.55 61.9, 320.17 2.72 M212.57 126.49 C238.53 99.42, 261.94 72.17, 320.17 2.72 M217.56 126.85 C255.21 80.1, 296.4 36.1, 325.81 2.32 M217.56 126.85 C248.46 91.27, 280.97 56.5, 325.81 2.32 M223.2 126.45 C245.32 96.89, 272.92 68.02, 330.8 2.68 M223.2 126.45 C246.93 99.22, 269.26 73.84, 330.8 2.68 M228.19 126.81 C266.27 81.56, 302.8 39.44, 336.44 2.29 M228.19 126.81 C268.68 80.87, 310.31 31.49, 336.44 2.29 M233.83 126.42 C261.99 92.9, 293.86 58.89, 341.43 2.65 M233.83 126.42 C265.72 90.09, 297.62 53.24, 341.43 2.65 M238.82 126.78 C276.56 81.01, 314.76 40.31, 347.07 2.25 M238.82 126.78 C275.07 83.85, 309.78 43.16, 347.07 2.25 M244.47 126.38 C281.08 86.31, 311.83 47.08, 353.37 1.1 M244.47 126.38 C284.42 79.98, 324.2 34.41, 353.37 1.1 M249.45 126.74 C283.83 89.61, 316.66 48.25, 358.36 1.46 M249.45 126.74 C288.06 81.49, 329.09 35.62, 358.36 1.46 M255.1 126.35 C286.95 88.5, 321.78 49.74, 362.69 2.58 M255.1 126.35 C278.54 101.51, 299.57 76.6, 362.69 2.58 M260.08 126.71 C289.9 92.16, 318.76 59.09, 367.68 2.94 M260.08 126.71 C286.6 97.68, 309.24 69.16, 367.68 2.94 M265.73 126.32 C304.67 81.34, 343.89 36.13, 371.35 4.81 M265.73 126.32 C296.05 89.95, 328.32 54.46, 371.35 4.81 M270.71 126.68 C312.11 79.85, 350.1 38.08, 375.03 6.68 M270.71 126.68 C308.29 85.27, 344.32 43.1, 375.03 6.68 M276.36 126.28 C302.37 95.05, 332.55 61.6, 377.39 10.06 M276.36 126.28 C300.17 99.46, 322.39 72.19, 377.39 10.06 M281.34 126.64 C304.76 96.82, 332.3 67.75, 380.41 12.68 M281.34 126.64 C307.49 95.18, 332.91 65.5, 380.41 12.68 M286.33 127 C314.42 93.01, 345.57 57.56, 381.46 17.57 M286.33 127 C313.51 94.85, 341.77 62.68, 381.46 17.57 M291.97 126.61 C313.48 100.45, 339.17 75.97, 383.16 21.7 M291.97 126.61 C311.58 103.09, 333.11 79.56, 383.16 21.7 M296.96 126.97 C320.59 100.34, 342.62 72.36, 384.87 25.84 M296.96 126.97 C321.66 99.71, 344.27 72.56, 384.87 25.84 M302.6 126.57 C334.11 86.35, 368.78 51.97, 385.27 31.48 M302.6 126.57 C330.75 94.29, 357.85 62.13, 385.27 31.48 M307.59 126.93 C326.81 105.47, 343.03 83.78, 385 37.88 M307.59 126.93 C327.54 105.51, 344.6 84.21, 385 37.88 M313.23 126.54 C332.05 106.16, 349.05 86.64, 385.4 43.52 M313.23 126.54 C332.89 103.96, 352.85 83.32, 385.4 43.52 M318.22 126.9 C331.98 110.58, 346.57 93.14, 385.14 49.92 M318.22 126.9 C331.03 111.1, 344.19 94.95, 385.14 49.92 M323.86 126.5 C335.72 108.89, 350.56 93.22, 385.53 55.56 M323.86 126.5 C345.11 101.47, 364.04 79.57, 385.53 55.56 M328.85 126.86 C346.1 108.02, 361.21 90.1, 385.27 61.96 M328.85 126.86 C342.2 112.06, 355.94 96.74, 385.27 61.96 M334.49 126.47 C350.84 109.97, 364.71 94.6, 385.67 67.6 M334.49 126.47 C353.49 104.81, 372.17 82.14, 385.67 67.6 M339.48 126.83 C348.96 114.71, 363.17 101.01, 385.4 74 M339.48 126.83 C354.75 110.09, 368.15 93.09, 385.4 74 M345.12 126.43 C354.84 117.22, 365.07 103.77, 385.8 79.64 M345.12 126.43 C358.46 110.23, 371.31 95.91, 385.8 79.64 M350.11 126.79 C364.3 111.08, 375.22 97.18, 385.54 86.04 M350.11 126.79 C363.14 112.28, 374.94 98.31, 385.54 86.04 M354.44 127.91 C362.81 117.31, 370.87 110.18, 385.28 92.44 M354.44 127.91 C362.9 118.85, 369.83 111.91, 385.28 92.44 M360.74 126.76 C367.63 119.85, 374.61 113.88, 386.98 96.57 M360.74 126.76 C369 116.72, 377.31 108.85, 386.98 96.57 M367.7 124.86 C371.04 118.25, 376.15 112.61, 382.13 108.25 M367.7 124.86 C371.11 120.48, 374.22 117.39, 382.13 108.25" stroke="#ffec99" stroke-width="0.5" fill="none"></path><path d="M31.5 0 M31.5 0 C144.73 2.16, 257.89 1.55, 352.5 0 M31.5 0 C123.21 -0.03, 215.81 -0.04, 352.5 0 M352.5 0 C373.48 -1.54, 384.54 11.54, 384 31.5 M352.5 0 C371.52 1.89, 382.06 11.18, 384 31.5 M384 31.5 C384.2 57.65, 382.83 81.45, 384 94.5 M384 31.5 C384.94 50.55, 383.94 67.7, 384 94.5 M384 94.5 C385.07 116.86, 374.35 127.03, 352.5 126 M384 94.5 C385.17 117.04, 373.57 125.11, 352.5 126 M352.5 126 C247.11 128.47, 140.42 127.09, 31.5 126 M352.5 126 C271.97 126.76, 193.29 127.03, 31.5 126 M31.5 126 C11.34 124.76, -1.26 114.07, 0 94.5 M31.5 126 C9.77 126.57, -1.64 113.31, 0 94.5 M0 94.5 C0.36 74.76, 1.16 53.5, 0 31.5 M0 94.5 C-0.19 81.51, -0.09 68.38, 0 31.5 M0 31.5 C-0.91 8.69, 10.9 0.79, 31.5 0 M0 31.5 C2.16 12.64, 11.39 -0.47, 31.5 0" stroke="#000000" stroke-width="1" fill="none"></path></g><g transform="translate(302.01171875 705.19140625) rotate(0 145.3125 57.60000000000002)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">directories:</text><text x="0" y="19.2" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge"> - name: a</text><text x="0" y="38.4" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   digest: &lt;directory-a-digest&gt;</text><text x="0" y="57.599999999999994" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">   size: 0</text><text x="0" y="76.8" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">files: []</text><text x="0" y="96" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#000000" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">symlinks: []</text></g><g transform="translate(303.75390625 45.62890625) rotate(0 89.0625 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_WITH_KEEP</text></g><g transform="translate(308.25390625 240.4765625) rotate(0 98.4375 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_COMPLICATED</text></g><g transform="translate(307.28125 554.6148437500001) rotate(0 51.5625 9.600000000000023)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_A</text></g><g transform="translate(310.6875 674.4585937500001) rotate(0 51.5625 9.600000000000023)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#f08c00" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">DIRECTORY_B</text></g><g transform="translate(18.95703125 42.53671874999998) rotate(0 28.125 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_A</text></g><g transform="translate(22.55078125 130.24374999999998) rotate(0 28.125 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_B</text></g><g transform="translate(13.5546875 210.27109374999998) rotate(0 46.875 9.599999999999994)"><text x="0" y="0" font-family="Cascadia, Segoe UI Emoji" font-size="16px" fill="#1971c2" text-anchor="start" style="white-space: pre;" direction="ltr" dominant-baseline="text-before-edge">BLOB_EMPTY</text></g><g stroke-linecap="round"><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-0.52 1.13 C-60.08 19.9, -297.46 94.83, -357.2 113.61 M1.4 0.68 C-58.25 18.98, -298.08 93.09, -357.74 111.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-331.96 94.73 C-340.78 97.36, -347.89 102.93, -357.37 112.7 M-333.95 93.43 C-339.75 97.47, -345.42 102.99, -358.69 111" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(388.15234375 147.38671875) rotate(0 -178.16880695513453 57.143675736445005)"><path d="M-325.89 114.33 C-336.76 110.86, -345.74 110.35, -357.37 112.7 M-327.89 113.04 C-335.08 112.25, -342.26 112.89, -358.69 111" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M-0.42 1.04 C43.76 -38.95, 220.7 -199.19, 264.79 -239.05 M1.55 0.54 C45.63 -39.42, 220.36 -198.5, 263.94 -238.07" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M251.72 -211.19 C251.81 -219.53, 257.18 -225.36, 265.07 -236.65 M250.73 -212.1 C253.06 -218.02, 257.3 -223.31, 264.08 -238.4" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(399.61021320513464 319.000855513555) rotate(0 132.18131301403048 -119.00356223583219)"><path d="M237.91 -226.37 C241.88 -230.52, 251.02 -232.22, 265.07 -236.65 M236.92 -227.29 C242.47 -229.5, 250.03 -231.14, 264.08 -238.4" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M0.21 0.21 C-60.17 -24.98, -301.37 -126.03, -361.5 -151.72 M-1.14 -0.73 C-61.8 -26.18, -302.73 -127.84, -362.74 -153.25" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M-332.43 -152.64 C-341.83 -151.66, -356.48 -151.02, -362.88 -151.77 M-332.23 -151.12 C-344.25 -151.99, -356 -152.45, -362.93 -152.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(396.39146320513464 414.62194926355494) rotate(0 -181.26559592220468 -76.52005761642474)"><path d="M-340.42 -133.74 C-346.81 -139.68, -358.54 -145.95, -362.88 -151.77 M-340.22 -132.22 C-349.22 -140.26, -357.91 -147.96, -362.93 -152.59" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M-0.84 0.01 C13.92 -16, 74.3 -80.87, 89.45 -97.18 M0.92 -1.04 C15.95 -16.73, 76.9 -79.62, 91.58 -95.58" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M79.65 -69.91 C84.59 -79.16, 87.99 -87.88, 90.6 -97.23 M78.91 -67.92 C83.62 -76.08, 87.25 -85.83, 90.62 -96.45" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g><g transform="translate(407.44140625 751.55078125) rotate(0 45.37286624398547 -48.585232615470886)"><path d="M64.75 -84.03 C75.18 -87.85, 84.2 -91.25, 90.6 -97.23 M64.01 -82.04 C73.25 -85.89, 81.47 -91.29, 90.62 -96.45" stroke="#1e1e1e" stroke-width="1" fill="none"></path></g></g><mask></mask></svg>
\ No newline at end of file
diff --git a/users/fogti/.gitignore b/users/fogti/.gitignore
new file mode 100644
index 0000000000..b8553ace55
--- /dev/null
+++ b/users/fogti/.gitignore
@@ -0,0 +1,2 @@
+.#*
+target/
diff --git a/users/fogti/OWNERS b/users/fogti/OWNERS
new file mode 100644
index 0000000000..fb396265ca
--- /dev/null
+++ b/users/fogti/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+fogti
diff --git a/users/fogti/dbwospof.md b/users/fogti/dbwospof.md
new file mode 100644
index 0000000000..f1d68cde06
--- /dev/null
+++ b/users/fogti/dbwospof.md
@@ -0,0 +1,112 @@
+# distributed build without single points of failure
+
+## problem statement
+> If we want to distribute a build across several build nodes, and want to avoid
+> a "single point of failure", what needs to be considered?
+
+## motivation
+
+* distribute the build across several build nodes, because some packages take
+  extremely long to build
+  (e.g. `firefox`, `thunderbird`, `qtwebengine`, `webkitgtk`, ...)
+* avoid a centralised setup like e.g. with Hydra, because we want to keep using
+  an on-demand workflow as usual with Nix
+  (e.g. `nixos-rebuild` on each host when necessary).
+
+## list of abbreviations
+
+<dl>
+  <dt>CA</dt>		<dd>content-addressed</dd>
+  <dt>drv</dt>		<dd>derivation</dd>
+  <dt>FOD</dt>		<dd>fixed-output derivation</dd>
+  <dt>IHA</dt>		<dd>input-hash-addressed</dd>
+  <dt>inhash</dt>	<dd>input hash</dd>
+  <dt>outhash</dt>	<dd>output hash</dd>
+</dl>
+
+## build graph
+
+The build graph can't be easily distributed. It is instead left on the coordinator,
+and the build nodes just get individual build jobs, which just consist of
+derivations (and some information about how to get the inputs from some central
+or distributed store (e.g. Ceph), this may be transmitted "out of band").
+
+## inhash-exclusive
+
+It is necessary that each derivation build is exclusive in the sense that
+the same derivation is never build multiple times simultaneously, because
+this otherwise either wastes compute resources (obviously) and, in the case
+of non-deterministic builds, increases complexity
+(the store needs to decide which result to prefer, and the build nodes with
+"losing" build results need to pull the "winning" build results from the store,
+replacing the local version). Although this might be unnecessary in case
+of IHA drvs, enforcing it always reduces the amount of possible suprising
+results when mixing CA drvs and IHA drvs.
+
+## what can be meaningfully distributed
+
+The following is strongly opinionated, but I consider the following
+(based upon the original build graph implementation from yzix 12.2021):
+* We can't push the entire build graph to each build node, because they would
+  overlap 100%, and thus create extreme contention on the inhash-exclusive lock
+* We could try to split the build graph into multiple parts with independent
+  inputs (partitioning), but this can be really complex, and I'm not sure
+  if it is worth it... This also basically excludes the yzix node types
+  [ `Eval`, `AssertEqual` ] (should be done by the evaluator).
+  Implementing this option however would make an abort of a build graph
+  (the simple variant does not kill running tasks,
+  just stop new tasks from being scheduled) really hard, and complex to get right.
+* It does not make sense to distribute "node tasks" across build nodes which
+  almost exclusively interact with the store, and are not CPU-bound, but I/O bound.
+  This applies to most, if not all, useful FODs. It applies to the yzix node types
+  [ `Dump`, `UnDump`, `Fetch`, `Require` ] (should be performed by evaluator+store).
+* TODO: figure out how to do forced rebuilds (e.g. also prefer a node which is not
+  the build node of the previous realisation of that task)
+
+## coarse per-derivation workflow
+
+```
+    derivation
+    |        |
+    |        |
+   key     build
+    |        |
+    |        |
+    V        V
+  inhash  outhash
+    |     (either CA or IHA)
+     \      /
+      \    /
+       \  /
+    realisation
+```
+
+## build results
+
+Just for completeness, two build results are currently considered:
+
+* success: the build succeeded, and the result is uploaded to the central store
+* failure: the build failed (e.g. build process terminated via error exit code or was killed)
+* another case might be "partial": the build succeeded, but uploading to the
+  central store failed (the result is only available on the build node that built it).
+  This case is interesting, because we don't need to rerun the build, just the upload step
+  needs to be fixed/done semi-manually (e.g. maybe the central store ran out of storage,
+  or the network was unavailable)
+
+## build task queue
+
+It is naรฏve to think that something like a queue via `rabbitmq` (`AMQP`) or `MQTT`
+suffices, because some requirements are missing:
+
+1. some way to push build results to the clients, and these should be associated
+  to the build inputs (a hacky way might use multiple queues for that, e.g.
+  a `tasks` input queue and a `done` output queue).
+2. some way to lock "inhashes" (see section inhash-exclusive).
+
+The second point is somewhat easy to realise using `etcd`, and using the `watch`
+mechanism it can be used to simulate a queue, and the inhash-addressing of
+queued derivations can be seamlessly integrated.
+
+TODO: maybe we want to adjust the priorities of tasks in the queue, but Nix currently
+doesn't seem to do this, so consider this only when it starts to make sense as a
+performance or lag optimization.
diff --git a/users/fogti/store-ref-scanner/.gitignore b/users/fogti/store-ref-scanner/.gitignore
new file mode 100644
index 0000000000..5a44eef09a
--- /dev/null
+++ b/users/fogti/store-ref-scanner/.gitignore
@@ -0,0 +1 @@
+/Cargo.lock
diff --git a/users/fogti/store-ref-scanner/Cargo.toml b/users/fogti/store-ref-scanner/Cargo.toml
new file mode 100644
index 0000000000..0ed0c20a39
--- /dev/null
+++ b/users/fogti/store-ref-scanner/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "store-ref-scanner"
+version = "0.1.0"
+description = "scanner/extractor of Nix-like store paths from byte arrays/streams"
+license = "MIT OR Apache-2.0"
+categories = ["no-std", "parsing"]
+edition = "2021"
+homepage = "https://cs.tvl.fyi/depot/-/tree/users/fogti/store-ref-scanner"
+include = ["/src"]
+
+[dependencies]
diff --git a/users/fogti/store-ref-scanner/default.nix b/users/fogti/store-ref-scanner/default.nix
new file mode 100644
index 0000000000..38f3fd64ec
--- /dev/null
+++ b/users/fogti/store-ref-scanner/default.nix
@@ -0,0 +1,49 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  sourceFilter = name: type:
+    let
+      baseName = builtins.baseNameOf (builtins.toString name);
+    in
+    (baseName == "Cargo.toml")
+    || (type == "directory" && baseName == "src")
+    || (lib.hasSuffix ".rs" baseName)
+  ;
+in
+
+pkgs.buildRustCrate rec {
+  pname = "store-ref-scanner";
+  crateName = "store-ref-scanner";
+  version = "0.1.0";
+  edition = "2021";
+  src = lib.cleanSourceWith { filter = sourceFilter; src = ./.; };
+
+  passthru.tests = pkgs.buildRustCrate {
+    pname = "store-ref-scanner-tests";
+    inherit crateName src version edition;
+    buildTests = true;
+    postInstall = ''
+      set -ex
+      export RUST_BACKTRACE=1
+      # recreate a file hierarchy as when running tests with cargo
+      # the source for test data
+      # build outputs
+      testRoot=target/debug
+      mkdir -p $testRoot
+      chmod +w -R .
+      # test harness executables are suffixed with a hash,
+      # like cargo does this allows to prevent name collision
+      # with the main executables of the crate
+      hash=$(basename $out)
+      ls -lasR $out
+      for file in $out/tests/*; do
+        f=$testRoot/$(basename $file)-$hash
+        cp $file $f
+        $f 2>&1 | tee -a $out/tests.log
+      done
+      rm -rf $out/tests
+      set +ex
+    '';
+  };
+
+}
diff --git a/users/fogti/store-ref-scanner/fuzz/.gitignore b/users/fogti/store-ref-scanner/fuzz/.gitignore
new file mode 100644
index 0000000000..b400c27826
--- /dev/null
+++ b/users/fogti/store-ref-scanner/fuzz/.gitignore
@@ -0,0 +1,2 @@
+corpus
+artifacts
diff --git a/users/fogti/store-ref-scanner/fuzz/Cargo.lock b/users/fogti/store-ref-scanner/fuzz/Cargo.lock
new file mode 100644
index 0000000000..7395dec05e
--- /dev/null
+++ b/users/fogti/store-ref-scanner/fuzz/Cargo.lock
@@ -0,0 +1,44 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "arbitrary"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de"
+
+[[package]]
+name = "cc"
+version = "1.0.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
+
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36a9a84a6e8b55dfefb04235e55edb2b9a2a18488fcae777a6bdaa6f06f1deb3"
+dependencies = [
+ "arbitrary",
+ "cc",
+ "once_cell",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
+
+[[package]]
+name = "store-ref-scanner"
+version = "0.1.0"
+
+[[package]]
+name = "store-ref-scanner-fuzz"
+version = "0.0.0"
+dependencies = [
+ "libfuzzer-sys",
+ "store-ref-scanner",
+]
diff --git a/users/fogti/store-ref-scanner/fuzz/Cargo.toml b/users/fogti/store-ref-scanner/fuzz/Cargo.toml
new file mode 100644
index 0000000000..1832be0032
--- /dev/null
+++ b/users/fogti/store-ref-scanner/fuzz/Cargo.toml
@@ -0,0 +1,36 @@
+[package]
+name = "store-ref-scanner-fuzz"
+version = "0.0.0"
+authors = ["Automatically generated"]
+publish = false
+edition = "2018"
+
+[package.metadata]
+cargo-fuzz = true
+
+[dependencies]
+libfuzzer-sys = "0.4"
+
+[dependencies.store-ref-scanner]
+path = ".."
+
+# Prevent this from interfering with workspaces
+[workspace]
+members = ["."]
+
+[[bin]]
+name = "hbm-roundtrip"
+path = "fuzz_targets/hbm-roundtrip.rs"
+test = false
+doc = false
+
+[[bin]]
+name = "nocrash"
+path = "fuzz_targets/nocrash.rs"
+test = false
+doc = false
+
+[profile.release]
+incremental = false
+overflow-checks = true
+panic = "abort"
diff --git a/users/fogti/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs b/users/fogti/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs
new file mode 100644
index 0000000000..9e21a7738a
--- /dev/null
+++ b/users/fogti/store-ref-scanner/fuzz/fuzz_targets/hbm-roundtrip.rs
@@ -0,0 +1,10 @@
+#![no_main]
+use libfuzzer_sys::fuzz_target;
+
+fuzz_target!(|data: [u8; 16]| {
+    use store_ref_scanner::HalfBytesMask;
+    let a = HalfBytesMask(data);
+    let b = a.into_expanded();
+    let c = HalfBytesMask::from_expanded(b);
+    assert_eq!(a, c);
+});
diff --git a/users/fogti/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs b/users/fogti/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs
new file mode 100644
index 0000000000..48100a628d
--- /dev/null
+++ b/users/fogti/store-ref-scanner/fuzz/fuzz_targets/nocrash.rs
@@ -0,0 +1,9 @@
+#![no_main]
+use libfuzzer_sys::fuzz_target;
+
+fuzz_target!(|data: &[u8]| {
+    use store_ref_scanner::{StoreRefScanner, StoreSpec};
+
+    StoreRefScanner::new(&data[..], &StoreSpec::DFL_NIX2).count();
+    StoreRefScanner::new(&data[..], &StoreSpec::DFL_YZIX1).count();
+});
diff --git a/users/fogti/store-ref-scanner/src/hbm.rs b/users/fogti/store-ref-scanner/src/hbm.rs
new file mode 100644
index 0000000000..2520efd836
--- /dev/null
+++ b/users/fogti/store-ref-scanner/src/hbm.rs
@@ -0,0 +1,167 @@
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub struct HalfBytesMask(pub [u8; 16]);
+
+#[allow(clippy::as_conversions, clippy::zero_prefixed_literal)]
+impl HalfBytesMask {
+    pub const B32_REVSHA256: HalfBytesMask =
+        HalfBytesMask([0, 0, 0, 0, 0, 0, 255, 3, 0, 0, 0, 0, 222, 127, 207, 7]);
+
+    pub const B64_BLAKE2B256: HalfBytesMask = HalfBytesMask([
+        0, 0, 0, 0, 0, 8, 255, 3, 254, 255, 255, 135, 254, 255, 255, 7,
+    ]);
+
+    pub const DFL_REST: HalfBytesMask = HalfBytesMask([
+        0, 0, 0, 0, 0, 104, 255, 163, 254, 255, 255, 135, 254, 255, 255, 7,
+    ]);
+
+    #[inline]
+    pub const fn from_expanded(x: [bool; 128]) -> Self {
+        let mut ret = [0u8; 16];
+        let mut idx = 0;
+        while idx < 16 {
+            let fin = idx * 8;
+            let mut idx2 = 0;
+            while idx2 < 8 {
+                if x[fin + idx2] {
+                    ret[idx] += (1 << idx2) as u8;
+                }
+                idx2 += 1;
+            }
+            idx += 1;
+        }
+        Self(ret)
+    }
+
+    /// create a mask by allowing all characters via the mask which are included in the given string
+    pub fn from_bytes(s: &[u8]) -> Self {
+        s.iter().fold(Self([0u8; 16]), |mut ret, &i| {
+            ret.set(i, true);
+            ret
+        })
+    }
+
+    pub const fn into_expanded(self) -> [bool; 128] {
+        let Self(ihbm) = self;
+        let mut ret = [false; 128];
+        let mut idx = 0;
+        while idx < 16 {
+            let fin = idx * 8;
+            let curi = ihbm[idx];
+            let mut idx2 = 0;
+            while idx2 < 8 {
+                ret[fin + idx2] = (curi >> idx2) & 0b1 != 0;
+                idx2 += 1;
+            }
+            idx += 1;
+        }
+        ret
+    }
+
+    pub fn contains(&self, byte: u8) -> bool {
+        if byte >= 0x80 {
+            false
+        } else {
+            (self.0[usize::from(byte / 8)] >> u32::from(byte % 8)) & 0b1 != 0
+        }
+    }
+
+    pub fn set(&mut self, byte: u8, allow: bool) {
+        if byte >= 0x80 {
+            if cfg!(debug_assertions) {
+                panic!(
+                    "tried to manipulate invalid byte {:?} in HalfBytesMask",
+                    byte
+                );
+            } else {
+                return;
+            }
+        }
+        let block = &mut self.0[usize::from(byte / 8)];
+        let bitpat = (1 << u32::from(byte % 8)) as u8;
+        if allow {
+            *block |= bitpat;
+        } else {
+            *block &= !bitpat;
+        }
+    }
+
+    #[cfg(test)]
+    fn count_ones(&self) -> u8 {
+        self.0
+            .iter()
+            .map(|i| i.count_ones())
+            .sum::<u32>()
+            .try_into()
+            .unwrap()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn maskbase() {
+        assert_eq!(HalfBytesMask::B32_REVSHA256.count_ones(), 32);
+        assert_eq!(HalfBytesMask::B64_BLAKE2B256.count_ones(), 64);
+    }
+
+    #[test]
+    fn non_ascii() {
+        for i in 0x80..=0xff {
+            assert!(!HalfBytesMask::DFL_REST.contains(i));
+        }
+    }
+
+    #[test]
+    fn dflmask() {
+        assert_eq!(
+            HalfBytesMask::from_expanded(
+                [
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                ]
+                .map(|i| i != 0)
+            ),
+            Default::default(),
+        );
+
+        assert_eq!(
+            HalfBytesMask::from_expanded(
+                [
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+                    1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1,
+                    1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+                ]
+                .map(|i| i != 0)
+            ),
+            HalfBytesMask::B32_REVSHA256,
+        );
+
+        assert_eq!(
+            HalfBytesMask::from_expanded(
+                [
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+                    1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1,
+                    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
+                ]
+                .map(|i| i != 0)
+            ),
+            HalfBytesMask::B64_BLAKE2B256,
+        );
+
+        assert_eq!(
+            HalfBytesMask::from_bytes(
+                b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-._?="
+            ),
+            HalfBytesMask::DFL_REST,
+        );
+    }
+}
diff --git a/users/fogti/store-ref-scanner/src/lib.rs b/users/fogti/store-ref-scanner/src/lib.rs
new file mode 100644
index 0000000000..0f86a769fe
--- /dev/null
+++ b/users/fogti/store-ref-scanner/src/lib.rs
@@ -0,0 +1,215 @@
+#![no_std]
+#![forbid(clippy::cast_ptr_alignment, trivial_casts, unconditional_recursion)]
+#![deny(clippy::as_conversions)]
+
+mod hbm;
+pub use hbm::HalfBytesMask;
+
+mod spec;
+pub use spec::*;
+
+/// limit maximal length of store basename
+const BASENAME_MAXLEN: usize = 255;
+
+/// this is a trait which implements the interface of possible inputs
+/// (usually byte slices)
+pub trait ScannerInput: AsRef<[u8]> + Sized {
+    /// Splits the input into two at the given index.
+    /// Afterwards self contains elements [at, len), and the returned input part contains elements [0, at).
+    fn split_to(&mut self, at: usize) -> Self;
+    fn finish(&mut self);
+}
+
+impl ScannerInput for &[u8] {
+    fn split_to(&mut self, at: usize) -> Self {
+        let (a, b) = self.split_at(at);
+        *self = b;
+        a
+    }
+
+    fn finish(&mut self) {
+        *self = &[];
+    }
+}
+
+impl ScannerInput for &mut [u8] {
+    fn split_to(&mut self, at: usize) -> Self {
+        // Lifetime dance taken from `impl Write for &mut [u8]`.
+        // Taken from crate `std`.
+        let (a, b) = core::mem::take(self).split_at_mut(at);
+        *self = b;
+        a
+    }
+
+    fn finish(&mut self) {
+        *self = &mut [];
+    }
+}
+
+/// this is the primary structure of this crate
+///
+/// it represents a scanner which scans binary slices for store references,
+/// and implements an iterator interfaces which returns these as byte slices.
+pub struct StoreRefScanner<'x, Input: 'x> {
+    input: Input,
+    spec: &'x StoreSpec<'x>,
+}
+
+impl<'x, Input> StoreRefScanner<'x, Input>
+where
+    Input: ScannerInput + 'x,
+{
+    pub fn new(input: Input, spec: &'x StoreSpec<'x>) -> Self {
+        for i in [&spec.valid_hashbytes, &spec.valid_restbytes] {
+            for j in [b'\0', b' ', b'\t', b'\n', b'/', b'\\'] {
+                assert!(!i.contains(j));
+            }
+        }
+        Self { input, spec }
+    }
+}
+
+impl<'x, Input: 'x> Iterator for StoreRefScanner<'x, Input>
+where
+    Input: ScannerInput + 'x,
+{
+    type Item = Input;
+
+    fn next(&mut self) -> Option<Input> {
+        let hbl: usize = self.spec.hashbytes_len.into();
+        'outer: while !self.input.as_ref().is_empty() {
+            if !self.spec.path_to_store.is_empty() {
+                let p2sas = self.spec.path_to_store;
+                while !self.input.as_ref().starts_with(p2sas.as_bytes()) {
+                    if self.input.as_ref().is_empty() {
+                        break 'outer;
+                    }
+                    self.input.split_to(1);
+                }
+                self.input.split_to(p2sas.len());
+                if self.input.as_ref().is_empty() {
+                    break 'outer;
+                }
+            }
+            let hsep = matches!(self.input.as_ref().iter().next(), Some(b'/') | Some(b'\\'));
+            self.input.split_to(1);
+            if hsep && self.spec.check_rest(self.input.as_ref()) {
+                // we have found a valid hash
+                // rest contains the store basename and all following components
+                // now let's search for the end
+                // and then cut off possible following components after the basename
+                let rlen = self
+                    .input
+                    .as_ref()
+                    .iter()
+                    .enumerate()
+                    .take(BASENAME_MAXLEN)
+                    .skip(hbl)
+                    .find(|&(_, &i)| !self.spec.valid_restbytes.contains(i))
+                    .map(|(eosp, _)| eosp)
+                    .unwrap_or_else(|| core::cmp::min(BASENAME_MAXLEN, self.input.as_ref().len()));
+                return Some(self.input.split_to(rlen));
+            }
+        }
+        self.input.finish();
+        None
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    extern crate alloc;
+    use alloc::{vec, vec::Vec};
+
+    #[test]
+    fn simple_nix2() {
+        let drv: &[u8] = br#"
+            Derive([("out","","r:sha256","")],[("/nix/store/2ax7bvjdfkzim69q957i0jlg0nvmapg0-util-linux-2.37.2.drv",["dev"]),("/nix/store/6b55ssmh8pzqsc4q4kw1yl3kqvr4fvqj-bash-5.1-p12.drv",["out"]),("/nix/store/fp2vx24kczlzv84avds28wyzsmrn8kyv-source.drv",["out"]),("/nix/store/s6c2lm5hpsvdwnxq9y1g3ngncghjzc3k-stdenv-linux.drv",["out"]),("/nix/store/xlnzpf4mzghi8vl0krabrgcbnqk5qjf3-pkg-config-wrapper-0.29.2.drv",["out"])],["/nix/store/03sl46khd8gmjpsad7223m32ma965vy9-fix-static.patch","/nix/store/2q3z7587yhlz0i2xvfvvap42zk5carlv-bcache-udev-modern.patch","/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"],"x86_64-linux","/0g15yibzzi3rmw29gqlbms05x9dbghbvh61v1qggydvmzh3bginw/bin/bash",["-e","/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"],[("buildInputs","/0sdk1r4l43yw4g6lmqdhd92vhdfhlwz3m76jxzvzsqsv63czw2km"),("builder","/0g15yibzzi3rmw29gqlbms05x9dbghbvh61v1qggydvmzh3bginw/bin/bash"),("configureFlags",""),("depsBuildBuild",""),("depsBuildBuildPropagated",""),("depsBuildTarget",""),("depsBuildTargetPropagated",""),("depsHostHost",""),("depsHostHostPropagated",""),("depsTargetTarget",""),("depsTargetTargetPropagated",""),("doCheck",""),("doInstallCheck",""),("makeFlags","PREFIX=/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9 UDEVLIBDIR=/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9/lib/udev/"),("name","bcache-tools-1.0.7"),("nativeBuildInputs","/1kw0rwgdyq9q69wmmsa5d2kap6p52b0yldbzi4w17bhcq5g5cp2f"),("out","/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"),("outputHashAlgo","sha256"),("outputHashMode","recursive"),("outputs","out"),("patches","/nix/store/2q3z7587yhlz0i2xvfvvap42zk5carlv-bcache-udev-modern.patch /nix/store/03sl46khd8gmjpsad7223m32ma965vy9-fix-static.patch"),("pname","bcache-tools"),("preBuild","sed -e \"s|/bin/sh|/0g15yibzzi3rmw29gqlbms05x9dbghbvh61v1qggydvmzh3bginw/bin/sh|\" -i *.rules\n"),("preInstall","mkdir -p \"$out/sbin\" \"$out/lib/udev/rules.d\" \"$out/share/man/man8\"\n"),("prePatch","sed -e \"/INSTALL.*initramfs\\/hook/d\" \\\n    -e \"/INSTALL.*initcpio\\/install/d\" \\\n    -e \"/INSTALL.*dracut\\/module-setup.sh/d\" \\\n    -e \"s/pkg-config/$PKG_CONFIG/\" \\\n    -i Makefile\n"),("propagatedBuildInputs",""),("propagatedNativeBuildInputs",""),("src","/nix/store/6izcafvfcbz19chi7hl20834g0fa043n-source"),("stdenv","/01ncyv8bxibj0imgfvmxgqy648n697bachil6aw6i46g1jk0bbds"),("strictDeps",""),("system","x86_64-linux"),("version","1.0.7")])
+        "#;
+        // we convert everything into strings because it is way easier to compare elements in error messages
+        let refs: Vec<&str> = StoreRefScanner::new(drv, &StoreSpec::DFL_NIX2)
+            .map(|i| core::str::from_utf8(i).unwrap())
+            .collect();
+        let refs_expect: Vec<&[u8]> = vec![
+            b"2ax7bvjdfkzim69q957i0jlg0nvmapg0-util-linux-2.37.2.drv",
+            b"6b55ssmh8pzqsc4q4kw1yl3kqvr4fvqj-bash-5.1-p12.drv",
+            b"fp2vx24kczlzv84avds28wyzsmrn8kyv-source.drv",
+            b"s6c2lm5hpsvdwnxq9y1g3ngncghjzc3k-stdenv-linux.drv",
+            b"xlnzpf4mzghi8vl0krabrgcbnqk5qjf3-pkg-config-wrapper-0.29.2.drv",
+            b"03sl46khd8gmjpsad7223m32ma965vy9-fix-static.patch",
+            b"2q3z7587yhlz0i2xvfvvap42zk5carlv-bcache-udev-modern.patch",
+            b"9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh",
+            b"9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh",
+            b"2q3z7587yhlz0i2xvfvvap42zk5carlv-bcache-udev-modern.patch",
+            b"03sl46khd8gmjpsad7223m32ma965vy9-fix-static.patch",
+            b"6izcafvfcbz19chi7hl20834g0fa043n-source",
+        ];
+        let refs_expect: Vec<&str> = refs_expect
+            .into_iter()
+            .map(|i| core::str::from_utf8(i).unwrap())
+            .collect();
+        assert_eq!(refs, refs_expect);
+    }
+
+    #[test]
+    fn simple_yzix1() {
+        // I haven't yet produced any yzix derivation which included /yzixs absolute paths...
+        let fake: &[u8] = br#"
+            /yzixs/4Zx1PBoft1YyAuKdhjAY1seZFHloxQ+8voHQRkRMuys:         ASCII text
+            /yzixs/dNE3yogD4JHKHzNa2t3jQMZddT8wjqlMDB0naDIFo0A:         ASCII text
+            /yzixs/FMluSVOHLc4bxX7F4lBCXafNljBnDn+rAM5HzG7k8LI:         unified diff output, ASCII text
+            /yzixs/g2G3GRL87hGEdw9cq2BZWqDQP_HeHSPRLbJ9P9KH+HI:         unified diff output, ASCII text
+            /yzixs/H08Av1ZAONwFdzVLpFQm0Sc0dvyk0sbnk82waoBig7I:         ASCII text
+            /yzixs/IndARQp+gaGDLS3K+PeyXdaRqAcCyS3EIbRXkkYjC94:         unified diff output, ASCII text
+            /yzixs/IrLPnbkEolTAuWRxkXpuvVs6Imb1iB6wUJcI+fxWwkU:         POSIX shell script, ASCII text executable
+            /yzixs/JsS_H3n3TSh2R6fiIzgOPZdjSmRkV71vGxstJJKPmr4:         unified diff output, ASCII text
+            /yzixs/LZ6pQh1x8DRxZ2IYzetBRS4LuE__IXFjpOfQPxHVwpw:         unified diff output, ASCII text
+            /yzixs/mEi2RPep9daRs0JUvwt1JsDfgYSph5sH_+_ihwn8IGQ:         ASCII text
+            /yzixs/nd4DyljinP3auDMHL_LrpsRJkWQpSHQK2jqtyyzWcBA:         POSIX shell script, ASCII text executable
+            /yzixs/nzpaknF0_ONSHtd0i_e1E3pkLF1QPeJQhAB7x9Ogo_M:         unified diff output, ASCII text
+            /yzixs/UZ3uzVUUMC1gKGLw6tg_aLFwoFrJedXB3xbhEgQOaiY:         unified diff output, ASCII text
+            /yzixs/VKyXxKTXsDGxYJ24YgbvCc1bZkA5twp3TC+Gbi4Kwd8:         unified diff output, ASCII text
+            /yzixs/VPJMl8O1xkc1LsJznpoQrCrQO0Iy+ODCPsgoUBLiRZc:         unified diff output, ASCII text
+            /yzixs/W6r1ow001ASHRj+gtRfyj9Fb_gCO_pBztX8WhYXVdIc:         unified diff output, ASCII text
+            /yzixs/xvwEcXIob_rQynUEtQiQbwaDXEobTVKEGaBMir9oH9k:         unified diff output, ASCII text
+            /yzixs/ZPvQbRJrtyeSITvW3FUZvw99hhNOO3CFqGgmWgScxcg:         ASCII text
+        "#;
+        let refs: Vec<&str> = StoreRefScanner::new(fake, &StoreSpec::DFL_YZIX1)
+            .map(|i| core::str::from_utf8(i).unwrap())
+            .collect();
+        let refs_expect: Vec<&[u8]> = vec![
+            b"4Zx1PBoft1YyAuKdhjAY1seZFHloxQ+8voHQRkRMuys",
+            b"dNE3yogD4JHKHzNa2t3jQMZddT8wjqlMDB0naDIFo0A",
+            b"FMluSVOHLc4bxX7F4lBCXafNljBnDn+rAM5HzG7k8LI",
+            b"g2G3GRL87hGEdw9cq2BZWqDQP_HeHSPRLbJ9P9KH+HI",
+            b"H08Av1ZAONwFdzVLpFQm0Sc0dvyk0sbnk82waoBig7I",
+            b"IndARQp+gaGDLS3K+PeyXdaRqAcCyS3EIbRXkkYjC94",
+            b"IrLPnbkEolTAuWRxkXpuvVs6Imb1iB6wUJcI+fxWwkU",
+            b"JsS_H3n3TSh2R6fiIzgOPZdjSmRkV71vGxstJJKPmr4",
+            b"LZ6pQh1x8DRxZ2IYzetBRS4LuE__IXFjpOfQPxHVwpw",
+            b"mEi2RPep9daRs0JUvwt1JsDfgYSph5sH_+_ihwn8IGQ",
+            b"nd4DyljinP3auDMHL_LrpsRJkWQpSHQK2jqtyyzWcBA",
+            b"nzpaknF0_ONSHtd0i_e1E3pkLF1QPeJQhAB7x9Ogo_M",
+            b"UZ3uzVUUMC1gKGLw6tg_aLFwoFrJedXB3xbhEgQOaiY",
+            b"VKyXxKTXsDGxYJ24YgbvCc1bZkA5twp3TC+Gbi4Kwd8",
+            b"VPJMl8O1xkc1LsJznpoQrCrQO0Iy+ODCPsgoUBLiRZc",
+            b"W6r1ow001ASHRj+gtRfyj9Fb_gCO_pBztX8WhYXVdIc",
+            b"xvwEcXIob_rQynUEtQiQbwaDXEobTVKEGaBMir9oH9k",
+            b"ZPvQbRJrtyeSITvW3FUZvw99hhNOO3CFqGgmWgScxcg",
+        ];
+        let refs_expect: Vec<&str> = refs_expect
+            .into_iter()
+            .map(|i| core::str::from_utf8(i).unwrap())
+            .collect();
+        assert_eq!(refs, refs_expect);
+    }
+
+    #[test]
+    fn just_store() {
+        for i in [&StoreSpec::DFL_NIX2, &StoreSpec::DFL_YZIX1] {
+            let refs: Vec<&[u8]> = StoreRefScanner::new(i.path_to_store.as_bytes(), i).collect();
+            assert!(refs.is_empty());
+        }
+    }
+}
diff --git a/users/fogti/store-ref-scanner/src/spec.rs b/users/fogti/store-ref-scanner/src/spec.rs
new file mode 100644
index 0000000000..79da0842c5
--- /dev/null
+++ b/users/fogti/store-ref-scanner/src/spec.rs
@@ -0,0 +1,40 @@
+use crate::hbm::HalfBytesMask;
+
+pub struct StoreSpec<'path> {
+    /// path to store without trailing slash
+    pub path_to_store: &'path str,
+
+    /// compressed map of allowed ASCII characters in hash part
+    pub valid_hashbytes: HalfBytesMask,
+
+    /// compressed map of allowed ASCII characters in part after hash
+    pub valid_restbytes: HalfBytesMask,
+
+    /// exact length of hash part of store paths
+    pub hashbytes_len: u8,
+}
+
+impl StoreSpec<'_> {
+    pub(crate) fn check_rest(&self, rest: &[u8]) -> bool {
+        let hbl = self.hashbytes_len.into();
+        rest.iter()
+            .take(hbl)
+            .take_while(|&&i| self.valid_hashbytes.contains(i))
+            .count()
+            == hbl
+    }
+
+    pub const DFL_NIX2: StoreSpec<'static> = StoreSpec {
+        path_to_store: "/nix/store",
+        valid_hashbytes: HalfBytesMask::B32_REVSHA256,
+        valid_restbytes: HalfBytesMask::DFL_REST,
+        hashbytes_len: 32,
+    };
+
+    pub const DFL_YZIX1: StoreSpec<'static> = StoreSpec {
+        path_to_store: "/yzixs",
+        valid_hashbytes: HalfBytesMask::B64_BLAKE2B256,
+        valid_restbytes: HalfBytesMask::DFL_REST,
+        hashbytes_len: 43,
+    };
+}
diff --git a/users/grfn/OWNERS b/users/grfn/OWNERS
deleted file mode 100644
index da7ac5cb9e..0000000000
--- a/users/grfn/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherited: false
-owners:
-  - grfn
diff --git a/users/grfn/emacs.d/snippets/rust-mode/async test b/users/grfn/emacs.d/snippets/rust-mode/async test
deleted file mode 100644
index 8465225663..0000000000
--- a/users/grfn/emacs.d/snippets/rust-mode/async test
+++ /dev/null
@@ -1,10 +0,0 @@
-# -*- mode: snippet -*-
-# name: async test
-# uuid:
-# key: atest
-# condition: t
-# --
-#[tokio::test(flavor = "multi_thread")]
-async fn ${1:test_name}() {
-   `%`$0
-}
\ No newline at end of file
diff --git a/users/grfn/gws.fyi/default.nix b/users/grfn/gws.fyi/default.nix
deleted file mode 100644
index 5b7d8fc0e1..0000000000
--- a/users/grfn/gws.fyi/default.nix
+++ /dev/null
@@ -1,35 +0,0 @@
-args@{ pkgs, depot, ... }:
-with pkgs;
-let
-  site = import ./site.nix args;
-  resume = import ../resume args;
-  bucket = "s3://gws.fyi";
-  distributionID = "E2ST43JNBH8C64";
-
-  css = runCommand "main.css" {
-    buildInputs = [ pkgs.minify ];
-  } ''
-    minify --type css < ${./main.css} > $out
-  '';
-
-  keys = runCommand "ssh-keys" {} ''
-    touch $out
-    echo "${depot.users.grfn.keys.main}" >> $out
-  '';
-
-  website =
-    runCommand "gws.fyi" { } ''
-      mkdir -p $out
-      cp ${css} $out/main.css
-      cp ${site.index} $out/index.html
-      cp -r ${site.recipes} $out/recipes
-      cp ${resume} $out/resume.pdf
-      cp ${keys} $out/keys
-    '';
-
-in (writeShellScript "deploy.sh" ''
-  ${awscli2}/bin/aws --profile personal s3 sync ${website}/ ${bucket}
-  echo "Deployed to http://gws.fyi"
-'') // {
-  inherit website site;
-}
diff --git a/users/grfn/gws.fyi/index.org b/users/grfn/gws.fyi/index.org
deleted file mode 100644
index 654ba29921..0000000000
--- a/users/grfn/gws.fyi/index.org
+++ /dev/null
@@ -1,38 +0,0 @@
-#+OPTIONS: title:nil toc:nil num:nil
-#+HTML_HEAD: <title>griffin smith</title>
-#+HTML_HEAD: <link rel="stylesheet" href="./main.css">
-
-my name is griffin ward smith (aka grfn, glittershark, gws) and i'm a software
-engineer and musician
-
-* code
-
-- [[https://github.com/glittershark/][github]]
-- [[https://cs.tvl.fyi/depot/-/tree/users/grfn][my directory in the tvl monorepo]]
-
-* work
-
-i'm currently working for a relatively new stealth-mode startup. i can't talk much
-about it yet, but if you're interested in rust, distributed systems, and
-databases please reach out
-
-* projects
-
-- [[https://windtunnel.ci/][windtunnel]], a continuous benchmarking software-as-a-service currently accepting early alpha users (send me an email if you want to try it out!)
-- [[https://cs.tvl.fyi/depot/-/tree/users/grfn/achilles][achilles]], a compiler for (what I plan to become) a dependently typed, low-level functional programming language targeting LLVM
-- [[https://github.com/glittershark/org-clubhouse][org-clubhouse]], an emacs package for lightweight integration between [[https://orgmode.org/][org-mode]] and [[https://clubhouse.io/][the clubhouse project management tool]]
-- [[https://cs.tvl.fyi/depot/-/tree/users/grfn/xanthous][xanthous]], a terminal roguelike in haskell that I work on intermittently and exclusively for fun
-
-* music
-
-- https://sacrosanct.bandcamp.com/, a post-rock project with a [[https://bandcamp.com/h34rken][friend of mine]]
-- [[https://soundcloud.com/missingggg][my current soundcloud]], releasing instrumental hip-hop under the name *missing*
-- you can also find a log of all the music I listen to [[https://www.last.fm/user/wildgriffin45][on last.fm]]
-
-* contact
-
-- [[mailto:web@gws.fyi][web@gws.fyi]]
-- [[https://twitter.com/glittershark1][twitter]]
-- https://keybase.io/glittershark
-- grfn on IRC (hackint or libera.chat)
-- [[http://keys.gnupg.net/pks/lookup?op=get&search=0x44EF5B5E861C09A7][gpg key: 0F11A989879E8BBBFDC1E23644EF5B5E861C09A7]]
diff --git a/users/grfn/keyboard/flash b/users/grfn/keyboard/flash
deleted file mode 100755
index 76def36f9c..0000000000
--- a/users/grfn/keyboard/flash
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env bash
-exec "$(nix-build --no-out-link ../../.. -A users.grfn.keyboard.flash)"
diff --git a/users/grfn/resume/default.nix b/users/grfn/resume/default.nix
deleted file mode 100644
index 2db6a650bc..0000000000
--- a/users/grfn/resume/default.nix
+++ /dev/null
@@ -1,37 +0,0 @@
-{ pkgs, ... }:
-
-with pkgs.lib;
-
-pkgs.runCommandNoCC "resume.pdf" {
-  buildInputs = [(pkgs.texlive.combine {
-    inherit (pkgs.texlive)
-      capt-of
-      collection-fontsrecommended
-      enumitem
-      etoolbox
-      fancyvrb
-      float
-      fncychap
-      framed
-      l3packages
-      microtype
-      needspace
-      parskip
-      scheme-basic
-      tabulary
-      titlesec
-      ulem
-      upquote
-      varwidth
-      wrapfig
-      xcolor
-      ;
-  })];
-} ''
-  cp ${builtins.filterSource (path: type:
-    type == "regular" &&
-    any (ext: hasSuffix ext path) [".sty" ".cls" ".tex" ".png"]
-  ) ./.}/* .
-  pdflatex ./resume.tex
-  cp resume.pdf $out
-''
diff --git a/users/grfn/system/home/common/solarized.nix b/users/grfn/system/home/common/solarized.nix
deleted file mode 100644
index e94693edc5..0000000000
--- a/users/grfn/system/home/common/solarized.nix
+++ /dev/null
@@ -1,18 +0,0 @@
-rec {
-  base03  = "#002B36";
-  base02  = "#073642";
-  base01  = "#586e75";
-  base00  = "#657b83";
-  base0   = "#839496";
-  base1   = "#93a1a1";
-  base2   = "#eee8d5";
-  base3   = "#fdf6e3";
-  yellow  = "#b58900";
-  orange  = "#cb4b16";
-  red     = "#dc322f";
-  magenta = "#d33682";
-  violet  = "#6c71c4";
-  blue    = "#268bd2";
-  cyan    = "#2aa198";
-  green   = "#859900";
-}
diff --git a/users/grfn/system/home/modules/alsi.nix b/users/grfn/system/home/modules/alsi.nix
deleted file mode 100644
index c4a14e683c..0000000000
--- a/users/grfn/system/home/modules/alsi.nix
+++ /dev/null
@@ -1,58 +0,0 @@
-{ config, lib, pkgs, ... }:
-{
-  home.packages = [ config.lib.depot.third_party.alsi ];
-
-  xdg.configFile."alsi/alsi.logo" = {
-    source = ./nixos-logo.txt;
-    force = true;
-  };
-
-  xdg.configFile."alsi/alsi.conf" = {
-    force = true;
-    text = ''
-    #!${pkgs.perl}/bin/perl
-
-    scalar {
-      ALSI_VERSION         => "0.4.8",
-      COLORS_FILE          => "/home/grfn/.config/alsi/alsi.colors",
-      DE_FILE              => "/home/grfn/.config/alsi/alsi.de",
-      DEFAULT_COLOR_BOLD   => "blue",
-      DEFAULT_COLOR_NORMAL => "blue",
-      DF_COMMAND           => "df -Th -x sys -x tmpfs -x devtmpfs &>/dev/stdout",
-      GTK2_RC_FILE         => "/home/grfn/.gtkrc-2.0",
-      GTK3_RC_FILE         => "/home/grfn/.config/gtk-3.0/settings.ini",
-      LOGO_FILE            => "/home/grfn/.config/alsi/alsi.logo",
-      OUTPUT_FILE          => "/home/grfn/.config/alsi/alsi.output",
-      # PACKAGES_PATH      => "/var/lib/pacman/local/",
-      PS_COMMAND           => "ps -A",
-      USAGE_COLORS         => 0,
-      USAGE_COLORS_BOLD    => 0,
-      USAGE_PRECENT_GREEN  => 50,
-      USAGE_PRECENT_RED    => 100,
-      USAGE_PRECENT_YELLOW => 85,
-      USE_LOGO_FROM_FILE   => 1,
-      USE_VALUES_COLOR     => 0,
-      WM_FILE              => "/home/grfn/.config/alsi/alsi.wm",
-    }
-    '';
-  };
-
-  xdg.configFile."alsi/alsi.colors".text = ''
-    #!${pkgs.perl}/bin/perl
-
-    # Colors for alsi
-
-    scalar {
-       black   => {normal => "\e[0;30m", bold => "\e[1;30m"},
-       red     => {normal => "\e[0;31m", bold => "\e[1;31m"},
-       green   => {normal => "\e[0;32m", bold => "\e[1;32m"},
-       yellow  => {normal => "\e[0;33m", bold => "\e[1;33m"},
-       default => {normal => "\e[0;34m", bold => "\e[1;34m"},
-       blue    => {normal => "\e[0;34m", bold => "\e[1;34m"},
-       purple  => {normal => "\e[0;35m", bold => "\e[1;35m"},
-       cyan    => {normal => "\e[0;36m", bold => "\e[1;36m"},
-       white   => {normal => "\e[0;37m", bold => "\e[1;37m"},
-       reset   => "\e[0m",
-    }
-  '';
-}
diff --git a/users/grfn/system/home/modules/common.nix b/users/grfn/system/home/modules/common.nix
deleted file mode 100644
index 86e4ba400e..0000000000
--- a/users/grfn/system/home/modules/common.nix
+++ /dev/null
@@ -1,105 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-# Everything in here needs to work on linux or darwin
-
-{
-  imports = [
-    ../modules/shell.nix
-    ../modules/development.nix
-    ../modules/emacs.nix
-    ../modules/vim.nix
-    ../modules/tarsnap.nix
-    ../modules/twitter.nix
-    ../modules/lib/cloneRepo.nix
-  ];
-
-  programs.password-store.enable = true;
-
-  grfn.impure.clonedRepos.passwordStore = {
-    github = "glittershark/pass";
-    path = ".local/share/password-store";
-  };
-
-  home.packages = with pkgs; [
-    # System utilities
-    bat
-    htop
-    killall
-    bind
-    zip unzip
-    tree
-    ncat
-    bc
-    pv
-
-    # Security
-    gnupg
-    keybase
-    openssl
-
-    # Nix things
-    nixfmt
-    nix-prefetch-github
-    nix-review
-    cachix
-    (writeShellScriptBin "rebuild-mugwump" ''
-      set -eo pipefail
-      cd ~/code/depot
-      nix build -f . users.grfn.system.system.mugwumpSystem -o /tmp/mugwump
-      nix copy -f . users.grfn.system.system.mugwumpSystem \
-        --to ssh://mugwump
-      system=$(readlink -ef /tmp/mugwump)
-      ssh mugwump sudo nix-env -p /nix/var/nix/profiles/system --set $system
-      ssh mugwump sudo $system/bin/switch-to-configuration switch
-    '')
-    (writeShellScriptBin "rebuild-home" ''
-      set -eo pipefail
-      cd ~/code/depot
-      nix build -f . users.grfn.system.home.$(hostname)Home -o /tmp/home
-      /tmp/home/activate
-    '')
-  ];
-
-  programs.ssh = {
-    enable = true;
-
-    matchBlocks = {
-      "home" = {
-        host = "home.gws.fyi";
-        forwardAgent = true;
-      };
-
-      "dobharchu" = {
-        host = "dobharchu";
-        hostname = "172.16.0.4";
-        forwardAgent = true;
-        user = "griffin";
-      };
-
-      "cerberus" = {
-        host = "cerberus";
-        hostname = "172.16.0.3";
-        forwardAgent = true;
-        user = "griffin";
-      };
-
-      "mugwump" = {
-        host = "mugwump";
-        hostname = "172.16.0.5";
-        forwardAgent = true;
-      };
-
-      "roswell" = {
-        host = "roswell";
-        hostname = "18.223.118.13";
-        forwardAgent = true;
-      };
-    };
-  };
-
-  programs.direnv = {
-    enable = true;
-    enableBashIntegration = true;
-    enableZshIntegration = true;
-  };
-}
diff --git a/users/grfn/system/home/modules/development/readyset.nix b/users/grfn/system/home/modules/development/readyset.nix
deleted file mode 100644
index 19f44b7703..0000000000
--- a/users/grfn/system/home/modules/development/readyset.nix
+++ /dev/null
@@ -1,16 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  imports = [
-    ./rust.nix
-  ];
-
-  home.packages = with pkgs; [
-    # This goes in $PATH so I can run it from rofi and parent to my WM
-    (writeShellScriptBin "dotclip" "xclip -out -selection clipboard | dot -Tpng | feh -")
-  ];
-
-  programs.zsh.shellAliases = {
-    "tf" = "terraform";
-  };
-}
diff --git a/users/grfn/system/home/modules/email.nix b/users/grfn/system/home/modules/email.nix
deleted file mode 100644
index 0a3e58205b..0000000000
--- a/users/grfn/system/home/modules/email.nix
+++ /dev/null
@@ -1,87 +0,0 @@
-{ lib, pkgs, config, ... }:
-
-with lib;
-
-let
-
-  # from home-manager/modules/services/lieer.nix
-  escapeUnitName = name:
-    let
-      good = upperChars ++ lowerChars ++ stringToCharacters "0123456789-_";
-      subst = c: if any (x: x == c) good then c else "-";
-    in stringAsChars subst name;
-
-  accounts = {
-    personal = {
-      primary = true;
-      address = "root@gws.fyi";
-      aliases = [ "grfn@gws.fyi" ];
-      passEntry = "root-gws-msmtp";
-    };
-
-    work = {
-      address = "griffin@readyset.io";
-      passEntry = "readyset/msmtp";
-    };
-
-  };
-
-in {
-  programs.lieer.enable = true;
-  programs.notmuch.enable = true;
-  services.lieer.enable = true;
-  programs.msmtp.enable = true;
-
-  home.packages = with pkgs; [
-    mu
-    msmtp
-  ];
-
-  systemd.user.services = mapAttrs' (name: account: {
-    name = escapeUnitName "lieer-${name}";
-    value.Service = {
-      ExecStart = mkForce "${pkgs.writeShellScript "sync-${name}" ''
-        ${pkgs.gmailieer}/bin/gmi sync --path ~/mail/${name}
-      ''}";
-      Environment = "NOTMUCH_CONFIG=${config.home.sessionVariables.NOTMUCH_CONFIG}";
-    };
-
-  }) accounts;
-
-  # xdg.configFile."notifymuch/notifymuch.cfg".text = generators.toINI {} {
-  #   notifymuch = {
-  #     query = "is:unread and is:important";
-  #     mail_client = "";
-  #     recency_interval_hours = "48";
-  #     hidden_tags = "inbox unread attachment replied sent encrypted signed";
-  #   };
-  # };
-
-  accounts.email.maildirBasePath = "mail";
-  accounts.email.accounts = mapAttrs (_: params@{ passEntry, ... }: {
-    realName = "Griffin Smith";
-    passwordCommand = "pass ${passEntry}";
-
-    flavor = "gmail.com";
-
-    imapnotify = {
-      enable = true;
-      boxes = [ "Inbox" ];
-    };
-
-    gpg = {
-      key = "0F11A989879E8BBBFDC1E23644EF5B5E861C09A7";
-      signByDefault = true;
-    };
-
-    notmuch.enable = true;
-    lieer = {
-      enable = true;
-      sync = {
-        enable = true;
-        frequency = "*:*";
-      };
-    };
-    msmtp.enable = true;
-  } // builtins.removeAttrs params ["passEntry"]) accounts;
-}
diff --git a/users/grfn/system/home/modules/i3.nix b/users/grfn/system/home/modules/i3.nix
deleted file mode 100644
index c56c9d556b..0000000000
--- a/users/grfn/system/home/modules/i3.nix
+++ /dev/null
@@ -1,369 +0,0 @@
-{ config, lib, pkgs, ... }:
-let
-  mod = "Mod4";
-  solarized = import ../common/solarized.nix;
-  # TODO pull this out into lib
-  emacsclient = eval: pkgs.writeShellScript "emacsclient-eval" ''
-    msg=$(emacsclient --eval '${eval}' 2>&1)
-    echo "''${msg:1:-1}"
-  '';
-  screenlayout = {
-    home = pkgs.writeShellScript "screenlayout_home.sh" ''
-      xrandr \
-        --output eDP-1 --mode 1920x1200 --pos 0x2160 --rotate normal \
-        --output DP-1 --off \
-        --output DP-2 --off \
-        --output DP-3 --mode 3840x2160 --pos 0x0 --rotate normal \
-        --output DP-4 --off
-    '';
-  };
-
-  inherit (builtins) map;
-  inherit (lib) mkMerge range;
-in {
-  options = with lib; {
-    system.machine.wirelessInterface = mkOption {
-      description = ''
-        Name of the primary wireless interface. Used by i3status, etc.
-      '';
-      default = "wlp3s0";
-      type = types.str;
-    };
-
-    system.machine.i3FontSize = mkOption {
-      description = "Font size to use in i3 window decorations etc.";
-      default = 6;
-      type = types.int;
-    };
-  };
-
-  config =
-    let decorationFont = "MesloLGSDZ ${toString config.system.machine.i3FontSize}"; in
-    {
-      home.packages = with pkgs; [
-        rofi
-        rofi-pass
-        python38Packages.py3status
-        i3lock
-        i3status
-        dconf # for gtk
-
-        # Screenshots
-        maim
-
-        # GIFs
-        picom
-        peek
-
-        (pkgs.writeShellScriptBin "lock" ''
-          playerctl pause
-          ${pkgs.i3lock}/bin/i3lock -c 222222
-        '')
-      ];
-
-      xsession.scriptPath = ".xsession";
-
-      xsession.windowManager.i3 = {
-        enable = true;
-        config = {
-          modifier = mod;
-          keybindings =
-            mkMerge (
-              (map
-                (n: {
-                  "${mod}+${toString n}" =
-                    "workspace ${toString n}";
-                  "${mod}+Shift+${toString n}" =
-                    "move container to workspace ${toString n}";
-                })
-                (range 0 9))
-            ++ [(rec {
-              "${mod}+h" = "focus left";
-              "${mod}+j" = "focus down";
-              "${mod}+k" = "focus up";
-              "${mod}+l" = "focus right";
-              "${mod}+semicolon" = "focus parent";
-
-              "${mod}+Shift+h" = "move left";
-              "${mod}+Shift+j" = "move down";
-              "${mod}+Shift+k" = "move up";
-              "${mod}+Shift+l" = "move right";
-
-              "${mod}+Shift+x" = "kill";
-
-              "${mod}+Return" = "exec alacritty";
-
-              "${mod}+Shift+s" = "split h";
-              "${mod}+Shift+v" = "split v";
-              "${mod}+e" = "layout toggle split";
-              "${mod}+w" = "layout tabbed";
-              "${mod}+s" = "layout stacking";
-
-              "${mod}+f" = "fullscreen";
-
-              "${mod}+Shift+r" = "restart";
-
-              "${mod}+r" = "mode resize";
-
-              # Marks
-              "${mod}+Shift+m" = ''exec i3-input -F "mark %s" -l 1 -P 'Mark: ' '';
-              "${mod}+m" = ''exec i3-input -F '[con_mark="%s"] focus' -l 1 -P 'Go to: ' '';
-
-              # Screenshots
-              "${mod}+q" = "exec \"maim | xclip -selection clipboard -t image/png\"";
-              "${mod}+Shift+q" = "exec \"maim -s | xclip -selection clipboard -t image/png\"";
-              "${mod}+Ctrl+q" = "exec ${pkgs.writeShellScript "peek.sh" ''
-              ${pkgs.picom}/bin/picom &
-              picom_pid=$!
-              ${pkgs.peek}/bin/peek || true
-              kill -SIGINT $picom_pid
-            ''}";
-
-              # Launching applications
-              "${mod}+u" = "exec ${pkgs.writeShellScript "rofi" ''
-              rofi \
-                -modi 'combi' \
-                -combi-modi "window,drun,ssh,run" \
-                -font '${decorationFont}' \
-                -show combi
-            ''}";
-
-              # Passwords
-              "${mod}+p" = "exec rofi-pass -font '${decorationFont}'";
-
-              # Media
-              "XF86AudioPlay" = "exec playerctl play-pause";
-              "XF86AudioNext" = "exec playerctl next";
-              "XF86AudioPrev" = "exec playerctl previous";
-              "XF86AudioRaiseVolume" = "exec pulseaudio-ctl up";
-              "XF86AudioLowerVolume" = "exec pulseaudio-ctl down";
-              "XF86AudioMute" = "exec pulseaudio-ctl mute";
-
-              # Lock
-              Pause = "exec lock";
-
-              # Brightness
-              "XF86MonBrightnessDown" = "exec ${pkgs.brightnessctl}/bin/brightnessctl -q s 5%-";
-              "XF86MonBrightnessUp" = "exec ${pkgs.brightnessctl}/bin/brightnessctl -q s 5%+";
-
-              # Sleep/hibernate
-              # "${mod}+Escape" = "exec systemctl suspend";
-              # "${mod}+Shift+Escape" = "exec systemctl hibernate";
-
-              # Scratch buffer
-              "${mod}+minus" = "scratchpad show";
-              "${mod}+Shift+minus" = "move scratchpad";
-              "${mod}+space" = "focus mode_toggle";
-              "${mod}+Shift+space" = "floating toggle";
-
-              # Screen Layout
-              "${mod}+Shift+t" = "exec xrandr --auto";
-              "${mod}+t" = "exec ${screenlayout.home}";
-              "${mod}+Ctrl+t" = "exec ${pkgs.writeShellScript "fix_term.sh" ''
-              xrandr --output eDP-1 --off && ${screenlayout.home}
-            ''}";
-
-              # Notifications
-              "${mod}+Shift+n" = "exec killall -SIGUSR1 .dunst-wrapped";
-              "${mod}+n" = "exec killall -SIGUSR2 .dunst-wrapped";
-            })]);
-
-          fonts = [ decorationFont ];
-
-          colors = with solarized; rec {
-            focused = {
-              border = base01;
-              background = base01;
-              text = base3;
-              indicator = red;
-              childBorder = base02;
-            };
-            focusedInactive = focused // {
-              border = base03;
-              background = base03;
-              # text = base1;
-            };
-            unfocused = focusedInactive;
-            background = base03;
-          };
-
-          modes.resize = {
-            l = "resize shrink width 5 px or 5 ppt";
-            k = "resize grow height 5 px or 5 ppt";
-            j = "resize shrink height 5 px or 5 ppt";
-            h = "resize grow width 5 px or 5 ppt";
-
-            Return = "mode \"default\"";
-          };
-
-          bars = [{
-            statusCommand =
-              let i3status-conf = pkgs.writeText "i3status.conf" ''
-              general {
-                  output_format = i3bar
-                  colors = true
-                  color_good = "#859900"
-
-                  interval = 1
-              }
-
-              order += "external_script current_task"
-              order += "external_script inbox"
-              order += "spotify"
-              order += "volume_status"
-              order += "wireless ${config.system.machine.wirelessInterface}"
-              # order += "ethernet enp3s0f0"
-              order += "cpu_usage"
-              order += "battery 0"
-              # order += "volume master"
-              order += "time"
-              order += "tztime utc"
-
-              mpd {
-                  format = "%artist - %album - %title"
-              }
-
-              wireless ${config.system.machine.wirelessInterface} {
-                  format_up = "W: (%quality - %essid - %bitrate) %ip"
-                  format_down = "W: -"
-              }
-
-              ethernet enp3s0f0 {
-                  format_up = "E: %ip"
-                  format_down = "E: -"
-              }
-
-              battery 0 {
-                  format = "%status %percentage"
-                  path = "/sys/class/power_supply/BAT%d/uevent"
-                  low_threshold = 10
-              }
-
-              cpu_usage {
-                  format = "CPU: %usage"
-              }
-
-              load {
-                  format = "%5min"
-              }
-
-              time {
-                  format = "    %a %h %d โŒš   %I:%M     "
-              }
-
-              spotify {
-                  color_playing = "#fdf6e3"
-                  color_paused = "#93a1a1"
-                  format_stopped = ""
-                  format_down = ""
-                  format = "{title} - {artist} ({album})"
-              }
-
-              external_script inbox {
-                  script_path = '${emacsclient "(grfn/num-inbox-items-message)"}'
-                  format = 'Inbox: {output}'
-                  cache_timeout = 120
-                  color = "#93a1a1"
-              }
-
-              external_script current_task {
-                  script_path = '${emacsclient "(grfn/org-current-clocked-in-task-message)"}'
-                  # format = '{output}'
-                  cache_timeout = 60
-                  color = "#93a1a1"
-              }
-
-              tztime utc {
-                  timezone = "UTC"
-                  format = "    %Hยท%M    "
-              }
-
-              volume_status {
-                  format = "โ˜Š {percentage}"
-                  format_muted = "โ˜Š X"
-                  # device = "default"
-                  # mixer_idx = 0
-              }
-            '';
-              in "py3status -c ${i3status-conf}";
-            fonts = [ decorationFont ];
-            position = "top";
-            colors = with solarized; rec {
-              background = base03;
-              statusline = base3;
-              separator = base1;
-              activeWorkspace = {
-                border = base03;
-                background = base1;
-                text = base3;
-              };
-              focusedWorkspace = activeWorkspace;
-              inactiveWorkspace = activeWorkspace // {
-                background = base01;
-              };
-              urgentWorkspace = activeWorkspace // {
-                background = red;
-              };
-            };
-          }];
-        };
-      };
-
-      services.dunst = {
-        enable = true;
-        settings = with solarized; {
-          global = {
-            font = "MesloLGSDZ ${toString (config.system.machine.i3FontSize * 1.5)}";
-            allow_markup = true;
-            format = "<b>%s</b>\n%b";
-            sort = true;
-            alignment = "left";
-            geometry = "600x15-40+40";
-            idle_threshold = 120;
-            separator_color = "frame";
-            separator_height = 1;
-            word_wrap = true;
-            padding = 8;
-            horizontal_padding = 8;
-            max_icon_size = 45;
-          };
-
-          frame = {
-            width = 0;
-            color = "#aaaaaa";
-          };
-
-          shortcuts = {
-            close = "ctrl+space";
-            close_all = "ctrl+shift+space";
-            history = "ctrl+grave";
-            context = "ctrl+shift+period";
-          };
-
-          urgency_low = {
-            background = base03;
-            foreground = base3;
-            timeout = 5;
-          };
-
-          urgency_normal = {
-            background = base02;
-            foreground = base3;
-            timeout = 7;
-          };
-
-          urgency_critical = {
-            background = red;
-            foreground = base3;
-            timeout = 0;
-          };
-        };
-      };
-
-      gtk = {
-        enable = true;
-        iconTheme.name = "Adwaita";
-        theme.name = "Adwaita";
-      };
-  };
-}
diff --git a/users/grfn/system/home/modules/lib/cloneRepo.nix b/users/grfn/system/home/modules/lib/cloneRepo.nix
deleted file mode 100644
index dc487dc6bd..0000000000
--- a/users/grfn/system/home/modules/lib/cloneRepo.nix
+++ /dev/null
@@ -1,67 +0,0 @@
-{ lib, config, ... }:
-with lib;
-{
-  options = {
-    grfn.impure.clonedRepos = mkOption {
-      description = "Repositories to clone";
-      default = {};
-      type = with types; loaOf (
-        let sm = submodule {
-          options = {
-            url = mkOption {
-              type = nullOr str;
-              description = "URL of repository to clone";
-              default = null;
-            };
-
-            github = mkOption {
-              type = nullOr str;
-              description = "Github owner/repo of repository to clone";
-              default = null;
-            };
-
-            path = mkOption {
-              type = str;
-              description = "Path to clone to";
-            };
-
-            onClone = mkOption {
-              type = str;
-              description = ''
-                Shell command to run after cloning the repo for the first time.
-                Runs inside the repo itself.
-              '';
-              default = "";
-            };
-
-            after = mkOption {
-              type = listOf str;
-              description = "Activation hooks that this repository must be cloned after";
-              default = [];
-            };
-          };
-        };
-        in addCheck sm (cr: (! isNull cr.url || ! isNull cr.github))
-      );
-    };
-  };
-
-  config = {
-    home.activation =
-      mapAttrs
-      (_: {
-        url, path, github, onClone, after, ...
-      }:
-        let repoURL = if isNull url then "git@github.com:${github}" else url;
-        in hm.dag.entryAfter (["writeBoundary"] ++ after) ''
-          $DRY_RUN_CMD mkdir -p $(dirname "${path}")
-          if [[ ! -d ${path} ]]; then
-            $DRY_RUN_CMD git clone "${repoURL}" "${path}"
-            pushd ${path}
-            $DRY_RUN_CMD ${onClone}
-            popd
-          fi
-        '')
-      config.grfn.impure.clonedRepos;
-  };
-}
diff --git a/users/grfn/system/home/modules/nixos-logo.txt b/users/grfn/system/home/modules/nixos-logo.txt
deleted file mode 100644
index d4b16b44f0..0000000000
--- a/users/grfn/system/home/modules/nixos-logo.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-                 ((((((          ###%######       ##%###/
-               ,(((((((/(          #%#%#%#%#    .#%#%#%#%#
-                 ((((((///          %#######%. #####%###/
-                  (((((/(//,         /##%###%###%######
-                    (((//////          #####%########(
-         .(((((((((((((((///////////////#%%%########          ((
-        (((((((((((((((///////////////////#########         .((((
-       ((((((((((((((((/(//////////////////##########      ((((((((
-                   (#########                #########    (((((((((
-                  #########                   #########/((((((((((
-                *#########                     .#######(((((((((
- ###%###################                         ####(//((((((((((((((((
-####%##################                           .#////////((((((((((((((
-%%%%%%%%%%%%%%#######((                           ////////////((((((((((((
- ###%#######%#######////.                        ///////////////////((((
-         ###%###%#///////(                      /////////
-       .####%#### /////////                   /////////,
-      %#%#%#%#%*   /////////(                /////////
-      .#####%#       ////////(######################%#######%#####,
-        %####         (////////#####################%###%###%###%
-         .#          (//////(//((###################%#######%##%
-                    (//(((((((((((          #####%%%%(
-                  //(/((((((((((((((          ######%##
-                 (((((((((  (((((((((          #####%###/
-                (((((((((    /(((((((((         .###%####%
-                 ((((((        (((((((((          %#%#%#/
diff --git a/users/grfn/system/home/modules/obs.nix b/users/grfn/system/home/modules/obs.nix
deleted file mode 100644
index d1dade477c..0000000000
--- a/users/grfn/system/home/modules/obs.nix
+++ /dev/null
@@ -1,66 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with pkgs;
-
-let
-  libuiohook = stdenv.mkDerivation rec {
-    pname = "libuiohook";
-    version = "1.1";
-    src = fetchFromGitHub {
-      owner = "kwhat";
-      repo = "libuiohook";
-      rev = version;
-      sha256 = "1isfxn3cfrdqq22d3mlz2lzm4asf9gprs7ww2xy9c3j3srk9kd7r";
-    };
-
-    preConfigure = ''
-      ./bootstrap.sh
-    '';
-
-    nativeBuildInputs = [ pkg-config ];
-    buildInputs = [
-      libtool autoconf automake
-      x11
-      xorg.libXtst
-      xorg.libXinerama
-      xorg.libxkbfile
-      libxkbcommon
-    ];
-  };
-
-  obs-input-overlay = stdenv.mkDerivation rec {
-    pname = "obs-input-overlay";
-    version = "4.8";
-    src = fetchFromGitHub {
-      owner = "univrsal";
-      repo = "input-overlay";
-      rev = "v${version}";
-      sha256 = "1dklg0dx9ijwyhgwcaqz859rbpaivmqxqvh9w3h4byrh5pnkz8bf";
-      fetchSubmodules = true;
-    };
-
-    nativeBuildInputs = [ cmake ];
-    buildInputs = [ obs-studio libuiohook ];
-
-    postPatch = ''
-      sed -i CMakeLists.txt \
-        -e '2iinclude(${obs-studio.src}/cmake/Modules/ObsHelpers.cmake)' \
-        -e '2ifind_package(LibObs REQUIRED)'
-    '';
-
-    cmakeFlags = [
-      "-Wno-dev"
-    ];
-  };
-in
-{
-  home.packages = [
-    obs-studio
-    obs-input-overlay
-  ];
-
-  xdg.configFile."obs-studio/plugins/input-overlay/bin/64bit/input-overlay.so".source =
-    "${obs-input-overlay}/lib/obs-plugins/input-overlay.so";
-  xdg.configFile."obs-studio/plugins/input-overlay/data".source =
-    "${obs-input-overlay}/share/obs/obs-plugins/input-overlay";
-}
diff --git a/users/grfn/system/home/modules/rtlsdr.nix b/users/grfn/system/home/modules/rtlsdr.nix
deleted file mode 100644
index a1c717617a..0000000000
--- a/users/grfn/system/home/modules/rtlsdr.nix
+++ /dev/null
@@ -1,21 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-
-  nixpkgs-gnuradio = import (pkgs.fetchFromGitHub {
-    owner = "doronbehar";
-    repo = "nixpkgs";
-    rev = "712561aa5f10bfe6112a1726a912585612a70d1f";
-    sha256 = "04yqflbwjcfl9vlplphpj82csqqz9k6m3nj1ybhwgmsc4by7vivl";
-  }) {};
-
-in
-
-{
-  home.packages = with pkgs; [
-    rtl-sdr
-    nixpkgs-gnuradio.gnuradio
-    nixpkgs-gnuradio.gnuradio.plugins.osmosdr
-    nixpkgs-gnuradio.gqrx
-  ];
-}
diff --git a/users/grfn/system/home/modules/tarsnap.nix b/users/grfn/system/home/modules/tarsnap.nix
deleted file mode 100644
index 4bff19910f..0000000000
--- a/users/grfn/system/home/modules/tarsnap.nix
+++ /dev/null
@@ -1,64 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  home.packages = with pkgs; [
-    tarsnap
-  ];
-
-  home.file.".tarsnaprc".text = ''
-  ### Recommended options
-
-  # Tarsnap cache directory
-  cachedir /home/grfn/.cache/tarsnap
-
-  # Tarsnap key file
-  keyfile /home/grfn/.private/tarsnap.key
-
-  # Don't archive files which have the nodump flag set.
-  nodump
-
-  # Print statistics when creating or deleting archives.
-  print-stats
-
-  # Create a checkpoint once per GB of uploaded data.
-  checkpoint-bytes 1G
-
-  ### Commonly useful options
-
-  # Use SI prefixes to make numbers printed by --print-stats more readable.
-  humanize-numbers
-
-  ### Other options, not applicable to most systems
-
-  # Aggressive network behaviour: Use multiple TCP connections when
-  # writing archives.  Use of this option is recommended only in
-  # cases where TCP congestion control is known to be the limiting
-  # factor in upload performance.
-  #aggressive-networking
-
-  # Exclude files and directories matching specified patterns.
-  # Only one file or directory per command; multiple "exclude"
-  # commands may be given.
-  #exclude
-
-  # Include only files and directories matching specified patterns.
-  # Only one file or directory per command; multiple "include"
-  # commands may be given.
-  #include
-
-  # Attempt to reduce tarsnap memory consumption.  This option
-  # will slow down the process of creating archives, but may help
-  # on systems where the average size of files being backed up is
-  # less than 1 MB.
-  #lowmem
-
-  # Try even harder to reduce tarsnap memory consumption.  This can
-  # significantly slow down tarsnap, but reduces its memory usage
-  # by an additional factor of 2 beyond what the lowmem option does.
-  #verylowmem
-
-  # Snapshot time.  Use this option if you are backing up files
-  # from a filesystem snapshot rather than from a "live" filesystem.
-  #snaptime <file>
-  '';
-}
diff --git a/users/grfn/system/system/iso.nix b/users/grfn/system/system/iso.nix
deleted file mode 100644
index 4adccebfb8..0000000000
--- a/users/grfn/system/system/iso.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-{ depot, lib, pkgs, ... }:
-
-let
-  configuration = { ... }: {
-    imports = [
-      "${pkgs.path}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix"
-      "${pkgs.path}/nixos/modules/installer/cd-dvd/channel.nix"
-    ];
-
-    networking.networkmanager.enable = true;
-    networking.useDHCP = false;
-    networking.firewall.enable = false;
-    networking.wireless.enable = lib.mkForce false;
-  };
-in (depot.third_party.nixos {
-  inherit configuration;
-}).config.system.build.isoImage
diff --git a/users/grfn/system/system/machines/mugwump.nix b/users/grfn/system/system/machines/mugwump.nix
deleted file mode 100644
index 6e6deb6c2b..0000000000
--- a/users/grfn/system/system/machines/mugwump.nix
+++ /dev/null
@@ -1,251 +0,0 @@
-{ config, lib, pkgs, modulesPath, depot, ... }:
-
-with lib;
-
-{
-  imports = [
-    ../modules/common.nix
-    (modulesPath + "/installer/scan/not-detected.nix")
-    "${depot.path}/ops/modules/prometheus-fail2ban-exporter.nix"
-  ];
-
-  networking.hostName = "mugwump";
-
-  boot = {
-    loader.systemd-boot.enable = true;
-
-    kernelModules = [ "kvm-intel" ];
-    extraModulePackages = [ ];
-
-    initrd = {
-      availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usb_storage" "usbhid" "sd_mod" ];
-      kernelModules = [
-        "uas" "usbcore" "usb_storage" "vfat" "nls_cp437" "nls_iso8859_1"
-      ];
-
-      postDeviceCommands = pkgs.lib.mkBefore ''
-        mkdir -m 0755 -p /key
-        sleep 2
-        mount -n -t vfat -o ro `findfs UUID=9048-A9D5` /key
-      '';
-
-      luks.devices."cryptroot" = {
-        device = "/dev/disk/by-uuid/803a9028-339c-4617-a213-4fe138161f6d";
-        keyFile = "/key/keyfile";
-        preLVM = false;
-      };
-    };
-  };
-
-  fileSystems = {
-    "/" = {
-      device = "/dev/mapper/cryptroot";
-      fsType = "btrfs";
-    };
-    "/boot" = {
-      device = "/dev/disk/by-uuid/7D74-0E4B";
-      fsType = "vfat";
-    };
-  };
-
-  networking.interfaces = {
-    enp0s25.useDHCP = false;
-    wlp2s0.useDHCP = false;
-  };
-
-  networking.firewall.enable = true;
-  networking.firewall.allowedTCPPorts = [ 22 80 443 ];
-
-  security.sudo.extraRules = [{
-    groups = ["wheel"];
-    commands = [{ command = "ALL"; options = ["NOPASSWD"]; }];
-  }];
-
-  nix.gc.dates = "monthly";
-
-  services.fail2ban = {
-    enable = true;
-    ignoreIP = [
-      "172.16.0.0/16"
-    ];
-  };
-
-  services.openssh = {
-    allowSFTP = false;
-    passwordAuthentication = false;
-    permitRootLogin = "no";
-  };
-
-  services.grafana = {
-    enable = true;
-    port = 3000;
-    domain = "metrics.gws.fyi";
-    rootUrl = "https://metrics.gws.fyi";
-    dataDir = "/var/lib/grafana";
-    analytics.reporting.enable = false;
-
-    provision = {
-      enable = true;
-      datasources = [{
-        name = "Prometheus";
-        type = "prometheus";
-        url = "http://localhost:9090";
-      }];
-    };
-  };
-
-  security.acme.email = "root@gws.fyi";
-  security.acme.acceptTerms = true;
-
-  services.nginx = {
-    enable = true;
-    statusPage = true;
-    recommendedGzipSettings = true;
-    recommendedOptimisation = true;
-    recommendedTlsSettings = true;
-
-    virtualHosts = {
-      "metrics.gws.fyi" = {
-        enableACME = true;
-        forceSSL = true;
-        locations."/" = {
-          proxyPass = "http://localhost:${toString config.services.grafana.port}";
-        };
-      };
-    };
-  };
-
-  services.ddclient = {
-    enable = true;
-    domains = [ "home.gws.fyi" ];
-    interval = "1d";
-    zone = "gws.fyi";
-    protocol = "cloudflare";
-    username = "root@gws.fyi";
-    quiet = true;
-  };
-
-  systemd.services.ddclient.serviceConfig = {
-    EnvironmentFile = "/etc/secrets/cloudflare.env";
-    DynamicUser = lib.mkForce false;
-    ExecStart = lib.mkForce (
-      let runtimeDir =
-            config.systemd.services.ddclient.serviceConfig.RuntimeDirectory;
-      in pkgs.writeShellScript "ddclient" ''
-        set -eo pipefail
-
-        ${pkgs.gnused}/bin/sed -i -s s/password=/password=$CLOUDFLARE_API_KEY/ /run/${runtimeDir}/ddclient.conf
-        exec ${pkgs.ddclient}/bin/ddclient \
-          -file /run/${runtimeDir}/ddclient.conf \
-          -login=$CLOUDFLARE_EMAIL \
-      '');
-  };
-
-  security.acme.certs."metrics.gws.fyi" = {
-    dnsProvider = "cloudflare";
-    credentialsFile = "/etc/secrets/cloudflare.env";
-    webroot = mkForce null;
-  };
-
-  services.prometheus = {
-    enable = true;
-    exporters = {
-      node = {
-        enable = true;
-        openFirewall = false;
-
-        enabledCollectors = [
-          "processes"
-          "systemd"
-          "tcpstat"
-          "wifi"
-        ];
-      };
-
-      nginx = {
-        enable = true;
-        openFirewall = true;
-        sslVerify = false;
-        constLabels = [ "host=mugwump" ];
-      };
-
-      blackbox = {
-        enable = true;
-        openFirewall = true;
-        configFile = pkgs.writeText "blackbox-exporter.yaml" (builtins.toJSON {
-          modules = {
-            https_2xx = {
-              prober = "http";
-              http = {
-                method = "GET";
-                fail_if_ssl = false;
-                fail_if_not_ssl = true;
-                preferred_ip_protocol = "ip4";
-              };
-            };
-          };
-        });
-      };
-    };
-
-    scrapeConfigs = [{
-      job_name = "node";
-      scrape_interval = "5s";
-      static_configs = [{
-        targets = ["localhost:${toString config.services.prometheus.exporters.node.port}"];
-      }];
-    } {
-      job_name = "nginx";
-      scrape_interval = "5s";
-      static_configs = [{
-        targets = ["localhost:${toString config.services.prometheus.exporters.nginx.port}"];
-      }];
-    } {
-      job_name = "blackbox";
-      metrics_path = "/probe";
-      params.module = ["https_2xx"];
-      scrape_interval = "5s";
-      static_configs = [{
-        targets = [
-          "https://gws.fyi"
-          "https://windtunnel.ci"
-          "https://app.windtunnel.ci"
-          "https://metrics.gws.fyi"
-        ];
-      }];
-      relabel_configs = [{
-        source_labels = ["__address__"];
-        target_label = "__param_target";
-      } {
-        source_labels = ["__param_target"];
-        target_label = "instance";
-      } {
-        target_label = "__address__";
-        replacement = "localhost:${toString config.services.prometheus.exporters.blackbox.port}";
-      }];
-    }];
-  };
-
-  virtualisation.docker.enable = true;
-
-  services.buildkite-agents = listToAttrs (map (n: rec {
-    name = "mugwump-${toString n}";
-    value = {
-      inherit name;
-      enable = true;
-      tokenPath = "/etc/secrets/buildkite-agent-token";
-      privateSshKeyPath = "/etc/secrets/buildkite-ssh-key";
-      runtimePackages = with pkgs; [
-        docker
-        nix
-        gnutar
-        gzip
-      ];
-    };
-  }) (range 1 1));
-
-  users.users."buildkite-agent-mugwump-1" = {
-    isSystemUser = true;
-    extraGroups = [ "docker" ];
-  };
-}
diff --git a/users/grfn/system/system/machines/roswell.nix b/users/grfn/system/system/machines/roswell.nix
deleted file mode 100644
index 6eb4a510b8..0000000000
--- a/users/grfn/system/system/machines/roswell.nix
+++ /dev/null
@@ -1,17 +0,0 @@
-{ depot, config, lib, pkgs, modulesPath, ... }:
-
-{
-  imports = [
-    ../modules/common.nix
-    "${modulesPath}/installer/scan/not-detected.nix"
-    "${modulesPath}/virtualisation/amazon-image.nix"
-  ];
-
-  ec2.hvm = true;
-
-  networking.hostName = "roswell";
-
-  users.users.grfn.openssh.authorizedKeys.keys = [
-    depot.users.grfn.keys.main
-  ];
-}
diff --git a/users/grfn/system/system/modules/common.nix b/users/grfn/system/system/modules/common.nix
deleted file mode 100644
index 741b0473a5..0000000000
--- a/users/grfn/system/system/modules/common.nix
+++ /dev/null
@@ -1,75 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-
-  depot = import ../../../../.. {};
-
-in
-
-with lib;
-
-{
-  boot.loader.systemd-boot.enable = true;
-  boot.loader.efi.canTouchEfiVariables = true;
-
-  networking.useDHCP = false;
-  networking.networkmanager.enable = true;
-
-  i18n = {
-    defaultLocale = "en_US.UTF-8";
-  };
-
-  time.timeZone = lib.mkDefault "America/New_York";
-
-  environment.systemPackages = with pkgs; [
-    wget
-    vim
-    zsh
-    git
-    w3m
-    libnotify
-    file
-    lm_sensors
-    dnsutils
-    htop
-  ];
-
-  services.openssh.enable = true;
-
-  programs.ssh.startAgent = true;
-
-  networking.firewall.enable = mkDefault false;
-
-  users.mutableUsers = true;
-  programs.zsh.enable = true;
-  environment.pathsToLink = [ "/share/zsh" ];
-  users.users.grfn = {
-    isNormalUser = true;
-    initialPassword = "password";
-    extraGroups = [
-      "wheel"
-      "networkmanager"
-      "audio"
-      "docker"
-    ];
-    shell = pkgs.zsh;
-  };
-
-  nix = {
-    trustedUsers = [ "grfn" ];
-    autoOptimiseStore = true;
-    distributedBuilds = true;
-
-    gc = {
-      automatic = true;
-      dates = mkDefault "weekly";
-      options = "--delete-older-than 30d";
-    };
-  };
-
-  services.udev.packages = with pkgs; [
-    yubikey-personalization
-  ];
-
-  services.pcscd.enable = true;
-}
diff --git a/users/grfn/system/system/modules/development.nix b/users/grfn/system/system/modules/development.nix
deleted file mode 100644
index bfa0e22cff..0000000000
--- a/users/grfn/system/system/modules/development.nix
+++ /dev/null
@@ -1,6 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  virtualisation.docker.enable = true;
-  users.users.grfn.extraGroups = [ "docker" ];
-}
diff --git a/users/grfn/system/system/modules/kernel.nix b/users/grfn/system/system/modules/kernel.nix
deleted file mode 100644
index dd213fcca3..0000000000
--- a/users/grfn/system/system/modules/kernel.nix
+++ /dev/null
@@ -1,39 +0,0 @@
-{ config, lib, pkgs, ... }:
-with lib.versions;
-let
-  inherit (pkgs) stdenvNoCC;
-  kernelRelease = config.boot.kernelPackages.kernel.version;
-  mj = major kernelRelease;
-  mm = majorMinor kernelRelease;
-  patched-linux-ck = stdenvNoCC.mkDerivation {
-    name = "linux-ck";
-    src = builtins.fetchurl {
-      name = "linux-ck-patch-${mm}-ck1.xz";
-      # example: http://ck.kolivas.org/patches/5.0/5.4/5.4-ck1/patch-5.4-ck1.xz
-      url = "http://ck.kolivas.org/patches/${mj}.0/${mm}/${mm}-ck1/patch-${mm}-ck1.xz";
-      sha256 = "1kka38rmjcqsv4j2anczrsni0bf6yfdx2vsxbna3ic84nh3rz434";
-    };
-
-    unpackPhase = ''
-      ${pkgs.xz}/bin/unxz -kfdc $src > patch-${mm}-ck1
-    '';
-
-    installPhase = ''
-      cp patch-${mm}-ck1 $out
-    '';
-  };
-in
-{
-  boot.kernelPackages = pkgs.linuxPackages_5_10.extend (self: super: {
-    kernel = super.kernel.override {
-      ignoreConfigErrors = true;
-      kernelPatches = super.kernel.kernelPatches ++ [{
-        name = "linux-ck";
-        patch = patched-linux-ck;
-      }];
-      argsOverride = {
-        modDirVersion = super.kernel.modDirVersion + "-ck1";
-      };
-    };
-  });
-}
diff --git a/users/grfn/system/system/modules/laptop.nix b/users/grfn/system/system/modules/laptop.nix
deleted file mode 100644
index 05c5333e51..0000000000
--- a/users/grfn/system/system/modules/laptop.nix
+++ /dev/null
@@ -1,15 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  imports = [
-    ./reusable/battery.nix
-  ];
-
-  laptop.onLowBattery.enable = true;
-
-  services.logind.extraConfig = ''
-    HandlePowerKey=hibernate
-  '';
-
-  services.tlp.enable = true;
-}
diff --git a/users/grfn/system/system/modules/reusable/battery.nix b/users/grfn/system/system/modules/reusable/battery.nix
deleted file mode 100644
index ca92e0c3f6..0000000000
--- a/users/grfn/system/system/modules/reusable/battery.nix
+++ /dev/null
@@ -1,32 +0,0 @@
-{ config, lib, pkgs, ... }:
-with lib;
-{
-  options = {
-    laptop.onLowBattery = {
-      enable = mkEnableOption "Perform action on low battery";
-
-      thresholdPercentage = mkOption {
-        description = "Threshold battery percentage on which to perform the action";
-        default = 8;
-        type = types.int;
-      };
-
-      action = mkOption {
-        description = "Action to perform on low battery";
-        default = "hibernate";
-        type = types.enum [ "hibernate" "suspend" "suspend-then-hibernate" ];
-      };
-    };
-  };
-
-  config =
-    let cfg = config.laptop.onLowBattery;
-    in mkIf cfg.enable {
-    services.udev.extraRules = concatStrings [
-      ''SUBSYSTEM=="power_supply", ''
-      ''ATTR{status}=="Discharging", ''
-      ''ATTR{capacity}=="[0-${toString cfg.thresholdPercentage}]", ''
-      ''RUN+="${pkgs.systemd}/bin/systemctl ${cfg.action}"''
-    ];
-  };
-}
diff --git a/users/grfn/system/system/modules/tvl.nix b/users/grfn/system/system/modules/tvl.nix
deleted file mode 100644
index 905ec8ced5..0000000000
--- a/users/grfn/system/system/modules/tvl.nix
+++ /dev/null
@@ -1,37 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-{
-  nix = {
-    buildMachines = [{
-      hostName = "whitby.tvl.fyi";
-      sshUser = "grfn";
-      sshKey = "/root/.ssh/id_rsa";
-      system = "x86_64-linux";
-      maxJobs = 64;
-      supportedFeatures = ["big-parallel" "kvm" "nixos-test" "benchmark"];
-    }];
-
-    extraOptions = ''
-      builders-use-substitutes = true
-    '';
-
-    binaryCaches = [
-      "https://cache.nixos.org"
-      "ssh://nix-ssh@whitby.tvl.fyi"
-    ];
-    trustedBinaryCaches = [
-      "https://cache.nixos.org"
-      "ssh://nix-ssh@whitby.tvl.fyi"
-    ];
-    binaryCachePublicKeys = [
-      "cache.tvl.fyi:fd+9d1ceCPvDX/xVhcfv8nAa6njEhAGAEe+oGJDEeoc="
-    ];
-  };
-
-  programs.ssh.knownHosts.whitby = {
-    hostNames = [ "whitby" "whitby.tvl.fyi" "49.12.129.211"];
-    publicKeyFile = pkgs.writeText "whitby.pub" ''
-      ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILNh/w4BSKov0jdz3gKBc98tpoLta5bb87fQXWBhAl2I
-    '';
-  };
-}
diff --git a/users/grfn/system/system/modules/work/kolide.deb b/users/grfn/system/system/modules/work/kolide.deb
deleted file mode 100644
index a319a5806f..0000000000
--- a/users/grfn/system/system/modules/work/kolide.deb
+++ /dev/null
Binary files differdiff --git a/users/grfn/system/system/modules/work/kolide.nix b/users/grfn/system/system/modules/work/kolide.nix
deleted file mode 100644
index 29ee0a0d7c..0000000000
--- a/users/grfn/system/system/modules/work/kolide.nix
+++ /dev/null
@@ -1,49 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-let
-  deb = ./kolide.deb;
-
-  kolide = pkgs.runCommand "kolide-data" {
-    buildInputs = [ pkgs.binutils-unwrapped ];
-  } ''
-    cp ${deb} ./kolide.deb
-    ar x kolide.deb
-    mkdir result
-    tar xzf data.tar.gz -C result
-    patchelf \
-      --set-interpreter ${pkgs.glibc}/lib/ld-linux-x86-64.so.2 \
-      --set-rpath "${lib.makeLibraryPath (with pkgs; [
-        zlib
-      ])}" \
-      result/usr/local/kolide-k2/bin/osqueryd
-    mv result $out
-  '';
-
-in {
-  systemd.services."launcher.kolide-k2" = {
-    wantedBy = [ "multi-user.target" ];
-    after = [ "network.target" "syslog.service" ];
-    description = "The Kolide Launcher";
-    serviceConfig = {
-      ExecStart = ''
-        ${kolide}/usr/local/kolide-k2/bin/launcher \
-          -config \
-          ${pkgs.writeText "launcher.flags" ''
-            with_initial_runner
-            control
-            autoupdate
-            root_directory /var/lib/kolide
-            osqueryd_path ${kolide}/usr/local/kolide-k2/bin/osqueryd
-            enroll_secret_path ${kolide}/etc/kolide-k2/secret
-            control_hostname k2control.kolide.com
-            update_channel stable
-            transport jsonrpc
-            hostname k2device.kolide.com
-          ''}
-      '';
-      StateDirectory = "kolide";
-      Restart = "on-failure";
-      RestartSec = 3;
-    };
-  };
-}
diff --git a/users/grfn/xanthous/.envrc b/users/grfn/xanthous/.envrc
deleted file mode 100644
index be81feddb1..0000000000
--- a/users/grfn/xanthous/.envrc
+++ /dev/null
@@ -1 +0,0 @@
-eval "$(lorri direnv)"
\ No newline at end of file
diff --git a/users/grfn/xanthous/default.nix b/users/grfn/xanthous/default.nix
deleted file mode 100644
index 1316ff583f..0000000000
--- a/users/grfn/xanthous/default.nix
+++ /dev/null
@@ -1,23 +0,0 @@
-{ depot ? (import ../../../. {})
-, pkgs ? depot.third_party.nixpkgs
-, ... }:
-
-let
-  ignore = depot.third_party.gitignoreSource.gitignoreFilter ./.;
-  src = builtins.path {
-    name = "xanthous-source";
-    path = ./.;
-    filter = path: type: ignore path type
-      || builtins.baseNameOf path == "package.yaml";
-  };
-  # generated by cabal2nix
-  basePkg = pkgs.haskellPackages.callPackage ./pkg.nix { };
-in
-
-pkgs.haskell.lib.overrideCabal basePkg (default: {
-  inherit src;
-  version = "canon";
-  configureFlags = [
-    "--ghc-option=-Wall --ghc-option=-Werror"
-  ] ++ (default.configureFlags or []);
-})
diff --git a/users/grfn/xanthous/pkg.nix b/users/grfn/xanthous/pkg.nix
deleted file mode 100644
index 020fa18266..0000000000
--- a/users/grfn/xanthous/pkg.nix
+++ /dev/null
@@ -1,80 +0,0 @@
-{ mkDerivation, aeson, array, async, base, bifunctors, brick
-, checkers, classy-prelude, comonad, comonad-extras, constraints
-, containers, criterion, data-default, data-interval, deepseq
-, directory, fgl, fgl-arbitrary, file-embed, filepath
-, generic-arbitrary, generic-lens, groups, hgeometry
-, hgeometry-combinatorial, hpack, JuicyPixels, lens
-, lens-properties, lib, lifted-async, linear, megaparsec, mmorph
-, monad-control, MonadRandom, mtl, optparse-applicative, parallel
-, parser-combinators, pointed, QuickCheck, quickcheck-instances
-, quickcheck-text, random, random-extras, random-fu, random-source
-, Rasterific, raw-strings-qq, reflection, semigroupoids, semigroups
-, splitmix, stache, streams, tasty, tasty-hunit, tasty-quickcheck
-, text, text-zipper, tomland, transformers, vector, vty, witherable
-, yaml, zlib
-}:
-mkDerivation {
-  pname = "xanthous";
-  version = "0.1.0.0";
-  src = ./.;
-  isLibrary = true;
-  isExecutable = true;
-  libraryHaskellDepends = [
-    aeson array async base bifunctors brick checkers classy-prelude
-    comonad comonad-extras constraints containers criterion
-    data-default data-interval deepseq directory fgl fgl-arbitrary
-    file-embed filepath generic-arbitrary generic-lens groups hgeometry
-    hgeometry-combinatorial JuicyPixels lens lifted-async linear
-    megaparsec mmorph monad-control MonadRandom mtl
-    optparse-applicative parallel parser-combinators pointed QuickCheck
-    quickcheck-instances quickcheck-text random random-extras random-fu
-    random-source Rasterific raw-strings-qq reflection semigroupoids
-    semigroups splitmix stache streams text text-zipper tomland
-    transformers vector vty witherable yaml zlib
-  ];
-  libraryToolDepends = [ hpack ];
-  executableHaskellDepends = [
-    aeson array async base bifunctors brick checkers classy-prelude
-    comonad comonad-extras constraints containers criterion
-    data-default data-interval deepseq directory fgl fgl-arbitrary
-    file-embed filepath generic-arbitrary generic-lens groups hgeometry
-    hgeometry-combinatorial JuicyPixels lens lifted-async linear
-    megaparsec mmorph monad-control MonadRandom mtl
-    optparse-applicative parallel parser-combinators pointed QuickCheck
-    quickcheck-instances quickcheck-text random random-extras random-fu
-    random-source Rasterific raw-strings-qq reflection semigroupoids
-    semigroups splitmix stache streams text text-zipper tomland
-    transformers vector vty witherable yaml zlib
-  ];
-  testHaskellDepends = [
-    aeson array async base bifunctors brick checkers classy-prelude
-    comonad comonad-extras constraints containers criterion
-    data-default data-interval deepseq directory fgl fgl-arbitrary
-    file-embed filepath generic-arbitrary generic-lens groups hgeometry
-    hgeometry-combinatorial JuicyPixels lens lens-properties
-    lifted-async linear megaparsec mmorph monad-control MonadRandom mtl
-    optparse-applicative parallel parser-combinators pointed QuickCheck
-    quickcheck-instances quickcheck-text random random-extras random-fu
-    random-source Rasterific raw-strings-qq reflection semigroupoids
-    semigroups splitmix stache streams tasty tasty-hunit
-    tasty-quickcheck text text-zipper tomland transformers vector vty
-    witherable yaml zlib
-  ];
-  benchmarkHaskellDepends = [
-    aeson array async base bifunctors brick checkers classy-prelude
-    comonad comonad-extras constraints containers criterion
-    data-default data-interval deepseq directory fgl fgl-arbitrary
-    file-embed filepath generic-arbitrary generic-lens groups hgeometry
-    hgeometry-combinatorial JuicyPixels lens lifted-async linear
-    megaparsec mmorph monad-control MonadRandom mtl
-    optparse-applicative parallel parser-combinators pointed QuickCheck
-    quickcheck-instances quickcheck-text random random-extras random-fu
-    random-source Rasterific raw-strings-qq reflection semigroupoids
-    semigroups splitmix stache streams text text-zipper tomland
-    transformers vector vty witherable yaml zlib
-  ];
-  prePatch = "hpack";
-  homepage = "https://github.com/glittershark/xanthous#readme";
-  description = "A WIP TUI RPG";
-  license = lib.licenses.gpl3Only;
-}
diff --git a/users/grfn/xanthous/src/Xanthous/Command.hs b/users/grfn/xanthous/src/Xanthous/Command.hs
deleted file mode 100644
index 30359c6c64..0000000000
--- a/users/grfn/xanthous/src/Xanthous/Command.hs
+++ /dev/null
@@ -1,77 +0,0 @@
---------------------------------------------------------------------------------
-module Xanthous.Command where
---------------------------------------------------------------------------------
-import Xanthous.Prelude hiding (Left, Right, Down)
---------------------------------------------------------------------------------
-import Graphics.Vty.Input (Key(..), Modifier(..))
-import qualified Data.Char as Char
---------------------------------------------------------------------------------
-import Xanthous.Data (Direction(..))
---------------------------------------------------------------------------------
-
-data Command
-  = Quit
-  | Move Direction
-  | StartAutoMove Direction
-  | PreviousMessage
-  | PickUp
-  | Drop
-  | Open
-  | Close
-  | Wait
-  | Eat
-  | Look
-  | Save
-  | Read
-  | ShowInventory
-  | DescribeInventory
-  | Wield
-  | GoUp
-  | GoDown
-  | Rest
-
-    -- | TODO replace with `:` commands
-  | ToggleRevealAll
-
-commandFromKey :: Key -> [Modifier] -> Maybe Command
-commandFromKey (KChar 'q') [] = Just Quit
-commandFromKey (KChar '.') [] = Just Wait
-commandFromKey (KChar (directionFromChar -> Just dir)) [] = Just $ Move dir
-commandFromKey (KChar c) []
-  | Char.isUpper c
-  , Just dir <- directionFromChar $ Char.toLower c
-  = Just $ StartAutoMove dir
-commandFromKey (KChar 'p') [MCtrl] = Just PreviousMessage
-commandFromKey (KChar ',') [] = Just PickUp
-commandFromKey (KChar 'd') [] = Just Drop
-commandFromKey (KChar 'o') [] = Just Open
-commandFromKey (KChar 'c') [] = Just Close
-commandFromKey (KChar ';') [] = Just Look
-commandFromKey (KChar 'e') [] = Just Eat
-commandFromKey (KChar 'S') [] = Just Save
-commandFromKey (KChar 'r') [] = Just Read
-commandFromKey (KChar 'i') [] = Just ShowInventory
-commandFromKey (KChar 'I') [] = Just DescribeInventory
-commandFromKey (KChar 'w') [] = Just Wield
-commandFromKey (KChar '<') [] = Just GoUp
-commandFromKey (KChar '>') [] = Just GoDown
-commandFromKey (KChar 'R') [] = Just Rest
-
--- DEBUG COMMANDS --
-commandFromKey (KChar 'r') [MMeta] = Just ToggleRevealAll
-
-commandFromKey _ _ = Nothing
-
---------------------------------------------------------------------------------
-
-directionFromChar :: Char -> Maybe Direction
-directionFromChar 'h' = Just Left
-directionFromChar 'j' = Just Down
-directionFromChar 'k' = Just Up
-directionFromChar 'l' = Just Right
-directionFromChar 'y' = Just UpLeft
-directionFromChar 'u' = Just UpRight
-directionFromChar 'b' = Just DownLeft
-directionFromChar 'n' = Just DownRight
-directionFromChar '.' = Just Here
-directionFromChar _   = Nothing
diff --git a/users/grfn/xanthous/src/Xanthous/Entities/Raws/stick.yaml b/users/grfn/xanthous/src/Xanthous/Entities/Raws/stick.yaml
deleted file mode 100644
index 4100808ca0..0000000000
--- a/users/grfn/xanthous/src/Xanthous/Entities/Raws/stick.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-Item:
-  name: stick
-  description: a wooden stick
-  longDescription: A sturdy branch broken off from some sort of tree
-  char:
-    char: โˆค
-    style:
-      foreground: yellow
-  wieldable:
-    damage: 2
-    attackMessage:
-      - You bonk the {{creature.creatureType.name}} over the head with your stick.
-      - You bash the {{creature.creatureType.name}} on the noggin with your stick.
-      - You whack the {{creature.creatureType.name}} with your stick.
-  # https://www.sciencedirect.com/topics/agricultural-and-biological-sciences/wood-density
-  # it's a hard stick. so it's dense wood.
-  density: 890000 # g/mยณ
-  volume: [ 0.003, 0.006 ] # โ‰ˆ3.5 cm radius ร— โ‰ˆ1m length
diff --git a/users/grfn/xanthous/test/Xanthous/Entities/RawsSpec.hs b/users/grfn/xanthous/test/Xanthous/Entities/RawsSpec.hs
deleted file mode 100644
index 2e6f35457f..0000000000
--- a/users/grfn/xanthous/test/Xanthous/Entities/RawsSpec.hs
+++ /dev/null
@@ -1,16 +0,0 @@
--- |
-
-module Xanthous.Entities.RawsSpec (main, test) where
-
-import Test.Prelude
-import Xanthous.Entities.Raws
-
-main :: IO ()
-main = defaultMain test
-
-test :: TestTree
-test = testGroup "Xanthous.Entities.Raws"
-  [ testGroup "raws"
-    [ testCase "are all valid" $ raws `deepseq` pure ()
-    ]
-  ]
diff --git a/users/isomer/OWNERS b/users/isomer/OWNERS
deleted file mode 100644
index 6997cd391d..0000000000
--- a/users/isomer/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherited: false
-owners:
-  - isomer
diff --git a/users/isomer/keys.nix b/users/isomer/keys.nix
deleted file mode 100644
index 8c29e27895..0000000000
--- a/users/isomer/keys.nix
+++ /dev/null
@@ -1,7 +0,0 @@
-# SSH public keys
-{ ... }:
-
-rec {
-  perry = "cert-authority,principals=perry ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCXWKN+FXlQAQ36R4+FHJ9f15Tz/48xLK1f85Yf9eBrvJJVMn6ge3Cy8AJ2nymBtVvCC86q616yl4Mn+CrKBH/vHr4jY9nxJ7HHgKI8ERr+7KpLIAiiaeIBljWwCy918lK3MijRCuj0P0d3v8CEFJjyCsiyglDVcNhsW87VqqZE6lUg4Alw1CGAmNjamxdoIZxjZAM9vJtZrlYnUiu+X7vTl5ttTaZkLCCfu+/bJAKFBWPG5BPaNjjfGVuTKqEc4plkI3JeZBu3Or3LzlYxcvp71i+eKGJ8F/nMBlo25iQsQpi8ZS7JYAhj3mYVrstw7j+nkgbordvDOK5NbDMi6GzX";
-  all = [ perry ];
-}
diff --git a/users/j4m3s/OWNERS b/users/j4m3s/OWNERS
new file mode 100644
index 0000000000..9d95afbeaa
--- /dev/null
+++ b/users/j4m3s/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+j4m3s
diff --git a/users/j4m3s/keys.nix b/users/j4m3s/keys.nix
new file mode 100644
index 0000000000..e5aaa30737
--- /dev/null
+++ b/users/j4m3s/keys.nix
@@ -0,0 +1,7 @@
+{ ... }:
+
+{
+  all = [
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH77KDNivaadAX0byGQgmel9hA7VcmFnL+IKYHgBVNVp tvl"
+  ];
+}
diff --git a/users/lukegb/OWNERS b/users/lukegb/OWNERS
index 676fbf1856..4ff54b467e 100644
--- a/users/lukegb/OWNERS
+++ b/users/lukegb/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - lukegb
+set noparent
+
+lukegb
diff --git a/users/lukegb/keys.nix b/users/lukegb/keys.nix
index e54009122f..4745df550c 100644
--- a/users/lukegb/keys.nix
+++ b/users/lukegb/keys.nix
@@ -3,8 +3,9 @@
 
 rec {
   termius = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINytpHct7PLdLNp6MoaOPP7ccBPUQKymVNMqix//Wt1f";
-  porcorosso-wsl = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMhQ3yjf59eQjOfVXzXz5u8BS5c6hdL1yY8GqccaIjx3";
   porcorosso-nixos = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILid+1rq3k3k7Kbaw8X63vrPrQdanH55TucQwp3ZWfo+";
   clouvider-lon01-nix = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINQU7Y+Ha5m0ebwUjA55xXT/xbWZAWx1fVNFufle+vQj";
-  all = [ termius porcorosso-wsl porcorosso-nixos clouvider-lon01-nix ];
+  lukegb-build = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICESF0H+OCxY/CfyG9VjM6iJe+VbYc4NmGjRrwPCHaD9";
+  lukegb-ca = "cert-authority,principals=\"lukegb\" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEqNOwlR7Qa8cbGpDfSCOweDPbAGQOZIcoRgh6s/J8DR";
+  all = [ termius porcorosso-nixos clouvider-lon01-nix lukegb-build lukegb-ca ];
 }
diff --git a/users/padraic-o-mhuiris/OWNERS b/users/padraic-o-mhuiris/OWNERS
new file mode 100644
index 0000000000..ee6715b160
--- /dev/null
+++ b/users/padraic-o-mhuiris/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+padraic-o-mhuiris
diff --git a/users/picnoir/tvix-daemon/.gitignore b/users/picnoir/tvix-daemon/.gitignore
new file mode 100644
index 0000000000..ea8c4bf7f3
--- /dev/null
+++ b/users/picnoir/tvix-daemon/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/users/picnoir/tvix-daemon/Cargo.lock b/users/picnoir/tvix-daemon/Cargo.lock
new file mode 100644
index 0000000000..683203f5ca
--- /dev/null
+++ b/users/picnoir/tvix-daemon/Cargo.lock
@@ -0,0 +1,1541 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "anstream"
+version = "0.6.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
+dependencies = [
+ "anstyle",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "axum"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64ct"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bstr"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
+dependencies = [
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cc"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "clap"
+version = "4.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "platforms",
+ "rustc_version",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
+
+[[package]]
+name = "der"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "document-features"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "enum-primitive-derive"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c"
+dependencies = [
+ "num-traits",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+
+[[package]]
+name = "futures-task"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+
+[[package]]
+name = "futures-util"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+dependencies = [
+ "futures-core",
+ "futures-macro",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+
+[[package]]
+name = "glob"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+
+[[package]]
+name = "h2"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "http"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
+[[package]]
+name = "memchr"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "nix-compat"
+version = "0.1.0"
+dependencies = [
+ "bitflags 2.4.2",
+ "bstr",
+ "data-encoding",
+ "ed25519",
+ "ed25519-dalek",
+ "enum-primitive-derive",
+ "glob",
+ "nom",
+ "num-traits",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "pin-project"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "platforms"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+
+[[package]]
+name = "serde"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
+
+[[package]]
+name = "socket2"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
+
+[[package]]
+name = "subtle"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
+
+[[package]]
+name = "syn"
+version = "2.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "thiserror"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.58"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "tokio"
+version = "1.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "num_cpus",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "tokio-listener"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96367e127b4cf47b92592a5154a563435fe28fe3fccf25917d4a34ee59c87303"
+dependencies = [
+ "axum",
+ "document-features",
+ "futures-core",
+ "futures-util",
+ "nix",
+ "pin-project",
+ "socket2",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-test"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7"
+dependencies = [
+ "async-stream",
+ "bytes",
+ "futures-core",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
+dependencies = [
+ "nu-ansi-term",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "tvix-daemon"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "nix-compat",
+ "tokio",
+ "tokio-listener",
+ "tokio-test",
+ "tracing",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+
+[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.4",
+ "windows_aarch64_msvc 0.52.4",
+ "windows_i686_gnu 0.52.4",
+ "windows_i686_msvc 0.52.4",
+ "windows_x86_64_gnu 0.52.4",
+ "windows_x86_64_gnullvm 0.52.4",
+ "windows_x86_64_msvc 0.52.4",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
+
+[[package]]
+name = "zeroize"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
diff --git a/users/picnoir/tvix-daemon/Cargo.nix b/users/picnoir/tvix-daemon/Cargo.nix
new file mode 100644
index 0000000000..2382027f9b
--- /dev/null
+++ b/users/picnoir/tvix-daemon/Cargo.nix
@@ -0,0 +1,5754 @@
+# This file was @generated by crate2nix 0.13.0 with the command:
+#   "generate" "--all-features"
+# See https://github.com/kolloch/crate2nix for more info.
+
+{ nixpkgs ? <nixpkgs>
+, pkgs ? import nixpkgs { config = { }; }
+, lib ? pkgs.lib
+, stdenv ? pkgs.stdenv
+, buildRustCrateForPkgs ? pkgs: pkgs.buildRustCrate
+  # This is used as the `crateOverrides` argument for `buildRustCrate`.
+, defaultCrateOverrides ? pkgs.defaultCrateOverrides
+  # The features to enable for the root_crate or the workspace_members.
+, rootFeatures ? [ "default" ]
+  # If true, throw errors instead of issueing deprecation warnings.
+, strictDeprecation ? false
+  # Used for conditional compilation based on CPU feature detection.
+, targetFeatures ? [ ]
+  # Whether to perform release builds: longer compile times, faster binaries.
+, release ? true
+  # Additional crate2nix configuration if it exists.
+, crateConfig ? if builtins.pathExists ./crate-config.nix
+  then pkgs.callPackage ./crate-config.nix { }
+  else { }
+}:
+
+rec {
+  #
+  # "public" attributes that we attempt to keep stable with new versions of crate2nix.
+  #
+
+  rootCrate = rec {
+    packageId = "tvix-daemon";
+
+    # Use this attribute to refer to the derivation building your root crate package.
+    # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }.
+    build = internal.buildRustCrateWithFeatures {
+      inherit packageId;
+    };
+
+    # Debug support which might change between releases.
+    # File a bug if you depend on any for non-debug work!
+    debug = internal.debugCrate { inherit packageId; };
+  };
+  # Refer your crate build derivation by name here.
+  # You can override the features with
+  # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }.
+  workspaceMembers = {
+    "tvix-daemon" = rec {
+      packageId = "tvix-daemon";
+      build = internal.buildRustCrateWithFeatures {
+        packageId = "tvix-daemon";
+      };
+
+      # Debug support which might change between releases.
+      # File a bug if you depend on any for non-debug work!
+      debug = internal.debugCrate { inherit packageId; };
+    };
+  };
+
+  # A derivation that joins the outputs of all workspace members together.
+  allWorkspaceMembers = pkgs.symlinkJoin {
+    name = "all-workspace-members";
+    paths =
+      let members = builtins.attrValues workspaceMembers;
+      in builtins.map (m: m.build) members;
+  };
+
+  #
+  # "internal" ("private") attributes that may change in every new version of crate2nix.
+  #
+
+  internal = rec {
+    # Build and dependency information for crates.
+    # Many of the fields are passed one-to-one to buildRustCrate.
+    #
+    # Noteworthy:
+    # * `dependencies`/`buildDependencies`: similar to the corresponding fields for buildRustCrate.
+    #   but with additional information which is used during dependency/feature resolution.
+    # * `resolvedDependencies`: the selected default features reported by cargo - only included for debugging.
+    # * `devDependencies` as of now not used by `buildRustCrate` but used to
+    #   inject test dependencies into the build
+
+    crates = {
+      "addr2line" = rec {
+        crateName = "addr2line";
+        version = "0.21.0";
+        edition = "2018";
+        sha256 = "1jx0k3iwyqr8klqbzk6kjvr496yd94aspis10vwsj5wy7gib4c4a";
+        dependencies = [
+          {
+            name = "gimli";
+            packageId = "gimli";
+            usesDefaultFeatures = false;
+            features = [ "read" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "rustc-demangle" "cpp_demangle" "std-object" "fallible-iterator" "smallvec" "memmap2" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "memmap2" = [ "dep:memmap2" ];
+          "object" = [ "dep:object" ];
+          "rustc-demangle" = [ "dep:rustc-demangle" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "gimli/rustc-dep-of-std" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "gimli/std" ];
+          "std-object" = [ "std" "object" "object/std" "object/compression" "gimli/endian-reader" ];
+        };
+      };
+      "adler" = rec {
+        crateName = "adler";
+        version = "1.0.2";
+        edition = "2015";
+        sha256 = "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj";
+        authors = [
+          "Jonas Schievink <jonasschievink@gmail.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "anstream" = rec {
+        crateName = "anstream";
+        version = "0.6.13";
+        edition = "2021";
+        sha256 = "1yv2idkyf9mp9xwc684v0ywqiy86lwc9gvllwdishl7y6czx0syr";
+        dependencies = [
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "anstyle-parse";
+            packageId = "anstyle-parse";
+          }
+          {
+            name = "anstyle-query";
+            packageId = "anstyle-query";
+            optional = true;
+          }
+          {
+            name = "anstyle-wincon";
+            packageId = "anstyle-wincon";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+          {
+            name = "colorchoice";
+            packageId = "colorchoice";
+          }
+          {
+            name = "utf8parse";
+            packageId = "utf8parse";
+          }
+        ];
+        features = {
+          "auto" = [ "dep:anstyle-query" ];
+          "default" = [ "auto" "wincon" ];
+          "wincon" = [ "dep:anstyle-wincon" ];
+        };
+        resolvedDefaultFeatures = [ "auto" "default" "wincon" ];
+      };
+      "anstyle" = rec {
+        crateName = "anstyle";
+        version = "1.0.6";
+        edition = "2021";
+        sha256 = "1g1ngvxrz9d6xsymxzzzg581jzyz1sn8d0jpjcwxks07cff2c0c9";
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "anstyle-parse" = rec {
+        crateName = "anstyle-parse";
+        version = "0.2.3";
+        edition = "2021";
+        sha256 = "134jhzrz89labrdwxxnjxqjdg06qvaflj1wkfnmyapwyldfwcnn7";
+        dependencies = [
+          {
+            name = "utf8parse";
+            packageId = "utf8parse";
+            optional = true;
+          }
+        ];
+        features = {
+          "core" = [ "dep:arrayvec" ];
+          "default" = [ "utf8" ];
+          "utf8" = [ "dep:utf8parse" ];
+        };
+        resolvedDefaultFeatures = [ "default" "utf8" ];
+      };
+      "anstyle-query" = rec {
+        crateName = "anstyle-query";
+        version = "1.0.2";
+        edition = "2021";
+        sha256 = "0j3na4b1nma39g4x7cwvj009awxckjf3z2vkwhldgka44hqj72g2";
+        dependencies = [
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_System_Console" "Win32_Foundation" ];
+          }
+        ];
+
+      };
+      "anstyle-wincon" = rec {
+        crateName = "anstyle-wincon";
+        version = "3.0.2";
+        edition = "2021";
+        sha256 = "19v0fv400bmp4niqpzxnhg83vz12mmqv7l2l8vi80qcdxj0lpm8w";
+        dependencies = [
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_System_Console" "Win32_Foundation" ];
+          }
+        ];
+
+      };
+      "async-stream" = rec {
+        crateName = "async-stream";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "0l8sjq1rylkb1ak0pdyjn83b3k6x36j22myngl4sqqgg7whdsmnd";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream-impl";
+            packageId = "async-stream-impl";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "async-stream-impl" = rec {
+        crateName = "async-stream-impl";
+        version = "0.3.5";
+        edition = "2018";
+        sha256 = "14q179j4y8p2z1d0ic6aqgy9fhwz8p9cai1ia8kpw4bw7q12mrhn";
+        procMacro = true;
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "async-trait" = rec {
+        crateName = "async-trait";
+        version = "0.1.77";
+        edition = "2021";
+        sha256 = "1adf1jh2yg39rkpmqjqyr9xyd6849p0d95425i6imgbhx0syx069";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "autocfg" = rec {
+        crateName = "autocfg";
+        version = "1.1.0";
+        edition = "2015";
+        sha256 = "1ylp3cb47ylzabimazvbz9ms6ap784zhb6syaz6c1jqpmcmq0s6l";
+        authors = [
+          "Josh Stone <cuviper@gmail.com>"
+        ];
+
+      };
+      "axum" = rec {
+        crateName = "axum";
+        version = "0.7.4";
+        edition = "2021";
+        sha256 = "17kv7v8m981cqmfbv5m538fzxhw51l9bajv06kfddi7njarb8dhj";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "axum-core";
+            packageId = "axum-core";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "http-body-util";
+            packageId = "http-body-util";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            optional = true;
+          }
+          {
+            name = "hyper-util";
+            packageId = "hyper-util";
+            optional = true;
+            features = [ "tokio" "server" "server-auto" ];
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "matchit";
+            packageId = "matchit";
+          }
+          {
+            name = "memchr";
+            packageId = "memchr";
+          }
+          {
+            name = "mime";
+            packageId = "mime";
+          }
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+            optional = true;
+            features = [ "raw_value" ];
+          }
+          {
+            name = "serde_path_to_error";
+            packageId = "serde_path_to_error";
+            optional = true;
+          }
+          {
+            name = "serde_urlencoded";
+            packageId = "serde_urlencoded";
+            optional = true;
+          }
+          {
+            name = "sync_wrapper";
+            packageId = "sync_wrapper";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            rename = "tokio";
+            optional = true;
+            features = [ "time" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            usesDefaultFeatures = false;
+            features = [ "util" ];
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            rename = "tokio";
+            features = [ "macros" "rt" "rt-multi-thread" "net" "test-util" ];
+          }
+          {
+            name = "tower";
+            packageId = "tower";
+            rename = "tower";
+            features = [ "util" "timeout" "limit" "load-shed" "steer" "filter" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+        ];
+        features = {
+          "__private_docs" = [ "tower/full" "dep:tower-http" ];
+          "default" = [ "form" "http1" "json" "matched-path" "original-uri" "query" "tokio" "tower-log" "tracing" ];
+          "form" = [ "dep:serde_urlencoded" ];
+          "http1" = [ "dep:hyper" "hyper?/http1" ];
+          "http2" = [ "dep:hyper" "hyper?/http2" ];
+          "json" = [ "dep:serde_json" "dep:serde_path_to_error" ];
+          "macros" = [ "dep:axum-macros" ];
+          "multipart" = [ "dep:multer" ];
+          "query" = [ "dep:serde_urlencoded" ];
+          "tokio" = [ "dep:hyper-util" "dep:tokio" "tokio/net" "tokio/rt" "tower/make" "tokio/macros" ];
+          "tower-log" = [ "tower/log" ];
+          "tracing" = [ "dep:tracing" "axum-core/tracing" ];
+          "ws" = [ "dep:hyper" "tokio" "dep:tokio-tungstenite" "dep:sha1" "dep:base64" ];
+        };
+        resolvedDefaultFeatures = [ "default" "form" "http1" "json" "matched-path" "original-uri" "query" "tokio" "tower-log" "tracing" ];
+      };
+      "axum-core" = rec {
+        crateName = "axum-core";
+        version = "0.4.3";
+        edition = "2021";
+        sha256 = "1qx28wg4j6qdcdrisqwyaavlzc0zvbsrcwa99zf9456lfbyn6p51";
+        dependencies = [
+          {
+            name = "async-trait";
+            packageId = "async-trait";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "http-body-util";
+            packageId = "http-body-util";
+          }
+          {
+            name = "mime";
+            packageId = "mime";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "sync_wrapper";
+            packageId = "sync_wrapper";
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "rustversion";
+            packageId = "rustversion";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+        ];
+        features = {
+          "__private_docs" = [ "dep:tower-http" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "tracing" ];
+      };
+      "backtrace" = rec {
+        crateName = "backtrace";
+        version = "0.3.69";
+        edition = "2018";
+        sha256 = "0dsq23dhw4pfndkx2nsa1ml2g31idm7ss7ljxp8d57avygivg290";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "addr2line";
+            packageId = "addr2line";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "miniz_oxide";
+            packageId = "miniz_oxide";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+          }
+          {
+            name = "object";
+            packageId = "object";
+            usesDefaultFeatures = false;
+            target = { target, features }: (!((target."windows" or false) && ("msvc" == target."env" or null) && (!("uwp" == target."vendor" or null))));
+            features = [ "read_core" "elf" "macho" "pe" "unaligned" "archive" ];
+          }
+          {
+            name = "rustc-demangle";
+            packageId = "rustc-demangle";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "cc";
+            packageId = "cc";
+          }
+        ];
+        features = {
+          "cpp_demangle" = [ "dep:cpp_demangle" ];
+          "default" = [ "std" ];
+          "rustc-serialize" = [ "dep:rustc-serialize" ];
+          "serde" = [ "dep:serde" ];
+          "serialize-rustc" = [ "rustc-serialize" ];
+          "serialize-serde" = [ "serde" ];
+          "verify-winapi" = [ "winapi/dbghelp" "winapi/handleapi" "winapi/libloaderapi" "winapi/memoryapi" "winapi/minwindef" "winapi/processthreadsapi" "winapi/synchapi" "winapi/tlhelp32" "winapi/winbase" "winapi/winnt" ];
+          "winapi" = [ "dep:winapi" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "base64ct" = rec {
+        crateName = "base64ct";
+        version = "1.6.0";
+        edition = "2021";
+        sha256 = "0nvdba4jb8aikv60az40x2w1y96sjdq8z3yp09rwzmkhiwv1lg4c";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+      "bitflags 1.3.2" = rec {
+        crateName = "bitflags";
+        version = "1.3.2";
+        edition = "2018";
+        sha256 = "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "bitflags 2.4.2" = rec {
+        crateName = "bitflags";
+        version = "2.4.2";
+        edition = "2021";
+        sha256 = "1pqd142hyqlzr7p9djxq2ff0jx07a2sb2xp9lhw69cbf80s0jmzd";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "bytemuck" = [ "dep:bytemuck" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "block-buffer" = rec {
+        crateName = "block-buffer";
+        version = "0.10.4";
+        edition = "2018";
+        sha256 = "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+          }
+        ];
+
+      };
+      "bstr" = rec {
+        crateName = "bstr";
+        version = "1.9.1";
+        edition = "2021";
+        sha256 = "01ipr5rncw3kf4dyc1p2g00njn1df2b0xpviwhb8830iv77wbvq5";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "regex-automata";
+            packageId = "regex-automata";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "dfa-search" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "memchr/alloc" "serde?/alloc" ];
+          "default" = [ "std" "unicode" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" "memchr/std" "serde?/std" ];
+          "unicode" = [ "dep:regex-automata" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "serde" "std" "unicode" ];
+      };
+      "bytes" = rec {
+        crateName = "bytes";
+        version = "1.5.0";
+        edition = "2018";
+        sha256 = "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "cc" = rec {
+        crateName = "cc";
+        version = "1.0.90";
+        edition = "2018";
+        sha256 = "1xg1bqnq50dpf6g1hl90caxgz4afnf74pxa426gh7wxch9561mlc";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "jobserver" = [ "dep:jobserver" ];
+          "libc" = [ "dep:libc" ];
+          "parallel" = [ "libc" "jobserver" ];
+        };
+      };
+      "cfg-if" = rec {
+        crateName = "cfg-if";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "clap" = rec {
+        crateName = "clap";
+        version = "4.5.3";
+        edition = "2021";
+        crateBin = [ ];
+        sha256 = "04w8fx68hzjzk45ir4b9jzwk4m7bki0k5afwns9zqgh61v82d5ll";
+        dependencies = [
+          {
+            name = "clap_builder";
+            packageId = "clap_builder";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "clap_derive";
+            packageId = "clap_derive";
+            optional = true;
+          }
+        ];
+        features = {
+          "cargo" = [ "clap_builder/cargo" ];
+          "color" = [ "clap_builder/color" ];
+          "debug" = [ "clap_builder/debug" "clap_derive?/debug" ];
+          "default" = [ "std" "color" "help" "usage" "error-context" "suggestions" ];
+          "deprecated" = [ "clap_builder/deprecated" "clap_derive?/deprecated" ];
+          "derive" = [ "dep:clap_derive" ];
+          "env" = [ "clap_builder/env" ];
+          "error-context" = [ "clap_builder/error-context" ];
+          "help" = [ "clap_builder/help" ];
+          "std" = [ "clap_builder/std" ];
+          "string" = [ "clap_builder/string" ];
+          "suggestions" = [ "clap_builder/suggestions" ];
+          "unicode" = [ "clap_builder/unicode" ];
+          "unstable-doc" = [ "clap_builder/unstable-doc" "derive" ];
+          "unstable-styles" = [ "clap_builder/unstable-styles" ];
+          "unstable-v5" = [ "clap_builder/unstable-v5" "clap_derive?/unstable-v5" "deprecated" ];
+          "usage" = [ "clap_builder/usage" ];
+          "wrap_help" = [ "clap_builder/wrap_help" ];
+        };
+        resolvedDefaultFeatures = [ "color" "default" "derive" "env" "error-context" "help" "std" "suggestions" "usage" ];
+      };
+      "clap_builder" = rec {
+        crateName = "clap_builder";
+        version = "4.5.2";
+        edition = "2021";
+        sha256 = "1d7p4hph4fyhaphkf0v5zv0kq4lz25a9jq2f901yrq3afqp9w4mf";
+        dependencies = [
+          {
+            name = "anstream";
+            packageId = "anstream";
+            optional = true;
+          }
+          {
+            name = "anstyle";
+            packageId = "anstyle";
+          }
+          {
+            name = "clap_lex";
+            packageId = "clap_lex";
+          }
+          {
+            name = "strsim";
+            packageId = "strsim";
+            optional = true;
+          }
+        ];
+        features = {
+          "color" = [ "dep:anstream" ];
+          "debug" = [ "dep:backtrace" ];
+          "default" = [ "std" "color" "help" "usage" "error-context" "suggestions" ];
+          "std" = [ "anstyle/std" ];
+          "suggestions" = [ "dep:strsim" "error-context" ];
+          "unicode" = [ "dep:unicode-width" "dep:unicase" ];
+          "unstable-doc" = [ "cargo" "wrap_help" "env" "unicode" "string" ];
+          "unstable-styles" = [ "color" ];
+          "unstable-v5" = [ "deprecated" ];
+          "wrap_help" = [ "help" "dep:terminal_size" ];
+        };
+        resolvedDefaultFeatures = [ "color" "env" "error-context" "help" "std" "suggestions" "usage" ];
+      };
+      "clap_derive" = rec {
+        crateName = "clap_derive";
+        version = "4.5.3";
+        edition = "2021";
+        sha256 = "0byp6k5kyvi9jcbnjjbyw7ak7avn87f2s4ya154f3xc01h29l8wh";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "heck";
+            packageId = "heck";
+          }
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "raw-deprecated" = [ "deprecated" ];
+          "unstable-v5" = [ "deprecated" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "clap_lex" = rec {
+        crateName = "clap_lex";
+        version = "0.7.0";
+        edition = "2021";
+        sha256 = "1kh1sckgq71kay2rrr149pl9gbsrvyccsq6xm5xpnq0cxnyqzk4q";
+
+      };
+      "colorchoice" = rec {
+        crateName = "colorchoice";
+        version = "1.0.0";
+        edition = "2021";
+        sha256 = "1ix7w85kwvyybwi2jdkl3yva2r2bvdcc3ka2grjfzfgrapqimgxc";
+
+      };
+      "const-oid" = rec {
+        crateName = "const-oid";
+        version = "0.9.6";
+        edition = "2021";
+        sha256 = "1y0jnqaq7p2wvspnx7qj76m7hjcqpz73qzvr9l2p9n2s51vr6if2";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+        };
+      };
+      "cpufeatures" = rec {
+        crateName = "cpufeatures";
+        version = "0.2.12";
+        edition = "2018";
+        sha256 = "012m7rrak4girqlii3jnqwrr73gv1i980q4wra5yyyhvzwk5xzjk";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-linux-android");
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("apple" == target."vendor" or null));
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (("loongarch64" == target."arch" or null) && ("linux" == target."os" or null));
+          }
+        ];
+
+      };
+      "crypto-common" = rec {
+        crateName = "crypto-common";
+        version = "0.1.6";
+        edition = "2018";
+        sha256 = "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "generic-array";
+            packageId = "generic-array";
+            features = [ "more_lengths" ];
+          }
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        features = {
+          "getrandom" = [ "rand_core/getrandom" ];
+          "rand_core" = [ "dep:rand_core" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "curve25519-dalek" = rec {
+        crateName = "curve25519-dalek";
+        version = "4.1.2";
+        edition = "2021";
+        sha256 = "0j7kqchcgycs4a11gvlda93h9w2jr05nn4hjpfyh2kn94a4pnrqa";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: ("x86_64" == target."arch" or null);
+          }
+          {
+            name = "curve25519-dalek-derive";
+            packageId = "curve25519-dalek-derive";
+            target = { target, features }: ((!("fiat" == target."curve25519_dalek_backend" or null)) && (!("serial" == target."curve25519_dalek_backend" or null)) && ("x86_64" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "fiat-crypto";
+            packageId = "fiat-crypto";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("fiat" == target."curve25519_dalek_backend" or null);
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "platforms";
+            packageId = "platforms";
+          }
+          {
+            name = "rustc_version";
+            packageId = "rustc_version";
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "default" = [ "alloc" "precomputed-tables" "zeroize" ];
+          "digest" = [ "dep:digest" ];
+          "ff" = [ "dep:ff" ];
+          "group" = [ "dep:group" "rand_core" ];
+          "group-bits" = [ "group" "ff/bits" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "digest" "precomputed-tables" "zeroize" ];
+      };
+      "curve25519-dalek-derive" = rec {
+        crateName = "curve25519-dalek-derive";
+        version = "0.1.1";
+        edition = "2021";
+        sha256 = "1cry71xxrr0mcy5my3fb502cwfxy6822k4pm19cwrilrg7hq4s7l";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "data-encoding" = rec {
+        crateName = "data-encoding";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "1rcbnwfmfxhlshzbn3r7srm3azqha3mn33yxyqxkzz2wpqcjm5ky";
+        authors = [
+          "Julien Cretin <git@ia0.eu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "der" = rec {
+        crateName = "der";
+        version = "0.7.8";
+        edition = "2021";
+        sha256 = "070bwiyr80800h31c5zd96ckkgagfjgnrrdmz3dzg2lccsd3dypz";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "const-oid";
+            packageId = "const-oid";
+            optional = true;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "zeroize?/alloc" ];
+          "arbitrary" = [ "dep:arbitrary" "const-oid?/arbitrary" "std" ];
+          "bytes" = [ "dep:bytes" "alloc" ];
+          "derive" = [ "dep:der_derive" ];
+          "flagset" = [ "dep:flagset" ];
+          "oid" = [ "dep:const-oid" ];
+          "pem" = [ "dep:pem-rfc7468" "alloc" "zeroize" ];
+          "std" = [ "alloc" ];
+          "time" = [ "dep:time" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "oid" "std" "zeroize" ];
+      };
+      "digest" = rec {
+        crateName = "digest";
+        version = "0.10.7";
+        edition = "2018";
+        sha256 = "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "block-buffer";
+            packageId = "block-buffer";
+            optional = true;
+          }
+          {
+            name = "crypto-common";
+            packageId = "crypto-common";
+          }
+        ];
+        features = {
+          "blobby" = [ "dep:blobby" ];
+          "block-buffer" = [ "dep:block-buffer" ];
+          "const-oid" = [ "dep:const-oid" ];
+          "core-api" = [ "block-buffer" ];
+          "default" = [ "core-api" ];
+          "dev" = [ "blobby" ];
+          "mac" = [ "subtle" ];
+          "oid" = [ "const-oid" ];
+          "rand_core" = [ "crypto-common/rand_core" ];
+          "std" = [ "alloc" "crypto-common/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "block-buffer" "core-api" "default" "std" ];
+      };
+      "document-features" = rec {
+        crateName = "document-features";
+        version = "0.2.8";
+        edition = "2018";
+        sha256 = "15cvgxqngxslgllz15m8aban6wqfgsi6nlhr0g25yfsnd6nq4lpg";
+        procMacro = true;
+        libPath = "lib.rs";
+        authors = [
+          "Slint Developers <info@slint-ui.com>"
+        ];
+        dependencies = [
+          {
+            name = "litrs";
+            packageId = "litrs";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "ed25519" = rec {
+        crateName = "ed25519";
+        version = "2.2.3";
+        edition = "2021";
+        sha256 = "0lydzdf26zbn82g7xfczcac9d7mzm3qgx934ijjrd5hjpjx32m8i";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "pkcs8";
+            packageId = "pkcs8";
+            optional = true;
+          }
+          {
+            name = "signature";
+            packageId = "signature";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "pkcs8?/alloc" ];
+          "default" = [ "std" ];
+          "pem" = [ "alloc" "pkcs8/pem" ];
+          "pkcs8" = [ "dep:pkcs8" ];
+          "serde" = [ "dep:serde" ];
+          "serde_bytes" = [ "serde" "dep:serde_bytes" ];
+          "std" = [ "pkcs8?/std" "signature/std" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "ed25519-dalek" = rec {
+        crateName = "ed25519-dalek";
+        version = "2.1.1";
+        edition = "2021";
+        sha256 = "0w88cafwglg9hjizldbmlza0ns3hls81zk1bcih3m5m3h67algaa";
+        authors = [
+          "isis lovecruft <isis@patternsinthevoid.net>"
+          "Tony Arcieri <bascule@gmail.com>"
+          "Michael Rosenberg <michael@mrosenberg.pub>"
+        ];
+        dependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" ];
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "subtle";
+            packageId = "subtle";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "zeroize";
+            packageId = "zeroize";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "curve25519-dalek";
+            packageId = "curve25519-dalek";
+            usesDefaultFeatures = false;
+            features = [ "digest" "rand_core" ];
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "curve25519-dalek/alloc" "ed25519/alloc" "serde?/alloc" "zeroize/alloc" ];
+          "asm" = [ "sha2/asm" ];
+          "batch" = [ "alloc" "merlin" "rand_core" ];
+          "default" = [ "fast" "std" "zeroize" ];
+          "digest" = [ "signature/digest" ];
+          "fast" = [ "curve25519-dalek/precomputed-tables" ];
+          "legacy_compatibility" = [ "curve25519-dalek/legacy_compatibility" ];
+          "merlin" = [ "dep:merlin" ];
+          "pem" = [ "alloc" "ed25519/pem" "pkcs8" ];
+          "pkcs8" = [ "ed25519/pkcs8" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "serde" = [ "dep:serde" "ed25519/serde" ];
+          "signature" = [ "dep:signature" ];
+          "std" = [ "alloc" "ed25519/std" "serde?/std" "sha2/std" ];
+          "zeroize" = [ "dep:zeroize" "curve25519-dalek/zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "fast" "std" "zeroize" ];
+      };
+      "enum-primitive-derive" = rec {
+        crateName = "enum-primitive-derive";
+        version = "0.3.0";
+        edition = "2018";
+        sha256 = "0k6wcf58h5kh64yq5nfq71va53kaya0kzxwsjwbgwm2n2zd9axxs";
+        procMacro = true;
+        authors = [
+          "Doug Goldstein <cardoe@cardoe.com>"
+        ];
+        dependencies = [
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+          }
+        ];
+
+      };
+      "equivalent" = rec {
+        crateName = "equivalent";
+        version = "1.0.1";
+        edition = "2015";
+        sha256 = "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl";
+
+      };
+      "fiat-crypto" = rec {
+        crateName = "fiat-crypto";
+        version = "0.2.6";
+        edition = "2018";
+        sha256 = "10hkkkjynhibvchznkxx81gwxqarn9i5sgz40d6xxb8xzhsz8xhn";
+        authors = [
+          "Fiat Crypto library authors <jgross@mit.edu>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+      };
+      "fnv" = rec {
+        crateName = "fnv";
+        version = "1.0.7";
+        edition = "2015";
+        sha256 = "1hc2mcqha06aibcaza94vbi81j6pr9a1bbxrxjfhc91zin8yr7iz";
+        libPath = "lib.rs";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "form_urlencoded" = rec {
+        crateName = "form_urlencoded";
+        version = "1.2.1";
+        edition = "2018";
+        sha256 = "0milh8x7nl4f450s3ddhg57a3flcv6yq8hlkyk6fyr3mcb128dp1";
+        authors = [
+          "The rust-url developers"
+        ];
+        dependencies = [
+          {
+            name = "percent-encoding";
+            packageId = "percent-encoding";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "percent-encoding/alloc" ];
+          "default" = [ "std" ];
+          "std" = [ "alloc" "percent-encoding/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-channel" = rec {
+        crateName = "futures-channel";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0y6b7xxqdjm9hlcjpakcg41qfl7lihf6gavk8fyqijsxhvbzgj7a";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" ];
+          "default" = [ "std" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "sink" = [ "futures-sink" ];
+          "std" = [ "alloc" "futures-core/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-core" = rec {
+        crateName = "futures-core";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "07aslayrn3lbggj54kci0ishmd1pr367fp7iks7adia1p05miinz";
+        features = {
+          "default" = [ "std" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-macro" = rec {
+        crateName = "futures-macro";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1b49qh9d402y8nka4q6wvvj0c88qq91wbr192mdn5h54nzs0qxc7";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "futures-sink" = rec {
+        crateName = "futures-sink";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "1dag8xyyaya8n8mh8smx7x6w2dpmafg2din145v973a3hw7f1f4z";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "futures-task" = rec {
+        crateName = "futures-task";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "013h1724454hj8qczp8vvs10qfiqrxr937qsrv6rhii68ahlzn1q";
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "futures-util" = rec {
+        crateName = "futures-util";
+        version = "0.3.30";
+        edition = "2018";
+        sha256 = "0j0xqhcir1zf2dcbpd421kgw6wvsk0rpxflylcysn1rlp3g02r1x";
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-macro";
+            packageId = "futures-macro";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-task";
+            packageId = "futures-task";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "pin-utils";
+            packageId = "pin-utils";
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+            optional = true;
+          }
+        ];
+        features = {
+          "alloc" = [ "futures-core/alloc" "futures-task/alloc" ];
+          "async-await-macro" = [ "async-await" "futures-macro" ];
+          "channel" = [ "std" "futures-channel" ];
+          "compat" = [ "std" "futures_01" ];
+          "default" = [ "std" "async-await" "async-await-macro" ];
+          "futures-channel" = [ "dep:futures-channel" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-macro" = [ "dep:futures-macro" ];
+          "futures-sink" = [ "dep:futures-sink" ];
+          "futures_01" = [ "dep:futures_01" ];
+          "io" = [ "std" "futures-io" "memchr" ];
+          "io-compat" = [ "io" "compat" "tokio-io" ];
+          "memchr" = [ "dep:memchr" ];
+          "portable-atomic" = [ "futures-core/portable-atomic" ];
+          "sink" = [ "futures-sink" ];
+          "slab" = [ "dep:slab" ];
+          "std" = [ "alloc" "futures-core/std" "futures-task/std" "slab" ];
+          "tokio-io" = [ "dep:tokio-io" ];
+          "unstable" = [ "futures-core/unstable" "futures-task/unstable" ];
+          "write-all-vectored" = [ "io" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "async-await" "async-await-macro" "default" "futures-macro" "slab" "std" ];
+      };
+      "generic-array" = rec {
+        crateName = "generic-array";
+        version = "0.14.7";
+        edition = "2015";
+        sha256 = "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45";
+        libName = "generic_array";
+        authors = [
+          "Bartล‚omiej Kamiล„ski <fizyk20@gmail.com>"
+          "Aaron Trent <novacrazy@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "typenum";
+            packageId = "typenum";
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "version_check";
+            packageId = "version_check";
+          }
+        ];
+        features = {
+          "serde" = [ "dep:serde" ];
+          "zeroize" = [ "dep:zeroize" ];
+        };
+        resolvedDefaultFeatures = [ "more_lengths" ];
+      };
+      "getrandom" = rec {
+        crateName = "getrandom";
+        version = "0.2.12";
+        edition = "2018";
+        sha256 = "1d8jb9bv38nkwlqqdjcav6gxckgwc9g30pm3qq506rvncpm9400r";
+        authors = [
+          "The Rand Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            usesDefaultFeatures = false;
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "js" = [ "wasm-bindgen" "js-sys" ];
+          "js-sys" = [ "dep:js-sys" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "libc/rustc-dep-of-std" "wasi/rustc-dep-of-std" ];
+          "wasm-bindgen" = [ "dep:wasm-bindgen" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "gimli" = rec {
+        crateName = "gimli";
+        version = "0.28.1";
+        edition = "2018";
+        sha256 = "0lv23wc8rxvmjia3mcxc6hj9vkqnv1bqq0h8nzjcgf71mrxx6wa2";
+        features = {
+          "default" = [ "read-all" "write" ];
+          "endian-reader" = [ "read" "dep:stable_deref_trait" ];
+          "fallible-iterator" = [ "dep:fallible-iterator" ];
+          "read" = [ "read-core" ];
+          "read-all" = [ "read" "std" "fallible-iterator" "endian-reader" ];
+          "rustc-dep-of-std" = [ "dep:core" "dep:alloc" "dep:compiler_builtins" ];
+          "std" = [ "fallible-iterator?/std" "stable_deref_trait?/std" ];
+          "write" = [ "dep:indexmap" ];
+        };
+        resolvedDefaultFeatures = [ "read" "read-core" ];
+      };
+      "glob" = rec {
+        crateName = "glob";
+        version = "0.3.1";
+        edition = "2015";
+        sha256 = "16zca52nglanv23q5qrwd5jinw3d3as5ylya6y1pbx47vkxvrynj";
+        authors = [
+          "The Rust Project Developers"
+        ];
+
+      };
+      "h2" = rec {
+        crateName = "h2";
+        version = "0.4.2";
+        edition = "2018";
+        sha256 = "0hqr2l7kl9zqjcjdv69v9jx6v65mlbsavsyff8mr6lgqkbjk1l1i";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "indexmap";
+            packageId = "indexmap";
+            features = [ "std" ];
+          }
+          {
+            name = "slab";
+            packageId = "slab";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "io-util" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            features = [ "codec" "io" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt-multi-thread" "macros" "sync" "net" ];
+          }
+        ];
+        features = { };
+      };
+      "hashbrown" = rec {
+        crateName = "hashbrown";
+        version = "0.14.3";
+        edition = "2021";
+        sha256 = "012nywlg0lj9kwanh69my5x67vjlfmzfi9a0rq4qvis2j8fil3r9";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "alloc" = [ "dep:alloc" ];
+          "allocator-api2" = [ "dep:allocator-api2" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "ahash" "inline-more" "allocator-api2" ];
+          "equivalent" = [ "dep:equivalent" ];
+          "nightly" = [ "allocator-api2?/nightly" "bumpalo/allocator_api" ];
+          "rayon" = [ "dep:rayon" ];
+          "rkyv" = [ "dep:rkyv" ];
+          "rustc-dep-of-std" = [ "nightly" "core" "compiler_builtins" "alloc" "rustc-internal-api" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "raw" ];
+      };
+      "heck" = rec {
+        crateName = "heck";
+        version = "0.5.0";
+        edition = "2021";
+        sha256 = "1sjmpsdl8czyh9ywl3qcsfsq9a307dg4ni2vnlwgnzzqhc4y0113";
+
+      };
+      "hermit-abi" = rec {
+        crateName = "hermit-abi";
+        version = "0.3.9";
+        edition = "2021";
+        sha256 = "092hxjbjnq5fmz66grd9plxd0sh6ssg5fhgwwwqbrzgzkjwdycfj";
+        authors = [
+          "Stefan Lankes"
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins/rustc-dep-of-std" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "http" = rec {
+        crateName = "http";
+        version = "1.1.0";
+        edition = "2018";
+        sha256 = "0n426lmcxas6h75c2cp25m933pswlrfjz10v91vc62vib2sdvf91";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Carl Lerche <me@carllerche.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "fnv";
+            packageId = "fnv";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "http-body" = rec {
+        crateName = "http-body";
+        version = "1.0.0";
+        edition = "2018";
+        sha256 = "0hyn8n3iadrbwq8y0p1rl1275s4nm49bllw5wji29g4aa3dqbb0w";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+        ];
+
+      };
+      "http-body-util" = rec {
+        crateName = "http-body-util";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "07agldas2qgcfc05ckiarlmf9vzragbda823nqhrqrc6mjrghx84";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Lucio Franco <luciofranco14@gmail.com>"
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+        ];
+
+      };
+      "httparse" = rec {
+        crateName = "httparse";
+        version = "1.8.0";
+        edition = "2018";
+        sha256 = "010rrfahm1jss3p022fqf3j3jmm72vhn4iqhykahb9ynpaag75yq";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "httpdate" = rec {
+        crateName = "httpdate";
+        version = "1.0.3";
+        edition = "2021";
+        sha256 = "1aa9rd2sac0zhjqh24c9xvir96g188zldkx0hr6dnnlx5904cfyz";
+        authors = [
+          "Pyfisch <pyfisch@posteo.org>"
+        ];
+
+      };
+      "hyper" = rec {
+        crateName = "hyper";
+        version = "1.2.0";
+        edition = "2021";
+        sha256 = "0fi6k7hz5fmdph0a5r8hw50d7h2n9zxkizmafcmb65f67bblhr8q";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            optional = true;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "h2";
+            packageId = "h2";
+            optional = true;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "httparse";
+            packageId = "httparse";
+            optional = true;
+          }
+          {
+            name = "httpdate";
+            packageId = "httpdate";
+            optional = true;
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+            optional = true;
+            features = [ "const_generics" "const_new" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "futures-channel";
+            packageId = "futures-channel";
+            features = [ "sink" ];
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+            features = [ "sink" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "fs" "macros" "net" "io-std" "io-util" "rt" "rt-multi-thread" "sync" "time" "test-util" ];
+          }
+        ];
+        features = {
+          "client" = [ "dep:want" "dep:pin-project-lite" "dep:smallvec" ];
+          "ffi" = [ "dep:libc" "dep:http-body-util" ];
+          "full" = [ "client" "http1" "http2" "server" ];
+          "http1" = [ "dep:futures-channel" "dep:futures-util" "dep:httparse" "dep:itoa" ];
+          "http2" = [ "dep:futures-channel" "dep:futures-util" "dep:h2" ];
+          "server" = [ "dep:httpdate" "dep:pin-project-lite" "dep:smallvec" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "default" "http1" "http2" "server" ];
+      };
+      "hyper-util" = rec {
+        crateName = "hyper-util";
+        version = "0.1.3";
+        edition = "2021";
+        sha256 = "1akngan7j0n2n0wd25c6952mvqbkj9gp1lcwzyxjc0d37l8yyf6a";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "http";
+            packageId = "http";
+          }
+          {
+            name = "http-body";
+            packageId = "http-body";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            features = [ "all" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "net" "rt" "time" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "hyper";
+            packageId = "hyper";
+            features = [ "full" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "test-util" ];
+          }
+        ];
+        features = {
+          "client" = [ "hyper/client" "dep:tracing" "dep:futures-channel" "dep:tower" "dep:tower-service" ];
+          "client-legacy" = [ "client" ];
+          "full" = [ "client" "client-legacy" "server" "server-auto" "service" "http1" "http2" "tokio" ];
+          "http1" = [ "hyper/http1" ];
+          "http2" = [ "hyper/http2" ];
+          "server" = [ "hyper/server" ];
+          "server-auto" = [ "server" "http1" "http2" ];
+          "service" = [ "dep:tower" "dep:tower-service" ];
+          "tokio" = [ "dep:tokio" "dep:socket2" ];
+        };
+        resolvedDefaultFeatures = [ "default" "http1" "http2" "server" "server-auto" "tokio" ];
+      };
+      "indexmap" = rec {
+        crateName = "indexmap";
+        version = "2.2.5";
+        edition = "2021";
+        sha256 = "1x4x9zdqvlkfks3y84dsynh1p8na3nn48nn454s26rqla6fr42vv";
+        dependencies = [
+          {
+            name = "equivalent";
+            packageId = "equivalent";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "hashbrown";
+            packageId = "hashbrown";
+            usesDefaultFeatures = false;
+            features = [ "raw" ];
+          }
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "borsh" = [ "dep:borsh" ];
+          "default" = [ "std" ];
+          "quickcheck" = [ "dep:quickcheck" ];
+          "rayon" = [ "dep:rayon" ];
+          "rustc-rayon" = [ "dep:rustc-rayon" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "itoa" = rec {
+        crateName = "itoa";
+        version = "1.0.10";
+        edition = "2018";
+        sha256 = "0k7xjfki7mnv6yzjrbnbnjllg86acmbnk4izz2jmm1hx2wd6v95i";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "lazy_static" = rec {
+        crateName = "lazy_static";
+        version = "1.4.0";
+        edition = "2015";
+        sha256 = "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2";
+        authors = [
+          "Marvin Lรถbel <loebel.marvin@gmail.com>"
+        ];
+        features = {
+          "spin" = [ "dep:spin" ];
+          "spin_no_std" = [ "spin" ];
+        };
+      };
+      "libc" = rec {
+        crateName = "libc";
+        version = "0.2.153";
+        edition = "2015";
+        sha256 = "1gg7m1ils5dms5miq9fyllrcp0jxnbpgkx71chd2i0lafa8qy6cw";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "align" "rustc-std-workspace-core" ];
+          "rustc-std-workspace-core" = [ "dep:rustc-std-workspace-core" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "extra_traits" "std" ];
+      };
+      "litrs" = rec {
+        crateName = "litrs";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "19cssch9gc0x2snd9089nvwzz79zx6nzsi3icffpx25p4hck1kml";
+        authors = [
+          "Lukas Kalbertodt <lukas.kalbertodt@gmail.com>"
+        ];
+        features = {
+          "check_suffix" = [ "unicode-xid" ];
+          "default" = [ "proc-macro2" ];
+          "proc-macro2" = [ "dep:proc-macro2" ];
+          "unicode-xid" = [ "dep:unicode-xid" ];
+        };
+      };
+      "lock_api" = rec {
+        crateName = "lock_api";
+        version = "0.4.11";
+        edition = "2018";
+        sha256 = "0iggx0h4jx63xm35861106af3jkxq06fpqhpkhgw0axi2n38y5iw";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "scopeguard";
+            packageId = "scopeguard";
+            usesDefaultFeatures = false;
+          }
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "atomic_usize" ];
+          "owning_ref" = [ "dep:owning_ref" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "atomic_usize" "default" ];
+      };
+      "log" = rec {
+        crateName = "log";
+        version = "0.4.21";
+        edition = "2021";
+        sha256 = "074hldq1q8rlzq2s2qa8f25hj4s3gpw71w64vdwzjd01a4g8rvch";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        features = {
+          "kv_serde" = [ "kv_std" "value-bag/serde" "serde" ];
+          "kv_std" = [ "std" "kv" "value-bag/error" ];
+          "kv_sval" = [ "kv" "value-bag/sval" "sval" "sval_ref" ];
+          "kv_unstable" = [ "kv" "value-bag" ];
+          "kv_unstable_serde" = [ "kv_serde" "kv_unstable_std" ];
+          "kv_unstable_std" = [ "kv_std" "kv_unstable" ];
+          "kv_unstable_sval" = [ "kv_sval" "kv_unstable" ];
+          "serde" = [ "dep:serde" ];
+          "sval" = [ "dep:sval" ];
+          "sval_ref" = [ "dep:sval_ref" ];
+          "value-bag" = [ "dep:value-bag" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "matchit" = rec {
+        crateName = "matchit";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "156bgdmmlv4crib31qhgg49nsjk88dxkdqp80ha2pk2rk6n6ax0f";
+        authors = [
+          "Ibraheem Ahmed <ibraheem@ibraheem.ca>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "memchr" = rec {
+        crateName = "memchr";
+        version = "2.7.1";
+        edition = "2021";
+        sha256 = "0jf1kicqa4vs9lyzj4v4y1p90q0dh87hvhsdd5xvhnp527sw8gaj";
+        authors = [
+          "Andrew Gallant <jamslam@gmail.com>"
+          "bluss"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "logging" = [ "dep:log" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+          "std" = [ "alloc" ];
+          "use_std" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "mime" = rec {
+        crateName = "mime";
+        version = "0.3.17";
+        edition = "2015";
+        sha256 = "16hkibgvb9klh0w0jk5crr5xv90l3wlf77ggymzjmvl1818vnxv8";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+
+      };
+      "minimal-lexical" = rec {
+        crateName = "minimal-lexical";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8";
+        authors = [
+          "Alex Huszagh <ahuszagh@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+        };
+        resolvedDefaultFeatures = [ "std" ];
+      };
+      "miniz_oxide" = rec {
+        crateName = "miniz_oxide";
+        version = "0.7.2";
+        edition = "2018";
+        sha256 = "19qlxb21s6kabgqq61mk7kd1qk2invyygj076jz6i1gj2lz1z0cx";
+        authors = [
+          "Frommi <daniil.liferenko@gmail.com>"
+          "oyvindln <oyvindln@users.noreply.github.com>"
+        ];
+        dependencies = [
+          {
+            name = "adler";
+            packageId = "adler";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "with-alloc" ];
+          "rustc-dep-of-std" = [ "core" "alloc" "compiler_builtins" "adler/rustc-dep-of-std" ];
+          "simd" = [ "simd-adler32" ];
+          "simd-adler32" = [ "dep:simd-adler32" ];
+        };
+      };
+      "mio" = rec {
+        crateName = "mio";
+        version = "0.8.11";
+        edition = "2018";
+        sha256 = "034byyl0ardml5yliy1hmvx8arkmn9rv479pid794sm07ia519m4";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "wasi";
+            packageId = "wasi";
+            target = { target, features }: ("wasi" == target."os" or null);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_Storage_FileSystem" "Win32_System_IO" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = {
+          "default" = [ "log" ];
+          "log" = [ "dep:log" ];
+          "os-ext" = [ "os-poll" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_Security" ];
+        };
+        resolvedDefaultFeatures = [ "net" "os-ext" "os-poll" ];
+      };
+      "nix" = rec {
+        crateName = "nix";
+        version = "0.26.4";
+        edition = "2018";
+        sha256 = "06xgl4ybb8pvjrbmc3xggbgk3kbs1j0c4c0nzdfrmpbgrkrym2sr";
+        authors = [
+          "The nix-rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            features = [ "extra_traits" ];
+          }
+        ];
+        features = {
+          "aio" = [ "pin-utils" ];
+          "default" = [ "acct" "aio" "dir" "env" "event" "feature" "fs" "hostname" "inotify" "ioctl" "kmod" "mman" "mount" "mqueue" "net" "personality" "poll" "process" "pthread" "ptrace" "quota" "reboot" "resource" "sched" "signal" "socket" "term" "time" "ucontext" "uio" "user" "zerocopy" ];
+          "dir" = [ "fs" ];
+          "memoffset" = [ "dep:memoffset" ];
+          "mount" = [ "uio" ];
+          "mqueue" = [ "fs" ];
+          "net" = [ "socket" ];
+          "pin-utils" = [ "dep:pin-utils" ];
+          "ptrace" = [ "process" ];
+          "sched" = [ "process" ];
+          "signal" = [ "process" ];
+          "socket" = [ "memoffset" ];
+          "ucontext" = [ "signal" ];
+          "user" = [ "feature" ];
+          "zerocopy" = [ "fs" "uio" ];
+        };
+        resolvedDefaultFeatures = [ "feature" "fs" "user" ];
+      };
+      "nix-compat" = rec {
+        crateName = "nix-compat";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [ ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ../../../tvix/nix-compat; }
+          else ../../../tvix/nix-compat;
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 2.4.2";
+          }
+          {
+            name = "bstr";
+            packageId = "bstr";
+            features = [ "alloc" "unicode" "serde" ];
+          }
+          {
+            name = "data-encoding";
+            packageId = "data-encoding";
+          }
+          {
+            name = "ed25519";
+            packageId = "ed25519";
+          }
+          {
+            name = "ed25519-dalek";
+            packageId = "ed25519-dalek";
+          }
+          {
+            name = "enum-primitive-derive";
+            packageId = "enum-primitive-derive";
+          }
+          {
+            name = "glob";
+            packageId = "glob";
+          }
+          {
+            name = "nom";
+            packageId = "nom";
+          }
+          {
+            name = "num-traits";
+            packageId = "num-traits";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+          {
+            name = "sha2";
+            packageId = "sha2";
+          }
+          {
+            name = "thiserror";
+            packageId = "thiserror";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "io-util" "macros" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_json";
+            packageId = "serde_json";
+          }
+        ];
+        features = {
+          "async" = [ "tokio" ];
+          "pin-project-lite" = [ "dep:pin-project-lite" ];
+          "tokio" = [ "dep:tokio" ];
+          "wire" = [ "tokio" "pin-project-lite" ];
+        };
+        resolvedDefaultFeatures = [ "pin-project-lite" "tokio" "wire" ];
+      };
+      "nom" = rec {
+        crateName = "nom";
+        version = "7.1.3";
+        edition = "2018";
+        sha256 = "0jha9901wxam390jcf5pfa0qqfrgh8li787jx2ip0yk5b8y9hwyj";
+        authors = [
+          "contact@geoffroycouprie.com"
+        ];
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "minimal-lexical";
+            packageId = "minimal-lexical";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" "memchr/std" "minimal-lexical/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "nu-ansi-term" = rec {
+        crateName = "nu-ansi-term";
+        version = "0.46.0";
+        edition = "2018";
+        sha256 = "115sywxh53p190lyw97alm14nc004qj5jm5lvdj608z84rbida3p";
+        authors = [
+          "ogham@bsago.me"
+          "Ryan Scheel (Havvy) <ryan.havvy@gmail.com>"
+          "Josh Triplett <josh@joshtriplett.org>"
+          "The Nushell Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "overload";
+            packageId = "overload";
+          }
+          {
+            name = "winapi";
+            packageId = "winapi";
+            target = { target, features }: ("windows" == target."os" or null);
+            features = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "processenv" ];
+          }
+        ];
+        features = {
+          "derive_serde_style" = [ "serde" ];
+          "serde" = [ "dep:serde" ];
+        };
+      };
+      "num-traits" = rec {
+        crateName = "num-traits";
+        version = "0.2.18";
+        edition = "2018";
+        sha256 = "0yjib8p2p9kzmaz48xwhs69w5dh1wipph9jgnillzd2x33jz03fs";
+        authors = [
+          "The Rust Project Developers"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "libm" = [ "dep:libm" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "num_cpus" = rec {
+        crateName = "num_cpus";
+        version = "1.16.0";
+        edition = "2015";
+        sha256 = "0hra6ihpnh06dvfvz9ipscys0xfqa9ca9hzp384d5m02ssvgqqa1";
+        authors = [
+          "Sean McArthur <sean@seanmonstar.com>"
+        ];
+        dependencies = [
+          {
+            name = "hermit-abi";
+            packageId = "hermit-abi";
+            target = { target, features }: ("hermit" == target."os" or null);
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (!(target."windows" or false));
+          }
+        ];
+
+      };
+      "object" = rec {
+        crateName = "object";
+        version = "0.32.2";
+        edition = "2018";
+        sha256 = "0hc4cjwyngiy6k51hlzrlsxgv5z25vv7c2cp0ky1lckfic0259m6";
+        dependencies = [
+          {
+            name = "memchr";
+            packageId = "memchr";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "all" = [ "read" "write" "std" "compression" "wasm" ];
+          "alloc" = [ "dep:alloc" ];
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "compression" = [ "dep:flate2" "dep:ruzstd" "std" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "read" "compression" ];
+          "doc" = [ "read_core" "write_std" "std" "compression" "archive" "coff" "elf" "macho" "pe" "wasm" "xcoff" ];
+          "pe" = [ "coff" ];
+          "read" = [ "read_core" "archive" "coff" "elf" "macho" "pe" "xcoff" "unaligned" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" "alloc" "memchr/rustc-dep-of-std" ];
+          "std" = [ "memchr/std" ];
+          "unstable-all" = [ "all" "unstable" ];
+          "wasm" = [ "dep:wasmparser" ];
+          "write" = [ "write_std" "coff" "elf" "macho" "pe" "xcoff" ];
+          "write_core" = [ "dep:crc32fast" "dep:indexmap" "dep:hashbrown" ];
+          "write_std" = [ "write_core" "std" "indexmap?/std" "crc32fast?/std" ];
+        };
+        resolvedDefaultFeatures = [ "archive" "coff" "elf" "macho" "pe" "read_core" "unaligned" ];
+      };
+      "once_cell" = rec {
+        crateName = "once_cell";
+        version = "1.19.0";
+        edition = "2021";
+        sha256 = "14kvw7px5z96dk4dwdm1r9cqhhy2cyj1l5n5b29mynbb8yr15nrz";
+        authors = [
+          "Aleksey Kladov <aleksey.kladov@gmail.com>"
+        ];
+        features = {
+          "alloc" = [ "race" ];
+          "atomic-polyfill" = [ "critical-section" ];
+          "critical-section" = [ "dep:critical-section" "portable-atomic" ];
+          "default" = [ "std" ];
+          "parking_lot" = [ "dep:parking_lot_core" ];
+          "portable-atomic" = [ "dep:portable-atomic" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "race" "std" ];
+      };
+      "overload" = rec {
+        crateName = "overload";
+        version = "0.1.1";
+        edition = "2018";
+        sha256 = "0fdgbaqwknillagy1xq7xfgv60qdbk010diwl7s1p0qx7hb16n5i";
+        authors = [
+          "Daniel Salvadori <danaugrs@gmail.com>"
+        ];
+
+      };
+      "parking_lot" = rec {
+        crateName = "parking_lot";
+        version = "0.12.1";
+        edition = "2018";
+        sha256 = "13r2xk7mnxfc5g0g6dkdxqdqad99j7s7z8zhzz4npw5r0g0v4hip";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "lock_api";
+            packageId = "lock_api";
+          }
+          {
+            name = "parking_lot_core";
+            packageId = "parking_lot_core";
+          }
+        ];
+        features = {
+          "arc_lock" = [ "lock_api/arc_lock" ];
+          "deadlock_detection" = [ "parking_lot_core/deadlock_detection" ];
+          "nightly" = [ "parking_lot_core/nightly" "lock_api/nightly" ];
+          "owning_ref" = [ "lock_api/owning_ref" ];
+          "serde" = [ "lock_api/serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "parking_lot_core" = rec {
+        crateName = "parking_lot_core";
+        version = "0.9.9";
+        edition = "2018";
+        sha256 = "13h0imw1aq86wj28gxkblhkzx6z1gk8q18n0v76qmmj6cliajhjc";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "redox_syscall";
+            packageId = "redox_syscall";
+            target = { target, features }: ("redox" == target."os" or null);
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+          }
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        features = {
+          "backtrace" = [ "dep:backtrace" ];
+          "deadlock_detection" = [ "petgraph" "thread-id" "backtrace" ];
+          "petgraph" = [ "dep:petgraph" ];
+          "thread-id" = [ "dep:thread-id" ];
+        };
+      };
+      "percent-encoding" = rec {
+        crateName = "percent-encoding";
+        version = "2.3.1";
+        edition = "2018";
+        sha256 = "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573";
+        authors = [
+          "The rust-url developers"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "std" = [ "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "std" ];
+      };
+      "pin-project" = rec {
+        crateName = "pin-project";
+        version = "1.1.5";
+        edition = "2021";
+        sha256 = "1cxl146x0q7lawp0m1826wsgj8mmmfs6ja8q7m6f7ff5j6vl7gxn";
+        dependencies = [
+          {
+            name = "pin-project-internal";
+            packageId = "pin-project-internal";
+          }
+        ];
+
+      };
+      "pin-project-internal" = rec {
+        crateName = "pin-project-internal";
+        version = "1.1.5";
+        edition = "2021";
+        sha256 = "0r9r4ivwiyqf45sv6b30l1dx282lxaax2f6gl84jwa3q590s8f1g";
+        procMacro = true;
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" "visit-mut" ];
+          }
+        ];
+
+      };
+      "pin-project-lite" = rec {
+        crateName = "pin-project-lite";
+        version = "0.2.13";
+        edition = "2018";
+        sha256 = "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa";
+
+      };
+      "pin-utils" = rec {
+        crateName = "pin-utils";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb";
+        authors = [
+          "Josef Brandl <mail@josefbrandl.de>"
+        ];
+
+      };
+      "pkcs8" = rec {
+        crateName = "pkcs8";
+        version = "0.10.2";
+        edition = "2021";
+        sha256 = "1dx7w21gvn07azszgqd3ryjhyphsrjrmq5mmz1fbxkj5g0vv4l7r";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+          {
+            name = "spki";
+            packageId = "spki";
+          }
+        ];
+        features = {
+          "3des" = [ "encryption" "pkcs5/3des" ];
+          "alloc" = [ "der/alloc" "der/zeroize" "spki/alloc" ];
+          "des-insecure" = [ "encryption" "pkcs5/des-insecure" ];
+          "encryption" = [ "alloc" "pkcs5/alloc" "pkcs5/pbes2" "rand_core" ];
+          "getrandom" = [ "rand_core/getrandom" ];
+          "pem" = [ "alloc" "der/pem" "spki/pem" ];
+          "pkcs5" = [ "dep:pkcs5" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "sha1-insecure" = [ "encryption" "pkcs5/sha1-insecure" ];
+          "std" = [ "alloc" "der/std" "spki/std" ];
+          "subtle" = [ "dep:subtle" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "platforms" = rec {
+        crateName = "platforms";
+        version = "3.3.0";
+        edition = "2018";
+        sha256 = "0k7q6pigmnvgpfasvssb12m2pv3pc94zrhrfg9by3h3wmhyfqvb2";
+        authors = [
+          "Tony Arcieri <bascule@gmail.com>"
+          "Sergey \"Shnatsel\" Davidoff <shnatsel@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "proc-macro2" = rec {
+        crateName = "proc-macro2";
+        version = "1.0.79";
+        edition = "2021";
+        sha256 = "0bn004ybzdqid81cqppr5c9jrvqsxv50x60sxc41cwpmk0igydg8";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        dependencies = [
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "quote" = rec {
+        crateName = "quote";
+        version = "1.0.35";
+        edition = "2018";
+        sha256 = "1vv8r2ncaz4pqdr78x7f138ka595sp2ncr1sa2plm4zxbsmwj7i9";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "default" = [ "proc-macro" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" ];
+        };
+        resolvedDefaultFeatures = [ "default" "proc-macro" ];
+      };
+      "rand_core" = rec {
+        crateName = "rand_core";
+        version = "0.6.4";
+        edition = "2018";
+        sha256 = "0b4j2v4cb5krak1pv6kakv4sz6xcwbrmy2zckc32hsigbrwy82zc";
+        authors = [
+          "The Rand Project Developers"
+          "The Rust Project Developers"
+        ];
+        dependencies = [
+          {
+            name = "getrandom";
+            packageId = "getrandom";
+            optional = true;
+          }
+        ];
+        features = {
+          "getrandom" = [ "dep:getrandom" ];
+          "serde" = [ "dep:serde" ];
+          "serde1" = [ "serde" ];
+          "std" = [ "alloc" "getrandom" "getrandom/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "getrandom" "std" ];
+      };
+      "redox_syscall" = rec {
+        crateName = "redox_syscall";
+        version = "0.4.1";
+        edition = "2018";
+        sha256 = "1aiifyz5dnybfvkk4cdab9p2kmphag1yad6iknc7aszlxxldf8j7";
+        libName = "syscall";
+        authors = [
+          "Jeremy Soller <jackpot51@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "bitflags";
+            packageId = "bitflags 1.3.2";
+          }
+        ];
+        features = {
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "bitflags/rustc-dep-of-std" ];
+        };
+      };
+      "regex-automata" = rec {
+        crateName = "regex-automata";
+        version = "0.4.6";
+        edition = "2021";
+        sha256 = "1spaq7y4im7s56d1gxa2hi4hzf6dwswb1bv8xyavzya7k25kpf46";
+        authors = [
+          "The Rust Project Developers"
+          "Andrew Gallant <jamslam@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" "syntax" "perf" "unicode" "meta" "nfa" "dfa" "hybrid" ];
+          "dfa" = [ "dfa-build" "dfa-search" "dfa-onepass" ];
+          "dfa-build" = [ "nfa-thompson" "dfa-search" ];
+          "dfa-onepass" = [ "nfa-thompson" ];
+          "hybrid" = [ "alloc" "nfa-thompson" ];
+          "internal-instrument" = [ "internal-instrument-pikevm" ];
+          "internal-instrument-pikevm" = [ "logging" "std" ];
+          "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ];
+          "meta" = [ "syntax" "nfa-pikevm" ];
+          "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ];
+          "nfa-backtrack" = [ "nfa-thompson" ];
+          "nfa-pikevm" = [ "nfa-thompson" ];
+          "nfa-thompson" = [ "alloc" ];
+          "perf" = [ "perf-inline" "perf-literal" ];
+          "perf-literal" = [ "perf-literal-substring" "perf-literal-multisubstring" ];
+          "perf-literal-multisubstring" = [ "std" "dep:aho-corasick" ];
+          "perf-literal-substring" = [ "aho-corasick?/perf-literal" "dep:memchr" ];
+          "std" = [ "regex-syntax?/std" "memchr?/std" "aho-corasick?/std" "alloc" ];
+          "syntax" = [ "dep:regex-syntax" "alloc" ];
+          "unicode" = [ "unicode-age" "unicode-bool" "unicode-case" "unicode-gencat" "unicode-perl" "unicode-script" "unicode-segment" "unicode-word-boundary" "regex-syntax?/unicode" ];
+          "unicode-age" = [ "regex-syntax?/unicode-age" ];
+          "unicode-bool" = [ "regex-syntax?/unicode-bool" ];
+          "unicode-case" = [ "regex-syntax?/unicode-case" ];
+          "unicode-gencat" = [ "regex-syntax?/unicode-gencat" ];
+          "unicode-perl" = [ "regex-syntax?/unicode-perl" ];
+          "unicode-script" = [ "regex-syntax?/unicode-script" ];
+          "unicode-segment" = [ "regex-syntax?/unicode-segment" ];
+        };
+        resolvedDefaultFeatures = [ "dfa-search" ];
+      };
+      "rustc-demangle" = rec {
+        crateName = "rustc-demangle";
+        version = "0.1.23";
+        edition = "2015";
+        sha256 = "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "rustc-dep-of-std" = [ "core" "compiler_builtins" ];
+        };
+      };
+      "rustc_version" = rec {
+        crateName = "rustc_version";
+        version = "0.4.0";
+        edition = "2018";
+        sha256 = "0rpk9rcdk405xhbmgclsh4pai0svn49x35aggl4nhbkd4a2zb85z";
+        authors = [
+          "Dirkjan Ochtman <dirkjan@ochtman.nl>"
+          "Marvin Lรถbel <loebel.marvin@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "semver";
+            packageId = "semver";
+          }
+        ];
+
+      };
+      "rustversion" = rec {
+        crateName = "rustversion";
+        version = "1.0.14";
+        edition = "2018";
+        sha256 = "1x1pz1yynk5xzzrazk2svmidj69jhz89dz5vrc28sixl20x1iz3z";
+        procMacro = true;
+        build = "build/build.rs";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "ryu" = rec {
+        crateName = "ryu";
+        version = "1.0.17";
+        edition = "2018";
+        sha256 = "188vrsh3zlnl5xl7lw0rp2sc0knpx8yaqpwvr648b6h12v4rfrp8";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "no-panic" = [ "dep:no-panic" ];
+        };
+      };
+      "scopeguard" = rec {
+        crateName = "scopeguard";
+        version = "1.2.0";
+        edition = "2015";
+        sha256 = "0jcz9sd47zlsgcnm1hdw0664krxwb5gczlif4qngj2aif8vky54l";
+        authors = [
+          "bluss"
+        ];
+        features = {
+          "default" = [ "use_std" ];
+        };
+      };
+      "semver" = rec {
+        crateName = "semver";
+        version = "1.0.22";
+        edition = "2018";
+        sha256 = "1jir6q2ps4s5v52bqxpvwj35p0m0ahl5pf62ppwksbv5kvk3zm4j";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "serde" = rec {
+        crateName = "serde";
+        version = "1.0.197";
+        edition = "2018";
+        sha256 = "1qjcxqd3p4yh5cmmax9q4ics1zy34j5ij32cvjj5dc5rw5rwic9z";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            optional = true;
+          }
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+            target = { target, features }: false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde_derive";
+            packageId = "serde_derive";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "serde_derive" ];
+          "serde_derive" = [ "dep:serde_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "default" "derive" "serde_derive" "std" ];
+      };
+      "serde_derive" = rec {
+        crateName = "serde_derive";
+        version = "1.0.197";
+        edition = "2015";
+        sha256 = "02v1x0sdv8qy06lpr6by4ar1n3jz3hmab15cgimpzhgd895v7c3y";
+        procMacro = true;
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            usesDefaultFeatures = false;
+            features = [ "proc-macro" ];
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            usesDefaultFeatures = false;
+            features = [ "clone-impls" "derive" "parsing" "printing" "proc-macro" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "serde_json" = rec {
+        crateName = "serde_json";
+        version = "1.0.114";
+        edition = "2021";
+        sha256 = "1q4saigxwkf8bw4y5kp6k33dnavlvvwa2q4zmag59vrjsqdrpw65";
+        authors = [
+          "Erick Tryzelaar <erick.tryzelaar@gmail.com>"
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "serde";
+            packageId = "serde";
+            features = [ "derive" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "serde/alloc" ];
+          "default" = [ "std" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "preserve_order" = [ "indexmap" "std" ];
+          "std" = [ "serde/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "raw_value" "std" ];
+      };
+      "serde_path_to_error" = rec {
+        crateName = "serde_path_to_error";
+        version = "0.1.16";
+        edition = "2021";
+        sha256 = "19hlz2359l37ifirskpcds7sxg0gzpqvfilibs7whdys0128i6dg";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+        ];
+
+      };
+      "serde_urlencoded" = rec {
+        crateName = "serde_urlencoded";
+        version = "0.7.1";
+        edition = "2018";
+        sha256 = "1zgklbdaysj3230xivihs30qi5vkhigg323a9m62k8jwf4a1qjfk";
+        authors = [
+          "Anthony Ramine <n.oxyde@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "form_urlencoded";
+            packageId = "form_urlencoded";
+          }
+          {
+            name = "itoa";
+            packageId = "itoa";
+          }
+          {
+            name = "ryu";
+            packageId = "ryu";
+          }
+          {
+            name = "serde";
+            packageId = "serde";
+          }
+        ];
+
+      };
+      "sha2" = rec {
+        crateName = "sha2";
+        version = "0.10.8";
+        edition = "2018";
+        sha256 = "1j1x78zk9il95w9iv46dh9wm73r6xrgj32y6lzzw7bxws9dbfgbr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "cpufeatures";
+            packageId = "cpufeatures";
+            target = { target, features }: (("aarch64" == target."arch" or null) || ("x86_64" == target."arch" or null) || ("x86" == target."arch" or null));
+          }
+          {
+            name = "digest";
+            packageId = "digest";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "digest";
+            packageId = "digest";
+            features = [ "dev" ];
+          }
+        ];
+        features = {
+          "asm" = [ "sha2-asm" ];
+          "asm-aarch64" = [ "asm" ];
+          "default" = [ "std" ];
+          "oid" = [ "digest/oid" ];
+          "sha2-asm" = [ "dep:sha2-asm" ];
+          "std" = [ "digest/std" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "sharded-slab" = rec {
+        crateName = "sharded-slab";
+        version = "0.1.7";
+        edition = "2018";
+        sha256 = "1xipjr4nqsgw34k7a2cgj9zaasl2ds6jwn89886kww93d32a637l";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+        ];
+        dependencies = [
+          {
+            name = "lazy_static";
+            packageId = "lazy_static";
+          }
+        ];
+        features = {
+          "loom" = [ "dep:loom" ];
+        };
+      };
+      "signal-hook-registry" = rec {
+        crateName = "signal-hook-registry";
+        version = "1.4.1";
+        edition = "2015";
+        sha256 = "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq";
+        authors = [
+          "Michal 'vorner' Vaner <vorner@vorner.cz>"
+          "Masaki Hara <ackie.h.gmai@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+          }
+        ];
+
+      };
+      "signature" = rec {
+        crateName = "signature";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "1pi9hd5vqfr3q3k49k37z06p7gs5si0in32qia4mmr1dancr6m3p";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "rand_core";
+            packageId = "rand_core";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+        ];
+        features = {
+          "derive" = [ "dep:derive" ];
+          "digest" = [ "dep:digest" ];
+          "rand_core" = [ "dep:rand_core" ];
+          "std" = [ "alloc" "rand_core?/std" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "slab" = rec {
+        crateName = "slab";
+        version = "0.4.9";
+        edition = "2018";
+        sha256 = "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg";
+        authors = [
+          "Carl Lerche <me@carllerche.com>"
+        ];
+        buildDependencies = [
+          {
+            name = "autocfg";
+            packageId = "autocfg";
+          }
+        ];
+        features = {
+          "default" = [ "std" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "smallvec" = rec {
+        crateName = "smallvec";
+        version = "1.13.1";
+        edition = "2018";
+        sha256 = "1mzk9j117pn3k1gabys0b7nz8cdjsx5xc6q7fwnm8r0an62d7v76";
+        authors = [
+          "The Servo Project Developers"
+        ];
+        features = {
+          "arbitrary" = [ "dep:arbitrary" ];
+          "const_new" = [ "const_generics" ];
+          "drain_keep_rest" = [ "drain_filter" ];
+          "serde" = [ "dep:serde" ];
+        };
+        resolvedDefaultFeatures = [ "const_generics" "const_new" ];
+      };
+      "socket2" = rec {
+        crateName = "socket2";
+        version = "0.5.6";
+        edition = "2021";
+        sha256 = "0w98g7dh9m74vpxln401hl4knpjzrx7jhng7cbh46x9vm70dkzq5";
+        authors = [
+          "Alex Crichton <alex@alexcrichton.com>"
+          "Thomas de Zeeuw <thomasdezeeuw@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.52.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Networking_WinSock" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" ];
+          }
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "all" ];
+      };
+      "spki" = rec {
+        crateName = "spki";
+        version = "0.7.3";
+        edition = "2021";
+        sha256 = "17fj8k5fmx4w9mp27l970clrh5qa7r5sjdvbsln987xhb34dc7nr";
+        authors = [
+          "RustCrypto Developers"
+        ];
+        dependencies = [
+          {
+            name = "base64ct";
+            packageId = "base64ct";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "der";
+            packageId = "der";
+            features = [ "oid" ];
+          }
+        ];
+        features = {
+          "alloc" = [ "base64ct?/alloc" "der/alloc" ];
+          "arbitrary" = [ "std" "dep:arbitrary" "der/arbitrary" ];
+          "base64" = [ "dep:base64ct" ];
+          "fingerprint" = [ "sha2" ];
+          "pem" = [ "alloc" "der/pem" ];
+          "sha2" = [ "dep:sha2" ];
+          "std" = [ "der/std" "alloc" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "strsim" = rec {
+        crateName = "strsim";
+        version = "0.11.0";
+        edition = "2015";
+        sha256 = "00gsdp2x1gkkxsbjxgrjyil2hsbdg49bwv8q2y1f406dwk4p7q2y";
+        authors = [
+          "Danny Guo <danny@dannyguo.com>"
+          "maxbachmann <oss@maxbachmann.de>"
+        ];
+
+      };
+      "subtle" = rec {
+        crateName = "subtle";
+        version = "2.5.0";
+        edition = "2018";
+        sha256 = "1g2yjs7gffgmdvkkq0wrrh0pxds3q0dv6dhkw9cdpbib656xdkc1";
+        authors = [
+          "Isis Lovecruft <isis@patternsinthevoid.net>"
+          "Henry de Valence <hdevalence@hdevalence.ca>"
+        ];
+        features = {
+          "default" = [ "std" "i128" ];
+        };
+      };
+      "syn" = rec {
+        crateName = "syn";
+        version = "2.0.52";
+        edition = "2021";
+        sha256 = "01saay6pi9x19f6lin3mw3xawdyyagpzzy39ghz2rw6i6rdx36dn";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "unicode-ident";
+            packageId = "unicode-ident";
+          }
+        ];
+        features = {
+          "default" = [ "derive" "parsing" "printing" "clone-impls" "proc-macro" ];
+          "printing" = [ "quote" ];
+          "proc-macro" = [ "proc-macro2/proc-macro" "quote/proc-macro" ];
+          "quote" = [ "dep:quote" ];
+          "test" = [ "syn-test-suite/all-features" ];
+        };
+        resolvedDefaultFeatures = [ "clone-impls" "default" "derive" "extra-traits" "full" "parsing" "printing" "proc-macro" "quote" "visit-mut" ];
+      };
+      "sync_wrapper" = rec {
+        crateName = "sync_wrapper";
+        version = "0.1.2";
+        edition = "2018";
+        sha256 = "0q01lyj0gr9a93n10nxsn8lwbzq97jqd6b768x17c8f7v7gccir0";
+        authors = [
+          "Actyx AG <developer@actyx.io>"
+        ];
+        features = {
+          "futures" = [ "futures-core" ];
+          "futures-core" = [ "dep:futures-core" ];
+        };
+      };
+      "thiserror" = rec {
+        crateName = "thiserror";
+        version = "1.0.58";
+        edition = "2021";
+        sha256 = "15rjgd1abi2mzjgzfhrvmsxf9h65n95h6sp8f4s52q4i00wqhih3";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "thiserror-impl";
+            packageId = "thiserror-impl";
+          }
+        ];
+
+      };
+      "thiserror-impl" = rec {
+        crateName = "thiserror-impl";
+        version = "1.0.58";
+        edition = "2021";
+        sha256 = "1xylyqcb8rv5yh2yf97hg4n4kg27qccc0ijafr1zqklrhahkn7y6";
+        procMacro = true;
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+          }
+        ];
+
+      };
+      "thread_local" = rec {
+        crateName = "thread_local";
+        version = "1.1.8";
+        edition = "2021";
+        sha256 = "173i5lyjh011gsimk21np9jn8al18rxsrkjli20a7b8ks2xgk7lb";
+        authors = [
+          "Amanieu d'Antras <amanieu@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "cfg-if";
+            packageId = "cfg-if";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+        ];
+        features = { };
+      };
+      "tokio" = rec {
+        crateName = "tokio";
+        version = "1.36.0";
+        edition = "2021";
+        sha256 = "0c89p36zbd4abr1z3l5mipp43x7z4c9b4vp4s6r8y0gs2mjmya31";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "backtrace";
+            packageId = "backtrace";
+            target = { target, features }: (target."tokio_taskdump" or false);
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+            optional = true;
+          }
+          {
+            name = "libc";
+            packageId = "libc";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "mio";
+            packageId = "mio";
+            optional = true;
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "num_cpus";
+            packageId = "num_cpus";
+            optional = true;
+          }
+          {
+            name = "parking_lot";
+            packageId = "parking_lot";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "signal-hook-registry";
+            packageId = "signal-hook-registry";
+            optional = true;
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+            features = [ "all" ];
+          }
+          {
+            name = "tokio-macros";
+            packageId = "tokio-macros";
+            optional = true;
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            optional = true;
+            target = { target, features }: (target."windows" or false);
+          }
+        ];
+        devDependencies = [
+          {
+            name = "libc";
+            packageId = "libc";
+            target = { target, features }: (target."unix" or false);
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            target = { target, features }: (!(builtins.elem "wasm" target."family"));
+          }
+          {
+            name = "windows-sys";
+            packageId = "windows-sys 0.48.0";
+            target = { target, features }: (target."windows" or false);
+            features = [ "Win32_Foundation" "Win32_Security_Authorization" ];
+          }
+        ];
+        features = {
+          "bytes" = [ "dep:bytes" ];
+          "full" = [ "fs" "io-util" "io-std" "macros" "net" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "sync" "time" ];
+          "io-util" = [ "bytes" ];
+          "libc" = [ "dep:libc" ];
+          "macros" = [ "tokio-macros" ];
+          "mio" = [ "dep:mio" ];
+          "net" = [ "libc" "mio/os-poll" "mio/os-ext" "mio/net" "socket2" "windows-sys/Win32_Foundation" "windows-sys/Win32_Security" "windows-sys/Win32_Storage_FileSystem" "windows-sys/Win32_System_Pipes" "windows-sys/Win32_System_SystemServices" ];
+          "num_cpus" = [ "dep:num_cpus" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "process" = [ "bytes" "libc" "mio/os-poll" "mio/os-ext" "mio/net" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Threading" "windows-sys/Win32_System_WindowsProgramming" ];
+          "rt-multi-thread" = [ "num_cpus" "rt" ];
+          "signal" = [ "libc" "mio/os-poll" "mio/net" "mio/os-ext" "signal-hook-registry" "windows-sys/Win32_Foundation" "windows-sys/Win32_System_Console" ];
+          "signal-hook-registry" = [ "dep:signal-hook-registry" ];
+          "socket2" = [ "dep:socket2" ];
+          "test-util" = [ "rt" "sync" "time" ];
+          "tokio-macros" = [ "dep:tokio-macros" ];
+          "tracing" = [ "dep:tracing" ];
+          "windows-sys" = [ "dep:windows-sys" ];
+        };
+        resolvedDefaultFeatures = [ "bytes" "default" "fs" "full" "io-std" "io-util" "libc" "macros" "mio" "net" "num_cpus" "parking_lot" "process" "rt" "rt-multi-thread" "signal" "signal-hook-registry" "socket2" "sync" "test-util" "time" "tokio-macros" "windows-sys" ];
+      };
+      "tokio-listener" = rec {
+        crateName = "tokio-listener";
+        version = "0.3.2";
+        edition = "2021";
+        sha256 = "00vkr1cywd2agn8jbkzwwf7y4ps3cfjm8l9ab697px2cgc97wdln";
+        dependencies = [
+          {
+            name = "axum";
+            packageId = "axum";
+            rename = "axum07";
+          }
+          {
+            name = "document-features";
+            packageId = "document-features";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+          }
+          {
+            name = "nix";
+            packageId = "nix";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."unix" or false);
+            features = [ "user" "fs" ];
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+          }
+          {
+            name = "socket2";
+            packageId = "socket2";
+            optional = true;
+            features = [ "all" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "net" "io-std" "time" "sync" ];
+          }
+          {
+            name = "tokio-util";
+            packageId = "tokio-util";
+            optional = true;
+            features = [ "net" "codec" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "rt" "io-util" ];
+          }
+        ];
+        features = {
+          "axum07" = [ "dep:hyper1" "dep:hyper-util" "dep:futures-util" "dep:tower-service" "dep:tower" ];
+          "clap" = [ "dep:clap" ];
+          "default" = [ "user_facing_default" "tokio-util" ];
+          "hyper014" = [ "dep:hyper014" ];
+          "inetd" = [ "dep:futures-util" ];
+          "nix" = [ "dep:nix" ];
+          "serde" = [ "dep:serde" "serde_with" ];
+          "serde_with" = [ "dep:serde_with" ];
+          "socket2" = [ "dep:socket2" ];
+          "socket_options" = [ "socket2" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "tonic010" = [ "dep:tonic_010" ];
+          "tonic011" = [ "dep:tonic" ];
+          "unix_path_tools" = [ "nix" ];
+          "user_facing_default" = [ "inetd" "unix" "unix_path_tools" "sd_listen" "socket_options" ];
+        };
+        resolvedDefaultFeatures = [ "default" "inetd" "nix" "sd_listen" "socket2" "socket_options" "tokio-util" "unix" "unix_path_tools" "user_facing_default" ];
+      };
+      "tokio-macros" = rec {
+        crateName = "tokio-macros";
+        version = "2.2.0";
+        edition = "2021";
+        sha256 = "0fwjy4vdx1h9pi4g2nml72wi0fr27b5m954p13ji9anyy8l1x2jv";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-stream" = rec {
+        crateName = "tokio-stream";
+        version = "0.1.14";
+        edition = "2021";
+        sha256 = "0hi8hcwavh5sdi1ivc9qc4yvyr32f153c212dpd7sb366y6rhz1r";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" "test-util" ];
+          }
+        ];
+        features = {
+          "default" = [ "time" ];
+          "fs" = [ "tokio/fs" ];
+          "full" = [ "time" "net" "io-util" "fs" "sync" "signal" ];
+          "io-util" = [ "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "signal" = [ "tokio/signal" ];
+          "sync" = [ "tokio/sync" "tokio-util" ];
+          "time" = [ "tokio/time" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+        };
+        resolvedDefaultFeatures = [ "default" "time" ];
+      };
+      "tokio-test" = rec {
+        crateName = "tokio-test";
+        version = "0.4.4";
+        edition = "2021";
+        sha256 = "1xzri2m3dg8nzdyznm77nymvil9cyh1gfdfrbnska51iqfmvls14";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "async-stream";
+            packageId = "async-stream";
+          }
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "rt" "sync" "time" "test-util" ];
+          }
+          {
+            name = "tokio-stream";
+            packageId = "tokio-stream";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+
+      };
+      "tokio-util" = rec {
+        crateName = "tokio-util";
+        version = "0.7.10";
+        edition = "2021";
+        sha256 = "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "bytes";
+            packageId = "bytes";
+          }
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+          }
+          {
+            name = "futures-sink";
+            packageId = "futures-sink";
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "sync" ];
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+        ];
+        features = {
+          "__docs_rs" = [ "futures-util" ];
+          "codec" = [ "tracing" ];
+          "compat" = [ "futures-io" ];
+          "full" = [ "codec" "compat" "io-util" "time" "net" "rt" ];
+          "futures-io" = [ "dep:futures-io" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hashbrown" = [ "dep:hashbrown" ];
+          "io-util" = [ "io" "tokio/rt" "tokio/io-util" ];
+          "net" = [ "tokio/net" ];
+          "rt" = [ "tokio/rt" "tokio/sync" "futures-util" "hashbrown" ];
+          "slab" = [ "dep:slab" ];
+          "time" = [ "tokio/time" "slab" ];
+          "tracing" = [ "dep:tracing" ];
+        };
+        resolvedDefaultFeatures = [ "codec" "default" "io" "net" "tracing" ];
+      };
+      "tower" = rec {
+        crateName = "tower";
+        version = "0.4.13";
+        edition = "2018";
+        sha256 = "073wncyqav4sak1p755hf6vl66njgfc1z1g1di9rxx3cvvh9pymq";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+        dependencies = [
+          {
+            name = "futures-core";
+            packageId = "futures-core";
+            optional = true;
+          }
+          {
+            name = "futures-util";
+            packageId = "futures-util";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "alloc" ];
+          }
+          {
+            name = "pin-project";
+            packageId = "pin-project";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+            optional = true;
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            optional = true;
+            features = [ "sync" ];
+          }
+          {
+            name = "tower-layer";
+            packageId = "tower-layer";
+          }
+          {
+            name = "tower-service";
+            packageId = "tower-service";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "macros" "sync" "test-util" "rt-multi-thread" ];
+          }
+        ];
+        features = {
+          "__common" = [ "futures-core" "pin-project-lite" ];
+          "balance" = [ "discover" "load" "ready-cache" "make" "rand" "slab" ];
+          "buffer" = [ "__common" "tokio/sync" "tokio/rt" "tokio-util" "tracing" ];
+          "default" = [ "log" ];
+          "discover" = [ "__common" ];
+          "filter" = [ "__common" "futures-util" ];
+          "full" = [ "balance" "buffer" "discover" "filter" "hedge" "limit" "load" "load-shed" "make" "ready-cache" "reconnect" "retry" "spawn-ready" "steer" "timeout" "util" ];
+          "futures-core" = [ "dep:futures-core" ];
+          "futures-util" = [ "dep:futures-util" ];
+          "hdrhistogram" = [ "dep:hdrhistogram" ];
+          "hedge" = [ "util" "filter" "futures-util" "hdrhistogram" "tokio/time" "tracing" ];
+          "indexmap" = [ "dep:indexmap" ];
+          "limit" = [ "__common" "tokio/time" "tokio/sync" "tokio-util" "tracing" ];
+          "load" = [ "__common" "tokio/time" "tracing" ];
+          "load-shed" = [ "__common" ];
+          "log" = [ "tracing/log" ];
+          "make" = [ "futures-util" "pin-project-lite" "tokio/io-std" ];
+          "pin-project" = [ "dep:pin-project" ];
+          "pin-project-lite" = [ "dep:pin-project-lite" ];
+          "rand" = [ "dep:rand" ];
+          "ready-cache" = [ "futures-core" "futures-util" "indexmap" "tokio/sync" "tracing" "pin-project-lite" ];
+          "reconnect" = [ "make" "tokio/io-std" "tracing" ];
+          "retry" = [ "__common" "tokio/time" ];
+          "slab" = [ "dep:slab" ];
+          "spawn-ready" = [ "__common" "futures-util" "tokio/sync" "tokio/rt" "util" "tracing" ];
+          "timeout" = [ "pin-project-lite" "tokio/time" ];
+          "tokio" = [ "dep:tokio" ];
+          "tokio-stream" = [ "dep:tokio-stream" ];
+          "tokio-util" = [ "dep:tokio-util" ];
+          "tracing" = [ "dep:tracing" ];
+          "util" = [ "__common" "futures-util" "pin-project" ];
+        };
+        resolvedDefaultFeatures = [ "__common" "futures-core" "futures-util" "log" "make" "pin-project" "pin-project-lite" "tokio" "tracing" "util" ];
+      };
+      "tower-layer" = rec {
+        crateName = "tower-layer";
+        version = "0.3.2";
+        edition = "2018";
+        sha256 = "1l7i17k9vlssrdg4s3b0ia5jjkmmxsvv8s9y9ih0jfi8ssz8s362";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+
+      };
+      "tower-service" = rec {
+        crateName = "tower-service";
+        version = "0.3.2";
+        edition = "2018";
+        sha256 = "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n";
+        authors = [
+          "Tower Maintainers <team@tower-rs.com>"
+        ];
+
+      };
+      "tracing" = rec {
+        crateName = "tracing";
+        version = "0.1.40";
+        edition = "2018";
+        sha256 = "1vv48dac9zgj9650pg2b4d0j3w6f3x9gbggf43scq5hrlysklln3";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+            optional = true;
+          }
+          {
+            name = "pin-project-lite";
+            packageId = "pin-project-lite";
+          }
+          {
+            name = "tracing-attributes";
+            packageId = "tracing-attributes";
+            optional = true;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+        ];
+        devDependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+        ];
+        features = {
+          "attributes" = [ "tracing-attributes" ];
+          "default" = [ "std" "attributes" ];
+          "log" = [ "dep:log" ];
+          "log-always" = [ "log" ];
+          "std" = [ "tracing-core/std" ];
+          "tracing-attributes" = [ "dep:tracing-attributes" ];
+          "valuable" = [ "tracing-core/valuable" ];
+        };
+        resolvedDefaultFeatures = [ "attributes" "default" "log" "std" "tracing-attributes" ];
+      };
+      "tracing-attributes" = rec {
+        crateName = "tracing-attributes";
+        version = "0.1.27";
+        edition = "2018";
+        sha256 = "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l";
+        procMacro = true;
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <dbarsky@amazon.com>"
+        ];
+        dependencies = [
+          {
+            name = "proc-macro2";
+            packageId = "proc-macro2";
+          }
+          {
+            name = "quote";
+            packageId = "quote";
+          }
+          {
+            name = "syn";
+            packageId = "syn";
+            usesDefaultFeatures = false;
+            features = [ "full" "parsing" "printing" "visit-mut" "clone-impls" "extra-traits" "proc-macro" ];
+          }
+        ];
+        features = { };
+      };
+      "tracing-core" = rec {
+        crateName = "tracing-core";
+        version = "0.1.32";
+        edition = "2018";
+        sha256 = "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+            optional = true;
+          }
+          {
+            name = "valuable";
+            packageId = "valuable";
+            optional = true;
+            usesDefaultFeatures = false;
+            target = { target, features }: (target."tracing_unstable" or false);
+          }
+        ];
+        features = {
+          "default" = [ "std" "valuable/std" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "std" = [ "once_cell" ];
+          "valuable" = [ "dep:valuable" ];
+        };
+        resolvedDefaultFeatures = [ "default" "once_cell" "std" "valuable" ];
+      };
+      "tracing-log" = rec {
+        crateName = "tracing-log";
+        version = "0.2.0";
+        edition = "2018";
+        sha256 = "1hs77z026k730ij1a9dhahzrl0s073gfa2hm5p0fbl0b80gmz1gf";
+        authors = [
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "log";
+            packageId = "log";
+          }
+          {
+            name = "once_cell";
+            packageId = "once_cell";
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+          }
+        ];
+        features = {
+          "ahash" = [ "dep:ahash" ];
+          "default" = [ "log-tracer" "std" ];
+          "interest-cache" = [ "lru" "ahash" ];
+          "lru" = [ "dep:lru" ];
+          "std" = [ "log/std" ];
+        };
+        resolvedDefaultFeatures = [ "log-tracer" "std" ];
+      };
+      "tracing-subscriber" = rec {
+        crateName = "tracing-subscriber";
+        version = "0.3.18";
+        edition = "2018";
+        sha256 = "12vs1bwk4kig1l2qqjbbn2nm5amwiqmkcmnznylzmnfvjy6083xd";
+        authors = [
+          "Eliza Weisman <eliza@buoyant.io>"
+          "David Barsky <me@davidbarsky.com>"
+          "Tokio Contributors <team@tokio.rs>"
+        ];
+        dependencies = [
+          {
+            name = "nu-ansi-term";
+            packageId = "nu-ansi-term";
+            optional = true;
+          }
+          {
+            name = "sharded-slab";
+            packageId = "sharded-slab";
+            optional = true;
+          }
+          {
+            name = "smallvec";
+            packageId = "smallvec";
+            optional = true;
+          }
+          {
+            name = "thread_local";
+            packageId = "thread_local";
+            optional = true;
+          }
+          {
+            name = "tracing-core";
+            packageId = "tracing-core";
+            usesDefaultFeatures = false;
+          }
+          {
+            name = "tracing-log";
+            packageId = "tracing-log";
+            optional = true;
+            usesDefaultFeatures = false;
+            features = [ "log-tracer" "std" ];
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tracing-log";
+            packageId = "tracing-log";
+          }
+        ];
+        features = {
+          "ansi" = [ "fmt" "nu-ansi-term" ];
+          "chrono" = [ "dep:chrono" ];
+          "default" = [ "smallvec" "fmt" "ansi" "tracing-log" "std" ];
+          "env-filter" = [ "matchers" "regex" "once_cell" "tracing" "std" "thread_local" ];
+          "fmt" = [ "registry" "std" ];
+          "json" = [ "tracing-serde" "serde" "serde_json" ];
+          "local-time" = [ "time/local-offset" ];
+          "matchers" = [ "dep:matchers" ];
+          "nu-ansi-term" = [ "dep:nu-ansi-term" ];
+          "once_cell" = [ "dep:once_cell" ];
+          "parking_lot" = [ "dep:parking_lot" ];
+          "regex" = [ "dep:regex" ];
+          "registry" = [ "sharded-slab" "thread_local" "std" ];
+          "serde" = [ "dep:serde" ];
+          "serde_json" = [ "dep:serde_json" ];
+          "sharded-slab" = [ "dep:sharded-slab" ];
+          "smallvec" = [ "dep:smallvec" ];
+          "std" = [ "alloc" "tracing-core/std" ];
+          "thread_local" = [ "dep:thread_local" ];
+          "time" = [ "dep:time" ];
+          "tracing" = [ "dep:tracing" ];
+          "tracing-log" = [ "dep:tracing-log" ];
+          "tracing-serde" = [ "dep:tracing-serde" ];
+          "valuable" = [ "tracing-core/valuable" "valuable_crate" "valuable-serde" "tracing-serde/valuable" ];
+          "valuable-serde" = [ "dep:valuable-serde" ];
+          "valuable_crate" = [ "dep:valuable_crate" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "ansi" "default" "fmt" "nu-ansi-term" "registry" "sharded-slab" "smallvec" "std" "thread_local" "tracing-log" ];
+      };
+      "tvix-daemon" = rec {
+        crateName = "tvix-daemon";
+        version = "0.1.0";
+        edition = "2021";
+        crateBin = [
+          {
+            name = "tvix-daemon";
+            path = "src/main.rs";
+            requiredFeatures = [ ];
+          }
+        ];
+        # We can't filter paths with references in Nix 2.4
+        # See https://github.com/NixOS/nix/issues/5410
+        src =
+          if ((lib.versionOlder builtins.nixVersion "2.4pre20211007") || (lib.versionOlder "2.5" builtins.nixVersion))
+          then lib.cleanSourceWith { filter = sourceFilter; src = ./.; }
+          else ./.;
+        dependencies = [
+          {
+            name = "clap";
+            packageId = "clap";
+            features = [ "derive" "env" ];
+          }
+          {
+            name = "nix-compat";
+            packageId = "nix-compat";
+            features = [ "wire" ];
+          }
+          {
+            name = "tokio";
+            packageId = "tokio";
+            features = [ "full" ];
+          }
+          {
+            name = "tokio-listener";
+            packageId = "tokio-listener";
+          }
+          {
+            name = "tracing";
+            packageId = "tracing";
+          }
+          {
+            name = "tracing-subscriber";
+            packageId = "tracing-subscriber";
+          }
+        ];
+        devDependencies = [
+          {
+            name = "tokio-test";
+            packageId = "tokio-test";
+          }
+        ];
+
+      };
+      "typenum" = rec {
+        crateName = "typenum";
+        version = "1.17.0";
+        edition = "2018";
+        sha256 = "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2";
+        build = "build/main.rs";
+        authors = [
+          "Paho Lurie-Gregg <paho@paholg.com>"
+          "Andre Bogus <bogusandre@gmail.com>"
+        ];
+        features = {
+          "scale-info" = [ "dep:scale-info" ];
+          "scale_info" = [ "scale-info/derive" ];
+        };
+      };
+      "unicode-ident" = rec {
+        crateName = "unicode-ident";
+        version = "1.0.12";
+        edition = "2018";
+        sha256 = "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k";
+        authors = [
+          "David Tolnay <dtolnay@gmail.com>"
+        ];
+
+      };
+      "utf8parse" = rec {
+        crateName = "utf8parse";
+        version = "0.2.1";
+        edition = "2018";
+        sha256 = "02ip1a0az0qmc2786vxk2nqwsgcwf17d3a38fkf0q7hrmwh9c6vi";
+        authors = [
+          "Joe Wilm <joe@jwilm.com>"
+          "Christian Duerr <contact@christianduerr.com>"
+        ];
+        features = { };
+        resolvedDefaultFeatures = [ "default" ];
+      };
+      "valuable" = rec {
+        crateName = "valuable";
+        version = "0.1.0";
+        edition = "2018";
+        sha256 = "0v9gp3nkjbl30z0fd56d8mx7w1csk86wwjhfjhr400wh9mfpw2w3";
+        features = {
+          "default" = [ "std" ];
+          "derive" = [ "valuable-derive" ];
+          "std" = [ "alloc" ];
+          "valuable-derive" = [ "dep:valuable-derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" "std" ];
+      };
+      "version_check" = rec {
+        crateName = "version_check";
+        version = "0.9.4";
+        edition = "2015";
+        sha256 = "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9";
+        authors = [
+          "Sergio Benitez <sb@sergio.bz>"
+        ];
+
+      };
+      "wasi" = rec {
+        crateName = "wasi";
+        version = "0.11.0+wasi-snapshot-preview1";
+        edition = "2018";
+        sha256 = "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw";
+        authors = [
+          "The Cranelift Project Developers"
+        ];
+        features = {
+          "compiler_builtins" = [ "dep:compiler_builtins" ];
+          "core" = [ "dep:core" ];
+          "default" = [ "std" ];
+          "rustc-dep-of-std" = [ "compiler_builtins" "core" "rustc-std-workspace-alloc" ];
+          "rustc-std-workspace-alloc" = [ "dep:rustc-std-workspace-alloc" ];
+        };
+        resolvedDefaultFeatures = [ "default" "std" ];
+      };
+      "winapi" = rec {
+        crateName = "winapi";
+        version = "0.3.9";
+        edition = "2015";
+        sha256 = "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+        dependencies = [
+          {
+            name = "winapi-i686-pc-windows-gnu";
+            packageId = "winapi-i686-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "i686-pc-windows-gnu");
+          }
+          {
+            name = "winapi-x86_64-pc-windows-gnu";
+            packageId = "winapi-x86_64-pc-windows-gnu";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnu");
+          }
+        ];
+        features = {
+          "debug" = [ "impl-debug" ];
+        };
+        resolvedDefaultFeatures = [ "consoleapi" "errhandlingapi" "fileapi" "handleapi" "processenv" ];
+      };
+      "winapi-i686-pc-windows-gnu" = rec {
+        crateName = "winapi-i686-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "winapi-x86_64-pc-windows-gnu" = rec {
+        crateName = "winapi-x86_64-pc-windows-gnu";
+        version = "0.4.0";
+        edition = "2015";
+        sha256 = "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki";
+        authors = [
+          "Peter Atashian <retep998@gmail.com>"
+        ];
+
+      };
+      "windows-sys 0.48.0" = rec {
+        crateName = "windows-sys";
+        version = "0.48.0";
+        edition = "2018";
+        sha256 = "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.48.5";
+          }
+        ];
+        features = {
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Data_Xml" = [ "Win32_Data" ];
+          "Win32_Data_Xml_MsXml" = [ "Win32_Data_Xml" ];
+          "Win32_Data_Xml_XmlLite" = [ "Win32_Data_Xml" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAccess" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_FunctionDiscovery" = [ "Win32_Devices" ];
+          "Win32_Devices_Geolocation" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_ImageAcquisition" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_Audio_Apo" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_DirectMusic" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_Endpoints" = [ "Win32_Media_Audio" ];
+          "Win32_Media_Audio_XAudio2" = [ "Win32_Media_Audio" ];
+          "Win32_Media_DeviceManager" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_LibrarySharingServices" = [ "Win32_Media" ];
+          "Win32_Media_MediaPlayer" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Speech" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_MobileBroadband" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkPolicyServer" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectNow" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_BackgroundIntelligentTransferService" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_NetworkListManager" = [ "Win32_Networking" ];
+          "Win32_Networking_RemoteDifferentialCompression" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authentication_Identity_Provider" = [ "Win32_Security_Authentication_Identity" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Authorization_UI" = [ "Win32_Security_Authorization" ];
+          "Win32_Security_ConfigurationSnapin" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_Tpm" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DataDeduplication" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_EnhancedStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileServerResourceManager" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_Packaging_Opc" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_VirtualDiskService" = [ "Win32_Storage" ];
+          "Win32_Storage_Vss" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps_Printing" = [ "Win32_Storage_Xps" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_AssessmentTool" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_CallObj" = [ "Win32_System_Com" ];
+          "Win32_System_Com_ChannelCredentials" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Events" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_UI" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_Contacts" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DesktopSharing" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ClrProfiling" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_ActiveScript" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Mmc" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_ParentalControls" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_RealTimeCommunications" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteAssistance" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_ServerBackup" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SettingsManagementInfrastructure" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_TaskScheduler" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UpdateAgent" = [ "Win32_System" ];
+          "Win32_System_UpdateAssessment" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_WindowsSync" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_Animation" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_Controls_RichEdit" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Ink" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Radial" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_LegacyWindowsEnvironmentFeatures" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Notifications" = [ "Win32_UI" ];
+          "Win32_UI_Ribbon" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_Common" = [ "Win32_UI_Shell" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_UI_Wpf" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Security" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_IO" "Win32_System_Pipes" "Win32_System_SystemServices" "Win32_System_Threading" "Win32_System_WindowsProgramming" "default" ];
+      };
+      "windows-sys 0.52.0" = rec {
+        crateName = "windows-sys";
+        version = "0.52.0";
+        edition = "2021";
+        sha256 = "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows-targets";
+            packageId = "windows-targets 0.52.4";
+          }
+        ];
+        features = {
+          "Wdk_Foundation" = [ "Wdk" ];
+          "Wdk_Graphics" = [ "Wdk" ];
+          "Wdk_Graphics_Direct3D" = [ "Wdk_Graphics" ];
+          "Wdk_Storage" = [ "Wdk" ];
+          "Wdk_Storage_FileSystem" = [ "Wdk_Storage" ];
+          "Wdk_Storage_FileSystem_Minifilters" = [ "Wdk_Storage_FileSystem" ];
+          "Wdk_System" = [ "Wdk" ];
+          "Wdk_System_IO" = [ "Wdk_System" ];
+          "Wdk_System_OfflineRegistry" = [ "Wdk_System" ];
+          "Wdk_System_Registry" = [ "Wdk_System" ];
+          "Wdk_System_SystemInformation" = [ "Wdk_System" ];
+          "Wdk_System_SystemServices" = [ "Wdk_System" ];
+          "Wdk_System_Threading" = [ "Wdk_System" ];
+          "Win32_Data" = [ "Win32" ];
+          "Win32_Data_HtmlHelp" = [ "Win32_Data" ];
+          "Win32_Data_RightsManagement" = [ "Win32_Data" ];
+          "Win32_Devices" = [ "Win32" ];
+          "Win32_Devices_AllJoyn" = [ "Win32_Devices" ];
+          "Win32_Devices_BiometricFramework" = [ "Win32_Devices" ];
+          "Win32_Devices_Bluetooth" = [ "Win32_Devices" ];
+          "Win32_Devices_Communication" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceAndDriverInstallation" = [ "Win32_Devices" ];
+          "Win32_Devices_DeviceQuery" = [ "Win32_Devices" ];
+          "Win32_Devices_Display" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration" = [ "Win32_Devices" ];
+          "Win32_Devices_Enumeration_Pnp" = [ "Win32_Devices_Enumeration" ];
+          "Win32_Devices_Fax" = [ "Win32_Devices" ];
+          "Win32_Devices_HumanInterfaceDevice" = [ "Win32_Devices" ];
+          "Win32_Devices_PortableDevices" = [ "Win32_Devices" ];
+          "Win32_Devices_Properties" = [ "Win32_Devices" ];
+          "Win32_Devices_Pwm" = [ "Win32_Devices" ];
+          "Win32_Devices_Sensors" = [ "Win32_Devices" ];
+          "Win32_Devices_SerialCommunication" = [ "Win32_Devices" ];
+          "Win32_Devices_Tapi" = [ "Win32_Devices" ];
+          "Win32_Devices_Usb" = [ "Win32_Devices" ];
+          "Win32_Devices_WebServicesOnDevices" = [ "Win32_Devices" ];
+          "Win32_Foundation" = [ "Win32" ];
+          "Win32_Gaming" = [ "Win32" ];
+          "Win32_Globalization" = [ "Win32" ];
+          "Win32_Graphics" = [ "Win32" ];
+          "Win32_Graphics_Dwm" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Gdi" = [ "Win32_Graphics" ];
+          "Win32_Graphics_GdiPlus" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Hlsl" = [ "Win32_Graphics" ];
+          "Win32_Graphics_OpenGL" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing" = [ "Win32_Graphics" ];
+          "Win32_Graphics_Printing_PrintTicket" = [ "Win32_Graphics_Printing" ];
+          "Win32_Management" = [ "Win32" ];
+          "Win32_Management_MobileDeviceManagementRegistration" = [ "Win32_Management" ];
+          "Win32_Media" = [ "Win32" ];
+          "Win32_Media_Audio" = [ "Win32_Media" ];
+          "Win32_Media_DxMediaObjects" = [ "Win32_Media" ];
+          "Win32_Media_KernelStreaming" = [ "Win32_Media" ];
+          "Win32_Media_Multimedia" = [ "Win32_Media" ];
+          "Win32_Media_Streaming" = [ "Win32_Media" ];
+          "Win32_Media_WindowsMediaFormat" = [ "Win32_Media" ];
+          "Win32_NetworkManagement" = [ "Win32" ];
+          "Win32_NetworkManagement_Dhcp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Dns" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_InternetConnectionWizard" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_IpHelper" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Multicast" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Ndis" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetBios" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetManagement" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetShell" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_NetworkDiagnosticsFramework" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_P2P" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_QoS" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Rras" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_Snmp" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WNet" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WebDav" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WiFi" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsConnectionManager" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFilteringPlatform" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsFirewall" = [ "Win32_NetworkManagement" ];
+          "Win32_NetworkManagement_WindowsNetworkVirtualization" = [ "Win32_NetworkManagement" ];
+          "Win32_Networking" = [ "Win32" ];
+          "Win32_Networking_ActiveDirectory" = [ "Win32_Networking" ];
+          "Win32_Networking_Clustering" = [ "Win32_Networking" ];
+          "Win32_Networking_HttpServer" = [ "Win32_Networking" ];
+          "Win32_Networking_Ldap" = [ "Win32_Networking" ];
+          "Win32_Networking_WebSocket" = [ "Win32_Networking" ];
+          "Win32_Networking_WinHttp" = [ "Win32_Networking" ];
+          "Win32_Networking_WinInet" = [ "Win32_Networking" ];
+          "Win32_Networking_WinSock" = [ "Win32_Networking" ];
+          "Win32_Networking_WindowsWebServices" = [ "Win32_Networking" ];
+          "Win32_Security" = [ "Win32" ];
+          "Win32_Security_AppLocker" = [ "Win32_Security" ];
+          "Win32_Security_Authentication" = [ "Win32_Security" ];
+          "Win32_Security_Authentication_Identity" = [ "Win32_Security_Authentication" ];
+          "Win32_Security_Authorization" = [ "Win32_Security" ];
+          "Win32_Security_Credentials" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography" = [ "Win32_Security" ];
+          "Win32_Security_Cryptography_Catalog" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Certificates" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_Sip" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_Cryptography_UI" = [ "Win32_Security_Cryptography" ];
+          "Win32_Security_DiagnosticDataQuery" = [ "Win32_Security" ];
+          "Win32_Security_DirectoryServices" = [ "Win32_Security" ];
+          "Win32_Security_EnterpriseData" = [ "Win32_Security" ];
+          "Win32_Security_ExtensibleAuthenticationProtocol" = [ "Win32_Security" ];
+          "Win32_Security_Isolation" = [ "Win32_Security" ];
+          "Win32_Security_LicenseProtection" = [ "Win32_Security" ];
+          "Win32_Security_NetworkAccessProtection" = [ "Win32_Security" ];
+          "Win32_Security_WinTrust" = [ "Win32_Security" ];
+          "Win32_Security_WinWlx" = [ "Win32_Security" ];
+          "Win32_Storage" = [ "Win32" ];
+          "Win32_Storage_Cabinets" = [ "Win32_Storage" ];
+          "Win32_Storage_CloudFilters" = [ "Win32_Storage" ];
+          "Win32_Storage_Compression" = [ "Win32_Storage" ];
+          "Win32_Storage_DistributedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_FileHistory" = [ "Win32_Storage" ];
+          "Win32_Storage_FileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_Imapi" = [ "Win32_Storage" ];
+          "Win32_Storage_IndexServer" = [ "Win32_Storage" ];
+          "Win32_Storage_InstallableFileSystems" = [ "Win32_Storage" ];
+          "Win32_Storage_IscsiDisc" = [ "Win32_Storage" ];
+          "Win32_Storage_Jet" = [ "Win32_Storage" ];
+          "Win32_Storage_Nvme" = [ "Win32_Storage" ];
+          "Win32_Storage_OfflineFiles" = [ "Win32_Storage" ];
+          "Win32_Storage_OperationRecorder" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging" = [ "Win32_Storage" ];
+          "Win32_Storage_Packaging_Appx" = [ "Win32_Storage_Packaging" ];
+          "Win32_Storage_ProjectedFileSystem" = [ "Win32_Storage" ];
+          "Win32_Storage_StructuredStorage" = [ "Win32_Storage" ];
+          "Win32_Storage_Vhd" = [ "Win32_Storage" ];
+          "Win32_Storage_Xps" = [ "Win32_Storage" ];
+          "Win32_System" = [ "Win32" ];
+          "Win32_System_AddressBook" = [ "Win32_System" ];
+          "Win32_System_Antimalware" = [ "Win32_System" ];
+          "Win32_System_ApplicationInstallationAndServicing" = [ "Win32_System" ];
+          "Win32_System_ApplicationVerifier" = [ "Win32_System" ];
+          "Win32_System_ClrHosting" = [ "Win32_System" ];
+          "Win32_System_Com" = [ "Win32_System" ];
+          "Win32_System_Com_Marshal" = [ "Win32_System_Com" ];
+          "Win32_System_Com_StructuredStorage" = [ "Win32_System_Com" ];
+          "Win32_System_Com_Urlmon" = [ "Win32_System_Com" ];
+          "Win32_System_ComponentServices" = [ "Win32_System" ];
+          "Win32_System_Console" = [ "Win32_System" ];
+          "Win32_System_CorrelationVector" = [ "Win32_System" ];
+          "Win32_System_DataExchange" = [ "Win32_System" ];
+          "Win32_System_DeploymentServices" = [ "Win32_System" ];
+          "Win32_System_DeveloperLicensing" = [ "Win32_System" ];
+          "Win32_System_Diagnostics" = [ "Win32_System" ];
+          "Win32_System_Diagnostics_Ceip" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_Debug_Extensions" = [ "Win32_System_Diagnostics_Debug" ];
+          "Win32_System_Diagnostics_Etw" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ProcessSnapshotting" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_Diagnostics_ToolHelp" = [ "Win32_System_Diagnostics" ];
+          "Win32_System_DistributedTransactionCoordinator" = [ "Win32_System" ];
+          "Win32_System_Environment" = [ "Win32_System" ];
+          "Win32_System_ErrorReporting" = [ "Win32_System" ];
+          "Win32_System_EventCollector" = [ "Win32_System" ];
+          "Win32_System_EventLog" = [ "Win32_System" ];
+          "Win32_System_EventNotificationService" = [ "Win32_System" ];
+          "Win32_System_GroupPolicy" = [ "Win32_System" ];
+          "Win32_System_HostCompute" = [ "Win32_System" ];
+          "Win32_System_HostComputeNetwork" = [ "Win32_System" ];
+          "Win32_System_HostComputeSystem" = [ "Win32_System" ];
+          "Win32_System_Hypervisor" = [ "Win32_System" ];
+          "Win32_System_IO" = [ "Win32_System" ];
+          "Win32_System_Iis" = [ "Win32_System" ];
+          "Win32_System_Ioctl" = [ "Win32_System" ];
+          "Win32_System_JobObjects" = [ "Win32_System" ];
+          "Win32_System_Js" = [ "Win32_System" ];
+          "Win32_System_Kernel" = [ "Win32_System" ];
+          "Win32_System_LibraryLoader" = [ "Win32_System" ];
+          "Win32_System_Mailslots" = [ "Win32_System" ];
+          "Win32_System_Mapi" = [ "Win32_System" ];
+          "Win32_System_Memory" = [ "Win32_System" ];
+          "Win32_System_Memory_NonVolatile" = [ "Win32_System_Memory" ];
+          "Win32_System_MessageQueuing" = [ "Win32_System" ];
+          "Win32_System_MixedReality" = [ "Win32_System" ];
+          "Win32_System_Ole" = [ "Win32_System" ];
+          "Win32_System_PasswordManagement" = [ "Win32_System" ];
+          "Win32_System_Performance" = [ "Win32_System" ];
+          "Win32_System_Performance_HardwareCounterProfiling" = [ "Win32_System_Performance" ];
+          "Win32_System_Pipes" = [ "Win32_System" ];
+          "Win32_System_Power" = [ "Win32_System" ];
+          "Win32_System_ProcessStatus" = [ "Win32_System" ];
+          "Win32_System_Recovery" = [ "Win32_System" ];
+          "Win32_System_Registry" = [ "Win32_System" ];
+          "Win32_System_RemoteDesktop" = [ "Win32_System" ];
+          "Win32_System_RemoteManagement" = [ "Win32_System" ];
+          "Win32_System_RestartManager" = [ "Win32_System" ];
+          "Win32_System_Restore" = [ "Win32_System" ];
+          "Win32_System_Rpc" = [ "Win32_System" ];
+          "Win32_System_Search" = [ "Win32_System" ];
+          "Win32_System_Search_Common" = [ "Win32_System_Search" ];
+          "Win32_System_SecurityCenter" = [ "Win32_System" ];
+          "Win32_System_Services" = [ "Win32_System" ];
+          "Win32_System_SetupAndMigration" = [ "Win32_System" ];
+          "Win32_System_Shutdown" = [ "Win32_System" ];
+          "Win32_System_StationsAndDesktops" = [ "Win32_System" ];
+          "Win32_System_SubsystemForLinux" = [ "Win32_System" ];
+          "Win32_System_SystemInformation" = [ "Win32_System" ];
+          "Win32_System_SystemServices" = [ "Win32_System" ];
+          "Win32_System_Threading" = [ "Win32_System" ];
+          "Win32_System_Time" = [ "Win32_System" ];
+          "Win32_System_TpmBaseServices" = [ "Win32_System" ];
+          "Win32_System_UserAccessLogging" = [ "Win32_System" ];
+          "Win32_System_Variant" = [ "Win32_System" ];
+          "Win32_System_VirtualDosMachines" = [ "Win32_System" ];
+          "Win32_System_WindowsProgramming" = [ "Win32_System" ];
+          "Win32_System_Wmi" = [ "Win32_System" ];
+          "Win32_UI" = [ "Win32" ];
+          "Win32_UI_Accessibility" = [ "Win32_UI" ];
+          "Win32_UI_ColorSystem" = [ "Win32_UI" ];
+          "Win32_UI_Controls" = [ "Win32_UI" ];
+          "Win32_UI_Controls_Dialogs" = [ "Win32_UI_Controls" ];
+          "Win32_UI_HiDpi" = [ "Win32_UI" ];
+          "Win32_UI_Input" = [ "Win32_UI" ];
+          "Win32_UI_Input_Ime" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_KeyboardAndMouse" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Pointer" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_Touch" = [ "Win32_UI_Input" ];
+          "Win32_UI_Input_XboxController" = [ "Win32_UI_Input" ];
+          "Win32_UI_InteractionContext" = [ "Win32_UI" ];
+          "Win32_UI_Magnification" = [ "Win32_UI" ];
+          "Win32_UI_Shell" = [ "Win32_UI" ];
+          "Win32_UI_Shell_PropertiesSystem" = [ "Win32_UI_Shell" ];
+          "Win32_UI_TabletPC" = [ "Win32_UI" ];
+          "Win32_UI_TextServices" = [ "Win32_UI" ];
+          "Win32_UI_WindowsAndMessaging" = [ "Win32_UI" ];
+          "Win32_Web" = [ "Win32" ];
+          "Win32_Web_InternetExplorer" = [ "Win32_Web" ];
+        };
+        resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_System" "Win32_System_Console" "Win32_System_IO" "Win32_System_Threading" "Win32_System_WindowsProgramming" "default" ];
+      };
+      "windows-targets 0.48.5" = rec {
+        crateName = "windows-targets";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.48.5";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.48.5";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.48.5";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.48.5";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows-targets 0.52.4" = rec {
+        crateName = "windows-targets";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "06sdd7fin3dj9cmlg6n1dw0n1l10jhn9b8ckz1cqf0drb9z7plvx";
+        authors = [
+          "Microsoft"
+        ];
+        dependencies = [
+          {
+            name = "windows_aarch64_gnullvm";
+            packageId = "windows_aarch64_gnullvm 0.52.4";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "aarch64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_aarch64_msvc";
+            packageId = "windows_aarch64_msvc 0.52.4";
+            target = { target, features }: (("aarch64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_gnu";
+            packageId = "windows_i686_gnu 0.52.4";
+            target = { target, features }: (("x86" == target."arch" or null) && ("gnu" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_i686_msvc";
+            packageId = "windows_i686_msvc 0.52.4";
+            target = { target, features }: (("x86" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnu";
+            packageId = "windows_x86_64_gnu 0.52.4";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("gnu" == target."env" or null) && (!("llvm" == target."abi" or null)) && (!(target."windows_raw_dylib" or false)));
+          }
+          {
+            name = "windows_x86_64_gnullvm";
+            packageId = "windows_x86_64_gnullvm 0.52.4";
+            target = { target, features }: (pkgs.rust.lib.toRustTarget stdenv.hostPlatform == "x86_64-pc-windows-gnullvm");
+          }
+          {
+            name = "windows_x86_64_msvc";
+            packageId = "windows_x86_64_msvc 0.52.4";
+            target = { target, features }: (("x86_64" == target."arch" or null) && ("msvc" == target."env" or null) && (!(target."windows_raw_dylib" or false)));
+          }
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.48.5" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_gnullvm 0.52.4" = rec {
+        crateName = "windows_aarch64_gnullvm";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "1jfam5qfngg8v1syxklnvy8la94b5igm7klkrk8z5ik5qgs6rx5w";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.48.5" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_aarch64_msvc 0.52.4" = rec {
+        crateName = "windows_aarch64_msvc";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "0xdn6db0rk8idn7dxsyflixq2dbj9x60kzdzal5rkxmwsffjb7ys";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.48.5" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_gnu 0.52.4" = rec {
+        crateName = "windows_i686_gnu";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "1lq1g35sbj55ms86by4c080jcqrlfjy9bw5r4mgrkq4riwkdhx5l";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.48.5" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_i686_msvc 0.52.4" = rec {
+        crateName = "windows_i686_msvc";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "00lfzw88dkf3fdcf2hpfhp74i9pwbp7rwnj1nhy79vavksifj58m";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.48.5" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnu 0.52.4" = rec {
+        crateName = "windows_x86_64_gnu";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "00qs6x33bf9lai2q68faxl56cszbv7mf7zqlslmc1778j0ahkvjy";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.48.5" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_gnullvm 0.52.4" = rec {
+        crateName = "windows_x86_64_gnullvm";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "0xr13xxakp14hs4v4hg2ynjcv7wrzr3hg7zk5agglj8v8pr7kjkp";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.48.5" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.48.5";
+        edition = "2018";
+        sha256 = "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "windows_x86_64_msvc 0.52.4" = rec {
+        crateName = "windows_x86_64_msvc";
+        version = "0.52.4";
+        edition = "2021";
+        sha256 = "1n0yc7xiv9iki1j3xl8nxlwwkr7dzsnwwvycvgxxv81d5bjm5drj";
+        authors = [
+          "Microsoft"
+        ];
+
+      };
+      "zeroize" = rec {
+        crateName = "zeroize";
+        version = "1.7.0";
+        edition = "2021";
+        sha256 = "0bfvby7k9pdp6623p98yz2irqnamcyzpn7zh20nqmdn68b0lwnsj";
+        authors = [
+          "The RustCrypto Project Developers"
+        ];
+        features = {
+          "default" = [ "alloc" ];
+          "derive" = [ "zeroize_derive" ];
+          "serde" = [ "dep:serde" ];
+          "std" = [ "alloc" ];
+          "zeroize_derive" = [ "dep:zeroize_derive" ];
+        };
+        resolvedDefaultFeatures = [ "alloc" ];
+      };
+    };
+
+    #
+    # crate2nix/default.nix (excerpt start)
+    #
+
+    /* Target (platform) data for conditional dependencies.
+      This corresponds roughly to what buildRustCrate is setting.
+    */
+    makeDefaultTarget = platform: {
+      unix = platform.isUnix;
+      windows = platform.isWindows;
+      fuchsia = true;
+      test = false;
+
+      /* We are choosing an arbitrary rust version to grab `lib` from,
+      which is unfortunate, but `lib` has been version-agnostic the
+      whole time so this is good enough for now.
+      */
+      os = pkgs.rust.lib.toTargetOs platform;
+      arch = pkgs.rust.lib.toTargetArch platform;
+      family = pkgs.rust.lib.toTargetFamily platform;
+      vendor = pkgs.rust.lib.toTargetVendor platform;
+      env = "gnu";
+      endian =
+        if platform.parsed.cpu.significantByte.name == "littleEndian"
+        then "little" else "big";
+      pointer_width = toString platform.parsed.cpu.bits;
+      debug_assertions = false;
+    };
+
+    /* Filters common temp files and build files. */
+    # TODO(pkolloch): Substitute with gitignore filter
+    sourceFilter = name: type:
+      let
+        baseName = builtins.baseNameOf (builtins.toString name);
+      in
+        ! (
+          # Filter out git
+          baseName == ".gitignore"
+          || (type == "directory" && baseName == ".git")
+
+          # Filter out build results
+          || (
+            type == "directory" && (
+              baseName == "target"
+              || baseName == "_site"
+              || baseName == ".sass-cache"
+              || baseName == ".jekyll-metadata"
+              || baseName == "build-artifacts"
+            )
+          )
+
+          # Filter out nix-build result symlinks
+          || (
+            type == "symlink" && lib.hasPrefix "result" baseName
+          )
+
+          # Filter out IDE config
+          || (
+            type == "directory" && (
+              baseName == ".idea" || baseName == ".vscode"
+            )
+          ) || lib.hasSuffix ".iml" baseName
+
+          # Filter out nix build files
+          || baseName == "Cargo.nix"
+
+          # Filter out editor backup / swap files.
+          || lib.hasSuffix "~" baseName
+          || builtins.match "^\\.sw[a-z]$$" baseName != null
+          || builtins.match "^\\..*\\.sw[a-z]$$" baseName != null
+          || lib.hasSuffix ".tmp" baseName
+          || lib.hasSuffix ".bak" baseName
+          || baseName == "tests.nix"
+        );
+
+    /* Returns a crate which depends on successful test execution
+      of crate given as the second argument.
+
+      testCrateFlags: list of flags to pass to the test exectuable
+      testInputs: list of packages that should be available during test execution
+    */
+    crateWithTest = { crate, testCrate, testCrateFlags, testInputs, testPreRun, testPostRun }:
+      assert builtins.typeOf testCrateFlags == "list";
+      assert builtins.typeOf testInputs == "list";
+      assert builtins.typeOf testPreRun == "string";
+      assert builtins.typeOf testPostRun == "string";
+      let
+        # override the `crate` so that it will build and execute tests instead of
+        # building the actual lib and bin targets We just have to pass `--test`
+        # to rustc and it will do the right thing.  We execute the tests and copy
+        # their log and the test executables to $out for later inspection.
+        test =
+          let
+            drv = testCrate.override
+              (
+                _: {
+                  buildTests = true;
+                  release = false;
+                }
+              );
+            # If the user hasn't set any pre/post commands, we don't want to
+            # insert empty lines. This means that any existing users of crate2nix
+            # don't get a spurious rebuild unless they set these explicitly.
+            testCommand = pkgs.lib.concatStringsSep "\n"
+              (pkgs.lib.filter (s: s != "") [
+                testPreRun
+                "$f $testCrateFlags 2>&1 | tee -a $out"
+                testPostRun
+              ]);
+          in
+          pkgs.runCommand "run-tests-${testCrate.name}"
+            {
+              inherit testCrateFlags;
+              buildInputs = testInputs;
+            } ''
+            set -e
+
+            export RUST_BACKTRACE=1
+
+            # recreate a file hierarchy as when running tests with cargo
+
+            # the source for test data
+            # It's necessary to locate the source in $NIX_BUILD_TOP/source/
+            # instead of $NIX_BUILD_TOP/
+            # because we compiled those test binaries in the former and not the latter.
+            # So all paths will expect source tree to be there and not in the build top directly.
+            # For example: $NIX_BUILD_TOP := /build in general, if you ask yourself.
+            # TODO(raitobezarius): I believe there could be more edge cases if `crate.sourceRoot`
+            # do exist but it's very hard to reason about them, so let's wait until the first bug report.
+            mkdir -p source/
+            cd source/
+
+            ${pkgs.buildPackages.xorg.lndir}/bin/lndir ${crate.src}
+
+            # build outputs
+            testRoot=target/debug
+            mkdir -p $testRoot
+
+            # executables of the crate
+            # we copy to prevent std::env::current_exe() to resolve to a store location
+            for i in ${crate}/bin/*; do
+              cp "$i" "$testRoot"
+            done
+            chmod +w -R .
+
+            # test harness executables are suffixed with a hash, like cargo does
+            # this allows to prevent name collision with the main
+            # executables of the crate
+            hash=$(basename $out)
+            for file in ${drv}/tests/*; do
+              f=$testRoot/$(basename $file)-$hash
+              cp $file $f
+              ${testCommand}
+            done
+          '';
+      in
+      pkgs.runCommand "${crate.name}-linked"
+        {
+          inherit (crate) outputs crateName;
+          passthru = (crate.passthru or { }) // {
+            inherit test;
+          };
+        }
+        (lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+          echo tested by ${test}
+        '' + ''
+          ${lib.concatMapStringsSep "\n" (output: "ln -s ${crate.${output}} ${"$"}${output}") crate.outputs}
+        '');
+
+    /* A restricted overridable version of builtRustCratesWithFeatures. */
+    buildRustCrateWithFeatures =
+      { packageId
+      , features ? rootFeatures
+      , crateOverrides ? defaultCrateOverrides
+      , buildRustCrateForPkgsFunc ? null
+      , runTests ? false
+      , testCrateFlags ? [ ]
+      , testInputs ? [ ]
+        # Any command to run immediatelly before a test is executed.
+      , testPreRun ? ""
+        # Any command run immediatelly after a test is executed.
+      , testPostRun ? ""
+      }:
+      lib.makeOverridable
+        (
+          { features
+          , crateOverrides
+          , runTests
+          , testCrateFlags
+          , testInputs
+          , testPreRun
+          , testPostRun
+          }:
+          let
+            buildRustCrateForPkgsFuncOverriden =
+              if buildRustCrateForPkgsFunc != null
+              then buildRustCrateForPkgsFunc
+              else
+                (
+                  if crateOverrides == pkgs.defaultCrateOverrides
+                  then buildRustCrateForPkgs
+                  else
+                    pkgs: (buildRustCrateForPkgs pkgs).override {
+                      defaultCrateOverrides = crateOverrides;
+                    }
+                );
+            builtRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = false;
+            };
+            builtTestRustCrates = builtRustCratesWithFeatures {
+              inherit packageId features;
+              buildRustCrateForPkgsFunc = buildRustCrateForPkgsFuncOverriden;
+              runTests = true;
+            };
+            drv = builtRustCrates.crates.${packageId};
+            testDrv = builtTestRustCrates.crates.${packageId};
+            derivation =
+              if runTests then
+                crateWithTest
+                  {
+                    crate = drv;
+                    testCrate = testDrv;
+                    inherit testCrateFlags testInputs testPreRun testPostRun;
+                  }
+              else drv;
+          in
+          derivation
+        )
+        { inherit features crateOverrides runTests testCrateFlags testInputs testPreRun testPostRun; };
+
+    /* Returns an attr set with packageId mapped to the result of buildRustCrateForPkgsFunc
+      for the corresponding crate.
+    */
+    builtRustCratesWithFeatures =
+      { packageId
+      , features
+      , crateConfigs ? crates
+      , buildRustCrateForPkgsFunc
+      , runTests
+      , makeTarget ? makeDefaultTarget
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isList features);
+        assert (builtins.isAttrs (makeTarget stdenv.hostPlatform));
+        assert (builtins.isBool runTests);
+        let
+          rootPackageId = packageId;
+          mergedFeatures = mergePackageFeatures
+            (
+              args // {
+                inherit rootPackageId;
+                target = makeTarget stdenv.hostPlatform // { test = runTests; };
+              }
+            );
+          # Memoize built packages so that reappearing packages are only built once.
+          builtByPackageIdByPkgs = mkBuiltByPackageIdByPkgs pkgs;
+          mkBuiltByPackageIdByPkgs = pkgs:
+            let
+              self = {
+                crates = lib.mapAttrs (packageId: value: buildByPackageIdForPkgsImpl self pkgs packageId) crateConfigs;
+                target = makeTarget pkgs.stdenv.hostPlatform;
+                build = mkBuiltByPackageIdByPkgs pkgs.buildPackages;
+              };
+            in
+            self;
+          buildByPackageIdForPkgsImpl = self: pkgs: packageId:
+            let
+              features = mergedFeatures."${packageId}" or [ ];
+              crateConfig' = crateConfigs."${packageId}";
+              crateConfig =
+                builtins.removeAttrs crateConfig' [ "resolvedDefaultFeatures" "devDependencies" ];
+              devDependencies =
+                lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig'.devDependencies or [ ]);
+              dependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self) target;
+                  buildByPackageId = depPackageId:
+                    # proc_macro crates must be compiled for the build architecture
+                    if crateConfigs.${depPackageId}.procMacro or false
+                    then self.build.crates.${depPackageId}
+                    else self.crates.${depPackageId};
+                  dependencies =
+                    (crateConfig.dependencies or [ ])
+                    ++ devDependencies;
+                };
+              buildDependencies =
+                dependencyDerivations {
+                  inherit features;
+                  inherit (self.build) target;
+                  buildByPackageId = depPackageId:
+                    self.build.crates.${depPackageId};
+                  dependencies = crateConfig.buildDependencies or [ ];
+                };
+              dependenciesWithRenames =
+                let
+                  buildDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self) target;
+                    dependencies = crateConfig.dependencies or [ ] ++ devDependencies;
+                  };
+                  hostDeps = filterEnabledDependencies {
+                    inherit features;
+                    inherit (self.build) target;
+                    dependencies = crateConfig.buildDependencies or [ ];
+                  };
+                in
+                lib.filter (d: d ? "rename") (hostDeps ++ buildDeps);
+              # Crate renames have the form:
+              #
+              # {
+              #    crate_name = [
+              #       { version = "1.2.3"; rename = "crate_name01"; }
+              #    ];
+              #    # ...
+              # }
+              crateRenames =
+                let
+                  grouped =
+                    lib.groupBy
+                      (dependency: dependency.name)
+                      dependenciesWithRenames;
+                  versionAndRename = dep:
+                    let
+                      package = crateConfigs."${dep.packageId}";
+                    in
+                    { inherit (dep) rename; inherit (package) version; };
+                in
+                lib.mapAttrs (name: builtins.map versionAndRename) grouped;
+            in
+            buildRustCrateForPkgsFunc pkgs
+              (
+                crateConfig // {
+                  # https://github.com/NixOS/nixpkgs/issues/218712
+                  dontStrip = stdenv.hostPlatform.isDarwin;
+                  src = crateConfig.src or (
+                    pkgs.fetchurl rec {
+                      name = "${crateConfig.crateName}-${crateConfig.version}.tar.gz";
+                      # https://www.pietroalbini.org/blog/downloading-crates-io/
+                      # Not rate-limited, CDN URL.
+                      url = "https://static.crates.io/crates/${crateConfig.crateName}/${crateConfig.crateName}-${crateConfig.version}.crate";
+                      sha256 =
+                        assert (lib.assertMsg (crateConfig ? sha256) "Missing sha256 for ${name}");
+                        crateConfig.sha256;
+                    }
+                  );
+                  extraRustcOpts = lib.lists.optional (targetFeatures != [ ]) "-C target-feature=${lib.concatMapStringsSep "," (x: "+${x}") targetFeatures}";
+                  inherit features dependencies buildDependencies crateRenames release;
+                }
+              );
+        in
+        builtByPackageIdByPkgs;
+
+    /* Returns the actual derivations for the given dependencies. */
+    dependencyDerivations =
+      { buildByPackageId
+      , features
+      , dependencies
+      , target
+      }:
+        assert (builtins.isList features);
+        assert (builtins.isList dependencies);
+        assert (builtins.isAttrs target);
+        let
+          enabledDependencies = filterEnabledDependencies {
+            inherit dependencies features target;
+          };
+          depDerivation = dependency: buildByPackageId dependency.packageId;
+        in
+        map depDerivation enabledDependencies;
+
+    /* Returns a sanitized version of val with all values substituted that cannot
+      be serialized as JSON.
+    */
+    sanitizeForJson = val:
+      if builtins.isAttrs val
+      then lib.mapAttrs (n: sanitizeForJson) val
+      else if builtins.isList val
+      then builtins.map sanitizeForJson val
+      else if builtins.isFunction val
+      then "function"
+      else val;
+
+    /* Returns various tools to debug a crate. */
+    debugCrate = { packageId, target ? makeDefaultTarget stdenv.hostPlatform }:
+      assert (builtins.isString packageId);
+      let
+        debug = rec {
+          # The built tree as passed to buildRustCrate.
+          buildTree = buildRustCrateWithFeatures {
+            buildRustCrateForPkgsFunc = _: lib.id;
+            inherit packageId;
+          };
+          sanitizedBuildTree = sanitizeForJson buildTree;
+          dependencyTree = sanitizeForJson
+            (
+              buildRustCrateWithFeatures {
+                buildRustCrateForPkgsFunc = _: crate: {
+                  "01_crateName" = crate.crateName or false;
+                  "02_features" = crate.features or [ ];
+                  "03_dependencies" = crate.dependencies or [ ];
+                };
+                inherit packageId;
+              }
+            );
+          mergedPackageFeatures = mergePackageFeatures {
+            features = rootFeatures;
+            inherit packageId target;
+          };
+          diffedDefaultPackageFeatures = diffDefaultPackageFeatures {
+            inherit packageId target;
+          };
+        };
+      in
+      { internal = debug; };
+
+    /* Returns differences between cargo default features and crate2nix default
+      features.
+
+      This is useful for verifying the feature resolution in crate2nix.
+    */
+    diffDefaultPackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , target
+      }:
+        assert (builtins.isAttrs crateConfigs);
+        let
+          prefixValues = prefix: lib.mapAttrs (n: v: { "${prefix}" = v; });
+          mergedFeatures =
+            prefixValues
+              "crate2nix"
+              (mergePackageFeatures { inherit crateConfigs packageId target; features = [ "default" ]; });
+          configs = prefixValues "cargo" crateConfigs;
+          combined = lib.foldAttrs (a: b: a // b) { } [ mergedFeatures configs ];
+          onlyInCargo =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: !(v ? "crate2nix") && (v ? "cargo")) combined);
+          onlyInCrate2Nix =
+            builtins.attrNames
+              (lib.filterAttrs (n: v: (v ? "crate2nix") && !(v ? "cargo")) combined);
+          differentFeatures = lib.filterAttrs
+            (
+              n: v:
+                (v ? "crate2nix")
+                && (v ? "cargo")
+                && (v.crate2nix.features or [ ]) != (v."cargo".resolved_default_features or [ ])
+            )
+            combined;
+        in
+        builtins.toJSON {
+          inherit onlyInCargo onlyInCrate2Nix differentFeatures;
+        };
+
+    /* Returns an attrset mapping packageId to the list of enabled features.
+
+      If multiple paths to a dependency enable different features, the
+      corresponding feature sets are merged. Features in rust are additive.
+    */
+    mergePackageFeatures =
+      { crateConfigs ? crates
+      , packageId
+      , rootPackageId ? packageId
+      , features ? rootFeatures
+      , dependencyPath ? [ crates.${packageId}.crateName ]
+      , featuresByPackageId ? { }
+      , target
+        # Adds devDependencies to the crate with rootPackageId.
+      , runTests ? false
+      , ...
+      } @ args:
+        assert (builtins.isAttrs crateConfigs);
+        assert (builtins.isString packageId);
+        assert (builtins.isString rootPackageId);
+        assert (builtins.isList features);
+        assert (builtins.isList dependencyPath);
+        assert (builtins.isAttrs featuresByPackageId);
+        assert (builtins.isAttrs target);
+        assert (builtins.isBool runTests);
+        let
+          crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
+          expandedFeatures = expandFeatures (crateConfig.features or { }) features;
+          enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
+          depWithResolvedFeatures = dependency:
+            let
+              inherit (dependency) packageId;
+              features = dependencyFeatures enabledFeatures dependency;
+            in
+            { inherit packageId features; };
+          resolveDependencies = cache: path: dependencies:
+            assert (builtins.isAttrs cache);
+            assert (builtins.isList dependencies);
+            let
+              enabledDependencies = filterEnabledDependencies {
+                inherit dependencies target;
+                features = enabledFeatures;
+              };
+              directDependencies = map depWithResolvedFeatures enabledDependencies;
+              foldOverCache = op: lib.foldl op cache directDependencies;
+            in
+            foldOverCache
+              (
+                cache: { packageId, features }:
+                  let
+                    cacheFeatures = cache.${packageId} or [ ];
+                    combinedFeatures = sortedUnique (cacheFeatures ++ features);
+                  in
+                  if cache ? ${packageId} && cache.${packageId} == combinedFeatures
+                  then cache
+                  else
+                    mergePackageFeatures {
+                      features = combinedFeatures;
+                      featuresByPackageId = cache;
+                      inherit crateConfigs packageId target runTests rootPackageId;
+                    }
+              );
+          cacheWithSelf =
+            let
+              cacheFeatures = featuresByPackageId.${packageId} or [ ];
+              combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
+            in
+            featuresByPackageId // {
+              "${packageId}" = combinedFeatures;
+            };
+          cacheWithDependencies =
+            resolveDependencies cacheWithSelf "dep"
+              (
+                crateConfig.dependencies or [ ]
+                ++ lib.optionals
+                  (runTests && packageId == rootPackageId)
+                  (crateConfig.devDependencies or [ ])
+              );
+          cacheWithAll =
+            resolveDependencies
+              cacheWithDependencies "build"
+              (crateConfig.buildDependencies or [ ]);
+        in
+        cacheWithAll;
+
+    /* Returns the enabled dependencies given the enabled features. */
+    filterEnabledDependencies = { dependencies, features, target }:
+      assert (builtins.isList dependencies);
+      assert (builtins.isList features);
+      assert (builtins.isAttrs target);
+
+      lib.filter
+        (
+          dep:
+          let
+            targetFunc = dep.target or (features: true);
+          in
+          targetFunc { inherit features target; }
+          && (
+            !(dep.optional or false)
+            || builtins.any (doesFeatureEnableDependency dep) features
+          )
+        )
+        dependencies;
+
+    /* Returns whether the given feature should enable the given dependency. */
+    doesFeatureEnableDependency = dependency: feature:
+      let
+        name = dependency.rename or dependency.name;
+        prefix = "${name}/";
+        len = builtins.stringLength prefix;
+        startsWithPrefix = builtins.substring 0 len feature == prefix;
+      in
+      feature == name || feature == "dep:" + name || startsWithPrefix;
+
+    /* Returns the expanded features for the given inputFeatures by applying the
+      rules in featureMap.
+
+      featureMap is an attribute set which maps feature names to lists of further
+      feature names to enable in case this feature is selected.
+    */
+    expandFeatures = featureMap: inputFeatures:
+      assert (builtins.isAttrs featureMap);
+      assert (builtins.isList inputFeatures);
+      let
+        expandFeaturesNoCycle = oldSeen: inputFeatures:
+          if inputFeatures != [ ]
+          then
+            let
+              # The feature we're currently expanding.
+              feature = builtins.head inputFeatures;
+              # All the features we've seen/expanded so far, including the one
+              # we're currently processing.
+              seen = oldSeen // { ${feature} = 1; };
+              # Expand the feature but be careful to not re-introduce a feature
+              # that we've already seen: this can easily cause a cycle, see issue
+              # #209.
+              enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or [ ]);
+            in
+            [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
+          # No more features left, nothing to expand to.
+          else [ ];
+        outFeatures = expandFeaturesNoCycle { } inputFeatures;
+      in
+      sortedUnique outFeatures;
+
+    /* This function adds optional dependencies as features if they are enabled
+      indirectly by dependency features. This function mimics Cargo's behavior
+      described in a note at:
+      https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
+    */
+    enableFeatures = dependencies: features:
+      assert (builtins.isList features);
+      assert (builtins.isList dependencies);
+      let
+        additionalFeatures = lib.concatMap
+          (
+            dependency:
+              assert (builtins.isAttrs dependency);
+              let
+                enabled = builtins.any (doesFeatureEnableDependency dependency) features;
+              in
+              if (dependency.optional or false) && enabled
+              then [ (dependency.rename or dependency.name) ]
+              else [ ]
+          )
+          dependencies;
+      in
+      sortedUnique (features ++ additionalFeatures);
+
+    /*
+      Returns the actual features for the given dependency.
+
+      features: The features of the crate that refers this dependency.
+    */
+    dependencyFeatures = features: dependency:
+      assert (builtins.isList features);
+      assert (builtins.isAttrs dependency);
+      let
+        defaultOrNil =
+          if dependency.usesDefaultFeatures or true
+          then [ "default" ]
+          else [ ];
+        explicitFeatures = dependency.features or [ ];
+        additionalDependencyFeatures =
+          let
+            name = dependency.rename or dependency.name;
+            stripPrefixMatch = prefix: s:
+              if lib.hasPrefix prefix s
+              then lib.removePrefix prefix s
+              else null;
+            extractFeature = feature: lib.findFirst
+              (f: f != null)
+              null
+              (map (prefix: stripPrefixMatch prefix feature) [
+                (name + "/")
+                (name + "?/")
+              ]);
+            dependencyFeatures = lib.filter (f: f != null) (map extractFeature features);
+          in
+          dependencyFeatures;
+      in
+      defaultOrNil ++ explicitFeatures ++ additionalDependencyFeatures;
+
+    /* Sorts and removes duplicates from a list of strings. */
+    sortedUnique = features:
+      assert (builtins.isList features);
+      assert (builtins.all builtins.isString features);
+      let
+        outFeaturesSet = lib.foldl (set: feature: set // { "${feature}" = 1; }) { } features;
+        outFeaturesUnique = builtins.attrNames outFeaturesSet;
+      in
+      builtins.sort (a: b: a < b) outFeaturesUnique;
+
+    deprecationWarning = message: value:
+      if strictDeprecation
+      then builtins.throw "strictDeprecation enabled, aborting: ${message}"
+      else builtins.trace message value;
+
+    #
+    # crate2nix/default.nix (excerpt end)
+    #
+  };
+}
+
diff --git a/users/picnoir/tvix-daemon/Cargo.toml b/users/picnoir/tvix-daemon/Cargo.toml
new file mode 100644
index 0000000000..2aca99f201
--- /dev/null
+++ b/users/picnoir/tvix-daemon/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "tvix-daemon"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+nix-compat = { path = "../../../tvix/nix-compat", features = ["wire"] }
+tokio-listener = "0.3.1"
+tokio = { version = "1.36.0", features = ["full"] }
+tracing-subscriber = "0.3.18"
+tracing = "0.1.40"
+clap = { version = "4.5.3", features = ["derive", "env"] }
+
+[dev-dependencies]
+tokio-test = "0.4.4"
diff --git a/users/picnoir/tvix-daemon/README.md b/users/picnoir/tvix-daemon/README.md
new file mode 100644
index 0000000000..1c65790a22
--- /dev/null
+++ b/users/picnoir/tvix-daemon/README.md
@@ -0,0 +1,16 @@
+# Tvix-daemon
+
+A **super** incomplete implementation of a Nix-compatible daemon. Same as the original except it's backed by Tvix-Store.
+
+For now, this is mostly used as a playground to implement the Nix daemon wire format in nix-compat.
+
+On the long run, I hope this to be useful to get some real-world usage experience of tvix-store.
+
+## Build
+
+When inside this directory:
+
+```sh
+mg shell :shell
+cargo build
+```
diff --git a/users/picnoir/tvix-daemon/default.nix b/users/picnoir/tvix-daemon/default.nix
new file mode 100644
index 0000000000..78b9aa9a1d
--- /dev/null
+++ b/users/picnoir/tvix-daemon/default.nix
@@ -0,0 +1,43 @@
+{ depot, pkgs, ... }:
+
+let
+  crate2nix = pkgs.callPackage ./Cargo.nix {
+    defaultCrateOverrides = {
+      tvix-castore = prev: {
+        PROTO_ROOT = depot.tvix.castore.protos.protos;
+        nativeBuildInputs = protobufDep prev;
+      };
+
+      tvix-store = prev: {
+        PROTO_ROOT = depot.tvix.store.protos.protos;
+        nativeBuildInputs = protobufDep prev;
+      };
+    };
+  };
+  protobufDep = prev: (prev.nativeBuildInputs or [ ]) ++ [ pkgs.buildPackages.protobuf ];
+in
+{
+  shell = (import ./shell.nix { inherit pkgs; });
+  tvix-daemon = crate2nix.rootCrate.build;
+  clippy = pkgs.stdenv.mkDerivation {
+    name = "tvix-daemon-clippy";
+    # The cleaned sources.
+    src = depot.third_party.gitignoreSource ./.;
+    cargoDeps = crate2nix.allWorkspaceMembers;
+
+    nativeBuildInputs = with pkgs; [
+      cargo
+      clippy
+      pkg-config
+      protobuf
+      rustc
+      rustPlatform.cargoSetupHook
+    ];
+
+    buildPhase = "cargo clippy --tests --all-features --benches --examples | tee $out";
+  };
+  meta.ci.targets = [
+    "tvix-daemon"
+    "shell"
+  ];
+}
diff --git a/users/picnoir/tvix-daemon/shell.nix b/users/picnoir/tvix-daemon/shell.nix
new file mode 100644
index 0000000000..6ec6b961fa
--- /dev/null
+++ b/users/picnoir/tvix-daemon/shell.nix
@@ -0,0 +1,21 @@
+{ pkgs, ... }:
+pkgs.mkShell {
+  name = "tvix-daemon";
+  packages = [
+    pkgs.cargo
+    pkgs.cargo-machete
+    pkgs.cargo-expand
+    pkgs.clippy
+    pkgs.evans
+    pkgs.fuse
+    pkgs.go
+    pkgs.grpcurl
+    pkgs.hyperfine
+    pkgs.nix_2_3 # b/313
+    pkgs.pkg-config
+    pkgs.rust-analyzer
+    pkgs.rustc
+    pkgs.rustfmt
+    pkgs.protobuf
+  ];
+}
diff --git a/users/picnoir/tvix-daemon/src/main.rs b/users/picnoir/tvix-daemon/src/main.rs
new file mode 100644
index 0000000000..dc49b209e0
--- /dev/null
+++ b/users/picnoir/tvix-daemon/src/main.rs
@@ -0,0 +1,116 @@
+use clap::Parser;
+use tokio::io::{AsyncReadExt, AsyncWriteExt};
+use tokio_listener::{self, SystemOptions, UserOptions};
+use tracing::{debug, error, info, instrument, Level};
+
+use nix_compat::worker_protocol::{self, server_handshake_client, ClientSettings, Trust};
+use nix_compat::ProtocolVersion;
+
+#[derive(Parser, Debug)]
+struct Cli {
+    /// Listening unix socket path
+    #[arg(short, long)]
+    socket: Option<String>,
+    /// Log verbosity level. Can be "error", "warn", "info", "debug", "trace", or a number 1-5
+    #[arg(short, long, env)]
+    verbosity: Option<Level>,
+}
+
+#[tokio::main]
+#[instrument()]
+async fn main() {
+    let args = Cli::parse();
+    tracing_subscriber::fmt()
+        .compact()
+        .with_max_level(
+            args.verbosity
+                .unwrap_or_else(|| panic!("Can't parse log verbosity")),
+        )
+        .try_init()
+        .unwrap();
+    info!("Started Tvix daemon");
+    let addr = args
+        .socket
+        .unwrap_or_else(|| "sd_listen_unix".to_string())
+        .parse()
+        .expect("Invalid listening socket address");
+    let system_options: SystemOptions = Default::default();
+    let mut user_options: UserOptions = Default::default();
+    user_options.recv_buffer_size = Some(1024);
+    user_options.send_buffer_size = Some(1024);
+    info!(user_options.send_buffer_size);
+    info!(user_options.recv_buffer_size);
+    let mut listener = tokio_listener::Listener::bind(&addr, &system_options, &user_options)
+        .await
+        .unwrap();
+    info!(listener_address = ?listener, "Listening for incoming connections");
+    while let Ok((conn, addr)) = listener.accept().await {
+        info!(addr = %addr, "Incoming connection");
+        tokio::spawn(async move { worker(conn).await });
+    }
+}
+
+/// Structure used to hold the client socket connection and some
+/// metadata about the connection.
+#[derive(Debug)]
+struct ClientConnection<R: AsyncReadExt + AsyncWriteExt + Unpin> {
+    pub conn: R,
+    pub version: ProtocolVersion,
+    pub client_settings: Option<ClientSettings>,
+}
+
+/// Worker in charge to respond a Nix client using the Nix wire
+/// protocol.
+#[instrument()]
+async fn worker<R>(mut conn: R)
+where
+    R: AsyncReadExt + AsyncWriteExt + Unpin + std::fmt::Debug,
+{
+    match server_handshake_client(&mut conn, "2.18.2", Trust::Trusted).await {
+        Ok(client_protocol_version) => {
+            let mut client_connection = ClientConnection {
+                conn,
+                version: client_protocol_version,
+                client_settings: None,
+            };
+            debug!("Client hanshake succeeded");
+            debug!(client_protocol_version = ?client_protocol_version);
+            // TODO: implement logging. For now, we'll just send
+            // STDERR_LAST, which is good enough to get Nix respond to
+            // us.
+            client_connection
+                .conn
+                .write_u64_le(worker_protocol::STDERR_LAST)
+                .await
+                .unwrap();
+            loop {
+                let op = worker_protocol::read_op(&mut client_connection.conn)
+                    .await
+                    .unwrap();
+                match op {
+                    worker_protocol::Operation::SetOptions => {
+                        let settings = op_set_options(&mut client_connection).await.unwrap();
+                        client_connection.client_settings = Some(settings);
+                        debug!(settings = ?client_connection.client_settings, "Received client settings");
+                    }
+                    _ => {
+                        error!(op = ?op, "Unimplemented operation");
+                        break;
+                    }
+                }
+            }
+        }
+        Err(e) => error!("Client handshake failed: {}", e),
+    }
+}
+
+async fn op_set_options<R>(conn: &mut ClientConnection<R>) -> std::io::Result<ClientSettings>
+where
+    R: AsyncReadExt + AsyncWriteExt + Unpin + std::fmt::Debug,
+{
+    let settings = worker_protocol::read_client_settings(&mut conn.conn, conn.version).await?;
+    // The client expects us to send some logs when we're processing
+    // the settings. Sending STDERR_LAST signal we're done processing.
+    conn.conn.write_u64_le(worker_protocol::STDERR_LAST).await?;
+    Ok(settings)
+}
diff --git a/users/picnoir/tvix-daemon/vm-test/README.md b/users/picnoir/tvix-daemon/vm-test/README.md
new file mode 100644
index 0000000000..bd7f14f7ef
--- /dev/null
+++ b/users/picnoir/tvix-daemon/vm-test/README.md
@@ -0,0 +1,5 @@
+# Integration VM Test
+
+This VM test fails for now. We use it to conveniently test our implementation on a real world Nix setup.
+
+For now, it only adds a new path to the store. It'll likely do more in the future.
diff --git a/users/picnoir/tvix-daemon/vm-test/default.nix b/users/picnoir/tvix-daemon/vm-test/default.nix
new file mode 100644
index 0000000000..e70690ee02
--- /dev/null
+++ b/users/picnoir/tvix-daemon/vm-test/default.nix
@@ -0,0 +1,28 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  nixosTestDrv = pkgs.nixosTest {
+    name = "tvix-daemon-vm-test";
+    nodes.machine = { config, pkgs, ... }: {
+      environment.systemPackages = [
+        (pkgs.writers.writeBashBin "poke-daemon" ''
+          NIX_REMOTE=unix:///nix/var/nix/daemon-socket/socket nix-instantiate -E '"''${/etc/nscd.conf}"'
+        '')
+      ];
+      systemd.services.nix-daemon.serviceConfig.ExecStart = [
+        ""
+        "${depot.users.picnoir.tvix-daemon.tvix-daemon}/bin/tvix-daemon"
+      ];
+
+    };
+    testScript = ''
+      machine.wait_for_unit("multi-user.target")
+      machine.succeed("poke-daemon")
+    '';
+  };
+in
+nixosTestDrv // {
+  # The test fails for now. TOREMOVE when we reach the stage where we
+  # can add stuff to the store.
+  meta.ci.skip = true;
+}
diff --git a/users/qyliss/OWNERS b/users/qyliss/OWNERS
index d54ea3622d..68724206af 100644
--- a/users/qyliss/OWNERS
+++ b/users/qyliss/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - qyliss
+set noparent
+
+qyliss
diff --git a/users/riking/OWNERS b/users/riking/OWNERS
deleted file mode 100644
index a39f4cd9f0..0000000000
--- a/users/riking/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-inherit: false
-owners:
- - riking
diff --git a/users/riking/adventofcode-2020/.gitignore b/users/riking/adventofcode-2020/.gitignore
deleted file mode 100644
index 076ff41215..0000000000
--- a/users/riking/adventofcode-2020/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*/target
-*/input.txt
diff --git a/users/riking/adventofcode-2020/day01/Cargo.lock b/users/riking/adventofcode-2020/day01/Cargo.lock
deleted file mode 100644
index a1a18948a7..0000000000
--- a/users/riking/adventofcode-2020/day01/Cargo.lock
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-[[package]]
-name = "anyhow"
-version = "1.0.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7"
-
-[[package]]
-name = "day01"
-version = "0.1.0"
-dependencies = [
- "anyhow",
-]
diff --git a/users/riking/adventofcode-2020/day01/default.nix b/users/riking/adventofcode-2020/day01/default.nix
deleted file mode 100644
index 0648a05af6..0000000000
--- a/users/riking/adventofcode-2020/day01/default.nix
+++ /dev/null
@@ -1,10 +0,0 @@
-{ depot, ... }:
-
-with depot.third_party;
-
-naersk.buildPackage {
-  src = ./.;
-
-  buildInputs = [];
-  doCheck = true;
-}
diff --git a/users/riking/adventofcode-2020/day01/src/main.rs b/users/riking/adventofcode-2020/day01/src/main.rs
deleted file mode 100644
index 3e6b339d7c..0000000000
--- a/users/riking/adventofcode-2020/day01/src/main.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use anyhow::anyhow;
-use std::fs::File;
-use std::io::prelude::*;
-use std::io::BufReader;
-
-const PART_2: bool = true;
-
-fn day01(is_part2: bool, numbers: &Vec<i64>) -> Result<String, anyhow::Error> {
-    //println!("{:?}", numbers);
-
-    for n1 in numbers.iter() {
-        for n2 in numbers.iter() {
-            if is_part2 {
-                for n3 in numbers.iter() {
-                    if n1 + n2 + n3 == 2020 {
-                        return Ok((n1 * n2 * n3).to_string());
-                    }
-                }
-            } else {
-                if n1 + n2 == 2020 {
-                    return Ok((n1 * n2).to_string());
-                }
-            }
-        }
-    }
-
-    Err(anyhow!("no solution found"))
-}
-
-fn parse(filename: &str) -> Result<Vec<i64>, anyhow::Error> {
-    let f = File::open(filename)?;
-    let mut reader = BufReader::new(f);
-
-    let mut values = Vec::<i64>::new();
-
-    let mut line = String::new();
-    loop {
-        line.clear();
-        reader.read_line(&mut line)?;
-        let trimmed_line = line.trim();
-        if trimmed_line.is_empty() {
-            break;
-        }
-
-        values.push(trimmed_line.parse()?);
-    }
-    Ok(values)
-}
-
-fn main() -> anyhow::Result<()> {
-    let args: Vec<String> = std::env::args().collect();
-
-    //println!("{:?}", args);
-    if args.len() != 2 {
-        return Err(anyhow!("usage: day01 input_file"));
-    }
-    let filename = args.into_iter().skip(1).next().expect("args len == 1");
-
-    let numbers = parse(&filename)?;
-
-    println!("{}", day01(PART_2, &numbers)?);
-
-    Ok(())
-}
-
-#[cfg(test)]
-mod tests {
-    use super::day01;
-
-    #[test]
-    fn test_part1() {
-        let vec = vec![1721, 979, 366, 299, 675, 1456];
-        let result = day01(false, &vec).unwrap();
-
-        assert_eq!(result, 514579.to_string());
-    }
-
-    #[test]
-    fn test_part2() {
-        let vec = vec![1721, 979, 366, 299, 675, 1456];
-        let result = day01(true, &vec).unwrap();
-
-        assert_eq!(result, 241861950.to_string());
-    }
-}
diff --git a/users/riking/dotfiles/.mybashrc b/users/riking/dotfiles/.mybashrc
deleted file mode 100644
index c5ebc34a1f..0000000000
--- a/users/riking/dotfiles/.mybashrc
+++ /dev/null
@@ -1,53 +0,0 @@
-
-# BEGIN: __USER_FUNCTIONS__
-function gh-clone() {
-	if [[ "x$2" == "x" ]]; then
-		IFS='/' read -ra PARTS <<< "$1"
-		user="${PARTS[0]}"
-		repo="${PARTS[1]}"
-	else
-		user="$1"
-		repo="$2"
-	fi
-	if [[ -d ~/go/src/github.com/"$user"/"$repo" ]]; then
-		cd ~/go/src/github.com/"${user}"/"${repo}"
-		return 0
-	fi
-	mkdir -p ~/go/src/github.com/"${user}"
-	cd ~/go/src/github.com/"${user}"
-	git clone git@github.com:"${user}"/"${repo}".git
-	cd ~/go/src/github.com/"${user}"/"${repo}"
-}
-
-function download() {
-	cd "${HOME}/Downloads"
-	wget "$@"
-}
-
-# todo: only one password pls
-function prodaccess() {
-	(ssh-add -L | grep -q 'ZgEu6S3SLatYN') || ssh-add "$HOME"/.ssh/id_ed25519
-	(ssh-add -L | grep -q 'Gfh2S3kUwZ8A6') || ssh-add "$HOME"/.ssh/id_rsa.discourse
-	echo "signing test" | gpg --clearsign > /dev/null
-}
-
-function reset-audio() {
-	pulseaudio -k && sudo alsa force-reload
-}
-
-function tvl-push() {
-	git push origin HEAD:refs/for/canon
-}
-
-# END: __USER_FUNCTIONS__
-
-# BEGIN: __USER_ENV__
-GOPATH=$HOME/go
-CDPATH=$HOME/go/src
-export GPG_TTY="$(tty)"
-
-export PATH="/usr/local/go/bin:$HOME/go/bin:$HOME/.rbenv/bin:$PATH"
-
-eval "$(rbenv init -)"
-# END: __USER_ENV__
-
diff --git a/users/riking/dotfiles/fish/conf.d/nix-env.fish b/users/riking/dotfiles/fish/conf.d/nix-env.fish
deleted file mode 100644
index 6f79f97528..0000000000
--- a/users/riking/dotfiles/fish/conf.d/nix-env.fish
+++ /dev/null
@@ -1,141 +0,0 @@
-# SPDX-License-Identifier: Unlicense
-# https://raw.githubusercontent.com/lilyball/nix-env.fish/master/conf.d/nix-env.fish
-
-# Setup Nix
-
-# We need to distinguish between single-user and multi-user installs.
-# This is difficult because there's no official way to do this.
-# We could look for the presence of /nix/var/nix/daemon-socket/socket but this will fail if the
-# daemon hasn't started yet. /nix/var/nix/daemon-socket will exist if the daemon has ever run, but
-# I don't think there's any protection against accidentally running `nix-daemon` as a user.
-# We also can't just look for /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh because
-# older single-user installs used the default profile instead of a per-user profile.
-# We can still check for it first, because all multi-user installs should have it, and so if it's
-# not present that's a pretty big indicator that this is a single-user install. If it does exist,
-# we still need to verify the install type. To that end we'll look for a root owner and sticky bit
-# on /nix/store. Multi-user installs set both, single-user installs don't. It's certainly possible
-# someone could do a single-user install as root and then manually set the sticky bit but that
-# would be extremely unusual.
-
-set -l nix_profile_path /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
-set -l single_user_profile_path ~/.nix-profile/etc/profile.d/nix.sh
-if test -e $nix_profile_path
-  # The path exists. Double-check that this is a multi-user install.
-  # We can't just check for ~/.nix-profile/โ€ฆ because this may be a single-user install running as
-  # the wrong user.
-
-  # stat is not portable. Splitting the output of ls -nd is reliable on most platforms.
-  set -l owner (string split -n ' ' (ls -nd /nix/store 2>/dev/null))[3]
-  if not test -k /nix/store -a $owner -eq 0
-    # /nix/store is either not owned by root or not sticky. Assume single-user.
-    set nix_profile_path $single_user_profile_path
-  end
-else
-  # The path doesn't exist. Assume single-user
-  set nix_profile_path $single_user_profile_path
-end
-
-if test -e $nix_profile_path
-  # Source the nix setup script
-  # We're going to run the regular Nix profile under bash and then print out a few variables
-  for line in (env -u BASH_ENV bash -c '. "$0"; for name in PATH "${!NIX_@}"; do printf "%s=%s\0" "$name" "${!name}"; done' $nix_profile_path | string split0)
-    set -xg (string split -m 1 = $line)
-  end
-
-  # Insert Nix's fish share directories into fish's special variables.
-  # nixpkgs-installed fish tries to set these up already if NIX_PROFILES is defined, which won't
-  # be the case when sourcing $__fish_data_dir/share/config.fish normally, but might be for a
-  # recursive invocation. To guard against that, we'll only insert paths that don't already exit.
-  # Furthermore, for the vendor_conf.d sourcing, we'll use the pre-existing presence of a path in
-  # $fish_function_path to determine whether we want to source the relevant vendor_conf.d folder.
-
-  # To start, let's locally define NIX_PROFILES if it doesn't already exist.
-  set -al NIX_PROFILES
-  if test (count $NIX_PROFILES) -eq 0
-    set -a NIX_PROFILES $HOME/.nix-profile
-  end
-  # Replicate the logic from nixpkgs version of $__fish_data_dir/__fish_build_paths.fish.
-  set -l __nix_profile_paths (string split ' ' -- $NIX_PROFILES)[-1..1]
-  set -l __extra_completionsdir \
-    $__nix_profile_paths/etc/fish/completions \
-    $__nix_profile_paths/share/fish/vendor_completions.d
-  set -l __extra_functionsdir \
-    $__nix_profile_paths/etc/fish/functions \
-    $__nix_profile_paths/share/fish/vendor_functions.d
-  set -l __extra_confdir \
-    $__nix_profile_paths/etc/fish/conf.d \
-    $__nix_profile_paths/share/fish/vendor_conf.d \
-
-  ### Configure fish_function_path ###
-  # Remove any of our extra paths that may already exist.
-  # Record the equivalent __extra_confdir path for any function path that exists.
-  set -l existing_conf_paths
-  for path in $__extra_functionsdir
-    if set -l idx (contains --index -- $path $fish_function_path)
-      set -e fish_function_path[$idx]
-      set -a existing_conf_paths $__extra_confdir[(contains --index -- $path $__extra_functionsdir)]
-    end
-  end
-  # Insert the paths before $__fish_data_dir.
-  if set -l idx (contains --index -- $__fish_data_dir/functions $fish_function_path)
-    # Fish has no way to simply insert into the middle of an array.
-    set -l new_path $fish_function_path[1..$idx]
-    set -e new_path[$idx]
-    set -a new_path $__extra_functionsdir
-    set fish_function_path $new_path $fish_function_path[$idx..-1]
-  else
-    set -a fish_function_path $__extra_functionsdir
-  end
-
-  ### Configure fish_complete_path ###
-  # Remove any of our extra paths that may already exist.
-  for path in $__extra_completionsdir
-    if set -l idx (contains --index -- $path $fish_complete_path)
-      set -e fish_complete_path[$idx]
-    end
-  end
-  # Insert the paths before $__fish_data_dir.
-  if set -l idx (contains --index -- $__fish_data_dir/completions $fish_complete_path)
-    set -l new_path $fish_complete_path[1..$idx]
-    set -e new_path[$idx]
-    set -a new_path $__extra_completionsdir
-    set fish_complete_path $new_path $fish_complete_path[$idx..-1]
-  else
-    set -a fish_complete_path $__extra_completionsdir
-  end
-
-  ### Source conf directories ###
-  # The built-in directories were already sourced during shell initialization.
-  # Any __extra_confdir that came from $__fish_data_dir/__fish_build_paths.fish was also sourced.
-  # As explained above, we're using the presence of pre-existing paths in $fish_function_path as a
-  # signal that the corresponding conf dir has also already been sourced.
-  # In order to simulate this, we'll run through the same algorithm as found in
-  # $__fish_data_dir/config.fish except we'll avoid sourcing the file if it comes from an
-  # already-sourced location.
-  # Caveats:
-  # * Files will be sourced in a different order than we'd ideally do (because we're coming in
-  #   after the fact to source them).
-  # * If there are existing extra conf paths, files in them may have been sourced that should have
-  #   been suppressed by paths we're inserting in front.
-  # * Similarly any files in $__fish_data_dir/vendor_conf.d that should have been suppressed won't
-  #   have been.
-  set -l sourcelist
-  for file in $__fish_config_dir/conf.d/*.fish $__fish_sysconf_dir/conf.d/*.fish
-    # We know these paths were sourced already. Just record them.
-    set -l basename (string replace -r '^.*/' '' -- $file)
-    contains -- $basename $sourcelist
-    or set -a sourcelist $basename
-  end
-  for root in $__extra_confdir
-    for file in $root/*.fish
-      set -l basename (string replace -r '^.*/' '' -- $file)
-      contains -- $basename $sourcelist
-      and continue
-      set -a sourcelist $basename
-      contains -- $root $existing_conf_paths
-      and continue # this is a pre-existing path, it will have been sourced already
-      [ -f $file -a -r $file ]
-      and source $file
-    end
-  end
-end
diff --git a/users/riking/dotfiles/fish/config.fish b/users/riking/dotfiles/fish/config.fish
deleted file mode 100644
index c2454762bd..0000000000
--- a/users/riking/dotfiles/fish/config.fish
+++ /dev/null
@@ -1,8 +0,0 @@
-set -gx GOPATH "$HOME/go"
-set -gx GPG_TTY (tty)
-set -gx DEPOT_ROOT "$GOPATH/src/code.tvl.fyi"
-
-set -gx PATH '/usr/local/go/bin' "$HOME/.cargo/bin" "$HOME/.rbenv/bin" $PATH
-status --is-interactive; and rbenv init - | source
-source ~/.opsrc.fish # work
-set -gx PATH "$HOME/go/bin" $PATH
diff --git a/users/riking/dotfiles/fish/fish_variables b/users/riking/dotfiles/fish/fish_variables
deleted file mode 100644
index fa8bff919f..0000000000
--- a/users/riking/dotfiles/fish/fish_variables
+++ /dev/null
@@ -1,32 +0,0 @@
-# This file contains fish universal variable definitions.
-# VERSION: 3.0
-SETUVAR __fish_initialized:3100
-SETUVAR fish_color_autosuggestion:555\x1ebrblack
-SETUVAR fish_color_cancel:\x2dr
-SETUVAR fish_color_command:005fd7
-SETUVAR fish_color_comment:990000
-SETUVAR fish_color_cwd:green
-SETUVAR fish_color_cwd_root:red
-SETUVAR fish_color_end:009900
-SETUVAR fish_color_error:ff0000
-SETUVAR fish_color_escape:00a6b2
-SETUVAR fish_color_history_current:\x2d\x2dbold
-SETUVAR fish_color_host:normal
-SETUVAR fish_color_host_remote:yellow
-SETUVAR fish_color_match:\x2d\x2dbackground\x3dbrblue
-SETUVAR fish_color_normal:normal
-SETUVAR fish_color_operator:00a6b2
-SETUVAR fish_color_param:00afff
-SETUVAR fish_color_quote:999900
-SETUVAR fish_color_redirection:00afff
-SETUVAR fish_color_search_match:bryellow\x1e\x2d\x2dbackground\x3dbrblack
-SETUVAR fish_color_selection:white\x1e\x2d\x2dbold\x1e\x2d\x2dbackground\x3dbrblack
-SETUVAR fish_color_status:red
-SETUVAR fish_color_user:brgreen
-SETUVAR fish_color_valid_path:\x2d\x2dunderline
-SETUVAR fish_greeting:Welcome\x20to\x20fish\x2c\x20the\x20friendly\x20interactive\x20shell\x0aType\x20\x60help\x60\x20for\x20instructions\x20on\x20how\x20to\x20use\x20fish
-SETUVAR fish_key_bindings:fish_default_key_bindings
-SETUVAR fish_pager_color_completion:\x1d
-SETUVAR fish_pager_color_description:B3A06D\x1eyellow
-SETUVAR fish_pager_color_prefix:white\x1e\x2d\x2dbold\x1e\x2d\x2dunderline
-SETUVAR fish_pager_color_progress:brwhite\x1e\x2d\x2dbackground\x3dcyan
diff --git a/users/riking/dotfiles/fish/functions/ddate.fish b/users/riking/dotfiles/fish/functions/ddate.fish
deleted file mode 100644
index 8152d31680..0000000000
--- a/users/riking/dotfiles/fish/functions/ddate.fish
+++ /dev/null
@@ -1,3 +0,0 @@
-function ddate --description 'current date in Discourse format'
-    TZ=UTC date '+[date=%Y-%m-%d time=%H:%M:%S timezone=\"%Z\"]'
-end
diff --git a/users/riking/dotfiles/fish/functions/gh-clone.fish b/users/riking/dotfiles/fish/functions/gh-clone.fish
deleted file mode 100644
index 109ec353f6..0000000000
--- a/users/riking/dotfiles/fish/functions/gh-clone.fish
+++ /dev/null
@@ -1,18 +0,0 @@
-function gh-clone --description 'Clone and CD to a github repository'
-    if test (count $argv) -eq 1
-        set user (string split "/" -- $argv[1])[1]
-        set repo (string split "/" -- $argv[1])[2]
-    else
-        set user $argv[1]
-        set repo $argv[2]
-    end
-
-    if test -d "$HOME/go/src/github.com/$user/$repo"
-        cd "$HOME/go/src/github.com/$user/$repo"
-        return 0
-    end
-    mkdir -p "$HOME/go/src/github.com/$user"
-    cd "$HOME/go/src/github.com/$user"
-    git clone "git@github.com:$user/$repo.git"
-    cd "$HOME/go/src/github.com/$user/$repo"
-end
diff --git a/users/riking/dotfiles/fish/functions/prodaccess.fish b/users/riking/dotfiles/fish/functions/prodaccess.fish
deleted file mode 100644
index 876c14c5e3..0000000000
--- a/users/riking/dotfiles/fish/functions/prodaccess.fish
+++ /dev/null
@@ -1,6 +0,0 @@
-function prodaccess
-    ssh-add "$HOME/.ssh/id_ecdsa_sk"
-    begin; ssh-add -L | grep -q 'ZgEu6S3SLatYN'; end || ssh-add "$HOME"/.ssh/id_ed25519
-    begin; ssh-add -L | grep -q 'Gfh2S3kUwZ8A6'; end || ssh-add "$HOME"/.ssh/id_rsa.discourse
-    echo "signing test" | gpg --clearsign > /dev/null
-end
diff --git a/users/riking/dotfiles/fish/functions/reset-audio.fish b/users/riking/dotfiles/fish/functions/reset-audio.fish
deleted file mode 100644
index eb48578a52..0000000000
--- a/users/riking/dotfiles/fish/functions/reset-audio.fish
+++ /dev/null
@@ -1,4 +0,0 @@
-function reset-audio --description "Resets pulse and alsa"
-    pulseaudio -k
-    sudo alsa force-reload
-end
diff --git a/users/riking/dotfiles/fish/functions/tvl-push.fish b/users/riking/dotfiles/fish/functions/tvl-push.fish
deleted file mode 100644
index f04ac830c0..0000000000
--- a/users/riking/dotfiles/fish/functions/tvl-push.fish
+++ /dev/null
@@ -1,3 +0,0 @@
-function tvl-push
-    git push origin HEAD:refs/for/canon
-end
diff --git a/users/riking/dotfiles/regolith/Xresources b/users/riking/dotfiles/regolith/Xresources
deleted file mode 100644
index f47b93511a..0000000000
--- a/users/riking/dotfiles/regolith/Xresources
+++ /dev/null
@@ -1,5 +0,0 @@
-#include "/etc/regolith/styles/ubuntu/root"
-
-i3-wm.program.lock: xset s activate
-i3-wm.program.1: /bin/sh $HOME/.config/regolith/initrc
-
diff --git a/users/riking/dotfiles/regolith/flags/ui-fingerprint b/users/riking/dotfiles/regolith/flags/ui-fingerprint
deleted file mode 100644
index b35aedd2dc..0000000000
--- a/users/riking/dotfiles/regolith/flags/ui-fingerprint
+++ /dev/null
@@ -1 +0,0 @@
-ec33ee15ff705ac4b167ba6b7f6df3c2
diff --git a/users/riking/dotfiles/regolith/initrc b/users/riking/dotfiles/regolith/initrc
deleted file mode 100755
index 9b14613cd4..0000000000
--- a/users/riking/dotfiles/regolith/initrc
+++ /dev/null
@@ -1,3 +0,0 @@
-
-xset s 900 5
-( xss-lock -n /usr/lib/xsecurelock/dimmer -l -- sh -c "XSECURELOCK_PASSWORD_PROMPT=time_hex XSECURELOCK_SHOW_DATETIME=1 XSECURELOCK_SAVER=saver_mpv XSECURELOCK_IMAGE_DURATION_SECONDS=10 XSECURELOCK_LIST_VIDEOS_COMMAND='find ~/Videos/Screensaver -type f' xsecurelock" )&
diff --git a/users/riking/dotfiles/tmux.conf b/users/riking/dotfiles/tmux.conf
deleted file mode 100644
index 1f253cb27f..0000000000
--- a/users/riking/dotfiles/tmux.conf
+++ /dev/null
@@ -1,6 +0,0 @@
-
-set -g mouse on
-set-option -g prefix C-a
-bind-key C-a send-prefix
-bind | split-window -h
-bind - split-window -v
diff --git a/users/riking/keys.nix b/users/riking/keys.nix
deleted file mode 100644
index 6dd2ff18a3..0000000000
--- a/users/riking/keys.nix
+++ /dev/null
@@ -1,20 +0,0 @@
-# SSH public keys
-{ ... }:
-
-rec {
-  sk-ecljg09 = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBBwJ7dJJUkvIK+bDsVsCsCZSlbs90aOLsHN7XesC8/AmLA5rIRLO8I5ADoOjsWAXl/WAgxqOMmB4LxZjoXWa1a0AAAAEc3NoOg== riking@sk-ECLJG09";
-  sk-portable1 = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBCfA8/0nKk4jXclWHjRZIuicPeyIo9oDwahpnWjEATr7YaFDAo632KTSgqlW0lpx8lX9alLsJRhFV2XaSurYw/EAAAAEc3NoOg== riking@sk-portable1";
-  sk-portable2 = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBEX3DXreQR93SR68QZHTdaVd5RjlRM8C0jcZ4kI4OZwqk7xuk68w3g22q2OM7O+chj+n1N3u0hLxi82QfRnwyasAAAAEc3NoOg== riking@sk-portable2";
-  sk-desktop = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBB+JvN8nAxD+yo49Ohf/UDq7Z049yvkURJIA1XNbvKaAkvfWnCN5m9vTC1FyGxTyCwy4QpD1pFP5fIn0X/kvvfgAAAAEc3NoOg== riking@sk-kane-DAN-A4";
-
-  u2f = [sk-ecljg09 sk-portable1 sk-portable2 sk-desktop];
-
-  ed1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAjWIfFH2bAWMZG+HudV1MVHWUl83M/ZgEu6S3SLatYN riking@kane-DAN-A4";
-  ed2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICBblB4C9IgAijv+qN6Zs8TM2Sz7phQvVmRrcDn4VYNo riking@ECLJG09";
-
-  passworded = [ed1 ed2];
-
-  unprotected = [];
-
-  all = u2f ++ passworded ++ unprotected;
-}
diff --git a/users/sterni/OWNERS b/users/sterni/OWNERS
index cace4d0f37..6434d4ca30 100644
--- a/users/sterni/OWNERS
+++ b/users/sterni/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - sterni
+set noparent
+
+sterni
diff --git a/users/sterni/clhs-lookup/default.nix b/users/sterni/clhs-lookup/default.nix
index b6a0bd0679..1cde38e8ce 100644
--- a/users/sterni/clhs-lookup/default.nix
+++ b/users/sterni/clhs-lookup/default.nix
@@ -36,4 +36,4 @@ let
     ];
   };
 in
-  clhs-lookup
+clhs-lookup
diff --git a/users/sterni/dot-time-man-pages/OWNERS b/users/sterni/dot-time-man-pages/OWNERS
index 980c17b424..b9bc074a80 100644
--- a/users/sterni/dot-time-man-pages/OWNERS
+++ b/users/sterni/dot-time-man-pages/OWNERS
@@ -1,3 +1 @@
-inherited: true
-owners:
-  - edef
+edef
diff --git a/users/sterni/dot-time-man-pages/default.nix b/users/sterni/dot-time-man-pages/default.nix
index bf7d63dbd7..c449cde613 100644
--- a/users/sterni/dot-time-man-pages/default.nix
+++ b/users/sterni/dot-time-man-pages/default.nix
@@ -9,9 +9,9 @@ let
       res = builtins.filter ({ username, ... }: username == user) depot.ops.users;
       len = builtins.length res;
     in
-      if len == 1
-      then (builtins.head res).email
-      else builtins.throw "findEmail: got ${toString len} results instead of 1";
+    if len == 1
+    then (builtins.head res).email
+    else builtins.throw "findEmail: got ${toString len} results instead of 1";
 
   # dot-time(7) man page, ported from dotti.me
   dot-time = rec {
@@ -65,6 +65,6 @@ let
   };
 
 in
-  depot.nix.buildManPages "dot-time" {} [
-    dot-time
-  ]
+depot.nix.buildManPages "dot-time" { } [
+  dot-time
+]
diff --git a/users/sterni/emacs/default.nix b/users/sterni/emacs/default.nix
index f7bdf21a02..9d057fef63 100644
--- a/users/sterni/emacs/default.nix
+++ b/users/sterni/emacs/default.nix
@@ -1,43 +1,110 @@
-{ depot, pkgs, ... }:
+{ depot, pkgs, lib, ... }:
 
 let
-  inherit (pkgs.emacsGcc.pkgs) withPackages;
+  inherit (pkgs.stdenv.hostPlatform) is64bit;
 
-  emacs = withPackages (epkgs: [
-    # basic setup
-    epkgs.elpaPackages.undo-tree
-    epkgs.melpaPackages.evil
-    epkgs.melpaPackages.evil-collection
-    epkgs.melpaPackages.use-package
-    # languages
+  # Wrap chktex(1) with the flags we want because the chktex flycheck checker
+  # ignores tex-chktex-extra-flags and has no other way to set flags. I did
+  # not want to mess around with chktexrc because that seems to involve copying
+  # around a lot of rules (that would need to be updated?).
+  #
+  # Warning 8 is about correct dash length. This is really annoying because it'll
+  # light up everywhere if you use typographically correct dashes in German text.
+  chktexLessWarnings = pkgs.writeShellScript "chktex-less-warnings" ''
+    exec chktex -n8 "$@"
+  '';
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs29-pgtk).withPackages (epkgs: [
     epkgs.bqn-mode
-    epkgs.elpaPackages.ada-mode
+    #epkgs.elpaPackages.ada-mode
+    epkgs.elpaPackages.rainbow-mode
+    epkgs.elpaPackages.undo-tree
+    epkgs.elpaPackages.which-key
     epkgs.melpaPackages.adoc-mode
+    epkgs.melpaPackages.cmake-mode
+    epkgs.melpaPackages.deft
+    epkgs.melpaPackages.direnv
     epkgs.melpaPackages.dockerfile-mode
+    epkgs.melpaPackages.editorconfig
+    epkgs.melpaPackages.elfeed
+    epkgs.melpaPackages.evil
+    epkgs.melpaPackages.evil-collection
+    epkgs.melpaPackages.flycheck
     epkgs.melpaPackages.haskell-mode
+    epkgs.melpaPackages.hl-todo
     epkgs.melpaPackages.jq-mode
+    epkgs.melpaPackages.lsp-haskell
+    epkgs.melpaPackages.lsp-mode
+    epkgs.melpaPackages.lsp-ui
+    epkgs.melpaPackages.magit
     epkgs.melpaPackages.markdown-mode
+    epkgs.melpaPackages.meson-mode
     epkgs.melpaPackages.nix-mode
+    epkgs.melpaPackages.org-clock-csv
+    epkgs.melpaPackages.paredit
+    epkgs.melpaPackages.rainbow-delimiters
     epkgs.melpaPackages.sly
+    epkgs.melpaPackages.use-package
     epkgs.melpaPackages.yaml-mode
     epkgs.rust-mode
-    epkgs.urweb-mode
-    # misc
-    epkgs.melpaPackages.hl-todo
-    epkgs.elpaPackages.rainbow-mode
-    epkgs.melpaPackages.rainbow-delimiters
-    # beyond text editing
-    epkgs.melpaPackages.elfeed
-    epkgs.melpaPackages.magit
     epkgs.tvlPackages.tvl
+    epkgs.urweb-mode
+  ] ++ lib.optionals is64bit [
+    epkgs.melpaPackages.languagetool
   ]);
+
+  configDirectory = pkgs.symlinkJoin {
+    name = "emacs.d";
+    paths = [
+      ./.
+      (pkgs.writeTextFile {
+        name = "injected-emacs.d";
+        destination = "/nix-inject.el";
+        text =
+          # Java doesn't seem to be available for non 64bit platforms in nixpkgs
+          # CBQN doesn't seem to support i686 at least
+          lib.optionalString is64bit ''
+            ;; bqn-mode
+            (setq bqn-interpreter-path "${pkgs.cbqn}/bin/BQN")
+
+            ;; languagetool
+            (setq languagetool-java-bin "${pkgs.jre}/bin/java"
+                  languagetool-console-command "${pkgs.languagetool}/share/languagetool-commandline.jar"
+                  languagetool-server-command "${pkgs.languagetool}/share/languagetool-server.jar")
+          '' + ''
+
+            ;; use bash instead of fish from SHELL for some things, as it plays
+            ;; nicer with TERM=dumb, as I don't need/want vterm anyways.
+            ;; We want it to source /etc/profile for some extra setup that
+            ;; kicks in if TERM=dumb, meaning we can't use dash/sh mode.
+            (setq shell-file-name "${pkgs.bash}/bin/bash"
+                  explicit-bash-args '("-l"))
+
+            ;; chktex wrapper that disables warnings I don't want
+            (setq flycheck-tex-chktex-executable "${chktexLessWarnings}")
+            (setq tex-chktex-program "${chktexLessWarnings}")
+
+            (provide 'nix-inject)
+        '';
+      })
+    ];
+    postBuild = ''
+      rm "$out/default.nix"
+    '';
+  };
 in
 
 # sadly we can't give an init-file via the command line
-pkgs.writeShellScriptBin "emacs" ''
-  exec ${emacs}/bin/emacs     \
-    --no-init-file            \
-    --directory ${./.}        \
-    --eval "(require 'init)" \
+(pkgs.writeShellScriptBin "emacs" ''
+  exec ${emacs}/bin/emacs          \
+    --no-init-file                 \
+    --directory ${configDirectory} \
+    --eval "(require 'init)"       \
     "$@"
-''
+'').overrideAttrs (super: {
+  buildCommand = ''
+    ${super.buildCommand}
+
+    ln -s "${emacs}/bin/emacsclient" "$out/bin/emacsclient"
+  '';
+})
diff --git a/users/sterni/emacs/init.el b/users/sterni/emacs/init.el
index 4b868cb242..4cb741f62d 100644
--- a/users/sterni/emacs/init.el
+++ b/users/sterni/emacs/init.el
@@ -1,5 +1,9 @@
+;; set up package infrastructure
+
+(require 'use-package)
+(package-initialize)
+
 ;; Set default font and fallback font via set-fontset-font
-;; TODO(sterni): Investigate non-emoji representation of some glyphs
 (let ((mono-font "Bitstream Vera Sans Mono-12")
       (emoji-font "Noto Color Emoji-12"))
   (setq default-frame-alist `((font . ,mono-font)))
@@ -14,8 +18,9 @@
 (global-auto-revert-mode 1)
 
 ;; Indent
-(set-default 'indent-tabs-mode nil)
-(setq tab-width 2)
+(setq-default indent-tabs-mode nil)
+(setq tab-width 2
+      css-indent-offset tab-width)
 
 ;; UTF-8
 (setq locale-coding-system 'utf-8)
@@ -44,16 +49,37 @@
   (mouse-wheel-mode t)
   (blink-cursor-mode -1))
 
-;; TODO(sterni): prevent some remaining backup files
+;; /tmp is a tmpfs, but we may want to recover from power loss
+(custom-set-variables
+ `(temporary-file-directory ,(concat (getenv "HOME") "/.emacs/tmp")))
+
 (setq auto-save-file-name-transforms
       `((".*" ,temporary-file-directory t)))
 (setq backup-directory-alist
       `((".*" . ,temporary-file-directory)))
+(setq undo-tree-history-directory-alist
+      `((".*" . ,temporary-file-directory)))
+(setq backup-by-copying t)
+(setq create-lockfiles nil)
+
+;; save history
+(savehist-mode)
+(setq savehist-additional-variables '(search-ring regexp-search-ring magit-cl-history))
 
 ;; buffers
+
+;; performance migitations
+(global-so-long-mode)
+
 ;; unique component should come first for better completion
 (setq uniquify-buffer-name-style 'forward)
 
+;; completions
+(ido-mode 1)
+(setq ido-enable-flex-matching t)
+(ido-everywhere)
+(fido-mode)
+
 ;; Display column numbers
 (column-number-mode t)
 (setq-default fill-column 80)
@@ -64,6 +90,9 @@
 (setq whitespace-style '(face trailing tabs)
       whitespace-line-column fill-column)
 (add-hook 'prog-mode-hook #'whitespace-mode)
+(setq-default indicate-empty-lines t)
+(setq-default indicate-buffer-boundaries 'left)
+(setq sentence-end-double-space nil)
 
 ;;; Configure built in modes
 
@@ -72,14 +101,63 @@
 (setq perl-continued-statement-offset 0)
 (setq perl-continued-brace-offset 0)
 
-;;; Configure packages
-(require 'use-package)
+;; org mode
 
-(package-initialize)
+(setq org-clock-persist 'history)
+(org-clock-persistence-insinuate)
+
+(let ((org-folder (concat (getenv "HOME") "/files/sync/org")))
+  (setq org-agenda-files (directory-files-recursively org-folder "\\.org$")
+        org-default-notes-file (concat org-folder "/inbox.org")
+        initial-buffer-choice org-default-notes-file
+        org-refile-targets '((org-agenda-files . (:maxlevel . 2)))))
+
+;; latex
+
+(defun latex-word-count ()
+  "Calls texcount on the file the current buffer points to and displays the result."
+  (interactive)
+  (save-buffer)
+  (let* ((file (buffer-file-name)) ; needs to happen outside with-current-buffer
+         (word-count
+             (with-output-to-string
+               (with-current-buffer standard-output
+                 (call-process "texcount" nil t nil "-brief" "-utf8" file)))))
+      (message (string-trim-right word-count))))
+
+;; ediff
+;; doesn't create new window for ediff controls which I always open accidentally
+(setq ediff-window-setup-function 'ediff-setup-windows-plain)
+
+;; man
+(setq Man-notify-method 'pushy) ; display man page in current window
+
+;; shell
 
+; default, but allows ';' as prompt
+(setq shell-prompt-pattern "^[^#$%>;\n]*[#$%>;] *")
+
+;; projects (see also evil config)
+
+(defun project-magit ()
+  "Run magit in the current project dir"
+  (interactive)
+  (magit (project-root (project-current t))))
+
+(define-key project-prefix-map (kbd "G") 'project-magit)
+
+(setq project-switch-commands
+      '((project-find-file "Find file")
+        (project-find-regexp "Find regexp")
+        (project-dired "Dired")
+        (project-shell "Shell")
+        (project-magit "Magit")))
+
+;;; Configure packages
 (use-package undo-tree
   :config
-  (global-undo-tree-mode))
+  (global-undo-tree-mode)
+  (setq undo-tree-auto-save-history t))
 
 (use-package magit
   :after evil
@@ -88,7 +166,9 @@
   ; gerrit doesn't like 80 column commit messagesโ€ฆ
   (add-hook 'git-commit-mode-hook (lambda () (setq fill-column 72)))
   (evil-define-key 'normal 'global (kbd "<leader>gr") 'magit-status))
-(use-package tvl :after magit)
+(use-package tvl
+  :after magit
+  :custom tvl-depot-path (concat (getenv "HOME") "/src/depot"))
 
 (setq ediff-split-window-function 'split-window-horizontally)
 
@@ -107,45 +187,107 @@
   ;; buffer management
   (evil-define-key 'normal 'global (kbd "<leader>bk") 'kill-buffer)
   (evil-define-key 'normal 'global (kbd "<leader>bb") 'switch-to-buffer)
-  ;; window management
-  (evil-define-key 'normal 'global (kbd "<leader>wk") 'delete-window)
-  (evil-define-key 'normal 'global (kbd "<leader>wo") 'delete-other-window)
-  (evil-define-key 'normal 'global (kbd "<leader>wh") 'split-window-below)
-  (evil-define-key 'normal 'global (kbd "<leader>wv") 'split-window-right)
-  (evil-define-key 'normal 'global (kbd "<leader>ww") 'other-window)
+  (evil-define-key 'normal 'global (kbd "<leader>bo") 'switch-to-buffer-other-window)
+  (evil-define-key 'normal 'global (kbd "<leader>bl") 'list-buffers)
+  (evil-define-key 'normal 'global (kbd "<leader>br") 'revert-buffer)
+  ;; window management: C-w hjkl is annoying in neo
+  (define-key evil-window-map (kbd "<left>") 'evil-window-left)
+  (define-key evil-window-map (kbd "<right>") 'evil-window-right)
+  (define-key evil-window-map (kbd "<up>") 'evil-window-up)
+  (define-key evil-window-map (kbd "<down>") 'evil-window-down)
+  ;; projects
+  (evil-define-key 'normal 'global (kbd "<leader>pf") 'project-find-file)
+  (evil-define-key 'normal 'global (kbd "<leader>pg") 'project-find-regexp)
+  (evil-define-key 'normal 'global (kbd "<leader>pd") 'project-dired)
+  (evil-define-key 'normal 'global (kbd "<leader>ps") 'project-shell)
+  (evil-define-key 'normal 'global (kbd "<leader>pR") 'project-query-replace-regexp)
+  (evil-define-key 'normal 'global (kbd "<leader>pK") 'project-kill-buffers)
+  (evil-define-key 'normal 'global (kbd "<leader>pp") 'project-switch-project)
   ;; emacs
   (evil-define-key 'visual 'global (kbd "<leader>ee") 'eval-region)
   (evil-define-key 'normal 'global (kbd "<leader>ee") 'eval-last-sexp)
   (evil-define-key 'normal 'global (kbd "<leader>ep") 'eval-print-last-sexp)
   (evil-define-key 'normal 'global (kbd "<leader>eh") 'help)
+  (evil-define-key 'normal 'global (kbd "<leader>em") 'man)
+  (evil-define-key '(normal visual) 'global (kbd "<leader>eu") 'browse-url-at-point)
+  (evil-define-key '(normal visual) 'global (kbd "<leader>ef") 'ffap)
   ;; modify what is displayed
   (evil-define-key 'normal 'global (kbd "<leader>dw")
     (lambda ()
       (interactive)
       (whitespace-mode 'toggle)
       (display-fill-column-indicator-mode 'toggle)))
-  ;; elfeed bindings for evil (can't use-package elfeed apparently)
-  (evil-define-key 'normal 'global (kbd "<leader>ff") 'elfeed)
-  (evil-define-key '(normal visual) elfeed-search-mode-map
-    (kbd "o") 'elfeed-search-browse-url
-    (kbd "r") 'elfeed-search-untag-all-unread
-    (kbd "u") 'elfeed-search-tag-all-unread
-    (kbd "<leader>ff") 'elfeed-search-fetch
-    (kbd "<leader>fc") 'elfeed-db-compact
-    (kbd "<leader>fr") 'elfeed-search-update--force))
+  ;; org-mode
+  (evil-define-key 'normal 'global (kbd "<leader>oa") 'org-agenda)
+  (evil-define-key 'normal 'global (kbd "<leader>oc") 'org-capture))
 
 (use-package evil-collection
   :after evil
   :config
   (evil-collection-init))
 
+;; parens
 (use-package rainbow-delimiters
-  :hook (prog-mode . rainbow-delimiters-mode))
+  :hook ((prog-mode . rainbow-delimiters-mode)))
+
+(setq show-paren-delay 0)
+(show-paren-mode)
+
+(use-package paredit
+  :hook ((emacs-lisp-mode . paredit-mode)
+         (lisp-mode . paredit-mode)
+         (ielm-mode . paredit-mode)
+         (lisp-interaction-mode . paredit-mode)))
+
+(use-package which-key :config (which-key-mode t))
 
 (use-package nix-mode :mode "\\.nix\\'")
 (use-package nix-drv-mode :mode "\\.drv\\'")
 
+(use-package direnv
+  :config (direnv-mode))
+
+(use-package editorconfig
+  :config (editorconfig-mode 1))
+
 (use-package haskell-mode)
+(use-package flycheck
+  :init (global-flycheck-mode)
+  :custom flycheck-keymap-prefix (kbd "<leader>!"))
+(use-package lsp-mode
+  :hook ((haskell-mode . lsp-deferred))
+  :commands (lsp lsp-deferred)
+  :custom
+  lsp-modeline-code-actions-segments '() ; using lsp-ui-sideline instead
+  :config
+  (evil-define-key 'normal 'global
+    (kbd "<leader>lwr") 'lsp-workspace-restart
+    (kbd "<leader>lwq") 'lsp-workspace-shutdown
+    (kbd "<leader>la=") 'lsp-format-buffer
+    (kbd "<leader>lar") 'lsp-rename
+    (kbd "<leader>laa") 'lsp-execute-code-action))
+(use-package lsp-ui
+  :after lsp-mode
+  :custom
+  lsp-ui-doc-enable t
+  lsp-ui-doc-border "DimGray"
+  lsp-ui-doc-delay 0.5
+  :config
+  (set-face-background 'lsp-ui-doc-background "WhiteSmoke")
+  (set-face-foreground 'lsp-ui-sideline-code-action "SaddleBrown")
+  (setq lsp-ui-sideline-code-actions-prefix "๐Ÿ”จ "
+        lsp-ui-sideline-show-diagnostics nil
+        lsp-ui-sideline-show-code-actions t) ; is :custom, but won't take effect?
+  (evil-define-key 'normal lsp-ui-mode-map
+    ;; TODO(sterni): emulate using xref for non-lsp?
+    (kbd "<leader>lgr") 'lsp-ui-peek-find-references
+    (kbd "<leader>lgd") 'lsp-ui-peek-find-definitions
+    (kbd "<leader>lc") 'lsp-ui-flycheck-list))
+(use-package lsp-haskell
+  :after lsp-mode
+  :custom
+  lsp-haskell-formatting-provider "ormolu")
+
 (use-package urweb-mode)
 (use-package bqn-mode
   :mode "\\.bqn\\'"
@@ -157,11 +299,14 @@
 (use-package rust-mode)
 (use-package sly
   :after evil
-  :hook ((sly-mrepl-mode . (lambda () (rainbow-delimiters-mode-enable))))
+  :hook ((sly-mrepl-mode . (lambda ()
+                             (enable-paredit-mode)
+                             (rainbow-delimiters-mode-enable))))
   :config
-  (evil-define-key 'normal sly-mrepl-mode-map (kbd "C-r") 'isearch-backward))
+  (evil-define-key '(normal insert) sly-mrepl-mode-map (kbd "C-r") 'isearch-backward))
 
-(use-package ada-mode)
+; TODO(sterni): https://github.com/NixOS/nixpkgs/pull/173893/files
+; (use-package ada-mode)
 
 (use-package rainbow-mode)
 (use-package hl-todo
@@ -178,7 +323,35 @@
   :mode (("\\.md\\'" . markdown-mode)))
 (use-package adoc-mode
   :mode (("\\.adoc\\'" . adoc-mode)))
+(use-package languagetool
+  :after evil
+  :custom
+  languagetool-java-arguments '("-Dfile.encoding=UTF-8")
+  languagetool-default-language "en-GB"
+  languagetool-mother-tongue "de-DE"
+  :config
+  (evil-define-key 'normal 'global (kbd "<leader>mll") 'languagetool-check)
+  (evil-define-key 'normal 'global (kbd "<leader>mlc") 'languagetool-correct-at-point)
+  (evil-define-key 'normal 'global (kbd "<leader>mls") 'languagetool-set-language)
+  (evil-define-key 'normal 'global (kbd "<leader>mlr") 'languagetool-clear-suggestions)
+  ;; Fill background of issues instead of just underlining to make it easier to read
+  (set-face-background 'languagetool-issue-default "yellow")
+  (set-face-background 'languagetool-issue-misspelling "red"))
+
+(use-package deft
+  :config
+  ;; This is based on (car deft-extensions), but unfortunately the variable is
+  ;; not re-bound in the hook defined by defcustom, so it is always "txt".
+  (setq deft-default-extension "org")
+  (evil-define-key 'normal 'global (kbd "<leader>mn") 'deft)
+  :custom
+  deft-directory (expand-file-name "~/files/sync/org/notes")
+  deft-extensions '("org" "md" "txt" "tex"))
+
+(unless (server-running-p)
+  (server-start))
 
-(require 'subscriptions)
+(require 'subscriptions) ; elfeed config
+(require 'nix-inject)
 
 (provide 'init)
diff --git a/users/sterni/emacs/subscriptions.el b/users/sterni/emacs/subscriptions.el
index 15601de56a..ba63da3063 100644
--- a/users/sterni/emacs/subscriptions.el
+++ b/users/sterni/emacs/subscriptions.el
@@ -1,87 +1,71 @@
-;;; elfeed subscriptions
+;;; elfeed
+(use-package elfeed
+  :after evil
+  :config
+  ;; elfeed bindings for evil
+  (evil-define-key 'normal 'global (kbd "<leader>mf") 'elfeed)
+  (evil-define-key '(normal visual) elfeed-search-mode-map
+    (kbd "o") 'elfeed-search-browse-url
+    (kbd "r") 'elfeed-search-untag-all-unread
+    (kbd "u") 'elfeed-search-tag-all-unread
+    (kbd "ff") 'elfeed-search-fetch
+    (kbd "fc") 'elfeed-db-compact)
+  ;; elfeed subscriptions
+  (setq elfeed-feeds
+        (append
+         ;; immutable subscriptions tracked in git
+         '(("https://repology.org/maintainer/sternenseemann%40systemli.org/feed-for-repo/nix_unstable/atom" dashboard releases)
+           ("https://www.stackage.org/feed" dashboard releases)
+           ("http://hundimbuero.blogspot.com/feeds/posts/default?alt=rss" blog cool-and-nice)
+           ("https://text.causal.agency/feed.atom" blog)
+           ("http://xsteadfastx.org/feed/" blog cool-and-nice)
+           ("https://tvl.fyi/feed.atom" blog cool-and-nice)
+           ("https://hannes.robur.coop/atom" blog)
+           ("https://stevelosh.com/rss.xml" blog)
+           ("https://blog.benjojo.co.uk/rss.xml" blog)
+           ("https://leahneukirchen.org/blog/index.atom" blog cool-and-nice)
+           ("https://leahneukirchen.org/trivium/index.atom" blog links cool-and-nice)
+           ("https://firefly.nu/feeds/all.atom.xml" blog cool-and-nice)
+           ("https://tazj.in/feed.atom" blog cool-and-nice)
+           ("https://alyssa.is/feed.xml" blog cool-and-nice)
+           ("https://eta.st/feed.xml" blog cool-and-nice)
+           ("https://spectrum-os.org/git/www/atom/bibliography.html" links blog)
+           ("https://vulns.xyz/feed.xml" blog)
+           ("https://www.german-foreign-policy.com/?type=9818" news)
+           ("https://niedzejkob.p4.team/rss.xml" blog)
+           ("https://grahamc.com/feed/" blog)
+           ("http://blog.nullspace.io/feed.xml" blog)
+           ("https://blog.kingcons.io/rss.xml" blog)
+           ("https://www.imperialviolet.org/iv-rss.xml" blog)
+           ("https://22gato.tumblr.com/rss" pictures cool-and-nice)
+           ("https://theprofoundprogrammer.com/rss" blog)
+           ("http://shitopenlabsays.tumblr.com/rss" openlab)
+           ("https://kristaps.bsd.lv/lowdown/atom.xml" releases)
+           ("http://0pointer.net/blog/index.atom" blog)
+           ("https://emacsninja.com/feed.atom" blog)
+           ("https://emacshorrors.com/feed.atom" blog)
+           ("http://therealmntmn.tumblr.com/rss" blog)
+           ("http://blog.duangle.com/feeds/posts/default" blog)
+           ("http://ccc.de/de/rss/updates.xml" news)
+           ("http://ffaaaffaffaffaa.tumblr.com/rss" pictures)
+           ("http://fotografiona.tumblr.com/rss" pictures)
+           ("http://guteaussicht.org/rss" pictures)
+           ("http://konvergenzfehler.de/feed/" blog)
+           ("https://markuscisler.com/feed.xml" blog)
+           ("http://www.plomlompom.de/PlomRogue/plomwiki.php?action=Blog_Atom" blog)
+           ("http://www.whvrt.de/rss" pictures)
+           ("https://echtsuppe.wordpress.com/feed/" blog defunct)
+           ("https://mgsloan.com/feed.xml" blog)
+           ("http://beza1e1.tuxen.de/blog_en.atom" blog)
+           ("https://anchor.fm/s/94bb000/podcast/rss" podcast))
+           ;; http://www.wollenzin.de/feed/ ;_;
 
-(setq elfeed-feeds
-      (append
-       ;; immutable subscriptions tracked in git
-       '(("https://repology.org/maintainer/sternenseemann%40systemli.org/feed-for-repo/nix_unstable/atom" dashboard releases)
-         ("http://hundimbuero.blogspot.com/feeds/posts/default?alt=rss" blog cool-and-nice)
-         ("gopher://text.causal.agency/0feed.atom" blog)
-         ("http://xsteadfastx.org/feed/" blog cool-and-nice)
-         ("https://hannes.robur.coop/atom" blog)
-         ("https://stevelosh.com/rss.xml" blog)
-         ("https://planet.lisp.org/rss20.xml" blog)
-         ("https://hyperthings.garden/rss/all-posts.xml" blog)
-         ("https://blog.benjojo.co.uk/rss.xml" blog)
-         ("https://leahneukirchen.org/blog/index.atom" blog cool-and-nice)
-         ("https://leahneukirchen.org/trivium/index.atom" blog links cool-and-nice)
-         ("https://firefly.nu/feeds/all.atom.xml" blog cool-and-nice)
-         ("https://tazj.in/feed.atom" blog cool-and-nice)
-         ("https://alyssa.is/feed.xml" blog cool-and-nice)
-         ("https://eta.st/feed.xml" blog cool-and-nice)
-         ("https://h.eta.st/rss" noisy cool-and-nice)
-         ("https://spectrum-os.org/git/www/atom/bibliography.html" links blog)
-         ("https://rachelbythebay.com/w/atom.xml" blog)
-         ("http://evrl.com/feed.xml" blog)
-         ("https://vulns.xyz/feed.xml" blog)
-         ("https://www.german-foreign-policy.com/?type=9818" news)
-         ("https://niedzejkob.p4.team/rss.xml" blog)
-         ("https://grahamc.com/feed/" blog)
-         ("https://michael.stapelberg.ch/feed.xml" blog)
-         ("https://kazu-yamamoto.hatenablog.jp/feed" blog)
-         ("https://ariadne.space/feed/" blog)
-         ("https://bodil.lol/rss.xml" blog)
-         ("http://blog.nullspace.io/feed.xml" blog)
-         ("https://blog.kingcons.io/rss.xml" blog)
-         ("http://jaspervdj.be/rss.xml" blog)
-         ("https://christine.website/blog.rss" blog)
-         ("https://drewdevault.com/feed.xml" blog)
-         ("https://www.imperialviolet.org/iv-rss.xml" blog)
-         ("https://latacora.micro.blog/feed.xml" blog)
-         ("https://22gato.tumblr.com/rss" pictures cool-and-nice)
-         ("https://theprofoundprogrammer.com/rss" blog)
-         ("https://wiki.openlab-augsburg.de/_feed" openlab)
-         ("http://shitopenlabsays.tumblr.com/rss" openlab)
-         ("http://suckless.org/atom.xml" releases)
-         ("https://kristaps.bsd.lv/lowdown/atom.xml" releases)
-         ("https://www.tweag.io/rss.xml" blog)
-         ("http://planet.haskell.org/atom.xml" planet blog)
-         ("http://0pointer.net/blog/index.atom" blog)
-         ("https://emacsninja.com/feed.atom" blog)
-         ("https://emacshorrors.com/feed.atom" blog)
-         ("http://therealmntmn.tumblr.com/rss" blog)
-         ("http://blog.duangle.com/feeds/posts/default" blog)
-         ("http://blog.johl.io/atom.xml" blog)
-         ("http://blog.z3bra.org/rss/feed.xml" blog)
-         ("http://ccc.de/de/rss/updates.xml" news)
-         ;; ("http://fabienne.us/feed/" blog) ; database error
-         ("http://feeds.feedburner.com/baschtcom" blog)
-         ("http://ffaaaffaffaffaa.tumblr.com/rss" pictures)
-         ("http://fnordig.de/feed.xml" blog)
-         ("http://fotografiona.tumblr.com/rss" pictures)
-         ("https://grandhotel-cosmopolis.org/de/feed" news)
-         ("http://guteaussicht.org/rss" pictures)
-         ("http://konvergenzfehler.de/feed/" blog)
-         ("https://markuscisler.com/feed.xml" blog)
-         ("http://n00bcore.de/feed/" podcast)
-         ("http://spacethatneverwas.tumblr.com/rss" pictures)
-         ("http://theresa.someserver.de/blog/?feed=rss2" blog)
-         ("http://www.frumble.de/blog/feed/" blog)
-         ("http://www.plomlompom.de/PlomRogue/plomwiki.php?action=Blog_Atom" blog)
-         ("http://www.whvrt.de/rss" pictures)
-         ("http://www.windytan.com/feeds/posts/default" blog)
-         ("https://echtsuppe.wordpress.com/feed/" blog defunct)
-         ("https://mgsloan.com/feed.xml" blog)
-         ("https://notes.sterni.lv/atom.xml" me)
-         ("http://arduina.net/feed/" defunct blog)
-         ("https://anchor.fm/s/94bb000/podcast/rss" podcast))
-         ;; http://www.wollenzin.de/feed/ ;_;
-
-       ;; add more feeds from an untracked file in $HOME
-       (let ((file (concat (getenv "HOME")
-                           "/.config/emacs-custom/mutable-subscriptions.el")))
-         (when (file-exists-p file)
-           (read (with-temp-buffer
-                   (insert-file-contents file)
-                   (buffer-string)))))))
+         ;; add more feeds from an untracked file in $HOME
+         (let ((file (concat (getenv "HOME")
+                             "/.config/emacs-custom/mutable-subscriptions.el")))
+           (when (file-exists-p file)
+             (read (with-temp-buffer
+                     (insert-file-contents file)
+                     (buffer-string))))))))
 
 (provide 'subscriptions)
diff --git a/users/sterni/exercises/aoc/.gitignore b/users/sterni/exercises/aoc/.gitignore
new file mode 100644
index 0000000000..b9720d7451
--- /dev/null
+++ b/users/sterni/exercises/aoc/.gitignore
@@ -0,0 +1,2 @@
+/*/input
+/*/*/input
\ No newline at end of file
diff --git a/users/sterni/exercises/aoc/2021/default.nix b/users/sterni/exercises/aoc/2021/default.nix
new file mode 100644
index 0000000000..d3ed563ec6
--- /dev/null
+++ b/users/sterni/exercises/aoc/2021/default.nix
@@ -0,0 +1,10 @@
+{ depot ? import ../../../../.. { }
+, pkgs ? depot.third_party.nixpkgs
+, ...
+}:
+
+pkgs.mkShell {
+  nativeBuildInputs = [
+    pkgs.cbqn
+  ];
+}
diff --git a/users/sterni/exercises/aoc/2021/solutions.bqn b/users/sterni/exercises/aoc/2021/solutions.bqn
new file mode 100755
index 0000000000..755c944046
--- /dev/null
+++ b/users/sterni/exercises/aoc/2021/solutions.bqn
@@ -0,0 +1,484 @@
+#!/usr/bin/env BQN
+
+โŸจXorโŸฉ โ† โ€ขImport "../lib.bqn"
+
+#
+# Utilities
+#
+
+โŸจIsAsciiNum,ReadInt,ReadDec,SplitOn,_fixโŸฉ โ† โ€ขImport โ€ขpathโˆพ"/../lib.bqn"
+
+ReadInput โ† {โ€ขfile.Lines โˆพ โ€ขpathโ€ฟ"/input/day"โ€ฟ(โ€ขFmt ๐•ฉ)}
+
+#
+# 2021-12-01
+#
+
+# part 1
+
+day1ExampleInput โ† 199โ€ฟ200โ€ฟ208โ€ฟ210โ€ฟ200โ€ฟ207โ€ฟ240โ€ฟ269โ€ฟ260โ€ฟ263
+day1Input โ† ReadDecยจReadInput 1
+
+# NB: Because distance from the ground is never smaller than zero, it's
+# no problem that nudge inserts a zero at the end of the right list
+PositiveDeltaCount โ† +ยดโˆ˜(โŠข<ยซ)+หห˜โˆ˜โ†•
+
+! 7 = 1 PositiveDeltaCount day1ExampleInput
+
+โ€ขOut "Day 1.1: "โˆพโ€ขFmt 1 PositiveDeltaCount day1Input
+
+# part 2
+
+! 5 = 3 PositiveDeltaCount day1ExampleInput
+
+โ€ขOut "Day 1.2: "โˆพโ€ขFmt 3 PositiveDeltaCount day1Input
+
+#
+# 2021-12-02
+#
+
+# part 1
+
+day2ExampleInput โ† โŸจ
+  "forward 5",
+  "down 5",
+  "forward 8",
+  "up 3",
+  "down 8",
+  "forward 2",
+โŸฉ
+
+day2Input โ† ReadInput 2
+
+ParseSubmarineCommand โ† (((โ†•2)โŠธ((((-1)โŠธโ‹†)โˆ˜(2โŠธ|))ร—(=โŸœ(โŒŠโˆ˜(รทโŸœ2))))โˆ˜("duf"โŠธโŠ)โˆ˜โŠ‘)ร—ReadDecโˆ˜(IsAsciiNum/โŠข))
+
+SubmarineDestProduct โ† {ร—ยด+ยดParseSubmarineCommandยจ๐•ฉ}
+
+! 150 = SubmarineDestProduct day2ExampleInput
+
+โ€ขOut "Day 2.1: "โˆพโ€ขFmt SubmarineDestProduct day2Input
+
+# part 2
+
+SubmarineAimedDestProduct โ† {
+  ร—ยด+ยด((ร—ยด)โˆ˜(1โŠธโ†“)โ‰(1โŠธโŠ‘))ยจ (<0โ€ฟ0โ€ฟ0) (โŠขโˆพ((โŠ‘โˆ˜โŒฝโŠฃ)+(โŠ‘โŠข)))` ParseSubmarineCommandยจ๐•ฉ
+}
+
+! 900 = SubmarineAimedDestProduct day2ExampleInput
+
+โ€ขOut "Day 2.2: "โˆพโ€ขFmt SubmarineAimedDestProduct day2Input
+
+#
+# 2021-12-03
+#
+
+BinTable โ† '0'-หœ>
+
+day3ExampleInput โ† BinTable โŸจ
+  "00100",
+  "11110",
+  "10110",
+  "10111",
+  "10101",
+  "01111",
+  "00111",
+  "11100",
+  "10000",
+  "11001",
+  "00010",
+  "01010",
+โŸฉ
+
+day3Input โ† BinTable ReadInput 3
+
+DeBinList โ† ((2โŠธร—)+โŠฃ)ยดโŒฝ
+_tableAggr โ† {((รทโŸœ2)โˆ˜(/โŸœโฅŠ)ยดโˆ˜โŒฝโˆ˜โ‰ข๐”ฝ(+ห))๐•ฉ}
+GammaRate โ† < _tableAggr
+
+! 22 = DeBinList GammaRate day3ExampleInput
+! 9  = DeBinList ยฌGammaRate day3ExampleInput
+
+โ€ขOut "Day 3.1: "โˆพโ€ขFmt (ยฌร—โ—‹DeBinListโŠข) GammaRate day3Input
+
+_lifeSupportRating โ† {
+  # Need to rename the arguments, otherwise the ternary expr becomes a function
+  bitPos โ† ๐•จ
+  Cmp โ† ๐”ฝ
+
+  crit โ† Cmp _tableAggr ๐•ฉ
+  matchPos โ† bitPos โŠ‘ห˜ crit ((โฅŠหœโŸœโ‰ข)=โŠข) ๐•ฉ
+  match โ† matchPos/๐•ฉ
+  {1=โ‰ match?โŠmatch;(bitPos+1) Cmp _lifeSupportRating match}
+}
+
+OxygenGeneratorRating โ† DeBinList 0 โ‰ค_lifeSupportRating โŠข
+CO2ScrubberRating โ† DebinList 0 >_lifeSupportRating โŠข
+
+! 23 = OxygenGeneratorRating day3ExampleInput
+! 10 = CO2ScrubberRating day3ExampleInput
+
+โ€ขOut "Day 3.2: "โˆพโ€ขFmt (OxygenGeneratorRatingร—CO2ScrubberRating) day3Input
+
+#
+# 2021-12-04
+#
+
+day4Numbers โ† ReadDecยจ ',' SplitOn โŠ‘ReadInput 4
+day4Boards โ† ReadDecยจ>ห˜(' 'โŠธSplitOnยจ)> (<โŸจโŸฉ) SplitOn 2โ†“ReadInput 4
+
+BoardWins โ† {C โ† โˆจยดโˆ˜(โˆงยดห˜) โ‹„ (CโˆจCโˆ˜โ‰)๐•ฉ}
+
+_CallNumber โ† {(๐•—โˆŠโฅŠ๐•ฉ) (โˆจโŸ(ยฌโˆ˜BoardWinsโˆ˜โŠข))ห˜ ๐•จ}
+
+BoardWinScores โ† {
+  ๐•ฉ (0โŠธ</ร—) (โŠข-ยป) (+ยด)โˆ˜(BoardWinsห˜/(+ยดโฅŠ)ห˜โˆ˜(๐•จโŠธร—โŸœยฌ))ยจ (<0โฅŠหœโ‰ข๐•จ) (๐•จ _CallNumber)`๐•ฉ
+}
+
+day4WinScores โ† day4Boards BoardWinScores day4Numbers
+
+โ€ขOut "Day 4.1: "โˆพโ€ขFmt โŠ‘day4WinScores
+โ€ขOut "Day 4.2: "โˆพโ€ขFmt โŠ‘โŒฝday4WinScores
+
+#
+# 2021-12-06
+#
+
+day6ExampleInput โ† โŸจ3,4,3,1,2โŸฉ
+day6Input โ† ReadDecยจ ',' SplitOn โŠ‘ReadInput 6
+
+LanternfishPopulation โ† {+ยด (1โŠธโŒฝ+(โŠ‘ร—((6โŠธ=)โˆ˜โ†•โˆ˜โ‰ )))โŸ๐•จ 9โ†‘โ‰ ยจโŠ” ๐•ฉ}
+
+! 26 = 18 LanternfishPopulation day6ExampleInput
+! 5934 = 80 LanternfishPopulation day6ExampleInput
+
+โ€ขOut "Day 6.1: "โˆพโ€ขFmt 80 LanternfishPopulation day6Input
+โ€ขOut "Day 6.2: "โˆพโ€ขFmt 256 LanternfishPopulation day6Input
+
+#
+# 2021-12-07
+#
+
+# part 1
+
+day7ExampleInput โ† โŸจ16,1,2,0,4,2,7,1,2,14โŸฉ
+day7Input โ† ReadDecยจ ','  SplitOn โŠ‘ReadInput 7
+
+PossiblePositions โ† (โŒŠยด+โŸœ(โ†•1โŠธ+)โŒˆยด)
+FuelConsumption โ† +หโˆ˜|โˆ˜(-โŒœ)
+_lowestFuelPossible โ† {โŒŠยดโˆ˜(๐”ฝโŸœPossiblePositions)หœ ๐•ฉ}
+
+! 37 = FuelConsumption _lowestFuelPossible day7ExampleInput
+
+โ€ขOut "Day 7.1: "โˆพโ€ขFmt FuelConsumption _lowestFuelPossible day7Input
+
+# part 2
+
+TriNum โ† 1โŠธ+ร—รทโŸœ2
+
+FuelConsumption2 โ† +หโˆ˜(TriNumยจ)โˆ˜|โˆ˜(-โŒœ)
+
+! 168 = FuelConsumption2 _lowestFuelPossible day7ExampleInput
+
+โ€ขOut "Day 7.2: "โˆพโ€ขFmt FuelConsumption2 _lowestFuelPossible day7Input
+
+#
+# 2021-12-09
+#
+
+# part 1
+
+ParseHeightMap โ† ((โ‰ โ‰(โ‰ โŠ‘))โฅŠโˆพ)โˆ˜-โŸœ'0'
+
+day9ExampleInput โ† ParseHeightMap โŸจ
+  "2199943210",
+  "3987894921",
+  "9856789892",
+  "8767896789",
+  "9899965678"
+โŸฉ
+day9Input โ† ParseHeightMap ReadInput 9
+
+Rotate โ† (โ‰โŒฝ)โˆ˜โŠขโŸโŠฃ # counter clockwise
+LowPoints โ† {โˆงยด๐•ฉโŠธ(โŠฃ<((-โŠข) Rotate โˆžโŠธยปห˜โˆ˜Rotateหœ))ยจ โ†•4}
+
+RiskLevelSum โ† (+ยดโฅŠ)โˆ˜(1โŠธ+ร—LowPoints)
+
+! 15 = RiskLevelSum day9ExampleInput
+
+โ€ขOut "Day 9.1: "โˆพโ€ขFmt RiskLevelSum day9Input
+
+# part 2
+
+NumberBasins โ† ((1โŠธ+โŠ’โŒพโฅŠ)ร—โŠข)โˆ˜LowPoints
+Basins โ† {๐•ฉโŠธ((<โŸœ9โŠฃ)โˆง(ยซโŒˆยปโŒˆยซห˜โŒˆยปห˜โŒˆโŠข)โˆ˜โŠข) _fix NumberBasins ๐•ฉ}
+LargestBasinsProduct โ† {ร—ยด 3โ†‘ โˆจ 1โ†“ โ‰ ยจ โŠ”โฅŠBasins ๐•ฉ}
+
+! 1134 = LargestBasinsProduct day9ExampleInput
+
+โ€ขOut "Day 9.2: "โˆพโ€ขFmt LargestBasinsProduct day9Input
+
+#
+# 2021-12-10
+#
+
+day10ExampleInput โ† โŸจ
+  "[({(<(())[]>[[{[]{<()<>>",
+  "[(()[<>])]({[<{<<[]>>(",
+  "{([(<{}[<>[]}>{[]{[(<()>",
+  "(((({<>}<{<{<>}{[]{[]{}",
+  "[[<[([]))<([[{}[[()]]]",
+  "[{[{({}]{}}([{[{{{}}([]",
+  "{<[[]]>}<{[{[{[]{()[[[]",
+  "[<(<(<(<{}))><([]([]()",
+  "<{([([[(<>()){}]>(<<{{",
+  "<{([{{}}[<[[[<>{}]]]>[]]",
+โŸฉ
+day10Input โ† ReadInput 10
+
+# part 1
+
+opp โ† "([{<"
+clp โ† ")]}>"
+SwapParen โ† (oppโˆพโŒฝclp)โŠธ((โŠ‘โŠ)โŠ‘(โŒฝโŠฃ))
+
+ParenStacks โ† ((<โŸจโŸฉ)โŠธ(((โŠ‘โˆŠ)โŸœclpโŠข)โ—ถ(โˆพหœโŸœSwapParen)โ€ฟ(1โŠธโ†“โŠฃ)`))
+LegalParens โ† ((1โŠธโ†‘)ยจโˆ˜ยปโˆ˜ParenStacks ((โˆŠโŸœoppโŠข)โˆจ(โ‰กโŸœโ‹ˆ)ยจ) โŠข)
+
+_ScoreFor_ โ† {๐•—โŠธ(๐•˜โŠธโŠโŠโŠฃ) ๐•ฉ}
+
+SyntaxScore โ† +ยดโˆ˜(0โ€ฟ3โ€ฟ57โ€ฟ1197โ€ฟ25137 _ScoreFor_ (" "โˆพclp))โˆ˜โˆพโˆ˜(1โŠธโ†‘โˆ˜(ยฌโˆ˜LegalParens/โŠข)ยจ)
+
+! 26397 = SyntaxScore day10ExampleInput
+โ€ขOut "Day 10.1: "โˆพโ€ขFmt SyntaxScore day10Input
+
+# part 2
+
+AutocompleteScore โ† {
+  Score โ† (5โŠธร—โŠธ+)หœยดโˆ˜โŒฝโˆ˜((1+โ†•4) _ScoreFor_ clp)
+  # TODO(sterni): we compute ParenStacks twice here
+  ((โŒŠรทโŸœ2)โˆ˜โ‰ โŠ‘โŠข) โˆง Scoreโˆ˜(โŠ‘โŒฝ)โˆ˜ParenStacksยจ (โˆงยดโˆ˜LegalParensยจ/โŠข) ๐•ฉ
+}
+
+! 288957 = AutocompleteScore day10ExampleInput
+โ€ขOut "Day 10.2: "โˆพโ€ขFmt AutocompleteScore day10Input
+
+#
+# 2021-12-11
+#
+
+day11Input โ† '0'-หœ> ReadInput 11
+day11ExampleInput โ† >โŸจ
+  โŸจ5,4,8,3,1,4,3,2,2,3,โŸฉ,
+  โŸจ2,7,4,5,8,5,4,7,1,1,โŸฉ,
+  โŸจ5,2,6,4,5,5,6,1,7,3,โŸฉ,
+  โŸจ6,1,4,1,3,3,6,1,4,6,โŸฉ,
+  โŸจ6,3,5,7,3,8,5,4,7,8,โŸฉ,
+  โŸจ4,1,6,7,5,2,4,6,4,5,โŸฉ,
+  โŸจ2,1,7,6,8,4,1,7,2,1,โŸฉ,
+  โŸจ6,8,8,2,8,8,1,1,3,4,โŸฉ,
+  โŸจ4,8,4,6,8,4,8,5,5,4,โŸฉ,
+  โŸจ5,2,8,3,7,5,1,5,2,6,โŸฉ,
+โŸฉ
+
+# part 1
+
+OctopusFlash โ† {
+  ((โฅŠโŸœ0)โˆ˜โ‰ข๐•ŠโŠข) ๐•ฉ;
+  flashing โ† (ยฌ๐•จ)โˆง9<๐•ฉ
+  energy โ† ((ยซห˜ยป)+(ยปห˜ยซ)+(ยปห˜ยป)+(ยซห˜ยซ)+(ยปห˜)+(ยซห˜)+ยซ+ยป) flashing
+  ((๐•จโˆจflashing)โŠธ๐•Š)โŸ(0<+ยดโฅŠflashing) energy+๐•ฉ
+}
+
+OctopusStep โ† ((9โŠธโ‰ฅ)ร—โŠข)โˆ˜OctopusFlashโˆ˜(1โŠธ+)
+OctopusFlashCount โ† {+ยดโฅŠ0=>(OctopusStepโŠฃ)`(1+๐•จ)โฅŠ<๐•ฉ}
+
+! 1656 = 100 OctopusFlashCount day11ExampleInput
+โ€ขOut "Day 11.1: "โˆพโ€ขFmt 100 OctopusFlashCount day11Input
+
+# part 2
+
+_iterCountUntil_ โ† {
+  0 ๐•Š ๐•ฉ;
+  ๐”พโ—ถโŸจ((๐•จ+1)โŠธ๐•Š)โˆ˜๐”ฝ, ๐•จห™โŸฉ ๐•ฉ
+}
+
+OctopusAllFlashing โ† OctopusStep _iterCountUntil_ (โˆงยดโˆ˜โฅŠโˆ˜(0โŠธ=))
+
+! 195 = OctopusAllFlashing day11ExampleInput
+
+โ€ขOut "Day 11.2: "โˆพโ€ขFmt OctopusAllFlashing day11Input
+
+#
+# 2021-12-13
+#
+
+SplitFoldingInstructions โ† ("fold along"โŠธ(โŠฃโ‰กโ‰ โŠธโ†‘)ยจโŠ”โŠข)โˆ˜(0โŠธ(โ‰ โŸœโ‰ ยจ/โŠข))
+day13ExampleInput โ† SplitFoldingInstructions โŸจ
+  "6,10",
+  "0,14",
+  "9,10",
+  "0,3",
+  "10,4",
+  "4,11",
+  "6,0",
+  "6,12",
+  "4,1",
+  "0,13",
+  "10,12",
+  "3,4",
+  "3,0",
+  "8,4",
+  "1,10",
+  "2,14",
+  "8,10",
+  "9,0",
+  "",
+  "fold along y=7",
+  "fold along x=5",
+โŸฉ
+day13Input โ† SplitFoldingInstructions ReadInput 13
+
+ParseDots โ† ReadDecยจโˆ˜(','โŠธSplitOn)ยจ
+ParseFolds โ† (โŠ‘โˆ˜'y'โŠธโˆŠโ‰ReadDecโˆ˜(IsAsciiNum/โŠข))ยจ
+day13ExampleDots โ† ParseDots โŠ‘ day13ExampleInput
+
+# part 1
+
+# ๐•จ=0 => x, ๐•จ=1 => y
+# ๐•ฉ is coordinate to fold around
+# ๐•— is input dot list (see ParseDots)
+_Fold โ† {โทโˆ˜((๐•ฉโŠธ(((2โŠธร—โŠฃ)-โŠข)โŒŠโŠข)โˆ˜โŠ‘โ‰1โŠธโŠ‘)ยจโŒพ(โŒฝยจโŸ๐•จ)) ๐•—}
+
+! 17 = โ‰  1 day13ExampleDots _Fold 7
+
+day13Dots โ† ParseDots โŠ‘ day13Input
+day13Folds โ† ParseFolds 1 โŠ‘ day13Input
+
+โ€ขOut "Day 13.1: "โˆพโ€ขFmt โ‰  (day13Dots _Fold)ยด โŠ‘day13Folds
+
+# part 2
+
+PerformAllFolds โ† {๐•ฉ {(๐•จ _Fold)ยด๐•ฉ}หœยด โŒฝ๐•จ}
+DotMatrix โ† {
+  โŸจwidth, heightโŸฉ โ† 1+โŒˆหโˆ˜โ€ฟ2โฅŠโˆพ๐•ฉ
+  {๐•ฉ? 'โ–ˆ';' '}ยจ heightโ€ฟwidthโฅŠโ‰ ยจโŠ”((โŠฃ+(widthโŠธร—)โˆ˜โŠข)ยด)ยจ ๐•ฉ
+}
+
+โ€ขOut "Day 13.2:"
+โ€ขOut โ€ขFmt DotMatrix day13Folds PerformAllFolds day13Dots
+
+#
+# 2021-12-14
+#
+
+day14Polymer โ† โŠ‘ReadInput 14
+day14Mapping โ† 2โ†“ReadInput 14
+
+lp โ† (2โŠธโ†‘)ยจ day14Mapping
+le โ† โทโˆพlp
+
+# returns array as long as ๐•จ detailing how many times the element
+# at any given index occurs in ๐•ฉ.
+Counts โ† ((โ‰ โŠฃ)โ†‘(/โผ)โˆ˜โŠ)
+
+deltaPairs โ† {
+  addedPairs โ† ((-1)โŠธโŠ‘ยจday14Mapping) (โŒฝโŒพ(0โŠธโŠ‘))โˆ˜(โˆพยจ)ยจ lp
+  removedPairs โ† โ‹ˆยจ (2โŠธโ†‘)ยจ lp
+  addedPairs (-โ—‹(lpโŠธCounts))ยจ removedPairs
+}
+
+pairCount โ† lp Counts โฅŠโˆ˜(โ‹ˆห˜) 2โ†•day14Polymer
+
+PairInsert โ† {๐•ฉ +ยด ๐•ฉร—ยจdeltaPairs}
+
+pairElementCount โ† (leโŠธCounts)ยจlp
+
+ElementRarityDiff โ† {
+ ((-1)โŠธโŠ‘-โŠ‘)โˆง โŒˆ2รทหœ +ยด pairElementCountร—PairInsertโŸ๐•ฉ pairCount
+}
+
+โ€ขOut "Day 14.1: "โˆพโ€ขFmt ElementRarityDiff 10
+โ€ขOut "Day 14.2: "โˆพโ€ขFmt ElementRarityDiff 40
+
+#
+# 2021-12-15
+#
+
+day15ExampleInput โ† >โŸจ
+  1โ€ฟ1โ€ฟ6โ€ฟ3โ€ฟ7โ€ฟ5โ€ฟ1โ€ฟ7โ€ฟ4โ€ฟ2
+  1โ€ฟ3โ€ฟ8โ€ฟ1โ€ฟ3โ€ฟ7โ€ฟ3โ€ฟ6โ€ฟ7โ€ฟ2
+  2โ€ฟ1โ€ฟ3โ€ฟ6โ€ฟ5โ€ฟ1โ€ฟ1โ€ฟ3โ€ฟ2โ€ฟ8
+  3โ€ฟ6โ€ฟ9โ€ฟ4โ€ฟ9โ€ฟ3โ€ฟ1โ€ฟ5โ€ฟ6โ€ฟ9
+  7โ€ฟ4โ€ฟ6โ€ฟ3โ€ฟ4โ€ฟ1โ€ฟ7โ€ฟ1โ€ฟ1โ€ฟ1
+  1โ€ฟ3โ€ฟ1โ€ฟ9โ€ฟ1โ€ฟ2โ€ฟ8โ€ฟ1โ€ฟ3โ€ฟ7
+  1โ€ฟ3โ€ฟ5โ€ฟ9โ€ฟ9โ€ฟ1โ€ฟ2โ€ฟ4โ€ฟ2โ€ฟ1
+  3โ€ฟ1โ€ฟ2โ€ฟ5โ€ฟ4โ€ฟ2โ€ฟ1โ€ฟ6โ€ฟ3โ€ฟ9
+  1โ€ฟ2โ€ฟ9โ€ฟ3โ€ฟ1โ€ฟ3โ€ฟ8โ€ฟ5โ€ฟ2โ€ฟ1
+  2โ€ฟ3โ€ฟ1โ€ฟ1โ€ฟ9โ€ฟ4โ€ฟ4โ€ฟ5โ€ฟ8โ€ฟ1
+โŸฉ
+day15Input โ† '0'-หœ ((โ‰ โ‹ˆโ‰ โˆ˜โŠ‘)โฅŠโˆพ)ReadInput 15
+
+LowestRiskLevel โ† {
+  start โ† 0ห™โŒพโŠ‘ (โฅŠโŸœโˆž) โ‰ข๐•ฉ
+  ir โ† (1โŠ‘โ‰ข๐•ฉ)โฅŠโˆž
+  Step โ† {๐•ฉ โŒŠ ๐•จ + (irโŠธยซโŒŠirโŠธยปโŒŠโˆžโŠธยซห˜โŒŠโˆžโŠธยปห˜) ๐•ฉ}
+  โŠ‘โŒฝโฅŠ ๐•ฉโŠธStep _fix start
+}
+
+! 40 = LowestRiskLevel day15ExampleInput
+
+โ€ขOut "Day 15.1: "โˆพโ€ขFmt LowestRiskLevel day15Input
+
+FiveByFiveMap โ† {(9โŠธ|)โŒพ(-โŸœ1) โˆพ(<๐•ฉ)+ +โŒœหœโ†•5}
+
+! 315 = LowestRiskLevel FiveByFiveMap day15ExampleInput
+
+โ€ขOut "Day 15.2: "โˆพโ€ขFmt LowestRiskLevel FiveByFiveMap day15Input
+
+#
+# 2021-12-20
+#
+
+ParsePic โ† (โ‹ˆโŸœ0)โˆ˜('#'โŠธ=)โˆ˜>
+
+day20ExampleAlgo โ† '#'="..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#....#..#..##..###..######.###...####..#..#####..##..#.#####...##.#.#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..#####.....#.#....###..#.##......#.....#..#..#..##..#...##.######.####.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.##..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#.....####.#..#..#.##.#....##..#.####....##...##..#...#......#.#.......#.......##..####..#...#.#.#...##..#.#..###..#####........#..####......#..#"
+day20ExamplePic โ† ParsePic โŸจ"#..#.", "#....", "##..#", "..#..", "..###"โŸฉ
+day20Input โ† ReadInput 20
+day20Algo โ† '#'=โŠ‘day20Input
+day20Pic โ† ParsePic 2โ†“day20Input
+
+GrowAxis โ† {(โŠข (-1)โŠธโŒฝโˆ˜โˆพ (โฅŠโŸœ๐•จ)โˆ˜(2ห™โŒพโŠ‘)โˆ˜โ‰ข) ๐•ฉ}
+Grow โ† {๐•จ GrowAxis ๐•จ GrowAxisห˜ ๐•ฉ}
+
+Enhance โ† {
+  inf โ† 1โŠ‘๐•ฉ
+  npic โ† ((โŠ‘โŸœ๐•จ)โˆ˜DebinListโˆ˜โฅŠ)ห˜ห˜ 3โ€ฟ3โ†• (infโŠธGrow)โŸ2 โŠ‘๐•ฉ
+  ninf โ† ๐•จโŠ‘หœ511ร—inf
+  npicโ‹ˆninf
+}
+_EnhancedPixelCount โ† {+ยดโฅŠโŠ‘ (๐•จโŠธEnhance)โŸ๐•— ๐•ฉ}
+
+! 35 = day20ExampleAlgo 2 _EnhancedPixelCount day20ExamplePic
+! 3351 = day20ExampleAlgo 50 _EnhancedPixelCount day20ExamplePic
+
+โ€ขOut "Day 20.1: "โˆพโ€ขFmt day20Algo 2 _EnhancedPixelCount day20Pic
+โ€ขOut "Day 20.2: "โˆพโ€ขFmt day20algo 50 _EnhancedPixelCount day20Pic
+
+#
+# 2021-12-25
+#
+
+day25Input โ† ".>v" โŠ > ReadInput 25
+day25ExampleInput โ† ".>v"โŠโˆ˜โ€ฟ10โฅŠ"v...>>.vv>.vv>>.vv..>>.>v>...v>>v>>.>.v.v>v.vv.v..>.>>..v....vv..>.>v.v.v..>>v.v....v..v.>"
+
+MoveHerd โ† {(๐•ฉโˆง๐•ฉโ‰ ๐•จ)+๐•จร— (๐•จ=๐•ฉ) (XorโŸœ(1โŠธโŒฝ)โˆจโŠข) (0=๐•ฉ)โˆง(-1)โŒฝ๐•จ=๐•ฉ}
+
+_fixCount โ† {
+  1 ๐•Š ๐•ฉ;
+  ๐•ฉ โ‰กโ—ถโŸจ(๐•จ+1)โŠธ๐•Š, ๐•จห™โŸฉ ๐”ฝ ๐•ฉ
+}
+
+MoveAllHerds โ† (2โŠธMoveHerd)โˆ˜(1โŠธMoveHerdห˜)
+
+! 58 = MoveAllHerds _fixCount day25ExampleInput
+โ€ขOut "Day 25.1: "โˆพโ€ขFmt MoveAllHerds _fixCount day25Input
diff --git a/users/sterni/exercises/aoc/2022/.skip-subtree b/users/sterni/exercises/aoc/2022/.skip-subtree
new file mode 100644
index 0000000000..39d1894495
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/.skip-subtree
@@ -0,0 +1 @@
+nix solutions don't use readTree and the rest is non-nix
diff --git a/users/sterni/exercises/aoc/2022/01/1.bqn b/users/sterni/exercises/aoc/2022/01/1.bqn
new file mode 100644
index 0000000000..022b476aa9
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/01/1.bqn
@@ -0,0 +1,7 @@
+lib โ† โ€ขImport โ€ขpathโˆพ"/../../lib.bqn"
+input โ† lib.ReadDecยจยจ (<"") lib.SplitOn โ€ขFLines โ€ขpathโˆพ"/input"
+
+aโ€ฟยทโ€ฟb โ† +`3โ†‘โˆจ+ยดยจ input
+
+โ€ขOut "day01.1: "โˆพโ€ขFmt a
+โ€ขOut "day01.2: "โˆพโ€ขFmt b
diff --git a/users/sterni/exercises/aoc/2022/01/1.k b/users/sterni/exercises/aoc/2022/01/1.k
new file mode 100644
index 0000000000..42d64dfb6c
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/01/1.k
@@ -0,0 +1 @@
+(+\e@3#>e:(+/.'1_)'(&0=#'i)_i:0:"input")_1
diff --git a/users/sterni/exercises/aoc/2022/02/2.bqn b/users/sterni/exercises/aoc/2022/02/2.bqn
new file mode 100644
index 0000000000..65e3c817bb
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/02/2.bqn
@@ -0,0 +1,7 @@
+lib โ† โ€ขImport โ€ขpathโˆพ"/../../lib.bqn"
+i โ† 3|"ABCXYZ"โŠธโŠยจ ' ' โŠ‘ยจโˆ˜lib.SplitOnยจ โ€ขFLines โ€ขpathโˆพ"/input"
+S1 โ† {1+๐•ฉ+3ร—3|1+๐•ฉ-๐•จ}
+S2 โ† {๐•จ S1 3|๐•จ+๐•ฉ-1}
+
+โ€ขOut "day02.1: "โˆพโ€ขFmt +ยดS1ยดยจi
+โ€ขOut "day02.2: "โˆพโ€ขFmt +ยดS2ยดยจi
diff --git a/users/sterni/exercises/aoc/2022/02/2.k b/users/sterni/exercises/aoc/2022/02/2.k
new file mode 100644
index 0000000000..9b6d10058d
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/02/2.k
@@ -0,0 +1 @@
++/{{y+1+3*3!1+y-x}[x]'y,3!x+y-1}.'3!"ABCXYZ"?(0:"input")_'1
diff --git a/users/sterni/exercises/aoc/2022/03/3.bqn b/users/sterni/exercises/aoc/2022/03/3.bqn
new file mode 100644
index 0000000000..642fccd450
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/03/3.bqn
@@ -0,0 +1,8 @@
+i โ† โ€ขFLines โ€ขpathโˆพ"/input"
+c โ† โˆพ(โ†•26)โŠธ+ยจ"aA"
+P โ† {1+โŠ‘cโŠโŠ‘๐•ฉ}
+S โ† (โˆŠ/โŠฃ)
+G โ† ((โŠฃ/(โ†•รทหœโŸœโ‰ ))โŠ”โŠข)
+
+โ€ขOut "day03.1: "โˆพโ€ขFmt +ยด(P Sห)ยจ2โ€ฟโˆ˜โŠธโฅŠยจi
+โ€ขOut "day03.2: "โˆพโ€ขFmt +ยด3(P Sยด)ยจโˆ˜G i
diff --git a/users/sterni/exercises/aoc/2022/03/3.k b/users/sterni/exercises/aoc/2022/03/3.k
new file mode 100644
index 0000000000..3e31f5f32c
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/03/3.k
@@ -0,0 +1 @@
++/'(58*r<1)+r:-96+(,/{?y@&~^x?y}/')'((2 0N#)';0N 3#)@\:0:"input"
diff --git a/users/sterni/exercises/aoc/2022/04/4.bqn b/users/sterni/exercises/aoc/2022/04/4.bqn
new file mode 100644
index 0000000000..0b8f1b4500
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/04/4.bqn
@@ -0,0 +1,11 @@
+โŸจSplitOn, ReadDecโŸฉ โ† โ€ขImport "../../lib.bqn"
+
+Sections โ† {
+  aโ€ฟb โ† ReadDecยจ (<'-') SplitOn ๐•ฉ
+  โ†•โŒพ(-โŸœa) 1+b
+}
+i โ† โˆ˜โ€ฟ2โฅŠSectionsยจ โˆพ(<',') SplitOnยจ โ€ขFLines "input"
+Is โ† โˆŠยดโˆ˜((โ‹โ‰ ยจ)โŠโŠข)
+
+โ€ขOut "day04.1: "โˆพโ€ขFmt +ยด(โˆงยดIs)ห˜ i
+โ€ขOut "day04.2: "โˆพโ€ขFmt +ยด(โˆจยดIs)ห˜ i
diff --git a/users/sterni/exercises/aoc/2022/05/5.bqn b/users/sterni/exercises/aoc/2022/05/5.bqn
new file mode 100644
index 0000000000..15b0dfc805
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/05/5.bqn
@@ -0,0 +1,18 @@
+โŸจReadDec, SplitOn, IsAsciiNumโŸฉ โ† โ€ขImport "../../lib.bqn"
+rsโ€ฟrc โ† (<"") SplitOn โ€ขFLines "../05/input"
+
+stacks โ† {
+  count โ† '0'-หœโŠ‘โŒฝ' ' (โ‰ /โŠข) โŠ‘โŒฝrs
+  ' ' (โ‰ /โŠข)ยจ<ห˜ (countร—4) ((ยปโˆ˜(0โŠธ=)โˆ˜(4โŠธ|)โˆ˜โ†•โŠฃ)/โ†‘) โ‰> (-1)โ†“rs
+}
+
+cmds โ† {0โ€ฟ1โ€ฟ1-หœ ReadDecยจ ((โˆงยดIsAsciiNum)ยจ/โŠข) (<' ') SplitOn ๐•ฉ}ยจ rc
+
+_ApplyCmd โ† {
+  s Fn _self cโ€ฟfโ€ฟt :
+  mโ€ฟk โ† 2โ†‘ c ((โ‰คโŸœ(โ†•โ‰ ))โŠ”โŠข) fโŠ‘s
+  (Fn m)โŠธโˆพโŒพ(tโŠธโŠ‘) kห™โŒพ(fโŠธโŠ‘) s
+}
+
+โ€ขOut "day05.1: "โˆพโŠ‘ยจstacks โŒฝ_ApplyCmdหœยด โŒฝ cmds
+โ€ขOut "day05.2: "โˆพโŠ‘ยจstacks โŠข_ApplyCmdหœยด โŒฝ cmds
diff --git a/users/sterni/exercises/aoc/2022/06/6.bqn b/users/sterni/exercises/aoc/2022/06/6.bqn
new file mode 100644
index 0000000000..041a2e9100
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/06/6.bqn
@@ -0,0 +1,4 @@
+i โ† โŠ‘โ€ขFLines "input"
+FirstMarker โ† {๐•ฉ+โŠ‘/(โˆงยดโˆ˜ยฌโŠ’)ห˜๐•ฉโ†•i}
+โ€ขOut "day06.1: "โˆพโ€ขFmt FirstMarker 4
+โ€ขOut "day06.2: "โˆพโ€ขFmt FirstMarker 14
diff --git a/users/sterni/exercises/aoc/2022/06/6.k b/users/sterni/exercises/aoc/2022/06/6.k
new file mode 100644
index 0000000000..3dc0de0a3e
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/06/6.k
@@ -0,0 +1 @@
+4 14{x+*&x=#'?'x':y}\:1:"input"
diff --git a/users/sterni/exercises/aoc/2022/07/7.bqn b/users/sterni/exercises/aoc/2022/07/7.bqn
new file mode 100644
index 0000000000..2fc387f340
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/07/7.bqn
@@ -0,0 +1,24 @@
+lib โ† โ€ขImport "../../lib.bqn"
+cmds โ† 1โ†“ '$' ((+`= โŸœ(โŠ‘ยจ))โŠ”โŠข) โ€ขFLines "input"
+paths โ† (<โŸจโŸฉ) {
+  ๐•จ ๐•Š "$ ls": ๐•จ;
+  ๐•จ ๐•Š "$ cd /": โŸจโŸฉ;
+  ๐•จ ๐•Š "$ cd ..": (-1)โ†“๐•จ;
+  ๐•จ ๐•Š ๐•ฉ: ๐•จโˆพ<5โ†“๐•ฉ # "$ cd โ€ฆ"
+}` โŠ‘ยจcmds
+ParseLs โ† {
+  dirsโ€ฟfiles โ† 2โ†‘((lib.IsAsciiNumโˆ˜โŠ‘โˆ˜โŠ‘)ยจโŠ”โŠข) ((<' ')โŠธlib.SplitOn)ยจ 1โ†“๐•ฉ
+  (1โŠ‘ยจdirs)โ‹ˆ(lib.ReadDec 0โŠธโŠ‘)ยจfiles
+}
+dirlists โ† ParseLsโŒพ(1โŠธโŠ‘)ยจโฅŠโ‹ˆห˜(("$ cd"โŠธโ‰ขโŸœ(4โŠธโ†‘)โˆ˜โŠ‘ยจ)โˆ˜(1โŠธโŠ)ห˜/โŠข) (โ’โ‰ ยจpaths)โŠโ‰pathsโ‰cmds
+DirSize โ† {โŠ‘๐•จ (โŠ‘โˆ˜(1โŠธโŠ‘ยจโˆ˜โŠฃโŠโŠข)โŠ‘โŠฃ) <๐•ฉ}
+DirName โ† โˆพ'/'โŠธโˆพยจ
+dirsizes โ† โŠ‘ยจ โŸจโŸฉ {
+  szs ๐•Š โŸจdir, subdirsโ€ฟfilesโŸฉ:
+  Canon โ† DirName dirโŠธโˆพโŸœโ‹ˆ
+  sz โ† +ยดfilesโˆพszsโŠธDirSizeโˆ˜Canonยจ subdirs
+  szsโˆพ<szโ‹ˆDirName dir
+}หœยด โŒฝdirlists
+
+โ€ขOut "day07.1: "โˆพโ€ขFmt +ยด 100000 (โ‰ฅ/โŠข) dirsizes
+โ€ขOut "day07.2: "โˆพโ€ขFmt (30000000-70000000-โŒˆยดdirsizes) โŒŠยดโˆ˜(โ‰ค/โŠข) dirsizes
diff --git a/users/sterni/exercises/aoc/2022/08/8.bqn b/users/sterni/exercises/aoc/2022/08/8.bqn
new file mode 100644
index 0000000000..91a16d9573
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/08/8.bqn
@@ -0,0 +1,15 @@
+i โ† >'0'-หœโ€ขFLines "input"
+Visible โ† {
+  _vis โ† {(โŒˆ`โˆ˜(ยฏ1โŠธยปห˜โŒพโ‰)<โŠข)โŒพ๐• ๐•—}
+  โˆจยด๐•ฉ _visยจ โŸจโŠข,โŒฝ,โ‰,โŒฝโ‰โŸฉ
+}
+
+โ€ขOut "day08.1: "โˆพโ€ขFmt +ยดโฅŠVisible i
+
+ViewingDistances โ† {
+  DirView โ† {โ‰ 1(ยปโŸœ(โˆง`(โŠ‘๐•ฉ)โŠธ>)/โŠข) 1โ†“๐•ฉ}
+  _spliceDir โ† {! =ยดโ‰ข๐•— โ‹„ ๐•โผ(โŠขโ†“(โŠโŸœ(๐•๐•—))โˆ˜โŠฃ)ยดยจ โ‹ˆโŒœหœโ†•โ‰ ๐•—}
+  ร—ยด DirViewยจยจ ๐•ฉ _spliceDirยจ โŸจโŠข, โŒฝห˜, โ‰, โŒฝห˜โ‰โŸฉ
+}
+
+โ€ขOut "day08.2: "โˆพโ€ขFmt โŒˆยดโฅŠViewingDistances i
diff --git a/users/sterni/exercises/aoc/2022/09/9.bqn b/users/sterni/exercises/aoc/2022/09/9.bqn
new file mode 100644
index 0000000000..fff38b5913
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/09/9.bqn
@@ -0,0 +1,17 @@
+โŸจSplitOn,ReadDecโŸฉ โ† โ€ขImport "../../lib.bqn"
+i โ† ReadDecโŒพ(1โŠธโŠ‘)ยจ (<' ')โŠธSplitOnยจ โ€ขFLines "input"
+
+UnitDelta โ† (โŠขรท(|+0โŠธ=))
+ExpandStep โ† {
+  ๐•Š "L"โ€ฟl: ๐•Š (-l)โ€ฟ0;
+  ๐•Š "R"โ€ฟr: ๐•Š rโ€ฟ0;
+  ๐•Š "U"โ€ฟu: ๐•Š 0โ€ฟu;
+  ๐•Š "D"โ€ฟd: ๐•Š 0โ€ฟ(-d);
+  ๐•Š delta: ((โŒˆยด|)โฅŠ<โˆ˜UnitDelta) delta
+}
+
+Step โ† {knots ๐•Š delta: {h ๐•Š t: (UnitDelta h-t) +โŸ(1<โŒˆยด|h-t) t}` (deltaโŠธ+)โŒพโŠ‘ knots}
+Visited โ† {+ยด0=โŠ’(ยฏ1โŠธโŠ‘)ยจ(<๐•จโฅŠ<0โ€ฟ0) Step` โˆพExpandStepยจ ๐•ฉ}
+
+โ€ขOut "day09.1: "โˆพโ€ขFmt  2 Visited i
+โ€ขOut "day09.2: "โˆพโ€ขFmt 10 Visited i
diff --git a/users/sterni/exercises/aoc/2022/10/10.bqn b/users/sterni/exercises/aoc/2022/10/10.bqn
new file mode 100644
index 0000000000..04e3d6a8e5
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/10/10.bqn
@@ -0,0 +1,25 @@
+โŸจSplitOn,ReadDecโŸฉ โ† โ€ขImport "../../lib.bqn"
+# Instead of implementing the VM described in the problem, translate the
+# program to instructions with equivalent timing for a similar VM that
+# only needs 1 cycle for every instruction.
+is โ† โˆพ{"noop": <"noop"; ๐•ฉ: (<"noop")โˆพ<ReadDecโŒพ(1โŠธโŠ‘) (<' ') SplitOn ๐•ฉ}ยจ โ€ขFLines "input"
+
+Op โ† {x ๐•Š "noop": x;x ๐•Š "addx"โ€ฟi: x+i}
+Draw โ† {๐•Š cโ€ฟxโ€ฟpic: picโˆจ(โ†•240)((c-1)โŠธ=โˆ˜โŠฃโˆงโˆŠ)(โŒŠโŒพ(รทโŸœ40)c)+ยฏ1+x+โ†•3}
+_vm โ† {
+  is _self s: (โŠ‘s)โ‰ฅโ‰ is? s;
+  is _self prevโ€ฟsumโ€ฟxโ€ฟpic:
+  cycle โ† prev+1
+  is _self โŸจ
+    cycle,
+    sum+xร—cycleร—โŠ‘cycleโˆŠ20โ€ฟ60โ€ฟ100โ€ฟ140โ€ฟ180โ€ฟ220,
+    x Op (ยฏ1+cycle)โŠ‘is,
+    Draw cycleโ€ฟxโ€ฟpic
+  โŸฉ
+}
+
+ยทโ€ฟsumโ€ฟยทโ€ฟpic โ† is _vm 1โ€ฟ0โ€ฟ1โ€ฟ(240โฅŠ0)
+
+โ€ขOut "day10.1: "โˆพโ€ขFmt sum
+โ€ขOut "day10.2:"
+โ€ขShow ".#" โŠหœ โˆ˜โ€ฟ40โฅŠpic
diff --git a/users/sterni/exercises/aoc/2022/11/11.bqn b/users/sterni/exercises/aoc/2022/11/11.bqn
new file mode 100644
index 0000000000..12b9b5097a
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/11/11.bqn
@@ -0,0 +1,41 @@
+# needs export BQNLIBS=/path/to/mlochbaum/bqn-libs
+โŸจReadDec,ImportBqnLibsโŸฉ โ† โ€ขImport "../../lib.bqn"
+โŸจSplitโŸฉ โ† ImportBqnLibs "strings.bqn"
+MakeOp โ† {
+  ๐•Š aโ€ฟ"+"โ€ฟb: ๐•Š aโ€ฟ+โ€ฟb;
+  ๐•Š aโ€ฟ"*"โ€ฟb: ๐•Š aโ€ฟร—โ€ฟb;
+  ๐•Š aโ€ฟopโ€ฟb:
+  isโ€ฟxs โ† (<"old") (โ‰กยจโŠ”โŠข) aโ€ฟb
+  {opยด (๐•ฉโ‹†โ‰ xs) โˆพReadDecยจ is}
+}
+ParseMonkey โ† {
+  ยทโ€ฟitemsโ€ฟopโ€ฟifโ€ฟthenโ€ฟelse:
+  {
+    initial โ‡ ReadDecยจ ", " Split 18โ†“items
+    op โ‡ MakeOp " " Split 19โ†“op
+    if โ‡ ReadDec 21โ†“if
+    then โ‡ ReadDec 29โ†“then
+    else โ‡ ReadDec 30โ†“else
+  }
+}
+monkeys โ† ParseMonkeyยจ 1โ†“' '((+`(โ‰ โŸœโŠ‘)ยจ)โŠ”โŠข)0(โ‰ โŸœโ‰ ยจ/โŠข)โ€ขFLines "input"
+items โ† {๐•ฉ.initial}ยจ monkeys
+lim โ† ร—ยด{๐•ฉ.if}ยจ monkeys
+
+Sim โ† {
+  div ๐•Š len:
+  Turn โ† {
+    items ๐•Š turnidx:
+    i โ† (โ‰ monkeys)|turnidx
+    m โ† iโŠ‘monkeys
+
+    worry โ† lim|โŒŠdivรทหœ m.Opยจ iโŠ‘items
+    elseโ€ฟthen โ† 2โ†‘0 (=โŸœ(m.ifโŠธ|)โŠ”โŠข) worry
+
+    โŸจthen, elseโŸฉโŠธ(โˆพหœยจ)โŒพ(m.thenโ€ฟm.elseโŠธโŠ) โŸจโŸฉห™โŒพ(iโŠธโŠ‘) items
+  }
+  ร—ยด2โ†‘โˆจ+ห(<items) ((โ‰ โŠ‘)โŠธ(>((โ†•โŠฃ)=|)ยจ)ร—(โ‰ ยจห˜)โˆ˜>โˆ˜(โŠฃยปTurn`)) โ†•lenร—โ‰ items
+}
+
+โ€ขOut "day11.1: "โˆพโ€ขFmt 3 Sim 20
+โ€ขOut "day11.2: "โˆพโ€ขFmt 1 Sim 10000
diff --git a/users/sterni/exercises/aoc/2022/12/12.bqn b/users/sterni/exercises/aoc/2022/12/12.bqn
new file mode 100644
index 0000000000..cf42f6f899
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/12/12.bqn
@@ -0,0 +1,16 @@
+โŸจImportBqnLibs,_fixโŸฉ โ† โ€ขImport "../../lib.bqn"
+โŸจReplaceAllโŸฉ โ† ImportBqnLibs "strings.bqn"
+i โ† >โ€ขFLines "input"
+
+elevation โ† 'a'-หœโŸจ"S","E"โŸฉโ€ฟโŸจ"a","z"โŸฉ ReplaceAllโŒพโฅŠ i
+starts โ† (โŠโŸœโˆžโ€ฟ0)ยจโŸจ'S'=i,0=elevationโŸฉ
+end โ† 'E'=i
+
+Step โ† {
+  ๐•Š steps:
+  Go โ† {๐•โผ((โŠขโˆพยจโ†•โˆ˜โ‰ข)(โ‰คโŸœ(โˆžโŠธยปห˜โˆ˜+โŸœ1))หœ๐•elevation)โŠ‘>((โฅŠโŸœโˆž)โˆ˜โ‰ขโŠธโ‹ˆ)หœโˆžโŠธยปห˜1+๐•steps}
+  stepsโŒŠยดGoยจโŸจโŠข,โŒฝห˜,โ‰,โ‰โŒฝโŸฉ
+}
+Shortest โ† {โŠ‘end/โŠธโŠโ—‹โฅŠStep _fix ๐•ฉ}
+
+โ€ขOutยจ "day12.1: "โ€ฟ"day12.2: "โˆพยจ โ€ขFmtโˆ˜Shortestยจ starts
diff --git a/users/sterni/exercises/aoc/2022/13/13.bqn b/users/sterni/exercises/aoc/2022/13/13.bqn
new file mode 100644
index 0000000000..0242cc5093
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/13/13.bqn
@@ -0,0 +1,14 @@
+lib โ† โ€ขImport "../../lib.bqn"
+str โ† lib.ImportBqnLibs "strings.bqn"
+i โ† >โŸจ"[","]"โŸฉโ€ฟโŸจ"โŸจ","โŸฉ"โŸฉโŠธ(โ€ขBQN str.ReplaceAll)ยจยจ0((โŸจโŸฉโŠธโ‰กยจยฏ1ห™โŸโŠฃยจ(+`(=โŸœโ‰ )ยจ))โŠ”โŠข)โ€ขFLines "input"
+
+Ord โ† {
+  i1 ๐•Š i2: 1โ€ฟ1โ‰กโ€ขTypeยจ i1โ€ฟi2? ยฏ1โ€ฟ1โ€ฟ0โŠ‘หœi1(=+โ‰ค)i2;
+  i1 ๐•Š l2: 1โ€ฟ0โ‰กโ€ขTypeยจ i1โ€ฟl2? l2 Ordหœ โ‹ˆi1;
+  l1 ๐•Š i2: 0โ€ฟ1โ‰กโ€ขTypeยจ l1โ€ฟi2? l1 Ord โ‹ˆi2;
+  l1 ๐•Š l2: 0โ€ฟ0โ‰กโ€ขTypeยจ l1โ€ฟl2?
+  โŠ‘1โ†‘0(โ‰ /โŠข)l1 Ordยจโ—‹((l1โŒˆโ—‹โ‰ l2)โŠธ(โ†‘โŒพ(+โŸœ1))) l2
+}
+
+โ€ขOut "day13.1: "โˆพโ€ขFmt +ยด1+/(1โŠธ=Ordยด)ห˜i
+โ€ขOut "day13.2: "โˆพโ€ขFmt ร—ยด1โ€ฟ2++ยดห˜ยฏ1=โŸจโŸจ2โŸฉโŸฉโ€ฟโŸจโŸจ6โŸฉโŸฉOrdโŒœโฅŠi
diff --git a/users/sterni/exercises/aoc/2022/15/15.bqn b/users/sterni/exercises/aoc/2022/15/15.bqn
new file mode 100644
index 0000000000..e47355856b
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/15/15.bqn
@@ -0,0 +1,18 @@
+lib โ† โ€ขImport "../../lib.bqn"
+
+F โ† ยฌโˆ˜('-'โŠธ=โˆจlib.IsAsciiNum)
+i โ† โŒฝห˜ห˜โˆ˜โ€ฟ2โ€ฟ2โฅŠlib.ReadDecยจ>(0โŠธ<โŸœโ‰ ยจ/โŠข)โˆ˜((F ยฏ1ห™โŸโŠฃยจ(+`F))โŠ”โŠข)ยจ โ€ขFLines "input"
+
+ssp โ† 4000000
+
+sds โ† (โŠห˜โˆพห˜(+ยดห˜(|(-ห))ห˜)) i
+
+# _fix is needed to deal with e.g. โŸจ0โ€ฟ15, 5โ€ฟ8, 12โ€ฟ23โŸฉ
+MergeRanges โ† ((โŠ‘โˆพโŠ‘โˆ˜โŒฝ)โˆ˜โˆงโˆ˜โˆพ)ยจโˆ˜(+`โˆ˜((<โˆžโ€ฟโˆž)โŠธยป{<ยด1โ€ฟ2โŠ๐•จโˆพ๐•ฉ}ยจโŠข)โŠ”โŠข) lib._fix
+
+Range โ† {cky ๐•Š yโ€ฟxโ€ฟd: x+ยฏ1โ€ฟ1ร—d-|cky-y}
+RangesY โ† {<ห˜โˆง๐•ฉ(โŠฃRangeห˜({cky ๐•Š yโ€ฟยทโ€ฟd: dโ‰ฅ|y-cky}ห˜/โŠข))sds}
+OutRangeY โ† {(1<โ‰ )โ—ถโŸจ0ห™,๐•ฉโŠธ+โˆ˜(sspโŠธร—โŸœ(+โŸœ1))โˆ˜(1โŠธโŠ‘)โˆ˜โˆพโŸฉ MergeRanges sspโŒŠ0โŒˆRangesY ๐•ฉ}
+
+โ€ขOut "day15.1: "โˆพโ€ขFmt +ยด-หœยดยจMergeRanges RangesY 2รทหœssp
+โ€ขOut "day15.2: "โˆพโ€ขFmt +ยดOutRangeYยจโ†•ssp
diff --git a/users/sterni/exercises/aoc/2022/16/16.k b/users/sterni/exercises/aoc/2022/16/16.k
new file mode 100644
index 0000000000..40d5ace60e
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/16/16.k
@@ -0,0 +1,21 @@
+/ parsing
+(f;r;t):+{x[1 4],,9_x^'","}'" "\' 0:"input"
+(f;t):`s$''(f;t)
+r:f!{`I$x[&(x<58)&47<x]}' r
+g:f!t
+
+/ total flow scoring
+tf: {+/+\{x,(30-#x)#0}((r.)'x)*`XX=':x}
+
+/ valves to open
+vto: f^(=r).0;
+
+/ paths to keep after each step
+best: {x[(1000&#x)#>tf'x]}
+
+p:{[n;ps]
+  ms:ps[&~fin:(#vto)={#?x[&`X=':x]}'ps];
+  rt: best[ps[&fin],(ms[w],'(,*|)'ms[w:&{(0|/vto=l)&~|/0&':x=l:*|x}'ms]),,/{x,/:,'g[*|x]}' ms];
+  $[n>1;o[n-1;rt];rt]}
+
+*tf'p[29;,,`AA]
diff --git a/users/sterni/exercises/aoc/2022/17/17.bqn b/users/sterni/exercises/aoc/2022/17/17.bqn
new file mode 100644
index 0000000000..21b94221aa
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/17/17.bqn
@@ -0,0 +1,51 @@
+jets โ† '>'= "<>" (โˆŠหœ/โŠข) โ€ขFChars "input"
+pieces โ† >ยจโŸจ1โ€ฟ1โ€ฟ1โ€ฟ1โŸฉโ€ฟโŸจ0โ€ฟ1โ€ฟ0,1โ€ฟ1โ€ฟ1,0โ€ฟ1โ€ฟ0โŸฉโ€ฟโŸจ0โ€ฟ0โ€ฟ1,0โ€ฟ0โ€ฟ1,1โ€ฟ1โ€ฟ1โŸฉโ€ฟโŸจโ‹ˆ1,โ‹ˆ1,โ‹ˆ1,โ‹ˆ1โŸฉโ€ฟโŸจ1โ€ฟ1,1โ€ฟ1โŸฉ
+w โ† 7
+initial โ† 0โ€ฟwโฅŠ0
+
+# Warning: mutated global!
+ji โ† 0
+_try โ† {(โŠข ๐•ฉห™โŸ(โ‰ โ—‹(+ยดโˆ˜โฅŠโˆ˜โˆจโŸœ๐•จ)) ๐”ฝ) ๐•ฉ}
+Fall โ† {
+  pushed โ† ๐•จ ((jiโŠ‘jets)โ—ถยซโ€ฟยป)ห˜ _try ๐•ฉ
+  ji โ†ฉ (โ‰ jets)|ji+1
+  fallen โ† ๐•จ ยป _try pushed
+  ๐•จ ๐•ŠโŸ(pushedโ‰ขfallen) fallen
+}
+Height โ† โ‰ โˆ˜(โˆจยดห˜/โŠข)
+ThrowPiece โ† {
+  piece โ† ๐•ฉ (|หœโŸœโ‰ โŠ‘โŠข) pieces
+  chamber โ† (((3+โ‰ piece)โŠธ+โˆ˜โŠ‘โˆ˜(1โŠธโ†‘)โˆ˜โŒฝโˆ˜(1โŠธ+)โˆ˜/โˆจยดห˜)โ†‘โŠข)โŒพโŒฝ๐•จ
+  falling โ† (โ‰ chamber)โ†‘(ยปโŸ2 wโŠธโ†‘)ห˜piece
+  chamber (โŠฃโˆจFall) falling
+}
+
+โ€ขOut "day17.1: "โˆพโ€ขFmt Height initial ThrowPieceหœยด โŒฝโ†•2022
+
+# https://mlochbaum.github.io/BQN/doc/control.html#while
+While โ† {๐•ฉ{๐”ฝโŸ๐”พโˆ˜๐”ฝ_๐•ฃ_๐”พโˆ˜๐”ฝโŸ๐”พ๐•ฉ}๐•จ@}ยด
+{
+  target โ† 1000000000000
+  ji โ†ฉ 0 โ‹„ i โ† 0 โ‹„ res โ† @
+
+  chamber โ† initial
+  cycles โ† โŸจโ‰ pieces,โ‰ jetsโŸฉโฅŠ<โŸจโŸฉ
+
+  While {๐•คโ‹„res=@}โ€ฟ{๐•ค
+    chamber โ†ฉ chamber ThrowPiece i
+    i +โ†ฉ 1
+
+    t โ† i|หœโ‰ pieces
+    cycles โ†ฉ {
+      new โ† ๐•ฉโˆพ<iโ‹ˆHeight chamber
+      res โ†ฉ {๐•Š ๐•ฉ:
+        โŸจpl,hlโŸฉโ€ฟยท โ† chk โ† ยฏ2โ†‘new
+        pdโ€ฟhd โ† -ยดโŒฝchk
+        @ห™โŸ(0โ‰ pd|target-pl) hl+hdร—pdรทหœtarget-pl
+      }โŸ(1<โ‰ new) @
+      new
+    }โŒพ(tโ€ฟjiโŠธโŠ‘) cycles
+  }
+
+  โ€ขOut "day17.2: "โˆพโ€ขFmt res
+}
diff --git a/users/sterni/exercises/aoc/2022/18/18.bqn b/users/sterni/exercises/aoc/2022/18/18.bqn
new file mode 100644
index 0000000000..76ec569fed
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/18/18.bqn
@@ -0,0 +1,14 @@
+lib โ† โ€ขImport "../../lib.bqn"
+
+i โ† (lib.ReadDecยจ(<',')โŠธlib.SplitOn)ยจ โ€ขFLines "input"
+dim โ† 1+โŒˆยดi
+cubes โ† iโˆŠหœโ†•dim
+
+views โ† โŸจ0โ€ฟ1โ€ฟ2, 1โ€ฟ2โ€ฟ0, 2โ€ฟ0โ€ฟ1โŸฉ
+Exposed โ† {(6ร—+ยดโฅŠ๐•ฉ)-2ร—+ยดviews{+ยดโฅŠ(โˆงหห˜)2โ†•๐•จโ‰๐•ฉ}ยจ<๐•ฉ}
+Interior โ† {(ยฌ๐•ฉ)โˆงยดviews{((lib.Xor`โˆ˜((โˆŠโˆงโŠข)โˆจยปโˆ˜(โˆŠโŒพโŒฝโˆงโŠข)))โŽ‰1)โŒพ(๐•จโŠธโ‰)๐•ฉ}ยจ<๐•ฉ}
+Displace โ† {โŒˆยด(โฅŠโŠขโ€ฟโŒฝโ‹ˆโŒœviews){Fโ€ฟa ๐•Š ๐•ฉ:((-โˆ˜ยฌโˆ˜(ยป((0โŠธ=โŠฃ)โˆง>)โŠข)โŒˆโŠข)โŽ‰1)โŒพ(F aโŠธโ‰)๐•ฉ}ยจ<๐•ฉ}
+Exterior โ† (โŠข-โ—‹Exposed ยฏ1โŠธ=โˆ˜(Displace lib._fix)โˆ˜(-โˆ˜Interior+โŠข))
+
+โ€ขOut "day18.1: "โˆพโ€ขFmt Exposed cubes
+โ€ขOut "day18.2: "โˆพโ€ขFmt Exterior cubes
diff --git a/users/sterni/exercises/aoc/2022/20/20.bqn b/users/sterni/exercises/aoc/2022/20/20.bqn
new file mode 100644
index 0000000000..8d4c905e87
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/20/20.bqn
@@ -0,0 +1,13 @@
+โŸจReadDecโŸฉ โ† โ€ขImport "../../lib.bqn"
+enc โ† ReadDecยจ โ€ขFLines "input"
+
+CoordSum โ† +ยดโˆ˜(1000โ€ฟ2000โ€ฟ3000โŠธ((โŠขโ‰ โŠธ|+โŸœ(โŠ‘โˆ˜(/=โŸœ0)โˆ˜โŠข))โŠโŠข))
+Mix โ† {
+  M โ† {m ๐•Š i:
+    l โ† โ‰ m
+    i {n โ† (l-1)|(๐•ฉโŠ‘m)+โŠ‘/๐•ฉ=๐•จ โ‹„ (nโŠธโ†‘(โˆพโŸœ๐•ฉ)โŠธโˆพnโŠธโ†“) ๐•ฉ(โ‰ /โŠข)๐•จ}หœยด โŒฝโ†•l
+  }
+  CoordSum ((โŠขMโŸ๐•จโ†•โˆ˜โ‰ )โŠโŠข) ๐•ฉ
+}
+โ€ขOut "day20.1: "โˆพโ€ขFmt 1 Mix enc
+โ€ขOut "day20.2: "โˆพโ€ขFmt 10 Mix 811589153ร—enc
diff --git a/users/sterni/exercises/aoc/2022/21/21.bqn b/users/sterni/exercises/aoc/2022/21/21.bqn
new file mode 100644
index 0000000000..2f91f55d44
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/21/21.bqn
@@ -0,0 +1,25 @@
+โŸจImportBqnLibs, IsAsciiNum, ReadDecโŸฉ โ† โ€ขImport "../../lib.bqn"
+โŸจReplaceAll, SplitโŸฉ โ† ImportBqnLibs "strings.bqn"
+
+i โ† ": "โŠธSplitยจ โ€ขFLines "input"
+ReplaceInts โ† {
+  ๐•Š ๐•ฉ: ๐•Šยด 2โ†‘(ยฌโˆ˜(โˆงยดIsAsciiNumโˆ˜โŠ‘โˆ˜โŒฝ)ยจโŠ”โŠข) ๐•ฉ;
+  # TODO: Efficient replace on tokens
+  is ๐•Š es: (((โ€ขFmtโŸ(0โŠธโ‰ โ€ขType))ยจโŒพ(1โŠธโŠ‘) <ห˜โ‰>is)โŠธReplaceAllโŒพ(1โŠธโŠ‘))ยจ es
+}
+
+c โ† 0
+CanEval โ† (IsAsciiNumโˆจโˆŠโŸœ"+-/* ")
+Eval โ† {
+  aโ€ฟsโ€ฟb โ† " " Split ๐•ฉ
+  f โ† โŠ‘+โ€ฟ-โ€ฟร—โ€ฟรทโŠหœ"+-*/"โŠs
+  a Fโ—‹ReadDec b
+}
+EvalExprs โ† {
+  pโ€ฟe โ† 2โ†‘((โˆงยดCanEvalโˆ˜โŠ‘โˆ˜โŒฝ)ยจโŠ”โŠข) ๐•ฉ
+  ev โ† (EvalโŒพ(โŠ‘โŒฝ))ยจ e
+  c +โ†ฉ1
+  (โŠ‘(โŠ‘ยจev)โˆŠหœ<"root")โ—ถโŸจEvalExprsโˆ˜(ReplaceIntsโŸœp),1โŠธโŠ‘โŠ‘โŸฉ ev
+}
+
+โ€ขShow EvalExprs ReplaceInts i
diff --git a/users/sterni/exercises/aoc/2022/25/25.bqn b/users/sterni/exercises/aoc/2022/25/25.bqn
new file mode 100644
index 0000000000..921099141f
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/25/25.bqn
@@ -0,0 +1,4 @@
+c โ† "=-012"
+F โ† +ยดโˆ˜(โŠขร—(5โŠธโ‹†)โˆ˜โŒฝโˆ˜โ†•โˆ˜โ‰ )โˆ˜(-โŸœ2)โˆ˜(cโŠธโŠ)
+T โ† {cโŠหœ5|2+๐•ฉ {(โŒŠ5รทหœ๐•จ+2) (๐•ŠโŸœ(๐•จโŠธโˆพ))โŸ(0<๐•จ) ๐•ฉ} โŸจโŸฉ}
+โ€ขOut "day25.1: "โˆพT +ยดFยจ โ€ขFLines "input"
diff --git a/users/sterni/exercises/aoc/2022/25/25.k b/users/sterni/exercises/aoc/2022/25/25.k
new file mode 100644
index 0000000000..df956f002f
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/25/25.k
@@ -0,0 +1 @@
+c@2+{(1_o,0)+x+-5*o:2<x}/5\+/{5/x-2}'(c:"=-012")?0:"input"
diff --git a/users/sterni/exercises/aoc/2022/README.md b/users/sterni/exercises/aoc/2022/README.md
new file mode 100644
index 0000000000..65d51dd21f
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/README.md
@@ -0,0 +1,8 @@
+# sterni's [Advent of Code 2022](adventofcode.com/2022)
+
+I'm trying to do it in BQN again to redeem myself for my unfinished [AoC 2021](../2021),
+but will allow myself falling back to another language if I get stuck, so I actually
+complete this one.
+~~I also plan to write additional solutions in Nix (when I have the time) in order to
+throw `//tvix/eval` against some new problems.~~
+We'll see how it goes, as my December promises to be quite busy.
diff --git a/users/sterni/exercises/aoc/2022/default.nix b/users/sterni/exercises/aoc/2022/default.nix
new file mode 100644
index 0000000000..01134d1306
--- /dev/null
+++ b/users/sterni/exercises/aoc/2022/default.nix
@@ -0,0 +1,53 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  inherit (pkgs.buildPackages) cbqn ngn-k;
+
+  # input files are not checked in
+  meta.ci.skip = true;
+
+  BQNLIBS = pkgs.fetchFromGitHub {
+    owner = "mlochbaum";
+    repo = "bqn-libs";
+    rev = "d56d8ea0b8c294fac7274678d9ab112553a03f42";
+    sha256 = "1c1bkqj62v8m13jgaa32ridy0fk5iqysq5b2qwxbqxhky5zwnk9h";
+  };
+in
+
+depot.nix.readTree.drvTargets {
+  shell = pkgs.mkShell {
+    name = "aoc-2022-shell";
+    packages = [
+      cbqn
+      ngn-k
+    ];
+
+    inherit BQNLIBS;
+  };
+
+  bqn = pkgs.runCommand "bqn-aoc-2022"
+    {
+      nativeBuildInputs = [
+        cbqn
+      ];
+
+      aoc = builtins.path {
+        name = "bqn-aoc-2022";
+        path = ./../.;
+        # Need lib.bqn from ../ and all inputs as well as bqn files from ./*
+        filter = path: type:
+          lib.hasSuffix ".bqn" path || (
+            lib.hasPrefix (toString ./.) path
+            && (
+              type == "directory"
+              || lib.hasSuffix "/input" path
+            )
+          );
+      };
+
+      inherit meta BQNLIBS;
+    }
+    ''
+      find "$aoc/2022" -name '*.bqn' -exec BQN {} \; | tee "$out"
+    '';
+}
diff --git a/users/sterni/exercises/aoc/lib.bqn b/users/sterni/exercises/aoc/lib.bqn
new file mode 100644
index 0000000000..e870a5dfa4
--- /dev/null
+++ b/users/sterni/exercises/aoc/lib.bqn
@@ -0,0 +1,18 @@
+IsAsciiNum โ‡ ('0'โŠธโ‰คโˆงโ‰คโŸœ'9')
+IsAlpha โ‡ (('a'โŠธโ‰คโˆงโ‰คโŸœ'z')โˆจ('A'โŠธโ‰คโˆงโ‰คโŸœ'Z'))
+
+# based on leah2's function
+ReadInt โ‡ {
+  ๐•จ ๐•Š ๐•ฉ: '-'=โŠ‘๐•ฉ? -๐•จ ๐•Š 1โ†“๐•ฉ;
+  ๐•จ ๐•Š ๐•ฉ: (๐•จโŠธร—+โŠฃ)ยดโˆ˜โŒฝ-โŸœ'0'๐•ฉ
+}
+ReadDec โ‡ 10โŠธReadInt
+
+SplitOn โ‡ ((โŠข (-1ห™)โŸโŠฃยจ +`โˆ˜(1โŠธยป<โŠข))โˆ˜(โ‰กยจ)โŠ”โŠข)
+SplitAt โ† ((โŠฃโ‰คโ†•โˆ˜โ‰ โˆ˜โŠข)โŠ”โŠข)
+
+_fix โ‡ {๐•ฉ ๐•Šโˆ˜โŠขโŸโ‰ข ๐”ฝ ๐•ฉ}
+
+ImportBqnLibs โ‡ {โ€ขImport ๐•ฉโˆพหœ"/"โˆพหœยฏ1โ†“1โŠ‘โ€ขSH "printenv"โ€ฟ"BQNLIBS"}
+
+Xor โ‡ (ยฌโŠธโˆงโˆจโˆงโŸœยฌ)
diff --git a/users/sterni/external/flipdot-gschichtler.nix b/users/sterni/external/flipdot-gschichtler.nix
new file mode 100644
index 0000000000..58f4fe1e7c
--- /dev/null
+++ b/users/sterni/external/flipdot-gschichtler.nix
@@ -0,0 +1,9 @@
+{ pkgs, depot, ... }:
+
+import depot.users.sterni.external.sources.flipdot-gschichtler { inherit pkgs; } // {
+  # all targets we care about for depot
+  meta.ci.targets = [
+    "bahnhofshalle"
+    "warteraum"
+  ];
+}
diff --git a/users/sterni/external/likely-music.nix b/users/sterni/external/likely-music.nix
new file mode 100644
index 0000000000..cfb6d120bd
--- /dev/null
+++ b/users/sterni/external/likely-music.nix
@@ -0,0 +1,11 @@
+{ depot, pkgs, ... }:
+
+import depot.users.sterni.external.sources.likely-music
+  {
+    inherit pkgs;
+    inherit (depot.third_party) napalm;
+  } // {
+  meta.ci.targets = [
+    "likely-music"
+  ];
+}
diff --git a/users/sterni/external/sources.json b/users/sterni/external/sources.json
new file mode 100644
index 0000000000..5233aed23e
--- /dev/null
+++ b/users/sterni/external/sources.json
@@ -0,0 +1,26 @@
+{
+    "flipdot-gschichtler": {
+        "branch": "master",
+        "description": "send text to the flipdots, orderly queued",
+        "homepage": "https://flipdot.openlab-augsburg.de",
+        "owner": "openlab-aux",
+        "repo": "flipdot-gschichtler",
+        "rev": "93683a7fff04e167963b70a8906f982567646501",
+        "sha256": "134kgmlv63vzdvc3lr0rys55klmzip7qpfnyzssahihp4mjyyq16",
+        "type": "tarball",
+        "url": "https://github.com/openlab-aux/flipdot-gschichtler/archive/93683a7fff04e167963b70a8906f982567646501.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    },
+    "likely-music": {
+        "branch": "master",
+        "description": "experimental application for probabilistic music composition",
+        "homepage": "",
+        "owner": "sternenseemann",
+        "repo": "likely-music",
+        "rev": "c9bef141d846c493a045385ab8146aa28fc8ef33",
+        "sha256": "1wqgxx8wk7lrvyn9h66gga2wf7dcq7si8wq1w5gfhjnwnsrnvs6y",
+        "type": "tarball",
+        "url": "https://github.com/sternenseemann/likely-music/archive/c9bef141d846c493a045385ab8146aa28fc8ef33.tar.gz",
+        "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+    }
+}
diff --git a/users/sterni/external/sources.nix b/users/sterni/external/sources.nix
new file mode 100644
index 0000000000..cdcde6da5c
--- /dev/null
+++ b/users/sterni/external/sources.nix
@@ -0,0 +1,197 @@
+# This file has been generated by Niv.
+_:
+let
+
+  #
+  # The fetchers. fetch_<type> fetches specs of type <type>.
+  #
+
+  fetch_file = pkgs: name: spec:
+    let
+      name' = sanitizeName name + "-src";
+    in
+    if spec.builtin or true then
+      builtins_fetchurl { inherit (spec) url sha256; name = name'; }
+    else
+      pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
+
+  fetch_tarball = pkgs: name: spec:
+    let
+      name' = sanitizeName name + "-src";
+    in
+    if spec.builtin or true then
+      builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
+    else
+      pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
+
+  fetch_git = name: spec:
+    let
+      ref =
+        if spec ? ref then spec.ref else
+        if spec ? branch then "refs/heads/${spec.branch}" else
+        if spec ? tag then "refs/tags/${spec.tag}" else
+        abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
+      submodules = if spec ? submodules then spec.submodules else false;
+      submoduleArg =
+        let
+          nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0;
+          emptyArgWithWarning =
+            if submodules == true
+            then
+              builtins.trace
+                (
+                  "The niv input \"${name}\" uses submodules "
+                  + "but your nix's (${builtins.nixVersion}) builtins.fetchGit "
+                  + "does not support them"
+                )
+                { }
+            else { };
+        in
+        if nixSupportsSubmodules
+        then { inherit submodules; }
+        else emptyArgWithWarning;
+    in
+    builtins.fetchGit
+      ({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg);
+
+  fetch_local = spec: spec.path;
+
+  fetch_builtin-tarball = name: throw
+    ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
+        $ niv modify ${name} -a type=tarball -a builtin=true'';
+
+  fetch_builtin-url = name: throw
+    ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
+        $ niv modify ${name} -a type=file -a builtin=true'';
+
+  #
+  # Various helpers
+  #
+
+  # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
+  sanitizeName = name:
+    (
+      concatMapStrings (s: if builtins.isList s then "-" else s)
+        (
+          builtins.split "[^[:alnum:]+._?=-]+"
+            ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
+        )
+    );
+
+  # The set of packages used when specs are fetched using non-builtins.
+  mkPkgs = sources: system:
+    let
+      sourcesNixpkgs =
+        import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
+      hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
+      hasThisAsNixpkgsPath = <nixpkgs> == ./.;
+    in
+    if builtins.hasAttr "nixpkgs" sources
+    then sourcesNixpkgs
+    else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
+      import <nixpkgs> { }
+    else
+      abort
+        ''
+          Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
+          add a package called "nixpkgs" to your sources.json.
+        '';
+
+  # The actual fetching function.
+  fetch = pkgs: name: spec:
+
+    if ! builtins.hasAttr "type" spec then
+      abort "ERROR: niv spec ${name} does not have a 'type' attribute"
+    else if spec.type == "file" then fetch_file pkgs name spec
+    else if spec.type == "tarball" then fetch_tarball pkgs name spec
+    else if spec.type == "git" then fetch_git name spec
+    else if spec.type == "local" then fetch_local spec
+    else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
+    else if spec.type == "builtin-url" then fetch_builtin-url name
+    else
+      abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
+
+  # If the environment variable NIV_OVERRIDE_${name} is set, then use
+  # the path directly as opposed to the fetched source.
+  replace = name: drv:
+    let
+      saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
+      ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
+    in
+    if ersatz == "" then drv else
+      # this turns the string into an actual Nix path (for both absolute and
+      # relative paths)
+    if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
+
+  # Ports of functions for older nix versions
+
+  # a Nix version of mapAttrs if the built-in doesn't exist
+  mapAttrs = builtins.mapAttrs or (
+    f: set: with builtins;
+    listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
+  );
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
+  range = first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1);
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
+  stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
+
+  # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
+  stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
+  concatMapStrings = f: list: concatStrings (map f list);
+  concatStrings = builtins.concatStringsSep "";
+
+  # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
+  optionalAttrs = cond: as: if cond then as else { };
+
+  # fetchTarball version that is compatible between all the versions of Nix
+  builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
+    let
+      inherit (builtins) lessThan nixVersion fetchTarball;
+    in
+    if lessThan nixVersion "1.12" then
+      fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
+    else
+      fetchTarball attrs;
+
+  # fetchurl version that is compatible between all the versions of Nix
+  builtins_fetchurl = { url, name ? null, sha256 }@attrs:
+    let
+      inherit (builtins) lessThan nixVersion fetchurl;
+    in
+    if lessThan nixVersion "1.12" then
+      fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
+    else
+      fetchurl attrs;
+
+  # Create the final "sources" from the config
+  mkSources = config:
+    mapAttrs
+      (
+        name: spec:
+          if builtins.hasAttr "outPath" spec
+          then
+            abort
+              "The values in sources.json should not have an 'outPath' attribute"
+          else
+            spec // { outPath = replace name (fetch config.pkgs name spec); }
+      )
+      config.sources;
+
+  # The "config" used by the fetchers
+  mkConfig =
+    { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
+    , sources ? if isNull sourcesFile then { } else builtins.fromJSON (builtins.readFile sourcesFile)
+    , system ? builtins.currentSystem
+    , pkgs ? mkPkgs sources system
+    }: rec {
+      # The sources, i.e. the attribute set of spec name to spec
+      inherit sources;
+
+      # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
+      inherit pkgs;
+    };
+
+in
+mkSources (mkConfig { }) // { __functor = _: settings: mkSources (mkConfig settings); }
diff --git a/users/sterni/htmlman/default.nix b/users/sterni/htmlman/default.nix
index b88bc26410..6bf21ce2db 100644
--- a/users/sterni/htmlman/default.nix
+++ b/users/sterni/htmlman/default.nix
@@ -19,9 +19,9 @@ let
     ;
 
   bins = getBins cheddar [ "cheddar" ]
-      // getBins mandoc [ "mandoc" ]
-      // getBins coreutils [ "cat" "mv" "mkdir" ]
-      ;
+    // getBins mandoc [ "mandoc" ]
+    // getBins coreutils [ "cat" "mv" "mkdir" ]
+  ;
 
   normalizeDrv = fetchurl {
     url = "https://necolas.github.io/normalize.css/8.0.1/normalize.css";
@@ -29,7 +29,10 @@ let
   };
 
   execlineStdoutInto = target: line: [
-    "redirfd" "-w" "1" target
+    "redirfd"
+    "-w"
+    "1"
+    target
   ] ++ line;
 
   # I will not write a pure nix markdown renderer
@@ -39,16 +42,24 @@ let
   # I will not write a pure nix markdown renderer
   markdown = md:
     let
-      html = runExecline.local "rendered-markdown" {
-        stdin = md;
-      } ([
-        "importas" "-iu" "out" "out"
-      ] ++ execlineStdoutInto "$out" [
-        bins.cheddar "--about-filter" "description.md"
-      ]);
-    in builtins.readFile html;
-
-  indexTemplate = { title, description, pages ? [] }: ''
+      html = runExecline.local "rendered-markdown"
+        {
+          stdin = md;
+        }
+        ([
+          "importas"
+          "-iu"
+          "out"
+          "out"
+        ] ++ execlineStdoutInto "$out" [
+          bins.cheddar
+          "--about-filter"
+          "description.md"
+        ]);
+    in
+    builtins.readFile html;
+
+  indexTemplate = { title, description, pages ? [ ] }: ''
     <!doctype html>
     <html>
       <head>
@@ -137,40 +148,40 @@ let
 
   htmlman =
     { title
-    # title of the index page
+      # title of the index page
     , description ? ""
-    # description which is displayed after
-    # the main heading on the index page
-    , pages ? []
-    # man pages of the following structure:
-    # {
-    #   name : string;
-    #   section : int;
-    #   path : either path string;
-    # }
-    # path is optional, if it is not given,
-    # the man page source must be located at
-    # "${manDir}/${name}.${toString section}"
+      # description which is displayed after
+      # the main heading on the index page
+    , pages ? [ ]
+      # man pages of the following structure:
+      # {
+      #   name : string;
+      #   section : int;
+      #   path : either path string;
+      # }
+      # path is optional, if it is not given,
+      # the man page source must be located at
+      # "${manDir}/${name}.${toString section}"
     , manDir ? null
-    # directory in which man page sources are located
+      # directory in which man page sources are located
     , style ? defaultStyle
-    # CSS to use as a string
+      # CSS to use as a string
     , normalizeCss ? true
-    # whether to include normalize.css before the custom CSS
+      # whether to include normalize.css before the custom CSS
     , linkXr ? "all"
-    # How to handle cross references in the html output:
-    #
-    # * none:     don't convert cross references into hyperlinks
-    # * all:      link all cross references as if they were
-    #             rendered into $out by htmlman
-    # * inManDir: link to all man pages which have their source
-    #             in `manDir` and use the format string defined
-    #             in linkXrFallback for all other cross references.
+      # How to handle cross references in the html output:
+      #
+      # * none:     don't convert cross references into hyperlinks
+      # * all:      link all cross references as if they were
+      #             rendered into $out by htmlman
+      # * inManDir: link to all man pages which have their source
+      #             in `manDir` and use the format string defined
+      #             in linkXrFallback for all other cross references.
     , linkXrFallback ? "https://manpages.debian.org/unstable/%N.%S.en.html"
-    # fallback link to use if linkXr == "inManDir" and the man
-    # page is not in ${manDir}. Placeholders %N (name of page)
-    # and %S (section of page) can be used. See mandoc(1) for
-    # more information.
+      # fallback link to use if linkXr == "inManDir" and the man
+      # page is not in ${manDir}. Placeholders %N (name of page)
+      # and %S (section of page) can be used. See mandoc(1) for
+      # more information.
     }:
 
     let
@@ -188,47 +199,70 @@ let
       mandocOpts = lib.concatStringsSep "," ([
         "style=style.css"
       ] ++ linkXrEnum.match linkXr {
-        all      = [ "man=./%N.%S.html" ];
+        all = [ "man=./%N.%S.html" ];
         inManDir = [ "man=./%N.%S.html;${linkXrFallback}" ];
-        none     = [ ];
+        none = [ ];
       });
 
       html =
-        runExecline.local "htmlman-${title}" {
-          derivationArgs = {
-            inherit index style;
-            passAsFile = [ "index" "style" ];
-          };
-        } ([
-          "multisubstitute" [
-            "importas" "-iu" "out" "out"
-            "importas" "-iu" "index" "indexPath"
-            "importas" "-iu" "style" "stylePath"
-          ]
-          "if" [ bins.mkdir "-p" "$out" ]
-          "if" [ bins.mv "$index" "\${out}/index.html" ]
-          "if" (execlineStdoutInto "\${out}/style.css" [
-            "if" ([
-              bins.cat
-            ] ++ lib.optional normalizeCss normalizeDrv
+        runExecline.local "htmlman-${title}"
+          {
+            derivationArgs = {
+              inherit index style;
+              passAsFile = [ "index" "style" ];
+            };
+          }
+          ([
+            "multisubstitute"
+            [
+              "importas"
+              "-iu"
+              "out"
+              "out"
+              "importas"
+              "-iu"
+              "index"
+              "indexPath"
+              "importas"
+              "-iu"
+              "style"
+              "stylePath"
+            ]
+            "if"
+            [ bins.mkdir "-p" "$out" ]
+            "if"
+            [ bins.mv "$index" "\${out}/index.html" ]
+            "if"
+            (execlineStdoutInto "\${out}/style.css" [
+              "if"
+              ([
+                bins.cat
+              ] ++ lib.optional normalizeCss normalizeDrv
               ++ [
-              "$style"
+                "$style"
+              ])
             ])
-          ])
-          # let mandoc check for available man pages
-          "execline-cd" "${manDir}"
-        ] ++ lib.concatMap ({ name, section, ... }@p:
-          execlineStdoutInto "\${out}/${name}.${toString section}.html" [
-          "if" [
-            bins.mandoc
-            "-mdoc"
-            "-T" "html"
-            "-O" mandocOpts
-            (resolvePath p)
-          ]
-        ]) pages);
-    in html // {
+            # let mandoc check for available man pages
+            "execline-cd"
+            "${manDir}"
+          ] ++ lib.concatMap
+            ({ name, section, ... }@p:
+              execlineStdoutInto "\${out}/${name}.${toString section}.html" [
+                "if"
+                [
+                  bins.mandoc
+                  "-mdoc"
+                  "-T"
+                  "html"
+                  "-O"
+                  mandocOpts
+                  (resolvePath p)
+                ]
+              ])
+            pages);
+    in
+    html // {
       deploy = deployScript title html;
     };
 in
-  htmlman
+htmlman
diff --git a/users/sterni/keys.nix b/users/sterni/keys.nix
index 815f62ee08..0a422bc0d1 100644
--- a/users/sterni/keys.nix
+++ b/users/sterni/keys.nix
@@ -3,5 +3,6 @@
 {
   all = [
     "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJk+KvgvI2oJTppMASNUfMcMkA2G5ZNt+HnWDzaXKLlo lukas@wolfgang"
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIZTrefeqOlXDz7nnDWw820+29vLgn6R3o4N1G3lRWrr lukas@ludwig"
   ];
 }
diff --git a/users/sterni/lv/gopher/default.nix b/users/sterni/lv/gopher/default.nix
new file mode 100644
index 0000000000..f8f42c82d5
--- /dev/null
+++ b/users/sterni/lv/gopher/default.nix
@@ -0,0 +1,8 @@
+{ depot, ... }:
+
+depot.users.sterni.nix.build.buildGopherHole {
+  name = "gopher-sterni.lv";
+  dir = [
+    "๐Ÿšง closed for construction ๐Ÿšง"
+  ];
+}
diff --git a/users/sterni/machines/.skip-subtree b/users/sterni/machines/.skip-subtree
new file mode 100644
index 0000000000..a79762853e
--- /dev/null
+++ b/users/sterni/machines/.skip-subtree
@@ -0,0 +1 @@
+Subdirectories are manually reexposed by default.nix as the contain NixOS modules
diff --git a/users/sterni/machines/default.nix b/users/sterni/machines/default.nix
new file mode 100644
index 0000000000..291d9756c7
--- /dev/null
+++ b/users/sterni/machines/default.nix
@@ -0,0 +1,81 @@
+{ depot, lib, pkgs, ... }:
+
+let
+  bins = depot.nix.getBins pkgs.nq [ "fq" "nq" ];
+
+  machines = lib.mapAttrs
+    (name: _:
+      depot.ops.nixos.nixosFor (import (./. + ("/" + name)))
+    )
+    (lib.filterAttrs (_: type: type == "directory") (builtins.readDir ./.));
+
+  # TODO(sterni): share code with rebuild-system
+  localDeployScriptFor = { system, config, ... }:
+    pkgs.writeShellScript "local-deploy-${system.name}" ''
+      set -eu
+      if [[ "$(hostname)" != "${config.networking.hostName}" ]]; then
+        echo "$0: unexpected hostname: $(hostname). Are you deploying on the right machine?"
+        exit 1
+      fi
+      nix-env -p /nix/var/nix/profiles/system --set "${system}"
+      "${system}/bin/switch-to-configuration" switch
+    '';
+
+  # Builds the system on the remote machine
+  deployScriptFor = { system, ... }@machine:
+    pkgs.writeShellScript "remote-deploy-${system.name}" ''
+      set -eu
+
+      if [ $# != 1 ]; then
+        printf 'usage: %s [USER@]HOST' "$0"
+        exit 100
+      fi
+
+      readonly TARGET_HOST="$1"
+      readonly DEPLOY_DRV="${
+        builtins.unsafeDiscardOutputDependency (
+          # Wrapper script around localDeployScriptFor that merely starts the
+          # local deploy script using and nq and then waits using fq. This means
+          # we can't Ctrl-C the deploy and it won't be terminated by a lost
+          # connection.
+          pkgs.writeShellScript "queue-deploy-${system.name}" ''
+            readonly STATE_DIR="''${XDG_STATE_HOME:-$HOME/.local/state}/sterni-deploy"
+            mkdir -p "$STATE_DIR"
+
+            export NQDIR="$STATE_DIR"
+
+            "${bins.nq}" "${localDeployScriptFor machine}"
+            "${bins.fq}"
+          ''
+        ).drvPath
+      }"
+
+      nix-copy-closure -s --gzip --to "$TARGET_HOST" "$DEPLOY_DRV"
+
+      readonly DEPLOY_OUT="$(ssh "$TARGET_HOST" "nix-store -r '$DEPLOY_DRV'")"
+
+      ssh "$TARGET_HOST" "$DEPLOY_OUT"
+    '';
+
+in
+
+depot.nix.readTree.drvTargets (
+  # this somehow becomes necessarily ugly with nixpkgs-fmt
+  machines // { inherit deployScriptFor; } //
+
+  lib.mapAttrs'
+    (name: _: {
+      name = "${name}System";
+      value = machines.${name}.system;
+    })
+    machines
+
+    //
+
+  lib.mapAttrs'
+    (name: _: {
+      name = "${name}Deploy";
+      value = deployScriptFor machines.${name};
+    })
+    machines
+)
diff --git a/users/sterni/machines/edwin/default.nix b/users/sterni/machines/edwin/default.nix
new file mode 100644
index 0000000000..68f20787a9
--- /dev/null
+++ b/users/sterni/machines/edwin/default.nix
@@ -0,0 +1,19 @@
+{ config, lib, pkgs, depot, ... }:
+
+{
+  imports = [
+    # Third party modules we use
+    "${depot.third_party.agenix.src}/modules/age.nix"
+    # Basic settings
+    ../../modules/common.nix
+    # These modules touch things related to booting (filesystems, initrd networkโ€ฆ)
+    ./hardware.nix
+    ./network.nix
+    # These modules configure services, websites etc.
+    (depot.path.origSrc + "/ops/modules/btrfs-auto-scrub.nix")
+  ];
+
+  config = {
+    system.stateVersion = "20.09";
+  };
+}
diff --git a/users/sterni/machines/edwin/hardware.nix b/users/sterni/machines/edwin/hardware.nix
new file mode 100644
index 0000000000..0e33de753a
--- /dev/null
+++ b/users/sterni/machines/edwin/hardware.nix
@@ -0,0 +1,63 @@
+{ config, lib, pkgs, depot, ... }:
+
+{
+  config = {
+    boot = {
+      loader.grub = {
+        enable = true;
+        # TODO(sterni): use /dev/disk/by-id ?
+        devices = [
+          "/dev/sda"
+          "/dev/sdb"
+        ];
+      };
+
+      kernelModules = [
+        "kvm-intel"
+      ];
+
+      initrd.availableKernelModules = [
+        "ahci"
+        "sd_mod"
+        "btrfs"
+        "realtek"
+        "r8169"
+      ];
+    };
+
+    boot.initrd.luks.devices = {
+      "crypt1".device = "/dev/disk/by-uuid/02ac34ee-be10-401b-90c2-1c6aa54c4d5f";
+      "crypt2".device = "/dev/disk/by-uuid/7ce07191-e704-4aed-a60f-dfa3ce386b26";
+      "crypt-swap1".device = "/dev/disk/by-uuid/fec7155c-6a65-4f25-b271-43763e4c31eb";
+      "crypt-swap2".device = "/dev/disk/by-uuid/7b0a03fc-51de-4578-9811-94b00df09d88";
+    };
+
+    fileSystems = {
+      "/" = {
+        device = "/dev/disk/by-label/root";
+        fsType = "btrfs";
+      };
+
+      "/boot" = {
+        device = "/dev/disk/by-label/boot";
+        fsType = "btrfs";
+      };
+    };
+
+    swapDevices = [
+      { device = "/dev/disk/by-label/swap1"; }
+      { device = "/dev/disk/by-label/swap2"; }
+    ];
+
+    powerManagement.cpuFreqGovernor = "performance";
+    hardware = {
+      enableRedistributableFirmware = true;
+      cpu.intel.updateMicrocode = true;
+    };
+
+    nix.settings = {
+      max-jobs = 2;
+      cores = 4;
+    };
+  };
+}
diff --git a/users/sterni/machines/edwin/network.nix b/users/sterni/machines/edwin/network.nix
new file mode 100644
index 0000000000..1e3d4e76f0
--- /dev/null
+++ b/users/sterni/machines/edwin/network.nix
@@ -0,0 +1,62 @@
+{ config, pkgs, lib, depot, ... }:
+
+let
+  ipv6 = "2a01:4f8:151:54d0::/64";
+
+  ipv4 = "176.9.107.207";
+  gatewayv4 = "176.9.107.193";
+  netmaskv4 = "255.255.255.224";
+in
+
+{
+  config = {
+    boot = {
+      kernelParams = [
+        "ip=${ipv4}::${gatewayv4}:${netmaskv4}::eth0:none"
+      ];
+
+      initrd.network = {
+        enable = true;
+        ssh = {
+          enable = true;
+          authorizedKeys = depot.users.sterni.keys.all;
+          hostKeys = [
+            "/etc/nixos/unlock_rsa_key_openssh"
+            "/etc/nixos/unlock_ed25519_key_openssh"
+          ];
+        };
+        postCommands = ''
+          echo 'cryptsetup-askpass' >> /root/.profile
+        '';
+      };
+    };
+
+    networking = {
+      usePredictableInterfaceNames = false;
+      useDHCP = false;
+      interfaces."eth0".useDHCP = false;
+
+      hostName = "edwin";
+
+      firewall = {
+        enable = true;
+        allowPing = true;
+        allowedTCPPorts = [ 22 80 443 ];
+      };
+    };
+
+    systemd.network = {
+      enable = true;
+      networks."eth0".extraConfig = ''
+        [Match]
+        Name = eth0
+
+        [Network]
+        Address = ${ipv6}
+        Gateway = fe80::1
+        Address = ${ipv4}/27
+        Gateway = ${gatewayv4}
+      '';
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/default.nix b/users/sterni/machines/ingeborg/default.nix
new file mode 100644
index 0000000000..2d026ae05b
--- /dev/null
+++ b/users/sterni/machines/ingeborg/default.nix
@@ -0,0 +1,33 @@
+{ config, lib, pkgs, depot, ... }:
+
+{
+  imports = [
+    # Third party modules
+    "${depot.third_party.agenix.src}/modules/age.nix"
+    # Basic settings
+    ../../modules/common.nix
+    # These modules touch things related to booting (filesystems, initrd networkโ€ฆ)
+    ./hardware.nix
+    ./network.nix
+    # (More or less) pluggable service configuration
+    (depot.path.origSrc + "/ops/modules/btrfs-auto-scrub.nix")
+    ./monitoring.nix
+    ./minecraft.nix
+    ./http/sterni.lv.nix
+    ./http/code.sterni.lv.nix
+    ./http/flipdot.openlab-augsburg.de.nix
+    ./tv.nix
+    ./quassel.nix
+
+    # Inactive:
+    # ./http/likely-music.sterni.lv.nix
+    # ./gopher.nix
+
+    # TODO(sterni): fail2ban
+    # TODO(sterni): automatic backups for full recovery
+  ];
+
+  config = {
+    system.stateVersion = "24.05";
+  };
+}
diff --git a/users/sterni/machines/ingeborg/gopher.nix b/users/sterni/machines/ingeborg/gopher.nix
new file mode 100644
index 0000000000..57275e13a5
--- /dev/null
+++ b/users/sterni/machines/ingeborg/gopher.nix
@@ -0,0 +1,19 @@
+{ depot, ... }:
+
+{
+  config = {
+    services.spacecookie = {
+      enable = true;
+      openFirewall = true;
+      settings = {
+        hostname = "sterni.lv";
+        root = depot.users.sterni.lv.gopher;
+        log = {
+          enable = true;
+          hide-ips = true;
+          hide-time = true;
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/hardware.nix b/users/sterni/machines/ingeborg/hardware.nix
new file mode 100644
index 0000000000..982598131e
--- /dev/null
+++ b/users/sterni/machines/ingeborg/hardware.nix
@@ -0,0 +1,76 @@
+{ config, lib, pkgs, depot, ... }:
+
+{
+  # Booting / Kernel
+  boot = {
+    loader.grub = {
+      enable = true;
+      devices = [
+        "/dev/disk/by-id/wwn-0x5000c500a4859731"
+        "/dev/disk/by-id/wwn-0x5000c500a485c1b5"
+      ];
+    };
+
+    initrd = {
+      availableKernelModules = [
+        "ahci"
+        "btrfs"
+        "sd_mod"
+        "xhci_pci"
+        "e1000e"
+      ];
+      kernelModules = [
+        "dm-snapshot"
+      ];
+    };
+
+    swraid = {
+      enable = true;
+      mdadmConf = ''
+        ARRAY /dev/md/boot-raid metadata=1.2 name=nixos:boot-raid UUID=13007b9d:ab7a1129:c45ec40f:3c9f2111
+        ARRAY /dev/md/encrypted-container-raid metadata=1.2 name=nixos:encrypted-container-raid UUID=38dfa683:a6d30690:32a5de6f:fb7980fe
+      '';
+    };
+
+    kernelModules = [
+      "kvm-intel"
+    ];
+  };
+
+  # Filesystems
+  services.lvm.enable = true;
+
+  boot.initrd.luks.devices."container" = {
+    device = "/dev/md/encrypted-container-raid";
+    preLVM = true;
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/mainvg/root";
+      fsType = "btrfs";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-label/boot";
+      fsType = "ext4";
+    };
+  };
+
+  swapDevices = [
+    { device = "/dev/mainvg/swap"; }
+  ];
+
+  # CPU
+  hardware = {
+    cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+    enableRedistributableFirmware = true;
+  };
+
+  nix.settings = {
+    max-jobs = 2;
+    cores = 4;
+  };
+
+  powerManagement.cpuFreqGovernor = "performance";
+}
diff --git a/users/sterni/machines/ingeborg/http/code.sterni.lv.nix b/users/sterni/machines/ingeborg/http/code.sterni.lv.nix
new file mode 100644
index 0000000000..fd4975ed1d
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/code.sterni.lv.nix
@@ -0,0 +1,263 @@
+{ depot, pkgs, lib, config, ... }:
+
+let
+  virtualHost = "code.sterni.lv";
+
+  repoSections = [
+    {
+      section = "active";
+      repos = {
+        spacecookie = {
+          description = "gopher server (and library for Haskell)";
+          upstream = "https://github.com/sternenseemann/spacecookie.git";
+        };
+        "mirror/depot" = {
+          description = "monorepo for the virus lounge";
+          upstream = "https://code.tvl.fyi/depot.git";
+          cgit.defbranch = "canon";
+        };
+        "mirror/flipdot-gschichtler" = {
+          description = "message queue system for OpenLab's flipdot display";
+          upstream = "https://github.com/openlab-aux/flipdot-gschichtler.git";
+        };
+        "mirror/nixpkgs" = {
+          description = "Nix packages collection";
+          upstream = "https://github.com/nixos/nixpkgs.git";
+          cgit.enable-commit-graph = "0"; # too slow
+        };
+        "mirror/vuizvui" = {
+          description = "Nix(OS) expressions used by the OpenLab and its members";
+          upstream = "https://github.com/openlab-aux/vuizvui.git";
+        };
+      };
+    }
+    {
+      section = "poc";
+      repos = {
+        emoji-generic = {
+          description = "generic emoji library for Haskell";
+          upstream = "https://github.com/sternenseemann/emoji-generic.git";
+        };
+        grav2ty = {
+          description = "โ€œrealisticโ€ 2d space game";
+          upstream = "https://github.com/sternenseemann/grav2ty.git";
+        };
+        haskell-dot-time = {
+          description = "UTC-centric time library for haskell with dot time support";
+          cgit.defbranch = "main";
+        };
+        buchstabensuppe = {
+          description = "toy font rendering for low pixelcount, high contrast displays";
+          upstream = "https://github.com/sternenseemann/buchstabensuppe.git";
+          cgit.defbranch = "main";
+        };
+        "mirror/saneterm" = {
+          description = "modern line-oriented terminal emulator without support for TUIs";
+          upstream = "https://git.8pit.net/saneterm.git";
+        };
+      };
+    }
+    {
+      # TODO(sterni): resisort, klammeraffe, cl-ca, ponify, tinyrl
+      section = "archive";
+      repos = {
+        gopher-proxy = {
+          description = "Gopher over HTTP proxy";
+          upstream = "https://github.com/sternenseemann/gopher-proxy.git";
+        };
+        likely-music = {
+          description = "experimental application for probabilistic music composition";
+          upstream = "https://github.com/sternenseemann/likely-music.git";
+        };
+        logbook = {
+          description = "file format for keeping a personal log";
+          upstream = "https://github.com/sternenseemann/logbook.git";
+        };
+        sternenblog = {
+          description = "file based cgi blog software";
+          upstream = "https://github.com/sternenseemann/sternenblog.git";
+        };
+      };
+    }
+  ];
+
+  repoPath = name: repo: repo.path or "/srv/git/${name}.git";
+
+  cgitRepoEntry = name: repo:
+    lib.concatStringsSep "\n" (
+      [
+        "repo.url=${name}"
+        "repo.path=${repoPath name repo}"
+      ]
+      ++ lib.optional (repo ? description) "repo.desc=${repo.description}"
+      ++ lib.mapAttrsToList (n: v: "repo.${n}=${v}") repo.cgit or { }
+    );
+
+  cgitHead = pkgs.writeText "cgit-head.html" ''
+    <style>
+    #summary {
+      max-width: 80em;
+    }
+
+    #summary * {
+      max-width: 100%;
+    }
+    </style>
+  '';
+
+  cgitConfig = pkgs.writeText "cgitrc" ''
+    virtual-root=/
+
+    enable-http-clone=1
+    clone-url=https://${virtualHost}/$CGIT_REPO_URL
+
+    enable-blame=1
+    enable-log-filecount=1
+    enable-log-linecount=1
+    enable-index-owner=0
+    enable-blame=1
+    enable-commit-graph=1
+
+    root-title=code.sterni.lv
+    css=/cgit.css
+    head-include=${cgitHead}
+
+    mimetype-file=${pkgs.mime-types}/etc/mime.types
+
+    about-filter=${depot.tools.cheddar.about-filter}/bin/cheddar-about
+    source-filter=${depot.tools.cheddar}/bin/cheddar
+    readme=:README.md
+    readme=:readme.md
+
+    section-sort=0
+    ${
+      lib.concatMapStringsSep "\n" (section:
+        ''
+          section=${section.section}
+
+        ''
+        + builtins.concatStringsSep "\n\n" (lib.mapAttrsToList cgitRepoEntry section.repos)
+      ) repoSections
+    }
+  '';
+
+  /* Merge a list of attrs, but fail when the same attribute occurs twice.
+
+     Type: [ attrs ] -> attrs
+  */
+  mergeManyDistinctAttrs = lib.foldAttrs
+    (
+      val: nul:
+        if nul == null then val else throw "Every attribute name may occur only once"
+    )
+    null;
+
+  flatRepos = mergeManyDistinctAttrs
+    (builtins.map (section: section.repos) repoSections);
+
+  reposToMirror = lib.filterAttrs (_: repo: repo ? upstream) flatRepos;
+
+  # User and group name used for running the mirror scripts
+  mirroredReposOwner = "git";
+
+  # Make repo name suitable for systemd unit/timer
+  unitName = name: "mirror-${lib.strings.sanitizeDerivationName name}";
+in
+
+{
+  imports = [
+    ./nginx.nix
+    ./fcgiwrap.nix
+  ];
+
+  config = {
+    services.nginx.virtualHosts."${virtualHost}" = {
+      enableACME = true;
+      forceSSL = true;
+      root = "${pkgs.cgit-pink}/cgit/";
+      extraConfig = ''
+        try_files $uri @cgit;
+
+        location @cgit {
+          include ${pkgs.nginx}/conf/fastcgi_params;
+          fastcgi_param    SCRIPT_FILENAME ${pkgs.cgit-pink}/cgit/cgit.cgi;
+          fastcgi_param    PATH_INFO       $uri;
+          fastcgi_param    QUERY_STRING    $args;
+          fastcgi_param    HTTP_HOST       $server_name;
+          fastcgi_param    CGIT_CONFIG     ${cgitConfig};
+          fastcgi_pass     unix:${toString config.services.fcgiwrap.socketAddress};
+        }
+      '';
+    };
+
+    users = {
+      users.${mirroredReposOwner} = {
+        group = mirroredReposOwner;
+        isSystemUser = true;
+      };
+
+      groups.${mirroredReposOwner} = { };
+    };
+
+
+    systemd.timers = lib.mapAttrs'
+      (
+        name: repo:
+          {
+            name = unitName name;
+            value = {
+              description = "regularly update mirror git repository ${name}";
+              wantedBy = [ "timers.target" ];
+              enable = true;
+              timerConfig = {
+                # Fire every 6h and distribute the workload over next 6h randomly
+                OnCalendar = "*-*-* 00/6:00:00";
+                RandomizedDelaySec = "6h";
+                Persistent = true;
+              };
+            };
+          }
+      )
+      reposToMirror;
+
+    systemd.services = lib.mapAttrs'
+      (
+        name: repo:
+          {
+            name = unitName name;
+            value = {
+              description = "mirror git repository ${name}";
+              requires = [ "network-online.target" ];
+              after = [ "network-online.target" ];
+
+              script =
+                let
+                  path = repoPath name repo;
+                in
+                ''
+                  set -euo pipefail
+
+                  export PATH="${lib.makeBinPath [ pkgs.coreutils pkgs.git ]}"
+
+                  if test ! -d "${path}"; then
+                    mkdir -p "$(dirname "${path}")"
+                    git clone --mirror "${repo.upstream}" "${path}"
+                    exit 0
+                  fi
+
+                  cd "${path}"
+
+                  git fetch "${repo.upstream}" '+refs/*:refs/*' --prune
+                '';
+
+              serviceConfig = {
+                Type = "oneshot";
+                User = mirroredReposOwner;
+                Group = mirroredReposOwner;
+              };
+            };
+          }
+      )
+      reposToMirror;
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/fcgiwrap.nix b/users/sterni/machines/ingeborg/http/fcgiwrap.nix
new file mode 100644
index 0000000000..19696d85d4
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/fcgiwrap.nix
@@ -0,0 +1,15 @@
+{ ... }:
+
+{
+  imports = [
+    ./nginx.nix
+  ];
+
+  config.services.fcgiwrap = {
+    enable = true;
+    socketType = "unix";
+    socketAddress = "/run/fcgiwrap.sock";
+    user = "http";
+    group = "http";
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/flipdot.openlab-augsburg.de.nix b/users/sterni/machines/ingeborg/http/flipdot.openlab-augsburg.de.nix
new file mode 100644
index 0000000000..c86956a0a4
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/flipdot.openlab-augsburg.de.nix
@@ -0,0 +1,36 @@
+{ depot, lib, config, ... }:
+
+let
+  inherit (depot.users.sterni.external.flipdot-gschichtler)
+    bahnhofshalle
+    warteraum
+    nixosModule
+    ;
+in
+
+{
+  imports = [
+    nixosModule
+    ./nginx.nix
+  ];
+
+  config = {
+    age.secrets = lib.genAttrs [
+      "warteraum-salt"
+      "warteraum-tokens"
+    ]
+      (name: {
+        file = depot.users.sterni.secrets."${name}.age";
+      });
+
+    services.flipdot-gschichtler = {
+      enable = true;
+      virtualHost = "flipdot.openlab-augsburg.de";
+      packages = {
+        inherit bahnhofshalle warteraum;
+      };
+      saltFile = config.age.secretsDir + "/warteraum-salt";
+      tokensFile = config.age.secretsDir + "/warteraum-tokens";
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/likely-music.sterni.lv.nix b/users/sterni/machines/ingeborg/http/likely-music.sterni.lv.nix
new file mode 100644
index 0000000000..8da03ac5e6
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/likely-music.sterni.lv.nix
@@ -0,0 +1,23 @@
+{ depot, ... }:
+
+let
+  inherit (depot.users.sterni.external.likely-music)
+    nixosModule
+    likely-music
+    ;
+in
+
+{
+  imports = [
+    ./nginx.nix
+    nixosModule
+  ];
+
+  config = {
+    services.likely-music = {
+      enable = true;
+      virtualHost = "likely-music.sterni.lv";
+      package = likely-music;
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/nginx.nix b/users/sterni/machines/ingeborg/http/nginx.nix
new file mode 100644
index 0000000000..d551b8391d
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/nginx.nix
@@ -0,0 +1,30 @@
+{ ... }:
+
+{
+  config = {
+    users = {
+      users.http = {
+        isSystemUser = true;
+        group = "http";
+      };
+
+      groups.http = { };
+    };
+
+    services.nginx = {
+      enable = true;
+      recommendedTlsSettings = true;
+      recommendedGzipSettings = true;
+      recommendedProxySettings = true;
+
+      user = "http";
+      group = "http";
+
+      appendHttpConfig = ''
+        charset utf-8;
+      '';
+    };
+
+    networking.firewall.allowedTCPPorts = [ 80 443 ];
+  };
+}
diff --git a/users/sterni/machines/ingeborg/http/sterni.lv.nix b/users/sterni/machines/ingeborg/http/sterni.lv.nix
new file mode 100644
index 0000000000..50c1bac293
--- /dev/null
+++ b/users/sterni/machines/ingeborg/http/sterni.lv.nix
@@ -0,0 +1,34 @@
+{ pkgs, depot, ... }:
+
+let
+  inherit (depot.users.sterni.nix.html)
+    __findFile
+    withDoctype
+    ;
+in
+
+{
+  imports = [
+    ./nginx.nix
+  ];
+
+  config = {
+    services.nginx.virtualHosts."sterni.lv" = {
+      enableACME = true;
+      forceSSL = true;
+      root = pkgs.writeTextFile {
+        name = "sterni.lv-http-root";
+        destination = "/index.html";
+        text = withDoctype (<html> { } [
+          (<head> { } [
+            (<meta> { charset = "utf-8"; } null)
+            (<title> { } "no thoughts")
+          ])
+          (<body> { } "๐Ÿฆฉ")
+        ]);
+      };
+      # TODO(sterni): tmp.sterni.lv
+      locations."/tmp/".root = toString /srv/http;
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/irccat.nix b/users/sterni/machines/ingeborg/irccat.nix
new file mode 100644
index 0000000000..0c40f15e33
--- /dev/null
+++ b/users/sterni/machines/ingeborg/irccat.nix
@@ -0,0 +1,23 @@
+{ depot, config, pkgs, lib, ... }:
+
+{
+  imports = [
+    (depot.path.origSrc + "/ops/modules/irccat.nix")
+  ];
+
+  config = {
+    services.depot.irccat = {
+      enable = true;
+      secretsFile = builtins.toFile "empty.json" "{}"; # TODO(sterni): register
+      config = {
+        tcp.listen = ":4722"; # ircc
+        irc = {
+          server = "irc.hackint.org:6697";
+          tls = true;
+          nick = config.networking.hostName;
+          realname = "irccat";
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/minecraft.nix b/users/sterni/machines/ingeborg/minecraft.nix
new file mode 100644
index 0000000000..383fee8ca0
--- /dev/null
+++ b/users/sterni/machines/ingeborg/minecraft.nix
@@ -0,0 +1,125 @@
+{ pkgs, depot, config, ... }:
+
+let
+  carpet = pkgs.fetchurl {
+    url = "https://github.com/gnembon/fabric-carpet/releases/download/1.4.128/fabric-carpet-1.20.3-1.4.128+v231205.jar";
+    sha256 = "1jh2pb9pjwyfv1ianzykmja21nqlv175a8rg926xg3w4hhhwzrfq";
+  };
+
+  carpet-extra = pkgs.fetchurl {
+    url = "https://github.com/gnembon/carpet-extra/releases/download/1.4.128/carpet-extra-1.20.3-1.4.128.jar";
+    sha256 = "0gxwm5ayr0y5dri0kxlnrrgy9pyaim34rl6km1j42fkyvc4r8p6x";
+  };
+
+  userGroup = "minecraft";
+
+  makeJvmOpts = megs: [
+    "-Xms${toString megs}M"
+    "-Xmx${toString megs}M"
+  ];
+
+  whitelist = {
+    spreadwasser = "242a66eb-2df2-4585-9a28-ac763ad0d0f9";
+    sternenseemann = "d8e48069-1905-4886-a5da-a4ee917ee254";
+  };
+
+  rconPasswordFile = config.age.secretsDir + "/minecraft-rcon";
+
+  baseProperties = {
+    white-list = true;
+    allow-flight = true;
+    difficulty = "hard";
+    function-permission-level = 4;
+    snooper-enabled = false;
+    view-distance = 12;
+    sync-chunk-writes = "false"; # the single biggest performance fix
+    max-tick-time = 6000000; # TODO(sterni): disable watchdog via carpet
+    enforce-secure-profile = false;
+  };
+in
+
+{
+  imports = [
+    ../../modules/minecraft-fabric.nix
+    ../../modules/backup-minecraft-fabric.nix
+  ];
+
+  config = {
+    environment.systemPackages = [
+      pkgs.mcrcon
+      pkgs.jre
+    ];
+
+    users = {
+      users."${userGroup}" = {
+        isNormalUser = true;
+        openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
+        shell = "${pkgs.fish}/bin/fish";
+      };
+
+      groups."${userGroup}" = { };
+    };
+
+    age.secrets = {
+      minecraft-rcon.file = depot.users.sterni.secrets."minecraft-rcon.age";
+    };
+
+    services.backup-minecraft-fabric-servers = {
+      enable = true;
+      repository = "/srv/backup/from-local/minecraft";
+    };
+
+    services.minecraft-fabric-server = {
+      creative = {
+        enable = false; # not actively used
+        version = "1.20.4";
+        mods = [
+          carpet
+          carpet-extra
+        ];
+        world = config.users.users.${userGroup}.home + "/worlds/creative";
+
+        jvmOpts = makeJvmOpts 2048;
+        user = userGroup;
+        group = userGroup;
+
+        inherit whitelist rconPasswordFile;
+        ops = whitelist;
+
+        serverProperties = baseProperties // {
+          server-port = 25566;
+          "rcon.port" = 25576;
+          gamemode = "creative";
+          enable-command-block = true;
+          motd = "storage design server";
+          spawn-protection = 2;
+        };
+      };
+
+      carpet = {
+        enable = true;
+        version = "1.20.4";
+        mods = [
+          carpet
+          carpet-extra
+        ];
+        world = config.users.users.${userGroup}.home + "/worlds/carpet";
+
+        jvmOpts = makeJvmOpts 4096;
+        user = userGroup;
+        group = userGroup;
+
+        inherit whitelist rconPasswordFile;
+        ops = whitelist;
+
+        serverProperties = baseProperties // {
+          server-port = 25565;
+          "rcon.port" = 25575;
+          motd = "ich tu fleissig hustlen nenn mich bob der baumeister";
+
+          level-seed = 7240251176989694927; # for posterity
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/monitoring.nix b/users/sterni/machines/ingeborg/monitoring.nix
new file mode 100644
index 0000000000..6244bc5e88
--- /dev/null
+++ b/users/sterni/machines/ingeborg/monitoring.nix
@@ -0,0 +1,152 @@
+{ pkgs, lib, config, ... }:
+
+let
+  ircChannel = "#sterni.lv";
+  irccatPort =
+    builtins.replaceStrings [ ":" ] [ "" ]
+      config.services.depot.irccat.config.tcp.listen;
+
+  mkIrcMessager =
+    { name
+    , msgExpr
+    }:
+    pkgs.writeShellScript name ''
+      set -euo pipefail
+      printf '%s %s\n' ${lib.escapeShellArg ircChannel} ${msgExpr} | \
+        ${lib.getBin pkgs.netcat-openbsd}/bin/nc -N localhost ${irccatPort}
+    '';
+
+  netdataPort = 19999;
+in
+
+{
+  imports = [
+    ./irccat.nix
+  ];
+
+  config = {
+    services.depot.irccat.config.irc.channels = [
+      ircChannel
+    ];
+
+    # Since we have irccat we can wire up mdadm --monitor
+    boot.swraid.mdadmConf = ''
+      PROGRAM ${
+        mkIrcMessager {
+          name = "mdmonitor-to-irc";
+          # prog EVENT MD_DEVICE COMPONENT_DEVICE
+          msgExpr = ''"mdmonitor: $1($2''${3:+, $3})"'';
+        }
+      }
+    '';
+
+    # TODO(sterni): irc notifications (?)
+    services = {
+      smartd = {
+        enable = true;
+        autodetect = true;
+        # Short self test every day 03:00
+        # Long self test every tuesday 05:00
+        defaults.autodetected = "-a -o on -s (S/../.././03|L/../../2/05)";
+        extraOptions = [
+          "-A"
+          "/var/log/smartd/"
+        ];
+      };
+
+      netdata = {
+        enable = true;
+        config = {
+          logs = {
+            access = "syslog";
+            error = "syslog";
+            debug = "syslog";
+            health = "syslog";
+            collector = "syslog";
+          };
+          web = {
+            "default port" = toString netdataPort;
+            "bind to" = "localhost:${toString netdataPort}";
+          };
+          health = {
+            "script to execute on alarm" = pkgs.writeShellScript "simple-alarm-notify" ''
+              set -euo pipefail
+
+              # This humongous list is copied over from netdata's alarm-notify.sh
+              roles="''${1}"               # the roles that should be notified for this event
+              args_host="''${2}"           # the host generated this event
+              unique_id="''${3}"           # the unique id of this event
+              alarm_id="''${4}"            # the unique id of the alarm that generated this event
+              event_id="''${5}"            # the incremental id of the event, for this alarm id
+              when="''${6}"                # the timestamp this event occurred
+              name="''${7}"                # the name of the alarm, as given in netdata health.d entries
+              chart="''${8}"               # the name of the chart (type.id)
+              status="''${9}"              # the current status : REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
+              old_status="''${10}"         # the previous status: REMOVED, UNINITIALIZED, UNDEFINED, CLEAR, WARNING, CRITICAL
+              value="''${11}"              # the current value of the alarm
+              old_value="''${12}"          # the previous value of the alarm
+              src="''${13}"                # the line number and file the alarm has been configured
+              duration="''${14}"           # the duration in seconds of the previous alarm state
+              non_clear_duration="''${15}" # the total duration in seconds this is/was non-clear
+              units="''${16}"              # the units of the value
+              info="''${17}"               # a short description of the alarm
+              value_string="''${18}"       # friendly value (with units)
+              # shellcheck disable=SC2034
+              # variable is unused, but https://github.com/netdata/netdata/pull/5164#discussion_r255572947
+              old_value_string="''${19}"   # friendly old value (with units), previously named "old_value_string"
+              calc_expression="''${20}"    # contains the expression that was evaluated to trigger the alarm
+              calc_param_values="''${21}"  # the values of the parameters in the expression, at the time of the evaluation
+              total_warnings="''${22}"     # Total number of alarms in WARNING state
+              total_critical="''${23}"     # Total number of alarms in CRITICAL state
+              total_warn_alarms="''${24}"  # List of alarms in warning state
+              total_crit_alarms="''${25}"  # List of alarms in critical state
+              classification="''${26}"     # The class field from .conf files
+              edit_command_line="''${27}"  # The command to edit the alarm, with the line number
+              child_machine_guid="''${28}" # the machine_guid of the child
+              transition_id="''${29}"      # the transition_id of the alert
+              summary="''${30}"            # the summary text field of the alert
+
+              # Verify that they haven't extended the arg list
+              ARG_COUNT_EXPECTED=30
+
+              if [[ "$#" != "$ARG_COUNT_EXPECTED" ]]; then
+                echo "$0: WARNING: unexpected number of arguments: $#. Did netdata add more?" >&2
+              fi
+
+              MSG="netdata: $status ''${name//_/ } ($chart): ''${summary//_/ } = $value_string"
+
+              # Filter rules by chart name. This is necessary, since the "enabled alarms"
+              # filter only allows for filtering alarm types, not specific alarms
+              # belonging to that alarm.
+              case "$chart" in
+                # netdata prefers the automatically assigned names (dm-<n>, md<n>,
+                # sd<c>) over ids for alerts, so this configuration assumes that
+                # we have two physical disks which we kind of assert using the
+                # grub configuration (it is more difficult with the soft raid
+                # config).
+                # ${assert builtins.length config.boot.loader.grub.devices == 2; ""}
+                disk_util.sda | disk_util.sdb | disk_backlog.sda | disk_backlog.sdb)
+
+                  ;;
+                disk_util.* | disk_backlog.*)
+                  echo "$0: INFO: DISCARDING message: $MSG" >&2
+                  exit 0
+                  ;;
+                *)
+                  ;;
+              esac
+
+              echo "$0: INFO: sending message: $MSG" >&2
+              ${
+                mkIrcMessager {
+                  name = "trivial-send-to-irc";
+                  msgExpr = "\"$1\"";
+                }
+              } "$MSG"
+            '';
+          };
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/network.nix b/users/sterni/machines/ingeborg/network.nix
new file mode 100644
index 0000000000..fceb530d55
--- /dev/null
+++ b/users/sterni/machines/ingeborg/network.nix
@@ -0,0 +1,62 @@
+{ config, pkgs, lib, depot, ... }:
+
+let
+  ipv6 = "2a01:4f9:2a:1bc6::/64";
+
+  ipv4 = "95.216.27.158";
+  gatewayv4 = "95.216.27.129";
+  netmaskv4 = "255.255.255.192";
+in
+
+{
+  config = {
+    boot = {
+      kernelParams = [
+        "ip=${ipv4}::${gatewayv4}:${netmaskv4}::eth0:none"
+      ];
+
+      initrd.network = {
+        enable = true;
+        ssh = {
+          enable = true;
+          authorizedKeys = depot.users.sterni.keys.all;
+          hostKeys = [
+            "/etc/nixos/unlock_rsa_key_openssh"
+            "/etc/nixos/unlock_ed25519_key_openssh"
+          ];
+        };
+        postCommands = ''
+          echo 'cryptsetup-askpass' >> /root/.profile
+        '';
+      };
+    };
+
+    networking = {
+      usePredictableInterfaceNames = false;
+      useDHCP = false;
+      interfaces."eth0".useDHCP = false;
+
+      hostName = "ingeborg";
+
+      firewall = {
+        enable = true;
+        allowPing = true;
+        allowedTCPPorts = [ 22 ];
+      };
+    };
+
+    systemd.network = {
+      enable = true;
+      networks."eth0".extraConfig = ''
+        [Match]
+        Name = eth0
+
+        [Network]
+        Address = ${ipv6}
+        Gateway = fe80::1
+        Address = ${ipv4}/27
+        Gateway = ${gatewayv4}
+      '';
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/quassel.nix b/users/sterni/machines/ingeborg/quassel.nix
new file mode 100644
index 0000000000..cd8dacc917
--- /dev/null
+++ b/users/sterni/machines/ingeborg/quassel.nix
@@ -0,0 +1,18 @@
+{ depot, ... }:
+
+{
+  imports = [
+    (depot.path.origSrc + "/ops/modules/quassel.nix")
+  ];
+
+  config = {
+    services.depot.quassel = {
+      enable = true;
+      acmeHost = "sterni.lv";
+      bindAddresses = [
+        "0.0.0.0"
+        "::"
+      ];
+    };
+  };
+}
diff --git a/users/sterni/machines/ingeborg/tv.nix b/users/sterni/machines/ingeborg/tv.nix
new file mode 100644
index 0000000000..016ad256ef
--- /dev/null
+++ b/users/sterni/machines/ingeborg/tv.nix
@@ -0,0 +1,13 @@
+{ pkgs, ... }:
+
+{
+  config = {
+    # TODO(sterni): smb or nfs may be a faster alternative?
+    services.openssh.allowSFTP = true;
+
+    users.users.tv = {
+      group = "users";
+      isNormalUser = true;
+    };
+  };
+}
diff --git a/users/sterni/mblog/.gitignore b/users/sterni/mblog/.gitignore
new file mode 100644
index 0000000000..ae957fcad0
--- /dev/null
+++ b/users/sterni/mblog/.gitignore
@@ -0,0 +1,5 @@
+# local test data
+test-msg
+
+# sly C-c C-k
+*.fasl
diff --git a/users/sterni/mblog/LICENSE b/users/sterni/mblog/LICENSE
new file mode 100644
index 0000000000..f288702d2f
--- /dev/null
+++ b/users/sterni/mblog/LICENSE
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/users/sterni/mblog/cli.lisp b/users/sterni/mblog/cli.lisp
index 93be7e8b8e..555f8def53 100644
--- a/users/sterni/mblog/cli.lisp
+++ b/users/sterni/mblog/cli.lisp
@@ -1,17 +1,75 @@
-(in-package :mblog)
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
+
+(in-package :cli)
 (declaim (optimize (safety 3)))
 
-(defparameter +synopsis+ "mnote-html FILE [FILE [ ... ]]")
+;; TODO(sterni): nicer messages for various errors signaled?
+
+(defun partition-by (f seq)
+  "Split SEQ into two lists, returned as multiple values. The first list
+  contains all elements for which F returns T, the second one the remaining
+  elements."
+  (loop for x in seq
+        if (funcall f x)
+          collecting x into yes
+        else
+          collecting x into no
+        finally (return (values yes no))))
+
+(defparameter +help+ '(("mnote-html" . "FILE [FILE [ ... ]]")
+                       ("mblog"      . "MAILDIR OUT")))
+
+(defun mnote-html (name flags &rest args)
+  "Convert all note mime messages given as ARGS to HTML fragments."
+  (declare (ignore name flags))
+  (loop for arg in args
+        do (note:apple-note-html-fragment
+            (note:make-apple-note (mime:mime-message (pathname arg)))
+            *standard-output*)))
+
+(defun mblog (name flags maildir outdir)
+  "Read a MAILDIR and build an mblog in OUTDIR "
+  (declare (ignore name flags))
+  (build-mblog (pathname maildir) (pathname outdir)))
+
+(defun display-help (name flags &rest args)
+  "Print help message for current executable."
+  (declare (ignore args flags))
+  (format *error-output* "Usage: ~A ~A~%"
+          name
+          (or (cdr (assoc name +help+ :test #'string=))
+              (concatenate 'string "Unknown executable: " name))))
+
+(defun usage-error (name flags &rest args)
+  "Print help and exit with a non-zero exit code."
+  (format *error-output* "~A: usage error~%" name)
+  (display-help name args flags)
+  (uiop:quit 100))
 
-;; TODO(sterni): handle relevant conditions
 (defun main ()
-  (let* ((args (uiop:command-line-arguments))
-         (help-p (or (not args)
-                     (find-if (lambda (x)
-                                (member x '("-h" "--help" "--usage")
-                                        :test #'string=))
-                              args))))
-    (if help-p (format *error-output* "Usage: ~A~%" +synopsis+)
-      (loop for arg in args
-            do (apple-note-html-fragment
-                (mime:mime-message (pathname arg)) *standard-output*)))))
+  "Dispatch to correct main function based on arguments and UIOP:ARGV0."
+  (config:init-from-env)
+  (multiple-value-bind (flags args)
+      (partition-by (lambda (x) (starts-with #\- x))
+                    (uiop:command-line-arguments))
+
+    (let ((prog-name (pathname-name (pathname (uiop:argv0))))
+          (help-requested-p (find-if (lambda (x)
+                                       (member x '("-h" "--help" "--usage")
+                                               :test #'string=))
+                                     args)))
+      (apply
+       (if help-requested-p
+           #'display-help
+           (cond
+             ((and (string= prog-name "mnote-html")
+                   (null flags))
+              #'mnote-html)
+             ((and (string= prog-name "mblog")
+                   (null flags)
+                   (= 2 (length args)))
+              #'mblog)
+             (t #'usage-error)))
+       (append (list prog-name flags)
+               args)))))
diff --git a/users/sterni/mblog/config.lisp b/users/sterni/mblog/config.lisp
new file mode 100644
index 0000000000..0d4cbfe8ae
--- /dev/null
+++ b/users/sterni/mblog/config.lisp
@@ -0,0 +1,31 @@
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2023 by sterni
+
+(in-package :config)
+
+(eval-when (:compile-toplevel :load-toplevel)
+  (defun plist-to-alist (lst)
+    (loop for (name . (default . (parser . nil))) on lst by #'cdddr
+          collect (cons name (list default parser))))
+
+  (defun symbol-to-env-var-name (symbol)
+    (concatenate 'string
+                 "MBLOG_"
+                 (string-upcase
+                  (remove #\* (substitute #\_ #\- (string symbol)))))))
+
+(defmacro define-configuration-variables (&rest args)
+  (let ((vars (plist-to-alist args))
+        (val-var-sym (gensym)))
+    `(progn
+       ,@(loop for (name . (default nil)) in vars
+              collect `(defvar ,name ,default))
+
+       (defun init-from-env ()
+         ,@(loop for (name . (nil parser)) in vars
+                 collect
+                 `(when-let ((,val-var-sym (getenv ,(symbol-to-env-var-name name))))
+                    (setf ,name (funcall ,parser ,val-var-sym))))))))
+
+(define-configuration-variables
+  *general-buffer-size* (min 4096 qbase64:+max-bytes-length+) #'parse-integer)
diff --git a/users/sterni/mblog/default.nix b/users/sterni/mblog/default.nix
index 16ae573ba7..6ad8a10ce3 100644
--- a/users/sterni/mblog/default.nix
+++ b/users/sterni/mblog/default.nix
@@ -1,12 +1,17 @@
+# SPDX-License-Identifier: GPL-3.0-only
+# SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
 { depot, pkgs, ... }:
 
-depot.nix.buildLisp.program {
-  name = "mnote-html";
+(depot.nix.buildLisp.program {
+  name = "mblog";
 
   srcs = [
     ./packages.lisp
+    ./config.lisp
+    ./maildir.lisp
     ./transformer.lisp
     ./note.lisp
+    ./mblog.lisp
     ./cli.lisp
   ];
 
@@ -15,17 +20,28 @@ depot.nix.buildLisp.program {
       sbcl = depot.nix.buildLisp.bundled "uiop";
       default = depot.nix.buildLisp.bundled "asdf";
     }
+    depot.lisp.klatre
     depot.third_party.lisp.alexandria
     depot.third_party.lisp.closure-html
+    depot.third_party.lisp.cl-date-time-parser
     depot.third_party.lisp.cl-who
+    depot.third_party.lisp.local-time
     depot.third_party.lisp.mime4cl
   ];
 
-  main = "mblog:main";
+  main = "cli:main";
 
   # due to sclf
   brokenOn = [
     "ccl"
     "ecl"
   ];
-}
+}).overrideAttrs (super: {
+  # The built binary dispatches based on argv[0]. Building two executables would
+  # waste a lot of space.
+  buildCommand = ''
+    ${super.buildCommand}
+
+    ln -s "$out/bin/mblog" "$out/bin/mnote-html"
+  '';
+})
diff --git a/users/sterni/mblog/maildir.lisp b/users/sterni/mblog/maildir.lisp
new file mode 100644
index 0000000000..42f18c619d
--- /dev/null
+++ b/users/sterni/mblog/maildir.lisp
@@ -0,0 +1,20 @@
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022 by sterni
+
+(in-package :maildir)
+(declaim (optimize (safety 3)))
+
+(defun list (dir)
+  "Returns a list of pathnames to messages in a maildir. The messages are
+  returned in no guaranteed order. Note that this function doesn't fully
+  implement the behavior prescribed by maildir(5): It only looks at `cur`
+  and `new` and won't clean up `tmp` nor move files from `new` to `cur`,
+  since it is strictly read-only."
+  (flet ((subdir-contents (subdir)
+           (directory
+            (merge-pathnames
+             (make-pathname :directory `(:relative ,subdir)
+                            :name :wild :type :wild)
+             dir))))
+    (mapcan #'subdir-contents '("cur" "new"))))
+
diff --git a/users/sterni/mblog/mblog.lisp b/users/sterni/mblog/mblog.lisp
new file mode 100644
index 0000000000..7823bde203
--- /dev/null
+++ b/users/sterni/mblog/mblog.lisp
@@ -0,0 +1,147 @@
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
+;; SPDX-FileCopyrightText: Copyright (C) 2006-2010 by Walter C. Pelissero
+
+(in-package :mblog)
+
+;; util
+
+;; Taken from SCLF, written by Walter C. Pelissero
+(defun pathname-as-directory (pathname)
+  "Converts PATHNAME to directory form and return it."
+  (setf pathname (pathname pathname))
+  (if (pathname-name pathname)
+      (make-pathname :directory (append (or (pathname-directory pathname)
+                                            '(:relative))
+                                        (list (file-namestring pathname)))
+                     :name nil
+                     :type nil
+                     :defaults pathname)
+      pathname))
+
+(defmacro with-overwrite-file ((&rest args) &body body)
+  "Like WITH-OPEN-FILE, but creates/supersedes the given file for writing."
+  `(with-open-file (,@args :direction :output
+                           :if-exists :supersede
+                           :if-does-not-exist :create)
+     ,@body))
+
+;; CSS
+
+(defvar *style* "
+header, main {
+  width: 100%;
+  max-width: 800px;
+}
+
+main img {
+  max-width: 100%;
+}
+
+a:link, a:visited {
+  color: blue;
+}
+")
+
+;; Templating
+
+(eval-when (:compile-toplevel :load-toplevel)
+  (setf (who:html-mode) :html5))
+
+(defmacro render-page ((stream title &key root) &body body)
+  "Surround BODY with standard mblog document skeleton and render it to STREAM
+  using CL-WHO. If :ROOT is T, assume that the page is the top level index page.
+  Otherwise it is assumed to be one level below the index page."
+  `(who:with-html-output (,stream nil :prologue t)
+    (:html
+     (:head
+      (:meta :charset "utf-8")
+      (:meta :viewport "width=device-width")
+      (:title (who:esc ,title))
+      (:link :rel "stylesheet"
+             :type "text/css"
+             :href ,(concatenate 'string (if root "" "../") "style.css")))
+     (:body
+      (:header
+       (:nav
+        (:a :href ,(who:escape-string (if root "" "..")) "index")))
+      (:main ,@body)))))
+
+;; Build Logic
+
+(defun build-note-page (note note-dir)
+  "Convert NOTE to HTML and write it to index.html in NOTE-DIR alongside any
+  extra attachments NOTE contains."
+  (with-overwrite-file (html-stream (merge-pathnames "index.html" note-dir))
+    (render-page (html-stream (apple-note-subject note))
+      (:article
+       (apple-note-html-fragment note html-stream))))
+
+  (mime:do-parts (part note)
+    (unless (string= (mime:mime-id part)
+                     (mime:mime-id (note:apple-note-text-part note)))
+      (let ((attachment-in (mime:mime-body-stream part))
+            (attachment-dst (merge-pathnames
+                             (mime:mime-part-file-name part)
+                             note-dir)))
+
+        (format *error-output* "Writing attachment ~A~%" attachment-dst)
+
+        (with-overwrite-file (attachment-out attachment-dst
+                              :element-type
+                              (stream-element-type attachment-in))
+          (redirect-stream attachment-in attachment-out
+                           :buffer-size *general-buffer-size*)))))
+
+  (values))
+
+(defun build-index-page (notes-list destination)
+  "Write an overview page linking all notes in NOTE-LIST in the given order to
+  DESTINATION. The notes are assumed to be in a sibling directory named like the
+  each note's UUID."
+  (with-overwrite-file (listing-stream destination)
+    (render-page (listing-stream "mblog" :root t)
+      (:h1 "mblog")
+      (:table
+       (dolist (note notes-list)
+         (who:htm
+          (:tr
+           (:td (:a :href (who:escape-string (apple-note-uuid note))
+                    (who:esc (apple-note-subject note))))
+           (:td (who:esc
+                 (klatre:format-dottime
+                  (universal-to-timestamp (apple-note-time note)))))))))))
+  (values))
+
+(defun build-mblog (notes-dir html-dir)
+  "Take MIME messages from maildir NOTES-DIR and build a complete mblog in HTML-DIR."
+  (setf notes-dir (pathname-as-directory notes-dir))
+  (setf html-dir (pathname-as-directory html-dir))
+
+  ;; TODO(sterni): avoid rewriting if nothing was updated
+  ;; TODO(sterni): clean up deleted things
+  ;; TODO(sterni): atom feed
+
+  (let ((all-notes '()))
+    (dolist (message-path (maildir:list notes-dir))
+      (let* ((note (make-apple-note (mime:mime-message message-path)))
+             (note-dir  (merge-pathnames (make-pathname
+                                          :directory
+                                          `(:relative ,(apple-note-uuid note)))
+                                         html-dir)))
+
+        (format *error-output* "Writing note message ~A to ~A~%"
+                message-path note-dir)
+        (ensure-directories-exist note-dir)
+        (build-note-page note note-dir)
+        (push note all-notes)))
+
+    ;; reverse sort the entries by time for the index page
+    (setf all-notes (sort all-notes #'> :key #'apple-note-time))
+
+    (build-index-page all-notes (merge-pathnames "index.html" html-dir))
+
+    (with-overwrite-file (css-stream (merge-pathnames "style.css" html-dir))
+      (write-string *style* css-stream))
+
+    (values)))
diff --git a/users/sterni/mblog/note.lisp b/users/sterni/mblog/note.lisp
index fa4de0956f..f056aaa72d 100644
--- a/users/sterni/mblog/note.lisp
+++ b/users/sterni/mblog/note.lisp
@@ -1,60 +1,118 @@
-(in-package :mblog)
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
+
+(in-package :note)
 (declaim (optimize (safety 3)))
 
 ;;; util
 
 (defun html-escape-stream (in out)
   "Escape characters read from stream IN and write them to
-  stream OUT escaped using WHO:ESCAPE-CHAR-MINIMAL."
-  (loop for char = (read-char in nil nil)
-        while char
-        do (write-string (who:escape-char-minimal char) out)))
+  stream OUT escaped using WHO:ESCAPE-STRING-MINIMAL."
+  (let ((buf (make-string config:*general-buffer-size*)))
+    (loop for len = (read-sequence buf in)
+          while (> len 0)
+          do (write-string (who:escape-string-minimal (subseq buf 0 len)) out))))
 
 (defun cid-header-value (cid)
   "Takes a Content-ID as present in Apple Notes' <object> tags and properly
   surrounds them with angle brackets for a MIME header"
   (concatenate 'string "<" cid ">"))
 
+(defun find-mime-message-date (message)
+  (when-let ((date-string (car (mime:mime-message-header-values "Date" message))))
+    (date-time-parser:parse-date-time date-string)))
+
 ;;; main implementation
 
-;; TODO(sterni): make this a โ€œparserโ€ instead of a predicate
+(defun apple-note-mime-subtype-p (x)
+  (member x '("plain" "html") :test #'string-equal))
+
+(deftype apple-note-mime-subtype ()
+  '(satisfies apple-note-mime-subtype-p))
+
+(defclass apple-note (mime:mime-message)
+  ((text-part
+    :type mime:mime-text
+    :initarg :text-part
+    :reader apple-note-text-part)
+   (subject
+    :type string
+    :initarg :subject
+    :reader apple-note-subject)
+   (uuid
+    :type string
+    :initarg :uuid
+    :reader apple-note-uuid)
+   (time
+    :type integer
+    :initarg :time
+    :reader apple-note-time)
+   (mime-subtype
+    :type apple-note-mime-subtype
+    :initarg :mime-subtype
+    :reader apple-note-mime-subtype))
+  (:documentation
+   "Representation of a Note created using Apple's Notes via the IMAP backend"))
+
 (defun apple-note-p (msg)
   "Checks X-Uniform-Type-Identifier of a MIME:MIME-MESSAGE
-  to determine if a given mime message is an Apple Note."
-  (when-let (uniform-id (assoc "X-Uniform-Type-Identifier"
-                               (mime:mime-message-headers msg)
-                               :test #'string=))
-    (string= (cdr uniform-id) "com.apple.mail-note")))
-
-(defun apple-note-html-fragment (msg out)
-  "Takes a MIME:MIME-MESSAGE and writes its text content as HTML to
-  the OUT stream. The <object> tags are resolved to <img> which
-  refer to the respective attachment's filename as a relative path,
-  but extraction of the attachments must be done separately. The
-  surrounding <html> and <body> tags are stripped and <head>
-  discarded completely, so only a fragment which can be included
-  in custom templates remains."
-  (let ((text (find-mime-text-part msg)))
+  to determine if a given mime message claims to be an Apple Note."
+  (when-let (uniform-id (car (mime:mime-message-header-values
+                              "X-Uniform-Type-Identifier"
+                              msg)))
+    (string-equal uniform-id "com.apple.mail-note")))
+
+(defun make-apple-note (msg)
+  (check-type msg mime-message)
+
+  (unless (apple-note-p msg)
+    (error "Passed message is not an Apple Note according to headers"))
+
+  (let ((text-part (mime:find-mime-text-part msg))
+        (subject (car (mime:mime-message-header-values "Subject" msg :decode t)))
+        (uuid (when-let ((val (car (mime:mime-message-header-values
+                                    "X-Universally-Unique-Identifier"
+                                    msg))))
+                (string-downcase val)))
+        (time (find-mime-message-date msg)))
+    ;; The idea here is that we don't need to check a lot manually, instead
+    ;; the type annotation are going to do this for us (with sufficient safety?)
+    (change-class msg 'apple-note
+                  :text-part text-part
+                  :subject subject
+                  :uuid uuid
+                  :time time
+                  :mime-subtype (mime:mime-subtype text-part))))
+
+(defgeneric apple-note-html-fragment (note out)
+  (:documentation
+   "Takes an APPLE-NOTE and writes its text content as HTML to
+   the OUT stream. The <object> tags are resolved to <img> which
+   refer to the respective attachment's filename as a relative path,
+   but extraction of the attachments must be done separately. The
+   surrounding <html> and <body> tags are stripped and <head>
+   discarded completely, so only a fragment which can be included
+   in custom templates remains."))
+
+(defmethod apple-note-html-fragment ((note apple-note) (out stream))
+  (let ((text (apple-note-text-part note)))
     (cond
-      ;; Sanity checking of the note
-      ((not (apple-note-p msg))
-       (error "Unsupported or missing X-Uniform-Type-Identifier"))
-      ((not text) (error "Malformed Apple Note: no text part"))
       ;; notemap creates text/plain notes we need to handle properly.
       ;; Additionally we *could* check X-Mailer which notemap sets
-      ((string= (mime:mime-subtype text) "plain")
-       (html-escape-stream (mime:mime-body-stream text :binary nil) out))
+      ((string-equal (apple-note-mime-subtype note) "plain")
+       (html-escape-stream (mime:mime-body-stream text) out))
       ;; Notes.app creates text/html parts
-      ((string= (mime:mime-subtype text) "html")
+      ((string-equal (apple-note-mime-subtype note) "html")
        (closure-html:parse
         (mime:mime-body-stream text)
         (make-instance
          'apple-note-transformer
          :cid-lookup
          (lambda (cid)
-           (when-let* ((part (mime:find-mime-part-by-id msg (cid-header-value cid)))
+           (when-let* ((part (mime:find-mime-part-by-id note (cid-header-value cid)))
                        (file (mime:mime-part-file-name part)))
              file))
          :next-handler
          (closure-html:make-character-stream-sink out))))
-      (t (error "Malformed Apple Note: unknown mime type")))))
+      (t (error "Internal error: unexpected MIME subtype")))))
diff --git a/users/sterni/mblog/packages.lisp b/users/sterni/mblog/packages.lisp
index ca2e41b682..d6e33955d3 100644
--- a/users/sterni/mblog/packages.lisp
+++ b/users/sterni/mblog/packages.lisp
@@ -1,15 +1,64 @@
-(defpackage :mblog
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022-2023 by sterni
+
+(defpackage :maildir
+  (:use :common-lisp)
+  (:shadow :list)
+  (:export :list)
+  (:documentation
+   "Very incomplete package for dealing with maildir(5)."))
+
+(defpackage :config
+  (:use
+   :common-lisp)
+  (:import-from :uiop :getenv)
+  (:import-from :alexandria :when-let)
+  (:export
+   :init-from-env
+   :*general-buffer-size*))
+
+(defpackage :note
   (:use
    :common-lisp
-   :mime4cl
    :closure-html
-   :who
-   :uiop)
-  (:shadow :with-html-output) ; conflict between closure-html and who
+   :cl-date-time-parser
+   :mime4cl
+   :config)
   (:import-from
    :alexandria
    :when-let*
    :when-let
    :starts-with-subseq
    :ends-with-subseq)
+  (:import-from :who :escape-string-minimal)
+  (:export
+   :apple-note
+   :apple-note-uuid
+   :apple-note-subject
+   :apple-note-time
+   :apple-note-text-part
+   :make-apple-note
+   :apple-note-html-fragment))
+
+(defpackage :mblog
+  (:use
+   :common-lisp
+   :klatre
+   :who
+   :maildir
+   :note
+   :config)
+  (:export :build-mblog)
+  (:import-from :local-time :universal-to-timestamp)
+  (:import-from :mime4cl :redirect-stream)
+  (:shadowing-import-from :common-lisp :list))
+
+(defpackage :cli
+  (:use
+   :common-lisp
+   :uiop
+   :note
+   :config
+   :mblog)
+  (:import-from :alexandria :starts-with)
   (:export :main))
diff --git a/users/sterni/mblog/transformer.lisp b/users/sterni/mblog/transformer.lisp
index f26c5652a2..c499eafbec 100644
--- a/users/sterni/mblog/transformer.lisp
+++ b/users/sterni/mblog/transformer.lisp
@@ -1,4 +1,7 @@
-(in-package :mblog)
+;; SPDX-License-Identifier: GPL-3.0-only
+;; SPDX-FileCopyrightText: Copyright (C) 2022 by sterni
+
+(in-package :note)
 (declaim (optimize (safety 3)))
 
 ;; Throw away these tags and all of their children
@@ -60,7 +63,7 @@
 
 (defun parse-content-id (attrlist)
   (when-let (data (find-if (lambda (x)
-                             (string= (hax:attribute-name x) "DATA"))
+                             (string-equal (hax:attribute-name x) "DATA"))
                            attrlist))
     (multiple-value-bind (starts-with-cid-p suffix)
         (starts-with-subseq "cid:" (hax:attribute-value data)
@@ -81,16 +84,16 @@
       ;; If we are not discarding any outer elements, we can set
       ;; up a new discard condition if we encounter an appropriate
       ;; element.
-      ((member name +discard-tags-with-children+ :test #'string=)
+      ((member name +discard-tags-with-children+ :test #'string-equal)
        (setf discard-until (cons name depth)))
       ;; Only drop this event, must be mirrored in END-ELEMENT to
       ;; avoid invalidly nested HTML.
-      ((member name +discard-tags-only+ :test #'string=) nil)
+      ((member name +discard-tags-only+ :test #'string-equal) nil)
       ;; If we encounter an object tag, we drop it and its contents,
       ;; but only after inspecting its attributes and emitting new
       ;; events representing an img tag which includes the respective
       ;; attachment via its filename.
-      ((string= name "OBJECT")
+      ((string-equal name "OBJECT")
        (progn
          (setf discard-until (cons "OBJECT" depth))
          ;; TODO(sterni): check type and only resolve images, raise error
@@ -116,12 +119,12 @@
       ;; If we are discarding and encounter the same tag again at the same
       ;; depth, we can stop, but still have to discard the current tag.
       ((and discard-until
-            (string= (car discard-until) name)
+            (string-equal (car discard-until) name)
             (= (cdr discard-until) depth))
        (setf discard-until nil))
       ;; In all other cases, we drop properly.
       (discard-until nil)
       ;; Mirrored tag stripping as in START-ELEMENT
-      ((member name +discard-tags-only+ :test #'string=) nil)
+      ((member name +discard-tags-only+ :test #'string-equal) nil)
       ;; In all other cases, we use HAX-PROXY-HANDLER to pass the event on.
       (t (call-next-method)))))
diff --git a/users/sterni/modules/backup-minecraft-fabric.nix b/users/sterni/modules/backup-minecraft-fabric.nix
new file mode 100644
index 0000000000..a80a7f51a9
--- /dev/null
+++ b/users/sterni/modules/backup-minecraft-fabric.nix
@@ -0,0 +1,125 @@
+# Companion module to minecraft-fabric.nix which automatically and regularly
+# creates backups of all minecraft servers' worlds to a shared borg(1)
+# repository.
+#
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2023 sterni <sternenseemann@systemli.org>
+{ pkgs, depot, config, lib, ... }:
+
+let
+  inherit (depot.nix) getBins;
+
+  bins = getBins pkgs.borgbackup [ "borg" ]
+    // getBins pkgs.mcrcon [ "mcrcon" ];
+
+  unvaried = ls: builtins.all (l: l == builtins.head ls) ls;
+
+  cfg = config.services.backup-minecraft-fabric-servers;
+
+  instances = lib.filterAttrs (_: i: i.enable) config.services.minecraft-fabric-server;
+  users = lib.mapAttrsToList (_: i: i.user) instances;
+  groups = lib.mapAttrsToList (_: i: i.group) instances;
+
+  mkBackupScript = instanceName: instanceCfg:
+    let
+      archivePrefix = "minecraft-fabric-${instanceName}-world-${builtins.baseNameOf instanceCfg.world}-";
+    in
+
+    pkgs.writeShellScript "backup-minecraft-fabric-${instanceName}" ''
+      export MCRCON_HOST="localhost"
+      export MCRCON_PORT="${toString instanceCfg.serverProperties."rcon.port"}"
+      # Unfortunately, mcrcon can't read the password from a file
+      export MCRCON_PASS="$(cat "''${CREDENTIALS_DIRECTORY}/${instanceName}-rcon-password")"
+
+      ${bins.mcrcon} save-all
+      unset MCRCON_PASS
+
+      # Give the server plenty of time to save
+      sleep 60
+
+      ${bins.borg} ${lib.escapeShellArgs [
+        "create"
+        "--verbose" "--filter" "AMEU" "--list"
+        "--stats" "--show-rc"
+        "--compression" "zlib"
+        "${cfg.repository}::${archivePrefix}{now}"
+        instanceCfg.world
+      ]}
+
+      ${bins.borg} ${lib.escapeShellArgs [
+        "prune"
+        "--list"
+        "--show-rc"
+        "--glob-archives" "${archivePrefix}*"
+        "--keep-hourly" "168"
+        "--keep-daily" "31"
+        "--keep-monthly" "6"
+        "--keep-yearly" "2"
+        cfg.repository
+      ]}
+
+      ${bins.borg} compact ${lib.escapeShellArg cfg.repository}
+    '';
+in
+
+{
+  imports = [
+    ./minecraft-fabric.nix
+  ];
+
+  options = {
+    services.backup-minecraft-fabric-servers = {
+      enable = lib.mkEnableOption "backups of all Minecraft fabric servers";
+
+      repository = lib.mkOption {
+        type = lib.types.path;
+        description = "Path to the borg(1) repository to use for all backups.";
+        default = "/var/lib/backup/minecraft-fabric";
+      };
+    };
+  };
+
+  config = lib.mkIf (cfg.enable && builtins.length (builtins.attrNames instances) > 0) {
+    assertions = [
+      {
+        assertion = unvaried users && unvaried groups;
+        message = "all instances under services.minecraft-fabric-server must use the same user and group";
+      }
+    ];
+
+    environment.systemPackages = [
+      pkgs.borgbackup
+    ];
+
+    systemd = {
+      services.backup-minecraft-fabric-servers = {
+        description = "Backup world of all fabric based Minecraft servers";
+        wantedBy = [ ];
+        after = builtins.map
+          (name: "minecraft-fabric-${name}.service")
+          (builtins.attrNames instances);
+
+        script = lib.concatStrings (lib.mapAttrsToList mkBackupScript instances);
+
+        serviceConfig = {
+          Type = "oneshot";
+          User = builtins.head users;
+          Group = builtins.head groups;
+          LoadCredential = lib.mapAttrsToList
+            (instanceName: instanceCfg: "${instanceName}-rcon-password:${instanceCfg.rconPasswordFile}")
+            instances;
+        };
+      };
+
+      timers.backup-minecraft-fabric-servers = {
+        description = "Regularly backup Minecraft fabric servers";
+        wantedBy = [ "timers.target" ];
+        timerConfig = {
+          OnCalendar = "*-*-* 00/3:00:00";
+          Persistent = true;
+          RandomizedDelaySec = "1h";
+        };
+      };
+    };
+  };
+}
diff --git a/users/sterni/modules/common.nix b/users/sterni/modules/common.nix
new file mode 100644
index 0000000000..2c513acad3
--- /dev/null
+++ b/users/sterni/modules/common.nix
@@ -0,0 +1,79 @@
+# This module is common in the weakest sense, i.e. contains common settings to
+# all my machines contained in depotโ€”as opposed to common to all my potential
+# machines. Consequently, this module is currently very server-centric.
+{ pkgs, lib, depot, config, ... }:
+
+let
+  me = "lukas";
+in
+
+{
+  config = {
+
+    # More common
+
+    time.timeZone = "Europe/Berlin";
+
+    nix = {
+      package = pkgs.nix_2_3;
+      settings = {
+        trusted-public-keys = lib.mkAfter [
+          "headcounter.org:/7YANMvnQnyvcVB6rgFTdb8p5LG1OTXaO+21CaOSBzg="
+        ];
+        substituters = lib.mkAfter [
+          "https://hydra.build"
+        ];
+        trusted-users = [ me ];
+      };
+    };
+    tvl.cache.enable = true;
+
+    programs.fish.enable = true;
+
+    users = {
+      users = {
+        root.openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
+        ${me} = {
+          isNormalUser = true;
+          extraGroups = [ "wheel" "http" "git" ];
+          openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
+          shell = pkgs.fish;
+        };
+      };
+    };
+
+    # Less common
+
+    services = {
+      journald.extraConfig = ''
+        SystemMaxUse=10G
+      '';
+
+      openssh.enable = true;
+    };
+
+    programs = {
+      mosh.enable = true;
+      tmux.enable = true;
+    };
+
+    environment.systemPackages = [
+      pkgs.wget
+      pkgs.git
+      pkgs.stow
+      pkgs.htop
+      pkgs.foot.terminfo
+      pkgs.vim
+      pkgs.smartmontools
+    ];
+
+    security.acme = {
+      defaults.email = builtins.getAttr "email" (
+        builtins.head (
+          builtins.filter (attrs: attrs.username == "sterni") depot.ops.users
+        )
+      );
+      acceptTerms = true;
+    };
+  };
+}
diff --git a/users/sterni/modules/default.nix b/users/sterni/modules/default.nix
new file mode 100644
index 0000000000..5cc8be3cc6
--- /dev/null
+++ b/users/sterni/modules/default.nix
@@ -0,0 +1,2 @@
+# Stop readTree from looking at this directory
+_: { }
diff --git a/users/sterni/modules/minecraft-fabric.nix b/users/sterni/modules/minecraft-fabric.nix
new file mode 100644
index 0000000000..6cc32cd205
--- /dev/null
+++ b/users/sterni/modules/minecraft-fabric.nix
@@ -0,0 +1,532 @@
+# Declarative, but low Nix module for a modded minecraft server using the
+# fabric mod loader. That is to say, the build of the final server JAR
+# is not encapsulated in a derivation.
+#
+# The module has the following interesting properties:
+#
+#   * The fabric installer is executed on each server startup to assemble the
+#     patched server.jar. This is unfortunately necessary, as it seems to be
+#     difficult to do so in a derivation (fabric-installer accesses the network,
+#     the build doesn't seem to be reproducible). At least this avoids the
+#     question of the patched jar's redistributability.
+#   * RCON is used for starting and stopping which should prevent data loss,
+#     since we can issue a manual save command.
+#   * The entire runtime directory of the server is assembled from scratch on
+#     each start, so only blessed state (like the world) and declarative
+#     configuration (whitelist.json, server.properties, ...) survive.
+#   * It supports more than one server running on the same machine.
+#
+# Missing features:
+#
+#   * Support for bans
+#   * Support for mutable whitelist, ops, โ€ฆ
+#   * Op levels
+#
+# SPDX-License-Identifier: MIT
+# SPDX-FileCopyrightText: 2022-2024 sterni <sternenseemann@systemli.org>
+
+{ lib, pkgs, config, depot, ... }:
+
+let
+  #
+  # Dependencies
+  #
+  inherit (depot.nix.utils) storePathName;
+  inherit (depot.nix) getBins;
+
+  bins = getBins pkgs.mcrcon [ "mcrcon" ]
+    // getBins pkgs.jre [ "java" ]
+    // getBins pkgs.diffutils [ "diff" ]
+    // getBins pkgs.moreutils [ "sponge" ]
+    // getBins pkgs.extrace [ "pwait" ]
+    // getBins pkgs.util-linux [ "flock" ];
+
+  #
+  # Needed JARs
+  #
+  fetchJar = { pname, version, url, sha256, passthru ? { } }:
+    pkgs.fetchurl {
+      name = "${pname}-${version}.jar";
+      inherit url sha256;
+      passthru = passthru // { inherit version; };
+    };
+
+  fabricInstallerJar =
+    fetchJar rec {
+      pname = "fabric-installer";
+      version = "1.0.0";
+      url = "https://maven.fabricmc.net/net/fabricmc/fabric-installer/${version}/fabric-installer-${version}.jar";
+      sha256 = "0yrlzly1g5a80df27jvrbhxbp10xqxfyk64q0s0j13kz78fmnzkx";
+    };
+
+  # log4j workaround for Minecraft Server >= 1.12 && < 1.17
+  log4jFix_112_116 = pkgs.fetchurl {
+    url = "https://launcher.mojang.com/v1/objects/02937d122c86ce73319ef9975b58896fc1b491d1/log4j2_112-116.xml";
+    sha256 = "1paha357xbaffl38ckzgdh4l5iib2ydqbv7jsg67nj31nlalclr9";
+  };
+
+  serverJars = {
+    # Manually updated list of known minecraft `server.jar`s for now.
+    # Making this comprehensive isn't that interesting for now, since the module
+    # is annoying to use outside of depot anyways as it uses //nix.
+    "1.16.5" = fetchJar {
+      pname = "server";
+      version = "1.16.5";
+      url = "https://launcher.mojang.com/v1/objects/1b557e7b033b583cd9f66746b7a9ab1ec1673ced/server.jar";
+      sha256 = "19ix6x5ij4jcwqam1dscnqwm0m251gysc2j793wjcrb9sb3jkwsq";
+      passthru = {
+        baseJvmOpts = [
+          "-Dlog4j.configurationFile=${log4jFix_112_116}"
+        ];
+      };
+    };
+    "1.17" = fetchJar {
+      pname = "server";
+      version = "1.17";
+      url = "https://launcher.mojang.com/v1/objects/0a269b5f2c5b93b1712d0f5dc43b6182b9ab254e/server.jar";
+      sha256 = "0jqz7hpx7zvjj2n5rfrh8jmdj6ziqyp8c9nq4sr4jmkbky6hsfbv";
+      passthru.baseJvmOpts = [
+        "-Dlog4j2.formatMsgNoLookups=true"
+      ];
+    };
+    "1.17.1" = fetchJar {
+      pname = "server";
+      version = "1.17.1";
+      url = "https://launcher.mojang.com/v1/objects/a16d67e5807f57fc4e550299cf20226194497dc2/server.jar";
+      sha256 = "0pzmzagvrrapjsnd8xg4lqwynwnb5rcqk2n9h2kzba8p2fs13hp8";
+      passthru.baseJvmOpts = [
+        "-Dlog4j2.formatMsgNoLookups=true"
+      ];
+    };
+    "1.18" = fetchJar {
+      pname = "server";
+      version = "1.18";
+      url = "https://launcher.mojang.com/v1/objects/3cf24a8694aca6267883b17d934efacc5e44440d/server.jar";
+      sha256 = "0vvycjcfq96z7cl5dsrq98k9b7j7l4x0y9nflrcqmcvink7fs5w4";
+      passthru.baseJvmOpts = [
+        "-Dlog4j2.formatMsgNoLookups=true"
+      ];
+    };
+    "1.18.1" = fetchJar {
+      pname = "server";
+      version = "1.18.1";
+      url = "https://launcher.mojang.com/v1/objects/125e5adf40c659fd3bce3e66e67a16bb49ecc1b9/server.jar";
+      sha256 = "1pyvym6xzjb1siizzj4ma7lpb05qhgxnzps8lmlbk00lv0515kgb";
+    };
+    "1.18.2" = fetchJar {
+      pname = "server";
+      version = "1.18.2";
+      url = "https://launcher.mojang.com/v1/objects/c8f83c5655308435b3dcf03c06d9fe8740a77469/server.jar";
+      sha256 = "0hx330bnadixph44sip0h5h986m11qxbdba6lbgwz4da6lg9vgjp";
+    };
+    "1.19" = fetchJar {
+      pname = "server";
+      version = "1.19";
+      url = "https://launcher.mojang.com/v1/objects/e00c4052dac1d59a1188b2aa9d5a87113aaf1122/server.jar";
+      sha256 = "1cnjrqr2vn8gppd1y1lcdrc46fd7m1b3zl28zpbw72fgy1bd1vyy";
+    };
+    "1.19.1" = fetchJar {
+      pname = "server";
+      version = "1.19.1";
+      url = "https://piston-data.mojang.com/v1/objects/8399e1211e95faa421c1507b322dbeae86d604df/server.jar";
+      sha256 = "0jnlb5z8a7qi6p6bbwnmdl77b8kq83ryfdp58dhx8kg2hf6lbfx8";
+    };
+    "1.19.2" = fetchJar {
+      pname = "server";
+      version = "1.19.2";
+      url = "https://piston-data.mojang.com/v1/objects/f69c284232d7c7580bd89a5a4931c3581eae1378/server.jar";
+      sha256 = "15jdxh5zvsgvvk9hnv47swgjfg8fr653g6nx99q1rxpmkq32frxj";
+    };
+    "1.19.3" = fetchJar {
+      pname = "server";
+      version = "1.19.3";
+      url = "https://piston-data.mojang.com/v1/objects/c9df48efed58511cdd0213c56b9013a7b5c9ac1f/server.jar";
+      sha256 = "06qykz3nq7qmfw4phs3wvq3nk28clg8s3qrs37856aai8b8kmgaf";
+    };
+    # Starting with 1.19.4 we could use --pidFile for systemd's PIDFile=, but as
+    # the service doesn't fork, there seems to be no point.
+    "1.19.4" = fetchJar {
+      pname = "server";
+      version = "1.19.4";
+      url = "https://piston-data.mojang.com/v1/objects/8f3112a1049751cc472ec13e397eade5336ca7ae/server.jar";
+      sha256 = "0lrzpqd6zjvqh9g2byicgh66n43z0hwzp863r22ifx2hll6s2955";
+    };
+    # https://feedback.minecraft.net/hc/en-us/articles/16499677456781-Minecraft-Java-Edition-1-20-Trails-Tales
+    "1.20" = fetchJar {
+      name = "server";
+      version = "1.20";
+      url = "https://piston-data.mojang.com/v1/objects/15c777e2cfe0556eef19aab534b186c0c6f277e1/server.jar";
+      sha256 = "0sym07vqrlbhyxxhlpz73ls0jh0g9qcl4plaa1scx0n1rr1cahgz";
+    };
+    # https://www.minecraft.net/en-us/article/minecraft--java-edition-1-20-1
+    "1.20.1" = fetchJar {
+      pname = "server";
+      version = "1.20.1";
+      url = "https://piston-data.mojang.com/v1/objects/84194a2f286ef7c14ed7ce0090dba59902951553/server.jar";
+      sha256 = "1q3r3c95vkai477r3gsmf2p0pmyl4zfn0qwl8y0y60m1qnfkmxrs";
+    };
+    # https://www.minecraft.net/en-us/article/minecraft-java-edition-1-20-2
+    "1.20.2" = fetchJar {
+      pname = "server";
+      version = "1.20.2";
+      url = "https://piston-data.mojang.com/v1/objects/5b868151bd02b41319f54c8d4061b8cae84e665c/server.jar";
+      sha256 = "1s7ag1p8v0vyzc6a8mjkd3rcf065hjb4avqa3zj4dbb9hn1y9bhx";
+    };
+    # https://www.minecraft.net/en-us/article/minecraft-java-edition-1-20-3
+    "1.20.3" = fetchJar {
+      pname = "server";
+      version = "1.20.3";
+      url = "https://piston-data.mojang.com/v1/objects/4fb536bfd4a83d61cdbaf684b8d311e66e7d4c49/server.jar";
+      sha256 = "1blb2cp1nlm0yr7yjhazj33g0hjlgfawx2v7y16h70pijfz8kv9n";
+    };
+    # https://www.minecraft.net/en-us/article/minecraft-java-edition-1-20-4
+    "1.20.4" = fetchJar {
+      pname = "server";
+      version = "1.20.4";
+      url = "https://piston-data.mojang.com/v1/objects/8dd1a28015f51b1803213892b50b7b4fc76e594d/server.jar";
+      sha256 = "0qykf9a3nacklqsyb30kg9m79nw462la6rf92gsdssdakprscgy0";
+    };
+  };
+
+  #
+  # mods directory for fabric
+  #
+  makeModFolder = name: mods:
+    pkgs.runCommand "${name}-fabric-mod-folder" { } (
+      ''
+        mkdir -p "$out"
+      '' + lib.concatMapStrings
+        (mod: ''
+          test -f "${mod}" || {
+              printf 'Not a regular file: %s\n' "${mod}" >&2
+              exit 1
+          }
+          ln -s "${mod}" "$out/${storePathName mod}"
+        '')
+        mods
+    );
+
+  #
+  # Create a server.properties file
+  #
+  propertyValue = v:
+    if builtins.isBool v
+    then lib.boolToString v
+    else toString v;
+
+  serverPropertiesFile = name: instanceCfg:
+    let
+      serverProperties' =
+        builtins.removeAttrs instanceCfg.serverProperties [
+          "rcon.password"
+        ] // {
+          enable-rcon = true;
+        };
+    in
+    pkgs.writeText "${name}-server.properties" (''
+      # created by minecraft-fabric.nix
+    '' + lib.concatStrings (lib.mapAttrsToList
+      (key: value: ''
+        ${key}=${propertyValue value}
+      '')
+      serverProperties'));
+
+  #
+  # Create JSON โ€œstateโ€ files
+  #
+  writeJson = name: data: pkgs.writeText "${name}.json" (builtins.toJSON data);
+
+  toWhitelist = name: uuid: { inherit name uuid; };
+
+  whitelistFile = name: instanceCfg:
+    writeJson "${name}-whitelist" (
+      lib.mapAttrsToList toWhitelist instanceCfg.whitelist
+    );
+
+  opsFile = name: instanceCfg:
+    writeJson "${name}-ops" (
+      lib.mapAttrsToList
+        (name: value:
+          toWhitelist name value // {
+            level = 4;
+            bypassesPlayerLimit = true;
+          }
+        )
+        instanceCfg.ops
+    );
+
+  #
+  # Service start and stop scripts
+  #
+  stopScript = name: instanceCfg:
+    pkgs.writeShellScript "minecraft-fabric-${name}-stop" ''
+      set -eu
+
+      # Before shutting down, display the diff between prescribed and used
+      # server.properties file for debugging purposes; filter out credential
+      actualProperties="''${RUNTIME_DIRECTORY}/server.properties"
+      sort "$actualProperties" | ${bins.sponge} "$actualProperties"
+      ( ${bins.diff} -u "${serverPropertiesFile name instanceCfg}" \
+          "$actualProperties" \
+          || true ) | grep -v rcon.password
+
+      export MCRCON_HOST=localhost
+      export MCRCON_PORT=${lib.escapeShellArg instanceCfg.serverProperties."rcon.port"}
+      # Unfortunately, mcrcon can't read the password from a file
+      export MCRCON_PASS="$(cat "''${CREDENTIALS_DIRECTORY}/rcon-password")"
+
+      # Send stop request
+      "${bins.mcrcon}" 'say Server is stopping' save-all stop
+
+      # Wait for service to come down (systemd SIGTERMs right after ExecStop)
+      "${bins.flock}" "''${RUNTIME_DIRECTORY}" true
+    '';
+
+  startScript = name: instanceCfg:
+    let
+      serverJar = serverJars.${instanceCfg.version} or
+        (throw "Don't have server.jar for Minecraft Server ${instanceCfg.version}");
+
+    in
+
+    pkgs.writeShellScript "minecraft-fabric-${name}-start" ''
+      set -eu
+
+      cd "''${RUNTIME_DIRECTORY}"
+
+      copyFromStore() {
+          install -m600 "$1" "$2"
+      }
+
+      # Check if world is available
+      if test ! -d "${instanceCfg.world}"; then
+          echo "Could not find world, generating new one" >&2
+          mkdir -p "${instanceCfg.world}"
+      fi
+
+      # Put required files into place
+      echo eula=true > eula.txt
+      ln -s "${instanceCfg.world}" "${instanceCfg.level-name or "world"}"
+      copyFromStore "${serverJar}" server.jar
+      copyFromStore "${whitelistFile name instanceCfg}" whitelist.json
+      copyFromStore "${opsFile name instanceCfg}" ops.json
+      ln -s "${makeModFolder name instanceCfg.mods}" mods
+
+      # Create config and set password from credentials (echo hopefully doesn't leak)
+      copyFromStore "${serverPropertiesFile name instanceCfg}" server.properties
+      echo "rcon.password=$(cat "$CREDENTIALS_DIRECTORY/rcon-password")" >> server.properties
+
+      # Build patched jar
+      "${bins.java}" -jar "${fabricInstallerJar}" \
+          server -mcversion "${instanceCfg.version}"
+
+      # Lock is held as long as the server is running, so that we can wait for
+      # the actual shutdown in the stop script without relying on $MAINPID.
+      exec "${bins.flock}" "''${RUNTIME_DIRECTORY}" \
+          "${bins.java}" \
+          ${lib.escapeShellArgs (serverJar.baseJvmOpts or [ ] ++ instanceCfg.jvmOpts)} \
+          -jar fabric-server-launch.jar nogui
+    '';
+
+  #
+  # Option types
+  #
+  impurePath = lib.types.path // {
+    name = "impurePath";
+    check = x:
+      lib.types.path.check x
+        && !(builtins.isPath x)
+        && !(lib.hasPrefix builtins.storeDir (toString x));
+  };
+
+
+  instanceType = lib.types.submodule {
+    options = {
+      enable = lib.mkEnableOption "Minecraft server instance with the fabric mod loader";
+
+      version = lib.mkOption {
+        type = lib.types.str;
+        description = "Minecraft Server version to use.";
+        example = "1.16.5";
+      };
+
+      mods = lib.mkOption {
+        type = with lib.types; listOf package;
+        description = "List of fabric mod JARs to load.";
+        default = [ ];
+      };
+
+      world = lib.mkOption {
+        type = impurePath;
+        description = "Path to the Minecraft world folder to use.";
+        example = "/var/minecraft/world";
+      };
+
+      jvmOpts = lib.mkOption {
+        type = with lib.types; listOf str;
+        default = [ ];
+        example = [
+          "-Xmx2048M"
+          "-Xms2048M"
+        ];
+        description = ''
+          Options to pass to
+          <citerefentry>
+            <refentrytitle>java</refentrytitle>
+            <manvolnum>1</manvolnum>
+          </citerefentry>
+          in order to tweak the runtime of the JVM.
+        '';
+      };
+
+      user = lib.mkOption {
+        type = lib.types.str;
+        default = "minecraft";
+        description = ''
+          Name of an existing user to run the server as. Needs to have write
+          access to the specified world.
+        '';
+      };
+
+      group = lib.mkOption {
+        type = lib.types.str;
+        default = "users";
+        description = ''
+          Name of an existing group to run the server under.
+        '';
+      };
+
+      rconPasswordFile = lib.mkOption {
+        type = impurePath;
+        description = ''
+          File (outised the store) that stores the password to use for Minecraft's
+          RCON interface.
+        '';
+        example = "/var/secrets/minecraft-rcon";
+      };
+
+      whitelist = lib.mkOption {
+        type = with lib.types; attrsOf str;
+        description = ''
+          Attribute set mapping whitelisted user names to their user ids.
+        '';
+        example = {
+          sternenseemann = "d8e48069-1905-4886-a5da-a4ee917ee254";
+        };
+      };
+
+      ops = lib.mkOption {
+        type = with lib.types; attrsOf str;
+        description = ''
+          Attribute set mapping op-ed user names to their user ids.
+          Setting permission levels is not possible at the moment,
+          set to 4 by default.
+        '';
+        example = {
+          sternenseemann = "d8e48069-1905-4886-a5da-a4ee917ee254";
+        };
+      };
+
+      serverProperties = lib.mkOption {
+        type = lib.types.submodule {
+          freeformType = lib.types.attrs;
+
+          # Only options the module needs to access are declared explicitly
+          options = {
+            server-port = lib.mkOption {
+              type = lib.types.port;
+              default = 25565;
+              description = ''
+                Port to listen on.
+              '';
+            };
+
+            "rcon.port" = lib.mkOption {
+              type = lib.types.port;
+              default = 25575;
+              description = ''
+                Port to use for the RCON control mechanism.
+              '';
+            };
+          };
+        };
+      };
+    };
+  };
+
+  cfg = config.services.minecraft-fabric-server;
+
+  serverPorts = lib.mapAttrsToList
+    (_: instanceCfg:
+      instanceCfg.serverProperties.server-port
+    )
+    cfg;
+
+  rconPorts = lib.mapAttrsToList
+    (_: instanceCfg:
+      instanceCfg.serverProperties."rcon.port"
+    )
+    cfg;
+in
+
+{
+  options = {
+    services.minecraft-fabric-server = lib.mkOption {
+      type = with lib.types; attrsOf instanceType;
+      default = { };
+      description = "Minecraft server instances with the fabric mod loader";
+    };
+  };
+
+  config = {
+    assertions = [
+      {
+        assertion = builtins.all (instance: !instance.enable) (builtins.attrValues cfg)
+          || pkgs.config.allowUnfreeRedistributable or false
+          || pkgs.config.allowUnfree or false;
+        message = lib.concatStringsSep " " [
+          "You need to allow unfree software for minecraft,"
+          "as you'll implicitly agree to Mojang's EULA."
+        ];
+      }
+      {
+        assertion =
+          let
+            allPorts = serverPorts ++ rconPorts;
+          in
+          lib.unique allPorts == allPorts;
+        message = "All assigned ports need to be unique.";
+      }
+    ];
+
+    systemd.services = lib.mapAttrs'
+      (name: instanceCfg:
+        {
+          name = "minecraft-fabric-${name}";
+          value = {
+            description = "Minecraft server ${name} with the fabric mod loader";
+            wantedBy = [ "multi-user.target" ];
+            after = [ "network.target" ];
+            inherit (instanceCfg) enable;
+
+            serviceConfig = {
+              Type = "simple";
+              User = instanceCfg.user;
+              Group = instanceCfg.group;
+              ExecStart = startScript name instanceCfg;
+              ExecStop = stopScript name instanceCfg;
+              RuntimeDirectory = "minecraft-fabric-${name}";
+              LoadCredential = "rcon-password:${instanceCfg.rconPasswordFile}";
+              RestartSec = "40s";
+            };
+          };
+        }
+      )
+      cfg;
+
+    networking.firewall = {
+      allowedTCPPorts = serverPorts;
+      allowedUDPPorts = serverPorts;
+    };
+  };
+}
diff --git a/users/sterni/nix/build/buildGopherHole/default.nix b/users/sterni/nix/build/buildGopherHole/default.nix
new file mode 100644
index 0000000000..eec13a8654
--- /dev/null
+++ b/users/sterni/nix/build/buildGopherHole/default.nix
@@ -0,0 +1,109 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  inherit (pkgs)
+    runCommand
+    writeText
+    ;
+
+  inherit (depot.users.sterni.nix.build)
+    buildGopherHole
+    ;
+
+  fileTypes = {
+    # RFC1436
+    text = "0";
+    menu = "1";
+    cso = "2";
+    error = "3";
+    binhex = "4";
+    dos = "5";
+    uuencoded = "6";
+    index-server = "7";
+    telnet = "8";
+    binary = "9";
+    mirror = "+";
+    gif = "g";
+    image = "I";
+    tn3270 = "T";
+    # non-standard
+    info = "i";
+    html = "h";
+  };
+
+  buildFile = { file, name, fileType ? fileTypes.text }:
+    runCommand name
+      {
+        passthru = {
+          # respect the file type the file derivation passes
+          # through. otherwise use explicitly set type or
+          # default value.
+          fileType = file.fileType or fileType;
+        };
+      } ''
+      ln -s ${file} "$out"
+    '';
+
+  buildGopherMap = dir:
+    let
+      /* strings constitute an info line or an empty line
+         if their length is zero. sets that contain a menu
+         value have that added to the gophermap as-is.
+
+         all other entries should be a set which can be built using
+         buildGopherHole and is linked by their name. The resulting
+         derivation is expected to passthru a fileType containing the
+         gopher file type char of themselves.
+      */
+      gopherMapLine = e:
+        if builtins.isString e
+        then e
+        else if e ? menu
+        then e.menu
+        else
+          let
+            drv = buildGopherHole e;
+            title = e.title or e.name;
+          in
+          "${drv.fileType}${title}\t${drv.name}";
+    in
+    writeText ".gophermap" (lib.concatMapStringsSep "\n" gopherMapLine dir);
+
+  buildDir =
+    { dir, name, ... }:
+
+    let
+      # filter all entries out that have to be symlinked:
+      # sets with the file or dir attribute
+      drvOnly = builtins.map buildGopherHole (builtins.filter
+        (x: !(builtins.isString x) && (x ? dir || x ? file))
+        dir);
+      gopherMap = buildGopherMap dir;
+    in
+    runCommand name
+      {
+        passthru = {
+          fileType = fileTypes.dir;
+        };
+      }
+      (''
+        mkdir -p "$out"
+        ln -s "${gopherMap}" "$out/.gophermap"
+      '' + lib.concatMapStrings
+        (drv: ''
+          ln -s "${drv}" "$out/${drv.name}"
+        '')
+        drvOnly);
+in
+
+{
+  # Dispatch into different file / dir handling code
+  # which is mutually recursive with this function.
+  __functor = _: args:
+    if args ? file then buildFile args
+    else if args ? dir then buildDir args
+    else builtins.throw "Unrecognized gopher hole item type: "
+      + lib.generators.toPretty { } args;
+
+  inherit fileTypes;
+}
diff --git a/users/sterni/nix/char/default.nix b/users/sterni/nix/char/default.nix
index aacfc9dcbe..9c6ce2fb25 100644
--- a/users/sterni/nix/char/default.nix
+++ b/users/sterni/nix/char/default.nix
@@ -53,17 +53,20 @@ let
   asciiAlpha = c:
     let
       v = ord c;
-    in (v >= 65 && v <= 90)
+    in
+    (v >= 65 && v <= 90)
     || (v >= 97 && v <= 122);
 
   asciiNum = c:
     let
       v = ord c;
-    in v >= 48 && v <= 57;
+    in
+    v >= 48 && v <= 57;
 
   asciiAlphaNum = c: asciiAlpha c || asciiNum c;
 
-in {
+in
+{
   inherit
     allChars
     char
@@ -78,18 +81,19 @@ in {
   # originally I generated a nix file containing a list of
   # characters, but infinisil uses a better way which I adapt
   # which is using builtins.readFile instead of import.
-  __generateAllChars = pkgs.runCommandCC "generate-all-chars" {
-    source = ''
-      #include <stdio.h>
-
-      int main(void) {
-        for(int i = 1; i <= 0xff; i++) {
-          putchar(i);
+  __generateAllChars = pkgs.runCommandCC "generate-all-chars"
+    {
+      source = ''
+        #include <stdio.h>
+
+        int main(void) {
+          for(int i = 1; i <= 0xff; i++) {
+            putchar(i);
+          }
         }
-      }
-    '';
-    passAsFile = [ "source" ];
-  } ''
+      '';
+      passAsFile = [ "source" ];
+    } ''
     $CC -o "$out" -x c "$sourcePath"
   '';
 }
diff --git a/users/sterni/nix/char/tests/default.nix b/users/sterni/nix/char/tests/default.nix
index 49b439adbb..cb17b74c57 100644
--- a/users/sterni/nix/char/tests/default.nix
+++ b/users/sterni/nix/char/tests/default.nix
@@ -10,7 +10,7 @@ let
   inherit (depot.users.sterni.nix)
     char
     string
-    int
+    num
     fun
     ;
 
@@ -18,14 +18,14 @@ let
 
   testAllCharConversion = it "tests conversion of all chars" [
     (assertEq "char.chr converts to char.allChars"
-      (builtins.genList (fun.rl char.chr (int.add 1)) 255)
+      (builtins.genList (fun.rl char.chr (num.add 1)) 255)
       charList)
     (assertEq "char.ord converts from char.allChars"
-      (builtins.genList (int.add 1) 255)
+      (builtins.genList (num.add 1) 255)
       (builtins.map char.ord charList))
   ];
 
 in
-  runTestsuite "char" [
-    testAllCharConversion
-  ]
+runTestsuite "char" [
+  testAllCharConversion
+]
diff --git a/users/sterni/nix/float/default.nix b/users/sterni/nix/float/default.nix
new file mode 100644
index 0000000000..ecb6465c88
--- /dev/null
+++ b/users/sterni/nix/float/default.nix
@@ -0,0 +1,23 @@
+{ depot, ... }:
+
+let
+  inherit (depot.users.sterni.nix)
+    num
+    ;
+in
+
+rec {
+  # In C++ Nix, the required builtins have been added in version 2.4
+  ceil = builtins.ceil or (throw "Nix implementation is missing builtins.ceil");
+  floor = builtins.floor or (throw "Nix implementation is missing builtins.floor");
+
+  truncate = f: if f >= 0 then floor f else ceil f;
+  round = f:
+    let
+      s = num.sign f;
+      a = s * f;
+    in
+    s * (if a >= floor a + 0.5 then ceil a else floor a);
+
+  intToFloat = i: i * 1.0;
+}
diff --git a/users/sterni/nix/float/tests/default.nix b/users/sterni/nix/float/tests/default.nix
new file mode 100644
index 0000000000..75e2a1bfa0
--- /dev/null
+++ b/users/sterni/nix/float/tests/default.nix
@@ -0,0 +1,49 @@
+{ depot, lib, ... }:
+
+let
+
+  inherit (depot.nix.runTestsuite)
+    runTestsuite
+    it
+    assertEq
+    ;
+
+  inherit (depot.users.sterni.nix)
+    float
+    ;
+
+  testsBuiltins = it "tests builtin operations" [
+    (assertEq "ceil pos" (float.ceil 1.5) 2)
+    (assertEq "ceil neg" (float.ceil (-1.5)) (-1))
+    (assertEq "floor pos" (float.floor 1.5) 1)
+    (assertEq "floor neg" (float.floor (-1.5)) (-2))
+  ];
+
+  testsConversionFrom = it "tests integer to float conversion" [
+    (assertEq "float.intToFloat is identity for floats" (float.intToFloat 1.3) 1.3)
+    (assertEq "float.intToFloat converts ints"
+      (builtins.all
+        (val: builtins.isFloat val)
+        (builtins.map float.intToFloat (builtins.genList (i: i - 500) 1000)))
+      true)
+  ];
+
+  exampleFloats = [ 0.5 0.45 0.3 0.1 200 203.457847 204.65547 (-1.5) (-2) (-1.3) (-0.45) ];
+  testsConversionTo = it "tests float to integer conversion" [
+    (assertEq "round"
+      (builtins.map float.round exampleFloats)
+      [ 1 0 0 0 200 203 205 (-2) (-2) (-1) 0 ])
+    (assertEq "truncate towards zero"
+      (builtins.map float.truncate exampleFloats)
+      [ 0 0 0 0 200 203 204 (-1) (-2) (-1) 0 ])
+  ];
+in
+
+runTestsuite "nix.num" ([
+  testsConversionFrom
+]
+  # Skip for e.g. C++ Nix < 2.4
+++ lib.optionals (builtins ? ceil && builtins ? floor) [
+  testsConversionTo
+  testsBuiltins
+])
diff --git a/users/sterni/nix/flow/default.nix b/users/sterni/nix/flow/default.nix
index b5783bd86d..4bef0abb91 100644
--- a/users/sterni/nix/flow/default.nix
+++ b/users/sterni/nix/flow/default.nix
@@ -68,13 +68,14 @@ let
           then s x
           else x == s;
       in
-        if b
-        then builtins.elemAt c 1
-        else switch x (builtins.tail conds);
+      if b
+      then builtins.elemAt c 1
+      else switch x (builtins.tail conds);
 
 
 
-in {
+in
+{
   inherit
     cond
     switch
diff --git a/users/sterni/nix/flow/tests/default.nix b/users/sterni/nix/flow/tests/default.nix
index 54cea01858..9f974a61c7 100644
--- a/users/sterni/nix/flow/tests/default.nix
+++ b/users/sterni/nix/flow/tests/default.nix
@@ -21,7 +21,7 @@ let
       (cond [ [ true 1 2 ] [ false 1 ] ]))
     (assertEq "last is true" "last"
       (cond [
-        [ false dontEval]
+        [ false dontEval ]
         [ false dontEval ]
         [ true "last" ]
       ]))
@@ -34,6 +34,6 @@ let
   ];
 
 in
-  runTestsuite "nix.flow" [
-    testCond
-  ]
+runTestsuite "nix.flow" [
+  testCond
+]
diff --git a/users/sterni/nix/fun/default.nix b/users/sterni/nix/fun/default.nix
index 6b3541ed4c..824cebfed2 100644
--- a/users/sterni/nix/fun/default.nix
+++ b/users/sterni/nix/fun/default.nix
@@ -39,6 +39,198 @@ let
     builtins.match ".*<attrspat ellipsis=\"1\">.*"
       (builtins.toXML f) != null;
 
+  /* Return the number of arguments the given function accepts or 0 if the value
+     is not a function.
+
+     Example:
+
+       argCount argCount
+       => 1
+
+       argCount builtins.add
+       => 2
+
+       argCount pkgs.stdenv.mkDerivation
+       => 1
+  */
+  argCount = f:
+    let
+      # N.B. since we are only interested if the result of calling is a function
+      # as opposed to a normal value or evaluation failure, we never need to
+      # check success, as value will be false (i.e. not a function) in the
+      # failure case.
+      called = builtins.tryEval (
+        f (builtins.throw "You should never see this error message")
+      );
+    in
+    if !(builtins.isFunction f || builtins.isFunction (f.__functor or null))
+    then 0
+    else 1 + argCount called.value;
+
+  /* Call a given function with a given list of arguments.
+
+     Example:
+
+       apply builtins.sub [ 20 10 ]
+       => 10
+  */
+  apply = f: args:
+    builtins.foldl' (f: x: f x) f args;
+
+  # TODO(sterni): think of a better name for unapply
+  /* Collect n arguments into a list and pass them to the given function.
+     Allows calling a function that expects a list by feeding it the list
+     elements individually as function arguments - the limitation is
+     that the list must be of constant length.
+
+     This is mainly useful for functions that wrap other, arbitrary functions
+     in conjunction with argCount and apply, since lists of arguments are
+     easier to deal with usually.
+
+     Example:
+
+       (unapply 3 lib.id) 1 2 3
+       => [ 1 2 3 ]
+
+       (unapply 5 lib.reverse) 1 2 null 4 5
+       => [ 5 4 null 2 1 ]
+
+       # unapply and apply compose the identity relation together
+
+       unapply (argCount f) (apply f)
+       # is equivalent to f (if the function has a constant number of arguments)
+
+       (unapply 2 (apply builtins.sub)) 20 10
+       => 10
+  */
+  unapply =
+    let
+      unapply' = acc: n: f: x:
+        if n == 1
+        then f (acc ++ [ x ])
+        else unapply' (acc ++ [ x ]) (n - 1) f;
+    in
+    unapply' [ ];
+
+  /* Optimize a tail recursive Nix function by intercepting the recursive
+     function application and expressing it in terms of builtins.genericClosure
+     instead. The main benefit of this optimization is that even a naively
+     written recursive algorithm won't overflow the stack.
+
+     For this to work the following things prerequisites are necessary:
+
+     - The passed function needs to be a fix point for its self reference,
+       i. e. the argument to tailCallOpt needs to be of the form
+       `self: # function body that uses self to call itself`.
+       This is because tailCallOpt needs to manipulate the call to self
+       which otherwise wouldn't be possible due to Nix's lexical scoping.
+
+     - The passed function may only call itself as a tail call, all other
+       forms of recursions will fail evaluation.
+
+     This function was mainly written to prove that builtins.genericClosure
+     can be used to express any (tail) recursive algorithm. It can be used
+     to avoid stack overflows for deeply recursive, but naively written
+     functions (in the context of Nix this mainly means using recursion
+     instead of (ab)using more performant and less limited builtins).
+     A better alternative to using this function is probably translating
+     the algorithm to builtins.genericClosure manually. Also note that
+     using tailCallOpt doesn't mean that the stack won't ever overflow:
+     Data structures, especially lazy ones, can still cause all the
+     available stack space to be consumed.
+
+     The optimization also only concerns avoiding stack overflows,
+     tailCallOpt will make functions slower if anything.
+
+     Type: (F -> F) -> F where F is any tail recursive function.
+
+     Example:
+
+     let
+       label' = self: acc: n:
+         if n == 0
+         then "This is " + acc + "cursed."
+         else self (acc + "very ") (n - 1);
+
+       # Equivalent to a naive recursive implementation in Nix
+       label = (lib.fix label') "";
+
+       labelOpt = (tailCallOpt label') "";
+     in
+
+     label 5
+     => "This is very very very very very cursed."
+
+     labelOpt 5
+     => "This is very very very very very cursed."
+
+     label 10000
+     => error: stack overflow (possible infinite recursion)
+
+     labelOpt 10000
+     => "This is very very very very very very very very veryโ€ฆ
+  */
+  tailCallOpt = f:
+    let
+      argc = argCount (lib.fix f);
+
+      # This function simulates being f for f's self reference. Instead of
+      # recursing, it will just return the arguments received as a specially
+      # tagged set, so the recursion step can be performed later.
+      fakef = unapply argc (args: {
+        __tailCall = true;
+        inherit args;
+      });
+      # Pass fakef to f so that it'll be called instead of recursing, ensuring
+      # only one recursion step is performed at a time.
+      encodedf = f fakef;
+
+      opt = args:
+        let
+          steps = builtins.genericClosure {
+            # This is how we encode a (tail) call: A set with final == false
+            # and the list of arguments to pass to be found in args.
+            startSet = [
+              {
+                key = 0;
+                final = false;
+                inherit args;
+              }
+            ];
+
+            operator =
+              { key, final, ... }@state:
+              let
+                # Plumbing to make genericClosure happy
+                newId = {
+                  key = key + 1;
+                };
+
+                # Perform recursion step
+                call = apply encodedf state.args;
+
+                # If call encodes a new call, return the new encoded call,
+                # otherwise signal that we're done.
+                newState =
+                  if builtins.isAttrs call && call.__tailCall or false
+                  then newId // {
+                    final = false;
+                    inherit (call) args;
+                  } else newId // {
+                    final = true;
+                    value = call;
+                  };
+              in
+
+              if final
+              then [ ] # end condition for genericClosure
+              else [ newState ];
+          };
+        in
+        # The returned list contains intermediate steps we ignore.
+        (builtins.head (builtins.filter (x: x.final) steps)).value;
+    in
+    unapply argc opt;
 in
 
 {
@@ -55,5 +247,9 @@ in
     lr
     lrs
     hasEllipsis
+    argCount
+    tailCallOpt
+    apply
+    unapply
     ;
 }
diff --git a/users/sterni/nix/fun/tests/default.nix b/users/sterni/nix/fun/tests/default.nix
index 6492554306..6b1e6fcc7b 100644
--- a/users/sterni/nix/fun/tests/default.nix
+++ b/users/sterni/nix/fun/tests/default.nix
@@ -7,6 +7,8 @@ let
     assertEq
     ;
 
+  inherit (depot.nix) escapeExecline;
+
   inherit (depot.users.sterni.nix)
     fun
     ;
@@ -23,7 +25,58 @@ let
     (assertEq "Ellipsis" true
       (fun.hasEllipsis ({ depot, pkgs, ... }: 42)))
   ];
+
+  argCountTests = it "checks fun.argCount" [
+    (assertEq "builtins.sub has two arguments" 2
+      (fun.argCount builtins.sub))
+    (assertEq "fun.argCount has one argument" 1
+      (fun.argCount fun.argCount))
+    (assertEq "runTestsuite has two arguments" 2
+      (fun.argCount runTestsuite))
+  ];
+
+  applyTests = it "checks that fun.apply is equivalent to calling" [
+    (assertEq "fun.apply builtins.sub" (builtins.sub 23 42)
+      (fun.apply builtins.sub [ 23 42 ]))
+    (assertEq "fun.apply escapeExecline" (escapeExecline [ "foo" [ "bar" ] ])
+      (fun.apply escapeExecline [ [ "foo" [ "bar" ] ] ]))
+  ];
+
+  unapplyTests = it "checks fun.unapply" [
+    (assertEq "fun.unapply 3 accepts 3 args" 3
+      (fun.argCount (fun.unapply 3 fun.id)))
+    (assertEq "fun.unapply 73 accepts 73 args" 73
+      (fun.argCount (fun.unapply 73 fun.id)))
+    (assertEq "fun.unapply 1 accepts 73 args" 1
+      (fun.argCount (fun.unapply 1 fun.id)))
+    (assertEq "fun.unapply collects arguments correctly"
+      (fun.unapply 5 fun.id 1 2 3 4 5)
+      [ 1 2 3 4 5 ])
+    (assertEq "fun.unapply calls the given function correctly" 1
+      (fun.unapply 1 builtins.head 1))
+  ];
+
+  fac' = self: acc: n: if n == 0 then acc else self (n * acc) (n - 1);
+
+  facPlain = fun.fix fac' 1;
+  facOpt = fun.tailCallOpt fac' 1;
+
+  tailCallOptTests = it "checks fun.tailCallOpt" [
+    (assertEq "optimized and unoptimized factorial have the same base case"
+      (facPlain 0)
+      (facOpt 0))
+    (assertEq "optimized and unoptimized factorial have same value for 1"
+      (facPlain 1)
+      (facOpt 1))
+    (assertEq "optimized and unoptimized factorial have same value for 100"
+      (facPlain 100)
+      (facOpt 100))
+  ];
 in
-  runTestsuite "nix.fun" [
-    hasEllipsisTests
-  ]
+runTestsuite "nix.fun" [
+  hasEllipsisTests
+  argCountTests
+  applyTests
+  unapplyTests
+  tailCallOptTests
+]
diff --git a/users/sterni/nix/html/default.nix b/users/sterni/nix/html/default.nix
index 2498d832aa..d25a7ab8da 100644
--- a/users/sterni/nix/html/default.nix
+++ b/users/sterni/nix/html/default.nix
@@ -20,7 +20,7 @@ let
      => "&lt;hello&gt;"
   */
   escapeMinimal = builtins.replaceStrings
-    [ "<"    ">"    "&"     "\""     "'"      ]
+    [ "<" ">" "&" "\"" "'" ]
     [ "&lt;" "&gt;" "&amp;" "&quot;" "&#039;" ];
 
   /* Return a string with a correctly rendered tag of the given name,
@@ -87,18 +87,20 @@ let
   renderTag = tag: attrs: content:
     let
       attrs' = builtins.concatStringsSep "" (
-        builtins.map (n:
-          " ${escapeMinimal n}=\"${escapeMinimal (toString attrs.${n})}\""
-        ) (builtins.attrNames attrs)
+        builtins.map
+          (n:
+            " ${escapeMinimal n}=\"${escapeMinimal (toString attrs.${n})}\""
+          )
+          (builtins.attrNames attrs)
       );
       content' =
         if builtins.isList content
         then builtins.concatStringsSep "" content
         else content;
     in
-      if content == null
-      then "<${tag}${attrs'}/>"
-      else "<${tag}${attrs'}>${content'}</${tag}>";
+    if content == null
+    then "<${tag}${attrs'}/>"
+    else "<${tag}${attrs'}>${content'}</${tag}>";
 
   /* Prepend "<!DOCTYPE html>" to a string.
 
@@ -111,7 +113,8 @@ let
   */
   withDoctype = doc: "<!DOCTYPE html>" + doc;
 
-in {
+in
+{
   inherit escapeMinimal renderTag withDoctype;
 
   __findFile = _: renderTag;
diff --git a/users/sterni/nix/html/tests/default.nix b/users/sterni/nix/html/tests/default.nix
index 8688b69371..ed520675c5 100644
--- a/users/sterni/nix/html/tests/default.nix
+++ b/users/sterni/nix/html/tests/default.nix
@@ -8,15 +8,17 @@ let
     ;
 
   exampleDocument = withDoctype (<html> { lang = "en"; } [
-    (<head> {} [
+    (<head> { } [
       (<meta> { charset = "utf-8"; } null)
-      (<title> {} "html.nix example document")
-      (<link> {
-        rel = "license";
-        href = "https://code.tvl.fyi/about/LICENSE";
-        type = "text/html";
-      } null)
-      (<style> {}  (esc ''
+      (<title> { } "html.nix example document")
+      (<link>
+        {
+          rel = "license";
+          href = "https://code.tvl.fyi/about/LICENSE";
+          type = "text/html";
+        }
+        null)
+      (<style> { } (esc ''
         hgroup h2 {
           font-weight: normal;
         }
@@ -26,39 +28,45 @@ let
         }
       ''))
     ])
-    (<body> {} [
-      (<main> {} [
-        (<hgroup> {} [
-          (<h1> {} (esc "html.nix"))
-          (<h2> {} [
-            (<em> {} "the")
+    (<body> { } [
+      (<main> { } [
+        (<hgroup> { } [
+          (<h1> { } (esc "html.nix"))
+          (<h2> { } [
+            (<em> { } "the")
             (esc " most cursed HTML DSL ever!")
           ])
         ])
-        (<dl> {} [
-          (<dt> {} [
+        (<dl> { } [
+          (<dt> { } [
             (esc "Q: Wait, it's all ")
-            (<a> {
-              href = "https://cl.tvl.fyi/q/hashtag:cursed";
-            } (esc "cursed"))
+            (<a>
+              {
+                href = "https://cl.tvl.fyi/q/hashtag:cursed";
+              }
+              (esc "cursed"))
             (esc " nix hacks?")
           ])
-          (<dd> {} (esc "A: Always has been. ๐Ÿ”ซ"))
-          (<dt> {} (esc "Q: Why does this work?"))
-          (<dd> {} [
+          (<dd> { } (esc "A: Always has been. ๐Ÿ”ซ"))
+          (<dt> { } (esc "Q: Why does this work?"))
+          (<dd> { } [
             (esc "Because nix ")
-            (<a> {
-              href = "https://github.com/NixOS/nix/blob/293220bed5a75efc963e33c183787e87e55e28d9/src/libexpr/parser.y#L410-L416";
-            } (esc "translates "))
-            (<a> {
-              href = "https://github.com/NixOS/nix/blob/293220bed5a75efc963e33c183787e87e55e28d9/src/libexpr/lexer.l#L100";
-            } (esc "SPATH tokens"))
+            (<a>
+              {
+                href = "https://github.com/NixOS/nix/blob/293220bed5a75efc963e33c183787e87e55e28d9/src/libexpr/parser.y#L410-L416";
+              }
+              (esc "translates "))
+            (<a>
+              {
+                href = "https://github.com/NixOS/nix/blob/293220bed5a75efc963e33c183787e87e55e28d9/src/libexpr/lexer.l#L100";
+              }
+              (esc "SPATH tokens"))
             (esc " like ")
-            (<code> {} (esc "<nixpkgs>"))
+            (<code> { } (esc "<nixpkgs>"))
             (esc " into calls to ")
-            (<code> {} (esc "__findFile"))
+            (<code> { } (esc "__findFile"))
             (esc " in the ")
-            (<em> {} (esc "current"))
+            (<em> { } (esc "current"))
             (esc " scope.")
           ])
         ])
@@ -67,7 +75,8 @@ let
   ]);
 in
 
-pkgs.runCommandNoCC "html.nix.html" {
+pkgs.runCommand "html.nix.html"
+{
   passAsFile = [ "exampleDocument" ];
   inherit exampleDocument;
   nativeBuildInputs = [ pkgs.html5validator ];
diff --git a/users/sterni/nix/int/default.nix b/users/sterni/nix/int/default.nix
index b315757127..8707445223 100644
--- a/users/sterni/nix/int/default.nix
+++ b/users/sterni/nix/int/default.nix
@@ -2,37 +2,28 @@
 
 let
 
-  # TODO(sterni): implement nix.float and figure out which of these
-  #               functions can be split out into a common nix.num
-  #               library.
-
   inherit (depot.users.sterni.nix)
     string
+    num
     ;
 
   inherit (builtins)
     bitOr
     bitAnd
     bitXor
-    mul
-    div
-    add
-    sub
     ;
 
-  abs = i: if i < 0 then -i else i;
-
   exp = base: pow:
     if pow > 0
     then base * (exp base (pow - 1))
     else if pow < 0
-    then 1.0 / exp base (abs pow)
+    then 1.0 / exp base (num.abs pow)
     else 1;
 
   bitShiftR = bit: count:
     if count == 0
     then bit
-    else div (bitShiftR bit (count - 1)) 2;
+    else (bitShiftR bit (count - 1)) / 2;
 
   bitShiftL = bit: count:
     if count == 0
@@ -47,12 +38,12 @@ let
         if i == 0
         then ""
         else go (bitShiftR i 4)
-           + string.charAt (bitAnd i 15) hexdigits;
+          + string.charAt (bitAnd i 15) hexdigits;
       sign = lib.optionalString (int < 0) "-";
     in
-      if int == 0
-      then "0"
-      else "${sign}${go (abs int)}";
+    if int == 0
+    then "0"
+    else "${sign}${go (num.abs int)}";
 
   fromHexMap = builtins.listToAttrs
     (lib.imap0 (i: c: { name = c; value = i; })
@@ -72,11 +63,12 @@ let
           val = v.val + (fromHexMap."${d}" * v.mul);
           mul = v.mul * 16;
         })
-        { val = 0; mul = 1; } digits;
+        { val = 0; mul = 1; }
+        digits;
     in
-      if negative
-      then -parsed.val
-      else parsed.val;
+    if negative
+    then -parsed.val
+    else parsed.val;
 
   # A nix integer is a 64bit signed integer
   maxBound = 9223372036854775807;
@@ -93,25 +85,24 @@ let
   odd = x: bitAnd x 1 == 1;
   even = x: bitAnd x 1 == 0;
 
-  # div and mod behave like quot and rem in Haskell,
-  # i. e. they truncate towards 0
-  mod = a: b: let res = a / b; in a - (res * b);
-
-  inRange = a: b: x: x >= a && x <= b;
+  quot' = builtins.div; # no typecheck
+  rem = a: b:
+    assert builtins.isInt a && builtins.isInt b;
+    let res = quot' a b; in a - (res * b);
+  quot = a: b:
+    assert builtins.isInt a && builtins.isInt b;
+    quot' a b;
 
-in {
+in
+{
   inherit
     maxBound
     minBound
-    abs
     exp
     odd
     even
-    add
-    sub
-    mul
-    div
-    mod
+    quot
+    rem
     bitShiftR
     bitShiftL
     bitOr
@@ -119,6 +110,5 @@ in {
     bitXor
     toHex
     fromHex
-    inRange
     ;
 }
diff --git a/users/sterni/nix/int/tests/default.nix b/users/sterni/nix/int/tests/default.nix
index fac45dd251..80bd05b6b5 100644
--- a/users/sterni/nix/int/tests/default.nix
+++ b/users/sterni/nix/int/tests/default.nix
@@ -15,9 +15,6 @@ let
     ;
 
   testBounds = it "checks minBound and maxBound" [
-    # this is gonna blow up in my face because
-    # integer overflow is undefined behavior in
-    # C++, so most likely anything could happen?
     (assertEq "maxBound is the maxBound" true
       (int.maxBound + 1 < int.maxBound))
     (assertEq "minBound is the minBound" true
@@ -31,22 +28,262 @@ let
   ];
 
   expectedBytes = [
-    "00" "01" "02" "03" "04" "05" "06" "07" "08" "09" "0A" "0B" "0C" "0D" "0E" "0F"
-    "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "1A" "1B" "1C" "1D" "1E" "1F"
-    "20" "21" "22" "23" "24" "25" "26" "27" "28" "29" "2A" "2B" "2C" "2D" "2E" "2F"
-    "30" "31" "32" "33" "34" "35" "36" "37" "38" "39" "3A" "3B" "3C" "3D" "3E" "3F"
-    "40" "41" "42" "43" "44" "45" "46" "47" "48" "49" "4A" "4B" "4C" "4D" "4E" "4F"
-    "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" "5A" "5B" "5C" "5D" "5E" "5F"
-    "60" "61" "62" "63" "64" "65" "66" "67" "68" "69" "6A" "6B" "6C" "6D" "6E" "6F"
-    "70" "71" "72" "73" "74" "75" "76" "77" "78" "79" "7A" "7B" "7C" "7D" "7E" "7F"
-    "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" "8A" "8B" "8C" "8D" "8E" "8F"
-    "90" "91" "92" "93" "94" "95" "96" "97" "98" "99" "9A" "9B" "9C" "9D" "9E" "9F"
-    "A0" "A1" "A2" "A3" "A4" "A5" "A6" "A7" "A8" "A9" "AA" "AB" "AC" "AD" "AE" "AF"
-    "B0" "B1" "B2" "B3" "B4" "B5" "B6" "B7" "B8" "B9" "BA" "BB" "BC" "BD" "BE" "BF"
-    "C0" "C1" "C2" "C3" "C4" "C5" "C6" "C7" "C8" "C9" "CA" "CB" "CC" "CD" "CE" "CF"
-    "D0" "D1" "D2" "D3" "D4" "D5" "D6" "D7" "D8" "D9" "DA" "DB" "DC" "DD" "DE" "DF"
-    "E0" "E1" "E2" "E3" "E4" "E5" "E6" "E7" "E8" "E9" "EA" "EB" "EC" "ED" "EE" "EF"
-    "F0" "F1" "F2" "F3" "F4" "F5" "F6" "F7" "F8" "F9" "FA" "FB" "FC" "FD" "FE" "FF"
+    "00"
+    "01"
+    "02"
+    "03"
+    "04"
+    "05"
+    "06"
+    "07"
+    "08"
+    "09"
+    "0A"
+    "0B"
+    "0C"
+    "0D"
+    "0E"
+    "0F"
+    "10"
+    "11"
+    "12"
+    "13"
+    "14"
+    "15"
+    "16"
+    "17"
+    "18"
+    "19"
+    "1A"
+    "1B"
+    "1C"
+    "1D"
+    "1E"
+    "1F"
+    "20"
+    "21"
+    "22"
+    "23"
+    "24"
+    "25"
+    "26"
+    "27"
+    "28"
+    "29"
+    "2A"
+    "2B"
+    "2C"
+    "2D"
+    "2E"
+    "2F"
+    "30"
+    "31"
+    "32"
+    "33"
+    "34"
+    "35"
+    "36"
+    "37"
+    "38"
+    "39"
+    "3A"
+    "3B"
+    "3C"
+    "3D"
+    "3E"
+    "3F"
+    "40"
+    "41"
+    "42"
+    "43"
+    "44"
+    "45"
+    "46"
+    "47"
+    "48"
+    "49"
+    "4A"
+    "4B"
+    "4C"
+    "4D"
+    "4E"
+    "4F"
+    "50"
+    "51"
+    "52"
+    "53"
+    "54"
+    "55"
+    "56"
+    "57"
+    "58"
+    "59"
+    "5A"
+    "5B"
+    "5C"
+    "5D"
+    "5E"
+    "5F"
+    "60"
+    "61"
+    "62"
+    "63"
+    "64"
+    "65"
+    "66"
+    "67"
+    "68"
+    "69"
+    "6A"
+    "6B"
+    "6C"
+    "6D"
+    "6E"
+    "6F"
+    "70"
+    "71"
+    "72"
+    "73"
+    "74"
+    "75"
+    "76"
+    "77"
+    "78"
+    "79"
+    "7A"
+    "7B"
+    "7C"
+    "7D"
+    "7E"
+    "7F"
+    "80"
+    "81"
+    "82"
+    "83"
+    "84"
+    "85"
+    "86"
+    "87"
+    "88"
+    "89"
+    "8A"
+    "8B"
+    "8C"
+    "8D"
+    "8E"
+    "8F"
+    "90"
+    "91"
+    "92"
+    "93"
+    "94"
+    "95"
+    "96"
+    "97"
+    "98"
+    "99"
+    "9A"
+    "9B"
+    "9C"
+    "9D"
+    "9E"
+    "9F"
+    "A0"
+    "A1"
+    "A2"
+    "A3"
+    "A4"
+    "A5"
+    "A6"
+    "A7"
+    "A8"
+    "A9"
+    "AA"
+    "AB"
+    "AC"
+    "AD"
+    "AE"
+    "AF"
+    "B0"
+    "B1"
+    "B2"
+    "B3"
+    "B4"
+    "B5"
+    "B6"
+    "B7"
+    "B8"
+    "B9"
+    "BA"
+    "BB"
+    "BC"
+    "BD"
+    "BE"
+    "BF"
+    "C0"
+    "C1"
+    "C2"
+    "C3"
+    "C4"
+    "C5"
+    "C6"
+    "C7"
+    "C8"
+    "C9"
+    "CA"
+    "CB"
+    "CC"
+    "CD"
+    "CE"
+    "CF"
+    "D0"
+    "D1"
+    "D2"
+    "D3"
+    "D4"
+    "D5"
+    "D6"
+    "D7"
+    "D8"
+    "D9"
+    "DA"
+    "DB"
+    "DC"
+    "DD"
+    "DE"
+    "DF"
+    "E0"
+    "E1"
+    "E2"
+    "E3"
+    "E4"
+    "E5"
+    "E6"
+    "E7"
+    "E8"
+    "E9"
+    "EA"
+    "EB"
+    "EC"
+    "ED"
+    "EE"
+    "EF"
+    "F0"
+    "F1"
+    "F2"
+    "F3"
+    "F4"
+    "F5"
+    "F6"
+    "F7"
+    "F8"
+    "F9"
+    "FA"
+    "FB"
+    "FC"
+    "FD"
+    "FE"
+    "FF"
   ];
 
   hexByte = i: string.fit { width = 2; char = "0"; } (int.toHex i);
@@ -64,20 +301,23 @@ let
   ];
 
   testHex = it "checks conversion to hex" (lib.flatten [
-    (lib.imap0 (i: hex: [
-      (assertEq "hexByte ${toString i} == ${hex}" (hexByte i) hex)
-      (assertEq "${toString i} == fromHex ${hex}" i (int.fromHex hex))
-    ]) expectedBytes)
-    (builtins.map ({ left, right }: [
-      (assertEq "toHex ${toString left} == ${right}" (int.toHex left) right)
-      (assertEq "${toString left} == fromHex ${right}" left (int.fromHex right))
-    ]) hexInts)
+    (lib.imap0
+      (i: hex: [
+        (assertEq "hexByte ${toString i} == ${hex}" (hexByte i) hex)
+        (assertEq "${toString i} == fromHex ${hex}" i (int.fromHex hex))
+      ])
+      expectedBytes)
+    (builtins.map
+      ({ left, right }: [
+        (assertEq "toHex ${toString left} == ${right}" (int.toHex left) right)
+        (assertEq "${toString left} == fromHex ${right}" left (int.fromHex right))
+      ])
+      hexInts)
   ]);
 
   testBasic = it "checks basic int operations" [
     (assertEq "122 is even" (int.even 122 && !(int.odd 122)) true)
     (assertEq "123 is odd" (int.odd 123 && !(int.even 123)) true)
-    (assertEq "abs -4959" (int.abs (-4959)) 4959)
   ];
 
   expNumbers = [
@@ -94,20 +334,23 @@ let
   ];
 
   testExp = it "checks exponentiation"
-    (builtins.map ({ left, right }:
-      assertEq
-        "2 ^ ${toString left} == ${toString right}"
-        (int.exp 2 left) right) expNumbers);
+    (builtins.map
+      ({ left, right }:
+        assertEq
+          "2 ^ ${toString left} == ${toString right}"
+          (int.exp 2 left)
+          right)
+      expNumbers);
 
   shifts = [
-    { a =   2; b = 5; c =   64; op = "<<"; }
-    { a =  -2; b = 5; c =  -64; op = "<<"; }
+    { a = 2; b = 5; c = 64; op = "<<"; }
+    { a = -2; b = 5; c = -64; op = "<<"; }
     { a = 123; b = 4; c = 1968; op = "<<"; }
-    { a =   1; b = 8; c =  256; op = "<<"; }
-    { a = 256; b = 8; c =    1; op = ">>"; }
-    { a = 374; b = 2; c =   93; op = ">>"; }
-    { a =   2; b = 2; c =    0; op = ">>"; }
-    { a =  99; b = 9; c =    0; op = ">>"; }
+    { a = 1; b = 8; c = 256; op = "<<"; }
+    { a = 256; b = 8; c = 1; op = ">>"; }
+    { a = 374; b = 2; c = 93; op = ">>"; }
+    { a = 2; b = 2; c = 0; op = ">>"; }
+    { a = 99; b = 9; c = 0; op = ">>"; }
   ];
 
   checkShift = { a, b, c, op }@args:
@@ -116,15 +359,18 @@ let
         "<<" = int.bitShiftL;
         ">>" = int.bitShiftR;
       };
-    in assertEq "${toString a} ${op} ${toString b} == ${toString c}" (f a b) c;
+    in
+    assertEq "${toString a} ${op} ${toString b} == ${toString c}" (f a b) c;
 
   checkShiftRDivExp = n:
     assertEq "${toString n} >> 5 == ${toString n} / 2 ^ 5"
-      (int.bitShiftR n 5) (int.div n (int.exp 2 5));
+      (int.bitShiftR n 5)
+      (n / (int.exp 2 5));
 
   checkShiftLMulExp = n:
     assertEq "${toString n} >> 6 == ${toString n} * 2 ^ 6"
-      (int.bitShiftL n 5) (int.mul n (int.exp 2 5));
+      (int.bitShiftL n 5)
+      (n * (int.exp 2 5));
 
   testBit = it "checks bitwise operations" (lib.flatten [
     (builtins.map checkShift shifts)
@@ -160,44 +406,50 @@ let
   ]);
 
   divisions = [
-    { a =  2; b =  1; c = 2; mod = 0;}
-    { a =  2; b =  2; c = 1; mod = 0;}
-    { a = 20; b = 10; c = 2; mod = 0;}
-    { a = 12; b =  5; c = 2; mod = 2;}
-    { a = 23; b =  4; c = 5; mod = 3;}
+    { a = 2; b = 1; c = 2; rem = 0; }
+    { a = 2; b = 2; c = 1; rem = 0; }
+    { a = 20; b = 10; c = 2; rem = 0; }
+    { a = 12; b = 5; c = 2; rem = 2; }
+    { a = 23; b = 4; c = 5; rem = 3; }
   ];
 
-  checkDiv = n: { a, b, c, mod }: [
-    (assertEq "${n}: div result" (int.div a b) c)
-    (assertEq "${n}: mod result" (int.mod a b) mod)
-    (assertEq "${n}: divMod law" ((int.div a b) * b + (int.mod a b)) a)
+  checkQuot = n: { a, b, c, rem }: [
+    (assertEq "${n}: quot result" (int.quot a b) c)
+    (assertEq "${n}: rem result" (int.rem a b) rem)
+    (assertEq "${n}: quotRem law" ((int.quot a b) * b + (int.rem a b)) a)
   ];
 
-  testDivMod = it "checks integer division and modulo"
+  testQuotRem = it "checks integer quotient and remainder"
     (lib.flatten [
-      (builtins.map (checkDiv "+a / +b") divisions)
-      (builtins.map (fun.rl (checkDiv "-a / +b") (x: x // {
-        a = -x.a;
-        c = -x.c;
-        mod = -x.mod;
-      })) divisions)
-      (builtins.map (fun.rl (checkDiv "+a / -b") (x: x // {
-        b = -x.b;
-        c = -x.c;
-      })) divisions)
-      (builtins.map (fun.rl (checkDiv "-a / -b") (x: x // {
-        a = -x.a;
-        b = -x.b;
-        mod = -x.mod;
-      })) divisions)
+      (builtins.map (checkQuot "+a / +b") divisions)
+      (builtins.map
+        (fun.rl (checkQuot "-a / +b") (x: x // {
+          a = -x.a;
+          c = -x.c;
+          rem = -x.rem;
+        }))
+        divisions)
+      (builtins.map
+        (fun.rl (checkQuot "+a / -b") (x: x // {
+          b = -x.b;
+          c = -x.c;
+        }))
+        divisions)
+      (builtins.map
+        (fun.rl (checkQuot "-a / -b") (x: x // {
+          a = -x.a;
+          b = -x.b;
+          rem = -x.rem;
+        }))
+        divisions)
     ]);
 
 in
-  runTestsuite "nix.int" [
-    testBounds
-    testHex
-    testBasic
-    testExp
-    testBit
-    testDivMod
-  ]
+runTestsuite "nix.int" [
+  testBounds
+  testHex
+  testBasic
+  testExp
+  testBit
+  testQuotRem
+]
diff --git a/users/sterni/nix/list/default.nix b/users/sterni/nix/list/default.nix
new file mode 100644
index 0000000000..568a76d637
--- /dev/null
+++ b/users/sterni/nix/list/default.nix
@@ -0,0 +1,30 @@
+{ ... }:
+
+{
+  /* For a list of length n that consists of lists of length m,
+     return a list of length m containing lists of length n
+     so that
+
+         builtins.elemAt (builtins.elemAt orig a) b
+         == builtins.elemAt (builtins.elemAt transposed b) a
+
+     Essentially, if you think of the nested list as an array with two
+     dimensions, the two index axes are swapped.
+
+     The length of the inner lists m is determined based on the first element
+     and assumed to be used for all other lists. Malformed input data may
+     cause the function to crash or lose data.
+
+     Type: <n>[ <m>[ ] ] -> <m>[ <n>[ ] ]
+  */
+  transpose = list:
+    let
+      innerLength = builtins.length (builtins.head list);
+      outerLength = builtins.length list;
+    in
+    builtins.genList
+      (inner: builtins.genList
+        (outer: builtins.elemAt (builtins.elemAt list outer) inner)
+        outerLength)
+      innerLength;
+}
diff --git a/users/sterni/nix/misc/default.nix b/users/sterni/nix/misc/default.nix
new file mode 100644
index 0000000000..1de9c973ec
--- /dev/null
+++ b/users/sterni/nix/misc/default.nix
@@ -0,0 +1,18 @@
+{ ... }:
+
+let
+  /* Returns true if it is being evaluated using restrict-eval, false if not.
+     It's more robust than using `builtins.getEnv` since it isn't fooled by
+     `env -i`.
+
+     See https://github.com/NixOS/nix/issues/6579 for a description of the
+     behavior. Precise cause in the evaluator / store implementation is unclear.
+
+     Type: bool
+  */
+  inRestrictedEval = builtins.pathExists (toString ./guinea-pig + "/.");
+in
+
+{
+  inherit inRestrictedEval;
+}
diff --git a/users/sterni/nix/misc/guinea-pig b/users/sterni/nix/misc/guinea-pig
new file mode 120000
index 0000000000..73537e478e
--- /dev/null
+++ b/users/sterni/nix/misc/guinea-pig
@@ -0,0 +1 @@
+default.nix
\ No newline at end of file
diff --git a/users/sterni/nix/num/default.nix b/users/sterni/nix/num/default.nix
new file mode 100644
index 0000000000..81e2f8377f
--- /dev/null
+++ b/users/sterni/nix/num/default.nix
@@ -0,0 +1,17 @@
+{ ... }:
+
+rec {
+  inherit (builtins)
+    mul
+    div
+    add
+    sub
+    ;
+
+  sign = i: if i < 0 then -1 else 1;
+  abs = i: if i < 0 then -i else i;
+
+  inRange = a: b: x: x >= a && x <= b;
+
+  sum = builtins.foldl' (a: b: a + b) 0;
+}
diff --git a/users/sterni/nix/num/tests/default.nix b/users/sterni/nix/num/tests/default.nix
new file mode 100644
index 0000000000..ca5f861deb
--- /dev/null
+++ b/users/sterni/nix/num/tests/default.nix
@@ -0,0 +1,26 @@
+{ depot, ... }:
+
+let
+
+  inherit (depot.nix.runTestsuite)
+    runTestsuite
+    it
+    assertEq
+    ;
+
+  inherit (depot.users.sterni.nix)
+    num
+    ;
+
+  testsBasic = it "tests basic operations" [
+    (assertEq "abs -4959" (num.abs (-4959)) 4959)
+    (assertEq "sum" (num.sum [ 123 321 1.5 ]) (123 + 321 + 1.5))
+    (assertEq "inRange"
+      (builtins.map (num.inRange 1.0 5) [ 0 0.5 3 4 4.5 5.5 5 6 ])
+      [ false false true true true false true false ])
+  ];
+in
+
+runTestsuite "nix.num" [
+  testsBasic
+]
diff --git a/users/sterni/nix/string/default.nix b/users/sterni/nix/string/default.nix
index 19d2cec243..381c8ddff7 100644
--- a/users/sterni/nix/string/default.nix
+++ b/users/sterni/nix/string/default.nix
@@ -21,7 +21,8 @@ let
   charAt = i: s:
     let
       r = builtins.substring i 1 s;
-    in if r == "" then null else r;
+    in
+    if r == "" then null else r;
 
   charIndex = char: s:
     let
@@ -32,7 +33,8 @@ let
           [ (charAt i s == char) i ]
           [ true (go (i + 1)) ]
         ];
-    in go 0;
+    in
+    go 0;
 
   toChars = lib.stringToCharacters;
   fromChars = lib.concatStrings;
@@ -46,15 +48,16 @@ let
     let
       leftS = fromChars (builtins.genList (_: char) left);
       rightS = fromChars (builtins.genList (_: char) right);
-    in "${leftS}${s}${rightS}";
+    in
+    "${leftS}${s}${rightS}";
 
   fit = { char ? " ", width, side ? "left" }: s:
     let
       diff = width - builtins.stringLength s;
     in
-      if diff <= 0
-      then s
-      else pad { inherit char; "${side}" = diff; } s;
+    if diff <= 0
+    then s
+    else pad { inherit char; "${side}" = diff; } s;
 
   # pattern matching for strings only
   match = val: matcher: matcher."${val}";
@@ -80,23 +83,27 @@ let
       tokens = lib.flatten (builtins.split "(%.)" formatString);
       argsNeeded = builtins.length (builtins.filter specifierWithArg tokens);
 
-      format = args: (builtins.foldl' ({ out ? "", argIndex ? 0 }: token: {
-        argIndex = argIndex + (if specifierWithArg token then 1 else 0);
-        out =
-          /**/ if token == "%s" then out + builtins.elemAt args argIndex
-          else if token == "%%" then out + "%"
-          else if isSpecifier token then throw "Unsupported format specifier ${token}"
-          else out + token;
-      }) {} tokens).out;
+      format = args: (builtins.foldl'
+        ({ out ? "", argIndex ? 0 }: token: {
+          argIndex = argIndex + (if specifierWithArg token then 1 else 0);
+          out =
+            if token == "%s" then out + builtins.elemAt args argIndex
+            else if token == "%%" then out + "%"
+            else if isSpecifier token then throw "Unsupported format specifier ${token}"
+            else out + token;
+        })
+        { }
+        tokens).out;
 
       accumulateArgs = argCount: args:
         if argCount > 0
         then arg: accumulateArgs (argCount - 1) (args ++ [ arg ])
         else format args;
     in
-      accumulateArgs argsNeeded [];
+    accumulateArgs argsNeeded [ ];
 
-in {
+in
+{
   inherit
     take
     drop
diff --git a/users/sterni/nix/string/tests/default.nix b/users/sterni/nix/string/tests/default.nix
index c8aec94640..e9015e95dc 100644
--- a/users/sterni/nix/string/tests/default.nix
+++ b/users/sterni/nix/string/tests/default.nix
@@ -63,10 +63,10 @@ let
   ];
 
 in
-  runTestsuite "nix.string" [
-    testTakeDrop
-    testIndexing
-    testFinding
-    testMatch
-    testPrintf
-  ]
+runTestsuite "nix.string" [
+  testTakeDrop
+  testIndexing
+  testFinding
+  testMatch
+  testPrintf
+]
diff --git a/users/sterni/nix/url/default.nix b/users/sterni/nix/url/default.nix
index 37bd0de66a..4a401873a1 100644
--- a/users/sterni/nix/url/default.nix
+++ b/users/sterni/nix/url/default.nix
@@ -10,9 +10,24 @@ let
     ;
 
   reserved = c: builtins.elem c [
-    "!" "#" "$" "&" "'" "(" ")"
-    "*" "+" "," "/" ":" ";" "="
-    "?" "@" "[" "]"
+    "!"
+    "#"
+    "$"
+    "&"
+    "'"
+    "("
+    ")"
+    "*"
+    "+"
+    ","
+    "/"
+    ":"
+    ";"
+    "="
+    "?"
+    "@"
+    "["
+    "]"
   ];
 
   unreserved = c: char.asciiAlphaNum c
@@ -21,11 +36,13 @@ let
   percentEncode = c:
     if unreserved c
     then c
-    else "%" + (string.fit {
-      width = 2;
-      char = "0";
-      side = "left";
-    } (int.toHex (char.ord c)));
+    else "%" + (string.fit
+      {
+        width = 2;
+        char = "0";
+        side = "left";
+      }
+      (int.toHex (char.ord c)));
 
   encode = { leaveReserved ? false }: s:
     let
@@ -34,7 +51,8 @@ let
         if leaveReserved && reserved c
         then c
         else percentEncode c;
-    in lib.concatStrings (builtins.map tr chars);
+    in
+    lib.concatStrings (builtins.map tr chars);
 
   decode = s:
     let
@@ -71,9 +89,10 @@ let
         ];
 
     in
-      (builtins.foldl' decodeStep {} tokens).result;
+    (builtins.foldl' decodeStep { } tokens).result;
 
-in {
+in
+{
   inherit
     encode
     decode
diff --git a/users/sterni/nix/url/tests/default.nix b/users/sterni/nix/url/tests/default.nix
index 7cf53cde15..4eb6f95ccd 100644
--- a/users/sterni/nix/url/tests/default.nix
+++ b/users/sterni/nix/url/tests/default.nix
@@ -14,11 +14,13 @@ let
 
   checkEncoding = args: { left, right }:
     assertEq "encode ${builtins.toJSON left} == ${builtins.toJSON right}"
-      (url.encode args left) right;
+      (url.encode args left)
+      right;
 
   checkDecoding = { left, right }:
-  assertEq "${builtins.toJSON left} == decode ${builtins.toJSON right}"
-    (url.decode left) right;
+    assertEq "${builtins.toJSON left} == decode ${builtins.toJSON right}"
+      (url.decode left)
+      right;
 
   unreserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_~";
 
@@ -33,7 +35,7 @@ let
   ];
 
   testEncode = it "checks url.encode"
-    (builtins.map (checkEncoding {}) encodeExpected);
+    (builtins.map (checkEncoding { }) encodeExpected);
 
   testDecode = it "checks url.decode"
     (builtins.map checkDecoding encodeExpected);
@@ -50,7 +52,7 @@ let
       "urn:oasis:names:specification:docbook:dtd:xml:4.1.2"
     ]);
 in
-  runTestsuite "nix.url" [
-    testEncode
-    testLeaveReserved
-  ]
+runTestsuite "nix.url" [
+  testEncode
+  testLeaveReserved
+]
diff --git a/users/sterni/nix/utf8/default.nix b/users/sterni/nix/utf8/default.nix
index 713f1f57cb..e76695f128 100644
--- a/users/sterni/nix/utf8/default.nix
+++ b/users/sterni/nix/utf8/default.nix
@@ -2,12 +2,11 @@
 
 let
 
-  # TODO(sterni): encode
-
   inherit (depot.users.sterni.nix)
     char
     flow
     fun
+    num
     int
     string
     util
@@ -27,7 +26,7 @@ let
      Type: integer -> integer
   */
   byteCount = i: flow.cond [
-    [ (int.bitAnd i 128 == 0)   1 ]
+    [ (int.bitAnd i 128 == 0) 1 ]
     [ (int.bitAnd i 224 == 192) 2 ]
     [ (int.bitAnd i 240 == 224) 3 ]
     [ (int.bitAnd i 248 == 240) 4 ]
@@ -40,40 +39,37 @@ let
      Based on table 3-7. from The Unicode Standard,
      Version 13.0, section 3.9.
 
-     Throws if the first byte is invalid.
-
-     Type: integer -> integer -> (integer -> bool)
+     Type: integer -> integer -> integer -> bool
   */
   wellFormedByte =
     # first byte's integer value
     first:
     # byte position as an index starting with 0
     pos:
-      let
-        defaultRange = int.inRange 128 191;
-      in
-        # The first byte is either ASCII which requires no checks
-        # or we automatically check it when we check the subsequent
-        # bytes. The downside is that this may generate bad error
-        # messages in very rare cases.
-        if pos == 0
-        then lib.const true
-        else if pos > 1 # 3rd and 4th byte have only one validity rule
-        then defaultRange
-        else assert pos == 1; flow.switch first [
-          [ (int.inRange 194 223) defaultRange          ] # C2..DF
-          [ 224                   (int.inRange 160 191) ] # E0
-          [ (int.inRange 225 236) defaultRange          ] # E1..EC
-          [ 237                   (int.inRange 128 159) ] # ED
-          [ (int.inRange 238 239) defaultRange          ] # EE..EF
-          [ 240                   (int.inRange 144 191) ] # F0
-          [ (int.inRange 241 243) defaultRange          ] # F1..F3
-          [ 244                   (int.inRange 128 143) ] # F4
-          [
-            (fun.const true)
-            (builtins.throw "Invalid first byte ${int.toHex first}")
-          ]
-        ];
+    let
+      defaultRange = num.inRange 128 191;
+
+      secondBytePredicate = flow.switch first [
+        [ (num.inRange 194 223) defaultRange ] # C2..DF
+        [ 224 (num.inRange 160 191) ] # E0
+        [ (num.inRange 225 236) defaultRange ] # E1..EC
+        [ 237 (num.inRange 128 159) ] # ED
+        [ (num.inRange 238 239) defaultRange ] # EE..EF
+        [ 240 (num.inRange 144 191) ] # F0
+        [ (num.inRange 241 243) defaultRange ] # F1..F3
+        [ 244 (num.inRange 128 143) ] # F4
+        [ (fun.const true) null ]
+      ];
+
+      firstBytePredicate = byte: assert first == byte;
+        first < 128 || secondBytePredicate != null;
+    in
+    # Either ASCII or in one of the byte ranges of Table 3-6.
+    if pos == 0 then firstBytePredicate
+    # return predicate according to Table 3-6.
+    else if pos == 1 then assert secondBytePredicate != null; secondBytePredicate
+    # 3rd and 4th byte have only one validity rule
+    else defaultRange;
 
   /* Iteration step for decoding an UTF-8 byte sequence.
      It decodes incrementally, i. e. it has to be fed
@@ -133,23 +129,24 @@ let
             # the current value by the amount of bytes left.
             offset = (count - (pos + 1)) * 6;
           in
-            code + (int.bitShiftL (int.bitAnd mask value) offset);
+          code + (int.bitShiftL (int.bitAnd mask value) offset);
       illFormedMsg =
         "Ill-formed byte ${int.toHex value} at position ${toString pos} in ${toString count} byte UTF-8 sequence";
     in
-      if !(wellFormedByte first pos value) then builtins.throw illFormedMsg
-      else if pos + 1 == count
-      then (builtins.removeAttrs args [ # allow extra state being passed through
-        "count"
-        "code"
-        "pos"
-        "first"
-      ]) // { result = newCode; }
-      else (builtins.removeAttrs args [ "result" ]) // {
-        inherit count first;
-        code = newCode;
-        pos  = pos + 1;
-      };
+    if !(wellFormedByte first pos value) then builtins.throw illFormedMsg
+    else if pos + 1 == count
+    then (builtins.removeAttrs args [
+      # allow extra state being passed through
+      "count"
+      "code"
+      "pos"
+      "first"
+    ]) // { result = newCode; }
+    else (builtins.removeAttrs args [ "result" ]) // {
+      inherit count first;
+      code = newCode;
+      pos = pos + 1;
+    };
 
   /* Decode an UTF-8 string into a list of codepoints.
 
@@ -160,49 +157,170 @@ let
   # TODO(sterni): option to fallback to replacement char instead of failure
   decode = s:
     let
-      iter = { codes ? [], ... }@args: byte:
-        let
-          res = step args byte;
-        in
-          # foldl' forceValues the calculate value only at the end
-          # this makes the thunk grow large enough to cause a stack
-          # overflow with sufficiently large strings. To avoid this
-          # we always deepSeq the result which also keeps memory
-          # usage of decode reasonable.
-          builtins.deepSeq res
-            (if res ? result
-            then res // {
-              codes = codes ++ [ res.result ];
-            }
-            else res);
-      iterResult =
-        builtins.foldl' iter {} (string.toChars s);
-      earlyEndMsg =
-        if iterResult ? count && iterResult ? pos
-        then "Missing ${toString (with iterResult; count - pos)} bytes at end of input"
-        else "Unexpected end of input";
+      stringLength = builtins.stringLength s;
+      iterResult = builtins.genericClosure {
+        startSet = [
+          {
+            key = "start";
+            stringIndex = -1;
+            state = { };
+            codepoint = null;
+          }
+        ];
+        operator = { state, stringIndex, ... }:
+          let
+            # updated values for current iteration step
+            newIndex = stringIndex + 1;
+            newState = step state (builtins.substring newIndex 1 s);
+          in
+          lib.optional (newIndex < stringLength) {
+            # unique keys to make genericClosure happy
+            key = toString newIndex;
+            # carryover state for the next step
+            stringIndex = newIndex;
+            state = newState;
+            # actual payload for later, steps with value null are filtered out
+            codepoint = newState.result or null;
+          };
+      };
     in
-      if iterResult ? result
-      then iterResult.codes
-      else builtins.throw earlyEndMsg;
+    # extract all steps that yield a code point into a list
+    builtins.map (v: v.codepoint) (
+      builtins.filter
+        (
+          { codepoint, stringIndex, state, ... }:
+
+          let
+            # error message in case we are missing bytes at the end of input
+            earlyEndMsg =
+              if state ? count && state ? pos
+              then "Missing ${toString (with state; count - pos)} bytes at end of input"
+              else "Unexpected end of input";
+          in
 
-  /* Decodes an UTF-8 string, but doesn't throw on error.
-     Instead it returns null.
+          # filter out all iteration steps without a codepoint value
+          codepoint != null
+          # if we are at the iteration step of a non-empty input string, throw
+          # an error if no codepoint was returned, as it indicates an incomplete
+          # UTF-8 sequence.
+          || (stringLength > 0 && stringIndex == stringLength - 1 && throw earlyEndMsg)
 
-     Type: string -> ( [ integer ] | null)
+        )
+        iterResult
+    );
+
+  /* Pretty prints a Unicode codepoint in the U+<HEX> notation.
+
+     Type: integer -> string
   */
-  decodeSafe = s:
+  formatCodepoint = cp: "U+" + string.fit
+    {
+      width = 4;
+      char = "0";
+    }
+    (int.toHex cp);
+
+  encodeCodepoint = cp:
     let
-      res = builtins.tryEval (decode s);
+      # Find the amount of bytes needed to encode the given codepoint.
+      # Note that this doesn't check if the Unicode codepoint is allowed,
+      # but rather allows all theoretically UTF-8-encodeable ones.
+      count = flow.switch cp [
+        [ (num.inRange 0 127) 1 ] # 00000000 0xxxxxxx
+        [ (num.inRange 128 2047) 2 ] # 00000yyy yyxxxxxx
+        [ (num.inRange 2048 65535) 3 ] # zzzzyyyy yyxxxxxx
+        [ (num.inRange 65536 1114111) 4 ] # 000uuuuu zzzzyyyy yyxxxxxx,
+        # capped at U+10FFFF
+
+        [ (fun.const true) (builtins.throw invalidCodepointMsg) ]
+      ];
+
+      invalidCodepointMsg = "${formatCodepoint cp} is not a Unicode codepoint";
+
+      # Extract the bit ranges x, y, z and u from the given codepoint
+      # according to Table 3-6. from The Unicode Standard, Version 13.0,
+      # section 3.9. u is split into uh and ul since they are used in
+      # different bytes in the end.
+      components = lib.mapAttrs
+        (_: { mask, offset }:
+          int.bitAnd (int.bitShiftR cp offset) mask
+        )
+        {
+          x = {
+            mask = if count > 1 then 63 else 127;
+            offset = 0;
+          };
+          y = {
+            mask = if count > 2 then 63 else 31;
+            offset = 6;
+          };
+          z = {
+            mask = 15;
+            offset = 12;
+          };
+          # u which belongs into the second byte
+          ul = {
+            mask = 3;
+            offset = 16;
+          };
+          # u which belongs into the first byte
+          uh = {
+            mask = 7;
+            offset = 18;
+          };
+        };
+      inherit (components) x y z ul uh;
+
+      # Finally construct the byte sequence for the given codepoint. This is
+      # usually done by using the component and adding a few bits as a prefix
+      # which depends on the length of the sequence. The longer the sequence,
+      # the further back each component is pushed. To simplify this, we
+      # always construct a 4 element list and take the last `count` elements.
+      # Thanks to laziness the bogus values created by this are never evaluated.
+      #
+      # Based on table 3-6. from The Unicode Standard,
+      # Version 13.0, section 3.9.
+      bytes = lib.sublist (4 - count) count [
+        # 11110uuu
+        (uh + 240)
+        # 10uuzzzz or 1110zzzz
+        (z + (if count > 3 then 128 + int.bitShiftL ul 4 else 224))
+        # 10yyyyyy or 110yyyyy
+        (y + (if count > 2 then 128 else 192))
+        # 10xxxxxx or 0xxxxxxx
+        (x + (if count > 1 then 128 else 0))
+      ];
+
+      firstByte = builtins.head bytes;
+
+      unableToEncodeMessage = "Can't encode ${formatCodepoint cp} as UTF-8";
+
     in
-      if res.success
-      then res.value
-      else null;
+    string.fromBytes (
+      builtins.genList
+        (i:
+          let
+            byte = builtins.elemAt bytes i;
+          in
+          if wellFormedByte firstByte i byte
+          then byte
+          else builtins.throw unableToEncodeMessage
+        )
+        count
+    );
+
+  /* Encode a list of Unicode codepoints into an UTF-8 string.
+
+     Type: [ integer ] -> string
+  */
+  encode = lib.concatMapStrings encodeCodepoint;
 
-in {
+in
+{
   inherit
+    encode
     decode
-    decodeSafe
     step
+    formatCodepoint
     ;
 }
diff --git a/users/sterni/nix/utf8/tests/default.nix b/users/sterni/nix/utf8/tests/default.nix
index ed38bd1241..40783eab24 100644
--- a/users/sterni/nix/utf8/tests/default.nix
+++ b/users/sterni/nix/utf8/tests/default.nix
@@ -25,9 +25,10 @@ let
     char
     ;
 
-  rustDecoder = rustSimple {
-    name = "utf8-decode";
-  } ''
+  rustDecoder = rustSimple
+    {
+      name = "utf8-decode";
+    } ''
     use std::io::{self, Read};
     fn main() -> std::io::Result<()> {
       let mut buffer = String::new();
@@ -47,34 +48,49 @@ let
 
   rustDecode = s:
     let
-      expr = runCommandLocal "${s}-decoded" {} ''
+      expr = runCommandLocal "${s}-decoded" { } ''
         printf '%s' ${lib.escapeShellArg s} | ${rustDecoder} > $out
       '';
-    in import expr;
+    in
+    import expr;
 
   hexDecode = l:
     utf8.decode (string.fromBytes (builtins.map int.fromHex l));
 
-  testFailures = it "checks UTF-8 decoding failures" [
-    (assertThrows "emtpy bytestring throws" (utf8.decode ""))
+  hexEncode = l: utf8.encode (builtins.map int.fromHex l);
+
+  testFailures = it "checks UTF-8 decoding failures" ([
     (assertThrows "truncated UTF-8 string throws" (hexDecode [ "F0" "9F" ]))
     # examples from The Unicode Standard
     (assertThrows "ill-formed: C0 AF" (hexDecode [ "C0" "AF" ]))
     (assertThrows "ill-formed: E0 9F 80" (hexDecode [ "E0" "9F" "80" ]))
     (assertEq "well-formed: F4 80 83 92" (hexDecode [ "F4" "80" "83" "92" ]) [ 1048786 ])
-  ];
+    (assertThrows "Codepoint out of range: 0xFFFFFF" (hexEncode [ "FFFFFF" ]))
+    (assertThrows "Codepoint out of range: -0x02" (hexEncode [ "-02" ]))
+  ] ++ builtins.genList
+    (i:
+      let
+        cp = i + int.fromHex "D800";
+      in
+      assertThrows "Can't encode UTF-16 reserved characters: ${utf8.formatCodepoint cp}"
+        (utf8.encode [ cp ])
+    )
+    (int.fromHex "07FF"));
 
   testAscii = it "checks decoding of ascii strings"
-    (builtins.map (s: assertEq "ASCII decoding is equal to UTF-8 decoding for \"${s}\""
-      (string.toBytes s) (utf8.decode s)) [
-        "foo bar"
-        "hello\nworld"
-        "carriage\r\nreturn"
-        "1238398494829304 []<><>({})[]!!)"
-        (string.take 127 char.allChars)
-      ]);
+    (builtins.map
+      (s: assertEq "ASCII decoding is equal to UTF-8 decoding for \"${s}\""
+        (string.toBytes s)
+        (utf8.decode s)) [
+      "foo bar"
+      "hello\nworld"
+      "carriage\r\nreturn"
+      "1238398494829304 []<><>({})[]!!)"
+      (string.take 127 char.allChars)
+    ]);
 
   randomUnicode = [
+    "" # empty string should yield empty list
     "๐Ÿฅฐ๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿˆโ€โฌ›๐Ÿ‘ฉ๐Ÿฝโ€๐Ÿฆฐ"
     # https://kermitproject.org/utf8.html
     "แš แ›‡แšปแ›ซแ›’แ›ฆแšฆแ›ซแš แšฑแšฉแš แšขแšฑแ›ซแš แ›แšฑแšชแ›ซแšทแ›–แšปแšนแ›ฆแ›šแšณแšขแ›—"
@@ -113,9 +129,20 @@ let
         randomUnicode
       ]));
 
+  testDecodingEncoding = it "checks that decoding and then encoding forms an identity"
+    (builtins.map
+      (s: assertEq "Decoding and then encoding โ€œ${s}โ€ yields itself"
+        (utf8.encode (utf8.decode s))
+        s)
+      (lib.flatten [
+        glassSentences
+        randomUnicode
+      ]));
+
 in
-  runTestsuite "nix.utf8" [
-    testFailures
-    testAscii
-    testDecoding
-  ]
+runTestsuite "nix.utf8" [
+  testFailures
+  testAscii
+  testDecoding
+  testDecodingEncoding
+]
diff --git a/users/sterni/nixpkgs-crate-holes/default.nix b/users/sterni/nixpkgs-crate-holes/default.nix
index 9ca72e5463..1630ecb8f1 100644
--- a/users/sterni/nixpkgs-crate-holes/default.nix
+++ b/users/sterni/nixpkgs-crate-holes/default.nix
@@ -24,6 +24,18 @@ let
     eprintf = depot.tools.eprintf;
   };
 
+  # list of maintainers we may @mention on GitHub
+  maintainerWhitelist = builtins.attrValues {
+    inherit (lib.maintainers)
+      sternenseemann
+      qyliss
+      jk
+      symphorien
+      erictapen
+      expipiplus1
+      ;
+  };
+
   # buildRustPackage handling
 
   /* Predicate by which we identify rust packages we are interested in,
@@ -43,24 +55,25 @@ let
   extractCargoLock = drv:
     if !(drv ? cargoDeps.outPath)
     then null
-    else pkgs.runCommandNoCC "${drv.name}-Cargo.lock" {} ''
-      if test -d "${drv.cargoDeps}"; then
-        cp "${drv.cargoDeps}/Cargo.lock" "$out"
-      fi
-
-      if test -f "${drv.cargoDeps}"; then
-        tar -xO \
-          --no-wildcards-match-slash --wildcards \
-          -f "${drv.cargoDeps}" \
-          '*/Cargo.lock' \
-          > "$out"
-      fi
-    '';
+    else
+      pkgs.runCommand "${drv.name}-Cargo.lock" { } ''
+        if test -d "${drv.cargoDeps}"; then
+          cp "${drv.cargoDeps}/Cargo.lock" "$out"
+        fi
+
+        if test -f "${drv.cargoDeps}"; then
+          tar -xO \
+            --no-wildcards-match-slash --wildcards \
+            -f "${drv.cargoDeps}" \
+            '*/Cargo.lock' \
+            > "$out"
+        fi
+      '';
 
   # nixpkgs traversal
 
   # Condition for us to recurse: Either at top-level or recurseForDerivation.
-  recurseInto = path: x: path == [] ||
+  recurseInto = path: x: path == [ ] ||
     (lib.isAttrs x && (x.recurseForDerivations or false));
 
   # Returns the value or false if an eval error occurs.
@@ -85,41 +98,59 @@ let
           doRec = tryEvalOrFalse (recurseInto path x);
           isRust = tryEvalOrFalse (isRustPackage x);
         in
-          if doRec then lib.concatLists (
-            lib.mapAttrsToList (n: go (path ++ [ n ])) x
-          ) else if isDrv && isRust then [
-            {
-              attr = path;
-              lock = extractCargoLock x;
-              maintainers = x.meta.maintainers or [];
-            }
-          ] else [];
-    in go [];
+        if doRec then
+          lib.concatLists
+            (
+              lib.mapAttrsToList (n: go (path ++ [ n ])) x
+            ) else if isDrv && isRust then [
+          {
+            attr = path;
+            lock = extractCargoLock x;
+            maintainers = x.meta.maintainers or [ ];
+          }
+        ] else [ ];
+    in
+    go [ ];
 
   # Report generation and formatting
 
-  reportFor = { attr, lock, ... }: let
-    # naรฏve attribute path to Nix syntax conversion
-    strAttr = lib.concatStringsSep "." attr;
-  in
+  reportFor = { attr, lock, maintainers ? [ ] }:
+    let
+      # naรฏve attribute path to Nix syntax conversion
+      strAttr = lib.concatStringsSep "." attr;
+      strMaintainers = lib.concatMapStringsSep " " (m: "@${m.github}") (
+        builtins.filter (x: builtins.elem x maintainerWhitelist) maintainers
+      );
+    in
     if lock == null
     then pkgs.emptyFile
-    else depot.nix.runExecline "${strAttr}-vulnerability-report" {} [
-      "pipeline" [
-        bins.cargo-audit
-        "audit" "--json"
-        "-n" "--db" rustsec-advisory-db
-        "-f" lock
-      ]
-      "importas" "out" "out"
-      "redirfd" "-w" "1" "$out"
-      bins.jq "-rj" "-f" ./format-audit-result.jq "--arg" "attr" strAttr
-    ];
+    else
+      depot.nix.runExecline "${strAttr}-vulnerability-report" { } [
+        "foreground"
+        [
+          "importas"
+          "out"
+          "out"
+          "redirfd"
+          "-w"
+          "1"
+          "$out"
+          depot.tools.rust-crates-advisory.lock-file-report
+          strAttr
+          lock
+          "true"
+          strMaintainers
+        ]
+        # ignore exit status of report
+        "exit"
+        "0"
+      ];
 
   # GHMF in issues splits paragraphs on newlines
-  description = lib.concatMapStringsSep "\n\n" (
-    builtins.replaceStrings [ "\n" ] [ " " ]
-  ) [
+  description = lib.concatMapStringsSep "\n\n"
+    (
+      builtins.replaceStrings [ "\n" ] [ " " ]
+    ) [
     ''
       The vulnerability report below was generated by
       [nixpkgs-crate-holes](https://code.tvl.fyi/tree/users/sterni/nixpkgs-crate-holes)
@@ -177,39 +208,63 @@ let
       );
     in
 
-    depot.nix.runExecline "nixpkgs-rust-pkgs-vulnerability-report.md" {
-      stdin = lib.concatMapStrings (report: "${report}\n") reports;
-    } [
-      "importas" "out" "out"
-      "redirfd" "-w" "1" "$out"
+    depot.nix.runExecline "nixpkgs-rust-pkgs-vulnerability-report.md"
+      {
+        stdin = lib.concatMapStrings (report: "${report}\n") reports;
+      } [
+      "importas"
+      "out"
+      "out"
+      "redirfd"
+      "-w"
+      "1"
+      "$out"
       # Print introduction paragraph for the issue
-      "if" [ bins.printf "%s\n\n" description ]
+      "if"
+      [ bins.printf "%s\n\n" description ]
       # Print all reports
-      "foreground" [
-        "forstdin" "-E" "report" bins.cat "$report"
+      "foreground"
+      [
+        "forstdin"
+        "-E"
+        "report"
+        bins.cat
+        "$report"
       ]
       # Print stats at the end (mostly as a gimmick), we already know how many
       # attributes there are and count the attributes with vulnerability by
       # finding the number of checkable list entries in the output.
-      "backtick" "-E" "vulnerableCount" [
-        "pipeline" [
-          bins.grep "^- \\[ \\]" "$out"
+      "backtick"
+      "-E"
+      "vulnerableCount"
+      [
+        "pipeline"
+        [
+          bins.grep
+          "^- \\[ \\]"
+          "$out"
         ]
-        bins.wc "-l"
+        bins.wc
+        "-l"
       ]
-      "if" [
+      "if"
+      [
         bins.printf
         "\n%s of %s checked attributes have vulnerable dependencies.\n\n"
         "$vulnerableCount"
         (toString (builtins.length reports))
       ]
-      "if" [
-        bins.printf "%s\n\n" runInstructions
+      "if"
+      [
+        bins.printf
+        "%s\n\n"
+        runInstructions
       ]
     ];
 
   singleReport =
-    { # Attribute to check: string or list of strings (attr path)
+    {
+      # Attribute to check: string or list of strings (attr path)
       attr
       # Path to importable nixpkgs checkout
     , nixpkgsPath
@@ -224,37 +279,63 @@ let
       strAttr = lib.concatStringsSep "." attr';
     in
 
-    depot.nix.runExecline "${strAttr}-report.html" {} [
-      "importas" "out" "out"
-      "backtick" "-I" "-E" "-N" "report" [
-        bins.cargo-audit "audit"
+    depot.nix.runExecline "${strAttr}-report.html" { } [
+      "importas"
+      "out"
+      "out"
+      "backtick"
+      "-I"
+      "-E"
+      "-N"
+      "report"
+      [
+        bins.cargo-audit
+        "audit"
         "--quiet"
-        "-n" "--db" rustsec-advisory-db
-        "-f" lockFile
+        "-n"
+        "--db"
+        rustsec-advisory-db
+        "-f"
+        lockFile
       ]
-      "pipeline" [
-        "ifte" [
-          bins.printf "%s" "$report"
-        ] [
-          bins.printf "%s\n" "No vulnerabilities found"
+      "pipeline"
+      [
+        "ifte"
+        [
+          bins.printf
+          "%s"
+          "$report"
+        ]
+        [
+          bins.printf
+          "%s\n"
+          "No vulnerabilities found"
         ]
-        bins.test "-n" "$report"
+        bins.test
+        "-n"
+        "$report"
       ]
-      "pipeline" [
-        bins.tee "/dev/stderr"
+      "pipeline"
+      [
+        bins.tee
+        "/dev/stderr"
       ]
-      "redirfd" "-w" "1" "$out"
+      "redirfd"
+      "-w"
+      "1"
+      "$out"
       bins.ansi2html
     ];
 
-in {
+in
+{
   full = reportForNixpkgs;
   single = singleReport;
 
   inherit
     extractCargoLock
     allLockFiles
-  ;
+    ;
 
   # simple sanity check, doesn't cover everything, but testing the full report
   # is quite expensive in terms of evaluation.
@@ -263,5 +344,5 @@ in {
     attr = [ "ripgrep" ];
   };
 
-  meta.targets = [ "testSingle" ];
+  meta.ci.targets = [ "testSingle" ];
 }
diff --git a/users/sterni/nixpkgs-crate-holes/format-audit-result.jq b/users/sterni/nixpkgs-crate-holes/format-audit-result.jq
deleted file mode 100644
index c527bc4da9..0000000000
--- a/users/sterni/nixpkgs-crate-holes/format-audit-result.jq
+++ /dev/null
@@ -1,59 +0,0 @@
-# Link to human-readable advisory info for a given vulnerability
-def link:
-  [ "https://rustsec.org/advisories/", .advisory.id, ".html" ] | add;
-
-# Format a list of version constraints
-def version_list:
-  [ .[] | "`" + . + "`" ] | join("; ");
-
-# show paths to fixing this vulnerability:
-#
-# - if there are patched releases, show them (the version we are using presumably
-#   predates the vulnerability discovery, so we likely want to upgrade to a
-#   patched release).
-# - if there are no patched releases, show the unaffected versions (in case we
-#   want to downgrade).
-# - otherwise we state that no unaffected versions are available at this time.
-#
-# This logic should be useful, but is slightly dumber than cargo-audit's
-# suggestion when using the non-JSON output.
-def patched:
-  if .versions.patched == [] then
-    if .versions.unaffected != [] then
-       "unaffected: " + (.versions.unaffected | version_list)
-    else
-      "no unaffected version available"
-    end
-  else
-    "patched: " + (.versions.patched | version_list)
-  end;
-
-# if the vulnerability has aliases (like CVE-*) emit them in parens
-def aliases:
-  if .advisory.aliases == [] then
-    ""
-  else
-    [ " (", (.advisory.aliases | join(", ")), ")" ] | add
-  end;
-
-# each vulnerability is rendered as a (normal) sublist item
-def format_vulnerability:
-  [ "  - "
-  , .package.name, " ", .package.version, ": "
-  , "[", .advisory.id, "](", link, ")"
-  , aliases
-  , ", ", patched
-  , "\n"
-  ] | add;
-
-# be quiet if no found vulnerabilities, otherwise render a GHFM checklist item
-if .vulnerabilities.found | not then
-  ""
-else
-  ([ "- [ ] "
-   , "`", $attr, "`: "
-   , (.vulnerabilities.count | tostring)
-   , " vulnerabilities in Cargo.lock\n"
-   ] + (.vulnerabilities.list | map(format_vulnerability))
-  ) | add
-end
diff --git a/users/sterni/secrets/default.nix b/users/sterni/secrets/default.nix
new file mode 100644
index 0000000000..5550103c5a
--- /dev/null
+++ b/users/sterni/secrets/default.nix
@@ -0,0 +1,3 @@
+{ depot, ... }:
+
+depot.ops.secrets.mkSecrets ./. (import ./secrets.nix)
diff --git a/users/sterni/secrets/minecraft-rcon.age b/users/sterni/secrets/minecraft-rcon.age
new file mode 100644
index 0000000000..6531a74b88
--- /dev/null
+++ b/users/sterni/secrets/minecraft-rcon.age
Binary files differdiff --git a/users/sterni/secrets/secrets.nix b/users/sterni/secrets/secrets.nix
new file mode 100644
index 0000000000..7132fbf8f3
--- /dev/null
+++ b/users/sterni/secrets/secrets.nix
@@ -0,0 +1,15 @@
+let
+  nonremote = [
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJk+KvgvI2oJTppMASNUfMcMkA2G5ZNt+HnWDzaXKLlo"
+  ];
+
+  ingeborg = [
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAHQn/j6NCYucpM7qIEIslVJxiFeUEKa0hi+HobTz/12"
+  ];
+in
+
+{
+  "warteraum-salt.age".publicKeys = nonremote ++ ingeborg;
+  "warteraum-tokens.age".publicKeys = nonremote ++ ingeborg;
+  "minecraft-rcon.age".publicKeys = nonremote ++ ingeborg;
+}
diff --git a/users/sterni/secrets/warteraum-salt.age b/users/sterni/secrets/warteraum-salt.age
new file mode 100644
index 0000000000..61fa5e6161
--- /dev/null
+++ b/users/sterni/secrets/warteraum-salt.age
Binary files differdiff --git a/users/sterni/secrets/warteraum-tokens.age b/users/sterni/secrets/warteraum-tokens.age
new file mode 100644
index 0000000000..e1bf32269f
--- /dev/null
+++ b/users/sterni/secrets/warteraum-tokens.age
@@ -0,0 +1,11 @@
+age-encryption.org/v1
+-> ssh-ed25519 aXKGcg G96nS/CgSFqyum5QtOwyCo2d7PRIx7pcQBVyFjtErUE
+gkQuhegobZ68Z76h93G57/trz7ixSkpa7Dz+OYMzAIw
+-> ssh-ed25519 OaL1CA u9p+ejyLs4cWgB/LjR8XIIE3tRPf+a5Kqwl0nA8pDio
+ZfPVZIcqgyep7C68sTybGFa+7HFDwwoDQwAmoDszua4
+-> |WcGV<-grease >a*ke{l }9Iv) ]qz
+Ehf2eOTQe0t7mnbgNEjJBtRSNRl+MlgEIiziu9YU206yMQXSLrm04PPo9ycw5x/k
+N/5r/M36qnKJfZUVbtcFom85+UYOQDRnfXXvPyTrsA
+--- hRzM1BnEG2VPMV6DTZF2j4WZk/2uM65yAFDK3F0rSQc
+(ซiนๅ™ูบโ)QZ๑q(๐gาl:๔Sœูบ•ฆว’@+fฃ
+R๗ิNoชๆI}ฐ{Œภf
\ No newline at end of file
diff --git a/users/tazjin/OWNERS b/users/tazjin/OWNERS
index c86f6eaa6a..ba1c065348 100644
--- a/users/tazjin/OWNERS
+++ b/users/tazjin/OWNERS
@@ -1,3 +1,3 @@
-inherited: false
-owners:
-  - tazjin
+set noparent
+
+tazjin
diff --git a/users/tazjin/aoc2019/default.nix b/users/tazjin/aoc2019/default.nix
index ce3146d1f7..a1798f4001 100644
--- a/users/tazjin/aoc2019/default.nix
+++ b/users/tazjin/aoc2019/default.nix
@@ -11,12 +11,16 @@ let
   getDay = f: head (matchSolution f);
 
   solutionFiles = filter (e: dir."${e}" == "regular" && isSolution e) (attrNames dir);
-  solutions = map (f: let day = getDay f; in {
-    name = day;
-    value = depot.nix.writeElispBin {
-      name = "aoc2019";
-      deps = p: with p; [ dash s ht ];
-      src = ./. + ("/" + f);
-    };
-  }) solutionFiles;
-in listToAttrs solutions
+  solutions = map
+    (f:
+      let day = getDay f; in {
+        name = day;
+        value = depot.nix.writeElispBin {
+          name = "aoc2019";
+          deps = p: with p; [ dash s ht ];
+          src = ./. + ("/" + f);
+        };
+      })
+    solutionFiles;
+in
+listToAttrs solutions
diff --git a/users/tazjin/aoc2020/default.nix b/users/tazjin/aoc2020/default.nix
index 7a7309ac5a..cd89da7de4 100644
--- a/users/tazjin/aoc2020/default.nix
+++ b/users/tazjin/aoc2020/default.nix
@@ -11,12 +11,16 @@ let
   getDay = f: head (matchSolution f);
 
   solutionFiles = filter (e: dir."${e}" == "regular" && isSolution e) (attrNames dir);
-  solutions = map (f: let day = getDay f; in depot.nix.writeElispBin {
-      name = day;
-      deps = p: with p; [ dash s ht p.f ];
-      src = ./. + ("/" + f);
-  }) solutionFiles;
-in pkgs.symlinkJoin {
+  solutions = map
+    (f:
+      let day = getDay f; in depot.nix.writeElispBin {
+        name = day;
+        deps = p: with p; [ dash s ht p.f ];
+        src = ./. + ("/" + f);
+      })
+    solutionFiles;
+in
+pkgs.symlinkJoin {
   name = "aoc2020";
   paths = solutions;
 }
diff --git a/users/tazjin/aoc2022/day1.rs b/users/tazjin/aoc2022/day1.rs
new file mode 100644
index 0000000000..078eb25f03
--- /dev/null
+++ b/users/tazjin/aoc2022/day1.rs
@@ -0,0 +1,27 @@
+// AoC 2022 - day 1.
+
+fn sum_elf(elf: &str) -> usize {
+    elf.lines()
+        .map(|s| s.parse::<usize>().expect("invalid input"))
+        .sum()
+}
+
+fn group_by_elf(input: &str) -> Vec<usize> {
+    input.rsplit("\n\n").map(sum_elf).collect()
+}
+
+fn top_elf(input: &str) -> usize {
+    group_by_elf(&input).into_iter().max().unwrap()
+}
+
+fn top_n_elves(n: usize, input: &str) -> usize {
+    let mut by_elf = group_by_elf(input);
+    by_elf.sort_by(|a, b| b.cmp(a)); // high->low
+    (by_elf[..n]).iter().sum()
+}
+
+fn main() {
+    let input = std::fs::read_to_string("input").expect("input should be in file named 'input'");
+    println!("top elf: {}", top_elf(&input));
+    println!("top 3 elves: {}", top_n_elves(3, &input));
+}
diff --git a/users/tazjin/aoc2023/day1.el b/users/tazjin/aoc2023/day1.el
new file mode 100644
index 0000000000..b1a7faff02
--- /dev/null
+++ b/users/tazjin/aoc2023/day1.el
@@ -0,0 +1,52 @@
+(require 's)
+(require 'f)
+
+;; task 1
+
+(defun digit-p (c)
+  (and (> c ?0)
+       (<= c ?9)))
+
+(defun aocd1-sum-values (lines)
+  (-sum
+   (-map (lambda (line)
+           (let ((digits (-filter #'digit-p (string-to-list line))))
+             (string-to-number (string (-first-item digits) (-last-item digits)))))
+         lines)))
+
+(let ((lines (s-lines (s-trim (f-read "~/Downloads/input.txt")))))
+  (aocd1-sum-values lines))
+
+;; task 2
+
+(defun replace-written-numbers (input)
+  (with-temp-buffer
+    (insert input)
+    (let ((start 1))
+      (while (< start (point-max))
+        (format-replace-strings
+         '(("oneight" . "18")
+           ("twone" . "21")
+           ("threeight" . "38")
+           ("fiveight" . "58")
+           ("sevenine" . "79")
+           ("eightwo" . "82")
+           ("eighthree" . "83")
+           ("nineight" . "98"))
+         nil start (min (+ 10 start) (point-max)))
+        (format-replace-strings
+         '(("one" . "1")
+           ("two" . "2")
+           ("three" . "3")
+           ("four" . "4")
+           ("five" . "5")
+           ("six" . "6")
+           ("seven" . "7")
+           ("eight" . "8")
+           ("nine" . "9"))
+         nil start (min (+ 5 start) (point-max)))
+        (setq start (1+ start))))
+    (buffer-string)))
+
+(let ((lines (s-lines (s-trim (f-read "~/Downloads/input.txt")))))
+  (aocd1-sum-values (-map #'replace-written-numbers lines)))
diff --git a/users/tazjin/aoc2023/day2.el b/users/tazjin/aoc2023/day2.el
new file mode 100644
index 0000000000..9374d7862c
--- /dev/null
+++ b/users/tazjin/aoc2023/day2.el
@@ -0,0 +1,64 @@
+(require 'dash)
+(require 's)
+(require 'f)
+
+(defvar aoc23-day2-example
+
+  "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
+Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
+Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
+Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
+Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green")
+
+;; part 1
+
+(cl-defstruct aoc23d2-set red green blue)
+
+(defun aoc23d2-parse-set (input)
+  (let ((set (make-aoc23d2-set :red 0 :green 0 :blue 0))
+        (colours (-map #'s-trim (s-split "," input))))
+    (cl-loop for colour in colours
+             do (pcase (s-split " " colour t)
+                  (`(,num "red") (setf (aoc23d2-set-red set) (string-to-number num)))
+                  (`(,num "green") (setf (aoc23d2-set-green set) (string-to-number num)))
+                  (`(,num "blue") (setf (aoc23d2-set-blue set) (string-to-number num)))))
+    set))
+
+(cl-defstruct aoc23d2-game id sets)
+
+(defun aoc23d2-parse-game (input)
+  (pcase-let* ((`(,id-str ,sets-str) (s-split-up-to ":" input 1 t))
+               (game-id (string-to-number (s-chop-left (length "Game ") id-str)))
+               (sets (-map #'aoc23d2-parse-set (s-split ";" sets-str t))))
+    (make-aoc23d2-game :id game-id :sets sets)))
+
+(defun aoc23d2-game-possible-p (game r g b)
+  (cl-every (lambda (set)
+              (and (<= (aoc23d2-set-red set) r)
+                   (<= (aoc23d2-set-green set) g)
+                   (<= (aoc23d2-set-blue set) b)))
+            (aoc23d2-game-sets game)))
+
+(let ((input (f-read "~/Downloads/input.txt")))
+  (-sum
+   (-map #'aoc23d2-game-id
+         (-filter (lambda (g) (aoc23d2-game-possible-p g 12 13 14))
+                  (-map #'aoc23d2-parse-game (s-lines (s-trim input)))))))
+
+;; part 2
+
+(defun aoc23d2-game-min-cubes-power (game)
+  (let ((r 0)
+        (g 0)
+        (b 0))
+    (-each (aoc23d2-game-sets game)
+      (lambda (set)
+        (setq r (max r (aoc23d2-set-red set)))
+        (setq g (max g (aoc23d2-set-green set)))
+        (setq b (max b (aoc23d2-set-blue set)))))
+    (* (max 1 r) (max 1 g) (max 1 b))))
+
+(let ((input (f-read "~/Downloads/input.txt")))
+  (-sum
+   (-map #'aoc23d2-game-min-cubes-power
+         (-map #'aoc23d2-parse-game (s-lines (s-trim input))))))
diff --git a/users/tazjin/aoc2023/day3.el b/users/tazjin/aoc2023/day3.el
new file mode 100644
index 0000000000..dd39c1b836
--- /dev/null
+++ b/users/tazjin/aoc2023/day3.el
@@ -0,0 +1,110 @@
+(defun aoc23d3-symbol-p (c)
+  (not (or (= c ? )
+           (and (>= c ?0)
+                (<= c ?9)))))
+
+(defun rectangle-for-bounds (bounds)
+  (let* ((start (save-excursion
+                     (goto-char (car bounds))
+                     (let ((col (current-column)))
+                       (forward-line -1)
+                       (move-to-column (max 0 (1- col))))
+                     (point)))
+         (end (save-excursion
+                (goto-char (cdr bounds))
+                (let ((col (current-column)))
+                  (forward-line 1)
+                  (move-to-column (1+ col)))
+                (point))))
+    (list start end)))
+
+(defun get-machine-part ()
+  (interactive)
+  (when-let* ((num-raw (number-at-point))
+              (num (abs num-raw))
+              ;; handles negative number edge case (bounds contain the `-')
+              (bounds-raw (bounds-of-thing-at-point 'number))
+              (bounds (if (< num-raw 0)
+                          (cons (1- (car bounds-raw)) (cdr bounds-raw))
+                        bounds-raw))
+              (rectangle (rectangle-for-bounds bounds))
+              (neighbours (apply #'concat
+                                 (apply #'extract-rectangle rectangle))))
+    (if (-any #'aoc23d3-symbol-p (string-to-list neighbours))
+        (cons num rectangle)
+      (cons nil rectangle))))
+
+
+(defun find-machine-parts (input)
+  (with-temp-buffer
+    (insert input)
+    (goto-char (point-min))
+    (save-excursion
+      (replace-string "." " "))
+
+    (cl-loop while (forward-word)
+             for result = (get-machine-part)
+             when (car result) collect (car result))))
+
+
+;; debugging
+
+(defvar aoc23d3-example "467..114..
+...*......
+..35..633.
+......#...
+617*......
+.....+.58.
+..592.....
+......755.
+...$.*....
+.664.598..")
+
+(defvar aoc23d3-example2 "12.......*..
++.........34
+.......-12..
+..78........
+..*....60...
+78..........
+.......23...
+....90*12...
+............
+2.2......12.
+.*.........*
+1.1.......56")
+
+(defvar aoc23d3-example3 "243.
+..*.
+....")
+
+(defun aoc23d3-debug (p)
+  "Interactive debugger for the solution, can be bound to a key in
+an input buffer. Dots should already have been replaced with
+spaces."
+  (interactive "P")
+  (unless p
+    (goto-char aoc23d3-last))
+  (rectangle-mark-mode 1)
+  (forward-word)
+  (setq aoc23d3-last (point))
+  (pcase (get-machine-part)
+    (`(nil ,b ,e) (progn (set-mark b)
+                          (goto-char e)
+                          (set-face-attribute 'region nil :background "#FAA0A0")))
+    (`(,num ,b ,e) (progn (set-mark b)
+                          (goto-char e)
+                          (set-face-attribute 'region nil :background "#d1ffbd")))
+    (other (deactivate-mark))))
+
+(cl-assert (= 4361 (-sum (find-machine-parts aoc23d3-example))) nil
+           "example from website is working")
+
+(cl-assert (= 413 (-sum (find-machine-parts aoc23d3-example2))) nil
+           "example from subreddit is working")
+
+(cl-assert (= 243 (-sum (find-machine-parts aoc23d3-example3))) nil
+           "example from telegram is working")
+
+;; day 1 (incomplete)
+
+(-sum (find-machine-parts (s-trim (f-read "~/Downloads/input.txt"))))
diff --git a/users/tazjin/blog/default.nix b/users/tazjin/blog/default.nix
index fd2f38839a..60c79f0941 100644
--- a/users/tazjin/blog/default.nix
+++ b/users/tazjin/blog/default.nix
@@ -4,25 +4,47 @@ with depot.nix.yants;
 
 let
   inherit (builtins) hasAttr filter;
+
+  config = {
+    name = "tazjin's blog";
+    baseUrl = "https://tazj.in/blog";
+    staticUrl = "https://tazj.in/static/";
+
+    footer = ''
+      <p class="footer">
+        <a class="uncoloured-link" href="https://tazj.in">homepage</a>
+        |
+        <a class="uncoloured-link" href="https://cs.tvl.fyi/">code</a>
+      </p>
+      <p class="lod">เฒ _เฒ </p>
+    '';
+  };
+
   inherit (depot.web.blog) post includePost renderPost;
 
-  posts = filter includePost (list post (import ./posts.nix));
+  posts = list post (import ./posts.nix);
 
-  rendered = pkgs.runCommandNoCC "tazjins-blog" {} ''
+  rendered = pkgs.runCommand "tazjins-blog" { } ''
     mkdir -p $out
 
     ${lib.concatStringsSep "\n" (map (post:
-      "cp ${renderPost post} $out/${post.key}.html"
+      "cp ${renderPost config post} $out/${post.key}.html"
     ) posts)}
   '';
 
-in {
-  inherit posts rendered;
+in
+{
+  inherit rendered config;
+
+  # Filter unlisted posts from the index
+  posts = filter includePost posts;
 
   # Generate embeddable nginx configuration for redirects from old post URLs
-  oldRedirects = lib.concatStringsSep "\n" (map (post: ''
-    location ~* ^(/en)?/${post.oldKey} {
-      return 301 https://tazj.in/blog/${post.key};
-    }
-  '') (filter (hasAttr "oldKey") posts));
+  oldRedirects = lib.concatStringsSep "\n" (map
+    (post: ''
+      location ~* ^(/en)?/${post.oldKey} {
+        return 301 https://tazj.in/blog/${post.key};
+      }
+    '')
+    (filter (hasAttr "oldKey") posts));
 }
diff --git a/users/tazjin/blog/posts.nix b/users/tazjin/blog/posts.nix
index b43598d013..a95a50d766 100644
--- a/users/tazjin/blog/posts.nix
+++ b/users/tazjin/blog/posts.nix
@@ -1,6 +1,19 @@
 # This file defines all the blog posts.
 [
   {
+    key = "reliably-switch-buffers";
+    title = "ะ—ะฐั‡ะตะผ reliably-switch-buffers?";
+    content = ./posts/reliably-switch-buffers.md;
+    date = 1692882000;
+  }
+  {
+    key = "tvix-eval-talk-2023";
+    title = "[ะดะพะบะปะฐะด] tvix-eval, ะธะผะฟะปะตะผะตะฝั‚ะฐั†ะธั ัะทั‹ะบะฐ Nix ะฝะฐ Rust";
+    date = 1694102400;
+    content = ./posts/tvix-eval-talk-2023.md;
+    tagfilter = false;
+  }
+  {
     key = "emacs-is-underrated";
     title = "Emacs is the most underrated tool";
     date = 1581286656;
@@ -37,7 +50,7 @@
     key = "the-smu-problem";
     title = "The SMU-problem of messaging apps";
     date = 1450354078;
-    content =./posts/the-smu-problem.md;
+    content = ./posts/the-smu-problem.md;
     oldKey = "1450354078";
   }
   {
@@ -46,6 +59,7 @@
     date = 1423995834;
     content = ./posts/sick-in-sweden.md;
     oldKey = "1423995834";
+    listed = false;
   }
   {
     key = "nsa-zettabytes";
@@ -54,4 +68,11 @@
     content = ./posts/nsa-zettabytes.md;
     oldKey = "1375310627";
   }
+  {
+    key = "thoughts";
+    title = "Some thoughts";
+    date = 1665095948;
+    content = ./posts/thoughts.md;
+    listed = false;
+  }
 ]
diff --git a/users/tazjin/blog/posts/best-tools.md b/users/tazjin/blog/posts/best-tools.md
index e4bad8f4cd..afe61767b1 100644
--- a/users/tazjin/blog/posts/best-tools.md
+++ b/users/tazjin/blog/posts/best-tools.md
@@ -42,19 +42,32 @@ list. I own several of them and there will probably be more in the future. They
 last forever and your wrists will thank you in the future, even if you do not
 suffer from RSI yet.
 
+Kinesis have announced a split version of the Advantage. Once that is
+easily available, I will buy one and evaluate it.
+
 [advantage]: https://kinesis-ergo.com/shop/advantage2/
 
 ## Speakers
 
-The speakers that I've hooked up to my audio setup (including both record player
-& Chromecast / TV) are the [Teufel Motiv 2][motiv-2]. I've had these for over a
-decade and they're incredibly good, but unfortunately Teufel no longer makes
-them.
+There are two sets of speakers I use, unfortunately one pair has been in storage
+since I left the UK.
+
+My original favourite speakers are the [Teufel Motiv 2][motiv-2], usually hooked
+up to a Chromecast and a record player. I've had these for over a decade and
+they're incredibly good, but unfortunately Teufel no longer makes them. Mine are
+currently in a warehouse somewhere in London, and I don't know when I will see
+them again ...
 
 It's possible to grab a pair on eBay occasionally, so keep an eye out if you're
 interested!
 
+In my Moscow flat, I have a pair of [Wharfedale Diamond 12][diamond-12]
+connected to a Philips amplifier older than myself. These provide an excellent,
+balanced, "Wharfedale-sound". Some people find it needs some getting used to,
+but don't want to go back after that initial phase.
+
 [motiv-2]: https://www.teufelaudio.com/uk/pc/motiv-2-p167.html
+[diamond-12]: https://www.wharfedaleusa.com/collections/diamond-12
 
 ## Headphones
 
@@ -85,40 +98,38 @@ flat.
 
 ## Phone
 
-The best phone I have used in recent years is the [iPhone SE][se]. It was the
-*last* phone that had a reasonable size (up to 4") *and* a 3.5mm headphone jack.
+My current phone is the [Palm phone][palm-phone]. It's basically the smallest
+smartphone on the market at about the size of a credit card. I picked this phone
+because I am trying to not use a phone anymore, but some things simply require a
+smartphone.
 
-Unfortunately, it runs iOS. Despite owning a whole bunch of SEs, I have finally
-moved on to an Android phone that is only moderately larger (still by an
-annoying amount), but does at least have a headphone jack: The [Samsung Galaxy
-S10e][s10e].
+The Palm has terrible battery life but is otherwise great. I always have it in
+power saving mode and only turn on receiving calls when I'm expecting a call,.
 
-It has pretty good hardware and I can almost reach 70% of the screen, which is
-better than other phones out there right now. Unfortunately it runs Samsung's
-impossible-to-remove bloatware on top of Android, but that is still less
-annoying to use than iOS.
+Previous phones I used and liked were:
 
-QUESTION: This is the only item on this list for which I am actively seeking a
-replacement, so if you have any tips about new phones that might fit these
-criteria that I've missed please let me know!
+* [Unihertz Atom L][atom-l] - small-screen, rugged phone with headphone jack
+* Original [iPhone SE][se] - perfect-sized phone, unfortunately with iOS
 
+[palm-phone]: https://palm.com/pages/product
+[atom-l]: https://www.unihertz.com/products/atom-l
 [se]: https://en.wikipedia.org/wiki/IPhone_SE
-[s10e]: https://www.phonearena.com/phones/Samsung-Galaxy-S10e_id11114
 
 # Other stuff
 
 ## Toothbrush
 
-The [Philips Sonicare][sonicare] (note: link goes to a newer generation than
-mine) is excellent and well worth its money.
+The [Philips Sonicare][sonicare] is excellent and well worth its price.
 
 I've had it for a few years and whereas I occasionally had minor teeth issues
 before, they seem to be mostly gone now. According to my dentist the state of my
 teeth is now usually pretty good and I draw a direct correlation back to this
 thing.
 
-The newer generations come with flashy features like apps and probably more
-LEDs, but I suspect that those can just be ignored.
+It has an app and stuff, but I just ignore that.
+
+I first got one of these in about 2014, and it lasted until 2020, at which point
+I upgraded to whatever the current model was.
 
 [sonicare]: https://www.philips.co.uk/c-m-pe/electric-toothbrushes
 
@@ -126,10 +137,12 @@ LEDs, but I suspect that those can just be ignored.
 
 The [Philipps SensoTouch 3D][sensotouch] is excellent. Super-comfortable close
 face shave in no time and leaves absolutely no mess around, as far as I can
-tell! I've had this for ~5 years and it's not showing any signs of aging yet.
+tell! I've had this for ~7 years and it's not showing any serious signs of aging
+yet.
 
-Another bonus is that its battery time is effectively infinite. I've never had
-to worry when bringing it on a longer trip!
+Another bonus is that its battery time is effectively infinite (in the order of
+months of use per charge). I've never had to worry when bringing it on a longer
+trip!
 
 [sensotouch]: https://www.philips.co.uk/c-p/1250X_40/norelco-sensotouch-3d-wet-and-dry-electric-razor-with-precision-trimmer
 
@@ -146,15 +159,26 @@ The one I settled on is the [Waterfield Muzetto][muzetto] leather bag. It's one
 of those things that comes with a bit of a price tag attached, but it's well
 worth it!
 
+**Unfortunately**, just like my speakers, this bag is now in storage somewhere
+in the UK since I left the country.
+
+After moving to Moscow I quickly ran into the same problem as in London when
+using the metro, but getting another Muzetto was kind of impractical.
+
+I couldn't find any other vertical messenger bags that I liked, and ended up
+going for a more traditional one: The [Brialdi Ostin][ostin].
+
 [muzetto]: https://www.sfbags.com/collections/shoulder-messenger-bags/products/muzetto-leather-bag
+[ostin]: https://www.brialdi.ru/shop/handbags/brialdi_ostin_brown/
 
 ## Wallet
 
-My wallet is the [Bellroy Slim Sleeve][slim-sleeve]. I don't carry cash unless
-I'm attending an event in Germany and this wallet fits that lifestyle perfectly.
+My wallet is the [Bellroy Coin Wallet][coin-wallet]. It's the slimmest wallet I
+could find that can deal with the volume of cards I (have to) carry around, as
+well as with cash.
 
-It's near indestructible, looks great, is very slim and fits a ton of cards,
-business cards, receipts and whatever else you want to be lugging around with
-you!
+I've used Bellroy wallets for a long time, with the [Slim Sleeve][slim-sleeve]
+serving me in the days when I lived in no-cash countries.
 
+[coin-wallet]: https://bellroy.com/products/coin-wallet
 [slim-sleeve]: https://bellroy.com/products/slim-sleeve-wallet/default/charcoal
diff --git a/users/tazjin/blog/posts/nixery-layers.md b/users/tazjin/blog/posts/nixery-layers.md
index 3f25ceadce..26526d11b5 100644
--- a/users/tazjin/blog/posts/nixery-layers.md
+++ b/users/tazjin/blog/posts/nixery-layers.md
@@ -260,13 +260,13 @@ TIP: This is implemented in [popcount][] in Nixery.
 Hopefully this detailed design review was useful to you. You can also watch [my
 NixCon talk][talk] about Nixery for a review of some of this, and some demos.
 
-[Nixery]: https://github.com/google/nixery
+[Nixery]: https://cs.tvl.fyi/depot/-/tree/tools/nixery
 [grhmc]: https://grahamc.com/blog/nix-and-layered-docker-images
 [Nix]: https://nixos.org/nix
 [registry protocols]: https://github.com/opencontainers/distribution-spec/blob/master/spec.md
 [nixery.dev]: https://nixery.dev
 [dominator trees]: https://en.wikipedia.org/wiki/Dominator_(graph_theory)
 [gonum/graph]: https://godoc.org/gonum.org/v1/gonum/graph
-[layers.go]: https://github.com/google/nixery/blob/master/builder/layers.go
-[popcount]: https://github.com/google/nixery/tree/master/popcount
+[layers.go]: https://cs.tvl.fyi/depot/-/blob/tools/nixery/layers/layers.go
+[popcount]: https://cs.tvl.fyi/depot/-/tree/tools/nixery/popcount
 [talk]: https://www.youtube.com/watch?v=pOI9H4oeXqA
diff --git a/users/tazjin/blog/posts/reliably-switch-buffers.md b/users/tazjin/blog/posts/reliably-switch-buffers.md
new file mode 100644
index 0000000000..ec56c4b2d0
--- /dev/null
+++ b/users/tazjin/blog/posts/reliably-switch-buffers.md
@@ -0,0 +1,18 @@
+ะ’ั‡ะตั€ะฐ ะฒะตั‡ะตั€ะพะผ ะฝะฐะฟะธัะฐะป ะฝะตะบะพั‚ะพั€ั‹ะต ะฟะฐั‚ั‡ะธ ะดะปั ะผะพะตะณะพ emacs-ะบะพะฝั„ะธะณะฐ. ะ˜ั… ะฝะฐ ัะฐะผะพะผ ะดะตะปะต ะดะฐะฒะฝะพ ัƒะถะต ั…ะพั‚ะตะป ะฝะฐะฟะธัะฐั‚ัŒ, ะพะฝะธ ั€ะตัˆะฐัŽั‚ ะผะฐะปะตะฝัŒะบะธะต ะฟั€ะพะฑะปะตะผั‹ ะบะพั‚ะพั€ั‹ะต ะผะฝะต ะฟะพัั‚ะพัะฝะฝะพ ะผะตัˆะฐะปะธ. ะžะฑ ะพะดะฝะพะน ะธะท ะฟั€ะพะฑะปะตะผ ั ั…ะพั‡ัƒ ั€ะฐััะบะฐะทะฐั‚ัŒ, ะฟะพั‚ะพะผัƒ ั‡ั‚ะพ ะพะฝะฐ ะฟั€ะธะฒะตะปะฐ ะบ ั‚ะพะผัƒ, ั‡ั‚ะพ "ะฟะพั€ะพะณ ั€ะฐะทะดั€ะฐะถะตะฝะธั" ะฑั‹ะป ะฟะตั€ะตัั‚ัƒะฟะปะตะฝ.
+
+Emacs ัƒ ะผะตะฝั ะพัะฝะพะฒะฝะฐั ั‡ะฐัั‚ัŒ ัะฒะพะตะน ั€ะฐะฑะพั‡ะตะน ัั€ะตะดั‹. ะžะฝ ัƒ ะผะตะฝั ัะฒะปัะตั‚ัั, ะบะพะฝะตั‡ะฝะพ, ั‚ะตะบัั‚ะพะฒั‹ะผ ั€ะตะดะฐะบั‚ะพั€ะพะผ, ะฝะพ ะธ ะตั‰ะต ะผะตะฝะตะดะถะตั€ะพะผ ะพะบะพะฝ, ะผัะนะป-ะบะปะธะตะฝั‚ะพะผ, ั‡ะฐั‚-ะบะปะธะตะฝั‚ะพะผ ะธ ะผะฝะพะณะพ ะดั€ัƒะณะพะณะพ.
+
+ะ’ะฝัƒั‚ั€ะธ emacs ะตัั‚ัŒ ะบะพะฝั†ะตะฟั†ะธั "ะฑัƒั„ะตั€ะพะฒ", ะพะดะธะฝ ะฑัƒั„ะตั€ ะผะพะถะตั‚ ะฑั‹ั‚ัŒ ะพะดะธะฝ ะพั‚ะบั€ั‹ั‚ั‹ะน ั„ะฐะนะป ะฒ ั‚ะตะบัั‚ะพะฒะพะผ ั€ะตะดะฐะบั‚ะพั€ะต, ะพะดะธะฝ ั‡ะฐั‚ ะฝะฐ ะขะตะปะตะณั€ะฐะผะต, ะธะปะธ ะพะดะฝะพ ะดะตัะบั‚ะพะฟะฝะพะต ะพะบะฝะพ (ะฝะฐะฟั€ะธะผะตั€, ะฑั€ะฐัƒะทะตั€). ะะฐะฒะธะณะฐั†ะธั ะผะตะถะดัƒ ะฝะธะผะธ ะพััƒั‰ะตัั‚ะฒะปัะตั‚ัั ั ะฟะพะผะพั‰ัŒัŽ ะบะพะผะฐะฝะดั‹ `switch-to-buffer` (ะธะปะธ ะบะพะต-ะบะฐะบะธั… ะฐะปัŒั‚ะตั€ะฝะฐั‚ะธะฒ, ะฝะฐะฟั€ะธะผะตั€ `ivy-switch-buffer`, `helm-switch-buffer` ะธ ั‚ะฐะบ ะดะฐะปะตะต). ะ‘ัƒั„ะตั€ - ะฝะฐ ัั‚ะพั€ะพะฝะต emacs-lisp ัะฒะปัะตั‚ัั ะพะฑัŠะตะบั‚ะพะผ ั ะฝะตะบะพั‚ะพั€ั‹ะผะธ ะฟะพะปัะผะธ. ะžะดะฝะพ ะธะท ะฝะธั…: `buffer-name`.
+
+ะฃ ะฒัะตั… buffer-switch ะบะพะผะฐะฝะด ะตัั‚ัŒ ะพะดะธะฝะฐะบะพะฒะฐั ะฟั€ะพะฑะปะตะผะฐ: ะžะฝะธ ะฑะตั€ัƒั‚ ัะฟะธัะพะบ ะฑัƒั„ะตั€ะพะฒ ะธะท emacs, ะฟะพะบะฐะทั‹ะฒะฐัŽั‚ *ะธะผะตะฝะฐ* ะฑัƒั„ะตั€ะพะฒ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปัŽ, ะธ ะฒ ั€ะตะทัƒะปัŒั‚ะฐั‚ะต ะฟะพะปัƒั‡ะฐัŽั‚ ะฒั‹ะฑั€ะฐะฝะฝะพะต *ะธะผั*. ะ—ะฐั‚ะตะผ ะพะฝะธ ะฟั€ะพััั‚ emacs ะพั‚ะบั€ั‹ั‚ัŒ ะฑัƒั„ะตั€ ั ัั‚ะธะผ ะธะผะตะฝะตะผ.
+
+ะšั‚ะพ-ั‚ะพ ะฝะฐะฒะตั€ะฝะพ ัƒะถะต ะฟะพะฝัะป ะบะฐะบะฐั ั‚ัƒั‚ ะฟั€ะพะฑะปะตะผะฐ. ะ˜ะผะตะฝะธ ะฑัƒั„ะตั€ะพะฒ ะผะพะณัƒั‚ ะผะตะฝัั‚ัŒัั, ะธ ะดะฐ, ะฝะต ั‚ะพะปัŒะบะพ ะผะพะณัƒั‚, ะฝะพ ะธ ะดะตะปะฐัŽั‚! ะะฐะฟั€ะธะผะตั€, ะขะตะปะตะณั€ะฐะผ-ะบะปะธะตะฝั‚ ะผะพะถะตั‚ ะฟะพะบะฐะทะฐั‚ัŒ ะบะฐะปะธั‡ะตัั‚ะฒะพ ะฝะตะฟั€ะพั‡ะธั‚ะฐะฝะฝั‹ั… ัะพะพะฑั‰ะตะฝะธะน ะฒ ะฝะฐะทะฒะฐะฝะธะธ, ะพะบะฝะพ ั ะฏะฝะดะตะบั ะœัƒะทั‹ะบะพะน ะผะตะฝัะตั‚ ะฝะฐะทะฒะฐะฝะธั ะฟะพ ั‚ั€ะตะบัƒ, ะธ ั‚ะฐะบ ะดะฐะปะตะต. ะŸะพะปัƒั‡ะฐะตั‚ัั ะดะพะฒะพะปัŒะฝะพ ั‡ะฐัั‚ะพ ั‚ะฐะบะฐั ัะธั‚ัƒะฐั†ะธั, ั‡ั‚ะพ ะฝะฐะทะฒะฐะฝะธะต ะผะตะฝัะตั‚ัั ะฟั€ะธ ะฒั‹ะฑะพั€ะต ะฑัƒั„ะตั€ะฐ, ะธ `switch-to-buffer` ะฑะพะปัŒัˆะต ะฝะต ะฝะฐะนะดะตั‚ ะฒั‹ะฑั€ะฐะฝะฝั‹ะน ะฑัƒั„ะตั€ ะธ ะฟั€ะพัั‚ะพ ะพั‚ะบั€ั‹ะฒะฐะตั‚ ะฝะพะฒั‹ะน, ะฟัƒัั‚ะพะน ะฑัƒั„ะตั€ ั ัั‚ะฐั€ั‹ะผ ะฝะฐะทะฒะฐะฝะธะตะผ! ะšะพะณะดะฐ ั€ะฐะทั€ะฐะฑะพั‚ั‹ะฒะฐะปะธ ัั‚ะธ ะบะพะผะฐะฝะดั‹ ะฒ emacs (ะดะฐ, ัั‚ะพ ัะพะฒะตั€ัˆะตะฝะฝะพ ะดะฐะฒะฝะพ, ะณะดะต-ั‚ะพ ะฒ 70ั…/80ั…, ะฑะพะปัŒัˆะธะฝัั‚ะฒะฐ ะฝะฐั ะฟะพะบะฐ ะฝะต ะฑั‹ะปะพ ั‚ะพะณะดะฐ!), ะพะฝะธ ะฝะธะบะพะณะดะฐ ะฝะต ัั‚ะฐะปะบะธะฒะฐะปะธััŒ ั ั‚ะฐะบะธะผะธ ัะธั‚ัƒะฐั†ะธัะผะธ, ะธ ัั‚ะพ ั€ะตัˆะตะฝะธะต, ะบะพั‚ะพั€ะพะต ั‚ะพะณะดะฐ ั…ะพั€ะพัˆะพ ั€ะฐะฑะพั‚ะฐะปะพ ั‚ะตะฟะตั€ัŒ ะฑะพะปัŒัˆะต ะฟั€ะพัั‚ะพ ะฝะต ะฐะดะตะบะฒะฐั‚ะฝะพ.
+
+ะคะธะบั ะฑั‹ะป ะฝะต ะพั‡ะตะฝัŒ ัะปะพะถะฝั‹ะผ. ะ’ะผะตัั‚ะพ ัะฟะธัะบะฐ ะธะผะตะฝ ะฑัƒั„ะตั€ะพะฒ ัะพะทะดะฐัŽ alist ั ะฝะฐะทะฒะฐะฝะธะตะผ ะธ *ั ัะฐะผะธะผ ะพะฑัŠะตะบั‚ะพะผ*, ะธ ะฟะพัะปะต ะฒั‹ะฑะพั€ะฐ ะฑัƒั„ะตั€ะฐ ั ัะฟะธัะบะฐ ะฟะตั€ะตะดะฐัŽ ะธะผะตะฝะฝะพ ัั‚ะพั‚ ะพะฑัŠะตะบั‚, ะฐ ะฝะต ั‚ะพะปัŒะบะพ ะตะณะพ ะฝะฐะทะฒะฐะฝะธะต, ะฒ ั„ัƒะฝะบั‚ั†ะธัŽ, ะบะพั‚ะพั€ะฐั ะพั‚ะบั€ั‹ะฒะฐะตั‚ ะฑัƒั„ะตั€.
+
+ะšะพะผะผะธั‚ ั ัั‚ะพะน ะฝะพะฒะพะน ั„ัƒะฝะบั†ะธะตะน ะทะดะตััŒ: cl/9147
+ะกะพะฒะตั‚ัƒัŽ ะตั‘ ะพัะพะฑะตะฝะฝะพ ะฒัะตะผ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปัะผ EXWM!
+
+ะ”ะปั ะผะตะฝั ัั‚ะพ ะฝะฐัั‚ะพัั‰ะตะต ัƒะปัƒั‡ัˆะตะฝะธะต ะถะธะทะฝะธ. ะšะพะฝะตั‡ะฝะพ, ัั‚ะพ ัั‚ั€ะฐะฝะฝะพ ะทะฒัƒั‡ะธั‚, ะฝะพ ะดะฐะถะต ะตัะปะธ ะฑั‹ ัƒ ะผะตะฝั ะฑั‹ะปะฐ ั‚ะฐะบะฐั ะฟั€ะพะฑะปะตะผะฐ ะฒัะตะณะพ ั€ะฐะท ะฒ ะดะตะฝัŒ, ัั‚ะพ ะบะฐะบะธะผ-ั‚ะพ ะพะฑั€ะฐะทะพะผ ะฟั€ะธะฒะตะปะพ ะฑั‹ ะบ ัƒั…ัƒะดัˆะตะฝะธัŽ ะผะพะตะณะพ ะฝะฐัั‚ั€ะพะตะฝะธั. ะšะฐะบ ะผะฐะปะตะฝัŒะบะธะน ะบะฐะผะตัˆะตะบ ะฒ ั‚ะฒะพะตะผ ะฑะพั‚ะธะฝะบะต.
+
+ะ’ั‹ะฝัŒั‚ะต ะบะฐะผะฝะธ ะธะท ัะฒะพะธั… ะฑะพั‚ะธะฝะพะบ!
diff --git a/users/tazjin/blog/posts/reversing-watchguard-vpn.md b/users/tazjin/blog/posts/reversing-watchguard-vpn.md
index 8968dc8645..e000d7a764 100644
--- a/users/tazjin/blog/posts/reversing-watchguard-vpn.md
+++ b/users/tazjin/blog/posts/reversing-watchguard-vpn.md
@@ -1,5 +1,5 @@
 TIP: WatchGuard has
-[responded](https://www.reddit.com/r/netsec/comments/5tg0f9/reverseengineering_watchguard_mobile_vpn/dds6knx/)
+[responded](https://web.archive.org/web/20230326041952/https://www.reddit.com/r/netsec/comments/5tg0f9/reverseengineering_watchguard_mobile_vpn/dds6knx/)
 to this post on Reddit. If you haven\'t read the post yet I\'d recommend
 doing that first before reading the response to have the proper context.
 
diff --git a/users/tazjin/blog/posts/thoughts.md b/users/tazjin/blog/posts/thoughts.md
new file mode 100644
index 0000000000..7ce23f9c87
--- /dev/null
+++ b/users/tazjin/blog/posts/thoughts.md
@@ -0,0 +1,142 @@
+<!--
+
+  This file contains a bunch of random thoughts I don't want to lose,
+  often resulting from conversation with other people, but that are
+  too far removed from what most people can relate to for me to just
+  publish them. Sometimes it's convenient to be able to share them,
+  though.
+
+  For that reason, if you stumble upon this file without me having
+  linked it to you intentionally, feel free to read it but keep the
+  sharing to a minimum (though do feel free to share the thoughts
+  themselves, of course).
+
+-->
+WARNING: This is not intended for a large audience. If you stumble
+upon this page by chance, please keep the sharing to a minimum.
+
+TIP: It's always work-in-progress. Things come and go. Or change. Who
+knows?
+
+---------
+
+### Three things
+
+*[mid/late 2020]*
+
+All things in the universe take the shape of one of approximately
+three things. If you had Hoogle for the entire universe, you'd
+probably find that one of them is `fmap`.
+
+There might be a few more, or a few less (or some may have been
+deprecated), but you get the idea. I guess [five][] would be a good
+number.
+
+[five]: https://principiadiscordia.com/book/23.php
+
+----------------------
+
+### Free energy principle
+
+*[mid/late 2020]*
+
+Karl Friston wrote:
+
+> The free-energy principle says that any self-organizing system that
+> is at equilibrium with its environment must minimize its free
+> energy.
+
+Or, somewhat paraphrased:
+
+> Any Markov blanket capable of modeling its environment aims to
+> reduce its level of surprise by either adapting its model, or
+> through other action.
+
+Seems reasonable to me.
+
+### More bizarre universe
+
+*[many years ago]*
+
+Douglas Adams wrote:
+
+> There is a theory which states that if ever anyone discovers exactly
+> what the Universe is for and why it is here, it will instantly
+> disappear and be replaced by something even more bizarre and
+> inexplicable. There is another theory which states that this has
+> already happened.
+
+### Alpha decay
+
+*[late 2022]*
+
+Finance people say:
+
+> Alpha Decay is commonly referred to as the loss of prediction power
+> of a trading strategy over time. As a consequence, the profitability
+> of a strategy tends to gradually decrease. Given enough time, the
+> strategy converges to having no superior predictive power and
+> returns when compared to a suitable benchmark.
+
+A market is a big optimiser. Any successful trading strategy adds
+friction in a place that the optimiser wants to remove.
+
+Alpha decay is unavoidable without changing and adapting the strategy.
+
+### Optimising universe
+
+*[late 2022]*
+
+*(thanks edef for helping me think through this one!)*
+
+Assume that the universe acts as a giant optimiser, and consider that
+the three things above are related and specialisations of more generic
+ideas:
+
+1. Every delineable entity in the universe (i.e. every *Markov
+   blanket*) attempts to reduce its level of surprise (the free energy
+   principle).
+
+2. The universe needs replacement (a more bizarre universe) if global
+   surprise drops to a minimum[^heat].
+
+3. Without improvement that outpaces the optimiser of the universe,
+   any strategy leading to (2) will get eroded by alpha decay long
+   before.
+
+4. We don't know if it is possible to outpace the optimiser from
+   within.
+
+On a personal note, it seems to me that achieving (2) is likely
+undesirable. It probably takes god[^god] a lot of resources to create
+an ever more complex universe and this process might be much less
+enjoyable than "running" (for lack of a better word) a universe. Under
+this assumption, a universe that achieves (2) faster than others might
+be a failure, and on a higher level conditions leading to its creation
+might be subject to another optimiser.
+
+Or it could be the other way around, but this seems more likely to me
+personally.
+
+### Superintelligence
+
+*[late 2022]*
+
+Under the previous assumption, achieving superintelligence is likely a
+bad idea for anyone feeling some kind of attachment to *this*
+universe.
+
+Or it might be the exact opposite, but I don't think so.
+
+-------------------------------
+
+[^heat]: Note that this is consistent with the heat death of the
+    universe.
+
+[^god]: I'm using the word "god" as the best English approximation of
+    a concept that different religions and philosophies all attempt to
+    approach. I think that for many cognitive purposes, an
+    anthropomorphised idea (as in the abrahamic religions) is useful,
+    but ideas from some Eastern religions or modern philosophers like
+    Bach or Watts are likely more aligned with the "nature of things"
+    as such.
diff --git a/users/tazjin/blog/posts/tvix-eval-talk-2023.md b/users/tazjin/blog/posts/tvix-eval-talk-2023.md
new file mode 100644
index 0000000000..4a0ec56881
--- /dev/null
+++ b/users/tazjin/blog/posts/tvix-eval-talk-2023.md
@@ -0,0 +1,19 @@
+7 ัะตะฝั‚ัะฑั€ั ั ะฒั‹ัั‚ัƒะฟะธะป ั ะดะพะบะปะฐะดะพะผ ะฟั€ะพ ั€ะตะฐะปะธะทะฐั†ะธัŽ ัะทั‹ะบะฐ Nix ะฝะฐ Rust, ะฝะฐ
+[ะœะพัะบะพะฒัะบะพะผ Rust-ะผะธั‚ะฐะฟะต][rustmsk] / [ะœะพัะบะพะฒัะบะพะผ ะบะปัƒะฑะต
+ะฟั€ะพะณั€ะฐะผะผะธัั‚ะพะฒ][progmsk].
+
+<iframe width="800" height="500" src="https://www.youtube.com/embed/7zS2_ZhwPfY?start=4013" title="RUST - ัะพะฒั€ะตะผะตะฝะฝั‹ะน ัะทั‹ะบ ะฟั€ะพะณั€ะฐะผะผะธั€ะพะฒะฐะฝะธั" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
+
+ะ’ะพั‚ ะฒัะต ัะฒัะทะฐะฝะฝั‹ะต ั ะฝะธะผ ััั‹ะปะบะธ, ะบะพั‚ะพั€ั‹ะต ะผะพะณัƒั‚ ะฑั‹ั‚ัŒ ะธะฝั‚ะตั€ะตัะฝั‹:
+
+* [Tvix](https://tvix.dev), ะณะปะฐะฒะฝั‹ะน ัะฐะนั‚ ะฟั€ะพะตะบั‚ะฐ
+* [TVL](https://tvl.fyi), ะฝะฐัˆะต ะพะฝะปะฐะนะฝ-ัะพะพะฑั‰ะตัั‚ะฒะพ
+* [Tvixbolt](https://bolt.tvix.dev/), ะฝะฐัˆ "godbolt" ะดะปั tvix
+* [MMTk](https://www.mmtk.io/), Rust-ะฑะธะฑะปะธะพั‚ะตะบะฐ ั ะบะพะผะฟะพะฝะตะฝั‚ะฐะผะธ ะดะปั garbage-collection
+* [ะ˜ะฝั‚ะตั€ะฒัŒัŽ / ะดะพะบะปะฐะด](https://www.youtube.com/live/0Lhahzs-Wos?si=BlFDVBUPsIpHg0p5), Nix -- ะฝะต ั‚ะพะปัŒะบะพ ะฟะฐะบะตั‚ะฝั‹ะน ะผะตะฝะตะดะถะตั€
+* [NixCon 2023](https://2023.nixcon.org/)
+* [Yew](https://yew.rs/), WASM-ั„ั€ะตะนะผะฒะพั€ะบ ะดะปั Rust
+* [tazlog](https://t.me/tazlog), ะผะพะน ะบะฐะฝะฐะป ะฝะฐ ะขะตะปะตะณะต
+
+[rustmsk]: https://t.me/ruRust_msk
+[progmsk]: https://prog.msk.ru/
diff --git a/users/tazjin/chase-geese/default.nix b/users/tazjin/chase-geese/default.nix
new file mode 100644
index 0000000000..3549f75868
--- /dev/null
+++ b/users/tazjin/chase-geese/default.nix
@@ -0,0 +1,13 @@
+# Helpers for mounting GeeseFS into the right place.
+{ depot, pkgs, ... }:
+
+pkgs.writeShellScriptBin "chase-geese" ''
+  set -ueo pipefail
+
+  echo "Fetching credentials ..."
+  eval $(pass show keys/tazjin-geesefs)
+
+  echo "Mounting the cloud ..."
+  mkdir -p ~/cloud
+  ${depot.third_party.geesefs}/bin/geesefs tazjins-files ~/cloud
+''
diff --git a/users/tazjin/covid/us_mortality.jq b/users/tazjin/covid/us_mortality.jq
deleted file mode 100644
index 584be3ef9a..0000000000
--- a/users/tazjin/covid/us_mortality.jq
+++ /dev/null
@@ -1,36 +0,0 @@
-# This turns the CDC mortality data[0] into a format useful for my
-# excess mortality spreadsheet. The US format is by far the worst one
-# I have dealt with, as expected.
-#
-# This requires miller for transforming the CSV appropriately.
-#
-# Params:
-#  state: abbreviation of the state to extract ('US' for whole country)
-#  period: time period (either "2020" for current data, or anything else
-#          for historical averages)
-#
-# Call as:
-#  mlr --icsv --ojson cat weekly.csv | \
-#    jq -rsf us_mortality.jq --arg state US --arg period 2020
-#
-# [0]: https://www.cdc.gov/nchs/nvss/vsrr/covid19/excess_deaths.htm
-
-def filter_period(period):
-  if period == "2020"
-  then . | map(select(.["Time Period"] == 2020))
-  else . | map(select(.["Time Period"] == "2015-2019"))
-  end;
-
-def collate_weeks(period):
-  (. | map(.["Number of Deaths"]) | add) as $count
-  | {
-    count: (if period == "2020" then $count else $count / 5 end),
-    week: .[0].Week,
-  };
-
-. | map(select(.Type == "Predicted (weighted)"))
-  | map(select(.["State Abbreviation"] == $state))
-  | filter_period($period)
-  | group_by(.Week)
-  | map(collate_weeks($period))
-  | .[] | "week \(.week): \(.count)"
diff --git a/users/tazjin/default.nix b/users/tazjin/default.nix
new file mode 100644
index 0000000000..1b68b7127a
--- /dev/null
+++ b/users/tazjin/default.nix
@@ -0,0 +1,30 @@
+# //users/tazjin-specific CI configuration.
+{ depot, pkgs, ... }:
+
+let
+  rustfmt = pkgs.writeShellScript "rustfmt-tazjin" ''
+    ${pkgs.fd}/bin/fd -e rs | \
+      ${pkgs.ripgrep}/bin/rg 'users/tazjin' | \
+      xargs ${pkgs.rustfmt}/bin/rustfmt --check --config-path users/tazjin
+  '';
+
+in
+depot.nix.readTree.drvTargets {
+  rustfmt = rustfmt.overrideAttrs (_: {
+    # rustfmt not respecting config atm, disable
+    meta.ci.skip = true;
+
+    meta.ci.extraSteps.rustfmt = {
+      command = rustfmt;
+    };
+  });
+
+  # Use a screen lock command that resets the keyboard layout
+  # before locking, to avoid locking me out when the layout is
+  # in Russian.
+  screenLock = pkgs.writeShellScriptBin "tazjin-screen-lock" ''
+    ${pkgs.xorg.setxkbmap}/bin/setxkbmap us
+    ${pkgs.xorg.setxkbmap}/bin/setxkbmap -option caps:super
+    exec ${pkgs.xsecurelock}/bin/xsecurelock
+  '';
+}
diff --git a/users/tazjin/dns/default.nix b/users/tazjin/dns/default.nix
index 14b9d42864..6ff6cc06e2 100644
--- a/users/tazjin/dns/default.nix
+++ b/users/tazjin/dns/default.nix
@@ -2,13 +2,12 @@
 { depot, pkgs, ... }:
 
 let
-  inherit (depot.nix.utils) drvTargets;
-
-  checkZone = zone: file: pkgs.runCommandNoCC "${zone}-check" {} ''
+  checkZone = zone: file: pkgs.runCommand "${zone}-check" { } ''
     ${pkgs.bind}/bin/named-checkzone -i local ${zone} ${file} | tee $out
   '';
 
-in drvTargets {
-  kontemplate-works = checkZone "kontemplate.works"./kontemplate.works.zone;
+in
+depot.nix.readTree.drvTargets {
+  kontemplate-works = checkZone "kontemplate.works" ./kontemplate.works.zone;
   tazj-in = checkZone "tazj.in" ./tazj.in.zone;
 }
diff --git a/users/tazjin/docs/install-zfs.md b/users/tazjin/docs/install-zfs.md
new file mode 100644
index 0000000000..415af30fd4
--- /dev/null
+++ b/users/tazjin/docs/install-zfs.md
@@ -0,0 +1,116 @@
+Current steps for my NixOS-on-ZFS installs with impermanence.
+
+## Target layout (example from tverskoy):
+
+Partitioning:
+
+```
+nvme0n1     259:0    0 238.5G  0 disk
+โ”œโ”€nvme0n1p1 259:1    0   128M  0 part /boot (type: EFI system)
+โ””โ”€nvme0n1p2 259:2    0 238.3G  0 part       (type: Solaris root)
+```
+
+ZFS layout:
+
+```
+NAME                   USED  AVAIL     REFER  MOUNTPOINT
+zpool                  212G  19.0G      248K  /zpool
+zpool/ephemeral        668M  19.0G      192K  /zpool/ephemeral
+zpool/ephemeral/home   667M  19.0G      667M  legacy
+zpool/local           71.3G  19.0G      192K  /zpool/local
+zpool/local/nix       71.3G  19.0G     71.3G  legacy
+zpool/safe             140G  19.0G      192K  /zpool/safe
+zpool/safe/depot       414M  19.0G      414M  legacy
+zpool/safe/persist     139G  19.0G      139G  legacy
+```
+
+With reset-snapshots:
+
+```
+NAME                                USED  AVAIL     REFER  MOUNTPOINT
+zpool/ephemeral/home@blank          144K      -      192K  -
+zpool/ephemeral/home@tazjin-clean   144K      -      200K  -
+```
+
+Legacy mountpoints are used because the NixOS wiki advises that using
+ZFS own mountpoints might lead to issues with the mount order during
+boot.
+
+## Install steps
+
+1. First, get internet.
+
+2. Use `fdisk` to set up the partition layout above (fwiw, EFI type
+   should be `1`, Solaris root should be `66`).
+
+3. Format the first partition for EFI: `mkfs.fat -F32 -n EFI $part1`
+
+4. Init ZFS stuff:
+
+   ```
+   zpool create \
+     # 2 SSD only settings
+     -o ashift=12 \
+     -o autotrim=on \
+     -R /mnt \
+     -O canmount=off \
+     -O mountpoint=none \
+     -O acltype=posixacl \
+     -O compression=lz4 \
+     -O atime=off \
+     -O xattr=sa \
+     -O encryption=aes-256-gcm \
+     -O keylocation=prompt \
+     -O keyformat=passphrase \
+     zpool $part2
+   ```
+
+   Reserve some space for deletions:
+
+   ```
+   zfs create -o refreservation=1G -o mountpoint=none zpool/reserved
+   ```
+
+   Create the datasets as per the target layout:
+
+   ```
+   # Throwaway datasets
+   zfs create -o canmount=off -o mountpoint=none zpool/ephemeral
+   zfs create -o mountpoint=legacy zpool/ephemeral/root
+   zfs create -o mountpoint=legacy zpool/ephemeral/home
+
+   # Persistent datasets
+   zfs create -o canmount=off -o mountpoint=none zpool/persistent
+   zfs create -o mountpoint=legacy zpool/persistent/nix
+   zfs create -o mountpoint=legacy zpool/persistent/depot
+   zfs create -o mountpoint=legacy zpool/persistent/data
+   ```
+
+   Create completely blank snapshots of the ephemeral datasets:
+
+   ```
+   zfs snapshot zpool/ephemeral/root@blank
+   zfs snapshot zpool/ephemeral/home@blank
+   ```
+
+   The ephemeral home volume needs the user folder already set up with
+   permissions. Mount it and create the folder there:
+
+   ```
+   mount -t zfs zpool/ephemeral/root /mnt
+   mkdir /mnt/home
+   mount -t zfs zpool/ephemeral/home /mnt/home
+   mkdir /mnt/home/tazjin
+   chmod 1000:100 /mnt/home/tazjin
+   zfs snapshot zpool/ephemeral/home@tazjin-clean
+   ```
+
+   Now the persistent Nix store volume can be mounted and installation
+   can begin.
+
+   ```
+   mkdir /mnt/nix
+   mount -t zfs zpool/persistent/nix /mnt/nix
+   ```
+
+4. Configure & install NixOS as usual.
diff --git a/users/tazjin/dotfiles/default.nix b/users/tazjin/dotfiles/default.nix
new file mode 100644
index 0000000000..9b783a9c85
--- /dev/null
+++ b/users/tazjin/dotfiles/default.nix
@@ -0,0 +1,3 @@
+_: {
+  dunstrc = ./dunstrc;
+}
diff --git a/users/tazjin/dotfiles/dunstrc b/users/tazjin/dotfiles/dunstrc
new file mode 100644
index 0000000000..2aa1141b6e
--- /dev/null
+++ b/users/tazjin/dotfiles/dunstrc
@@ -0,0 +1,54 @@
+[global]
+font = Iosevka Term 11
+origin = top-left
+markup = yes
+plain_text = no
+format = "<b>%s</b>\n%b"
+sort = no
+indicate_hidden = yes
+alignment = center
+bounce_freq = 0
+show_age_threshold = -1
+word_wrap = yes
+ignore_newline = no
+stack_duplicates = yes
+hide_duplicate_count = yes
+geometry = "300x50-15+49"
+shrink = no
+transparency = 5
+idle_threshold = 0
+monitor = 0
+follow = keyboard
+sticky_history = yes
+history_length = 15
+show_indicators = no
+line_height = 3
+separator_height = 2
+padding = 6
+horizontal_padding = 6
+separator_color = frame
+startup_notification = false
+dmenu = /usr/bin/dmenu -p dunst:
+browser = /usr/bin/firefox -new-tab
+icon_position = off
+max_icon_size = 80
+frame_width = 3
+frame_color = "#8EC07C"
+
+[urgency_low]
+frame_color = "#3B7C87"
+foreground = "#3B7C87"
+background = "#191311"
+timeout = 4
+
+[urgency_normal]
+frame_color = "#5B8234"
+foreground = "#5B8234"
+background = "#191311"
+timeout = 6
+
+[urgency_critical]
+frame_color = "#B7472A"
+foreground = "#B7472A"
+background = "#191311"
+timeout = 8
diff --git a/users/tazjin/dt/CMakeLists.txt b/users/tazjin/dt/CMakeLists.txt
deleted file mode 100644
index 85b659fea8..0000000000
--- a/users/tazjin/dt/CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- mode: cmake; -*-
-cmake_minimum_required(VERSION 3.16)
-project(dt)
-add_executable(dt dt.cc)
-find_package(absl REQUIRED)
-
-target_link_libraries(dt
-  absl::flags
-  absl::flags_parse
-  absl::hash
-  absl::time
-  absl::strings
-  farmhash
-)
-
-install(TARGETS dt DESTINATION bin)
diff --git a/users/tazjin/dt/README.md b/users/tazjin/dt/README.md
deleted file mode 100644
index ee43d56064..0000000000
--- a/users/tazjin/dt/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-dt
-==
-
-It's got a purpose.
-
-## Usage:
-
-```
-nix-build -E '(import (builtins.fetchGit "https://git.tazj.in/") {}).fun.dt'
-./result/bin/dt --one ... --two ...
-```
diff --git a/users/tazjin/dt/default.nix b/users/tazjin/dt/default.nix
deleted file mode 100644
index 8a728062db..0000000000
--- a/users/tazjin/dt/default.nix
+++ /dev/null
@@ -1,13 +0,0 @@
-{ depot, pkgs, ... }:
-
-let stdenv = with pkgs; overrideCC clangStdenv clang_11;
-in stdenv.mkDerivation {
-  name = "dt";
-  src = ./.;
-  nativeBuildInputs = [ pkgs.cmake ];
-  buildInputs = with depot.third_party; [
-    abseil_cpp
-    farmhash
-  ];
-  meta.ci = false;
-}
diff --git a/users/tazjin/dt/dt.cc b/users/tazjin/dt/dt.cc
deleted file mode 100644
index 5c4c3da768..0000000000
--- a/users/tazjin/dt/dt.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-#include <iostream>
-#include <vector>
-
-#include "absl/flags/flag.h"
-#include "absl/flags/parse.h"
-#include "absl/hash/hash.h"
-#include "absl/strings/str_cat.h"
-#include "absl/time/clock.h"
-#include "absl/time/time.h"
-#include "absl/types/optional.h"
-#include "farmhash.h"
-
-ABSL_FLAG(std::vector<std::string>, words, {}, "words to use");
-
-struct Result {
-  std::string a;
-  int ec;
-  absl::optional<std::string> p;
-};
-
-std::string which(const std::vector<std::string>& words) {
-  uint64_t fp;
-  std::string word;
-
-  for (const auto& w : words) {
-    auto nfp = util::Fingerprint64(w);
-    if (nfp > fp) {
-      fp = nfp;
-      word = w;
-    }
-  }
-
-  return word;
-}
-
-Result decide(const std::vector<std::string>& words) {
-  auto input = absl::FormatTime("%Y%m%d", absl::Now(), absl::UTCTimeZone());
-  for (const auto& w : words) {
-    input += w;
-  }
-
-  auto base = util::Fingerprint64(input);
-  Result result = { "nope" };
-
-  if (base % 10 == 0) {
-    result.a = "ca";
-  } else if (base % 8 == 0) {
-    result.a = "c1";
-    result.p = which(words);
-  } else if (base % 6 == 0) {
-    result.a = "skip";
-  } else if (base % 3 == 0) {
-    result.a = "e1";
-    result.ec = base % 10;
-    result.p = which(words);
-  } else if (base % 2 == 0) {
-    result.a = "ea";
-    result.ec = base % 10;
-  }
-
-  return result;
-}
-
-int main(int argc, char *argv[]) {
-  absl::ParseCommandLine(argc, argv);
-
-  auto words = absl::GetFlag(FLAGS_words);
-  if (words.size() < 2) {
-    std::cerr << "needs at least two!" << std::endl;
-    return 1;
-  }
-
-  auto result = decide(words);
-  std::cout << result.a
-            << (result.p.has_value() ? absl::StrCat(" ", "(", result.p.value(), ")")
-                                     : "")
-            << (result.ec > 0 ? absl::StrCat(": ", result.ec) : "")
-            << std::endl;
-}
diff --git a/users/tazjin/elisp-deps/deps.el b/users/tazjin/elisp-deps/deps.el
new file mode 100644
index 0000000000..954d71cfba
--- /dev/null
+++ b/users/tazjin/elisp-deps/deps.el
@@ -0,0 +1,83 @@
+;; Visualise the internal structure of an Emacs Lisp file using
+;; Graphviz.
+;;
+;; Entry point is the function `edeps-analyse-file'.
+
+(require 'map)
+
+(defun edeps-read-defs (file-name)
+  "Stupidly read all definitions from an Emacs Lisp file. This only
+considers top-level forms, where the first element of the form is
+a symbol whose name contains the string `def', and where the
+second element is a symbol.
+
+Returns a hashmap of all these symbols with the remaining forms
+in their bodies."
+
+  (with-temp-buffer
+    (insert-file-contents file-name)
+    (goto-char (point-min))
+
+    (let ((symbols (make-hash-table)))
+      (condition-case _err
+          (while t
+            (let ((form (read (current-buffer))))
+              (when (and (listp form)
+                         (symbolp (car form))
+                         (string-match "def" (symbol-name (car form)))
+                         (symbolp (cadr form)))
+                (when (and (map-contains-key symbols (cadr form))
+                           ;; generic methods have multiple definitions
+                           (not (eq (car form) 'cl-defmethod)))
+                  (error "Duplicate symbol: %s" (symbol-name (cadr form))))
+
+                (map-put! symbols (cadr form)
+                          (cons (car form) (cddr form))))))
+        (end-of-file symbols)))))
+
+(defun edeps-analyse-structure (symbols)
+  "Analyse the internal structure of the symbols found by
+edeps-read-defs, and return a hashmap with the results of the
+analysis. The hashmap uses the symbols as keys, "
+  (let ((deps (make-hash-table)))
+    (map-do
+     (lambda (sym val)
+       (dolist (expr (flatten-list (cdr val)))
+         (when (map-contains-key symbols expr)
+           (map-put! deps expr (cons sym (ht-get deps expr))))))
+     symbols)
+    deps))
+
+(defun edeps-graph-deps (symbols deps)
+  (with-temp-buffer
+    (insert "digraph edeps {\n")
+
+    ;; List all symbols first
+    (insert "  subgraph {\n")
+    (map-do
+     (lambda (sym val)
+       (insert "    " (format "\"%s\" [label=\"%s\\n(%s)\"];\n" sym sym (car val))))
+     symbols)
+    (insert "  }\n\n")
+
+    ;; Then drop all the edges in there ..
+    (insert "  subgraph {\n")
+    (map-do
+     (lambda (sym deps)
+       (dolist (dep deps)
+         (insert "    " (format "\"%s\" -> \"%s\";\n" dep sym))))
+     deps)
+    (insert "  }\n")
+
+    (insert "}\n")
+    (buffer-string)))
+
+(defun edeps-analyse-file (infile outfile)
+  "Produces a dot-graph in OUTFILE from an internal structural
+analysis of INFILE. This can be graphed using the graphviz
+package."
+  (let* ((symbols (edeps-read-defs infile))
+         (deps (edeps-analyse-structure symbols)))
+    (with-temp-buffer
+      (insert (edeps-graph-deps symbols deps))
+      (write-file outfile))))
diff --git a/users/tazjin/emacs/config/bindings.el b/users/tazjin/emacs/config/bindings.el
index 029aa2d0e5..d8b63e33e4 100644
--- a/users/tazjin/emacs/config/bindings.el
+++ b/users/tazjin/emacs/config/bindings.el
@@ -1,11 +1,11 @@
+;; Switch buffers reliably in the face of spurious renames.
+(global-set-key (kbd "C-x b") #'reliably-switch-buffer)
+
 ;; Font size
 (define-key global-map (kbd "C-=") 'increase-default-text-scale) ;; '=' because there lies '+'
 (define-key global-map (kbd "C--") 'decrease-default-text-scale)
 (define-key global-map (kbd "C-x C-0") 'set-default-text-scale)
 
-;; What does <tab> do? Well, it depends ...
-(define-key prog-mode-map (kbd "<tab>") #'company-indent-or-complete-common)
-
 ;; imenu instead of insert-file
 (global-set-key (kbd "C-x i") 'imenu)
 
@@ -15,7 +15,6 @@
 ;; Start eshell or switch to it if it's active.
 (global-set-key (kbd "C-x m") 'eshell)
 
-(global-set-key (kbd "C-x C-p") 'browse-repositories)
 (global-set-key (kbd "M-g M-g") 'goto-line-with-feedback)
 
 ;; Miscellaneous editing commands
@@ -23,7 +22,7 @@
 (global-set-key (kbd "C-c a") 'align-regexp)
 (global-set-key (kbd "C-c m") 'mc/mark-dwim)
 
-;; Browse URLs (very useful for Gitlab's SSH output!)
+;; Browse URLs (very useful for Gerrit's push output, etc!)
 (global-set-key (kbd "C-c b p") 'browse-url-at-point)
 (global-set-key (kbd "C-c b b") 'browse-url)
 
@@ -34,17 +33,17 @@
 ;; Open a file in project:
 (global-set-key (kbd "C-c f") 'project-find-file)
 
-;; Search in a project
-(global-set-key (kbd "C-c r g") 'rg-in-project)
-
 ;; Open a file via magit:
 (global-set-key (kbd "C-c C-f") #'magit-find-file-worktree)
 
 ;; Insert TODO comments
 (global-set-key (kbd "C-c t") 'insert-todo-comment)
 
-;; Make sharing music easier
-(global-set-key (kbd "s-s w") #'songwhip-lookup-url)
+;; Open the depot
+(global-set-key (kbd "s-s d") #'tvl-depot-status)
+
+;; Open any project through zoxide
+(global-set-key (kbd "s-s r") #'zoxide-open-project)
 
 ;; Add subthread collapsing to notmuch-show.
 ;;
@@ -56,4 +55,50 @@
     (interactive)
     (notmuch-show-open-or-close-subthread t))) ;; open
 
+;; Get rid of the annoying `save-some-buffers' shortcut which I
+;; *NEVER* use intentionally.
+(unbind-key (kbd "C-x s") 'global-map)
+
+;; German keyboard layout with Y and Z in the correct place.
+
+(quail-define-package
+ "german-qwerty" "German" "DE@" t
+ "German (Deutsch) input method with QWERTY keys"
+ nil t t t t nil nil nil nil nil t)
+
+;; 1!  2"  3ยง  4$  5%  6&  7/  8(  9)  0=  รŸ?  [{  ]}
+;;  qQ  wW  eE  rR  tT  yY  uU  iI  oO  pP  รผรœ  +*
+;;   aA  sS  dD  fF  gG  hH  jJ  kK  lL  รถร–  รคร„  #^
+;;    zZ  xX  cC  vV  bB  nN  mM  ,;  .:  -_
+
+(quail-define-rules
+ ("-" ?รŸ)
+ ("=" ?\[)
+ ("`" ?\])
+ ("[" ?รผ)
+ ("]" ?+)
+ (";" ?รถ)
+ ("'" ?รค)
+ ("\\" ?#)
+ ("/" ?-)
+
+ ("@" ?\")
+ ("#" ?ยง)
+ ("^" ?&)
+ ("&" ?/)
+ ("*" ?\()
+ ("(" ?\))
+ (")" ?=)
+ ("_" ??)
+ ("+" ?{)
+ ("~" ?})
+ ("{" ?รœ)
+ ("}" ?*)
+ (":" ?ร–)
+ ("\"" ?ร„)
+ ("|" ?^)
+ ("<" ?\;)
+ (">" ?:)
+ ("?" ?_))
+
 (provide 'bindings)
diff --git a/users/tazjin/emacs/config/custom.el b/users/tazjin/emacs/config/custom.el
index 2bb7ad4896..3e9a9dcd06 100644
--- a/users/tazjin/emacs/config/custom.el
+++ b/users/tazjin/emacs/config/custom.el
@@ -7,9 +7,9 @@
  '(ac-delay 0.2)
  '(avy-background t)
  '(cargo-process--enable-rust-backtrace 1)
- '(company-auto-complete (quote (quote company-explicit-action-p)))
- '(company-idle-delay 0.5)
- '(custom-enabled-themes (quote (gruber-darker)))
+ '(custom-safe-themes
+   (quote
+    ("d61fc0e6409f0c2a22e97162d7d151dee9e192a90fa623f8d6a071dbf49229c6" "3c83b3676d796422704082049fc38b6966bcad960f896669dfc21a7a37a748fa" "89336ca71dae5068c165d932418a368a394848c3b8881b2f96807405d8c6b5b6" default)))
  '(display-time-default-load-average nil)
  '(display-time-interval 30)
  '(elnode-send-file-program "/run/current-system/sw/bin/cat")
diff --git a/users/tazjin/emacs/config/desktop.el b/users/tazjin/emacs/config/desktop.el
index 43faede47b..aa232fec2f 100644
--- a/users/tazjin/emacs/config/desktop.el
+++ b/users/tazjin/emacs/config/desktop.el
@@ -4,13 +4,15 @@
 ;; window-management (EXWM) as well as additional system-wide
 ;; commands.
 
-(require 's)
-(require 'f)
-(require 'dash)
 (require 'exwm)
 (require 'exwm-config)
 (require 'exwm-randr)
 (require 'exwm-systemtray)
+(require 'exwm-xim )
+(require 'f)
+(require 'ring)
+(require 's)
+(require 'seq)
 
 (defcustom tazjin--screen-lock-command "tazjin-screen-lock"
   "Command to execute for locking the screen."
@@ -52,6 +54,7 @@
 (defun lock-screen ()
   (interactive)
   (set-xkb-layout "us")
+  (deactivate-input-method)
   (shell-command tazjin--screen-lock-command))
 
 (defun create-window-name ()
@@ -67,36 +70,17 @@
   human-accessible titles."
 
   (pcase (list (or exwm-class-name "unknown") (or exwm-title "unknown"))
-    ;; In Cider windows, rename the class and keep the workspace/file
-    ;; as the title.
-    (`("Google-chrome" ,(and (pred (lambda (title) (s-ends-with? " - Cider" title))) title))
-     (format "Cider<%s>" (s-chop-suffix " - Cider" title)))
-    (`("Google-chrome" ,(and (pred (lambda (title) (s-ends-with? " - Cider V" title))) title))
-     (format "Cider V<%s>" (s-chop-suffix " - Cider V" title)))
-
-    ;; Attempt to detect IRCCloud windows via their title, which is a
-    ;; combination of the channel name and network.
-    ;;
-    ;; This is what would often be referred to as a "hack". The regexp
-    ;; will not work if a network connection buffer is selected in
-    ;; IRCCloud, but since the title contains no other indication that
-    ;; we're dealing with an IRCCloud window
-    (`("Google-chrome"
-       ,(and (pred (lambda (title)
-                     (s-matches? "^[\*\+]\s#[a-zA-Z0-9/\-]+\s\|\s[a-zA-Z\.]+$" title)))
-             title))
-     (format "IRCCloud<%s>" title))
-
-    ;; For other Chrome windows, make the title shorter.
-    (`("Google-chrome" ,title)
-     (format "Chrome<%s>" (s-truncate 42 (s-chop-suffix " - Google Chrome" title))))
-
-    ;; Gnome-terminal -> Term
-    (`("Gnome-terminal" ,title)
-     ;; fish-shell buffers contain some unnecessary whitespace and
-     ;; such before the current working directory. This can be
-     ;; stripped since most of my terminals are fish shells anyways.
-     (format "Term<%s>" (s-trim-left (s-chop-prefix "fish" title))))
+    ;; Yandex.Music -> `ะฏ.Music<... stuff ...>'
+    (`("Chromium-browser" ,(and (pred (lambda (title) (s-starts-with? "Yandex.Music - " title))) title))
+     (format "ะฏ.Music<%s>" (s-chop-prefix "Yandex.Music - " title)))
+
+    ;; For other Chromium windows, make the title shorter.
+    (`("Chromium-browser" ,title)
+     (format "Chromium<%s>" (s-truncate 42 (s-chop-suffix " - Chromium" title))))
+
+    ;; similarly for Firefox
+    (`("firefox" ,title)
+     (format "FF<%s>" title))
 
     ;; Quassel buffers
     ;;
@@ -118,9 +102,6 @@
     (`(,class ,title) (format "%s<%s>" class (s-truncate 12 title)))))
 
 ;; EXWM launch configuration
-;;
-;; This used to use use-package, but when something breaks use-package
-;; it doesn't exactly make debugging any easier.
 
 (let ((titlef (lambda ()
                 (exwm-workspace-rename-buffer (create-window-name)))))
@@ -128,23 +109,57 @@
   (add-hook 'exwm-update-title-hook titlef))
 
 (fringe-mode 3)
+
+;; tab-bar related config
+(setq tab-bar-show 1)
+(setq tab-bar-tab-hints t)
+
+(setq tab-bar-format
+      '(tab-bar-format-history
+        tab-bar-format-tabs tab-bar-separator
+        tab-bar-format-align-right tab-bar-format-global))
+
+(setq tab-bar-new-tab-choice
+      (lambda () (get-buffer-create "*scratch*")))
+
+(tab-bar-mode 1)
+
+(setq x-no-window-manager t) ;; TODO(tazjin): figure out when to remove this
 (exwm-enable)
+(exwm-randr-enable)
 
-;; 's-N': Switch to certain workspace
-(setq exwm-workspace-number 10)
-(dotimes (i 10)
-  (exwm-input-set-key (kbd (format "s-%d" i))
-                      `(lambda ()
-                         (interactive)
-                         (exwm-workspace-switch-create ,i))))
+;; Tab-management shortcuts
+
+(defun tab-bar-select-or-return ()
+  "This function behaves like `tab-bar-select-tab', except it calls
+`tab-recent' if asked to jump to the current tab. This simulates
+the back&forth behaviour of i3."
+  (interactive)
+  (let* ((key (event-basic-type last-command-event))
+         (tab (if (and (characterp key) (>= key ?1) (<= key ?9))
+                  (- key ?0)
+                0))
+         (current (1+ (tab-bar--current-tab-index))))
+    (if (eq tab current)
+        (tab-recent)
+      (tab-bar-select-tab tab))))
+
+(dotimes (i 8)
+  (exwm-input-set-key (kbd (format "s-%d" (+ 1 i))) #'tab-bar-select-or-return))
+
+(exwm-input-set-key (kbd "s-9") #'tab-last)
+(exwm-input-set-key (kbd "s-f") #'tab-next)
+(exwm-input-set-key (kbd "s-b") #'tab-recent)
+(exwm-input-set-key (kbd "s-w") #'tab-close)
+(exwm-input-set-key (kbd "s-n") #'tab-new)
 
 ;; Launch applications / any command with completion (dmenu style!)
-(exwm-input-set-key (kbd "s-d") #'counsel-linux-app)
+(exwm-input-set-key (kbd "s-d") #'run-xdg-app)
 (exwm-input-set-key (kbd "s-x") #'run-external-command)
 (exwm-input-set-key (kbd "s-p") #'password-store-lookup)
 
-;; Add X11 terminal selector to a key
-(exwm-input-set-key (kbd "C-x t") #'ts/switch-to-terminal)
+;; Add vterm selector to a key
+(exwm-input-set-key (kbd "s-v") #'ts/switch-to-terminal)
 
 ;; Toggle between line-mode / char-mode
 (exwm-input-set-key (kbd "C-c C-t C-t") #'exwm-input-toggle-keyboard)
@@ -171,19 +186,19 @@
 (bind-xkb "no" "k n")
 (bind-xkb "ru" "k r")
 (bind-xkb "se" "k s")
-
-;; These are commented out because Emacs no longer starts (??) if
-;; they're set at launch.
-;;
 (bind-xkb "us" "ะป ะณ")
 (bind-xkb "de" "ะป ะฒ")
 (bind-xkb "no" "ะป ั‚")
 (bind-xkb "ru" "ะป ะบ")
 
+;; Configuration of EXWM input method handling for X applications
+(exwm-xim-enable)
+(setq default-input-method "russian-computer")
+(push ?\C-\\ exwm-input-prefix-keys)
+
 ;; Line-editing shortcuts
-(exwm-input-set-simulation-keys
- '(([?\C-d] . delete)
-   ([?\C-w] . ?\C-c)))
+(exwm-input-set-simulation-key (kbd "C-d") (kbd "DEL"))
+(exwm-input-set-simulation-key (kbd "C-w") (kbd "C-c"))
 
 ;; Show time & battery status in the mode line
 (display-time-mode)
@@ -192,101 +207,123 @@
 ;; enable display of X11 system tray within Emacs
 (exwm-systemtray-enable)
 
-;; Configure xrandr (multi-monitor setup).
+;; Multi-monitor configuration.
 ;;
-;; This makes some assumptions about how my machines are connected to
-;; my home setup during the COVID19 isolation period.
-
-(defun set-randr-config (screens)
-  (setq exwm-randr-workspace-monitor-plist
-        (-flatten (-map (lambda (screen)
-                          (-map (lambda (screen-id) (list screen-id (car screen))) (cdr screen)))
-                        screens))))
-
-;; Layouts for Vauxhall (laptop)
+;; With tab-bar-mode, each monitor only displays at most one
+;; workspace. Workspaces are only created, never deleted, meaning that
+;; the number of workspaces will be equivalent to the maximum number
+;; of displays that were connected during a session.
+;;
+;; The first workspace is special: It is kept on the primary monitor.
 
-(defun randr-vauxhall-layout-single ()
-  "Laptop screen only!"
-  (interactive)
-  (set-randr-config '(("eDP1" (number-sequence 0 9))))
-  (shell-command "xrandr --output eDP1 --auto --primary")
-  (shell-command "xrandr --output HDMI1 --off")
-  (shell-command "xrandr --output DP2 --off")
-  (exwm-randr-refresh))
-
-(defun randr-vauxhall-layout-all ()
-  "Use all screens at home."
+(defun exwm-assign-workspaces ()
+  "Assigns workspaces to the currently existing monitors, putting
+the first one on the primary display and allocating the others
+dynamically if needed in no particular order."
   (interactive)
-  (set-randr-config
-   '(("eDP1" 0)
-     ("HDMI1" 1 2 3 4 5)
-     ("DP2" 6 7 8 9)))
-
-  (shell-command "xrandr --output HDMI1 --right-of eDP1 --auto --primary")
-  (shell-command "xrandr --output DP2 --right-of HDMI1 --auto --rotate left")
-  (exwm-randr-refresh))
-
-(defun randr-vauxhall-layout-wide-only ()
-  "Use only the wide screen at home."
+  (let* ((randr-monitors (exwm-randr--get-monitors))
+         (primary (car randr-monitors))
+         (all-monitors (seq-map #'car (cadr randr-monitors)))
+         (sorted-primary-first (seq-sort (lambda (a b)
+                                           (or (equal a primary)
+                                               (< a b)))
+                                         all-monitors))
+         ;; assign workspace numbers to each monitor ...
+         (workspace-assignments
+          (flatten-list (seq-map-indexed (lambda (monitor idx)
+                                           (list idx monitor))
+                                         sorted-primary-first))))
+    ;; ensure that the required workspaces exist
+    (exwm-workspace-switch-create (- (seq-length all-monitors) 1))
+
+    ;; update randr config
+    (setq exwm-randr-workspace-monitor-plist workspace-assignments)
+    (exwm-randr-refresh)
+
+    ;; leave focus on primary workspace
+    (exwm-workspace-switch 0)))
+
+(defun list-available-monitors ()
+  "List connected, but unused monitors."
+  (let* ((all-connected
+          (seq-map (lambda (line) (car (s-split " " line)))
+                   (s-lines (s-trim (shell-command-to-string "xrandr | grep connected | grep -v disconnected")))))
+         (all-active (seq-map #'car (cadr (exwm-randr--get-monitors)))))
+    (seq-filter (lambda (s) (not (seq-contains-p all-active s)))
+                all-connected)))
+
+(defun exwm-enable-monitor ()
+  "Interactively construct an EXWM invocation that enable the
+given monitor and assigns a workspace to it."
   (interactive)
-  (set-randr-config
-   '(("eDP1" 8 9 0)
-     ("HDMI1" 1 2 3 4 5 6 7)))
 
-  (shell-command "xrandr --output DP2 --off")
-  (shell-command "xrandr --output HDMI1 --right-of eDP1 --auto --primary")
-  (exwm-randr-refresh))
-
-(defun randr-tverskoy-extend-tv ()
-  "Move the last workspace on the TV to the side."
+  (let* ((monitors (list-available-monitors))
+         (primary (car (exwm-randr--get-monitors)))
+         (monitor (pcase (seq-length monitors)
+                    (0 (error "No available monitors."))
+                    (1 (car monitors))
+                    (_
+                     (completing-read "Which monitor? " (list-available-monitors) nil t))))
+
+         (configurations `(("secondary (left)" . ,(format "--left-of %s" primary))
+                           ("secondary (right)" . ,(format "--right-of %s" primary))
+                           ("primary (left)" . ,(format "--left-of %s --primary" primary))
+                           ("primary (right)" . ,(format "--right-of %s --primary" primary))
+                           ("mirror" . ,(format "--same-as %s" primary))))
+
+         (where (completing-read (format "%s should be " monitor)
+                                 (seq-map #'car configurations)
+                                 nil t))
+         (xrandr-pos (cdr (assoc where configurations)))
+         (xrandr-cmd (format "xrandr --output %s --auto %s" monitor xrandr-pos)))
+    (message "Invoking '%s'" xrandr-cmd)
+    (shell-command xrandr-cmd)
+    (exwm-assign-workspaces)))
+
+(defun exwm-disable-monitor ()
+  "Interactively choose a monitor to disable."
   (interactive)
-  (set-randr-config
-   '(("eDP" 1 2 3 4 5 6 7 8 9)
-     ("HDMI-A-0" 0)))
 
-  (shell-command "xrandr --output HDMI-A-0 --above eDP --auto")
-  (exwm-randr-refresh))
+  (let* ((all (exwm-randr--get-monitors))
+         (active (seq-map #'car (cadr all)))
+         (monitor (if (> (seq-length active) 1)
+                      (completing-read "Disable which monitor? " active nil t)
+                    (error "Only one monitor is active!")))
 
-(defun randr-vauxhall-layout-remarkable ()
-  "Make the reMarkable the primary screen."
-  (interactive)
-  (shell-command "xrandr --output VIRTUAL1 --primary"))
+         ;; If this monitor was primary, pick another active one instead.
+         (remaining (seq-filter (lambda (s) (not (equal s monitor))) active))
+         (new-primary
+          (when (equal monitor (car all))
+            (pcase (seq-length remaining)
+              (1 (car remaining))
+              (_ (completing-read "New primary? " remaining nil t))))))
 
-;; Layouts for frog (desktop)
+    (when new-primary
+      (shell-command (format "xrandr --output %s --primary" new-primary)))
 
-(defun randr-frog-layout-right-only ()
-  "Use only the right screen on frog."
-  (interactive)
-  (set-randr-config `(("DisplayPort-0" ,(number-sequence 0 9))))
-  (shell-command "xrandr --output DisplayPort-0 --off")
-  (shell-command "xrandr --output DisplayPort-1 --auto --primary"))
+    (shell-command (format "xrandr --output %s --off" monitor))
+    (exwm-assign-workspaces)))
 
-(defun randr-frog-layout-both ()
-  "Use the left and right screen on frog."
+(defun exwm-switch-monitor ()
+  "Switch focus to another monitor by name."
   (interactive)
-  (set-randr-config `(("DisplayPort-0" 1 2 3 4 5)
-                      ("DisplayPort-1" 6 7 8 9 0)))
-
-  (shell-command "xrandr --output DisplayPort-0 --auto --primary --left-of DisplayPort-1")
-  (shell-command "xrandr --output DisplayPort-1 --auto --right-of DisplayPort-0 --rotate left"))
 
-(pcase (s-trim (shell-command-to-string "hostname"))
-  ("vauxhall"
-   (exwm-input-set-key (kbd "s-m s") #'randr-vauxhall-layout-single)
-   (exwm-input-set-key (kbd "s-m a") #'randr-vauxhall-layout-all)
-   (exwm-input-set-key (kbd "s-m w") #'randr-vauxhall-layout-wide-only)
-   (exwm-input-set-key (kbd "s-m r") #'randr-vauxhall-layout-remarkable))
+  ;; TODO: Filter out currently active? How to determine it?
+  (let* ((target (completing-read "Switch to monitor: "
+                                  (seq-map #'car (cadr (exwm-randr--get-monitors)))
+                                  nil t))
+         (target-workspace
+          (cl-loop for (workspace screen) on exwm-randr-workspace-monitor-plist by #'cddr
+                   when (equal screen target) return workspace)))
+    (exwm-workspace-switch target-workspace)))
 
-  ("frog"
-   (exwm-input-set-key (kbd "s-m b") #'randr-frog-layout-both)
-   (exwm-input-set-key (kbd "s-m r") #'randr-frog-layout-right-only)))
+(exwm-input-set-key (kbd "s-m e") #'exwm-enable-monitor)
+(exwm-input-set-key (kbd "s-m d") #'exwm-disable-monitor)
+(exwm-input-set-key (kbd "s-m o") #'exwm-switch-monitor)
 
 ;; Notmuch shortcuts as EXWM globals
 ;; (g m => gmail)
 (exwm-input-set-key (kbd "s-g m") #'notmuch)
-(exwm-input-set-key (kbd "s-g M") #'counsel-notmuch)
-
-(exwm-randr-enable)
 
 ;; Let buffers move seamlessly between workspaces by making them
 ;; accessible in selectors on all frames.
diff --git a/users/tazjin/emacs/config/functions.el b/users/tazjin/emacs/config/functions.el
index 5963d142c1..68a384d20f 100644
--- a/users/tazjin/emacs/config/functions.el
+++ b/users/tazjin/emacs/config/functions.el
@@ -2,9 +2,7 @@
 (require 'dash)
 (require 'map)
 
-(defun load-file-if-exists (filename)
-  (if (file-exists-p filename)
-      (load filename)))
+(require 'gio-list-apps) ;; native module!
 
 (defun goto-line-with-feedback ()
   "Show line numbers temporarily, while prompting for the line number input"
@@ -17,24 +15,19 @@
           (goto-line target)))
     (setq-local display-line-numbers nil)))
 
-;; These come from the emacs starter kit
-
 (defun esk-add-watchwords ()
   (font-lock-add-keywords
    nil '(("\\<\\(FIX\\(ME\\)?\\|TODO\\|DEBUG\\|HACK\\|REFACTOR\\|NOCOMMIT\\)"
           1 font-lock-warning-face t))))
 
+(add-hook 'prog-mode-hook 'esk-add-watchwords)
+
 (defun esk-sudo-edit (&optional arg)
   (interactive "p")
   (if (or arg (not buffer-file-name))
       (find-file (concat "/sudo:root@localhost:" (read-file-name "File: ")))
     (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))
 
-;; Open the NixOS man page
-(defun nixos-man ()
-  (interactive)
-  (man "configuration.nix"))
-
 ;; Get the nix store path for a given derivation.
 ;; If the derivation has not been built before, this will trigger a build.
 (defun nix-store-path (derivation)
@@ -114,7 +107,9 @@ the GPG agent correctly."
                    nil ;; predicate
                    t   ;; require-match
                    ))
-         (password (auth-source-pass-get 'secret entry)))
+         (password (or (let ((epa-suppress-error-buffer t))
+                         (auth-source-pass-get 'secret entry))
+                       (error "failed to decrypt '%s', wrong password?" entry))))
     (password-store-clear)
     (kill-new password)
     (setq password-store-kill-ring-pointer kill-ring-yank-pointer)
@@ -124,25 +119,8 @@ the GPG agent correctly."
           (run-at-time (password-store-timeout)
                        nil 'password-store-clear))))
 
-(defun browse-repositories ()
-  "Select a git repository and open its associated magit buffer."
-
-  (interactive)
-  (magit-status
-   (completing-read "Repository: " (magit-list-repos))))
-
-(defun bottom-right-window-p ()
-  "Determines whether the last (i.e. bottom-right) window of the
-  active frame is showing the buffer in which this function is
-  executed."
-  (let* ((frame (selected-frame))
-         (right-windows (window-at-side-list frame 'right))
-         (bottom-windows (window-at-side-list frame 'bottom))
-         (last-window (car (seq-intersection right-windows bottom-windows))))
-    (eq (current-buffer) (window-buffer last-window))))
-
 (defhydra mc/mark-more-hydra (:color pink)
-  ("<up>" mmlte--up "Mark previous like this")
+  ("<up>" mc/mmlte--up "Mark previous like this")
   ("<down>" mc/mmlte--down "Mark next like this")
   ("<left>" mc/mmlte--left (if (eq mc/mark-more-like-this-extended-direction 'up)
                                "Skip past the cursor furthest up"
@@ -168,26 +146,15 @@ the GPG agent correctly."
       (mc/mmlte--down)
       (mc/mark-more-hydra/body))))
 
-(defun memespace-region ()
-  "Make a meme out of it."
+(setq mc/cmds-to-run-for-all '(kill-region paredit-newline))
 
-  (interactive)
-  (let* ((start (region-beginning))
-         (end (region-end))
-         (memed
-          (message
-           (s-trim-right
-            (apply #'string
-                   (-flatten
-                    (nreverse
-                     (-reduce-from (lambda (acc x)
-                                     (cons (cons x (-repeat (+ 1 (length acc)) 32)) acc))
-                                   '()
-                                   (string-to-list (buffer-substring-no-properties start end))))))))))
-
-    (save-excursion (delete-region start end)
-                    (goto-char start)
-                    (insert memed))))
+(setq mc/cmds-to-run-once '(mc/mark-dwim
+                            mc/mark-more-hydra/mc/mmlte--down
+                            mc/mark-more-hydra/mc/mmlte--left
+                            mc/mark-more-hydra/mc/mmlte--right
+                            mc/mark-more-hydra/mc/mmlte--up
+                            mc/mark-more-hydra/mmlte--up
+                            mc/mark-more-hydra/nil))
 
 (defun insert-todo-comment (prefix todo)
   "Insert a comment at point with something for me to do."
@@ -231,11 +198,16 @@ the GPG agent correctly."
   (if prefix (text-scale-adjust 0)
     (set-face-attribute 'default nil :height (or to 120))))
 
-(defun scrot-select ()
+(defun screenshot-select (filename)
   "Take a screenshot based on a mouse-selection and save it to
   ~/screenshots."
-  (interactive)
-  (shell-command "scrot '$a_%Y-%m-%d_%s.png' -s -e 'mv $f ~/screenshots/'"))
+  (interactive "sScreenshot filename: ")
+  (let* ((path (f-join "~/screenshots"
+                       (format "%s-%d.png"
+                               (if (string-empty-p filename) "shot" filename)
+                               (time-convert nil 'integer)))))
+    (shell-command (format "maim --select %s" path))
+    (message "Wrote screenshot to %s" path)))
 
 (defun graph-unread-mails ()
   "Create a bar chart of unread mails based on notmuch tags.
@@ -283,6 +255,17 @@ the GPG agent correctly."
 
 (add-to-list 'project-find-functions #'find-depot-project)
 
+(defun find-cargo-project (dir)
+  "Attempt to find the current project in `project-find-functions'
+by looking for a `Cargo.toml' file."
+  (when dir
+    (unless (equal "/" dir)
+      (if (f-exists-p (f-join dir "Cargo.toml"))
+          (cons 'transient dir)
+        (find-cargo-project (f-parent dir))))))
+
+(add-to-list 'project-find-functions #'find-cargo-project)
+
 (defun magit-find-file-worktree ()
   (interactive)
   "Find a file in the current (ma)git worktree."
@@ -290,39 +273,80 @@ the GPG agent correctly."
                              (magit-read-file-from-rev "HEAD" "Find file")
                              #'pop-to-buffer-same-window))
 
-(defun songwhip--handle-result (status &optional cbargs)
-  ;; TODO(tazjin): Inspect status, which looks different in practice
-  ;; than the manual claims.
-  (if-let* ((response (json-parse-string
-                       (buffer-substring url-http-end-of-headers (point-max))))
-            (sw-path (ht-get* response "data" "path"))
-            (link (format "https://songwhip.com/%s" sw-path))
-            (select-enable-clipboard t))
-      (progn
-        (kill-new link)
-        (message "Copied Songwhip link (%s)" link))
-    (warn "Something went wrong while retrieving Songwhip link!")
-    ;; For debug purposes, the buffer is persisted in this case.
-    (setq songwhip--debug-buffer (current-buffer))))
-
-(defun songwhip-lookup-url (url)
-  "Look up URL on Songwhip and copy the resulting link to the clipboard."
-  (interactive "sEnter source URL: ")
-  (let ((songwhip-url "https://songwhip.com/api/")
-        (url-request-method "POST")
-        (url-request-extra-headers '(("Content-Type" . "application/json")))
-        (url-request-data
-         (json-serialize `((country . "GB")
-                           (url . ,url)))))
-    (url-retrieve "https://songwhip.com/api/" #'songwhip--handle-result nil t t)
-    (message "Requesting Songwhip URL ... please hold the line.")))
-
-(defun rg-in-project (&optional prefix)
-  "Interactively call ripgrep in the current project, or fall
-  back to ripgrep default behaviour if prefix is set."
-  (interactive "P")
-  (counsel-rg nil (unless prefix
-                    (if-let ((pr (project-current)))
-                        (project-root pr)))))
+(defun zoxide-open-project ()
+  "Query Zoxide for paths, and open the result as appropriate (magit or dired)."
+  (interactive)
+  (zoxide-open-with
+   nil
+   (lambda (path)
+     (condition-case err (magit-status-setup-buffer path)
+       (magit-outside-git-repo (dired path))))))
+
+(defun toggle-nix-test-and-exp ()
+  "Switch between the .nix and .exp file in a Tvix/Nix test."
+  (interactive)
+  (let* ((file (buffer-file-name))
+         (other (if (s-suffix? ".nix" file)
+                    (s-replace-regexp ".nix$" ".exp" file)
+                  (if (s-suffix? ".exp" file)
+                      (s-replace-regexp ".exp$" ".nix" file)
+                    (error "Not a .nix/.exp file!")))))
+    (find-file other)))
+
+(defun reliably-switch-buffer ()
+  "Reliably and interactively switch buffers, without ending up in a
+situation where the buffer was renamed during selection and an
+empty new buffer is created.
+
+This is done by, in contrast to most buffer-switching functions,
+retaining a list of the buffer *objects* and their associated
+names, instead of only their names (which might change)."
+
+  (interactive)
+  (let* ((buffers (seq-map (lambda (b) (cons (buffer-name b) b))
+                           (seq-filter (lambda (b) (not (string-prefix-p " " (buffer-name b))))
+                                       (buffer-list))))
+
+         ;; Annotate buffers that display remote files. I frequently
+         ;; want to see it, because I might have identically named
+         ;; files open locally and remotely at the same time, and it
+         ;; helps with differentiating them.
+         (completion-extra-properties
+          '(:annotation-function
+            (lambda (name)
+              (if-let* ((file (buffer-file-name (cdr (assoc name buffers))))
+                        (remote (file-remote-p file)))
+                  (format " [%s]" remote)))))
+
+         (name (completing-read "Switch to buffer: " (seq-map #'car buffers)))
+         (selected (or (cdr (assoc name buffers))
+                       ;; Allow users to manually select invisible buffers ...
+                       (get-buffer name))))
+    (switch-to-buffer (or selected name) nil 't)))
+
+(defun run-xdg-app ()
+  "Use `//users/tazjin/gio-list-apps' to retrieve a list of
+installed (and visible) XDG apps, and let users launch them."
+  (interactive)
+  (let* ((apps (taz-list-xdg-apps))
+
+         ;; Display the command that will be run as an annotation
+         (completion-extra-properties
+          '(:annotation-function (lambda (app) (format " [%s]" (cdr (assoc app apps)))))))
+
+    (run-external-command--handler (cdr (assoc (completing-read "App: " apps nil t) apps)))))
+
+(defun advice-remove-all (sym)
+  "Remove all advices from symbol SYM."
+  (interactive "aFunction symbol: ")
+  (advice-mapc (lambda (advice _props) (advice-remove sym advice)) sym))
+
+(defun M-x-always-same-window ()
+  "Run `execute-extended-command', but ensure that whatever it does
+always opens in the same window in which the command was invoked."
+  (interactive)
+  (let ((display-buffer-overriding-action
+         '((display-buffer-same-window) . ((inhibit-same-window . nil)))))
+    (call-interactively #'execute-extended-command)))
 
 (provide 'functions)
diff --git a/users/tazjin/emacs/config/init.el b/users/tazjin/emacs/config/init.el
index 8014c86a5a..ced3bf2ff8 100644
--- a/users/tazjin/emacs/config/init.el
+++ b/users/tazjin/emacs/config/init.el
@@ -1,28 +1,18 @@
 ;;; init.el --- Package bootstrapping. -*- lexical-binding: t; -*-
 
+;; Disable annoying warnings from native compilation.
+(setq native-comp-async-report-warnings-errors nil
+      warning-suppress-log-types '((comp)))
+
 ;; Packages are installed via Nix configuration, this file only
 ;; initialises the newly loaded packages.
 
 (require 'use-package)
 (require 'seq)
 
-;; TODO(tazjin): Figure out what's up with vc.
-;;
-;; Leaving vc enabled breaks all find-file operations with messages
-;; about .git folders being absent, but in random places.
-(require 'vc)
-(setq vc-handled-backends nil)
-
 (package-initialize)
 
 ;; Initialise all packages installed via Nix.
-;;
-;; TODO: Generate this section in Nix for all packages that do not
-;; require special configuration.
-
-;;
-;; Packages providing generic functionality.
-;;
 
 (use-package ace-window
   :bind (("C-x o" . ace-window))
@@ -39,13 +29,10 @@
 
 (use-package browse-kill-ring)
 
-(use-package company
-  :hook ((prog-mode . company-mode))
-  :config (setq company-tooltip-align-annotations t))
-
-(use-package counsel
-  :after (ivy)
-  :config (counsel-mode 1))
+(use-package consult
+  :bind
+  ("C-c r g" . consult-ripgrep)
+  ("C-s" . consult-line))
 
 (use-package dash)
 (use-package gruber-darker-theme)
@@ -55,39 +42,11 @@
   (eglot-autoshutdown t)
   (eglot-send-changes-idle-time 0.3))
 
-(use-package elfeed
-  :config
-  (setq elfeed-feeds
-        '("https://lobste.rs/rss"
-          "https://www.anti-spiegel.ru/feed/"
-          "https://www.reddit.com/r/lockdownskepticism/.rss"
-          "https://www.reddit.com/r/rust/.rss"
-          "https://news.ycombinator.com/rss"
-          ("https://xkcd.com/atom.xml" media)
-
-          ;; vlogcreations
-          ("https://www.youtube.com/feeds/videos.xml?channel_id=UCR0VLWitB2xM4q7tjkoJUPw" media)
-          )))
-
 (use-package ht)
 
 (use-package hydra)
 (use-package idle-highlight-mode :hook ((prog-mode . idle-highlight-mode)))
 
-(use-package ivy
-  :config
-  (ivy-mode 1)
-  (setq enable-recursive-minibuffers t)
-  (setq ivy-use-virtual-buffers t))
-
-(use-package ivy-prescient
-  :after (ivy prescient)
-  :config
-  (ivy-prescient-mode)
-  ;; Fixes an issue with how regexes are passed to ripgrep from counsel,
-  ;; see raxod502/prescient.el#43
-  (setf (alist-get 'counsel-rg ivy-re-builders-alist) #'ivy--regex-plus))
-
 (use-package multiple-cursors)
 
 (use-package notmuch
@@ -105,33 +64,20 @@
   (pinentry-start))
 
 (use-package prescient
-  :after (ivy counsel)
-  :config (prescient-persist-mode))
-
-(use-package rainbow-delimiters
-  :hook (prog-mode . rainbow-delimiters-mode)
-  :custom-face
-  (rainbow-delimiters-depth-1-face ((t (:foreground "#2aa198"))))
-  (rainbow-delimiters-depth-2-face ((t (:foreground "#b58900"))))
-  (rainbow-delimiters-depth-3-face ((t (:foreground "#268bd2"))))
-  (rainbow-delimiters-depth-4-face ((t (:foreground "#dc322f"))))
-  (rainbow-delimiters-depth-5-face ((t (:foreground "#859900"))))
-  (rainbow-delimiters-depth-6-face ((t (:foreground "#268bd2"))))
-  (rainbow-delimiters-depth-7-face ((t (:foreground "#cb4b16"))))
-  (rainbow-delimiters-depth-8-face ((t (:foreground "#d33682"))))
-  (rainbow-delimiters-depth-9-face ((t (:foreground "#839496")))))
+  :config
+  (prescient-persist-mode)
+  (setq completion-styles '(basic prescient)))
 
+(use-package rainbow-delimiters :hook (prog-mode . rainbow-delimiters-mode))
 (use-package rainbow-mode)
 (use-package s)
-(use-package string-edit)
+(use-package string-edit-at-point)
+(use-package term-switcher)
 
-(use-package swiper
-  :after (counsel ivy)
-  :bind (("C-s" . swiper)))
+(use-package undo-tree
+  :config (global-undo-tree-mode)
+  :custom (undo-tree-auto-save-history nil))
 
-(use-package telephone-line) ;; configuration happens outside of use-package
-(use-package term-switcher)
-(use-package undo-tree :config (global-undo-tree-mode))
 (use-package uuidgen)
 (use-package which-key :config (which-key-mode t))
 
@@ -148,20 +94,9 @@
 (use-package restclient)
 
 (use-package vterm
-  :config (progn
-            (setq vterm-shell "fish")
-            (setq vterm-exit-functions
-                  (lambda (&rest _) (kill-buffer (current-buffer))))
-            (setq vterm-kill-buffer-on-exit t))
-  :custom-face
-  (term-color-black ((t (:background "#282828" :foreground "#282828"))))
-  (term-color-blue ((t (:background "#96a6c8" :foreground "#96a6c8"))))
-  (term-color-cyan ((t (:background "#1fad83" :foreground "#1fad83"))))
-  (term-color-green ((t (:background "#73c936" :foreground "#73c936"))))
-  (term-color-magenta ((t (:background "#9e95c7" :foreground "#9e95c7"))))
-  (term-color-red ((t (:background "#f43841" :foreground "#f43841"))))
-  (term-color-white ((t (:background "#f5f5f5" :foreground "#f5f5f5"))))
-  (term-color-yellow ((t (:background "#ffdd33" :foreground "#ffdd33")))))
+  :custom
+  (vterm-shell "fish")
+  (vterm-kill-buffer-on-exit t))
 
 ;; vterm removed the ability to set a custom title generator function
 ;; via the public API, so this overrides its private title generation
@@ -182,7 +117,7 @@
          (cargo-process-mode . visual-line-mode))
   :bind (:map cargo-mode-map ("C-c C-c C-l" . ignore)))
 
-(use-package dockerfile-mode)
+(use-package dockerfile-ts-mode)
 
 (use-package erlang
   :hook ((erlang-mode . (lambda ()
@@ -202,9 +137,7 @@
 
 (use-package ielm
   :hook ((inferior-emacs-lisp-mode . (lambda ()
-                                       (paredit-mode)
-                                       (rainbow-delimiters-mode-enable)
-                                       (company-mode)))))
+                                       (rainbow-delimiters-mode-enable)))))
 
 (use-package jq-mode
   :config (add-to-list 'auto-mode-alist '("\\.jq\\'" . jq-mode)))
@@ -213,8 +146,6 @@
   :hook ((kotlin-mode . (lambda ()
                           (setq indent-line-function #'indent-relative)))))
 
-(use-package lsp-mode)
-
 (use-package markdown-mode
   :config
   (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))
@@ -233,26 +164,54 @@
 (use-package sly
   :hook ((sly-mrepl-mode . (lambda ()
                              (paredit-mode)
-                             (rainbow-delimiters-mode-enable)
-                             (company-mode))))
+                             (rainbow-delimiters-mode-enable))))
   :config
   (setq common-lisp-hyperspec-root "file:///home/tazjin/docs/lisp/"))
 
 (use-package telega
-  :bind (:map global-map ("s-t" . telega))
-  :config
-  (telega-mode-line-mode 1)
-  (add-hook 'telega-msg-ignore-predicates 'telega-msg-from-blocked-sender-p))
+  :bind (:map global-map ("s-c" . (lambda (p) (interactive "P")
+                                    (if p (call-interactively #'telega-chat-with)
+                                      (telega))))
+         :map telega-chat-button-map ("a" . ignore))
+  :config (telega-mode-line-mode 1)
+  :custom
+  (telega-emoji-use-images nil)
+  (telega-completing-read-function #'completing-read))
 
 (use-package terraform-mode)
-(use-package toml-mode)
+(use-package toml-ts-mode)
 
-(use-package tvl
-  :custom
-  (tvl-gerrit-remote "gerrit"))
+(use-package treecrumbs
+  :hook ((yaml-ts-mode . treecrumbs-mode)))
+
+(use-package tvl)
+
+(use-package vertico
+  :config
+  (vertico-mode))
 
 (use-package web-mode)
-(use-package yaml-mode)
+(use-package yaml-ts-mode)
+(use-package zoxide)
+
+(use-package passively
+  :custom
+  (passively-store-state "/persist/tazjin/known-russian-words.el"))
+
+;; Note taking configuration for deft.
+(use-package deft
+  :custom
+  (deft-directory "/persist/tazjin/deft/")
+  (deft-extensions '("md" "org" "txt"))
+  (deft-default-extension "md"))
+
+(use-package zetteldeft
+  :custom
+  ;; Configure for Markdown
+  (zetteldeft-link-indicator "[[")
+  (zetteldeft-link-suffix "]]")
+  (zetteldeft-title-prefix "# ")
+  (zetteldeft-list-prefix "* "))
 
 ;; Initialise midnight.el, which by default automatically cleans up
 ;; unused buffers at midnight.
@@ -272,7 +231,7 @@
 ;; The way this will work for now is that Emacs will *write*
 ;; configuration to the file tracked in my repository, while not
 ;; actually *reading* it from there (unless Emacs is rebuilt).
-(setq custom-file (expand-file-name "~/depot/tools/emacs/config/custom.el"))
+(setq custom-file (f-join depot-path "users" "tazjin" "emacs" "config" "custom.el"))
 (load-library "custom")
 
 (defvar home-dir (expand-file-name "~"))
@@ -287,10 +246,8 @@
                  look-and-feel
                  functions
                  settings
-                 modes
                  bindings
                  eshell-setup))
-(telephone-line-setup)
 (ace-window-display-mode)
 
 ;; If a local configuration library exists, it should be loaded.
diff --git a/users/tazjin/emacs/config/look-and-feel.el b/users/tazjin/emacs/config/look-and-feel.el
index 8cca6e1bf0..b771b4cd03 100644
--- a/users/tazjin/emacs/config/look-and-feel.el
+++ b/users/tazjin/emacs/config/look-and-feel.el
@@ -11,9 +11,6 @@
 (setq ring-bell-function 'ignore)
 (setq initial-scratch-message "")
 
-;; Remember layout changes
-(winner-mode 1)
-
 ;; Usually emacs will run as a proper GUI application, in which case a few
 ;; extra settings are nice-to-have:
 (when window-system
@@ -22,69 +19,39 @@
   (blink-cursor-mode -1))
 
 ;; Configure Emacs fonts.
-(let ((font (if (equal "frog" (s-trim (shell-command-to-string "hostname")))
-                ;; For unclear reasons, frog refuses to render the
-                ;; regular font weight - everything ends up bold,
-                ;; which makes it hard to distinguish e.g. read/unread
-                ;; emails.
-                ;;
-                ;; Semi-bold looks a little different than on vauxhall
-                ;; and other machines, but it's alright.
-                (format "JetBrains Mono Semi Light-%d" 12)
-              (format "JetBrains Mono-%d" 12))))
+(let ((font (format "JetBrains Mono-%d" 12)))
   (setq default-frame-alist `((font . ,font)))
   (set-frame-font font t t))
 
-;; Configure telephone-line
-(defun telephone-misc-if-last-window ()
-  "Renders the mode-line-misc-info string for display in the
-  mode-line if the currently active window is the last one in the
-  frame.
-
-  The idea is to not display information like the current time,
-  load, battery levels on all buffers."
-
-  (when (bottom-right-window-p)
-    (telephone-line-raw mode-line-misc-info t)))
-
-(defun telephone-line-setup ()
-  (telephone-line-defsegment telephone-line-last-window-segment ()
-    (telephone-misc-if-last-window))
-
-  ;; Display the current EXWM workspace index in the mode-line
-  (telephone-line-defsegment telephone-line-exwm-workspace-index ()
-    (when (bottom-right-window-p)
-      (format "[%s]" exwm-workspace-current-index)))
-
-  ;; Define a highlight font for ~ important ~ information in the last
-  ;; window.
-  (defface special-highlight '((t (:foreground "white" :background "#5f627f"))) "")
-  (add-to-list 'telephone-line-faces
-               '(highlight . (special-highlight . special-highlight)))
-
-  (setq telephone-line-lhs
-        '((nil . (telephone-line-position-segment))
-          (accent . (telephone-line-buffer-segment))))
-
-  (setq telephone-line-rhs
-        '((accent . (telephone-line-major-mode-segment))
-          (nil . (telephone-line-last-window-segment
-                  telephone-line-exwm-workspace-index))
-
-          ;; TODO(tazjin): lets not do this particular thing while I
-          ;; don't actually run notmuch, there are too many things
-          ;; that have a dependency on the modeline drawing correctly
-          ;; (including randr operations!)
-          ;;
-          ;; (highlight . (telephone-line-notmuch-counts))
-          ))
-
-  (setq telephone-line-primary-left-separator 'telephone-line-tan-left
-        telephone-line-primary-right-separator 'telephone-line-tan-right
-        telephone-line-secondary-left-separator 'telephone-line-tan-hollow-left
-        telephone-line-secondary-right-separator 'telephone-line-tan-hollow-right)
-
-  (telephone-line-mode 1))
+;; Configure the modeline
+
+;; Implements a mode-line warning if there are any logged in TTY
+;; sessions apart from the graphical one.
+;;
+;; The status is only updated once every 30 seconds, as it requires
+;; shelling out to some commands (for now).
+(defun list-tty-sessions ()
+  "List all logged in tty sessions, except tty7 (graphical)"
+  (let ((command "who | awk '{print $2}' | grep -v tty7"))
+    (-filter (lambda (s) (not (string-empty-p s)))
+             (s-lines
+              (s-trim (let ((default-directory "/"))
+                        (shell-command-to-string command)))))))
+
+(defvar cached-tty-sessions (cons (time-convert nil 'integer) (list-tty-sessions))
+   "Cached TTY session value to avoid running the command too often.")
+
+;; TODO(tazjin): add this to the modeline
+
+(defun get-cached-tty-sessions ()
+  (let ((time ))
+    (when (< 30
+             (- (time-convert nil 'integer)
+                (car cached-tty-sessions)))
+      (setq cached-tty-sessions
+            (cons (time-convert nil 'integer) (list-tty-sessions)))))
+
+  (cdr cached-tty-sessions))
 
 ;; Auto refresh buffers
 (global-auto-revert-mode 1)
@@ -119,4 +86,13 @@
 ;; Don't wrap around when moving between buffers
 (setq windmove-wrap-around nil)
 
+;; Don't show me all emacs warnings immediately. Unfortunately this is
+;; not very granular, as emacs displays most of its warnings in the
+;; `emacs' "category", but without it every time I
+;; fullscreen/unfullscreen the warning buffer destroys my layout.
+;;
+;; Warnings suppressed by this are still logged to the warnings
+;; buffer.
+(setq warning-suppress-types '((emacs)))
+
 (provide 'look-and-feel)
diff --git a/users/tazjin/emacs/config/mail-setup.el b/users/tazjin/emacs/config/mail-setup.el
index 7fbece1b10..7352c8ba10 100644
--- a/users/tazjin/emacs/config/mail-setup.el
+++ b/users/tazjin/emacs/config/mail-setup.el
@@ -1,8 +1,6 @@
 (require 'notmuch)
-(require 'counsel-notmuch)
 
 ;; (global-set-key (kbd "C-c m") 'notmuch-hello)
-;; (global-set-key (kbd "C-c C-m") 'counsel-notmuch)
 ;; (global-set-key (kbd "C-c C-e n") 'notmuch-mua-new-mail)
 
 (setq notmuch-cache-dir (format "%s/.cache/notmuch" (getenv "HOME")))
@@ -51,7 +49,7 @@
 ;; handle that gracefully.
 (define-key notmuch-message-mode-map (kbd "C-x C-s") #'ignore)
 
-;; Define a telephone-line segment for displaying the count of unread,
+;; Define a mode-line segment for displaying the count of unread,
 ;; important mails in the last window's mode-line:
 (defvar *last-notmuch-count-redraw* 0)
 (defvar *current-notmuch-count* nil)
@@ -76,10 +74,6 @@
              (not (equal *current-notmuch-count* "I: 0; D: 0")))
     *current-notmuch-count*))
 
-(telephone-line-defsegment telephone-line-notmuch-counts ()
-  "This segment displays the count of unread notmuch messages in
-  the last window's mode-line (if unread messages are present)."
-
-  (update-display-notmuch-counts))
+;; TODO(tazjin): re-add this segment to the modeline
 
 (provide 'mail-setup)
diff --git a/users/tazjin/emacs/config/modes.el b/users/tazjin/emacs/config/modes.el
deleted file mode 100644
index 69fb523d0d..0000000000
--- a/users/tazjin/emacs/config/modes.el
+++ /dev/null
@@ -1,37 +0,0 @@
-;; Initializes modes I use.
-
-(add-hook 'prog-mode-hook 'esk-add-watchwords)
-(add-hook 'prog-mode-hook 'hl-line-mode)
-
-;; Use auto-complete as completion at point
-(defun set-auto-complete-as-completion-at-point-function ()
-  (setq completion-at-point-functions '(auto-complete)))
-
-(add-hook 'auto-complete-mode-hook
-          'set-auto-complete-as-completion-at-point-function)
-
-;; Enable rainbow-delimiters for all things programming
-(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
-
-;; Enable Paredit & Company in Emacs Lisp mode
-(add-hook 'emacs-lisp-mode-hook 'company-mode)
-
-;; Always highlight matching brackets
-(show-paren-mode 1)
-
-;; Always auto-close parantheses and other pairs
-(electric-pair-mode)
-
-;; Keep track of recent files
-(recentf-mode)
-
-;; Easily navigate sillycased words
-(global-subword-mode 1)
-
-;; Transparently open compressed files
-(auto-compression-mode t)
-
-;; Configure go-mode for Go2 Alpha
-(add-to-list 'auto-mode-alist '("\\.go2$" . go-mode))
-
-(provide 'modes)
diff --git a/users/tazjin/emacs/config/settings.el b/users/tazjin/emacs/config/settings.el
index 8b15b6cda1..6c66ca608d 100644
--- a/users/tazjin/emacs/config/settings.el
+++ b/users/tazjin/emacs/config/settings.el
@@ -45,4 +45,45 @@
 ;; Show time in 24h format
 (setq display-time-24hr-format t)
 
+;; Use python-mode for Starlark files.
+(add-to-list 'auto-mode-alist '("\\.star\\'" . python-mode))
+
+;; Use cmake-mode for relevant files.
+(add-to-list 'auto-mode-alist '("ya\\.make\\'" . cmake-ts-mode))
+
+;; Use tree-sitter modes for various languages.
+(setq major-mode-remap-alist
+      '((bash-mode . bash-ts-mode)
+        (c++-mode . c++-ts-mode)
+        (c-mode . c-ts-mode)
+        (c-or-c++-mode . c-or-c++-ts-mode)
+        (json-mode . json-ts-mode)
+        (python-mode . python-ts-mode)
+        (rust-mode . rust-ts-mode)
+        (toml-mode . toml-ts-mode)
+        (yaml-mode . yaml-ts-mode)
+        (go-mode . go-ts-mode)
+        (cmake-mode . cmake-ts-mode)))
+
+;; Visually highlight current line in programming buffers
+(add-hook 'prog-mode-hook 'hl-line-mode)
+
+;; Enable rainbow-delimiters for all things programming
+(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)
+
+;; Always highlight matching brackets
+(show-paren-mode 1)
+
+;; Always auto-close parantheses and other pairs
+(electric-pair-mode)
+
+;; Keep track of recent files
+(recentf-mode)
+
+;; Easily navigate sillycased words
+(global-subword-mode 1)
+
+;; Transparently open compressed files
+(auto-compression-mode t)
+
 (provide 'settings)
diff --git a/users/tazjin/emacs/default.nix b/users/tazjin/emacs/default.nix
index e85729d5a8..46843432f1 100644
--- a/users/tazjin/emacs/default.nix
+++ b/users/tazjin/emacs/default.nix
@@ -1,134 +1,189 @@
 # This file builds an Emacs pre-configured with the packages I need
 # and my personal Emacs configuration.
-#
-# On NixOS machines, this Emacs currently does not support
-# Imagemagick, see https://github.com/NixOS/nixpkgs/issues/70631.
-#
-# Forcing Emacs to link against Imagemagick currently causes libvterm
-# to segfault, which is a lot less desirable than not having telega
-# render images correctly.
-{ lib, pkgs, ... }:
+{ depot, lib, pkgs, ... }:
 
-pkgs.makeOverridable({ emacs ? pkgs.emacsGcc }:
-let
-  emacsWithPackages = (pkgs.emacsPackagesGen emacs).emacsWithPackages;
+pkgs.makeOverridable
+  ({ emacs ? pkgs.emacs29 }:
+  let
+    emacsPackages = (pkgs.emacsPackagesFor emacs);
+    emacsWithPackages = emacsPackages.emacsWithPackages;
 
-  # $PATH for binaries that need to be available to Emacs
-  emacsBinPath = lib.makeBinPath [ pkgs.emacsPackages.telega ];
+    # If switching telega versions, use this variable because it will
+    # keep the version check, binary path and so on in sync.
+    currentTelega = epkgs: epkgs.melpaPackages.telega;
 
-  identity = x: x;
+    # $PATH for binaries that need to be available to Emacs
+    emacsBinPath = lib.makeBinPath [
+      (currentTelega pkgs.emacsPackages)
+      pkgs.libwebp # for dwebp, required by telega
+    ];
 
-  tazjinsEmacs = pkgfun: (emacsWithPackages(epkgs: pkgfun(with epkgs; [
-    ace-link
-    ace-window
-    avy
-    bazel
-    browse-kill-ring
-    cargo
-    clojure-mode
-    cmake-mode
-    company
-    counsel
-    counsel-notmuch
-    d-mode
-    direnv
-    dockerfile-mode
-    eglot
-    elfeed
-    elixir-mode
-    elm-mode
-    erlang
-    exwm
-    flymake
-    go-mode
-    google-c-style
-    gruber-darker-theme
-    haskell-mode
-    ht
-    hydra
-    idle-highlight-mode
-    ivy
-    ivy-prescient
-    jq-mode
-    kotlin-mode
-    lsp-mode
-    magit
-    markdown-toc
-    meson-mode
-    multi-term
-    multiple-cursors
-    nginx-mode
-    nix-mode
-    notmuch
-    paredit
-    password-store
-    pinentry
-    polymode
-    prescient
-    protobuf-mode
-    rainbow-delimiters
-    rainbow-mode
-    refine
-    request
-    restclient
-    rust-mode
-    sly
-    string-edit
-    swiper
-    telega
-    telephone-line
-    terraform-mode
-    toml-mode
-    transient
-    undo-tree
-    use-package
-    uuidgen
-    vterm
-    web-mode
-    websocket
-    which-key
-    xelb
-    yaml-mode
-    yasnippet
+    identity = x: x;
 
-    # Custom depot packages (either ours, or overridden ones)
-    tvlPackages.dottime
-    tvlPackages.nix-util
-    tvlPackages.rcirc
-    tvlPackages.term-switcher
-    tvlPackages.tvl
-  ])));
-in lib.fix(self: l: f: pkgs.writeShellScriptBin "tazjins-emacs" ''
-  export PATH="${emacsBinPath}:$PATH"
-  exec ${tazjinsEmacs f}/bin/emacs \
-    --debug-init \
-    --no-site-file \
-    --no-site-lisp \
-    --no-init-file \
-    --directory ${./config} ${if l != null then "--directory ${l}" else ""} \
-    --eval "(require 'init)" $@
-  '' // {
-    # Call overrideEmacs with a function (pkgs -> pkgs) to modify the
-    # packages that should be included in this Emacs distribution.
-    overrideEmacs = f': self l f';
+    # tree-sitter grammars for various ts-modes
+    customTreesitGrammars = emacs.pkgs.treesit-grammars.with-grammars (g: with g; [
+      tree-sitter-bash
+      tree-sitter-c
+      tree-sitter-cmake
+      tree-sitter-cpp
+      tree-sitter-css
+      tree-sitter-dockerfile
+      tree-sitter-go
+      tree-sitter-gomod
+      tree-sitter-hcl
+      tree-sitter-html
+      tree-sitter-java
+      tree-sitter-json
+      tree-sitter-latex
+      tree-sitter-make
+      tree-sitter-nix
+      tree-sitter-python
+      tree-sitter-rust
+      tree-sitter-sql
+      tree-sitter-toml
+      tree-sitter-yaml
+    ]);
 
-    # Call withLocalConfig with the path to a *folder* containing a
-    # `local.el` which provides local system configuration.
-    withLocalConfig = confDir: self confDir f;
+    tazjinsEmacs = pkgfun: (emacsWithPackages (epkgs: pkgfun (with epkgs; [
+      ace-link
+      ace-window
+      avy
+      bazel
+      browse-kill-ring
+      cargo
+      clojure-mode
+      consult
+      deft
+      direnv
+      elixir-mode
+      elm-mode
+      erlang
+      depotExwm
+      go-mode
+      google-c-style
+      gruber-darker-theme
+      haskell-mode
+      ht
+      hydra
+      idle-highlight-mode
+      inspector
+      jq-mode
+      kotlin-mode
+      kubernetes
+      magit
+      markdown-toc
+      multiple-cursors
+      nginx-mode
+      nix-mode
+      notmuch
+      paredit
+      password-store
+      pinentry
+      prescient
+      protobuf-mode
+      rainbow-delimiters
+      rainbow-mode
+      request
+      restclient
+      rust-mode
+      sly
+      string-edit-at-point
+      terraform-mode
+      undo-tree
+      uuidgen
+      vertico
+      vterm
+      web-mode
+      websocket
+      which-key
+      xelb
+      yasnippet
+      zetteldeft
+      zoxide
 
-    # Build a derivation that uses the specified local Emacs (i.e.
-    # built outside of Nix) instead
-    withLocalEmacs = emacsBin: pkgs.writeShellScriptBin "tazjins-emacs" ''
+      # experimental (not otherwise embedded in config yet)
+      orderless
+      corfu
+      eat
+
+      # Wonky stuff
+      (currentTelega epkgs)
+      customTreesitGrammars # TODO(tazjin): how is this *supposed* to work?!
+
+      # Custom depot packages (either ours, or overridden ones)
+      tvlPackages.dottime
+      tvlPackages.nix-util
+      tvlPackages.passively
+      tvlPackages.rcirc
+      tvlPackages.term-switcher
+      tvlPackages.treecrumbs
+      tvlPackages.tvl
+
+      # Dynamic/native modules
+      depot.users.tazjin.gio-list-apps
+    ])));
+
+    # Tired of telega.el runtime breakages through tdlib
+    # incompatibility. Target to make that a build failure instead.
+    tdlibCheck =
+      let
+        tgEmacs = emacsWithPackages (epkgs: [ (currentTelega epkgs) ]);
+        verifyTdlibVersion = builtins.toFile "verify-tdlib-version.el" ''
+          (require 'telega)
+          (defvar tdlib-version "${pkgs.tdlib.version}")
+          (when (or (version< tdlib-version
+                              telega-tdlib-min-version)
+                    (and telega-tdlib-max-version
+                          (version< telega-tdlib-max-version
+                                    tdlib-version)))
+             (message "Found TDLib version %s, but require %s to %s"
+                     tdlib-version telega-tdlib-min-version telega-tdlib-max-version)
+            (kill-emacs 1))
+        '';
+      in
+      pkgs.runCommand "tdlibCheck" { } ''
+        export PATH="${emacsBinPath}:$PATH"
+        ${tgEmacs}/bin/emacs --script ${verifyTdlibVersion} && touch $out
+      '';
+  in
+  lib.fix
+    (self: l: f: (pkgs.writeShellScriptBin "tazjins-emacs" ''
       export PATH="${emacsBinPath}:$PATH"
-      export EMACSLOADPATH="${(tazjinsEmacs f).deps}/share/emacs/site-lisp:"
-      exec ${emacsBin} \
+      exec ${tazjinsEmacs f}/bin/emacs \
         --debug-init \
         --no-site-file \
         --no-site-lisp \
         --no-init-file \
-        --directory ${./config} \
-        ${if l != null then "--directory ${l}" else ""} \
+        --directory ${./config} ${if l != null then "--directory ${l}" else ""} \
+        --eval "(add-to-list 'treesit-extra-load-path \"${customTreesitGrammars}/lib\")" \
         --eval "(require 'init)" $@
-    '';
-  }) null identity
-) {}
+    '').overrideAttrs
+      (_: {
+        passthru = {
+          # Expose original Emacs used for my configuration.
+          inherit emacs;
+
+          # Expose the pure emacs with all packages.
+          inherit emacsPackages;
+          emacsWithPackages = tazjinsEmacs f;
+
+          # Call overrideEmacs with a function (pkgs -> pkgs) to modify the
+          # packages that should be included in this Emacs distribution.
+          overrideEmacs = f': self l f';
+
+          # Call withLocalConfig with the path to a *folder* containing a
+          # `local.el` which provides local system configuration.
+          withLocalConfig = confDir: self confDir f;
+
+          # Expose telega/tdlib version check as a target that is built in
+          # CI.
+          #
+          # TODO(tazjin): uncomment when telega works again
+          inherit tdlibCheck;
+          meta.ci.targets = [ "tdlibCheck" ];
+        };
+      }))
+    null
+    identity
+  )
+{ }
diff --git a/users/tazjin/finito/default.nix b/users/tazjin/finito/default.nix
index e50ac32be4..9a39591eab 100644
--- a/users/tazjin/finito/default.nix
+++ b/users/tazjin/finito/default.nix
@@ -2,4 +2,8 @@
 
 depot.third_party.naersk.buildPackage {
   src = ./.;
+
+  # Got broken by a rustc update (?)
+  # https://buildkite.com/tvl/depot/builds/17910#01841493-dc42-44f8-b904-32bf3d835485
+  meta.ci.skip = true;
 }
diff --git a/users/tazjin/finito/finito-core/src/lib.rs b/users/tazjin/finito/finito-core/src/lib.rs
index 517bfad2bc..aaec03a77b 100644
--- a/users/tazjin/finito/finito-core/src/lib.rs
+++ b/users/tazjin/finito/finito-core/src/lib.rs
@@ -38,8 +38,8 @@
 //!
 //!   * an event type representing all possible events in the machine
 //!
-//!   * an action type representing a description of all possible
-//!     side-effects of the machine
+//!   * an action type representing a description of all possible side-effects
+//!     of the machine
 //!
 //! Using the definition above we can now say that a transition in a
 //! state-machine, involving these three types, takes an initial state
@@ -92,14 +92,13 @@
 //!
 //!   * `finito`: Core components and classes of Finito
 //!
-//!   * `finito-in-mem`: In-memory implementation of state machines
-//!     that do not need to live longer than an application using
-//!     standard library concurrency primitives.
+//!   * `finito-in-mem`: In-memory implementation of state machines that do not
+//!     need to live longer than an application using standard library
+//!     concurrency primitives.
 //!
-//!   * `finito-postgres`: Postgres-backed, persistent implementation
-//!     of state machines that, well, do need to live longer. Uses
-//!     Postgres for concurrency synchronisation, so keep that in
-//!     mind.
+//!   * `finito-postgres`: Postgres-backed, persistent implementation of state
+//!     machines that, well, do need to live longer. Uses Postgres for
+//!     concurrency synchronisation, so keep that in mind.
 //!
 //! Which should cover most use-cases. Okay, enough prose, lets dive
 //! in.
@@ -110,8 +109,8 @@
 
 extern crate serde;
 
-use serde::Serialize;
 use serde::de::DeserializeOwned;
+use serde::Serialize;
 use std::fmt::Debug;
 use std::mem;
 
@@ -120,7 +119,10 @@ use std::mem;
 ///
 /// This trait is used to implement transition logic and to "tie the
 /// room together", with the room being our triplet of types.
-pub trait FSM where Self: Sized {
+pub trait FSM
+where
+    Self: Sized,
+{
     /// A human-readable string uniquely describing what this FSM
     /// models. This is used in log messages, database tables and
     /// various other things throughout Finito.
@@ -166,7 +168,7 @@ pub trait FSM where Self: Sized {
 
     /// `act` interprets and executes FSM actions. This is the only
     /// part of an FSM in which side-effects are allowed.
-    fn act(Self::Action, &Self::State) -> Result<Vec<Self::Event>, Self::Error>;
+    fn act(action: Self::Action, state: &Self::State) -> Result<Vec<Self::Event>, Self::Error>;
 }
 
 /// This function is the primary function used to advance a state
@@ -223,11 +225,13 @@ pub trait FSMBackend<S: 'static> {
     /// Insert a new state-machine into the backend's storage and
     /// return its newly allocated key.
     fn insert_machine<F>(&self, initial: F) -> Result<Self::Key, Self::Error>
-    where F: FSM + Serialize + DeserializeOwned;
+    where
+        F: FSM + Serialize + DeserializeOwned;
 
     /// Retrieve the current state of an FSM by its key.
     fn get_machine<F: FSM>(&self, key: Self::Key) -> Result<F, Self::Error>
-    where F: FSM + Serialize + DeserializeOwned;
+    where
+        F: FSM + Serialize + DeserializeOwned;
 
     /// Advance a state machine by applying an event and persisting it
     /// as well as any resulting actions.
@@ -236,8 +240,9 @@ pub trait FSMBackend<S: 'static> {
     /// on the backend used. Please consult the backend's
     /// documentation for details.
     fn advance<'a, F: FSM>(&'a self, key: Self::Key, event: F::Event) -> Result<F, Self::Error>
-    where F: FSM + Serialize + DeserializeOwned,
-          F::State: From<&'a S>,
-          F::Event: Serialize + DeserializeOwned,
-          F::Action: Serialize + DeserializeOwned;
+    where
+        F: FSM + Serialize + DeserializeOwned,
+        F::State: From<&'a S>,
+        F::Event: Serialize + DeserializeOwned,
+        F::Action: Serialize + DeserializeOwned;
 }
diff --git a/users/tazjin/finito/finito-door/src/lib.rs b/users/tazjin/finito/finito-door/src/lib.rs
index 68542c0bc4..441ab0e3d2 100644
--- a/users/tazjin/finito/finito-door/src/lib.rs
+++ b/users/tazjin/finito/finito-door/src/lib.rs
@@ -27,15 +27,15 @@
 //! The door can only be locked if it is closed. Oh, and it has a few
 //! extra features:
 //!
-//! * whenever the door's state changes, an IRC channel receives a
-//!   message about that
+//! * whenever the door's state changes, an IRC channel receives a message about
+//!   that
 //!
-//! * the door calls the police if the code is intered incorrectly more
-//!   than a specified number of times (mhm, lets say, three)
+//! * the door calls the police if the code is intered incorrectly more than a
+//!   specified number of times (mhm, lets say, three)
 //!
-//! * if the police is called the door can not be interacted with
-//!   anymore (and honestly, for the sake of this example, we don't
-//!   care how its functionality is restored)
+//! * if the police is called the door can not be interacted with anymore (and
+//!   honestly, for the sake of this example, we don't care how its
+//!   functionality is restored)
 //!
 //! ## The Door - Visualized
 //!
@@ -71,7 +71,8 @@
 //!
 //! Alright, enough foreplay, lets dive in!
 
-#[macro_use] extern crate serde_derive;
+#[macro_use]
+extern crate serde_derive;
 
 extern crate failure;
 extern crate finito;
@@ -292,11 +293,13 @@ mod tests {
     use finito::advance;
 
     fn test_fsm<S: FSM>(initial: S, events: Vec<S::Event>) -> (S, Vec<S::Action>) {
-        events.into_iter().fold((initial, vec![]), |(state, mut actions), event| {
-            let (new_state, mut new_actions) = advance(state, event);
-            actions.append(&mut new_actions);
-            (new_state, actions)
-        })
+        events
+            .into_iter()
+            .fold((initial, vec![]), |(state, mut actions), event| {
+                let (new_state, mut new_actions) = advance(state, event);
+                actions.append(&mut new_actions);
+                (new_state, actions)
+            })
     }
 
     #[test]
@@ -313,7 +316,10 @@ mod tests {
         ];
         let (final_state, actions) = test_fsm(initial, events);
 
-        assert_eq!(final_state, DoorState::Locked { code: 4567, attempts: 2 });
+        assert_eq!(final_state, DoorState::Locked {
+            code: 4567,
+            attempts: 2
+        });
         assert_eq!(actions, vec![
             DoorAction::NotifyIRC("door was closed".into()),
             DoorAction::NotifyIRC("door was opened".into()),
diff --git a/users/tazjin/finito/finito-postgres/src/error.rs b/users/tazjin/finito/finito-postgres/src/error.rs
index e130d18361..ed33775cd7 100644
--- a/users/tazjin/finito/finito-postgres/src/error.rs
+++ b/users/tazjin/finito/finito-postgres/src/error.rs
@@ -1,10 +1,9 @@
 //! This module defines error types and conversions for issue that can
 //! occur while dealing with persisted state machines.
 
-use std::result;
-use std::fmt;
-use uuid::Uuid;
 use std::error::Error as StdError;
+use std::{fmt, result};
+use uuid::Uuid;
 
 // errors to chain:
 use postgres::Error as PgError;
@@ -41,20 +40,15 @@ impl fmt::Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         use ErrorKind::*;
         let msg = match &self.kind {
-            Serialization(err) =>
-                format!("JSON serialization error: {}", err),
+            Serialization(err) => format!("JSON serialization error: {}", err),
 
-            Database(err) =>
-                format!("PostgreSQL error: {}", err),
+            Database(err) => format!("PostgreSQL error: {}", err),
 
-            DBPool(err) =>
-                format!("Database connection pool error: {}", err),
+            DBPool(err) => format!("Database connection pool error: {}", err),
 
-            FSMNotFound(id) =>
-                format!("FSM with ID {} not found", id),
+            FSMNotFound(id) => format!("FSM with ID {} not found", id),
 
-            ActionNotFound(id) =>
-                format!("Action with ID {} not found", id),
+            ActionNotFound(id) => format!("Action with ID {} not found", id),
         };
 
         match &self.context {
@@ -66,7 +60,7 @@ impl fmt::Display for Error {
 
 impl StdError for Error {}
 
-impl <E: Into<ErrorKind>> From<E> for Error {
+impl<E: Into<ErrorKind>> From<E> for Error {
     fn from(err: E) -> Error {
         Error {
             kind: err.into(),
@@ -99,11 +93,11 @@ pub trait ResultExt<T> {
     fn context<C: fmt::Display>(self, ctx: C) -> Result<T>;
 }
 
-impl <T, E: Into<Error>> ResultExt<T> for result::Result<T, E> {
+impl<T, E: Into<Error>> ResultExt<T> for result::Result<T, E> {
     fn context<C: fmt::Display>(self, ctx: C) -> Result<T> {
         self.map_err(|err| Error {
             context: Some(format!("{}", ctx)),
-            .. err.into()
+            ..err.into()
         })
     }
 }
diff --git a/users/tazjin/finito/finito-postgres/src/lib.rs b/users/tazjin/finito/finito-postgres/src/lib.rs
index ae147f751f..ea63cc9dfd 100644
--- a/users/tazjin/finito/finito-postgres/src/lib.rs
+++ b/users/tazjin/finito/finito-postgres/src/lib.rs
@@ -4,8 +4,10 @@
 //!
 //! TODO: events & actions should have `SERIAL` keys
 
-#[macro_use] extern crate postgres;
-#[macro_use] extern crate postgres_derive;
+#[macro_use]
+extern crate postgres;
+#[macro_use]
+extern crate postgres_derive;
 
 extern crate chrono;
 extern crate finito;
@@ -14,23 +16,25 @@ extern crate serde;
 extern crate serde_json;
 extern crate uuid;
 
-#[cfg(test)] mod tests;
-#[cfg(test)] extern crate finito_door;
+#[cfg(test)]
+mod tests;
+#[cfg(test)]
+extern crate finito_door;
 
 mod error;
-pub use error::{Result, Error, ErrorKind};
+pub use error::{Error, ErrorKind, Result};
 
 use chrono::prelude::{DateTime, Utc};
 use error::ResultExt;
-use finito::{FSM, FSMBackend};
+use finito::{FSMBackend, FSM};
 use postgres::transaction::Transaction;
 use postgres::GenericConnection;
-use serde::Serialize;
+use r2d2_postgres::{r2d2, PostgresConnectionManager};
 use serde::de::DeserializeOwned;
+use serde::Serialize;
 use serde_json::Value;
 use std::marker::PhantomData;
 use uuid::Uuid;
-use r2d2_postgres::{r2d2, PostgresConnectionManager};
 
 type DBPool = r2d2::Pool<PostgresConnectionManager>;
 type DBConn = r2d2::PooledConnection<PostgresConnectionManager>;
@@ -112,15 +116,13 @@ pub struct FinitoPostgres<S> {
     db_pool: DBPool,
 }
 
-impl <S> FinitoPostgres<S> {
+impl<S> FinitoPostgres<S> {
     pub fn new(state: S, db_pool: DBPool, _pool_size: usize) -> Self {
-        FinitoPostgres {
-            state, db_pool,
-        }
+        FinitoPostgres { state, db_pool }
     }
 }
 
-impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
+impl<State: 'static> FSMBackend<State> for FinitoPostgres<State> {
     type Key = Uuid;
     type Error = Error;
 
@@ -134,10 +136,11 @@ impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
         let fsm = S::FSM_NAME.to_string();
         let state = serde_json::to_value(initial).context("failed to serialise FSM")?;
 
-        self.conn()?.execute(query, &[&id, &fsm, &state]).context("failed to insert FSM")?;
+        self.conn()?
+            .execute(query, &[&id, &fsm, &state])
+            .context("failed to insert FSM")?;
 
         return Ok(id);
-
     }
 
     fn get_machine<S: FSM + DeserializeOwned>(&self, key: Uuid) -> Result<S> {
@@ -156,10 +159,12 @@ impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
     /// processing is finished as running actions may result in additional
     /// transitions.
     fn advance<'a, S>(&'a self, key: Uuid, event: S::Event) -> Result<S>
-    where S: FSM + Serialize + DeserializeOwned,
-          S::State: From<&'a State>,
-          S::Event: Serialize + DeserializeOwned,
-          S::Action: Serialize + DeserializeOwned {
+    where
+        S: FSM + Serialize + DeserializeOwned,
+        S::State: From<&'a State>,
+        S::Event: Serialize + DeserializeOwned,
+        S::Action: Serialize + DeserializeOwned,
+    {
         let conn = self.conn()?;
         let tx = conn.transaction().context("could not begin transaction")?;
         let state = get_machine_internal(&tx, key, true)?;
@@ -187,16 +192,18 @@ impl <State: 'static> FSMBackend<State> for FinitoPostgres<State> {
     }
 }
 
-impl <State: 'static> FinitoPostgres<State> {
+impl<State: 'static> FinitoPostgres<State> {
     /// Execute several actions at the same time, each in a separate
     /// thread. Note that actions returning further events, causing
     /// further transitions, returning further actions and so on will
     /// potentially cause multiple threads to get created.
-    fn run_actions<'a, S>(&'a self, fsm_id: Uuid, action_ids: Vec<Uuid>) where
+    fn run_actions<'a, S>(&'a self, fsm_id: Uuid, action_ids: Vec<Uuid>)
+    where
         S: FSM + Serialize + DeserializeOwned,
         S::Event: Serialize + DeserializeOwned,
         S::Action: Serialize + DeserializeOwned,
-        S::State: From<&'a State> {
+        S::State: From<&'a State>,
+    {
         let state: S::State = (&self.state).into();
         let conn = self.conn().expect("TODO");
 
@@ -214,17 +221,19 @@ impl <State: 'static> FinitoPostgres<State> {
 
     /// Retrieve a single connection from the database connection pool.
     fn conn(&self) -> Result<DBConn> {
-        self.db_pool.get().context("failed to retrieve connection from pool")
+        self.db_pool
+            .get()
+            .context("failed to retrieve connection from pool")
     }
 }
 
-
-
 /// Insert a single state-machine into the database and return its
 /// newly allocated, random UUID.
-pub fn insert_machine<C, S>(conn: &C, initial: S) -> Result<Uuid> where
+pub fn insert_machine<C, S>(conn: &C, initial: S) -> Result<Uuid>
+where
     C: GenericConnection,
-    S: FSM + Serialize {
+    S: FSM + Serialize,
+{
     let query = r#"
       INSERT INTO machines (id, fsm, state)
       VALUES ($1, $2, $3)
@@ -240,13 +249,12 @@ pub fn insert_machine<C, S>(conn: &C, initial: S) -> Result<Uuid> where
 }
 
 /// Insert a single event into the database and return its UUID.
-fn insert_event<C, S>(conn: &C,
-                      fsm_id: Uuid,
-                      event: &S::Event) -> Result<Uuid>
+fn insert_event<C, S>(conn: &C, fsm_id: Uuid, event: &S::Event) -> Result<Uuid>
 where
     C: GenericConnection,
     S: FSM,
-    S::Event: Serialize {
+    S::Event: Serialize,
+{
     let query = r#"
       INSERT INTO events (id, fsm, fsm_id, event)
       VALUES ($1, $2, $3, $4)
@@ -254,21 +262,19 @@ where
 
     let id = Uuid::new_v4();
     let fsm = S::FSM_NAME.to_string();
-    let event_value = serde_json::to_value(event)
-        .context("failed to serialize event")?;
+    let event_value = serde_json::to_value(event).context("failed to serialize event")?;
 
     conn.execute(query, &[&id, &fsm, &fsm_id, &event_value])?;
-    return Ok(id)
+    return Ok(id);
 }
 
 /// Insert a single action into the database and return its UUID.
-fn insert_action<C, S>(conn: &C,
-                       fsm_id: Uuid,
-                       event_id: Uuid,
-                       action: &S::Action) -> Result<Uuid> where
+fn insert_action<C, S>(conn: &C, fsm_id: Uuid, event_id: Uuid, action: &S::Action) -> Result<Uuid>
+where
     C: GenericConnection,
     S: FSM,
-    S::Action: Serialize {
+    S::Action: Serialize,
+{
     let query = r#"
       INSERT INTO actions (id, fsm, fsm_id, event_id, content, status)
       VALUES ($1, $2, $3, $4, $5, $6)
@@ -276,23 +282,26 @@ fn insert_action<C, S>(conn: &C,
 
     let id = Uuid::new_v4();
     let fsm = S::FSM_NAME.to_string();
-    let action_value = serde_json::to_value(action)
-        .context("failed to serialize action")?;
+    let action_value = serde_json::to_value(action).context("failed to serialize action")?;
 
-    conn.execute(
-        query,
-        &[&id, &fsm, &fsm_id, &event_id, &action_value, &ActionStatus::Pending]
-    )?;
+    conn.execute(query, &[
+        &id,
+        &fsm,
+        &fsm_id,
+        &event_id,
+        &action_value,
+        &ActionStatus::Pending,
+    ])?;
 
-    return Ok(id)
+    return Ok(id);
 }
 
 /// Update the state of a specified machine.
-fn update_state<C, S>(conn: &C,
-                      fsm_id: Uuid,
-                      state: &S) -> Result<()> where
+fn update_state<C, S>(conn: &C, fsm_id: Uuid, state: &S) -> Result<()>
+where
     C: GenericConnection,
-    S: FSM + Serialize {
+    S: FSM + Serialize,
+{
     let query = r#"
       UPDATE machines SET state = $1 WHERE id = $2
     "#;
@@ -312,23 +321,28 @@ fn update_state<C, S>(conn: &C,
 fn alter_for_update(alter: bool, query: &str) -> String {
     match alter {
         false => query.to_string(),
-        true  => format!("{} FOR UPDATE", query),
+        true => format!("{} FOR UPDATE", query),
     }
 }
 
 /// Retrieve the current state of a state machine from the database,
 /// optionally locking the machine state for the duration of some
 /// enclosing transaction.
-fn get_machine_internal<C, S>(conn: &C,
-                              id: Uuid,
-                              for_update: bool) -> Result<S> where
+fn get_machine_internal<C, S>(conn: &C, id: Uuid, for_update: bool) -> Result<S>
+where
     C: GenericConnection,
-    S: FSM + DeserializeOwned {
-    let query = alter_for_update(for_update, r#"
+    S: FSM + DeserializeOwned,
+{
+    let query = alter_for_update(
+        for_update,
+        r#"
       SELECT state FROM machines WHERE id = $1
-    "#);
+    "#,
+    );
 
-    let rows = conn.query(&query, &[&id]).context("failed to retrieve FSM")?;
+    let rows = conn
+        .query(&query, &[&id])
+        .context("failed to retrieve FSM")?;
 
     if let Some(row) = rows.into_iter().next() {
         Ok(serde_json::from_value(row.get(0)).context("failed to deserialize FSM")?)
@@ -339,20 +353,25 @@ fn get_machine_internal<C, S>(conn: &C,
 
 /// Retrieve an action from the database, optionally locking it for
 /// the duration of some enclosing transaction.
-fn get_action<C, S>(conn: &C, id: Uuid) -> Result<(ActionStatus, S::Action)> where
+fn get_action<C, S>(conn: &C, id: Uuid) -> Result<(ActionStatus, S::Action)>
+where
     C: GenericConnection,
     S: FSM,
-    S::Action: DeserializeOwned {
-    let query = alter_for_update(true, r#"
+    S::Action: DeserializeOwned,
+{
+    let query = alter_for_update(
+        true,
+        r#"
       SELECT status, content FROM actions
       WHERE id = $1 AND fsm = $2
-    "#);
+    "#,
+    );
 
     let rows = conn.query(&query, &[&id, &S::FSM_NAME])?;
 
     if let Some(row) = rows.into_iter().next() {
-        let action = serde_json::from_value(row.get(1))
-            .context("failed to deserialize FSM action")?;
+        let action =
+            serde_json::from_value(row.get(1)).context("failed to deserialize FSM action")?;
         Ok((row.get(0), action))
     } else {
         Err(ErrorKind::ActionNotFound(id).into())
@@ -360,13 +379,17 @@ fn get_action<C, S>(conn: &C, id: Uuid) -> Result<(ActionStatus, S::Action)> whe
 }
 
 /// Update the status of an action after an attempt to run it.
-fn update_action_status<C, S>(conn: &C,
-                              id: Uuid,
-                              status: ActionStatus,
-                              error: Option<String>,
-                              _fsm: PhantomData<S>) -> Result<()> where
+fn update_action_status<C, S>(
+    conn: &C,
+    id: Uuid,
+    status: ActionStatus,
+    error: Option<String>,
+    _fsm: PhantomData<S>,
+) -> Result<()>
+where
     C: GenericConnection,
-    S: FSM {
+    S: FSM,
+{
     let query = r#"
       UPDATE actions SET status = $1, error = $2
       WHERE id = $3 AND fsm = $4
@@ -389,10 +412,16 @@ fn update_action_status<C, S>(conn: &C,
 /// panic), the error will be persisted. Should it fail by panicking
 /// (which developers should never do explicitly in action
 /// interpreters) its status will not be changed.
-fn run_action<S>(tx: Transaction, id: Uuid, state: &S::State, _fsm: PhantomData<S>)
-                 -> Result<Vec<S::Event>> where
+fn run_action<S>(
+    tx: Transaction,
+    id: Uuid,
+    state: &S::State,
+    _fsm: PhantomData<S>,
+) -> Result<Vec<S::Event>>
+where
     S: FSM,
-    S::Action: DeserializeOwned {
+    S::Action: DeserializeOwned,
+{
     let (status, action) = get_action::<Transaction, S>(&tx, id)?;
 
     let result = match status {
@@ -401,29 +430,25 @@ fn run_action<S>(tx: Transaction, id: Uuid, state: &S::State, _fsm: PhantomData<
                 // If the action succeeded, update its status to
                 // completed and return the created events.
                 Ok(events) => {
-                    update_action_status(
-                        &tx, id, ActionStatus::Completed, None, PhantomData::<S>
-                    )?;
+                    update_action_status(&tx, id, ActionStatus::Completed, None, PhantomData::<S>)?;
                     events
-                },
+                }
 
                 // If the action failed, persist the debug message and
                 // return nothing.
                 Err(err) => {
                     let msg = Some(format!("{:?}", err));
-                    update_action_status(
-                        &tx, id, ActionStatus::Failed, msg, PhantomData::<S>
-                    )?;
+                    update_action_status(&tx, id, ActionStatus::Failed, msg, PhantomData::<S>)?;
                     vec![]
-                },
+                }
             }
-        },
+        }
 
         _ => {
             // TODO: Currently only pending actions are run because
             // retryable actions are not yet implemented.
             vec![]
-        },
+        }
     };
 
     tx.commit().context("failed to commit transaction")?;
diff --git a/users/tazjin/finito/finito-postgres/src/tests.rs b/users/tazjin/finito/finito-postgres/src/tests.rs
index b1b5821be3..dd270c3875 100644
--- a/users/tazjin/finito/finito-postgres/src/tests.rs
+++ b/users/tazjin/finito/finito-postgres/src/tests.rs
@@ -16,7 +16,11 @@ fn test_insert_machine() {
     let door = insert_machine(&conn, initial).expect("Failed to insert door");
     let result = get_machine(&conn, &door, false).expect("Failed to fetch door");
 
-    assert_eq!(result, DoorState::Opened, "Inserted door state should match");
+    assert_eq!(
+        result,
+        DoorState::Opened,
+        "Inserted door state should match"
+    );
 }
 
 #[test]
@@ -41,7 +45,10 @@ fn test_advance() {
     }
 
     let result = get_machine(&conn, &door, false).expect("Failed to fetch door");
-    let expected = DoorState::Locked { code: 4567, attempts: 2 };
+    let expected = DoorState::Locked {
+        code: 4567,
+        attempts: 2,
+    };
 
     assert_eq!(result, expected, "Advanced door state should match");
 }
diff --git a/users/tazjin/generator-example/.gitignore b/users/tazjin/generator-example/.gitignore
new file mode 100644
index 0000000000..ea8c4bf7f3
--- /dev/null
+++ b/users/tazjin/generator-example/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/users/tazjin/generator-example/Cargo.lock b/users/tazjin/generator-example/Cargo.lock
new file mode 100644
index 0000000000..a6f25ee394
--- /dev/null
+++ b/users/tazjin/generator-example/Cargo.lock
@@ -0,0 +1,124 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "genawaiter"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0"
+dependencies = [
+ "genawaiter-macro",
+ "genawaiter-proc-macro",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "genawaiter-macro"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc"
+
+[[package]]
+name = "genawaiter-proc-macro"
+version = "0.99.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784f84eebc366e15251c4a8c3acee82a6a6f427949776ecb88377362a9621738"
+dependencies = [
+ "proc-macro-error",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "generator-example"
+version = "0.1.0"
+dependencies = [
+ "genawaiter",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "syn-mid",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.20+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn-mid"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baa8e7560a164edb1621a55d18a0c59abf49d360f47aa7b821061dd7eea7fac9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
diff --git a/users/riking/adventofcode-2020/day01/Cargo.toml b/users/tazjin/generator-example/Cargo.toml
index d90ab548bb..faf313973f 100644
--- a/users/riking/adventofcode-2020/day01/Cargo.toml
+++ b/users/tazjin/generator-example/Cargo.toml
@@ -1,10 +1,9 @@
 [package]
-name = "day01"
+name = "generator-example"
 version = "0.1.0"
-authors = ["Kane York <kanepyork@gmail.com>"]
-edition = "2018"
+edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-anyhow = "1.0.34"
+genawaiter = "0.99.1"
diff --git a/users/tazjin/generator-example/README.md b/users/tazjin/generator-example/README.md
new file mode 100644
index 0000000000..0bec13ee9a
--- /dev/null
+++ b/users/tazjin/generator-example/README.md
@@ -0,0 +1,11 @@
+generator-example
+=================
+
+This is an experiment with the [`genawaiter`][] crate, to see if it
+could be suitable for dealing with the execution flattening problem in
+Tvix.
+
+It constructs a dummy example that is similar to some of the problems
+we have in Tvix that require generator-like thunk forcing.
+
+[`genawaiter`]: https://docs.rs/genawaiter/latest/genawaiter/index.html
diff --git a/users/tazjin/generator-example/src/main.rs b/users/tazjin/generator-example/src/main.rs
new file mode 100644
index 0000000000..4aa931caf8
--- /dev/null
+++ b/users/tazjin/generator-example/src/main.rs
@@ -0,0 +1,115 @@
+use genawaiter::rc::{Co, Gen};
+use std::cell::RefCell;
+use std::future::Future;
+use std::pin::Pin;
+use std::rc::Rc;
+
+#[derive(Debug)]
+enum ValueRepr {
+    Int(i64),
+    Thunk((i64, i64)),
+}
+
+#[derive(Clone, Debug)]
+struct Value(Rc<RefCell<ValueRepr>>);
+
+impl Value {
+    fn force(&self) {
+        let mut inner = self.0.borrow_mut();
+        match *inner {
+            ValueRepr::Int(_) => return,
+            ValueRepr::Thunk((a, b)) => {
+                *inner = ValueRepr::Int(a + b);
+            }
+        }
+    }
+
+    fn is_forced(&self) -> bool {
+        matches!(*self.0.borrow(), ValueRepr::Int(_))
+    }
+
+    fn int(&self) -> i64 {
+        match *self.0.borrow() {
+            ValueRepr::Int(i) => i,
+            ValueRepr::Thunk(_) => panic!("unforced thunk!"),
+        }
+    }
+}
+
+impl From<i64> for Value {
+    fn from(value: i64) -> Self {
+        Value(Rc::new(RefCell::new(ValueRepr::Int(value))))
+    }
+}
+
+impl From<(i64, i64)> for Value {
+    fn from(value: (i64, i64)) -> Self {
+        Value(Rc::new(RefCell::new(ValueRepr::Thunk(value))))
+    }
+}
+
+async fn list_maker(values: Vec<Value>, co: Co<Value>) -> Vec<i64> {
+    let mut output: Vec<i64> = vec![];
+
+    for value in values {
+        if !value.is_forced() {
+            co.yield_(value.clone()).await;
+        }
+
+        output.push(value.int());
+    }
+
+    output
+}
+
+async fn list_reverser(values: Vec<Value>, co: Co<Value>) -> Vec<i64> {
+    let mut output = list_maker(values, co).await;
+    output.reverse();
+    output
+}
+
+struct Frame {
+    gen: Gen<Value, (), Pin<Box<dyn Future<Output = Vec<i64>>>>>,
+}
+
+fn pin_future(
+    f: impl Future<Output = Vec<i64>> + 'static,
+) -> Pin<Box<dyn Future<Output = Vec<i64>>>> {
+    Box::pin(f)
+}
+
+fn main() {
+    let mut frames: Vec<Frame> = vec![];
+
+    let values: Vec<Value> = vec![
+        42.into(),
+        (12, 54).into(),
+        4.into(),
+        (40, 2).into(),
+        2.into(),
+    ];
+    let second = values.clone();
+
+    frames.push(Frame {
+        gen: Gen::new(|co| pin_future(list_maker(values, co))),
+    });
+
+    frames.push(Frame {
+        gen: Gen::new(|co| pin_future(list_reverser(second, co))),
+    });
+
+    for (idx, mut frame) in frames.into_iter().enumerate() {
+        loop {
+            match frame.gen.resume() {
+                genawaiter::GeneratorState::Yielded(val) => {
+                    println!("yielded {:?} in frame {}", val, idx);
+                    val.force();
+                }
+                genawaiter::GeneratorState::Complete(list) => {
+                    println!("result {}: {:?}", idx, list);
+                    break;
+                }
+            }
+        }
+    }
+}
diff --git a/users/tazjin/gio-list-apps/.gitignore b/users/tazjin/gio-list-apps/.gitignore
new file mode 100644
index 0000000000..2f7896d1d1
--- /dev/null
+++ b/users/tazjin/gio-list-apps/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/tazjin/gio-list-apps/Cargo.lock b/users/tazjin/gio-list-apps/Cargo.lock
new file mode 100644
index 0000000000..b475b35a6c
--- /dev/null
+++ b/users/tazjin/gio-list-apps/Cargo.lock
@@ -0,0 +1,616 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
+[[package]]
+name = "cfg-expr"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "ctor"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
+dependencies = [
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "emacs"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6797a940189d353de79bec32abe717aeeecd79a08236e84404c888354e040665"
+dependencies = [
+ "anyhow",
+ "ctor",
+ "emacs-macros",
+ "emacs_module",
+ "once_cell",
+ "rustc_version",
+ "thiserror",
+]
+
+[[package]]
+name = "emacs-macros"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69656fdfe7c2608b87164964db848b5c3795de7302e3130cce7131552c6be161"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "emacs_module"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3067bc974045ed2c6db333bd4fc30d3bdaafa6421a9a889fa7b2826b6f7f2fa"
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-core",
+ "futures-macro",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gio"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7884cba6b1c5db1607d970cadf44b14a43913d42bc68766eea6a5e2fe0891524"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-util",
+ "gio-sys",
+ "glib",
+ "libc",
+ "once_cell",
+ "pin-project-lite",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "gio-list-apps"
+version = "0.1.0"
+dependencies = [
+ "emacs",
+ "gio",
+]
+
+[[package]]
+name = "gio-sys"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+ "winapi",
+]
+
+[[package]]
+name = "glib"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "331156127e8166dd815cf8d2db3a5beb492610c716c03ee6db4f2d07092af0a7"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "futures-util",
+ "gio-sys",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "memchr",
+ "once_cell",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "179643c50bf28d20d2f6eacd2531a88f2f5d9747dd0b86b8af1e8bb5dd0de3c0"
+dependencies = [
+ "heck",
+ "proc-macro-crate",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898"
+dependencies = [
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "indexmap"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
+dependencies = [
+ "once_cell",
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
+
+[[package]]
+name = "strsim"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a"
+
+[[package]]
+name = "thiserror"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "toml"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "version-compare"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "winnow"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
+dependencies = [
+ "memchr",
+]
diff --git a/users/tazjin/gio-list-apps/Cargo.toml b/users/tazjin/gio-list-apps/Cargo.toml
new file mode 100644
index 0000000000..eb62d1fcaf
--- /dev/null
+++ b/users/tazjin/gio-list-apps/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "gio-list-apps"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+emacs = "0.18.0"
+gio = "0.18.1"
diff --git a/users/tazjin/gio-list-apps/default.nix b/users/tazjin/gio-list-apps/default.nix
new file mode 100644
index 0000000000..c63f4dd487
--- /dev/null
+++ b/users/tazjin/gio-list-apps/default.nix
@@ -0,0 +1,14 @@
+{ depot, pkgs, lib, ... }:
+
+pkgs.rustPlatform.buildRustPackage {
+  name = "gio-list-apps";
+  src = lib.cleanSource ./.;
+  cargoLock.lockFile = ./Cargo.lock;
+  nativeBuildInputs = [ pkgs.pkg-config ];
+  buildInputs = [ pkgs.gtk3 depot.users.tazjin.emacs.emacs ];
+
+  postInstall = ''
+    mkdir -p $out/share/emacs/site-lisp
+    ln -s $out/lib/libgio_list_apps.so $out/share/emacs/site-lisp/gio-list-apps.so
+  '';
+}
diff --git a/users/tazjin/gio-list-apps/src/lib.rs b/users/tazjin/gio-list-apps/src/lib.rs
new file mode 100644
index 0000000000..55eb8dc0be
--- /dev/null
+++ b/users/tazjin/gio-list-apps/src/lib.rs
@@ -0,0 +1,31 @@
+use emacs::{defun, Env, IntoLisp, Result, Value};
+use gio::traits::AppInfoExt;
+use gio::AppInfo;
+
+emacs::plugin_is_GPL_compatible!();
+
+#[emacs::module(defun_prefix = "taz", mod_in_name = false)]
+fn init(_: &Env) -> Result<()> {
+    Ok(())
+}
+
+/// Returns an alist of the currently available XDG applications (through their
+/// `.desktop' shortcuts), and the command line parameters needed to start them.
+///
+/// Hidden applications or applications without specified command-line
+/// parameters are not included.
+#[defun]
+fn list_xdg_apps(env: &Env) -> Result<Value> {
+    let mut visible_apps: Vec<Value> = vec![];
+
+    for app in AppInfo::all().into_iter().filter(AppInfo::should_show) {
+        if let Some(cmd) = app
+            .commandline()
+            .and_then(|p| Some(p.to_str()?.to_string()))
+        {
+            visible_apps.push(env.cons(app.name().as_str().into_lisp(env)?, cmd.into_lisp(env)?)?);
+        }
+    }
+
+    env.list(&visible_apps)
+}
diff --git a/users/tazjin/hanebuschtag.txt b/users/tazjin/hanebuschtag.txt
index fc5c065220..daeb41c9aa 100644
--- a/users/tazjin/hanebuschtag.txt
+++ b/users/tazjin/hanebuschtag.txt
@@ -5,6 +5,7 @@ burwurgurken
 burwurka
 gaschnurzel
 gezwurkel
+grunzelgewunzel
 gurzelschnurzelgurke
 hanemazurka
 hanemazurkelgurkel
@@ -19,6 +20,8 @@ hanewurkini
 hanewurzeln
 ronzelschlawonzel
 ronzelwonzel
+schabernackel
+schabernackensteak
 schlagurkelwini
 schlaraffenwurburzel
 schlawiburschnurschlakini
diff --git a/users/tazjin/home/khamovnik.nix b/users/tazjin/home/khamovnik.nix
new file mode 100644
index 0000000000..6bac67eb1c
--- /dev/null
+++ b/users/tazjin/home/khamovnik.nix
@@ -0,0 +1,10 @@
+# Home manage configuration for zamalek.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+{
+  imports = [
+    depot.users.tazjin.home.shared
+  ];
+}
diff --git a/users/tazjin/home/persistence.nix b/users/tazjin/home/persistence.nix
new file mode 100644
index 0000000000..9ea5ca8eb9
--- /dev/null
+++ b/users/tazjin/home/persistence.nix
@@ -0,0 +1,42 @@
+# Persistence configuration for machines with throw-away setups.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+{
+  imports = [ (depot.third_party.sources.impermanence + "/home-manager.nix") ];
+
+  home.persistence."/persist/tazjin/home" = {
+    allowOther = true;
+
+    directories = [
+      ".cargo"
+      ".config/audacity"
+      ".config/chromium"
+      ".config/google-chrome"
+      ".config/quassel-irc.org"
+      ".config/syncthing"
+      ".config/unity3d"
+      ".electrum"
+      ".gnupg"
+      ".local/share/audacity"
+      ".local/share/direnv"
+      ".local/share/fish"
+      ".local/share/keyrings"
+      ".local/share/zoxide"
+      ".mozilla/firefox"
+      ".password-store"
+      ".rustup"
+      ".ssh"
+      ".steam"
+      ".telega"
+      ".thunderbird"
+      "go"
+      "mail"
+    ];
+
+    files = [
+      ".notmuch-config"
+    ];
+  };
+}
diff --git a/users/tazjin/home/shared.nix b/users/tazjin/home/shared.nix
new file mode 100644
index 0000000000..38d8add4ac
--- /dev/null
+++ b/users/tazjin/home/shared.nix
@@ -0,0 +1,91 @@
+# Shared home configuration for all machines.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+
+let
+  # URL handler to open `tg://` URLs in telega.el
+  telega-launcher = pkgs.writeShellScriptBin "telega-launcher" ''
+    echo "Opening ''${1} in telega.el ..."
+    ${depot.users.tazjin.emacs.emacs}/bin/emacsclient -e "(telega-browse-url \"''${1}\")"
+  '';
+in
+{
+  home.activation.screenshots = lib.hm.dag.entryAnywhere ''
+    $DRY_RUN_CMD mkdir -p $HOME/screenshots
+  '';
+
+  programs.git = {
+    enable = true;
+    userName = "Vincent Ambo";
+    userEmail = "mail@tazj.in";
+    extraConfig = {
+      pull.rebase = true;
+      init.defaultBranch = "canon";
+      safe.directory = [ "/depot" ];
+    };
+  };
+
+  programs.fish = {
+    enable = true;
+    interactiveShellInit = ''
+      # emacs vterm integration
+      source (find '${pkgs.emacsPackages.vterm}' -name 'emacs-vterm.fish')
+
+      # z
+      ${pkgs.zoxide}/bin/zoxide init fish | source
+    '';
+  };
+
+  services.screen-locker = {
+    enable = true;
+    inactiveInterval = 10; # minutes
+    lockCmd = "${depot.users.tazjin.screenLock}/bin/tazjin-screen-lock";
+  };
+
+  home.packages = [ telega-launcher ];
+
+  xdg.desktopEntries.telega-launcher = {
+    name = "Telega Launcher";
+    exec = "${telega-launcher}/bin/telega-launcher";
+    terminal = false;
+    mimeType = [ "x-scheme-handler/tg" ];
+  };
+
+  xdg.mimeApps = {
+    enable = true;
+    defaultApplications = {
+      "x-scheme-handler/tg" = [ "telega-launcher.desktop" ];
+      "text/html" = [ "firefox.desktop" ];
+      "x-scheme-handler/http" = [ "firefox.desktop" ];
+      "x-scheme-handler/https" = [ "firefox.desktop" ];
+      "x-scheme-handler/about" = [ "firefox.desktop" ];
+      "x-scheme-handler/unknown" = [ "firefox.desktop" ];
+    };
+  };
+
+  services.picom = {
+    enable = true;
+    vSync = true;
+    backend = "glx";
+  };
+
+  services.syncthing.enable = true;
+
+  # Enable the dunst notification daemon, but force the
+  # configuration file separately instead of going via the strange
+  # Nix->dunstrc encoding route.
+  services.dunst.enable = true;
+  xdg.configFile."dunst/dunstrc" = {
+    source = depot.users.tazjin.dotfiles.dunstrc;
+    onChange = ''
+      ${pkgs.procps}/bin/pkill -u "$USER" ''${VERBOSE+-e} dunst || true
+    '';
+  };
+
+  systemd.user.startServices = true;
+
+  # Previous default version, see https://github.com/nix-community/home-manager/blob/master/docs/release-notes/rl-2211.adoc
+  home.stateVersion = "18.09";
+}
diff --git a/users/tazjin/home/tverskoy.nix b/users/tazjin/home/tverskoy.nix
new file mode 100644
index 0000000000..6f1116340c
--- /dev/null
+++ b/users/tazjin/home/tverskoy.nix
@@ -0,0 +1,18 @@
+# Home manage configuration for tverskoy.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+{
+  imports = [
+    depot.users.tazjin.home.shared
+    depot.users.tazjin.home.persistence
+  ];
+
+  home.persistence."/persist/tazjin/home" = {
+    directories = [
+      ".config/spotify"
+      ".local/share/Steam"
+    ];
+  };
+}
diff --git a/users/tazjin/home/zamalek.nix b/users/tazjin/home/zamalek.nix
new file mode 100644
index 0000000000..d24de945bb
--- /dev/null
+++ b/users/tazjin/home/zamalek.nix
@@ -0,0 +1,11 @@
+# Home manage configuration for zamalek.
+
+{ depot, pkgs, ... }: # readTree
+{ config, lib, ... }: # home-manager
+
+{
+  imports = [
+    depot.users.tazjin.home.shared
+    depot.users.tazjin.home.persistence
+  ];
+}
diff --git a/users/tazjin/homepage/default.nix b/users/tazjin/homepage/default.nix
index 2ce1cf6322..b46f9d4917 100644
--- a/users/tazjin/homepage/default.nix
+++ b/users/tazjin/homepage/default.nix
@@ -12,16 +12,21 @@ with nix.yants;
 
 let
   inherit (builtins) readFile replaceStrings sort;
-  inherit (pkgs) writeFile runCommandNoCC;
+  inherit (pkgs) writeFile runCommand;
 
   # The different types of entries on the homepage.
-  entryClass = enum "entryClass" [ "blog" "project" "misc" ];
+  entryClass = enum "entryClass" [
+    "blog"
+    "project"
+    "note"
+    "misc"
+  ];
 
   # The definition of a single entry.
   entry = struct "entry" {
     class = entryClass;
-    title = string;
-    url = string;
+    title = option string;
+    url = option string;
     date = int; # epoch
     description = option string;
   };
@@ -33,28 +38,42 @@ let
     title = post.title;
     url = "/blog/${post.key}";
     date = post.date;
+    description = post.description or "Blog post from ${formatDate post.date}";
   });
 
-  formatDate = defun [ int string ] (date: readFile (runCommandNoCC "date" {} ''
-    date --date='@${toString date}' '+%Y-%m-%d' > $out
+  formatDate = defun [ int string ] (date: readFile (runCommand "date" { } ''
+    date --date='@${toString date}' '+%Y-%m-%d' | tr -d '\n' > $out
   ''));
 
-  formatEntryDate = defun [ entry string ] (entry: entryClass.match entry.class {
-    blog = "Blog post from ${formatDate entry.date}";
-    project = "Project from ${formatDate entry.date}";
-    misc = "Posted on ${formatDate entry.date}";
-  });
+  entryUrl = defun [ entry string ] (entry:
+    if entry.class == "note"
+    then "#${toString entry.date}"
+    else entry.url
+  );
+
+  hasDescription = defun [ entry bool ] (entry:
+    ((entry ? description) && (entry.description != null))
+  );
+
+  entryTitle = defun [ entry string ] (entry:
+    let
+      optionalColon = lib.optionalString (hasDescription entry) ":";
+      titleText =
+        if (!(entry ? title) && (entry.class == "note"))
+        then "[${formatDate entry.date}]"
+        else lib.optionalString (entry ? title) ((escape entry.title) + optionalColon);
+    in
+    lib.optionalString (titleText != "")
+      ''<span class="entry-title ${entry.class}">${titleText}</span>''
+  );
 
   entryToDiv = defun [ entry string ] (entry: ''
-    <a href="${entry.url}" class="entry ${entry.class}">
-      <div>
-        <p class="entry-title">${escape entry.title}</p>
-        ${
-          lib.optionalString ((entry ? description) && (entry.description != null))
-          "<p class=\"entry-description\">${escape entry.description}</p>"
-        }
-        <p class="entry-date">${formatEntryDate entry}</p>
-      </div>
+    <a href="${entryUrl entry}" id="${toString entry.date}" class="entry">
+      ${entryTitle entry}
+      ${
+        lib.optionalString (hasDescription entry)
+        "<span class=\"entry-description\">${escape entry.description}</span>"
+      }
     </a>
   '');
 
@@ -67,7 +86,8 @@ let
   pageEntries = import ./entries.nix;
   homepage = index ((map postToEntry users.tazjin.blog.posts) ++ pageEntries);
   atomFeed = import ./feed.nix (args // { inherit entry pageEntries; });
-in runCommandNoCC "website" {} ''
+in
+runCommand "website" { } ''
   mkdir $out
   cp ${homepage} $out/index.html
   cp ${atomFeed} $out/feed.atom
diff --git a/users/tazjin/homepage/entries.nix b/users/tazjin/homepage/entries.nix
index 1e2b0b03df..0e98c073ef 100644
--- a/users/tazjin/homepage/entries.nix
+++ b/users/tazjin/homepage/entries.nix
@@ -1,12 +1,52 @@
+let
+  note = date: description: {
+    class = "note";
+    inherit description date;
+  };
+in
 [
   {
+    class = "project";
+    title = "VolgaSprint - Nix hacking in Kazan";
+    url = "https://volgasprint.org/";
+    date = 1712307024;
+    description = ''
+      Hacking on Nix projects for a week in Kazan, Russia, in August
+      2024. Come join us!
+    '';
+  }
+  {
+    class = "misc";
+    title = "@tazlog on Telegram";
+    url = "https://t.me/tazlog";
+    date = 1643321164;
+    description = ''
+      My Telegram channel with occasional random life updates and musings.
+    '';
+  }
+  {
+    class = "project";
+    title = "Ship It! #37";
+    url = "https://changelog.com/shipit/37";
+    date = 1641819600;
+    description = ''
+      Podcast episode about TVL, Nix, monorepos and all sorts of related things.
+    '';
+  }
+  {
+    class = "project";
+    title = "Tvix";
+    url = "https://tvl.fyi/blog/rewriting-nix";
+    date = 1638381387;
+    description = "TVL is rewriting Nix with funding from NLNet.";
+  }
+  {
     class = "misc";
     title = "Interview with Joscha Bach";
     url = "https://www.youtube.com/watch?v=P-2P3MSZrBM";
     date = 1594594800;
     description = ''
-      A fascinating, mind-bending interview by Lex Fridman with Joscha
-      Bach about the Nature of the Universe.
+      Mind-bending discussion with philosopher Joscha Bach.
     '';
   }
   {
@@ -14,7 +54,7 @@
     title = "The Virus Lounge";
     url = "https://tvl.fyi";
     date = 1587435629;
-    description = "A daily social video call in these trying pandemic times. Join us!";
+    description = "A community around Nix, monorepos, build tooling and more!";
   }
   {
     class = "project";
@@ -42,7 +82,7 @@
     title = "dottime";
     url = "https://dotti.me/";
     date = 1560898800;
-    description = "A universal convention for conveying time (by edef <3)";
+    description = "A universal convention for conveying time";
   }
   {
     class = "project";
@@ -57,18 +97,63 @@
     url = "https://principiadiscordia.com/book/1.php";
     date = 1495494000;
     description = ''
-      The Principia is a short book I read as a child, and didn't
-      understand until much later. It shaped much of my world view.
+      A short book about everything that everyone should read.
     '';
   }
   {
     class = "misc";
-    title = "This Week in Virology";
-    url = "http://www.microbe.tv/twiv/";
-    date = 1585517557;
-    description = ''
-      Podcast with high-quality information about virology,
-      epidemiology and so on. Highly relevant to COVID19.
-    '';
+    title = "Nix โ€” ะฝะต ั‚ะพะปัŒะบะพ ะฟะฐะบะตั‚ะฝั‹ะน ะผะตะฝะตะดะถะตั€";
+    date = 1663923600;
+    url = "https://www.youtube.com/watch?v=0Lhahzs-Wos";
+    description = "ะ”ะฒัƒั…ั‡ะฐัะพะฒะพะน (!) ั€ะฐะทะณะพะฒะพั€ ั ะฒะฒะตะดะตะฝะธะตะผ ะฒ Nix, NixOS ะธ ั‚ะฐะบ ะดะฐะปะตะต";
+  }
+  {
+    class = "project";
+    title = "yandex-cloud-rs";
+    date = 1650877200;
+    url = "https://docs.rs/yandex-cloud";
+    description = "ะŸั€ะพัั‚ะพะน SDK ะฝะฐ Rust ะดะปั ั€ะฐะฑะพั‚ั‹ ั API Yandex Cloud.";
+  }
+  {
+    class = "project";
+    title = "nix-1p";
+    date = 1564650000;
+    url = "https://code.tvl.fyi/about/nix/nix-1p";
+    description = "A (more or less) one-page introduction to the Nix language.";
+  }
+  {
+    class = "misc";
+    title = "ะกั‚ะฐะฒะธะผ NixOS!";
+    date = 1678784400;
+    url = "https://progmsk.timepad.ru/event/2358560/";
+    description = "ะ’ัั‚ั€ะตั‡ะฐ ะฒ undef.space ะดะปั ะฟะพะผะพั‰ะธ ะฒ ะฝะฐั‡ะฐะปะต ั€ะฐะฑะพั‚ั‹ ั Nix/NixOS";
+  }
+  {
+    class = "misc";
+    title = "Tvix - September '22";
+    date = 1662973200;
+    url = "https://tvl.fyi/blog/tvix-status-september-22";
+    description = "Tvix update blog post over on TVL";
+  }
+  {
+    class = "project";
+    title = "Tvixbolt";
+    date = 1667293200;
+    url = "https://bolt.tvix.dev/";
+    description = "In-browser language evaluator for Nix, based on Tvix";
+  }
+  {
+    class = "project";
+    title = "ะžะžะž ะขะ’ะ›";
+    date = 1609491600;
+    url = "https://tvl.su/ru/";
+    description = "ะžั„ะธั†ะธะฐะปัŒะฝั‹ะน ัะฐะนั‚ ะผะพะตะน ะบะพะผะฟะฐะฝะธะธ ะฟะพ IT-ะบะพะฝัะฐะปั‚ะธะฝะณัƒ.";
   }
+
+  # Notes.
+  (note 1676106000 "If you have a Huawei device that sometimes struggles on public Wi-Fi networks, try enabling MAC-address randomisation. Huawei devices often get pushed onto management networks!")
+  (note 1686868637 "I moved some of my pages (including this one) to a machine in my flat in Moscow. If you end up having access trouble because your ISP blocks Russian resources, please let me know.")
+  (note 1686868636 "Protip: Use the Reddit blackout to click the 'Logout' button, and never come back.")
+  (note 1486550941 "โ†“ I no longer recommend people to use this. Generate your configuration from a language like Nix instead.")
+  (note 1576800001 "โ†“ No longer just my projects, it's all of TVL! Go check it out.")
 ]
diff --git a/users/tazjin/homepage/feed.nix b/users/tazjin/homepage/feed.nix
index 984b1c2950..8043d7ff30 100644
--- a/users/tazjin/homepage/feed.nix
+++ b/users/tazjin/homepage/feed.nix
@@ -4,25 +4,11 @@
 with depot.nix.yants;
 
 let
-  inherit (builtins) map readFile sort foldl';
+  inherit (builtins) filter map readFile;
   inherit (lib) max singleton;
   inherit (pkgs) writeText;
-  inherit (depot.nix) renderMarkdown;
   inherit (depot.web) blog atom-feed;
 
-  postToEntry = defun [ blog.post atom-feed.entry ] (post: rec {
-    id = "https://tazj.in/blog/${post.key}";
-    title = post.title;
-    content = readFile (renderMarkdown post.content);
-    published = post.date;
-    updated = post.updated or post.date;
-
-    links = singleton {
-      rel = "alternate";
-      href = id;
-    };
-  });
-
   pageEntryToEntry = defun [ entry atom-feed.entry ] (e: {
     id = "tazjin:${e.class}:${toString e.date}";
     updated = e.date;
@@ -36,16 +22,13 @@ let
     };
   });
 
-  allEntries = (map postToEntry depot.users.tazjin.blog.posts)
-             ++ (map pageEntryToEntry pageEntries);
-
-  mostRecentlyUpdated = foldl' max 0 (map (e: e.updated) allEntries);
+  allEntries = (with depot.users.tazjin.blog; map (blog.toFeedEntry config) posts)
+    ++ (map pageEntryToEntry (filter (e: e.class != "note") pageEntries));
 
   feed = {
     id = "https://tazj.in/";
     title = "tazjin's interblag";
     subtitle = "my posts, projects and other interesting things";
-    updated = mostRecentlyUpdated;
     rights = "ยฉ 2020 tazjin";
     authors = [ "tazjin" ];
 
@@ -54,6 +37,7 @@ let
       href = "https://tazjin/feed.atom";
     };
 
-    entries = sort (a: b: a.published > b.published) allEntries;
+    entries = allEntries;
   };
-in writeText "feed.atom" (atom-feed.renderFeed feed)
+in
+writeText "feed.atom" (atom-feed.renderFeed feed)
diff --git a/users/tazjin/homepage/header.html b/users/tazjin/homepage/header.html
index 2a5aed4fed..320b5ded8c 100644
--- a/users/tazjin/homepage/header.html
+++ b/users/tazjin/homepage/header.html
@@ -3,6 +3,7 @@
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <meta name="description" content="tazjin&#39;s blog">
   <link rel="stylesheet" type="text/css" href="static/tvl.css" media="all">
+  <link rel="stylesheet" type="text/css" href="static/tazjin.css" media="all">
   <link rel="icon" type="image/webp" href="/static/favicon.webp">
   <link rel="alternate" type="application/atom+xml" href="/feed.atom">
   <title>tazjin&#39;s interblag</title>
@@ -15,19 +16,14 @@
     <hr>
   </header>
   <div class="introduction">
-    <p>Hello, illuminated visitor.</p>
     <p>
-      I'm tazjin. Usually you can find
-      me <a class="dark-link" href="https://git.tazj.in/about">programming computers</a>
-      using tools such as <a class="dark-link" href="https://nixos.org/nix">Nix</a>
-      and <a class="dark-link" href="https://www.gnu.org/software/emacs/">Emacs</a>.
-    </p>
-    <p>
-      Below is a collection of
+      Below are some of
       my <span class="project">projects</span>, <span class="blog">blog
-      posts</span> and some <span class="misc">random things</span> by
-      me or others. If you'd like to get in touch about anything, send
-      me a mail at mail@[this domain] or ping me on IRC.
+      posts</span>, <span class="note">notes</span> and some
+      other <span class="misc">random things</span>. If you'd like to
+      get in touch, email me at mail@[this domain] or ping me
+      on <a class="dark-link" href="https://tvl.fyi">TVL</a> IRC.
     </p>
+    <hr>
   </div>
   <div class="entry-container">
diff --git a/users/tazjin/homepage/static/tazjin.css b/users/tazjin/homepage/static/tazjin.css
new file mode 100644
index 0000000000..f921b562ee
--- /dev/null
+++ b/users/tazjin/homepage/static/tazjin.css
@@ -0,0 +1,57 @@
+/* Homepage styling */
+
+.dark {
+    background-color: #181818;
+    color: #e4e4ef;
+}
+
+.dark-link, .interblag-title {
+    color: #96a6c8;
+}
+
+
+.interblag-title {
+    text-decoration: none;
+}
+
+.entry-container {
+    display: flex;
+    flex-direction: column;
+    flex-wrap: nowrap;
+    justify-content: flex-start;
+}
+
+.entry {
+    margin-top: 5px;
+    margin-bottom: 5px;
+    padding-left: 5px;
+    text-decoration: none;
+}
+
+.entry:nth-child(odd) {
+    background: #282828;
+}
+
+.entry-description {
+    color: #e4e4ef;
+}
+
+.misc {
+    color: #73c936;
+    border-color: #73c936;
+}
+
+.blog {
+    color: #268bd2;
+    border-color: #268bd2;
+}
+
+.project {
+    color: #ff4f58;
+    border-color: #ff4f58;
+}
+
+.note {
+    color: #ffdd33;
+    border-color: #ffdd33;
+}
diff --git a/users/tazjin/keys.nix b/users/tazjin/keys.nix
deleted file mode 100644
index 6ad20e1150..0000000000
--- a/users/tazjin/keys.nix
+++ /dev/null
@@ -1,10 +0,0 @@
-# My SSH public keys
-{ ... }:
-
-let withAll = keys: keys // { all = builtins.attrValues keys; };
-in withAll {
-  # frog = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKMZzRdcrHTuCPoaFy36MPr5IW/hnImlse/OBOn6udL/ tazjin@frog";
-  vauxhall = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHs+9QfZTD5qGsBQaWqp5whmXJ9qy/m9swE2M9QBaIQVoIYGemq3HXTzrQ6XekwudJCltP4EpM7h/Qc+Or309Yw=";
-  s10e = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDf7CNlYoauHcSYsMNnCZt5h9QSYH/7keYkg8g3hT32+";
-  tverskoy = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBAWvA3RpXpMAqruUbB+eVgvvHCzhs5R9khFRza3YSLeFiIqOxVVgyhzW/BnCSD9t/5JrqRdJIGQLnkQU9m4REhUAAAAEc3NoOg== tazjin@tverskoy";
-}
diff --git a/users/tazjin/keys/default.nix b/users/tazjin/keys/default.nix
new file mode 100644
index 0000000000..16b232b094
--- /dev/null
+++ b/users/tazjin/keys/default.nix
@@ -0,0 +1,12 @@
+# My SSH public keys
+{ ... }:
+
+let withAll = keys: keys // { all = builtins.attrValues keys; };
+in withAll {
+  tverskoy = "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBAWvA3RpXpMAqruUbB+eVgvvHCzhs5R9khFRza3YSLeFiIqOxVVgyhzW/BnCSD9t/5JrqRdJIGQLnkQU9m4REhUAAAAEc3NoOg== tazjin@tverskoy";
+  tverskoy_ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM1fGWz/gsq+ZeZXjvUrV+pBlanw1c3zJ9kLTax9FWQy tazjin@tverskoy";
+  zamalek_sk = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIOAw3OaPAjnC6hArGYEmBoXhPf7aZdRGlDZcSqm6gbB8AAAABHNzaDo= tazjin@zamalek";
+  zamalek_ed25519 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDBRXeb8EuecLHP0bW4zuebXp4KRnXgJTZfeVWXQ1n1R tazjin@zamalek";
+  khamovnik_yk = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPgOyR4rRM8IaVGgN2ZxGlKtd7GLYbxdRTRa3u9EhRNSkHAvRTN9sgw7mm0iPLnHChPy10anKV43vTaIm906Gm8=";
+  khamovnik_agenix = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG4YSl5+DHQR3rOoBJLQfQ840U0CrYkByMKdzu/LDxoT tazjin@khamovnik";
+}
diff --git a/users/tazjin/kinesis/README.md b/users/tazjin/kinesis/README.md
new file mode 100644
index 0000000000..7cd95a5e5f
--- /dev/null
+++ b/users/tazjin/kinesis/README.md
@@ -0,0 +1,10 @@
+Kinesis configuration
+=====================
+
+This folder backs up the configuration for my Kinesis keyboards.
+Configuration is not mutually compatible between the Advantage 2 and
+the Advantage 360, so they are stored in different folders and
+(mostly) programmed on-board.
+
+I keep these around in case I get a new keyboard and want to bootstrap
+it to behave the same way as the previous one.
diff --git a/users/tazjin/kinesis/advantage2/qwerty.txt b/users/tazjin/kinesis/advantage2/qwerty.txt
new file mode 100755
index 0000000000..624e809c22
--- /dev/null
+++ b/users/tazjin/kinesis/advantage2/qwerty.txt
@@ -0,0 +1,6 @@
+[caps]>[rwin]

+[lctrl]>[lalt]

+[delete]>[lctrl]

+[rctrl]>[rwin]

+{pup}>{-rwin}{b}{+rwin}

+{pdown}>{-rwin}{f}{+rwin}

diff --git a/users/tazjin/nittredir/background.js b/users/tazjin/nittredir/background.js
deleted file mode 100644
index 8d07f85285..0000000000
--- a/users/tazjin/nittredir/background.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/**
- * Register a URL change handler that redirects twitter.com links to nitter.net
- */
-
-chrome.webRequest.onBeforeRequest.addListener(function(details) {
-  let url = new URL(details.url);
-  return {
-    redirectUrl: ('https://nitter.net' + url.pathname)
-  };
-}, {urls: ['*://twitter.com/*'], types: ['main_frame']}, ['blocking']);
diff --git a/users/tazjin/nittredir/manifest.json b/users/tazjin/nittredir/manifest.json
deleted file mode 100644
index 4efe1a6cc2..0000000000
--- a/users/tazjin/nittredir/manifest.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "manifest_version": 2,
-  "name": "nittredir",
-  "version": "1.0",
-  "description": "Redirect twitter.com to nitter.net",
-  "background": {
-    "scripts": ["background.js"],
-    "persistent": true
-  },
-  "permissions": [
-    "webRequest",
-    "webRequestBlocking",
-    "*://twitter.com/*"
-  ]
-}
diff --git a/users/tazjin/nix.svg b/users/tazjin/nix.svg
index 4da795a436..d2ef7c81aa 100644
--- a/users/tazjin/nix.svg
+++ b/users/tazjin/nix.svg
@@ -33,7 +33,7 @@
        id="lambda-4"
        href="#lambda-path"
        visibility="visible"
-       fill="#f8f8ff" />
+       fill="#d52b1e" />
     <use
        id="lambda-5"
        transform="rotate(60,407.11155,-715.78724)"
@@ -45,6 +45,6 @@
        id="lambda-6"
        href="#lambda-path"
        visibility="visible"
-       fill="#d52b1e" />
+       fill="#f8f8ff" />
   </g>
 </svg>
diff --git a/users/tazjin/nixos/.gitignore b/users/tazjin/nixos/.gitignore
new file mode 100644
index 0000000000..212d3ad270
--- /dev/null
+++ b/users/tazjin/nixos/.gitignore
@@ -0,0 +1 @@
+local-config.nix
diff --git a/users/tazjin/nixos/camden/default.nix b/users/tazjin/nixos/camden/default.nix
index ebd4caac75..130b51dd38 100644
--- a/users/tazjin/nixos/camden/default.nix
+++ b/users/tazjin/nixos/camden/default.nix
@@ -1,7 +1,8 @@
 # This file configures camden.tazj.in, my homeserver.
 { depot, pkgs, lib, ... }:
 
-config: let
+config:
+let
   nginxRedirect = { from, to, acmeHost }: {
     serverName = from;
     useACMEHost = acmeHost;
@@ -9,22 +10,13 @@ config: let
 
     extraConfig = "return 301 https://${to}$request_uri;";
   };
-in lib.fix(self: {
-  # Disable the current ACME module and use the old one from 19.09
-  # instead, until the various regressions have been sorted out.
-  # TODO(tazjin): Remove this once the new ACME module works.
-  disabledModules = [ "security/acme.nix" ];
-  imports =
-    let oldChannel = fetchTarball {
-      # NixOS 19.09 on 2020-10-04
-      url = "https://github.com/NixOS/nixpkgs-channels/archive/75f4ba05c63be3f147bcc2f7bd4ba1f029cedcb1.tar.gz";
-      sha256 = "157c64220lf825ll4c0cxsdwg7cxqdx4z559fdp7kpz0g6p8fhhr";
-    };
-    in [
-      "${depot.path}/ops/modules/quassel.nix"
-      "${depot.path}/ops/modules/smtprelay.nix"
-      "${oldChannel}/nixos/modules/security/acme.nix"
-    ];
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+in
+lib.fix (self: {
+  imports = [
+    (mod "quassel.nix")
+    (mod "smtprelay.nix")
+  ];
 
   # camden is intended to boot unattended, despite having an encrypted
   # root partition.
@@ -37,8 +29,14 @@ in lib.fix(self: {
   boot = {
     initrd = {
       availableKernelModules = [
-        "ahci" "xhci_pci" "usbhid" "usb_storage" "sd_mod" "sdhci_pci"
-        "rtsx_usb_sdmmc" "r8169"
+        "ahci"
+        "xhci_pci"
+        "usbhid"
+        "usb_storage"
+        "sd_mod"
+        "sdhci_pci"
+        "rtsx_usb_sdmmc"
+        "r8169"
       ];
 
       kernelModules = [ "dm-snapshot" ];
@@ -56,7 +54,7 @@ in lib.fix(self: {
       efi.canTouchEfiVariables = true;
     };
 
-    cleanTmpDir = true;
+    tmp.cleanOnBoot = true;
   };
 
   fileSystems = {
@@ -76,16 +74,14 @@ in lib.fix(self: {
     };
   };
 
-  nix = {
-    maxJobs = lib.mkDefault 4;
-
-    trustedUsers = [ "root" "tazjin" ];
-
-    binaryCaches = [
+  nix.settings = {
+    max-jobs = lib.mkDefault 4;
+    trusted-users = [ "root" "tazjin" ];
+    substituters = [
       "https://tazjin.cachix.org"
     ];
 
-    binaryCachePublicKeys = [
+    trusted-public-keys = [
       "tazjin.cachix.org-1:IZkgLeqfOr1kAZjypItHMg1NoBjm4zX9Zzep8oRSh7U="
     ];
   };
@@ -112,7 +108,7 @@ in lib.fix(self: {
   programs.mosh.enable = true;
 
   fonts = {
-    fonts = [ pkgs.jetbrains-mono ];
+    packages = [ pkgs.jetbrains-mono ];
     fontconfig.defaultFonts.monospace = [ "JetBrains Mono" ];
   };
 
@@ -128,7 +124,7 @@ in lib.fix(self: {
       bat
       curl
       direnv
-      emacs27-nox
+      emacs28-nox
       fswebcam
       git
       gnupg
@@ -152,7 +148,7 @@ in lib.fix(self: {
     };
 
     # Set up a user & group for general git shenanigans
-    groups.git = {};
+    groups.git = { };
     users.git = {
       group = "git";
       isSystemUser = true;
@@ -167,7 +163,7 @@ in lib.fix(self: {
   services.tailscale.enable = true;
 
   # Allow sudo-ing via the forwarded SSH agent.
-  security.pam.enableSSHAgentAuth = true;
+  security.pam.sshAgentAuth.enable = true;
 
   # NixOS 20.03 broke nginx and I can't be bothered to debug it
   # anymore, all solution attempts have failed, so here's a
@@ -191,38 +187,36 @@ in lib.fix(self: {
   # Provision a TLS certificate outside of nginx to avoid
   # nixpkgs#38144
   security.acme = {
-    # acceptTerms = true;
+    acceptTerms = true;
 
     certs."tazj.in" = {
       email = "mail@tazj.in";
-      user = "nginx";
       group = "nginx";
       webroot = "/var/lib/acme/acme-challenge";
-      extraDomains = {
-        "cs.tazj.in" = null;
-        "git.tazj.in" = null;
-        "www.tazj.in" = null;
+      postRun = "systemctl reload nginx";
+
+      extraDomainNames = [
+        "cs.tazj.in"
+        "git.tazj.in"
+        "www.tazj.in"
 
         # Local domains (for this machine only)
-        "camden.tazj.in" = null;
-      };
-      postRun = "systemctl reload nginx";
+        "camden.tazj.in"
+      ];
     };
 
     certs."quassel.tazj.in" = {
       email = "mail@tazj.in";
       webroot = "/var/lib/acme/challenge-quassel";
-      user = "nginx"; # required because of a bug in the ACME module
       group = "quassel";
-      allowKeysForGroup = true;
     };
   };
 
   # Forward logs to Google Cloud Platform
   services.journaldriver = {
-    enable                 = true;
-    logStream              = "home";
-    googleCloudProject     = "tazjins-infrastructure";
+    enable = true;
+    logStream = "home";
+    googleCloudProject = "tazjins-infrastructure";
     applicationCredentials = "/etc/gcp/key.json";
   };
 
diff --git a/users/tazjin/nixos/default.nix b/users/tazjin/nixos/default.nix
index 04123a3b5a..8f82c39ea1 100644
--- a/users/tazjin/nixos/default.nix
+++ b/users/tazjin/nixos/default.nix
@@ -1,10 +1,12 @@
 { depot, lib, ... }:
 
 let systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
-in {
+in depot.nix.readTree.drvTargets {
   camdenSystem = systemFor depot.users.tazjin.nixos.camden;
   frogSystem = systemFor depot.users.tazjin.nixos.frog;
   tverskoySystem = systemFor depot.users.tazjin.nixos.tverskoy;
-
-  meta.targets = [ "camdenSystem" "frogSystem" "tverskoySystem" ];
+  zamalekSystem = systemFor depot.users.tazjin.nixos.zamalek;
+  koptevoRaw = depot.ops.nixos.nixosFor depot.users.tazjin.nixos.koptevo;
+  koptevoSystem = systemFor depot.users.tazjin.nixos.koptevo;
+  khamovnikSystem = systemFor depot.users.tazjin.nixos.khamovnik;
 }
diff --git a/users/tazjin/nixos/frog/default.nix b/users/tazjin/nixos/frog/default.nix
index b3c803c871..dfb6b46d5a 100644
--- a/users/tazjin/nixos/frog/default.nix
+++ b/users/tazjin/nixos/frog/default.nix
@@ -1,6 +1,7 @@
 { depot, lib, pkgs, ... }:
 
-config: let
+config:
+let
   inherit (pkgs) lieer;
 
   quasselClient = pkgs.quassel.override {
@@ -8,13 +9,10 @@ config: let
     enableDaemon = false;
     monolithic = false;
   };
-in lib.fix(self: {
-  imports = [
-    "${depot.path}/ops/modules/v4l2loopback.nix"
-  ];
-
+in
+lib.fix (self: {
   boot = {
-    tmpOnTmpfs = true;
+    tmp.useTmpfs = true;
     kernelModules = [ "kvm-amd" ];
 
     loader = {
@@ -59,10 +57,9 @@ in lib.fix(self: {
     };
   };
 
-  nix = {
-    maxJobs = 48;
-    binaryCaches = ["ssh://nix-ssh@whitby.tvl.fyi"];
-    binaryCachePublicKeys = ["cache.tvl.fyi:fd+9d1ceCPvDX/xVhcfv8nAa6njEhAGAEe+oGJDEeoc="];
+  nix.settings = {
+    max-jobs = 48;
+    substituters = [ "ssh://nix-ssh@whitby.tvl.fyi" ];
   };
 
   networking = {
@@ -109,7 +106,7 @@ in lib.fix(self: {
   };
 
   fonts = {
-    fonts = with pkgs; [
+    packages = with pkgs; [
       corefonts
       dejavu_fonts
       jetbrains-mono
@@ -155,8 +152,8 @@ in lib.fix(self: {
 
   services.xserver = {
     enable = true;
-    layout = "us";
-    xkbOptions = "caps:super";
+    xkb.layout = "us";
+    xkb.options = "caps:super";
     exportConfiguration = true;
     videoDrivers = [ "amdgpu" ];
     displayManager = {
@@ -220,13 +217,12 @@ in lib.fix(self: {
       bat
       chromium
       clang-manpages
-      clang-tools_11
-      clang_11
+      clang-tools
+      clang
       curl
       direnv
       dnsutils
-      emacs27 # mostly for emacsclient
-      exa
+      emacs28 # mostly for emacsclient
       fd
       file
       gdb
@@ -242,7 +238,7 @@ in lib.fix(self: {
       jq
       kubectl
       linuxPackages.perf
-      manpages
+      man-pages
       miller
       msmtp
       nix-prefetch-github
@@ -260,7 +256,6 @@ in lib.fix(self: {
       ripgrep
       rustup
       screen
-      scrot
       spotify
       tokei
       transmission
diff --git a/users/tazjin/nixos/khamovnik/default.nix b/users/tazjin/nixos/khamovnik/default.nix
new file mode 100644
index 0000000000..8ea925c90d
--- /dev/null
+++ b/users/tazjin/nixos/khamovnik/default.nix
@@ -0,0 +1,133 @@
+# Yandex work laptop
+#
+# Some of the configuration for this machine is not public.
+{ depot, lib, pkgs, ... }:
+
+config:
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+  usermod = name: depot.path.origSrc + ("/users/tazjin/nixos/modules/" + name);
+  private = /arc/junk/tazjin;
+
+  zdevice = device: {
+    inherit device;
+    fsType = "zfs";
+  };
+in
+{
+  imports = [
+    (usermod "chromium.nix")
+    (usermod "desktop.nix")
+    (usermod "fonts.nix")
+    (usermod "home-config.nix")
+    (usermod "laptop.nix")
+    (usermod "physical.nix")
+    (pkgs.home-manager.src + "/nixos")
+  ] ++ (if (builtins.pathExists private) then [
+    (private + "/nixos/yandex.nix")
+    (private + "/emacs/module.nix")
+  ] else [ ]);
+
+  # from hardware-configuration.nix
+  boot = {
+    initrd.luks.devices."luks-9c3cd590-a648-450d-ae42-ed3859d4c717".device =
+      "/dev/disk/by-uuid/9c3cd590-a648-450d-ae42-ed3859d4c717";
+
+    initrd.availableKernelModules = [
+      "xhci_pci"
+      "thunderbolt"
+      "ahci"
+      "nvme"
+      "usb_storage"
+      "sd_mod"
+      "rtsx_pci_sdmmc"
+    ];
+    kernelModules = [ "kvm-intel" ];
+  };
+
+  fileSystems = {
+    "/" = {
+      device = "/dev/disk/by-uuid/1f783029-c4f9-4192-b893-84f4f0c2a493";
+      fsType = "ext4";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/DD01-2B3E";
+      fsType = "vfat";
+    };
+  };
+
+  swapDevices = [{
+    device = "/dev/disk/by-uuid/9b9049c5-5975-441d-9ac6-2f9150775fd6";
+  }];
+
+  tvl.cache.enable = true;
+
+  networking.hostName = "khamovnik";
+
+  nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
+  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
+  hardware.cpu.intel.updateMicrocode = true;
+  hardware.enableRedistributableFirmware = true;
+  hardware.opengl.extraPackages = with pkgs; [
+    intel-compute-runtime
+    intel-media-driver
+    intel-vaapi-driver
+  ];
+
+  # from generated configuration.nix
+  # Bootloader.
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  # Setup keyfile
+  boot.initrd.secrets = {
+    "/crypto_keyfile.bin" = null;
+  };
+
+  # Enable swap on luks
+  boot.initrd.luks.devices."luks-e9a4b4dc-ade2-45bf-8ed0-0ed5c4c392c9".device = "/dev/disk/by-uuid/e9a4b4dc-ade2-45bf-8ed0-0ed5c4c392c9";
+  boot.initrd.luks.devices."luks-e9a4b4dc-ade2-45bf-8ed0-0ed5c4c392c9".keyFile = "/crypto_keyfile.bin";
+
+  # Select internationalisation properties.
+  i18n.defaultLocale = "en_US.UTF-8";
+  i18n.extraLocaleSettings = {
+    LC_ADDRESS = "ru_RU.UTF-8";
+    LC_IDENTIFICATION = "ru_RU.UTF-8";
+    LC_MEASUREMENT = "ru_RU.UTF-8";
+    LC_MONETARY = "ru_RU.UTF-8";
+    LC_NAME = "ru_RU.UTF-8";
+    LC_NUMERIC = "ru_RU.UTF-8";
+    LC_PAPER = "ru_RU.UTF-8";
+    LC_TELEPHONE = "ru_RU.UTF-8";
+    LC_TIME = "ru_RU.UTF-8";
+  };
+
+  # Enable sound with pipewire.
+  sound.enable = true;
+  hardware.pulseaudio.enable = false;
+  security.rtkit.enable = true;
+  services.pipewire = {
+    enable = true;
+    alsa.enable = true;
+    alsa.support32Bit = true;
+    pulse.enable = true;
+  };
+
+  # Try to work around Intel CPU throttling bugs
+  services.throttled.enable = true;
+
+  virtualisation.docker.enable = true;
+
+  hardware.bluetooth.enable = true;
+  users.users.tazjin.extraGroups = [ "tss" ];
+
+  environment.systemPackages = with pkgs; [
+    tdesktop
+    linuxPackages.perf
+    hotspot
+    protobuf
+  ];
+
+  system.stateVersion = "23.05"; # Did you read the comment?
+}
diff --git a/users/tazjin/nixos/koptevo/default.nix b/users/tazjin/nixos/koptevo/default.nix
new file mode 100644
index 0000000000..ea8dfd4bd8
--- /dev/null
+++ b/users/tazjin/nixos/koptevo/default.nix
@@ -0,0 +1,187 @@
+# NUC in my closet.
+_: # ignore readTree options
+
+{ config, depot, lib, pkgs, ... }:
+
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+  usermod = name: depot.path.origSrc + ("/users/tazjin/nixos/modules/" + name);
+in
+{
+  imports = [
+    (mod "quassel.nix")
+    (mod "www/base.nix")
+    (mod "www/tazj.in.nix")
+    (usermod "airsonic.nix")
+    (usermod "geesefs.nix")
+    (usermod "predlozhnik.nix")
+    (usermod "tgsa.nix")
+    (usermod "miniflux.nix")
+    (depot.third_party.agenix.src + "/modules/age.nix")
+  ];
+
+  boot = {
+    loader.systemd-boot.enable = true;
+    loader.efi.canTouchEfiVariables = true;
+    initrd.availableKernelModules = [ "ahci" "xhci_pci" "usb_storage" "sd_mod" "sdhci_pci" ];
+    kernelModules = [ "kvm-intel" ];
+    kernelParams = [ "nomodeset" ];
+  };
+
+  nix.settings.trusted-users = [ "tazjin" ];
+
+  fileSystems = {
+    "/" = {
+      device = "rpool/root";
+      fsType = "zfs";
+    };
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/E214-E6B3";
+      fsType = "vfat";
+    };
+
+    "/var" = {
+      device = "rpool/var";
+      fsType = "zfs";
+    };
+
+    "/home" = {
+      device = "rpool/home";
+      fsType = "zfs";
+    };
+  };
+
+  hardware.cpu.intel.updateMicrocode = true;
+  hardware.enableRedistributableFirmware = true;
+  services.fwupd.enable = true;
+
+  networking = {
+    hostName = "koptevo";
+    hostId = "07bbbf4f";
+    domain = "tazj.in";
+    useDHCP = true;
+    firewall.enable = true;
+    firewall.allowedTCPPorts = [ 22 80 443 ];
+
+    wireless.enable = true;
+    wireless.networks."How do I computer fast?" = {
+      psk = "washyourface";
+    };
+  };
+
+  time.timeZone = "UTC";
+
+  security.acme.acceptTerms = true;
+  security.acme.defaults.email = lib.mkForce "acme@tazj.in";
+
+  programs.fish.enable = true;
+
+  users.users.tazjin = {
+    isNormalUser = true;
+    extraGroups = [ "wheel" "docker" "systemd-journal" ];
+    shell = pkgs.fish;
+    openssh.authorizedKeys.keys = depot.users.tazjin.keys.all;
+  };
+
+  age.secrets =
+    let
+      secretFile = name: depot.users.tazjin.secrets."${name}.age";
+    in
+    {
+      tgsa-yandex.file = secretFile "tgsa-yandex";
+    };
+
+  security.sudo.wheelNeedsPassword = false;
+
+  services.openssh.enable = true;
+
+  services.depot.quassel = {
+    enable = true;
+    acmeHost = "koptevo.tazj.in";
+    bindAddresses = [
+      "0.0.0.0"
+    ];
+  };
+
+  services.tailscale = {
+    enable = true;
+    useRoutingFeatures = "server"; # for exit-node usage
+  };
+
+  # Automatically collect garbage from the Nix store.
+  services.depot.automatic-gc = {
+    enable = true;
+    interval = "daily";
+    diskThreshold = 15; # GiB
+    maxFreed = 10; # GiB
+    preserveGenerations = "14d";
+  };
+
+  services.nginx.virtualHosts."koptevo.tazj.in" = {
+    addSSL = true;
+    enableACME = true;
+
+    extraConfig = ''
+      location = / {
+        return 302 https://at.tvl.fyi/?q=%2F%2Fusers%2Ftazjin%2Fnixos%2Fkoptevo%2Fdefault.nix;
+      }
+    '';
+  };
+
+  # I don't use the podcast nor playlist feature,
+  # but I *have to* supply podcasts to gonic ...
+  systemd.tmpfiles.rules = [
+    "d /tmp/fake-podcasts 0555 nobody nobody -"
+    "d /tmp/fake-playlists 0555 nobody nobody -"
+  ];
+
+  services.gonic = {
+    enable = true;
+    settings = {
+      listen-addr = "0.0.0.0:4747";
+      scan-interval = 5;
+      scan-at-start-enabled = true;
+      podcast-path = [ "/tmp/fake-podcasts" ];
+      playlists-path = [ "/tmp/fake-playlists" ];
+      music-path = [ "/var/lib/geesefs/tazjins-files/music" ];
+    };
+  };
+
+  # hack to work around the strict sandboxing of the gonic module
+  # breaking DNS resolution
+  systemd.services.gonic.serviceConfig.BindReadOnlyPaths = [
+    "-/etc/resolv.conf"
+  ];
+
+  # add a hard dependency on the FUSE mount
+  systemd.services.gonic.requires = [ "geesefs.service" ];
+
+  services.nginx.virtualHosts."music.tazj.in" = {
+    addSSL = true;
+    enableACME = true;
+
+    locations."/" = {
+      proxyPass = "http://127.0.0.1:4747";
+    };
+  };
+
+  # List packages installed in system profile. To search, run:
+  # $ nix search wget
+  environment.systemPackages = with pkgs; [
+    curl
+    htop
+    jq
+    nmap
+    bat
+    emacs-nox
+    nano
+    wget
+  ];
+
+  programs.mtr.enable = true;
+  programs.mosh.enable = true;
+  zramSwap.enable = true;
+
+  system.stateVersion = "23.05";
+}
diff --git a/users/tazjin/nixos/modules/airsonic.nix b/users/tazjin/nixos/modules/airsonic.nix
new file mode 100644
index 0000000000..815f183778
--- /dev/null
+++ b/users/tazjin/nixos/modules/airsonic.nix
@@ -0,0 +1,32 @@
+# airsonic is a decent, web-based player UI for subsonic
+{ pkgs, ... }:
+
+let
+  env = builtins.toFile "env.js" ''
+    window.env = {
+      SERVER_URL: "https://music.tazj.in",
+    }
+  '';
+
+  airsonicDist = pkgs.fetchzip {
+    name = "airsonic-refix";
+
+    # from master CI @ f894d5eacebec2f47486f340c8610f446d4f64b3
+    # https://github.com/tamland/airsonic-refix/actions/runs/6150155527
+    url = "https://storage.yandexcloud.net/tazjin-public/airsonic-refix-f894d5ea.zip";
+    sha256 = "02rnh9h7rh22wkghays389yddwbwg7sawmczdxdmjrcnkc7mq2jz";
+
+    stripRoot = false;
+    postFetch = "cp ${env} $out/env.js";
+  };
+in
+{
+  services.nginx.virtualHosts."player.tazj.in" = {
+    enableACME = true;
+    forceSSL = true;
+    root = "${airsonicDist}";
+
+    # deal with SPA routing requirements
+    locations."/".extraConfig = "try_files $uri /index.html;";
+  };
+}
diff --git a/users/tazjin/nixos/modules/chromium.nix b/users/tazjin/nixos/modules/chromium.nix
new file mode 100644
index 0000000000..22f1c8d362
--- /dev/null
+++ b/users/tazjin/nixos/modules/chromium.nix
@@ -0,0 +1,30 @@
+# Configure the Chromium browser with various useful things.
+{ pkgs, ... }:
+
+{
+  environment.systemPackages = [
+    (pkgs.chromium.override {
+      enableWideVine = true; # DRM support (for ะšะธะฝะพะฟะพะธัะบ)
+    })
+  ];
+
+  programs.chromium = {
+    enable = true;
+    homepageLocation = "about:blank";
+
+    extensions = [
+      "dbepggeogbaibhgnhhndojpepiihcmeb" # Vimium
+      "cjpalhdlnbpafiamejdnhcphjbkeiagm" # uBlock Origin
+      "mohaicophfnifehkkkdbcejkflmgfkof" # nitter redirect
+      "lhdifindchogekmjooeiolmjdlheilae" # Huruf
+    ];
+
+    extraOpts = {
+      SpellcheckEnabled = true;
+      SpellcheckLanguage = [
+        "ru"
+        "en-GB"
+      ];
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/default.nix b/users/tazjin/nixos/modules/default.nix
new file mode 100644
index 0000000000..d747e8e131
--- /dev/null
+++ b/users/tazjin/nixos/modules/default.nix
@@ -0,0 +1,2 @@
+# Make readTree happy at this level.
+_: { }
diff --git a/users/tazjin/nixos/modules/desktop.nix b/users/tazjin/nixos/modules/desktop.nix
new file mode 100644
index 0000000000..12a42b8faa
--- /dev/null
+++ b/users/tazjin/nixos/modules/desktop.nix
@@ -0,0 +1,55 @@
+# EXWM and other desktop configuration.
+{ config, depot, lib, pkgs, ... }:
+
+{
+  services = {
+    pipewire = {
+      enable = true;
+      alsa.enable = true;
+      alsa.support32Bit = true;
+      pulse.enable = true;
+    };
+
+    redshift.enable = true;
+    blueman.enable = true;
+
+    xserver = {
+      enable = true;
+      xkb.layout = "us";
+      xkb.options = "caps:super";
+
+      libinput.enable = true;
+
+      displayManager = {
+        # Give EXWM permission to control the session.
+        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER";
+        lightdm.enable = true;
+        # lightdm.greeters.gtk.clock-format = "%H:%M"; # TODO(tazjin): TZ?
+      };
+
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${config.tazjin.emacs}/bin/tazjins-emacs --internal-border=0 --border-width=0";
+      };
+    };
+  };
+
+  # Set variables to enable EXWM-XIM and other Emacs features.
+  environment.sessionVariables = {
+    XMODIFIERS = "@im=exwm-xim";
+    GTK_IM_MODULE = "xim";
+    QT_IM_MODULE = "xim";
+    CLUTTER_IM_MODULE = "xim";
+    EDITOR = "emacsclient";
+    _JAVA_AWT_WM_NONREPARENTING = "1";
+  };
+
+  # Do not restart the display manager automatically
+  systemd.services.display-manager.restartIfChanged = lib.mkForce false;
+
+  # If something needs more than 10s to stop it should probably be
+  # killed.
+  systemd.extraConfig = ''
+    DefaultTimeoutStopSec=10s
+  '';
+}
diff --git a/users/tazjin/nixos/modules/fonts.nix b/users/tazjin/nixos/modules/fonts.nix
new file mode 100644
index 0000000000..ee1b84e581
--- /dev/null
+++ b/users/tazjin/nixos/modules/fonts.nix
@@ -0,0 +1,24 @@
+# Attempt at configuring reasonable font-rendering.
+
+{ pkgs, ... }:
+
+{
+  fonts = {
+    packages = with pkgs; [
+      corefonts
+      dejavu_fonts
+      jetbrains-mono
+      noto-fonts-cjk
+      noto-fonts-emoji
+    ];
+
+    fontconfig = {
+      hinting.enable = true;
+      subpixel.lcdfilter = "light";
+
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/geesefs.nix b/users/tazjin/nixos/modules/geesefs.nix
new file mode 100644
index 0000000000..c45ee528f6
--- /dev/null
+++ b/users/tazjin/nixos/modules/geesefs.nix
@@ -0,0 +1,38 @@
+{ depot, pkgs, ... }:
+
+{
+  imports = [
+    (depot.third_party.agenix.src + "/modules/age.nix")
+  ];
+
+  age.secrets.geesefs-tazjins-files.file = depot.users.tazjin.secrets."geesefs-tazjins-files.age";
+  programs.fuse.userAllowOther = true;
+
+  systemd.services.geesefs = {
+    description = "geesefs @ tazjins-files";
+    wantedBy = [ "multi-user.target" ];
+    path = [ pkgs.fuse ];
+
+    serviceConfig = {
+      # TODO: can't get fusermount to work for non-root users (e.g. DynamicUser) here, why?
+
+      Restart = "always";
+      LoadCredential = "geesefs-tazjins-files:/run/agenix/geesefs-tazjins-files";
+      StateDirectory = "geesefs";
+      ExecStartPre = "/run/wrappers/bin/umount -a -t fuse.geesefs";
+    };
+
+    script = ''
+      set -u # bail out if systemd is misconfigured ...
+      set -x
+
+      mkdir -p $STATE_DIRECTORY/tazjins-files $STATE_DIRECTORY/cache
+
+      ${depot.third_party.geesefs}/bin/geesefs \
+        -f -o allow_other \
+        --cache $STATE_DIRECTORY/cache \
+        --shared-config $CREDENTIALS_DIRECTORY/geesefs-tazjins-files \
+        tazjins-files $STATE_DIRECTORY/tazjins-files
+    '';
+  };
+}
diff --git a/users/tazjin/nixos/modules/hidpi.nix b/users/tazjin/nixos/modules/hidpi.nix
new file mode 100644
index 0000000000..2ff61d499a
--- /dev/null
+++ b/users/tazjin/nixos/modules/hidpi.nix
@@ -0,0 +1,19 @@
+# Configuration for machines with HiDPI displays, which are a total
+# mess, of course.
+{ ... }:
+
+{
+  # Expose a variable to all programs that might be interested in the
+  # screen settings to do conditional initialisation (mostly for Emacs).
+  environment.variables.HIDPI_SCREEN = "true";
+
+  # TODO(tazjin): this option has been removed and needs to be replaced
+  # by manual configuration: https://github.com/NixOS/nixpkgs/issues/222805
+  # Ensure a larger font size in early boot stage.
+  # hardware.video.hidpi.enable = true;
+
+  # Bump DPI across the board.
+  # TODO(tazjin): This should actually be set per monitor, but I
+  # haven't yet figured out the right interface for doing that.
+  services.xserver.dpi = 161;
+}
diff --git a/users/tazjin/nixos/modules/home-config.nix b/users/tazjin/nixos/modules/home-config.nix
new file mode 100644
index 0000000000..bda8f7a440
--- /dev/null
+++ b/users/tazjin/nixos/modules/home-config.nix
@@ -0,0 +1,19 @@
+# Inject the right home-manager config for the machine.
+
+{ config, depot, pkgs, ... }:
+
+{
+  users.users.tazjin = {
+    isNormalUser = true;
+    createHome = true;
+    extraGroups = [ "wheel" "networkmanager" "video" "adbusers" ];
+    uid = 1000;
+    shell = pkgs.fish;
+    initialHashedPassword = "$2b$05$1eBPdoIgan/C/L8JFqIHBuVscQyTKw1L/4VBlzlLvLBEf6CXS3EW6";
+  };
+
+  nix.settings.trusted-users = [ "tazjin" ];
+
+  home-manager.useGlobalPkgs = true;
+  home-manager.users.tazjin = depot.users.tazjin.home."${config.networking.hostName}";
+}
diff --git a/users/tazjin/nixos/modules/laptop.nix b/users/tazjin/nixos/modules/laptop.nix
new file mode 100644
index 0000000000..e0d67dc259
--- /dev/null
+++ b/users/tazjin/nixos/modules/laptop.nix
@@ -0,0 +1,15 @@
+# Configuration specifically for laptops that move around.
+{ ... }:
+
+{
+  time.timeZone = "Europe/Moscow";
+
+  # Automatically detect location for redshift & so on ...
+  services.geoclue2.enable = true;
+  location.provider = "geoclue2";
+
+  # Enable power-saving features.
+  services.tlp.enable = true;
+
+  programs.light.enable = true;
+}
diff --git a/users/tazjin/nixos/modules/miniflux.nix b/users/tazjin/nixos/modules/miniflux.nix
new file mode 100644
index 0000000000..72089bfb3d
--- /dev/null
+++ b/users/tazjin/nixos/modules/miniflux.nix
@@ -0,0 +1,22 @@
+{ config, depot, lib, pkgs, ... }:
+
+{
+  age.secrets.miniflux.file = depot.users.tazjin.secrets."miniflux.age";
+
+  services.miniflux = {
+    enable = true;
+    adminCredentialsFile = "/run/agenix/miniflux";
+    config.LISTEN_ADDR = "127.0.0.1:6359";
+    config.BASE_URL = "https://feeds.tazj.in";
+  };
+
+  services.nginx.virtualHosts."feeds" = {
+    serverName = "feeds.tazj.in";
+    enableACME = true;
+    forceSSL = true;
+
+    locations."/" = {
+      proxyPass = "http://127.0.0.1:6359";
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/persistence.nix b/users/tazjin/nixos/modules/persistence.nix
new file mode 100644
index 0000000000..b864d13a8d
--- /dev/null
+++ b/users/tazjin/nixos/modules/persistence.nix
@@ -0,0 +1,26 @@
+# Configuration for persistent (non-home) data.
+{ config, depot, pkgs, lib, ... }:
+
+{
+  imports = [
+    (depot.third_party.sources.impermanence + "/nixos.nix")
+  ];
+
+  environment.persistence."/persist" = {
+    directories = [
+      "/etc/NetworkManager/system-connections"
+      "/etc/mullvad-vpn"
+      "/var/cache/mullvad-vpn"
+      "/var/lib/bluetooth"
+      "/var/lib/systemd/coredump"
+      "/var/lib/tailscale"
+      "/var/log"
+    ];
+
+    files = lib.optional (builtins.isNull config.networking.hostId) [
+      "/etc/machine-id"
+    ];
+  };
+
+  programs.fuse.userAllowOther = true;
+}
diff --git a/users/tazjin/nixos/modules/physical.nix b/users/tazjin/nixos/modules/physical.nix
new file mode 100644
index 0000000000..bb85c6fb98
--- /dev/null
+++ b/users/tazjin/nixos/modules/physical.nix
@@ -0,0 +1,105 @@
+# Default configuration settings for physical machines that I use.
+{ lib, pkgs, config, depot, ... }:
+
+let
+  pass-otp = pkgs.pass.withExtensions (e: [ e.pass-otp ]);
+in
+{
+  options = with lib; {
+    tazjin.emacs = mkOption {
+      type = types.package;
+      default = depot.users.tazjin.emacs;
+      description = ''
+        Derivation with my Emacs package, with configuration included.
+      '';
+    };
+  };
+
+  config = {
+    # Install all the default software.
+    environment.systemPackages =
+      # programs from the depot
+      (with depot; [
+        users.tazjin.screenLock
+        users.tazjin.chase-geese
+        config.tazjin.emacs
+        third_party.agenix.cli
+      ]) ++
+
+      # programs from nixpkgs
+      (with pkgs; [
+        (aspellWithDicts (d: [ d.ru ]))
+        amber
+        bat
+        curl
+        ddcutil
+        direnv
+        dnsutils
+        electrum
+        firefox
+        config.tazjin.emacs.emacs # emacsclient
+        expect
+        fd
+        file
+        gdb
+        git
+        gnupg
+        gtk3 # for gtk-launch
+        htop
+        hyperfine
+        iftop
+        imagemagick
+        josh
+        jq
+        lieer
+        maim
+        man-pages
+        moreutils
+        mosh
+        msmtp
+        networkmanagerapplet
+        nix-prefetch-github
+        nmap
+        notmuch
+        openssh
+        openssl
+        pass-otp
+        pavucontrol
+        pinentry
+        pinentry-emacs
+        pulseaudio # for pactl
+        pwgen
+        quasselClient
+        rink
+        ripgrep
+        rustup
+        screen
+        tig
+        tokei
+        tree
+        unzip
+        vlc
+        volumeicon
+        whois
+        xclip
+        xsecurelock
+        zoxide
+      ]);
+
+    # Run services & configure programs for all machines.
+    services.fwupd.enable = true;
+
+    # Disable the broken NetworkManager-wait-online.service
+    systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
+
+    # Disable the thing that prints annoying warnings when trying to
+    # run manually patchelfed binaries
+    environment.stub-ld.enable = false;
+
+    programs = {
+      fish.enable = true;
+      mosh.enable = true;
+      ssh.startAgent = true;
+    };
+  };
+}
diff --git a/users/tazjin/nixos/modules/predlozhnik.nix b/users/tazjin/nixos/modules/predlozhnik.nix
new file mode 100644
index 0000000000..db20963df1
--- /dev/null
+++ b/users/tazjin/nixos/modules/predlozhnik.nix
@@ -0,0 +1,10 @@
+# Host predlozhnik.ru, serving //users/tazjin/predlozhnik
+{ depot, ... }:
+
+{
+  services.nginx.virtualHosts."predlozhnik.ru" = {
+    root = depot.corp.russian.predlozhnik;
+    enableACME = true;
+    forceSSL = true;
+  };
+}
diff --git a/users/tazjin/nixos/modules/tgsa.nix b/users/tazjin/nixos/modules/tgsa.nix
new file mode 100644
index 0000000000..e162e0d822
--- /dev/null
+++ b/users/tazjin/nixos/modules/tgsa.nix
@@ -0,0 +1,29 @@
+{ config, depot, lib, pkgs, ... }:
+
+{
+  systemd.services.tgsa = {
+    description = "telegram -> SA bbcode thing";
+    wantedBy = [ "multi-user.target" ];
+
+    serviceConfig = {
+      DynamicUser = true;
+      Restart = "always";
+      LoadCredential = "tgsa-yandex.json:/run/agenix/tgsa-yandex";
+    };
+
+    script = ''
+      export YANDEX_KEY_FILE="''${CREDENTIALS_DIRECTORY}/tgsa-yandex.json"
+      ${depot.users.tazjin.tgsa}/bin/tgsa
+    '';
+  };
+
+  services.nginx.virtualHosts."tgsa" = {
+    serverName = "tgsa.tazj.in";
+    enableACME = true;
+    forceSSL = true;
+
+    locations."/" = {
+      proxyPass = "http://127.0.0.1:8472";
+    };
+  };
+}
diff --git a/users/tazjin/nixos/tverskoy/default.nix b/users/tazjin/nixos/tverskoy/default.nix
index 9f19210d17..733929219a 100644
--- a/users/tazjin/nixos/tverskoy/default.nix
+++ b/users/tazjin/nixos/tverskoy/default.nix
@@ -1,38 +1,34 @@
+# tverskoy is my Thinkpad X13 AMD 1st gen
 { depot, lib, pkgs, ... }:
 
-config: let
+config:
+let
   quasselClient = pkgs.quassel.override {
     client = true;
     enableDaemon = false;
     monolithic = false;
   };
 
-  # Use a screen lock command that resets the keyboard layout
-  # before locking, to avoid locking me out when the layout is
-  # in Russian.
-  screenLock = pkgs.writeShellScriptBin "tazjin-screen-lock" ''
-    ${pkgs.xorg.setxkbmap}/bin/setxkbmap us
-    ${pkgs.xorg.setxkbmap}/bin/setxkbmap -option caps:super
-    exec ${pkgs.xsecurelock}/bin/xsecurelock
-  '';
-in lib.fix(self: {
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+  usermod = name: depot.path.origSrc + ("/users/tazjin/nixos/modules/" + name);
+in
+lib.fix (self: {
   imports = [
-    "${depot.third_party.impermanence}/nixos.nix"
-    "${depot.path + "/ops/modules/automatic-gc.nix"}"
-    "${pkgs.home-manager.src}/nixos"
-  ];
-
-  nix = {
-    binaryCachePublicKeys = [
-      "cache.tvl.su:kjc6KOMupXc1vHVufJUoDUYeLzbwSr9abcAKdn/U1Jk="
-    ];
+    (mod "open_eid.nix")
+    (usermod "chromium.nix")
+    (usermod "desktop.nix")
+    (usermod "fonts.nix")
+    (usermod "home-config.nix")
+    (usermod "laptop.nix")
+    (usermod "persistence.nix")
+    (usermod "physical.nix")
 
-    binaryCaches = [
-      "https://cache.tvl.su"
-    ];
-  };
+    (pkgs.home-manager.src + "/nixos")
+  ] ++ lib.optional (builtins.pathExists ./local-config.nix) ./local-config.nix;
+
+  tvl.cache.enable = true;
 
-  boot = {
+  boot = rec {
     initrd.availableKernelModules = [ "nvme" "ehci_pci" "xhci_pci" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
     initrd.kernelModules = [ ];
 
@@ -41,19 +37,22 @@ in lib.fix(self: {
       zfs rollback -r zpool/ephemeral/home@tazjin-clean
     '';
 
+    # Install thinkpad modules for TLP
+    extraModulePackages = [ kernelPackages.acpi_call ];
+
     kernelModules = [ "kvm-amd" "i2c_dev" ];
-    extraModulePackages = [ ];
-    kernelPackages = pkgs.linuxPackages_latest;
+    kernelPackages = pkgs.zfsUnstable.latestCompatibleLinuxPackages;
     loader.systemd-boot.enable = true;
     loader.efi.canTouchEfiVariables = true;
-    zfs.enableUnstable = true;
   };
 
+  virtualisation.docker.enable = true;
+  users.users.tazjin.extraGroups = [ "docker" "vboxusers" "adbusers" ];
+
   fileSystems = {
-    "/" =  {
-      device = "tmpfs";
-      fsType = "tmpfs";
-      options = [ "defaults" "size=4G" "mode=755" ];
+    "/" = {
+      device = "zpool/ephemeral/root";
+      fsType = "zfs";
     };
 
     "/home" = {
@@ -96,6 +95,8 @@ in lib.fix(self: {
 
     opengl = {
       enable = true;
+      driSupport32Bit = true;
+
       extraPackages = with pkgs; [
         vaapiVdpau
         libvdpau-va-gl
@@ -117,258 +118,58 @@ in lib.fix(self: {
     ];
   };
 
-  fonts = {
-    fonts = with pkgs; [
-      corefonts
-      dejavu_fonts
-      jetbrains-mono
-      noto-fonts-cjk
-      noto-fonts-emoji
-    ];
-
-    fontconfig = {
-      hinting.enable = true;
-      subpixel.lcdfilter = "light";
-
-      defaultFonts = {
-        monospace = [ "JetBrains Mono" ];
-      };
-    };
-  };
-
-  environment.persistence."/persist" = {
-    directories = [
-      "/etc/NetworkManager/system-connections"
-      "/etc/mullvad-vpn"
-      "/var/cache/mullvad-vpn"
-      "/var/lib/bluetooth"
-      "/var/lib/systemd/coredump"
-      "/var/log"
-    ];
-    files = [
-      "/etc/machine-id"
-    ];
-  };
-
   security.rtkit.enable = true;
 
   services = {
-    pipewire = {
-      enable = true;
-      alsa.enable = true;
-      pulse.enable = true;
-    };
-
-    redshift.enable = true;
-    blueman.enable = true;
-    mullvad-vpn.enable = true;
-    tlp.enable = true;
-    fwupd.enable = true;
+    tailscale.enable = true;
     printing.enable = true;
 
     # expose i2c device as /dev/i2c-amdgpu-dm and make it user-accessible
+    # this is required for sending control commands to the Dasung screen.
     udev.extraRules = ''
       SUBSYSTEM=="i2c-dev", ACTION=="add", DEVPATH=="/devices/pci0000:00/0000:00:08.1/0000:06:00.0/i2c-5/i2c-dev/i2c-5", SYMLINK+="i2c-amdgpu-dm", TAG+="uaccess"
     '';
 
-    xserver = {
-      enable = true;
-      layout = "us";
-      xkbOptions = "caps:super";
-      videoDrivers = [ "amdgpu" ];
-
-      libinput.enable = true;
-
-      displayManager = {
-        # Give EXWM permission to control the session.
-        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localuser:$USER";
-        lightdm.enable = true;
-        # lightdm.greeters.gtk.clock-format = "%H:%M"; # TODO(tazjin): TZ?
-      };
-
-      windowManager.session = lib.singleton {
-        name = "exwm";
-        start = "${depot.users.tazjin.emacs}/bin/tazjins-emacs";
-      };
-    };
+    xserver.videoDrivers = [ "amdgpu" ];
 
     # Automatically collect garbage from the Nix store.
     depot.automatic-gc = {
       enable = true;
       interval = "1 hour";
-      diskThreshold = 42; # GiB
-      maxFreed = 100; # GiB
+      diskThreshold = 16; # GiB
+      maxFreed = 10; # GiB
       preserveGenerations = "14d";
     };
   };
 
-  # Automatically detect location to use for redshift
-  location.provider = "geoclue2";
-
-  # Do not restart the display manager automatically
-  systemd.services.display-manager.restartIfChanged = lib.mkForce false;
+  systemd.user.services.lieer-tazjin = {
+    description = "Synchronise mail@tazj.in via lieer";
+    script = "${pkgs.lieer}/bin/gmi sync";
 
-  time.timeZone = "Europe/Stockholm";
-
-  users.users.tazjin = {
-    isNormalUser = true;
-    createHome = true;
-    extraGroups = [ "wheel" "networkmanager" "video" "adbusers" ];
-    uid = 1000;
-    shell = pkgs.fish;
-    initialHashedPassword = "$6$d3FywUNCuZnJ4l.$ZW2ul59MLYon1v1xhC3lTJZfZ91lWW6Tpi13MpME0cJcYZNrsx7ABdgQRn.K05awruG2Y9ARAzURnmiJ31WTS1";
+    serviceConfig = {
+      WorkingDirectory = "%h/mail/account.tazjin";
+      Type = "oneshot";
+    };
   };
 
-  programs = {
-    fish.enable = true;
-    light.enable = true;
-    ssh.startAgent = true;
-    mosh.enable = true;
-    steam.enable = true;
-    adb.enable = true;
-
-    # Required by impermanence
-    fuse.userAllowOther = true;
-  };
+  systemd.user.timers.lieer-tazjin = {
+    wantedBy = [ "timers.target" ];
 
-  environment.systemPackages =
-    # programs from the depot
-    (with depot; [
-      screenLock
-      tools.nsfv-setup
-      users.tazjin.emacs
-      third_party.dfmt
-    ]) ++
-
-    # programs from nixpkgs
-    (with pkgs; [
-      bat
-      chromium
-      curl
-      ddcutil
-      direnv
-      dmd
-      dnsutils
-      emacs27-nox # emacsclient
-      exa
-      fd
-      file
-      firefox
-      gdb
-      git
-      gnupg
-      google-chrome
-      gtk3 # for gtk-launch
-      htop
-      hyperfine
-      iftop
-      imagemagick
-      jq
-      lieer
-      manpages
-      mosh
-      msmtp
-      mullvad-vpn
-      networkmanagerapplet
-      nix-prefetch-github
-      notmuch
-      openssh
-      openssl
-      paperlike-go
-      pass
-      pavucontrol
-      picom
-      pinentry
-      pinentry-emacs
-      pulseaudioLight # for pactl
-      pwgen
-      quasselClient
-      rink
-      ripgrep
-      rustup
-      screen
-      scrot
-      spotify
-      syncthing
-      tokei
-      tree
-      unzip
-      vlc
-      xsecurelock
-    ]);
-
-    systemd.user.services.lieer-tazjin = {
-      description = "Synchronise mail@tazj.in via lieer";
-      script = "${pkgs.lieer}/bin/gmi sync";
-
-      serviceConfig = {
-        WorkingDirectory = "%h/mail/account.tazjin";
-        Type = "oneshot";
-      };
+    timerConfig = {
+      OnActiveSec = "1";
+      OnUnitActiveSec = "180";
     };
+  };
 
-    systemd.user.timers.lieer-tazjin = {
-      wantedBy = [ "timers.target" ];
+  # android stuff for hacking on Awful.apk
+  programs.adb.enable = true;
 
-      timerConfig = {
-        OnActiveSec = "1";
-        OnUnitActiveSec = "180";
-      };
-    };
+  # systemd-oomd seems to have been enabled by default around ~
+  # December 2022, and it's really into killing my X session as soon
+  # as I do anything stressful to the machine
+  systemd.services.systemd-oomd.enable = lib.mkForce false;
 
-    home-manager.useGlobalPkgs = true;
-    home-manager.users.tazjin = { config, lib, ... }: {
-      imports = [ "${depot.third_party.impermanence}/home-manager.nix" ];
-
-      home.persistence."/persist/tazjin/home" = {
-        allowOther = true;
-
-        directories = [
-          ".cargo"
-          ".config/google-chrome"
-          ".config/quassel-irc.org"
-          ".config/spotify"
-          ".config/syncthing"
-          ".elfeed"
-          ".gnupg"
-          ".local/share/Steam"
-          ".local/share/direnv"
-          ".local/share/fish"
-          ".mozilla/firefox"
-          ".password-store"
-          ".rustup"
-          ".ssh"
-          ".steam"
-          ".telega"
-          "go"
-          "mail"
-        ];
-
-        files = [
-          ".config/mimeapps.list"
-          ".notmuch-config"
-        ];
-      };
-
-      programs.git = {
-        enable = true;
-        userName = "Vincent Ambo";
-        userEmail = "mail@tazj.in";
-        extraConfig = {
-          pull.rebase = true;
-          init.defaultBranch = "canon";
-        };
-      };
-
-      services.screen-locker = {
-        enable = true;
-        enableDetectSleep = true;
-        inactiveInterval = 10; # minutes
-        lockCmd = "${screenLock}/bin/tazjin-screen-lock";
-      };
-
-      systemd.user.startServices = true;
-    };
+  environment.systemPackages = [ pkgs.vulkan-tools ];
 
-    system.stateVersion = "20.09";
+  system.stateVersion = "20.09";
 })
diff --git a/users/tazjin/nixos/zamalek/default.nix b/users/tazjin/nixos/zamalek/default.nix
new file mode 100644
index 0000000000..a340e8a3e8
--- /dev/null
+++ b/users/tazjin/nixos/zamalek/default.nix
@@ -0,0 +1,88 @@
+# zamalek is my Huawei MateBook X (unknown year)
+{ depot, lib, pkgs, ... }:
+
+config:
+let
+  mod = name: depot.path.origSrc + ("/ops/modules/" + name);
+  usermod = name: depot.path.origSrc + ("/users/tazjin/nixos/modules/" + name);
+
+  zdevice = device: {
+    inherit device;
+    fsType = "zfs";
+  };
+in
+{
+  imports = [
+    (usermod "chromium.nix")
+    (usermod "desktop.nix")
+    (usermod "fonts.nix")
+    (usermod "hidpi.nix")
+    (usermod "home-config.nix")
+    (usermod "laptop.nix")
+    (usermod "persistence.nix")
+    (usermod "physical.nix")
+
+    (pkgs.home-manager.src + "/nixos")
+  ] ++ lib.optional (builtins.pathExists ./local-config.nix) ./local-config.nix;
+
+  tvl.cache.enable = true;
+
+  boot = {
+    initrd.availableKernelModules = [ "nvme" "xhci_pci" ];
+    loader.systemd-boot.enable = true;
+    loader.efi.canTouchEfiVariables = true;
+    supportedFilesystems = [ "zfs" ];
+    zfs.devNodes = "/dev/";
+
+    extraModprobeConfig = ''
+      options snd_hda_intel power_save=1
+      options iwlwifi power_save=1
+      options iwldvm force_cam=0
+      options i915 enable_guc=3 enable_fbc=1
+    '';
+  };
+
+  fileSystems = {
+    "/" = zdevice "zpool/ephemeral/root";
+    "/home" = zdevice "zpool/ephemeral/home";
+    "/persist" = zdevice "zpool/persistent/data" // { neededForBoot = true; };
+    "/nix" = zdevice "zpool/persistent/nix";
+    "/depot" = zdevice "zpool/persistent/depot";
+
+    "/boot" = {
+      device = "/dev/disk/by-uuid/2487-3908";
+      fsType = "vfat";
+    };
+  };
+
+  networking = {
+    hostName = "zamalek";
+    domain = "tvl.su";
+    hostId = "ee399356";
+    networkmanager.enable = true;
+
+    extraHosts = ''
+      10.101.240.1 wifi.silja.fi
+    '';
+
+    nameservers = [
+      "8.8.8.8"
+      "8.8.4.4"
+    ];
+  };
+
+  hardware = {
+    cpu.intel.updateMicrocode = true;
+    bluetooth.enable = true;
+    enableRedistributableFirmware = true;
+    opengl.enable = true;
+  };
+
+  services.xserver.libinput.touchpad.clickMethod = "clickfinger";
+  services.xserver.libinput.touchpad.tapping = false;
+  services.avahi.enable = true;
+  services.tailscale.enable = true;
+  powerManagement.powertop.enable = true;
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/tazjin/presentations/bootstrapping-2018/default.nix b/users/tazjin/presentations/bootstrapping-2018/default.nix
index 0dff14b2a1..2775d0b3fb 100644
--- a/users/tazjin/presentations/bootstrapping-2018/default.nix
+++ b/users/tazjin/presentations/bootstrapping-2018/default.nix
@@ -4,24 +4,26 @@
 
 with pkgs;
 
-let tex = texlive.combine {
-  inherit (texlive)
-    beamer
-    beamertheme-metropolis
-    etoolbox
-    euenc
-    extsizes
-    fontspec
-    lualibs
-    luaotfload
-    luatex
-    minted
-    ms
-    pgfopts
-    scheme-basic
-    translator;
-};
-in stdenv.mkDerivation {
+let
+  tex = texlive.combine {
+    inherit (texlive)
+      beamer
+      beamertheme-metropolis
+      etoolbox
+      euenc
+      extsizes
+      fontspec
+      lualibs
+      luaotfload
+      luatex
+      minted
+      ms
+      pgfopts
+      scheme-basic
+      translator;
+  };
+in
+stdenv.mkDerivation {
   name = "nuug-bootstrapping-slides";
   src = ./.;
 
diff --git a/users/tazjin/presentations/tvix-eval-2023/README.md b/users/tazjin/presentations/tvix-eval-2023/README.md
new file mode 100644
index 0000000000..b14ba8ff50
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/README.md
@@ -0,0 +1,12 @@
+These are the slides for a talk at the Moscow Rust User Group /
+ProgMSK on 2023-09-07.
+
+After building, the presentation can be launched with `pdfpc`
+(available in `nixpkgs`), like this:
+
+```
+pdfpc --windowed=both result/presentation.pdf -R presentation.pdfpc -d 40
+```
+
+I keep the JSON file formatted using `jq . presentation.pdfpc | sponge
+presentation.pdfpc` for easier diffs.
diff --git a/users/tazjin/presentations/tvix-eval-2023/cppnix-example-lexer.cpp b/users/tazjin/presentations/tvix-eval-2023/cppnix-example-lexer.cpp
new file mode 100644
index 0000000000..7c52bce8b6
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/cppnix-example-lexer.cpp
@@ -0,0 +1,13 @@
+attrpath
+  : attrpath '.' attr {
+    $$ = $1; $1->push_back(AttrName(data->symbols.create($3)));
+  }
+  | attrpath '.' string_attr
+    { $$ = $1;
+      ExprString * str = dynamic_cast<ExprString *>($3);
+      if (str) {
+          $$->push_back(AttrName(data->symbols.create(str->s)));
+          delete str;
+      } else
+          $$->push_back(AttrName($3));
+    }
diff --git a/users/tazjin/presentations/tvix-eval-2023/cppnix-example-smuggling.cpp b/users/tazjin/presentations/tvix-eval-2023/cppnix-example-smuggling.cpp
new file mode 100644
index 0000000000..37b9219b2e
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/cppnix-example-smuggling.cpp
@@ -0,0 +1,12 @@
+struct Env {
+  // ... some struct fields ...
+  Value* values[0];
+};
+
+// ....
+
+if (env->type == Env::HasWithExpr) {
+  // ...
+  evalAttrs(*env->up, (Expr *) env->values[0], *v, noPos, "<borked>");
+  //                  ^^^^^^^^^^^^^^^^^^^^^^^
+}
diff --git a/users/tazjin/presentations/tvix-eval-2023/default.nix b/users/tazjin/presentations/tvix-eval-2023/default.nix
new file mode 100644
index 0000000000..a4d855197c
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/default.nix
@@ -0,0 +1,63 @@
+{ depot, pkgs, ... }:
+
+let
+  inherit (pkgs) fontconfig texlive stdenv imagemagick runCommand qrencode;
+
+  tex = texlive.combine {
+    inherit (texlive)
+      babel
+      babel-russian
+      beamer
+      beamertheme-metropolis
+      etoolbox
+      euenc
+      extsizes
+      fontspec
+      listings
+      xetex
+      minted
+      ms
+      pgfopts
+      scheme-basic
+      translator;
+  };
+
+  linksQrCode = runCommand "qrcode.png" { } ''
+    ${qrencode}/bin/qrencode -o code.png -s 8 \
+      --background=fafafa \
+      --foreground=000000 \
+      'https://tazj.in/blog/tvix-eval-talk-2023'
+
+    # latex has trouble with the PDF produced by qrencode
+    ${imagemagick}/bin/convert code.png $out
+  '';
+in
+stdenv.mkDerivation {
+  name = "progmsk-tvix-eval";
+  src = ./.;
+
+  nativeBuildInputs = [ tex imagemagick fontconfig ];
+
+  FONTCONFIG_FILE = pkgs.makeFontsConf {
+    fontDirectories = with pkgs; [ jetbrains-mono fira fira-code fira-mono ];
+  };
+
+  buildPhase = ''
+    # LaTeX needs a cache folder in /home/ ...
+    mkdir home
+    export HOME=$PWD/home
+
+    cp ${depot.tvix.logo}/logo.png tvix-logo.png
+    cp ${linksQrCode} qrcode.png
+
+    # As usual, TeX needs to be run twice ...
+    ${tex}/bin/xelatex presentation.tex
+    ${tex}/bin/xelatex presentation.tex
+  '';
+
+  installPhase = ''
+    mkdir -p $out
+    cp presentation.pdf $out/
+    cp $src/presentation.pdfpc $out/
+  '';
+}
diff --git a/users/tazjin/presentations/tvix-eval-2023/presentation.pdfpc b/users/tazjin/presentations/tvix-eval-2023/presentation.pdfpc
new file mode 100644
index 0000000000..ab5cba68bf
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/presentation.pdfpc
@@ -0,0 +1,98 @@
+{
+  "pdfpcFormat": 2,
+  "duration": 40,
+  "disableMarkdown": false,
+  "noteFontSize": 20,
+  "pages": [
+    {
+      "idx": 1,
+      "label": "2",
+      "overlay": 0,
+      "note": "ะŸั€ะธะฒะตั‚, ะผะตะฝั ะทะพะฒัƒั‚ .... ะฏ ัƒะถะต ะผะฝะพะณะพ ะปะตั‚, ั 2016ะณ. ะฟั€ะธะผะตั€ะฝะพ, ะฟะธัˆัƒ ะฝะฐ ะ ะฐัั‚ะต, ะธ ั…ะพั‚ั ะฝะฐ ั€ะฐะฑะพั‚ะต ัƒ ะผะตะฝั ั‡ะฐัั‚ะพ ะฑั‹ะฒะฐัŽั‚ ะดั€ัƒะณะธะต ัะทั‹ะบะธ, ะ ะฐัั‚ - ะปัŽะฑะธะผั‹ะน ะผะพะน ัะทั‹ะบ.\n\nะŸะฐั€ัƒ ะปะตั‚ ะฝะฐะทะฐะด, ะฒะพ ะฒั€ะตะผั ะšะพะดะธะดะฐ, ั ัะพะทะดะฐะป ะพะฝะปะฐะนะฝ-ะบะพะผะผัŽะฝะธั‚ะธ ะขะ’ะ›, ะธ ัะตะณะพะดะฝั ั…ะพั‡ัƒ ะฒะฐะผ ะพะฑ ะพะดะฝะพะผ ะธะท ะฝะฐัˆะธั… ะฟั€ะพะตะบั‚ะพะฒ ั€ะฐััะบะฐะทะฐั‚ัŒ."
+    },
+    {
+      "idx": 2,
+      "label": "3",
+      "overlay": 0,
+      "note": "ะผะพะฝะพั€ะตะฟะพ: ะพะฑัŠััะฝะธั‚ัŒ. ะ’ะตััŒ ะบะพะด ะพั€ะณะฐ ะฒ ะพะดะฝะพะผ ะผะตัั‚ะต. ะ•ะดะธะฝะฝั‹ะน ั‚ัƒะปะธะฝะณ. ะœะฝะพะณะพ ะธะท ะฝะฐั ั€ะฐะฝัŒัˆะต ั€ะฐะฑะพั‚ะฐะปะธ ะฒ ะบะพะผะฟะฐะฝะธัั…, ะณะดะต ั‚ะฐะบ ะดะตะปะฐัŽั‚ (ะฝะฟ ะ“ัƒะณะป).\n\nะœั‹ ั…ะพั‚ะตะปะธ ัะพะทะดะฐั‚ัŒ ั‚ะฐะบะพะน ะถะต ั‚ัƒะปะธะฝะณ, ะฝะพ ะพั‚ะบั€ั‹ั‚ะพ. ะะพ ัƒ ะฝะฐั ะผะตะฝัŒัˆะต ั€ะตััƒั€ัะพะฒ ั‡ะตะผ ัƒ ะ“ัƒะณะปะฐ, ะฝะฐะผะฝะพะณะพ ะผะตะฝัŒัˆะต. ะŸั€ะธัˆะปะพััŒ ะฒั‹ะฑั€ะฐั‚ัŒ ัั„ั„. ัะฟะพัะพะฑ.\nะคะพะบัƒัะธั€ัƒะตะผ ะฝะฐ ะะธะบั, ะฟะพั‚ะพะผัƒ ั‡ั‚ะพ (...). ะ•ัั‚ัŒ ะดะพะบะปะฐะด.\n\nะœั‹ ะฝะฐั‡ะฐะปะธ ะตะณะพ ะฝะต ั‚ะพะปัŒะบะพ ะดะปั ะฟะฐะบะตั‚ะพะฒ ะธัะฟะพะปัŒะทะพะฒะฐั‚ัŒ (ะบะพะฝั„ะธะณ ะธ ั‚ะด.), ั…ะพั‚ะตะปะพััŒ ั€ะตัˆะตะฝะธะต, ะบะพั‚ะพั€ะพะต ั€ะฐะฑะพั‚ะฐะตั‚ ะฒะตะทะดะต."
+    },
+    {
+      "idx": 3,
+      "label": "4",
+      "overlay": 0,
+      "note": "ะ‘ัƒะดะตะผ ั„ะพะบัƒัะธั€ะพะฒะฐั‚ัŒ ั‚ะพะปัŒะบะพ ะฝะฐ ัะทั‹ะบ ัะตะนั‡ะฐั. ะžัั‚ะฐะปัŒะฝั‹ะต ั‡ะฐัั‚ะธ ะธะฝั‚ะตั€ะตัะฝั‹ะต, ะฝะพ ะฝะต ัะตะณะพะดะฝั.\nะฏะทั‹ะบ ะปะตะฝะธะฒั‹ะน, ะทะฝะฐั‡ะธั‚ ั‚ะพะปัŒะบะพ ะฒั‹ั‡ะธัะปัะตะผ ะบะพะด, ะบะพะณะดะฐ ะตะณะพ ั€ะตะทัƒะปัŒั‚ะฐั‚ ะฝัƒะถะตะฝ ะณะดะต-ั‚ะพ.\nะ—ะฝะฐั‡ะธั‚, ะฝะฐะผ ะฝัƒะถะตะฝ ั€ะฐะฝั‚ะฐะนะผ-ะฟั€ะตะดัั‚ะฐะฒะปะตะฝะธะต ะพั‚ะปะพะถะตะฝะฝั‹ั… ะฒั‹ั‡ะธัะปะตะฝะธะน.\nะžั€ะณะฐะฝะธั‡ะฝะพ ั€ะฐะทะฒะธะฒะฐะปัั: ะดะพะฑะฐะฒะธะปะธ ั„ะธั‡ะธ, ะบะพะณะดะฐ ะฝัƒะถะดะฐะปะธััŒ. ะœะฝะพะณะพ ั„ัƒะฝะบั‚ั†ะธะธ ั€ะฐะฑะพั‚ะฐัŽั‚ ั‚ะพะปัŒะบะพ ัะปัƒั‡ะฐะนะฝะพ, ะบะพะผะฑะธะฝะฐั†ะธะธ ั„ะธั‡ - ั‡ะฐัั‚ะพ ัั‚ั€ะฐะฝะฝะพ. (ัˆัƒั‚ะบะฐ ะฟั€ะพ ะก++?)\nะะพ ะตัั‚ัŒ ั…ะพั€ะพัˆะธะน ั„ะฐะบั‚ะพั€: ะฒะตััŒ ะฟัƒะฑะปะธั‡ะฝั‹ะน ะบะพะด ะฒ ะฟั€ะธะฝั†ะธะฟะต ะฒ ะพะดะฝะพะผ ั€ะตะฟะพ. ะžะฑัŠััะฝัั‚ัŒ nixpkgs."
+    },
+    {
+      "idx": 4,
+      "label": "5",
+      "overlay": 0,
+      "note": "ะขะตะบัƒัˆะฐั ะธะผะฟะปะตะผะตะฝั‚ะฐั†ะธั ะฝะฐ ะก++. ะ’ะพั‚ ะฟั€ะธะผะตั€. ะšั‚ะพ-ั‚ะพ ะทะดะตััŒ ะฟะพะฝะธะผะฐะตั‚, ั‡ั‚ะพ ะผั‹ ะฒะธะดะตะผ? ะญั‚ะพ ั‡ะฐัั‚ัŒ ะฟะฐั€ัะตั€ะฐ ะฒ ัะบ, ะฝะพ ั‚ัƒั‚ ัะพะทะดะฐัŽั‚ ั€ะฐะฝั‚ะฐะนะผ-ะทะฝะฐั‡ะตะฝะธั ะฒะพ ะฒั€ะตะผั ะฟะฐั€ัะธะฝะณะฐ. ะžั‡ะตะฝัŒ ัะปะพะถะฝะพ ะฟะพะฝะธะผะฐั‚ัŒ, ั‡ะธั‚ะฐั‚ัŒ, ะดะตะฑะฐะถะธั‚ัŒ ะธ ั‚ะฐะบ ะดะฐะปะตะต.\nะงะธั‚ะฐะปะธ ะฟะฐั€ัะตั€, ะธ ะดะฐะถะต ะฝะฐัˆะปะธ ั‚ะฐะผ ะฝะตะธะทะฒะตัั‚ะฝั‹ะต ั„ะธั‡ะธ ัะทั‹ะบะฐ."
+    },
+    {
+      "idx": 5,
+      "label": "6",
+      "overlay": 0,
+      "note": "ะ’ั‚ะพั€ะพะน ะฟั€ะธะผะตั€. ะ•ัั‚ัŒ ัั‚ั€ะฐะบั‚ Env, ะบะพั‚ะพั€ะฐั ะธัะฟะพะปัŒะทัƒะตั‚ัั ะฒะพ ะผะฝะพะณะธั… ะผะตัั‚ะฐั… ะฒ ะบะพะดะต. ะขะฐะผ ะผะฐััะธะฒ ั‚ะธะฟะฐ Value.\nะ’ะพั‚ ะธัะฟะพะปัŒะทะพะฒะฐะฝะธะต ัั‚ะพะณะพ ะผะฐััะธะฒะฐ. ะงั‚ะพ ะผั‹ ะฒะธะดะตะผ? ะšั‚ะพ ะฟะพะฝะธะผะฐะตั‚?\nะ”ะฐ, ั‚ะฐะผ ะฝะฐ ัะฐะผะพะผ ะดะตะปะต ะฟั€ะพะธัั…ะพะดะธั‚ ะบะฐัั‚ ะฝะฐ ะดั€ัƒะณะพะน ั‚ะธะฟ. ะ—ะฝะฐั‡ะธั‚, ะฒ ัั‚ั€ัƒะบั‚ัƒั€ะต ะดะพะฑะฐะฒะปััŽั‚ ะดะฐะฝะฝั‹ะต, ะบะพั‚ะพั€ั‹ะต ะฝะต ะฟะพะดะพะนะดะตั‚. ะžั‡ะตะฝัŒ unsafe!\n\nะ”ะฐ, ั‡ั‚ะพ ะถะต ะดะตะปะฐั‚ัŒ? ะŸั‹ั‚ะฐะปะธััŒ ะฟะพั‡ะธัั‚ะธั‚ัŒ ะบะพะด, ะฝะพ ัะปัƒั‡ะธะปะพััŒ burnout ะพั‡ะตะฝัŒ ะฑั‹ัั‚ั€ะพ. ะœะตะฝัะตัˆัŒ ะพะดะฝัƒ ะผะฐะปะตะฝัŒะบัƒัŽ ัˆั‚ัƒะบัƒ -> segfaults.\nะŸะพั‡ะตะผัƒ ะบะพะด ะฒะพั‚ ั‚ะฐะบะพะน? -> ะพะฑัŠััะฝัั‚ัŒ.\nะŸั€ะธัˆะปะฐััŒ ะพั‡ะตะฒะธะดะฝะฐั ะธะดะตั."
+    },
+    {
+      "idx": 6,
+      "label": "6",
+      "overlay": 1,
+      "note": "ะŸะตั€ะตะฟะธัะฐั‚ัŒ ะฟั€ะพะตะบั‚ั‹ ะฟะพะปะฝะพัั‚ัŒัŽ, ะพะฑั‹ั‡ะฝะพ ะพั‡ะตะฝัŒ ัะปะพะถะฝะพ. ะะพ ะผั‹ ะผะพะถะตะผ ะผะตะฝัั‚ัŒ ะฐั€ั…., ะธ ะพะฑั…ะพะดะธั‚ัŒ ะฟะตั€ะตะฟะธัะธะฒะฐะฝะธะต ะฝะตะบะพั‚ะพั€ั‹ั… ั‡ะฐัั‚ะตะน.\nะ ะฐัั‚ - ะฝะฐะผ ะพั‡ะตะฒะธะดะฝั‹ะน ะฒั‹ะฑะพั€ ะดะปั ะธะผะฟะปะตะผะตะฝั‚ะฐั†ะธั ัะทั‹ะบะฐ. ะœะฝะพะณะพ ะฝะฐั ะทะฝะฐัŽั‚ ะ ะฐัั‚, ะธ ะฒ ั†ะตะปะพะผ, ะฟะพั‡ะตะผัƒ ะธะผะตะฝะฝะพ ะ ะฐัั‚, ะฒั‹ ัƒะถะต ัะฐะผะธ ะฟะพะฝะธะผะฐะตั‚ะต.\n\nะœั‹ ะพั‚ NLNet, ะพั€ะณะฐะฝะธะทะฐั†ะธั, ..., ะฟะพะปัƒั‡ะธะปะธ ะดะตะฝะณะธ ะทะฐ ัั‚ะพะณะพ ะธ ะฝะฐั‡ะฐะปะธ ั ัะทั‹ะบะพะผ. ะญั‚ะพั‚ ะฟั€ะพะตะบั‚ ะฝะฐะทั‹ะฒะฐะตะผ tvix-eval.\n\nะ•ัั‚ัŒ ะตั‰ะต ะพะดะฝะฐ ะฒะฐะถะฝะฐั ะฟั€ะธั‡ะธะฝะฐ ะดะปั ะฒั‹ะฑะพั€ะฐ ะ ะฐัั‚ะฐ."
+    },
+    {
+      "idx": 7,
+      "label": "6",
+      "overlay": 2,
+      "note": "ะŸะฐั€ัƒ ะปะตั‚ ะฝะฐะทะฐะด, ัˆะฒะตะดัะบะธะน ะฟะฐั€ะตะฝัŒ ัŽะทะตั€ะฝะตะนะผะพะผ, ะบะพั‚ะพั€ั‹ะน ะฝะตะปัŒะทั ะฟั€ะพะธัะฝะพัะธั‚ัŒ, ะฝะฐะฟะธัะฐะป ะฝะฐ ะ ะฐัั‚ะต ะพั‡ะตะฝัŒ ะฑั‹ัั‚ั€ั‹ะน ะธ ะฒ ั†ะตะปะพะผ ั…ะพั€ะพัˆะธะน ะฟะฐั€ัะตั€ ะดะปั ะะธะบัะฐ.\nะญั‚ะพั‚ ะฟะฐั€ัะตั€ ัƒะถะต ะธัะฟะพะปัŒะทัƒะตั‚ัั ะฒ ั€ะฐะทะฝั‹ั… ั ะะธะบัะพะผ ัะฒัะทะฐะฝะฝั‹ั… ะฟั€ะพะตะบั‚ะฐั…. ะžะฝ ัะบะพั€ะตะต ะฒัะตะณะพ ะฒ ะฟัƒั‚ะธ ัั‚ะฐั‚ัŒ ะดะตั„ะพะปัŒั‚ะฝะธะผ ะฟะฐั€ัะตั€ะพะผ ะะธะบัะฐ.\nะšะพะฝะตั‡ะฝะพ, ะฝะตะฟะปะพั…ะพ ะตัะปะธ ะผั‹ ะตะณะพ ั‚ะพะถะต ะธัะฟะพะปัŒะทัƒะตะผ.\nะš ัะพะถะฐะปะตะฝะธัŽ, ะฐะฒั‚ะพั€ ั€ะฝะธะบัะฐ ัƒะผะตั€ะป ะฒ 2021 ะณะพะดัƒ. ะœะฐะปะธ ะธัะฒะตัั‚ะฝะพ ะพ ั‚ะพะผ, ั‡ั‚ะพ ัะปัƒั‡ะธะปะพััŒ. ะœั‹ ะตะผัƒ ะพั‡ะตะฝัŒ ะฑะปะฐะณะพะดะฐั€ะฝั‹ะต, ะธ ั ะฟั€ะพัั‚ะพ ั…ะพั‚ะตะป ะตะณะพ ะทะดะตััŒ ัƒะฟะพะผัะฝัƒั‚ัŒ."
+    },
+    {
+      "idx": 9,
+      "label": "8",
+      "overlay": 0,
+      "note": "ะฟะพะบะฐะทะฐั‚ัŒ opcode.rs, compiler/mod (compile_binop)\n\nั‡ั‚ะพะฑั‹ ะพะฝ ะฝะต ั€ะฐะทะถะธั€ะตะป (ะฟั€ะพ variant_size_differences)\n"
+    },
+    {
+      "idx": 10,
+      "label": "9",
+      "overlay": 0,
+      "note": "ะฟะพะบะฐะทะฐั‚ัŒ value/mod.rs, ะฟะพั‚ะพะผ value/list.rs\n\nะบะพั€ะพั‚ะบะฐั ะพะฑัŠััะฝะตะฝะธะต ัะธั‚ัƒะฐั†ะธะธ ั Gc<...> vs. Rc<...>"
+    },
+    {
+      "idx": 11,
+      "label": "10",
+      "overlay": 0,
+      "note": "ะฟะพะบะฐะทะฐั‚ัŒ vm/mod.rs\n\nะฟะพัะปะตะดะพะฒะฐั‚ะตะปัŒะฝะพ ะฒั‹ะฟะพะปัŒะฝัะตั‚ ะธะฝัั‚ั€ัƒะบั‚ั†ะธะธ ะฒ execute_bytecode\n\nัะฝะฐั‡ะฐะปะฐ ะฝะฐ ะฐะปั„ะฐะฒะธั‚ะฝั‹ะผ ะฟะพั€ัะดะบะต, ะฟะพั‚ะพะผ ั ะฟะพะผะพั‰ัŒัŽ ะฟั€ะพั„ะฐะนะปะตั€ะฐ ะผะตะฝัะปะธ ัั‚ะพ"
+    },
+    {
+      "idx": 12,
+      "label": "10",
+      "overlay": 1,
+      "note": "ะฟะพะบะฐะทะฐั‚ัŒ ะดะธะฐะณั€ะฐะผะผัƒ\n\nะณะตะฝะตั€ะฐั‚ะพั€ั‹ะน ะผะพะถะฝะพ ะฟั€ะธะพัั‚ะฐะฝะพะฒะธั‚ัŒ\n\nTCO - ั…ะฒะพัั‚ะพะฒั‹ะน ะฒั‹ะทะพะฒ\n\nasync - ะพั‡ะตะฝัŒ ะฝะฐัะทั‡ะธะฒะฝั‹ะน (intrusive), ะฝะฐะดะพ ะฑั‹ะปะพ ะตะณะพ ะฒะตะทะดะต ะดะพะฑะฐะฒะธั‚ัŒ, ะฝะตัƒะดะพะฑ"
+    },
+    {
+      "idx": 13,
+      "label": "10",
+      "overlay": 2,
+      "note": "ะ—ะฐะฒะธัะธะผะพ ะพั‚ ะฒั€ะตะผะตะฝะธ, ะผะพะถะฝะพ ะปะธะฑะพ ั‚ะพะปัŒะบะพ ะฟั€ะพ tvixbolt, ะปะธะฑะพ ั‚ะพะถะต ะฟั€ะพ ั‚ะตัั‚ั‹ ะธะท cppnix"
+    },
+    {
+      "idx": 14,
+      "label": "11",
+      "overlay": 0,
+      "note": "ะฝะฐ ัะฐะผะพะผ ะดะตะปะต ัƒะดะธะฒะธั‚ะตะปัŒะฝะพ ะปะตะณะบะพ, ะฝะพ ัั‚ะฐะปะบะธะฒะฐะปะธััŒ ั ะฟั€ะพะฑะปะตะผะพะน, ั‡ั‚ะพ ะพะฝ ะธะฝะพะณะดะฐ ะฟะตั€ะตัั‚ะฐะป ั€ะฐะฑะพั‚ะฐั‚ัŒ\n\nะฟะพะบะฐะทะฐั‚ัŒ ะฟั€ะธะผะตั€ ั SystemTime::now\n\nะตัั‚ัŒ ะบะพะต-ะบะฐะบะธะต ะฑะธะฑะปะธะพั‚ะตะบะธ, ะบะพั‚ะพั€ั‹ะต ะผะฑ ะฟะพะผะพะณัƒั‚, ะฝะพ ะผั‹ ะธั… ะฟะพะบะฐ ะฝะต ะฟั€ะพะฒะตั€ะธะปะธ\n\nะฒ ั†ะตะปะพะผ, wasm ะฝะฐ ั€ะฐัั‚ะต ะดะพะฒะพะปัŒะฝะพ ัƒะดะพะฑะฝะพ"
+    },
+    {
+      "idx": 15,
+      "label": "12",
+      "overlay": 0,
+      "note": "ะพั‚ะบั€ั‹ั‚ั‹ะน ะฟั€ะพะตะบั‚, ะฟั€ะธะฝะธะผะฐะตะผ ะบะพะผะผะธั‚ั‹ ะพั‚ ะฒัะตั…\n\nะตัั‚ัŒ ะตั‰ะต ะฑะฐะณะธ, TODOs, ะธ ั‚ะด ะฒ tvix-eval\n\nะฝะพ ะตัั‚ัŒ ั‚ะพะถะต ะพัั‚ะฐะปัŒะฝั‹ะต ั‡ะฐัั‚ะธ ั‚ะฒะธะบัะฐ, ั‡ั‚ะพ-ั‚ะพ ะฝะฐะนะดะตั‚ัั"
+    },
+    {
+      "idx": 16,
+      "label": "13",
+      "overlay": 0,
+      "note": "ัะฟะฐัะธะฑะพ ะฒัะตะผ, ะฒะพั‚ ััั‹ะปะบะธ, ะฝะฐ QR-ะบะพะดะต ะตัั‚ัŒ ะฒัะต ะฒะพั‚ ัั‚ะพั‚ ะฒะพั‚, ะธ ั‚ะฐะผ ั‚ะพะถะต ะฟะพั‚ะพะผ ะดะพะฑะฐะฒะปัŽ ัะฐะผ ะดะพะบะปะฐะด\n\nะตั‰ะต ะทะฐะฒั‚ั€ะฐ ะฝะฐั‡ะธะฝะฐะตั‚ัั NixCon, ะตัะปะธ ะฒะฐะผ ะฒะดั€ัƒะณ ะธะฝั‚ะตั€ะตัะฝะพ, ะผะพะถะฝะพ ะพะฝะปะฐะนะฝ ะฟะพัะผะพั‚ั€ะตั‚ัŒ. ะขะฐะผ ะฑัƒะดะตั‚ ะดะพะบะปะฐะด ะฟั€ะพ tvix ั‚ะพะถะต, ะฝะพ ะพะฑ ะพัั‚ะฐะปัŒะฝั‹ั… ั‡ะฐัั‚ัั…."
+    }
+  ]
+}
diff --git a/users/tazjin/presentations/tvix-eval-2023/presentation.tex b/users/tazjin/presentations/tvix-eval-2023/presentation.tex
new file mode 100644
index 0000000000..294dad7942
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/presentation.tex
@@ -0,0 +1,148 @@
+\documentclass[12pt]{beamer}
+
+\usepackage[utf8]{inputenc}
+\usepackage[main=russian,english]{babel}
+\usepackage{fontspec}
+\usepackage{listings}
+
+\setmainfont{JetBrains Mono}
+\setsansfont{JetBrains Mono}
+
+\usetheme{metropolis}
+\newenvironment{code}{\ttfamily}{\par}
+\title{tvix-eval \\ ะบะพะผะฟะธะปัั‚ะพั€ ะธ ั€ะฐะฝั‚ะฐะนะผ ะดะปั Nix, ะฝะฐ Rust}
+
+\titlegraphic{\vspace{4.8cm}\flushright\includegraphics[width=6cm,keepaspectratio=true]{tvix-logo.png}}
+
+\date{2023-09-07}
+\author{ะ’ะธะฝัะตะฝั‚ ะะผะฑะพ}
+\institute{TVL}
+
+\begin{document}
+  %% Slide -1 (before counter):
+  \begin{frame}
+    \begin{center}
+      \titlepage
+    \end{center}
+  \end{frame}
+
+  %% Slide 0 (title):
+  \begin{frame}
+    \begin{center}
+      \titlepage
+    \end{center}
+  \end{frame}
+
+  %% Slide 1:
+  \begin{frame}{\textbf{ะข}he \textbf{V}irus \textbf{L}ounge}
+    \begin{itemize}
+    \item ะพะฝะปะฐะนะฝ-ะบะพะผัŒัŽะฝะธั‚ะธ, ะทะฐะฝะธะผะฐัŽั‰ะตะตัั ั‚ัƒะปะธะฝะณะพะผ ะดะปั ะผะพะฝะพั€ะตะฟะพ
+    \item ะพัะฝะพะฒะฝะพะน ั„ะพะบัƒั ะฝะฐ Nix
+    \item Nix ะฝะต ั‚ะพะปัŒะบะพ ะดะปั ัะฑะพั€ะบะธ ะฟะฐะบะตั‚ะพะฒ
+    \item ะฅะพั‚ะตะปะพััŒ ั€ะตัˆะตะฝะธะต, ั‡ั‚ะพะฑั‹ ะธัะฟะพะปัŒะทะพะฒะฐั‚ัŒ Nix ะฒะตะทะดะต
+    \end{itemize}
+  \end{frame}
+
+  %% Slide 2:
+  \begin{frame}{ะžัะพะฑะตะฝะฝะพัั‚ะธ ัะทั‹ะบะฐ Nix}
+    \begin{itemize}
+    \item ะ›ะตะฝะธะฒั‹ะน ัะทั‹ะบ. ะ’ั‹ั‡ะธัะปัั‚ัŒ ะฒัะต ัั€ะฐะทัƒ ะฝะตะปัŒะทั.
+    \item ะฏะทั‹ะบ ั€ะฐะทะฒะธะฒะฐะปัั ะพั€ะณะฐะฝะธั‡ะฝะพ.
+    \item ะ‘ะพะปัŒัˆะธะฝัั‚ะฒะพ ะบะพะดะฐ ะฝะฐ Nix --- ะฒ ะพะดะฝะพะผ ะผะตัั‚ะต: \begin{code}nixpkgs\end{code}
+    \end{itemize}
+  \end{frame}
+
+  %% Slide 3:
+  \begin{frame}{ะขะตะบัƒั‰ะฐั ะธะผะฟะปะตะผะตะฝั‚ะฐั†ะธั: C++ Nix}
+    \lstinputlisting[
+      language=c++,
+      basicstyle={\scriptsize}
+    ]{cppnix-example-lexer.cpp}
+  \end{frame}
+
+  %% Slide 4:
+  \begin{frame}{ะขะตะบัƒั‰ะฐั ะธะผะฟะปะตะผะตะฝั‚ะฐั†ะธั: C++ Nix}
+    \lstinputlisting[
+      language=c++,
+      basicstyle={\scriptsize}
+    ]{cppnix-example-smuggling.cpp}
+  \end{frame}
+
+  %% Slide 5:
+  \section{``Let's rewrite it in Rust!''}
+
+  %% Slide 6:
+  \section*{ะกะฟะฐัะธะฑะพ, jD91mZM2!\\\normalsize{ะฐะฒั‚ะพั€ ``rnix-parser''; *2002 - \textdagger 2021}}
+
+  %% Slide 7:
+  \begin{frame}{tvix-eval, - (ัะทั‹ะบ) Nix, ะฝะฐ Rust}
+    \begin{itemize}
+    \item ะฝะฐะฟะธัะฐะฝะพ ั ััƒั‰ะตัั‚ะฒัƒัŽั‰ะธะผ ะฟะฐั€ัะตั€ะพะผ
+    \item bytecode-ะธะฝั‚ะตั€ะฟั€ะตั‚ะฐั‚ะพั€, ะฒะผะตัั‚ะพ tree-walk
+    \item ะดะพะปะถะฝะฐ ั€ะฐะฑะพั‚ะฐั‚ัŒ ะฝะต ั‚ะพะปัŒะบะพ ะดะปั ะพัั‚ะฐะปัŒะฝั‹ั… ั‡ะฐัั‚ะตะน tvix
+    \end{itemize}
+  \end{frame}
+
+  %% Slide 8:
+  \begin{frame}{tvix-eval, ะพัะฝะพะฒะฝั‹ะต ั‡ะฐัั‚ะธ}
+    \begin{enumerate}
+    \item ัะพะฑัั‚ะฒะตะฝะฝั‹ะน ะฑะฐะนั‚ะบะพะด ะธ ะบะพะผะฟะธะปัั‚ะพั€
+    \end{enumerate}
+  \end{frame}
+
+  %% ะฟะพะบะฐะทะฐั‚ัŒ opcode.rs, ะฑั‹ัั‚ั€ะพ ะฟะพะบะฐะทะฐั‚ัŒ compiler/mod.rs
+
+  %% Slide 9:
+  \begin{frame}{tvix-eval, ะพัะฝะพะฒะฝั‹ะต ั‡ะฐัั‚ะธ}
+    \begin{enumerate}
+    \item ัะพะฑัั‚ะฒะตะฝะฝั‹ะน ะฑะฐะนั‚ะบะพะด ะธ ะบะพะผะฟะธะปัั‚ะพั€
+    \item ะฟั€ะตะดัั‚ะฐะฒะปะตะฝะธะต ะทะฝะฐั‡ะตะฝะธะน ัะทั‹ะบะฐ ะฒ ั€ะฐะฝั‚ะฐะนะผะต
+    \end{enumerate}
+  \end{frame}
+
+  %% ะฟะพะบะฐะทะฐั‚ัŒ Value
+
+  %% Slide 10:
+  \begin{frame}{tvix-eval, ะพัะฝะพะฒะฝั‹ะต ั‡ะฐัั‚ะธ}
+    \begin{enumerate}
+    \item ัะพะฑัั‚ะฒะตะฝะฝั‹ะน ะฑะฐะนั‚ะบะพะด ะธ ะบะพะผะฟะธะปัั‚ะพั€
+    \item ะฟั€ะตะดัั‚ะฐะฒะปะตะฝะธะต ะทะฝะฐั‡ะตะฝะธะธ ัะทั‹ะบะฐ ะฒ ั€ะฐะฝั‚ะฐะนะผะต
+    \item ... ะธ ัะฐะผ ั€ะฐะฝั‚ะฐะนะผ!
+    \end{enumerate}
+  \end{frame}
+
+  %% ะฟะพะบะฐะทะฐั‚ัŒ VM
+
+  \section{``ะŸะพะดะพะถะดะธ, ะฝะฐะฟะธัะฐั‚ัŒ ั€ะฐะฝั‚ะฐะนะผ ะถะต ะฝะต ั‚ะฐะบ ะฟั€ะพัั‚ะพ?''}
+
+  %% ะพะฑัŠััะฝะธั‚ัŒ ะฟั€ะพะฑะปะตะผัƒ ัะพ ัั‚ะตะบะพะผ ะธ ั€ะตัˆะตะฝะธะต, ะฟะพะบะฐะทะฐั‚ัŒ ะดะธะฐะณั€ะฐะผะผัƒ
+
+  \section{``ะ ะพั‚ะบัƒะดะฐ ะทะฝะฐะตัˆัŒ, ั‡ั‚ะพ ัั‚ะพ ะฒัะต ะฟั€ะฐะฒะธะปัŒะฝะพ ั€ะฐะฑะพั‚ะฐะตั‚?''}
+
+  %% ะฟะพะบะฐะทะฐั‚ัŒ ะบะฐะบ ั‚ะตัั‚ั‹ ั€ะฐะฑะพั‚ะฐัŽั‚
+  %% ะพะฑัŠััะฝะธั‚ัŒ ะดะตะฑะฐะณะธะฝะณ, ะขะฒะธะบัะฑะพะปั‚ ะธ ั‚ะด
+
+  %% Slide 10:
+  \begin{frame}{tvix-eval, ะฒ ะฑั€ะฐัƒะทะตั€ะต}
+    \begin{itemize}
+    \item ัƒะดะธะฒะธั‚ะตะปัŒะฝะพ ะปะตะณะบะพ ะดะตะปะฐั‚ัŒ
+    \item ะฝะพ ะตัั‚ัŒ ัะปะพะถะฝะพัั‚ะธ ะฒ \begin{code}std::\end{code}
+      % ะฟะพะบะฐะทะฐั‚ัŒ ะฟั€ะธะผะตั€
+    \end{itemize}
+  \end{frame}
+
+  %% Slide 11:
+  \begin{frame}{ะ ั‡ั‚ะพ ะดะฐะปัŒัˆะต?}
+    ะ’ tvix-eval ะตัั‚ัŒ ะตั‰ะต ะบะพะต-ะบะฐะบะธะต ะธะฝั‚ะตั€ะตัะฝั‹ะต ะฟั€ะพะฑะปะตะผั‹. ะœะพะถะตั‚ ั‚ั‹ ะธั…
+    ั€ะตัˆะธัˆัŒ?
+  \end{frame}
+
+  \begin{frame}{ะกะฟะฐัะธะฑะพ!}
+    \begin{center}
+      \includegraphics[width=6cm,keepaspectratio=true]{qrcode.png}
+
+      https://tazj.in/blog/tvix-eval-talk-2023 \\
+      t.me/tazjin | t.me/tazlog
+    \end{center}
+  \end{frame}
+\end{document}
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/.gitignore b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/.gitignore
new file mode 100644
index 0000000000..73b9c106db
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/.gitignore
@@ -0,0 +1,2 @@
+target/
+dist/
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.lock b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.lock
new file mode 100644
index 0000000000..ef879254cb
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.lock
@@ -0,0 +1,899 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "anymap2"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "boolinator"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
+
+[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
+[[package]]
+name = "bytes"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+
+[[package]]
+name = "cc"
+version = "1.0.83"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
+
+[[package]]
+name = "futures-io"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
+
+[[package]]
+name = "futures-task"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
+
+[[package]]
+name = "futures-util"
+version = "0.3.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
+name = "gloo"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28999cda5ef6916ffd33fb4a7b87e1de633c47c0dc6d97905fee1cdaa142b94d"
+dependencies = [
+ "gloo-console",
+ "gloo-dialogs",
+ "gloo-events",
+ "gloo-file",
+ "gloo-history",
+ "gloo-net",
+ "gloo-render",
+ "gloo-storage",
+ "gloo-timers",
+ "gloo-utils",
+ "gloo-worker",
+]
+
+[[package]]
+name = "gloo-console"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-dialogs"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-events"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-file"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7"
+dependencies = [
+ "gloo-events",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-history"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85725d90bf0ed47063b3930ef28e863658a7905989e9929a8708aab74a1d5e7f"
+dependencies = [
+ "gloo-events",
+ "gloo-utils",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_urlencoded",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-net"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-sink",
+ "gloo-utils",
+ "http",
+ "js-sys",
+ "pin-project",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-render"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-storage"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-worker"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a"
+dependencies = [
+ "anymap2",
+ "bincode",
+ "gloo-console",
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+
+[[package]]
+name = "http"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "implicit-clone"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c6ecbd987bb94f1f3c76c6787879756cf4b6f73bfff48d79308e8c56b46f65f"
+dependencies = [
+ "indexmap",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "js-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "memchr"
+version = "2.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "object"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "pin-project"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pinned"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b"
+dependencies = [
+ "futures",
+ "rustversion",
+ "thiserror",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+dependencies = [
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prokio"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488"
+dependencies = [
+ "futures",
+ "gloo",
+ "num_cpus",
+ "once_cell",
+ "pin-project",
+ "pinned",
+ "tokio",
+ "tokio-stream",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.105"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "tokio"
+version = "1.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
+dependencies = [
+ "backtrace",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.87"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+
+[[package]]
+name = "wasm-fs-demo"
+version = "0.1.0"
+dependencies = [
+ "yew",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "yew"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc"
+dependencies = [
+ "console_error_panic_hook",
+ "futures",
+ "gloo",
+ "implicit-clone",
+ "indexmap",
+ "js-sys",
+ "prokio",
+ "rustversion",
+ "serde",
+ "slab",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "yew-macro",
+]
+
+[[package]]
+name = "yew-macro"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b64c253c1d401f1ea868ca9988db63958cfa15a69f739101f338d6f05eea8301"
+dependencies = [
+ "boolinator",
+ "once_cell",
+ "prettyplease",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.toml b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.toml
new file mode 100644
index 0000000000..4a445065e4
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "wasm-fs-demo"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+yew = { version = "0.20.0", features = [ "csr" ]}
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/index.html b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/index.html
new file mode 100644
index 0000000000..e024c466cd
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/index.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta charset="utf-8" />
+        <title>wasm-fs-demo</title>
+    </head>
+</html>
diff --git a/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/src/main.rs b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/src/main.rs
new file mode 100644
index 0000000000..4ad177ad7a
--- /dev/null
+++ b/users/tazjin/presentations/tvix-eval-2023/wasm-fs-demo/src/main.rs
@@ -0,0 +1,41 @@
+use std::time::{SystemTime, UNIX_EPOCH};
+use yew::prelude::*;
+
+fn time_example() -> Html {
+    let epoch = match SystemTime::now().duration_since(UNIX_EPOCH) {
+        Ok(duration) => duration.as_secs(),
+        Err(err) => {
+            return html! {
+                format!("failed to calculate duration: {}", err)
+            }
+        }
+    };
+
+    html! {
+        <p>
+          {"Seconds since epoch: "}{epoch}
+        </p>
+    }
+}
+
+struct App;
+impl Component for App {
+    type Message = ();
+    type Properties = ();
+
+    fn create(_: &Context<Self>) -> Self {
+        Self
+    }
+
+    fn update(&mut self, _: &Context<Self>, _: Self::Message) -> bool {
+        false
+    }
+
+    fn view(&self, _: &Context<Self>) -> Html {
+        time_example()
+    }
+}
+
+fn main() {
+    yew::Renderer::<App>::new().render();
+}
diff --git a/users/tazjin/rlox/src/bytecode/compiler.rs b/users/tazjin/rlox/src/bytecode/compiler.rs
index 3e8a80653f..89584f19d7 100644
--- a/users/tazjin/rlox/src/bytecode/compiler.rs
+++ b/users/tazjin/rlox/src/bytecode/compiler.rs
@@ -63,9 +63,11 @@ enum Precedence {
     Equality,   // == !=
     Comparison, // < > <= >=
     Term,       // + -
-    Factor,     // * /
-    Unary,      // ! -
-    Call,       // . ()
+    Factor,     //
+    // 
+    // * /
+    Unary, // ! -
+    Call,  // . ()
     Primary,
 }
 
@@ -78,11 +80,7 @@ struct ParseRule<T: Iterator<Item = Token>> {
 }
 
 impl<T: Iterator<Item = Token>> ParseRule<T> {
-    fn new(
-        prefix: Option<ParseFn<T>>,
-        infix: Option<ParseFn<T>>,
-        precedence: Precedence,
-    ) -> Self {
+    fn new(prefix: Option<ParseFn<T>>, infix: Option<ParseFn<T>>, precedence: Precedence) -> Self {
         ParseRule {
             prefix,
             infix,
@@ -105,18 +103,16 @@ impl Precedence {
             Precedence::Factor => Precedence::Unary,
             Precedence::Unary => Precedence::Call,
             Precedence::Call => Precedence::Primary,
-            Precedence::Primary => panic!(
-                "invalid parser state: no higher precedence than Primary"
-            ),
+            Precedence::Primary => {
+                panic!("invalid parser state: no higher precedence than Primary")
+            }
         }
     }
 }
 
 fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
     match token {
-        TokenKind::LeftParen => {
-            ParseRule::new(Some(Compiler::grouping), None, Precedence::None)
-        }
+        TokenKind::LeftParen => ParseRule::new(Some(Compiler::grouping), None, Precedence::None),
 
         TokenKind::Minus => ParseRule::new(
             Some(Compiler::unary),
@@ -124,57 +120,33 @@ fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
             Precedence::Term,
         ),
 
-        TokenKind::Plus => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Term)
-        }
+        TokenKind::Plus => ParseRule::new(None, Some(Compiler::binary), Precedence::Term),
 
-        TokenKind::Slash => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Factor)
-        }
+        TokenKind::Slash => ParseRule::new(None, Some(Compiler::binary), Precedence::Factor),
 
-        TokenKind::Star => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Factor)
-        }
+        TokenKind::Star => ParseRule::new(None, Some(Compiler::binary), Precedence::Factor),
 
-        TokenKind::Number(_) => {
-            ParseRule::new(Some(Compiler::number), None, Precedence::None)
-        }
+        TokenKind::Number(_) => ParseRule::new(Some(Compiler::number), None, Precedence::None),
 
-        TokenKind::True => {
-            ParseRule::new(Some(Compiler::literal), None, Precedence::None)
-        }
+        TokenKind::True => ParseRule::new(Some(Compiler::literal), None, Precedence::None),
 
-        TokenKind::False => {
-            ParseRule::new(Some(Compiler::literal), None, Precedence::None)
-        }
+        TokenKind::False => ParseRule::new(Some(Compiler::literal), None, Precedence::None),
 
-        TokenKind::Nil => {
-            ParseRule::new(Some(Compiler::literal), None, Precedence::None)
-        }
+        TokenKind::Nil => ParseRule::new(Some(Compiler::literal), None, Precedence::None),
 
-        TokenKind::Bang => {
-            ParseRule::new(Some(Compiler::unary), None, Precedence::None)
-        }
+        TokenKind::Bang => ParseRule::new(Some(Compiler::unary), None, Precedence::None),
 
-        TokenKind::BangEqual => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Equality)
-        }
+        TokenKind::BangEqual => ParseRule::new(None, Some(Compiler::binary), Precedence::Equality),
 
-        TokenKind::EqualEqual => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Equality)
-        }
+        TokenKind::EqualEqual => ParseRule::new(None, Some(Compiler::binary), Precedence::Equality),
 
-        TokenKind::Greater => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
-        }
+        TokenKind::Greater => ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison),
 
         TokenKind::GreaterEqual => {
             ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
         }
 
-        TokenKind::Less => {
-            ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
-        }
+        TokenKind::Less => ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison),
 
         TokenKind::LessEqual => {
             ParseRule::new(None, Some(Compiler::binary), Precedence::Comparison)
@@ -184,9 +156,7 @@ fn rule_for<T: Iterator<Item = Token>>(token: &TokenKind) -> ParseRule<T> {
             ParseRule::new(Some(Compiler::variable), None, Precedence::None)
         }
 
-        TokenKind::String(_) => {
-            ParseRule::new(Some(Compiler::string), None, Precedence::None)
-        }
+        TokenKind::String(_) => ParseRule::new(Some(Compiler::string), None, Precedence::None),
 
         _ => ParseRule::new(None, None, Precedence::None),
     }
@@ -236,9 +206,7 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
 
     fn define_variable(&mut self, var: Option<ConstantIdx>) -> LoxResult<()> {
         if self.locals.scope_depth == 0 {
-            self.emit_op(OpCode::OpDefineGlobal(
-                var.expect("should be global"),
-            ));
+            self.emit_op(OpCode::OpDefineGlobal(var.expect("should be global")));
         } else {
             self.locals
                 .locals
@@ -305,9 +273,7 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
     }
 
     fn block(&mut self) -> LoxResult<()> {
-        while !self.check(&TokenKind::RightBrace)
-            && !self.check(&TokenKind::Eof)
-        {
+        while !self.check(&TokenKind::RightBrace) && !self.check(&TokenKind::Eof) {
             self.declaration()?;
         }
 
@@ -712,9 +678,8 @@ impl<T: Iterator<Item = Token>> Compiler<T> {
 
 pub fn compile(code: &str) -> Result<(Interner, Chunk), Vec<Error>> {
     let chars = code.chars().collect::<Vec<char>>();
-    let tokens = scanner::scan(&chars).map_err(|errors| {
-        errors.into_iter().map(Into::into).collect::<Vec<Error>>()
-    })?;
+    let tokens = scanner::scan(&chars)
+        .map_err(|errors| errors.into_iter().map(Into::into).collect::<Vec<Error>>())?;
 
     let mut compiler = Compiler {
         tokens: tokens.into_iter().peekable(),
diff --git a/users/tazjin/rlox/src/bytecode/mod.rs b/users/tazjin/rlox/src/bytecode/mod.rs
index c6f3a737ae..117f17824a 100644
--- a/users/tazjin/rlox/src/bytecode/mod.rs
+++ b/users/tazjin/rlox/src/bytecode/mod.rs
@@ -23,10 +23,7 @@ impl crate::Lox for Interpreter {
         Interpreter {}
     }
 
-    fn interpret(
-        &mut self,
-        code: String,
-    ) -> Result<Self::Value, Vec<Self::Error>> {
+    fn interpret(&mut self, code: String) -> Result<Self::Value, Vec<Self::Error>> {
         let (strings, chunk) = compiler::compile(&code)?;
         vm::interpret(strings, chunk).map_err(|e| vec![e])
     }
diff --git a/users/tazjin/rlox/src/bytecode/vm.rs b/users/tazjin/rlox/src/bytecode/vm.rs
index d287ec7cb8..30ffebc79c 100644
--- a/users/tazjin/rlox/src/bytecode/vm.rs
+++ b/users/tazjin/rlox/src/bytecode/vm.rs
@@ -118,12 +118,7 @@ impl VM {
 
                 OpCode::OpNegate => {
                     let v = self.pop();
-                    with_type!(
-                        self,
-                        v,
-                        Value::Number(num),
-                        self.push(Value::Number(-num))
-                    );
+                    with_type!(self, v, Value::Number(num), self.push(Value::Number(-num)));
                 }
 
                 OpCode::OpSubtract => binary_op!(self, Number, -),
@@ -141,15 +136,18 @@ impl VM {
                             self.push(Value::String(new_s.into()));
                         }
 
-                        (Value::Number(n_a), Value::Number(n_b)) =>
-                            self.push(Value::Number(n_a + n_b)),
+                        (Value::Number(n_a), Value::Number(n_b)) => {
+                            self.push(Value::Number(n_a + n_b))
+                        }
 
-                        _ => return Err(Error {
-                            line: self.chunk.get_line(self.ip - 1),
-                            kind: ErrorKind::TypeError(
-                                "'+' operator only works on strings and numbers".into()
-                            ),
-                        })
+                        _ => {
+                            return Err(Error {
+                                line: self.chunk.get_line(self.ip - 1),
+                                kind: ErrorKind::TypeError(
+                                    "'+' operator only works on strings and numbers".into(),
+                                ),
+                            })
+                        }
                     }
                 }
 
@@ -205,8 +203,7 @@ impl VM {
                         self.stack.len() > local_idx.0,
                         "stack is not currently large enough for local"
                     );
-                    self.stack[local_idx.0] =
-                        self.stack.last().unwrap().clone();
+                    self.stack[local_idx.0] = self.stack.last().unwrap().clone();
                 }
 
                 OpCode::OpJumpPlaceholder(_) => {
@@ -255,9 +252,7 @@ impl VM {
     fn print_value(&self, val: Value) -> String {
         match val {
             Value::String(LoxString::Heap(s)) => s,
-            Value::String(LoxString::Interned(id)) => {
-                self.strings.lookup(id).into()
-            }
+            Value::String(LoxString::Interned(id)) => self.strings.lookup(id).into(),
             _ => format!("{:?}", val),
         }
     }
diff --git a/users/tazjin/rlox/src/main.rs b/users/tazjin/rlox/src/main.rs
index 2d8cf4f354..ee61ae01a1 100644
--- a/users/tazjin/rlox/src/main.rs
+++ b/users/tazjin/rlox/src/main.rs
@@ -1,8 +1,5 @@
-use std::env;
-use std::fs;
-use std::io;
 use std::io::Write;
-use std::process;
+use std::{env, fs, io, process};
 
 mod bytecode;
 mod scanner;
@@ -15,10 +12,7 @@ pub trait Lox {
     type Error: std::fmt::Display;
 
     fn create() -> Self;
-    fn interpret(
-        &mut self,
-        source: String,
-    ) -> Result<Self::Value, Vec<Self::Error>>;
+    fn interpret(&mut self, source: String) -> Result<Self::Value, Vec<Self::Error>>;
 }
 
 fn main() {
@@ -29,9 +23,7 @@ fn main() {
     }
 
     match env::var("LOX_INTERPRETER").as_ref().map(String::as_str) {
-        Ok("treewalk") => {
-            pick::<treewalk::interpreter::Interpreter>(args.nth(1))
-        }
+        Ok("treewalk") => pick::<treewalk::interpreter::Interpreter>(args.nth(1)),
         _ => pick::<bytecode::Interpreter>(args.nth(1)),
     }
 }
@@ -46,8 +38,7 @@ fn pick<I: Lox>(file_arg: Option<String>) {
 
 // Run Lox code from a file and print results to stdout
 fn run_file<I: Lox>(file: &str) {
-    let contents =
-        fs::read_to_string(file).expect("failed to read the input file");
+    let contents = fs::read_to_string(file).expect("failed to read the input file");
     let mut lox = I::create();
     run(&mut lox, contents);
 }
diff --git a/users/tazjin/rlox/src/scanner.rs b/users/tazjin/rlox/src/scanner.rs
index 4e8f07b61f..314b56d6d3 100644
--- a/users/tazjin/rlox/src/scanner.rs
+++ b/users/tazjin/rlox/src/scanner.rs
@@ -106,15 +106,9 @@ impl<'a> Scanner<'a> {
 
             // possible multi-character tokens
             '!' => self.add_if_next('=', TokenKind::BangEqual, TokenKind::Bang),
-            '=' => {
-                self.add_if_next('=', TokenKind::EqualEqual, TokenKind::Equal)
-            }
+            '=' => self.add_if_next('=', TokenKind::EqualEqual, TokenKind::Equal),
             '<' => self.add_if_next('=', TokenKind::LessEqual, TokenKind::Less),
-            '>' => self.add_if_next(
-                '=',
-                TokenKind::GreaterEqual,
-                TokenKind::Greater,
-            ),
+            '>' => self.add_if_next('=', TokenKind::GreaterEqual, TokenKind::Greater),
 
             '/' => {
                 // support comments until EOL by discarding characters
@@ -234,8 +228,7 @@ impl<'a> Scanner<'a> {
             self.advance();
         }
 
-        let ident: String =
-            self.source[self.start..self.current].iter().collect();
+        let ident: String = self.source[self.start..self.current].iter().collect();
 
         // Determine whether this is an identifier, or a keyword:
         let token_kind = match ident.as_str() {
diff --git a/users/tazjin/rlox/src/treewalk/interpreter.rs b/users/tazjin/rlox/src/treewalk/interpreter.rs
index d9fe336616..3285775bbe 100644
--- a/users/tazjin/rlox/src/treewalk/interpreter.rs
+++ b/users/tazjin/rlox/src/treewalk/interpreter.rs
@@ -34,11 +34,7 @@ impl Callable {
         }
     }
 
-    fn call(
-        &self,
-        lox: &mut Interpreter,
-        args: Vec<Value>,
-    ) -> Result<Value, Error> {
+    fn call(&self, lox: &mut Interpreter, args: Vec<Value>) -> Result<Value, Error> {
         match self {
             Callable::Builtin(builtin) => builtin.call(args),
 
@@ -50,10 +46,8 @@ impl Callable {
                     fn_env.define(param, value)?;
                 }
 
-                let result = lox.interpret_block_with_env(
-                    Some(Rc::new(RwLock::new(fn_env))),
-                    &func.body,
-                );
+                let result =
+                    lox.interpret_block_with_env(Some(Rc::new(RwLock::new(fn_env))), &func.body);
 
                 match result {
                     // extract returned values if applicable
@@ -109,22 +103,13 @@ pub struct Environment {
 }
 
 impl Environment {
-    fn define(
-        &mut self,
-        name: &scanner::Token,
-        value: Value,
-    ) -> Result<(), Error> {
+    fn define(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
         let ident = identifier_str(name)?;
         self.values.insert(ident.into(), value);
         Ok(())
     }
 
-    fn get(
-        &self,
-        ident: &str,
-        line: usize,
-        depth: usize,
-    ) -> Result<Value, Error> {
+    fn get(&self, ident: &str, line: usize, depth: usize) -> Result<Value, Error> {
         if depth > 0 {
             match &self.enclosing {
                 None => {
@@ -137,9 +122,7 @@ impl Environment {
                     })
                 }
                 Some(parent) => {
-                    let env = parent
-                        .read()
-                        .expect("fatal: environment lock poisoned");
+                    let env = parent.read().expect("fatal: environment lock poisoned");
                     return env.get(ident, line, depth - 1);
                 }
             }
@@ -154,11 +137,7 @@ impl Environment {
             })
     }
 
-    fn assign(
-        &mut self,
-        name: &scanner::Token,
-        value: Value,
-    ) -> Result<(), Error> {
+    fn assign(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
         let ident = identifier_str(name)?;
 
         match self.values.get_mut(ident) {
@@ -242,22 +221,14 @@ impl Lox for Interpreter {
 
 impl Interpreter {
     // Environment modification helpers
-    fn define_var(
-        &mut self,
-        name: &scanner::Token,
-        value: Value,
-    ) -> Result<(), Error> {
+    fn define_var(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
         self.env
             .write()
             .expect("environment lock is poisoned")
             .define(name, value)
     }
 
-    fn assign_var(
-        &mut self,
-        name: &scanner::Token,
-        value: Value,
-    ) -> Result<(), Error> {
+    fn assign_var(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
         self.env
             .write()
             .expect("environment lock is poisoned")
@@ -271,11 +242,10 @@ impl Interpreter {
             kind: ErrorKind::UndefinedVariable(ident.into()),
         })?;
 
-        self.env.read().expect("environment lock is poisoned").get(
-            ident,
-            var.name.line,
-            depth,
-        )
+        self.env
+            .read()
+            .expect("environment lock is poisoned")
+            .get(ident, var.name.line, depth)
     }
 
     /// Interpret the block in the supplied environment. If no
@@ -324,16 +294,10 @@ impl Interpreter {
                 Value::Literal(Literal::String(output))
             }
             Statement::Var(var) => return self.interpret_var(var),
-            Statement::Block(block) => {
-                return self.interpret_block_with_env(None, block)
-            }
+            Statement::Block(block) => return self.interpret_block_with_env(None, block),
             Statement::If(if_stmt) => return self.interpret_if(if_stmt),
-            Statement::While(while_stmt) => {
-                return self.interpret_while(while_stmt)
-            }
-            Statement::Function(func) => {
-                return self.interpret_function(func.clone())
-            }
+            Statement::While(while_stmt) => return self.interpret_while(while_stmt),
+            Statement::Function(func) => return self.interpret_function(func.clone()),
             Statement::Return(ret) => {
                 return Err(Error {
                     line: 0,
@@ -348,9 +312,7 @@ impl Interpreter {
     fn interpret_var(&mut self, var: &parser::Var) -> Result<Value, Error> {
         let init = var.initialiser.as_ref().ok_or_else(|| Error {
             line: var.name.line,
-            kind: ErrorKind::InternalError(
-                "missing variable initialiser".into(),
-            ),
+            kind: ErrorKind::InternalError("missing variable initialiser".into()),
         })?;
         let value = self.eval(init)?;
         self.define_var(&var.name, value.clone())?;
@@ -369,10 +331,7 @@ impl Interpreter {
         }
     }
 
-    fn interpret_while(
-        &mut self,
-        stmt: &parser::While,
-    ) -> Result<Value, Error> {
+    fn interpret_while(&mut self, stmt: &parser::While) -> Result<Value, Error> {
         let mut value = Value::Literal(Literal::Nil);
         while eval_truthy(&self.eval(&stmt.condition)?) {
             value = self.interpret_stmt(&stmt.body)?;
@@ -381,10 +340,7 @@ impl Interpreter {
         Ok(value)
     }
 
-    fn interpret_function(
-        &mut self,
-        func: Rc<parser::Function>,
-    ) -> Result<Value, Error> {
+    fn interpret_function(&mut self, func: Rc<parser::Function>) -> Result<Value, Error> {
         let name = func.name.clone();
         let value = Value::Callable(Callable::Function {
             func,
@@ -414,9 +370,7 @@ impl Interpreter {
             (TokenKind::Minus, Value::Literal(Literal::Number(num))) => {
                 Ok(Literal::Number(-num).into())
             }
-            (TokenKind::Bang, right) => {
-                Ok(Literal::Boolean(!eval_truthy(&right)).into())
-            }
+            (TokenKind::Bang, right) => Ok(Literal::Boolean(!eval_truthy(&right)).into()),
 
             (op, right) => Err(Error {
                 line: expr.operator.line,
@@ -478,10 +432,7 @@ impl Interpreter {
         Ok(value)
     }
 
-    fn eval_logical(
-        &mut self,
-        logical: &parser::Logical,
-    ) -> Result<Value, Error> {
+    fn eval_logical(&mut self, logical: &parser::Logical) -> Result<Value, Error> {
         let left = eval_truthy(&self.eval(&logical.left)?);
         let right = eval_truthy(&self.eval(&logical.right)?);
 
@@ -490,10 +441,7 @@ impl Interpreter {
             TokenKind::Or => Ok(Literal::Boolean(left || right).into()),
             kind => Err(Error {
                 line: logical.operator.line,
-                kind: ErrorKind::InternalError(format!(
-                    "Invalid logical operator: {:?}",
-                    kind
-                )),
+                kind: ErrorKind::InternalError(format!("Invalid logical operator: {:?}", kind)),
             }),
         }
     }
@@ -504,10 +452,7 @@ impl Interpreter {
             Value::Literal(v) => {
                 return Err(Error {
                     line: call.paren.line,
-                    kind: ErrorKind::RuntimeError(format!(
-                        "not callable: {:?}",
-                        v
-                    )),
+                    kind: ErrorKind::RuntimeError(format!("not callable: {:?}", v)),
                 })
             }
         };
@@ -546,10 +491,7 @@ fn eval_truthy(lit: &Value) -> bool {
     }
 }
 
-fn set_enclosing_env(
-    this: &RwLock<Environment>,
-    parent: Rc<RwLock<Environment>>,
-) {
+fn set_enclosing_env(this: &RwLock<Environment>, parent: Rc<RwLock<Environment>>) {
     this.write()
         .expect("environment lock is poisoned")
         .enclosing = Some(parent);
diff --git a/users/tazjin/rlox/src/treewalk/parser.rs b/users/tazjin/rlox/src/treewalk/parser.rs
index 003cc34b46..5794b42d15 100644
--- a/users/tazjin/rlox/src/treewalk/parser.rs
+++ b/users/tazjin/rlox/src/treewalk/parser.rs
@@ -124,56 +124,54 @@ pub enum Statement {
 
 // Parser
 
-/*
-program        โ†’ declaration* EOF ;
-
-declaration    โ†’ funDecl
-               | varDecl
-               | statement ;
-
-funDecl        โ†’ "fun" function ;
-function       โ†’ IDENTIFIER "(" parameters? ")" block ;
-parameters     โ†’ IDENTIFIER ( "," IDENTIFIER )* ;
-
-
-statement      โ†’ exprStmt
-               | forStmt
-               | ifStmt
-               | printStmt
-               | returnStmt
-               | whileStmt
-               | block ;
-
-forStmt        โ†’ "for" "(" ( varDecl | exprStmt | ";" )
-                 expression? ";"
-                 expression? ")" statement ;
-
-returnStmt     โ†’ "return" expression? ";" ;
-
-whileStmt      โ†’ "while" "(" expression ")" statement ;
-
-exprStmt       โ†’ expression ";" ;
-
-ifStmt         โ†’ "if" "(" expression ")" statement
-               ( "else" statement )? ;
-
-printStmt      โ†’ "print" expression ";" ;
-
-expression     โ†’ assignment ;
-assignment     โ†’ IDENTIFIER "=" assignment
-               | logic_or ;
-logic_or       โ†’ logic_and ( "or" logic_and )* ;
-logic_and      โ†’ equality ( "and" equality )* ;
-equality       โ†’ comparison ( ( "!=" | "==" ) comparison )* ;
-comparison     โ†’ term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
-term           โ†’ factor ( ( "-" | "+" ) factor )* ;
-factor         โ†’ unary ( ( "/" | "*" ) unary )* ;
-unary          โ†’ ( "!" | "-" ) unary | call ;
-call           โ†’ primary ( "(" arguments? ")" )* ;
-arguments      โ†’ expression ( "," expression )* ;
-primary        โ†’ NUMBER | STRING | "true" | "false" | "nil"
-               | "(" expression ")" ;
-*/
+// program        โ†’ declaration* EOF ;
+//
+// declaration    โ†’ funDecl
+// | varDecl
+// | statement ;
+//
+// funDecl        โ†’ "fun" function ;
+// function       โ†’ IDENTIFIER "(" parameters? ")" block ;
+// parameters     โ†’ IDENTIFIER ( "," IDENTIFIER )* ;
+//
+//
+// statement      โ†’ exprStmt
+// | forStmt
+// | ifStmt
+// | printStmt
+// | returnStmt
+// | whileStmt
+// | block ;
+//
+// forStmt        โ†’ "for" "(" ( varDecl | exprStmt | ";" )
+// expression? ";"
+// expression? ")" statement ;
+//
+// returnStmt     โ†’ "return" expression? ";" ;
+//
+// whileStmt      โ†’ "while" "(" expression ")" statement ;
+//
+// exprStmt       โ†’ expression ";" ;
+//
+// ifStmt         โ†’ "if" "(" expression ")" statement
+// ( "else" statement )? ;
+//
+// printStmt      โ†’ "print" expression ";" ;
+//
+// expression     โ†’ assignment ;
+// assignment     โ†’ IDENTIFIER "=" assignment
+// | logic_or ;
+// logic_or       โ†’ logic_and ( "or" logic_and )* ;
+// logic_and      โ†’ equality ( "and" equality )* ;
+// equality       โ†’ comparison ( ( "!=" | "==" ) comparison )* ;
+// comparison     โ†’ term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
+// term           โ†’ factor ( ( "-" | "+" ) factor )* ;
+// factor         โ†’ unary ( ( "/" | "*" ) unary )* ;
+// unary          โ†’ ( "!" | "-" ) unary | call ;
+// call           โ†’ primary ( "(" arguments? ")" )* ;
+// arguments      โ†’ expression ( "," expression )* ;
+// primary        โ†’ NUMBER | STRING | "true" | "false" | "nil"
+// | "(" expression ")" ;
 
 struct Parser {
     tokens: Vec<Token>,
@@ -213,9 +211,7 @@ impl Parser {
                 if params.len() >= 255 {
                     return Err(Error {
                         line: self.peek().line,
-                        kind: ErrorKind::InternalError(
-                            "255 parameter limit exceeded.".into(),
-                        ),
+                        kind: ErrorKind::InternalError("255 parameter limit exceeded.".into()),
                     });
                 }
 
@@ -429,10 +425,7 @@ impl Parser {
 
             return Err(Error {
                 line: equals.line,
-                kind: ErrorKind::InvalidAssignmentTarget(format!(
-                    "{:?}",
-                    equals
-                )),
+                kind: ErrorKind::InvalidAssignmentTarget(format!("{:?}", equals)),
             });
         }
 
@@ -495,9 +488,7 @@ impl Parser {
     }
 
     fn unary(&mut self) -> ExprResult {
-        if self.match_token(&TokenKind::Bang)
-            || self.match_token(&TokenKind::Minus)
-        {
+        if self.match_token(&TokenKind::Bang) || self.match_token(&TokenKind::Minus) {
             return Ok(Expr::Unary(Unary {
                 operator: self.previous().clone(),
                 right: Box::new(self.unary()?),
@@ -557,10 +548,7 @@ impl Parser {
 
             TokenKind::LeftParen => {
                 let expr = self.expression()?;
-                self.consume(
-                    &TokenKind::RightParen,
-                    ErrorKind::UnmatchedParens,
-                )?;
+                self.consume(&TokenKind::RightParen, ErrorKind::UnmatchedParens)?;
                 return Ok(Expr::Grouping(Grouping(Box::new(expr))));
             }
 
@@ -632,11 +620,7 @@ impl Parser {
         &self.tokens[self.current - 1]
     }
 
-    fn consume(
-        &mut self,
-        kind: &TokenKind,
-        err: ErrorKind,
-    ) -> Result<Token, Error> {
+    fn consume(&mut self, kind: &TokenKind, err: ErrorKind) -> Result<Token, Error> {
         if self.check_token(kind) {
             return Ok(self.advance());
         }
diff --git a/users/tazjin/rlox/src/treewalk/resolver.rs b/users/tazjin/rlox/src/treewalk/resolver.rs
index 8231ce5a9e..3d12973aa0 100644
--- a/users/tazjin/rlox/src/treewalk/resolver.rs
+++ b/users/tazjin/rlox/src/treewalk/resolver.rs
@@ -56,13 +56,14 @@ impl<'a> Resolver<'a> {
                 // The resolver does not clone references, so unless
                 // the interpreter is called before the resolver this
                 // case should never happen.
-                None => return Err(Error {
-                    line: 0,
-                    kind: ErrorKind::InternalError(
-                        "multiple function references before interpretation"
-                            .into(),
-                    ),
-                }),
+                None => {
+                    return Err(Error {
+                        line: 0,
+                        kind: ErrorKind::InternalError(
+                            "multiple function references before interpretation".into(),
+                        ),
+                    })
+                }
             },
         }
     }
@@ -79,10 +80,7 @@ impl<'a> Resolver<'a> {
         Ok(())
     }
 
-    fn resolve_function(
-        &mut self,
-        func: &'a mut parser::Function,
-    ) -> Result<(), Error> {
+    fn resolve_function(&mut self, func: &'a mut parser::Function) -> Result<(), Error> {
         self.declare(&func.name.lexeme);
         self.define(&func.name.lexeme);
 
@@ -123,17 +121,13 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    fn resolve_variable(
-        &mut self,
-        var: &'a mut parser::Variable,
-    ) -> Result<(), Error> {
+    fn resolve_variable(&mut self, var: &'a mut parser::Variable) -> Result<(), Error> {
         if let Some(scope) = self.scopes.last_mut() {
             if let Some(false) = scope.get(var.name.lexeme.as_str()) {
                 return Err(Error {
                     line: var.name.line,
                     kind: ErrorKind::StaticError(
-                        "can't read local variable in its own initialiser"
-                            .into(),
+                        "can't read local variable in its own initialiser".into(),
                     ),
                 });
             }
@@ -143,10 +137,7 @@ impl<'a> Resolver<'a> {
         Ok(())
     }
 
-    fn resolve_assign(
-        &mut self,
-        assign: &'a mut parser::Assign,
-    ) -> Result<(), Error> {
+    fn resolve_assign(&mut self, assign: &'a mut parser::Assign) -> Result<(), Error> {
         self.resolve_expr(&mut assign.value)?;
         assign.depth = self.resolve_local(&assign.name);
         Ok(())
@@ -162,10 +153,7 @@ impl<'a> Resolver<'a> {
         None
     }
 
-    fn resolve_call(
-        &mut self,
-        call: &'a mut parser::Call,
-    ) -> Result<(), Error> {
+    fn resolve_call(&mut self, call: &'a mut parser::Call) -> Result<(), Error> {
         self.resolve_expr(&mut call.callee)?;
 
         for arg in call.args.iter_mut() {
@@ -198,10 +186,7 @@ impl<'a> Resolver<'a> {
     }
 }
 
-pub fn resolve(
-    globals: &[String],
-    block: &mut parser::Block,
-) -> Result<(), Error> {
+pub fn resolve(globals: &[String], block: &mut parser::Block) -> Result<(), Error> {
     let mut resolver: Resolver = Default::default();
 
     // Scope for static globals only starts, never ends.
diff --git a/users/tazjin/russian/russian.el b/users/tazjin/russian/russian.el
new file mode 100644
index 0000000000..28f1addeaa
--- /dev/null
+++ b/users/tazjin/russian/russian.el
@@ -0,0 +1,97 @@
+(require 'cl-macs)
+(require 'ht)
+(require 'seq)
+(require 's)
+
+;; Type definitions for Russian structures
+
+(cl-defstruct russian-word
+  "Definition and metadata of a single Russian word."
+  (word nil :type string)
+  (translations :type list
+                :documentation "List of lists of strings, each a set of translations.")
+
+  (notes nil :type list ;; of string
+         :documentation "free-form notes about this word")
+
+  (roots nil :type list ;; of string
+         :documentation "list of strings that correspond with roots (exact string match)"))
+
+(defun russian--merge-words (previous new)
+  "Merge two Russian word definitions together. If no previous
+  definition exists, only the new one will be returned."
+  (if (not previous) new
+    (cl-assert (equal (russian-word-word previous)
+                      (russian-word-word new))
+               "different words passed into merge function")
+    (make-russian-word :word (russian-word-word previous)
+                       :translations (-concat (russian-word-translations previous)
+                                              (russian-word-translations new))
+                       :notes (-concat (russian-word-notes previous)
+                                       (russian-word-notes new))
+                       :roots (-concat (russian-word-roots previous)
+                                       (russian-word-roots new)))))
+
+;; Definitions for creating a data structure of all Russian words.
+
+(defvar russian-words (make-hash-table)
+  "Table of all Russian words in the corpus.")
+
+(defun russian--define-word (word)
+  "Define a single word in the corpus, optionally merging it with
+  another entry."
+  (let ((key (russian-word-word word)))
+    (ht-set russian-words key (russian--merge-words
+                               (ht-get russian-words key)
+                               word))))
+
+(defmacro define-russian-words (&rest words)
+  "Define the list of all available words. There may be more than
+  one entry for a word in some cases."
+  (declare (indent defun))
+
+  ;; Clear the table before proceeding with insertion
+  (setq russian-words (make-hash-table))
+
+  (seq-map
+   (lambda (word)
+     (russian--define-word (make-russian-word :word (car word)
+                                              :translations (cadr word)
+                                              :notes (caddr word)
+                                              :roots (cadddr word))))
+   words)
+
+  '(message "Defined %s unique words." (ht-size russian-words)))
+
+;; Helpers to train Russian words through passively.
+
+(defun russian--format-word (word)
+  "Format a Russian word suitable for echo display."
+  (apply #'s-concat
+         (-flatten
+          (list (russian-word-word word)
+                " - "
+                (s-join ", " (russian-word-translations word))
+                (when-let ((roots (russian-word-roots word)))
+                  (list " [" (s-join ", " roots) "]"))
+                (when-let ((notes (russian-word-notes word)))
+                  (list " (" (s-join "; " notes) ")"))))))
+
+(defun display-russian-words ()
+  "Convert Russian words to passively terms and start passively."
+  (interactive)
+  (setq passively-learn-terms (make-hash-table))
+  (ht-map
+   (lambda (k v)
+     (ht-set passively-learn-terms k (russian--format-word v)))
+   russian-words)
+  (passively-enable))
+
+(defun lookup-last-russian-word (in-eww)
+  "Look up the last Russian word in Wiktionary"
+  (interactive "P")
+  (let ((url (concat "https://ru.wiktionary.org/wiki/" passively-last-displayed)))
+    (if in-eww (eww url)
+      (browse-url url))))
+
+(provide 'russian)
diff --git a/users/tazjin/russian/words.el b/users/tazjin/russian/words.el
index 46276cdd0a..784e5bddde 100644
--- a/users/tazjin/russian/words.el
+++ b/users/tazjin/russian/words.el
@@ -4,7 +4,10 @@
 ;; translations :: '(translation ...)
 ;; roots :: '(root ...)
 
-'( ;; 1-50
+(require 'russian)
+
+(define-russian-words
+  ;; 1-50
   ("ะธ" ("and" "though"))
   ("ะฒ" ("in" "at"))
   ("ะฝะต" ("not"))
@@ -51,9 +54,9 @@
   ("ัƒะถะต" ("already" "by now"))
   ("ะดะปั" ("for" "to"))
   ("ะฒะพั‚" ("here" "there" "this is" "that's")
-   "calling attention to something")
+   ("calling attention to something"))
   ("ะบั‚ะพ" ("who" "that" "some"))
-  ("ะดะฐ" ("yes" "but") "affirmation (..., right?)")
+  ("ะดะฐ" ("yes" "but") ("affirmation (..., right?)"))
   ("ะณะพะฒะพั€ะธั‚ัŒ" ("to say" "to tell" "to speak") nil ("-ะณะพะฒะพั€-"))
   ("ะณะพะด" ("year"))
 
@@ -85,14 +88,14 @@
   ("ะดะฒะฐ" ("two"))
   ("ั‚ะฐะผ" ("there" "then"))
   ("ั‡ะตะผ" ("than" "instead of")
-   "ั‡ะตะผ ..., ั‚ะตะผ ...")
+   ("ั‡ะตะผ ..., ั‚ะตะผ ..."))
   ("ะณะปะฐะท" ("eye" "sight"))
   ("ะถะธะทะฝัŒ" ("life") nil ("-ะถะธ-"))
   ("ะฟะตั€ะฒั‹ะน" ("first" "front" "former"))
   ("ะดะตะฝัŒ" ("day"))
   ("ั‚ัƒั‚" ("here" "now" "then"))
   ("ะฒะพ" ("in" "at")
-   "as particle also: wow, exactly, ...")
+   ("as particle also: wow, exactly, ..."))
   ("ะฝะธั‡ั‚ะพ" ("nothing"))
   ("ะฟะพั‚ะพะผ" ("afterwards" "then"))
   ("ะพั‡ะตะฝัŒ" ("very"))
@@ -350,7 +353,7 @@
   ("ะฟะพัะฒะธั‚ัŒัั" ("to appear" "to show up"))
   ("ะฒะพะทะดัƒั…" ("air"))
   ("ั€ะฐะทะฝั‹ะน" ("different"))
-  ("ะฒั‹ั…ะพะดะธั‚ัŒ" ("to go out" "to exit") "MR says 'to nurse'??" ("-ั…ะพะด-"))
+  ("ะฒั‹ั…ะพะดะธั‚ัŒ" ("to go out" "to exit") ("MR says 'to nurse'??") ("-ั…ะพะด-"))
   ("ะฟั€ะพัะธั‚ัŒ" ("to ask"))
   ("ะฑั€ะฐั‚" ("brat"))
   ("ัะพะฑัั‚ะฒะตะฝะฝั‹ะน" ("one's own"))
@@ -411,4 +414,310 @@
   ("ัะฟะฐั‚ัŒ" ("to sleep"))
   ("ะพัั‚ะฐะฒะธั‚ัŒ" ("to leave" "to abandon"))
   ("ะฒะพะนั‚ะธ" ("to enter" "to come in") nil ("-ะน-"))
-  ("ะดะปะธะฝะฝั‹ะน" ("long")))
+  ("ะดะปะธะฝะฝั‹ะน" ("long"))
+
+  ;; 401 - 500
+  ("ั‡ัƒะฒัั‚ะฒะพ" ("feeling"))
+  ("ะธะพะปั‡ะฐั‚ัŒ" ("to keep silence" "make no complaint" "say nothing"))
+  ("ั€ะฐััะบะฐะทั‹ะฒะฐั‚ัŒ" ("to tell" "narrate"))
+  ("ะพั‚ะฒะตั‡ะฐั‚ัŒ" ("to answer" "to reply"))
+  ("ัั‚ะฐะฝะพะฒะธั‚ัŒัั" ("to stand" "to become"))
+  ("ะพัั‚ะฐะฝะพะฒะธั‚ัŒัั" ("to stop"))
+  ("ะฑะตั€ะตะณ" ("bank" "shore" "coast"))
+  ("ัะตะผัŒั" ("family"))
+  ("ะธัะบะฐั‚ัŒ" ("to search"))
+  ("ะณะตะฝะตั€ะฐะป" ("general"))
+  ("ะผะพะผะตะฝั‚" ("moment" "instant"))
+  ("ะดะตััั‚ัŒ" ("ten"))
+  ("ะฝะฐั‡ะฐั‚ัŒ" ("to begin"))
+  ("ัะปะตะดัƒัŽัˆะธะน" ("next" "following"))
+  ("ะปะธั‡ะฝั‹ะน" ("personal"))
+  ("ั‚ั€ัƒะด" ("labour" "work"))
+  ("ะฒะตั€ะธั‚ัŒ" ("to believe"))
+  ("ะณั€ัƒะฟะฟะฐ" ("group"))
+  ("ะฝะตะผะฝะพะณะพ" ("a little"))
+  ("ะฒะฟั€ะพั‡ะตะผ" ("however" "though"))
+  ("ะฒะธะดะฝะพ" ("evidently" "obviously"))
+  ("ัะฒะปัั‚ัŒัั" ("to appear"))
+  ("ะผัƒะถ" ("husband"))
+  ("ั€ะฐะทะฒะต" ("really?" "perhaps") ("when pondering something"))
+  ("ะดะฒะธะถะตะฝะธะต" ("movement" "motion"))
+  ("ะฟะพั€ัะดะพะบ" ("order"))
+  ("ะพั‚ะฒะตั‚" ("answer" "reply"))
+  ("ั‚ะธั…ะพ" ("quietly" "silently") ("also as exclamation"))
+  ("ะทะฝะฐะบะพะผั‹ะน" ("familiar" "acquainted"))
+  ("ะณะฐะทะตั‚ะฐ" ("newspaper"))
+  ("ะฟะพะผะพั‰ัŒ" ("help"))
+  ("ัะธะปัŒะฝั‹ะน" ("strong" "powerful"))
+  ("ัะบะพั€ั‹ะน" ("quick" "fast"))
+  ("ัะพะฑะฐะบะฐ" ("dog"))
+  ("ะดะตั€ะตะฒะพ" ("tree"))
+  ("ัะฝะตะณ" ("snow"))
+  ("ัะพะฝ" ("dream"))
+  ("ัะผั‹ัะป" ("sense" "meaning" "purpose") ("making sense" "in the sense"))
+  ("ัะผะพั‡ัŒ" ("to be able") ("ัะฒ"))
+  ("ะฟั€ะพั‚ะธะฒ" ("against" "opposite" "contrary to"))
+  ("ะฑะตะถะฐั‚ัŒ" ("to run" "to hurry"))
+  ("ะดะฒะพั€" ("yard" "court"))
+  ("ั„ะพั€ะผะฐ" ("form" "shape" "uniform"))
+  ("ะฟั€ะพัั‚ะพะน" ("simple" "easy" "plain"))
+  ("ะฟั€ะธะตั…ะฐั‚ัŒ" ("to arrive" "to come"))
+  ("ะธะฝะพะน" ("different" "other"))
+  ("ะบั€ะธั‡ะฐั‚ัŒ" ("to cry" "to shout"))
+  ("ะฒะพะทะผะพะถะฝะพัั‚ัŒ" ("possibility" "opportunity" "chance"))
+  ("ะพะฑั‰ะตัั‚ะฒะพ" ("society"))
+  ("ะทะตะปั‘ะฝั‹ะน" ("green"))
+  ("ะณั€ัƒะดัŒ" ("breast" "chest"))
+  ("ัƒะณะพะป" ("corner" "angle"))
+  ("ะพั‚ะบั€ั‹ั‚ัŒ" ("to open"))
+  ("ะฟั€ะพะธัั…ะพะดะธั‚ัŒ" ("to happen" "to occur" "to take place"))
+  ("ะปะฐะดะฝะพ" ("well" "all right" "okay"))
+  ("ั‡ั‘ั€ะฝั‹ะน" ("black") ("noun (m.): as in 'she wears black'"))
+  ("ะฒะตะบ" ("century" "age"))
+  ("ะบะฐั€ะผะฐะฝ" ("pocket"))
+  ("ะตั…ะฐั‚ัŒ" ("to go" "ride" "drive" "travel"))
+  ("ะฝะตะผะตั†" ("German"))
+  ("ะฝะฐะฒะตั€ะฝะพะต" ("probably" "most likely"))
+  ("ะณัƒะฑะฐ" ("lip"))
+  ("ะดัะดั" ("uncle"))
+  ("ะฟั€ะธั…ะพะดะธั‚ัŒ" ("to come" "to arrive"))
+  ("ั‡ะฐัั‚ะพ" ("often"))
+  ("ะดะพะผะพะน" ("home") ("as in direction"))
+  ("ะพะณะพะฝัŒ" ("fire"))
+  ("ะฟะธัะฐั‚ะตะปัŒ" ("writer"))
+  ("ะฐั€ะผะธั" ("army"))
+  ("ัะพัั‚ะพัะฝะธะต" ("state" "condition" "fortune"))
+  ("ะทัƒะฑ" ("tooth"))
+  ("ะพั‡ะตั€ะตะดัŒ" ("queue" "line" "turn"))
+  ("ะบะพะน" ("which") ("old-fashioned, literary (in set expressions)"))
+  ("ะฟะพะดะฝัั‚ัŒัั" ("to rise" "to climb"))
+  ("ะบะฐะผะตะฝัŒ" ("stone"))
+  ("ะณะพัั‚ัŒ" ("guest"))
+  ("ะฟะพะบะฐะทะฐั‚ัŒัั" ("to appear" "to come in sight"))
+  ("ะฒะตั‚ะตั€" ("window"))
+  ("ัะพะฑะธั€ะฐั‚ัŒัั" ("to gather" "to assemble" "to intend") ("TODO: intend??"))
+  ("ะฟะพะฟะฐัั‚ัŒ" ("to hit" "to find oneself") ("to get (in phrases)"))
+  ("ะฟั€ะธะฝัั‚ัŒ" ("to take" "to admit" "to accept"))
+  ("ัะฝะฐั‡ะฐะปะฐ" ("at first" "from the beginning"))
+  ("ะปะธะฑะพ" ("or"))
+  ("ะฟะพะตั…ะฐั‚ัŒ" ("to depart" "to set off"))
+  ("ัƒัะปั‹ัˆะฐั‚ัŒ" ("to hear"))
+  ("ัƒะผะตั‚ัŒ" ("to be able" "know" "can"))
+  ("ัะปัƒั‡ะธั‚ัŒัั" ("to happen"))
+  ("ัั‚ั€ะฐะฝะฝั‹ะน" ("strange"))
+  ("ะตะดะธะฝัั‚ะฒะตะฝะฝั‹ะน" ("only" "sole"))
+  ("ั€ะพั‚ะฐ" ("company") ("(military)"))
+  ("ะทะฐะบะพะฝ" ("law" "act" "statute"))
+  ("ะบะพั€ะพั‚ะบะธะน" ("short"))
+  ("ะผะพั€ะต" ("sea"))
+  ("ะดะพะฑั€ั‹ะน" ("kind"))
+  ("ั‚ั‘ะผะฝั‹ะน" ("dark"))
+  ("ะณะพั€ะฐ" ("mountain" "hill"))
+  ("ะฒั€ะฐั‡" ("doctor"))
+  ("ะบั€ะฐะน" ("border, edge" "land, country"))
+  ("ัั‚ะฐั€ะฐั‚ัŒัั" ("to try" "to endeavour"))
+  ("ะปัƒั‡ัˆะธะน" ("better" "best"))
+
+  ;; 501 - 600
+  ("ั€ะตะบะฐ" ("river"))
+  ("ะฒะพะตะฝะฝั‹ะน" ("military"))
+  ("ะผะตั€ะฐ" ("measure" "step"))
+  ("ัั‚ั€ะฐัˆะฝั‹ะน" ("terrible" "frightful"))
+  ("ะฒะฟะพะปะฝะต" ("quite" "fully"))
+  ("ะทะฒะฐั‚ัŒ" ("to call"))
+  ("ะฟั€ะพะธะทะพะนั‚ะธ" ("to happen" "to occur" "take place"))
+  ("ะฒะฟะตั€ะตะด" ("forward"))
+  ("ะผะตะดะปะตะฝะฝะพ" ("slowly"))
+  ("ะฒะพะทะปะต" ("by" "near" "close by"))
+  ("ะฝะธะบะฐะบ" ("in no way" "by no means"))
+  ("ะทะฐะฝะธะผะฐั‚ัŒัั" ("to be occupied" "to engage"))
+  ("ะดะตะนัั‚ะฒะธะต" ("action" "effort"))
+  ("ะดะพะฒะพะปัŒะฝะพ" ("enough" "rather"))
+  ("ะฒะตั‰ัŒ" ("thing"))
+  ("ะฝะตะพะฑั…ะพะดะธะผั‹ะน" ("necessary") ("not possible to go around"))
+  ("ั…ะพะด" ("move"))
+  ("ะฑะพะปัŒ" ("pain"))
+  ("ััƒะดัŒะฑะฐ" ("fate" "fortune" "destiny"))
+  ("ะฟั€ะธั‡ะธะฝะฐ" ("cause" "reason" "motive"))
+  ("ะฟะพะปะพะถะธั‚ัŒ" ("to lay down" "put down" "place"))
+  ("ะตะดะฒะฐ" ("hardly" "just" "barely"))
+  ("ั‡ะตั€ั‚ะฐ" ("line" "boundary" "trait"))
+  ("ะดะตะฒะพั‡ะบะฐ" ("girl" "little girl"))
+  ("ะปั‘ะณะบะธะน" ("light" "easy"))
+  ("ะฒะพะปะพั" ("hair"))
+  ("ะบัƒะฟะธั‚ัŒ" ("to buy" "purchase"))
+  ("ะฝะพะผะตั€" ("number" "size" "room" "issue"))
+  ("ะพัะฝะพะฒะฝะพะน" ("main"))
+  ("ัˆะธั€ะพะบะธะน" ("wide"))
+  ("ัƒะผะตั€ะตั‚ัŒ" ("to die"))
+  ("ะดะฐะปะตะบะพ" ("far" "far off"))
+  ("ะฟะปะพั…ะพ" ("badly"))
+  ("ะณะปะฐะฒะฐ" ("head" "chief"))
+  ("ะบั€ะฐัะธะฒั‹ะน" ("beautiful"))
+  ("ัะตั€ั‹ะน" ("grey" "dull"))
+  ("ะฟะธั‚ัŒ" ("to drink"))
+  ("ะบะพะผะฐะฝะดะธั€" ("commander" "officer"))
+  ("ะพะฑั‹ั‡ะฝะพ" ("usually"))
+  ("ะฟะฐั€ั‚ะธั" ("party"))
+  ("ะฟั€ะพะฑะปะตะผะฐ" ("problem" "issue"))
+  ("ัั‚ั€ะฐั…" ("fear"))
+  ("ะฟั€ะพั…ะพะดะธั‚ัŒ" ("to pass" "go" "study"))
+  ("ััะฝะพ" ("clear" "clearly"))
+  ("ัะฝัั‚ัŒ" ("to take away" "take off"))
+  ("ะฑัƒะผะฐะณะฐ" ("paper"))
+  ("ะณะตั€ะพะน" ("hero"))
+  ("ะฟะฐั€ะฐ" ("pair" "couple"))
+  ("ะณะพััƒะดะฐั€ัั‚ะฒะพ" ("State"))
+  ("ะดะตั€ะตะฒะฝั" ("village"))
+  ("ั€ะตั‡ัŒ" ("speech"))
+  ("ะฝะฐั‡ะฐั‚ัŒัั" ("to begin"))
+  ("ัั€ะตะดัั‚ะฒะพ" ("means" "remedy"))
+  ("ะฟะพะปะพะถะตะฝะธะต" ("position" "posture" "condition" "state"))
+  ("ัะฒัะทัŒ" ("tie, bond" "connection, relation"))
+  ("ะฝะตะฑะพะปัŒัˆะพะน" ("small" "not great"))
+  ("ะฟั€ะตะดัั‚ะฐะฒะปัั‚ัŒ" ("to present" "introduce" "imagine"))
+  ("ะทะฐะฒั‚ั€ะฐ" ("tomorrow"))
+  ("ะพะฑัŠััะฝะธั‚ัŒ" ("to explain"))
+  ("ะฟัƒัั‚ะพะน" ("empty" "hollow" "idle"))
+  ("ะฟั€ะพะธะทะฝะตัั‚ะธ" ("to pronounce" "say" "utter"))
+  ("ั‡ะตะปะพะฒะตั‡ะตัะบะธะน" ("human"))
+  ("ะฝั€ะฐะฒะธั‚ัŒัั" ("to please" "be likeable to"))
+  ("ะพะดะฝะฐะถะดั‹" ("once" "one day"))
+  ("ะผะธะผะพ" ("past" "by"))
+  ("ะธะฝะฐั‡ะต" ("otherwise" "differently|"))
+  ("ััƒั‰ะตัั‚ะฒั€ะพะฒะฐั‚ัŒ" ("to exist" "to be"))
+  ("ะบะปะฐัั" ("class"))
+  ("ัƒะดะฐั‚ัŒัั" ("turn out well" "succeed" "manage"))
+  ("ั‚ะพะปัั‚ั‹ะน" ("thick" "heavy" "fat"))
+  ("ั†ะตะปัŒ" ("goal" "object" "target"))
+  ("ัะบะฒะพะทัŒ" ("through"))
+  ("ะฟั€ะธะนั‚ะธััŒ" ("to fit" "fall" "have to") ("ั‚ะตะฑะต ะฟั€ะธะดั‘ั‚ัั - you have to"))
+  ("ั‡ะธัั‚ั‹ะน" ("clean" "pure"))
+  ("ะทะฝะฐั‚ัŒ" ("to know"))
+  ("ะฟั€ะตะถะฝะธะน" ("former"))
+  ("ะฟั€ะพั„ะตััะพั€" ("professor"))
+  ("ะณะพัะฟะพะดะธะฝ" ("gentleman" "Mr."))
+  ("ัั‡ะฐัั‚ัŒะต" ("happiness" "luck"))
+  ("ั…ัƒะดะพะน" ("thin" "skinny"))
+  ("ะดัƒั…" ("spirit"))
+  ("ะฟะปะฐะฝ" ("plan"))
+  ("ั‡ัƒะถะพะน" ("somebody else's" "strange" "foreign"))
+  ("ะทะฐะป" ("hall"))
+  ("ะฟั€ะตะดัั‚ะฐะฒะธั‚ัŒ" ("to present" "produce" "introduce"))
+  ("ะพัะพะฑั‹ะน" ("special"))
+  ("ะดะธั€ะตะบั‚ะพั€" ("director" "manager"))
+  ("ะฑั‹ะฒัˆะธะน" ("former" "ex-"))
+  ("ะฟะฐะผัั‚ัŒ" ("memory"))
+  ("ะฑะปะธะทะบะธะน" ("near" "similar" "intimate"))
+  ("ัะตะน" ("this"))
+  ("ั€ะตะทัƒะปัŒั‚ะฐั‚" ("result" "outcome"))
+  ("ะฑะพะปัŒะฝะพะน" ("sick"))
+  ("ะดะฐะฝะฝั‹ะน" ("given" "present"))
+  ("ะบัั‚ะฐั‚ะธ" ("to the point" "at the same time"))
+  ("ะฝะฐะทะฒะฐั‚ัŒ" ("to call" "name"))
+  ("ัะปะตะด" ("track" "footprint"))
+  ("ัƒะปั‹ะฑะฐั‚ัŒัั" ("to smile") ("ะฝัะฒ"))
+  ("ะฑัƒั‚ั‹ะปะบะฐ" ("bottle"))
+
+  ;; 601 - 700
+  ("ั‚ั€ัƒะดะฝะพ" ("with difficulty"))
+  ("ัƒัะปะพะฒะธะต" ("condition" "term"))
+  ("ะฟั€ะตะถะดะต" ("before"))
+  ("ัƒะผ" ("mind" "brains" "intellect"))
+  ("ัƒะปั‹ะฑะฝัƒั‚ัŒัั" ("to smile"))
+  ("ะฟั€ะพั†ะตัั" ("process"))
+  ("ะบะฐั€ั‚ะธะฝะฐ" ("picture" "painting"))
+  ("ะฒะผะตัั‚ะพ" ("instead"))
+  ("ัั‚ะฐั€ัˆะธะน" ("elder" "senior"))
+  ("ะปะตะณะบะพ" ("easily" "lightly"))
+  ("ั†ะตะฝั‚ั€" ("center"))
+  ("ะฟะพะดะพะฑะฝั‹ะน" ("similar" "like"))
+  ("ะฒะพะทะผะพะถะฝะพ" ("possible") ("as ... as possible"))
+  ("ะพะบะพะปะพ" ("by" "near"))
+  ("ัะผะตัั‚ัŒัั" ("to laugh"))
+  ("ัั‚ะพ" ("hundred"))
+  ("ะฑัƒะดัƒั‰ะตะต" ("future"))
+  ("ั…ะฒะฐั‚ะฐั‚ัŒ" ("to snatch" "to seize" "to suffice") ("ะฝัะฒ"))
+  ("ั‡ะธัะปะพ" ("number"))
+  ("ะฒััะบะพะต" ("any" "every"))
+  ("ั€ัƒะฑะปัŒ" ("ruble"))
+  ("ะฟะพั‡ัƒะฒัั‚ะฒะพะฒะฐั‚ัŒ" ("to feel") ("ัะฒ"))
+  ("ะฟั€ะธะฝะตัั‚ะธ" ("to bring"))
+  ("ะฒะตั€ะฐ" ("faith" "belief"))
+  ("ะฒะพะฒัะต" ("quiet" "not ... at all"))
+  ("ัƒะดะฐั€" ("blow" "stroke"))
+  ("ั‚ะตะปะตั„ะพะฝ" ("telephone"))
+  ("ะบะพะปะตะฝะพ" ("knee"))
+  ("ัะพะณะปะฐัะธั‚ัŒัั" ("to agree" "to consent"))
+  ("ะผะฐะปะพ" ("little" "few" "not enough"))
+  ("ะบะพั€ะธะดะพั€" ("corridor" "passage"))
+  ("ะผัƒะถะธะบ" ("man"))
+  ("ะฟั€ะฐะฒั‹ะน" ("right"))
+  ("ะฐะฒั‚ะพั€" ("author"))
+  ("ั…ะพะปะพะดะฝั‹ะน" ("cold" "cool"))
+  ("ั…ะฒะฐั‚ะธั‚" ("to snatch" "to seize" "to suffice") ("ัะฒ"))
+  ("ะผะฝะพะณะธะต" ("many"))
+  ("ะฒัั‚ั€ะตั‡ะฐ" ("meeting" "reception"))
+  ("ะบะฐะฑะธะฝะตั‚" ("study" "room" "office suite"))
+  ("ะดะพะบัƒะผะตะฝั‚" ("document"))
+  ("ัะฐะผะพะปั‘ั‚" ("airplane"))
+  ("ะฒะฝะธะท" ("down" "downwards"))
+  ("ะฟั€ะธะฝะธะผะฐั‚ัŒ" ("to take" "to admit" "to accept"))
+  ("ะธะณั€ะฐ" ("game" "play"))
+  ("ั€ะฐััะบะฐะท" ("story"))
+  ("ั…ะปะตะฑ" ("bread"))
+  ("ั€ะฐะทะฒะธั‚ะธะต" ("development"))
+  ("ัƒะฑะธั‚ัŒ" ("to kill"))
+  ("ั€ะพะดะฝะพะน" ("own" "native" "dear"))
+  ("ะพั‚ะบั€ั‹ั‚ั‹ะน" ("open"))
+  ("ะผะตะฝะตะต" ("less"))
+  ("ะฟั€ะตะดะปะพะถะธั‚ัŒ" ("to offer" "to propose" "to suggest"))
+  ("ะถั‘ะปั‚ั‹ะน" ("yellow"))
+  ("ะฟั€ะธั…ะพะดะธั‚ัŒัั" ("to fit" "to fall" "to have to"))
+  ("ะฒั‹ะฟะธั‚ัŒ" ("to drink"))
+  ("ะบั€ะธะบะฝัƒั‚ัŒ" ("to cry" "to shout"))
+  ("ั‚ั€ัƒะฑะบะฐ" ("tube" "roll" "pipe"))
+  ("ะฒั€ะฐะณ" ("enemy"))
+  ("ะฟะพะบะฐะทั‹ะฒะฐั‚ัŒ" ("to show" "to display"))
+  ("ะดะฒะพะต" ("two") ("cardinal number"))
+  ("ะดะพะบั‚ะพั€" ("doctor"))
+  ("ะปะฐะดะพะฝัŒ" ("palm"))
+  ("ะฒั‹ะทะฒะฐั‚ัŒ" ("to call" "to send"))
+  ("ัะฟะพะบะพะนะฝะพ" ("quietly"))
+  ("ะฟะพะฟั€ะพัะธั‚ัŒ" ("to ask"))
+  ("ะฝะฐัƒะบะฐ" ("science"))
+  ("ะปะตะนั‚ะตะฝะฐะฝั‚" ("lieutenant"))
+  ("ัะปัƒะถะฑะฐ" ("service" "work"))
+  ("ะพะบะฐะทั‹ะฒะฐั‚ัŒัั" ("to turn out" "to find oneself"))
+  ("ะฟั€ะธะฒะตัั‚ะธ" ("to bring"))
+  ("ัะพั€ะพะบ" ("forty"))
+  ("ัั‡ั‘ั‚" ("bill" "account"))
+  ("ะฒะพะทะฒั€ะฐั‰ะฐั‚ัŒัั" ("to return"))
+  ("ะทะพะปะพั‚ะพะน" ("golden"))
+  ("ะผะตัั‚ะฝั‹ะน" ("local"))
+  ("ะบัƒั…ะฝั" ("kitchen"))
+  ("ะบั€ัƒะฟะฝั‹ะน" ("large" "big" "prominent"))
+  ("ั€ะตัˆะตะฝะธะต" ("decision" "conclusion"))
+  ("ะผะพะปะพะดะฐั" ("bride" "young"))
+  ("ั‚ั€ะธะดั†ะฐั‚ัŒ" ("thirty"))
+  ("ั€ะพะผะฐะฝ" ("novel" "romance"))
+  ("ะบะพะผะฟะฐะฝะธั" ("company"))
+  ("ั‡ะฐัั‚ั‹ะน" ("frequent"))
+  ("ั€ะพััะธะนัะบะธะน" ("Russian"))
+  ("ั€ะฐะฑะพั‡ะธะน" ("worky"))
+  ("ะฟะพั‚ะตั€ัั‚ัŒ" ("to lose"))
+  ("ั‚ะตั‡ะตะฝะธะต" ("current"))
+  ("ัะธะฝะธะน" ("dark blue"))
+  ("ัั‚ะพะปัŒะบะพ" ("so much" "so many"))
+  ("ั‚ั‘ะฟะปั‹ะน" ("warm"))
+  ("ะผะตั‚ั€" ("metre"))
+  ("ะดะพัั‚ะฐั‚ัŒ" ("to reach" "get" "obtain"))
+  ("ะถะตะปะตะทะฝั‹ะน" ("ferreous" "iron"))
+  ("ะธะฝัั‚ะธั‚ัƒั‚" ("institute"))
+  ("ัะพะพะฑั‰ะธั‚ัŒ" ("to report" "to let know"))
+  ("ะธะฝั‚ะตั€ะตั" ("interest"))
+  ("ะพะฑั‹ั‡ะฝั‹ะน" ("usual" "ordinary"))
+  ("ะฟะพัะฒะปัั‚ัŒัั" ("to appear" "to show up"))
+  ("ัƒะฟะฐัั‚ัŒ" ("to fall")))
+
+(provide 'russian-words)
diff --git a/users/tazjin/rustfmt.toml b/users/tazjin/rustfmt.toml
new file mode 100644
index 0000000000..0c719dcfec
--- /dev/null
+++ b/users/tazjin/rustfmt.toml
@@ -0,0 +1,22 @@
+edition = "2021"
+newline_style = "Unix"
+
+# Default code with is 100 characters, comments should follow
+# suit.
+wrap_comments = true
+
+# The default of this option creates hard-to-read nesting of
+# conditionals, turn it off.
+combine_control_expr = false
+
+# Group imports by module, but no higher. This avoids hard-to-read
+# nested use statements.
+imports_granularity = "Module"
+
+# Avoid vertical visual clutter by unnecessarily exploding
+# block-like arguments.
+overflow_delimited_expr = true
+
+# Miscellaneous
+format_code_in_doc_comments = true
+normalize_comments = true
diff --git a/users/tazjin/secrets/default.nix b/users/tazjin/secrets/default.nix
new file mode 100644
index 0000000000..5550103c5a
--- /dev/null
+++ b/users/tazjin/secrets/default.nix
@@ -0,0 +1,3 @@
+{ depot, ... }:
+
+depot.ops.secrets.mkSecrets ./. (import ./secrets.nix)
diff --git a/users/tazjin/secrets/geesefs-tazjins-files.age b/users/tazjin/secrets/geesefs-tazjins-files.age
new file mode 100644
index 0000000000..9132c7d108
--- /dev/null
+++ b/users/tazjin/secrets/geesefs-tazjins-files.age
@@ -0,0 +1,18 @@
+age-encryption.org/v1
+-> ssh-ed25519 dcsaLw SrmIul/C/aRTYy5+vVBB0H2bS65XayYf2TXrOSTEbGg
+Js016EtAxiFmyJ4gTmXEjsKT9JmIntcMNgAds+qT7Js
+-> ssh-ed25519 zcCuhA NfUQBKL1KgvUosB2y3oI5HwPjA+4kf8kbBbpNf43JAk
+oE4R2rz1sdBitKzQlzMzneyu8Rvc5utHYRyCeGCQR8g
+-> ssh-rsa zXi7VA
+aDDiygAF5benqqJ1387F9qVDyvb48BkBLAwRi7eUYWkG41s9XUdmK5ppjFdxy1c8
+fx5YcPjO3m84pIv0RxiK7rZhkVi1/eiHhT5lId83wIzQdybjKFSc85YjFO3mGv9A
+EeFMEmlfsRkBHYq/j6Npbg4M5kMxSuSwGSyt6qnoHSWT2phS+41WLA/XT9ln4pRR
+dBDO0ZyK/CpgfDuGo/JARLiGeEMwt7SvkyXidcbD8glg9buu1VGxb/8m/ob1yrbn
+y3mjfOFzO2zF8ZHuScWQlZgvaVk412Xne+n+wva12tS52dEX4FRSMtmUOB8Ai4oq
+wvWB6Ikru5jXRxe8NgDoZw
+-> ssh-ed25519 At5Mag yBwWJVhArq9iwngwaIph56iGfje8T55Ig0nW9268Kic
+T9IWxRJF1U0STinVlBJoaGoegERnWRjnGZeW0HHGQ9Y
+-> C"!?nfs%-grease >>.%|I mA Fd7?aw2m 37I
+vRH3yR7+Ow
+--- AKc40DwXghKw6GHzJUYNJYE0JqMr4M+hR41VRA1IvS4
+๒ลืtฺœ๑ใ)u;T™•	Œ'เ๖8๖=๘S7Rอ<Cx†t๙z_mdา0ZH2สแ^N—jท†๏*Cษ—	๋Šฃด1อนyฦผa—eน8c~วๆi	5/ฒ‹xลŸ7nลS^ฑaวsyg’ะฏ(CŠพ™คฅอ ั%N’ธกะ่๘|Y!ฎvะA๐๐I
\ No newline at end of file
diff --git a/users/tazjin/secrets/miniflux.age b/users/tazjin/secrets/miniflux.age
new file mode 100644
index 0000000000..753dc6f034
--- /dev/null
+++ b/users/tazjin/secrets/miniflux.age
@@ -0,0 +1,14 @@
+age-encryption.org/v1
+-> ssh-ed25519 dcsaLw SJBK+ym6o6dcB/+HFWzArbXS9RmyDjnglVxcXduJA1g
+pPWIi2A4G4X7I14HoZUWsNd/MOfhW1ZanwB/5OROSrw
+-> ssh-ed25519 zcCuhA oo/8OTqpV85g/9pha0qkmxwlYAlsc7v+nXbbtj67Jmc
+AexsAIgW6e5fYoPNJJZYdP61OvON2bKiL9ZJgLdG/zU
+-> ssh-ed25519 ph9lig 4evTl0M3SfdlmTixm3WnVqfHMPf/TYIyBKPdlfPisC0
+AK4GyhgqXN2wxbcFRGwbNNQJ4/2iFPt3CKGHosNJbmY
+-> ssh-ed25519 At5Mag JJ8r/qD5i+LLAY7jnnHXAgykAuHtzxtGGzdqw7BAogY
+wotjW3yaTq1IdqVUwoCVwzglXsmnzniQIt7SDBrF4jY
+-> sPHo{W-grease , h6 =mEp^w `ccnF
+QQEb+Vh1+Fv++oPQwdTfOB2Cg5JaP4GCOq0o3J+xSqCY1gE0cguwLGXwa6+Tylu2
+Kuh4pMovAxnlHUt44u6f
+--- yWQyncCrxJzVHffFaFT704BEp8hjUn09a+23r4S39N4
+}ปาฃฯ
๏l
˜ุI&m_{้ฝ์จXฬฎฟด๋?f1Mฝจษง|„ฦJ’‰ฝืVฦLไ<H๕5 ŒN›Q๖ใผๆQV์#>็งs2Mฐ0 ขI
\ No newline at end of file
diff --git a/users/tazjin/secrets/secrets.nix b/users/tazjin/secrets/secrets.nix
new file mode 100644
index 0000000000..12f12f721c
--- /dev/null
+++ b/users/tazjin/secrets/secrets.nix
@@ -0,0 +1,16 @@
+let
+  myKeys = import ../keys { };
+  allKeys = [
+    # local keys
+    myKeys.tverskoy_ed25519
+    myKeys.zamalek_ed25519
+    myKeys.khamovnik_agenix
+    # koptevo
+    "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMw2ZfdNZCXCOtbQNT6hztXCIkTcO9MBrOuDqMlmGOYK root@koptevo"
+  ];
+in
+{
+  "geesefs-tazjins-files.age".publicKeys = allKeys;
+  "miniflux.age".publicKeys = allKeys;
+  "tgsa-yandex.age".publicKeys = allKeys;
+}
diff --git a/users/tazjin/secrets/tgsa-yandex.age b/users/tazjin/secrets/tgsa-yandex.age
new file mode 100644
index 0000000000..b1400d0673
--- /dev/null
+++ b/users/tazjin/secrets/tgsa-yandex.age
Binary files differdiff --git a/users/tazjin/tgsa/.gitignore b/users/tazjin/tgsa/.gitignore
new file mode 100644
index 0000000000..29e65519ba
--- /dev/null
+++ b/users/tazjin/tgsa/.gitignore
@@ -0,0 +1,3 @@
+result
+/target
+**/*.rs.bk
diff --git a/users/tazjin/tgsa/Cargo.lock b/users/tazjin/tgsa/Cargo.lock
new file mode 100644
index 0000000000..6be9c490d4
--- /dev/null
+++ b/users/tazjin/tgsa/Cargo.lock
@@ -0,0 +1,1567 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
+
+[[package]]
+name = "ascii"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
+
+[[package]]
+name = "autocfg"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
+
+[[package]]
+name = "base64"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
+
+[[package]]
+name = "buf_redux"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
+dependencies = [
+ "memchr",
+ "safemem",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "cc"
+version = "1.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "chrono"
+version = "0.4.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "chunked_transfer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901"
+
+[[package]]
+name = "convert_case"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "crimp"
+version = "4087.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ead2c83f7d1f9b8e5a6f7a25985d0d1759ccd2cd72abb1eee2db65d05e12b39"
+dependencies = [
+ "curl",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "cssparser"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a"
+dependencies = [
+ "cssparser-macros",
+ "dtoa-short",
+ "itoa 0.4.8",
+ "matches",
+ "phf 0.8.0",
+ "proc-macro2",
+ "quote",
+ "smallvec",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "cssparser-macros"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
+dependencies = [
+ "quote",
+ "syn 2.0.57",
+]
+
+[[package]]
+name = "curl"
+version = "0.4.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2",
+ "windows-sys",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.72+curl-8.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "windows-sys",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "dtoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
+
+[[package]]
+name = "dtoa-short"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74"
+dependencies = [
+ "dtoa",
+]
+
+[[package]]
+name = "ego-tree"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591"
+
+[[package]]
+name = "errno"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
+
+[[package]]
+name = "filetime"
+version = "0.2.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "windows-sys",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
+dependencies = [
+ "mac",
+ "new_debug_unreachable",
+]
+
+[[package]]
+name = "fxhash"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "getopts"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.9.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "html5ever"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
+dependencies = [
+ "log",
+ "mac",
+ "markup5ever",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "idna"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+
+[[package]]
+name = "itoa"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+
+[[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.153"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+
+[[package]]
+name = "libz-sys"
+version = "1.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
+
+[[package]]
+name = "lock_api"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
+
+[[package]]
+name = "mac"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
+
+[[package]]
+name = "markup5ever"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
+dependencies = [
+ "log",
+ "phf 0.10.1",
+ "phf_codegen 0.10.0",
+ "string_cache",
+ "string_cache_codegen",
+ "tendril",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
+
+[[package]]
+name = "memchr"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
+[[package]]
+name = "multipart"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182"
+dependencies = [
+ "buf_redux",
+ "httparse",
+ "log",
+ "mime",
+ "mime_guess",
+ "quick-error",
+ "rand 0.8.5",
+ "safemem",
+ "tempfile",
+ "twoway",
+]
+
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "nodrop"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+
+[[package]]
+name = "openssl"
+version = "0.10.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
+dependencies = [
+ "bitflags 2.5.0",
+ "cfg-if",
+ "foreign-types",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.57",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "phf"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+dependencies = [
+ "phf_macros",
+ "phf_shared 0.8.0",
+ "proc-macro-hack",
+]
+
+[[package]]
+name = "phf"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
+dependencies = [
+ "phf_shared 0.10.0",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+dependencies = [
+ "phf_generator 0.8.0",
+ "phf_shared 0.8.0",
+]
+
+[[package]]
+name = "phf_codegen"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
+dependencies = [
+ "phf_generator 0.10.0",
+ "phf_shared 0.10.0",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+dependencies = [
+ "phf_shared 0.8.0",
+ "rand 0.7.3",
+]
+
+[[package]]
+name = "phf_generator"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
+dependencies = [
+ "phf_shared 0.10.0",
+ "rand 0.8.5",
+]
+
+[[package]]
+name = "phf_macros"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c"
+dependencies = [
+ "phf_generator 0.8.0",
+ "phf_shared 0.8.0",
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "precomputed-hash"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.20+deprecated"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quote"
+version = "1.0.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "getrandom 0.1.16",
+ "libc",
+ "rand_chacha 0.2.2",
+ "rand_core 0.5.1",
+ "rand_hc",
+ "rand_pcg",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha 0.3.1",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
+dependencies = [
+ "getrandom 0.1.16",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.12",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
+dependencies = [
+ "rand_core 0.5.1",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "ring"
+version = "0.16.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
+dependencies = [
+ "cc",
+ "libc",
+ "once_cell",
+ "spin",
+ "untrusted",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "rouille"
+version = "3.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921"
+dependencies = [
+ "base64 0.13.1",
+ "chrono",
+ "filetime",
+ "multipart",
+ "percent-encoding",
+ "rand 0.8.5",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "sha1_smol",
+ "threadpool",
+ "time",
+ "tiny_http",
+ "url",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
+dependencies = [
+ "bitflags 2.5.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
+
+[[package]]
+name = "safemem"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
+
+[[package]]
+name = "schannel"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "scraper"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5684396b456f3eb69ceeb34d1b5cb1a2f6acf7ca4452131efa3ba0ee2c2d0a70"
+dependencies = [
+ "cssparser",
+ "ego-tree",
+ "getopts",
+ "html5ever",
+ "matches",
+ "selectors",
+ "smallvec",
+ "tendril",
+]
+
+[[package]]
+name = "selectors"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe"
+dependencies = [
+ "bitflags 1.3.2",
+ "cssparser",
+ "derive_more",
+ "fxhash",
+ "log",
+ "matches",
+ "phf 0.8.0",
+ "phf_codegen 0.8.0",
+ "precomputed-hash",
+ "servo_arc",
+ "smallvec",
+ "thin-slice",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+
+[[package]]
+name = "serde"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.197"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.57",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.115"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
+dependencies = [
+ "itoa 1.0.11",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "servo_arc"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432"
+dependencies = [
+ "nodrop",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+
+[[package]]
+name = "siphasher"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "socket2"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "spin"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
+name = "string_cache"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
+dependencies = [
+ "new_debug_unreachable",
+ "once_cell",
+ "parking_lot",
+ "phf_shared 0.10.0",
+ "precomputed-hash",
+ "serde",
+]
+
+[[package]]
+name = "string_cache_codegen"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
+dependencies = [
+ "phf_generator 0.10.0",
+ "phf_shared 0.10.0",
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11a6ae1e52eb25aab8f3fb9fca13be982a373b8f1157ca14b897a825ba4a2d35"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "tendril"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
+dependencies = [
+ "futf",
+ "mac",
+ "utf-8",
+]
+
+[[package]]
+name = "tgsa"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "base64 0.21.7",
+ "crimp",
+ "ego-tree",
+ "lazy_static",
+ "openssl",
+ "ring",
+ "rouille",
+ "scraper",
+ "serde",
+ "serde_json",
+ "url",
+]
+
+[[package]]
+name = "thin-slice"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c"
+
+[[package]]
+name = "threadpool"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
+dependencies = [
+ "num_cpus",
+]
+
+[[package]]
+name = "time"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
+dependencies = [
+ "deranged",
+ "libc",
+ "num-conv",
+ "num_threads",
+ "powerfmt",
+ "serde",
+ "time-core",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "tiny_http"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
+dependencies = [
+ "ascii",
+ "chunked_transfer",
+ "httpdate",
+ "log",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
+[[package]]
+name = "twoway"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "unicase"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unicode-normalization"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "url"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
+name = "wasi"
+version = "0.9.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.57",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.57",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
+name = "web-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.4",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.4",
+ "windows_aarch64_msvc 0.52.4",
+ "windows_i686_gnu 0.52.4",
+ "windows_i686_msvc 0.52.4",
+ "windows_x86_64_gnu 0.52.4",
+ "windows_x86_64_gnullvm 0.52.4",
+ "windows_x86_64_msvc 0.52.4",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
diff --git a/users/tazjin/tgsa/Cargo.toml b/users/tazjin/tgsa/Cargo.toml
new file mode 100644
index 0000000000..8764ef6524
--- /dev/null
+++ b/users/tazjin/tgsa/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "tgsa"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0"
+crimp = "4087.0"
+rouille = { version = "3.5", default-features = false }
+url = "2.3"
+scraper = "0.13"
+ego-tree = "0.6" # in tandem with 'scraper'
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
+ring = "0.16.20"
+openssl = "0.10.54"
+base64 = "0.21.2"
+lazy_static = "1.4.0"
diff --git a/users/tazjin/tgsa/default.nix b/users/tazjin/tgsa/default.nix
new file mode 100644
index 0000000000..063781047a
--- /dev/null
+++ b/users/tazjin/tgsa/default.nix
@@ -0,0 +1,17 @@
+{ depot, pkgs, ... }:
+
+depot.third_party.naersk.buildPackage {
+  src = depot.nix.sparseTree {
+    root = ./.;
+    paths = [
+      ./Cargo.lock
+      ./Cargo.toml
+      ./src
+    ];
+  };
+
+  buildInputs = with pkgs; [
+    pkg-config
+    openssl
+  ];
+}
diff --git a/users/tazjin/tgsa/src/main.rs b/users/tazjin/tgsa/src/main.rs
new file mode 100644
index 0000000000..d9a5d4abc2
--- /dev/null
+++ b/users/tazjin/tgsa/src/main.rs
@@ -0,0 +1,403 @@
+use anyhow::{anyhow, Context, Result};
+use scraper::{Html, Selector};
+use std::collections::HashMap;
+use std::sync::RwLock;
+use std::time::{Duration, Instant};
+
+mod translate;
+
+#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+struct TgLink {
+    username: String,
+    message_id: usize,
+    translated: bool,
+}
+
+impl TgLink {
+    fn human_friendly_url(&self) -> String {
+        format!("t.me/{}/{}", self.username, self.message_id)
+    }
+
+    fn to_url(&self, embed: bool) -> String {
+        format!(
+            "https://t.me/{}/{}{}",
+            self.username,
+            self.message_id,
+            if embed { "?embed=1" } else { "" }
+        )
+    }
+
+    fn parse(url: &str, translated: bool) -> Option<Self> {
+        let url = url.strip_prefix('/')?;
+        let parsed = url::Url::parse(url).ok()?;
+
+        if parsed.host()? != url::Host::Domain("t.me") {
+            // only t.me links are supported
+            return None;
+        }
+
+        let parts = parsed.path_segments()?.collect::<Vec<&str>>();
+        if parts.len() != 2 {
+            // only message links are supported
+            return None;
+        }
+
+        Some(TgLink {
+            username: parts[0].into(),
+            message_id: parts[1].parse().ok()?,
+            translated,
+        })
+    }
+}
+
+fn fetch_post(link: &TgLink, embed: bool) -> Result<String> {
+    println!("fetching {}#{}", link.username, link.message_id);
+    let response = crimp::Request::get(&link.to_url(embed))
+        .send()
+        .context("failed to fetch embed data")?
+        .as_string()
+        .context("failed to decode embed data")?
+        .error_for_status(|resp| {
+            anyhow!("telegram request failed: {} ({})", resp.body, resp.status)
+        })?;
+
+    Ok(response.body)
+}
+
+// in some cases, posts can not be embedded, but telegram still
+// includes their content in metadata tags for content previews.
+//
+// we skip images in this case, as they are scaled down to thumbnail
+// size and not useful.
+fn fetch_fallback(link: &TgLink) -> Result<Option<String>> {
+    let post = fetch_post(link, false)?;
+    let doc = Html::parse_document(&post);
+    let desc_sel = Selector::parse("meta[property=\"og:description\"]").unwrap();
+    let desc_elem = match doc.select(&desc_sel).next() {
+        None => return Ok(None),
+        Some(elem) => elem,
+    };
+
+    let content = match desc_elem.value().attr("content") {
+        None => return Ok(None),
+        Some(content) => content.to_string(),
+    };
+
+    Ok(Some(content))
+}
+
+#[derive(Debug)]
+struct TgMessage {
+    author: String,
+    message: Option<String>,
+    photos: Vec<String>,
+    videos: Vec<String>,
+    has_audio: bool,
+}
+
+fn extract_photo_url(style: &str) -> Option<&str> {
+    let url_start = style.find("url('")? + 5;
+    let url_end = style.find("')")?;
+
+    Some(&style[url_start..url_end])
+}
+
+fn parse_tgmessage(embed: &str) -> Result<TgMessage> {
+    let doc = Html::parse_document(embed);
+
+    let author_sel = Selector::parse("a.tgme_widget_message_owner_name").unwrap();
+    let author = doc
+        .select(&author_sel)
+        .next()
+        .ok_or_else(|| anyhow!("failed to find message author"))?
+        .text()
+        .collect::<Vec<&str>>()
+        .concat();
+
+    let msg_sel = Selector::parse("div.tgme_widget_message_text.js-message_text").unwrap();
+
+    // The ElementRef::text() iterator does not yield newlines present
+    // in the message, so it is partially reimplemented here.
+    let message = if let Some(msg_elem) = doc.select(&msg_sel).next() {
+        use ego_tree::iter::Edge;
+        use scraper::node::Node;
+
+        let mut out = String::new();
+
+        for edge in &mut msg_elem.traverse() {
+            if let Edge::Open(node) = edge {
+                match node.value() {
+                    Node::Text(ref text) => out.push_str(text),
+                    Node::Element(elem) if elem.name() == "br" => out.push('\n'),
+                    _ => {}
+                }
+            }
+        }
+
+        Some(out)
+    } else {
+        // Not all Telegram messages have a textual message.
+        None
+    };
+
+    let photo_sel = Selector::parse("a.tgme_widget_message_photo_wrap").unwrap();
+    let mut photos = vec![];
+
+    for photo in doc.select(&photo_sel) {
+        if let Some(style) = photo.value().attr("style") {
+            if let Some(url) = extract_photo_url(style) {
+                photos.push(url.to_string())
+            }
+        }
+    }
+
+    let video_sel = Selector::parse("i.tgme_widget_message_video_thumb").unwrap();
+    let mut videos = vec![];
+
+    for video in doc.select(&video_sel) {
+        if let Some(style) = video.value().attr("style") {
+            if let Some(url) = extract_photo_url(style) {
+                videos.push(url.to_string())
+            }
+        }
+    }
+
+    let audio_sel = Selector::parse("audio.tgme_widget_message_voice.js-message_voice").unwrap();
+    let mut has_audio = false;
+    if doc.select(&audio_sel).next().is_some() {
+        has_audio = true;
+    }
+
+    Ok(TgMessage {
+        author,
+        message,
+        photos,
+        videos,
+        has_audio,
+    })
+}
+
+// create a permanent media url that tgsa can redirect if telegram
+// changes its upstream links.
+//
+// assumes that tgsa lives at tgsa.tazj.in (which it does)
+fn media_url(link: &TgLink, idx: usize) -> String {
+    format!(
+        "https://tgsa.tazj.in/img/{}/{}/{}",
+        link.username, link.message_id, idx
+    )
+}
+
+fn to_bbcode(link: &TgLink, msg: &TgMessage) -> String {
+    let mut out = String::new();
+
+    out.push_str(&format!("[quote=\"{}\"]\n", msg.author));
+
+    for video in 0..msg.videos.len() {
+        out.push_str(&format!("[url=\"{}\"]", link.to_url(true)));
+
+        // video thumbnail links are appended to the photos, hence the
+        // addition here
+        out.push_str(&format!(
+            "[img]{}[/img]",
+            media_url(link, video + msg.photos.len())
+        ));
+
+        out.push_str("[/url]\n");
+        out.push_str("[sub](Click thumbnail to open video)[/sub]\n")
+    }
+
+    for photo in 0..msg.photos.len() {
+        out.push_str(&format!("[timg]{}[/timg]\n", media_url(link, photo)));
+    }
+
+    if msg.has_audio {
+        out.push_str(&format!(
+            "[i]This message has audio attached. Go [url=\"{}\"]to Telegram[/url] to listen.[/i]",
+            link.to_url(true),
+        ));
+    }
+
+    if let Some(message) = &msg.message {
+        out.push_str(message);
+    }
+
+    out.push_str("\n[/quote]\n");
+
+    out.push_str(&format!(
+        "[sub](from [url=\"{}\"]{}[/url], via [url=\"https://tgsa.tazj.in\"]tgsa[/url])[/sub]\n",
+        link.to_url(true),
+        link.human_friendly_url(),
+    ));
+
+    out
+}
+
+// cache everything for one hour
+const CACHE_EXPIRY: Duration = Duration::from_secs(60 * 60);
+
+#[derive(Clone)]
+struct TgPost {
+    bbcode: String,
+    at: Instant,
+    media: Vec<String>,
+}
+
+type Cache = RwLock<HashMap<TgLink, TgPost>>;
+
+fn fetch_with_cache(cache: &Cache, link: &TgLink) -> Result<TgPost> {
+    if let Some(entry) = cache.read().unwrap().get(link) {
+        if Instant::now() - entry.at < CACHE_EXPIRY {
+            println!("serving {}#{} from cache", link.username, link.message_id);
+            return Ok(entry.clone());
+        }
+    }
+
+    // limit concurrent fetching
+    // TODO(tazjin): per link?
+    let mut writer = cache.write().unwrap();
+
+    let post = fetch_post(link, true)?;
+    let mut msg = parse_tgmessage(&post)?;
+
+    if msg.message.is_none() {
+        msg.message = fetch_fallback(link)?;
+    }
+
+    if let Some(message) = &msg.message {
+        if link.translated {
+            println!("translating {}#{}", link.username, link.message_id);
+            msg.message = Some(translate::fetch_translation(message)?);
+        }
+    }
+
+    let bbcode = to_bbcode(link, &msg);
+
+    let mut media = vec![];
+    media.append(&mut msg.photos);
+    media.append(&mut msg.videos);
+
+    let post = TgPost {
+        bbcode,
+        media,
+        at: Instant::now(),
+    };
+
+    writer.insert(link.clone(), post.clone());
+
+    Ok(post)
+}
+
+fn handle_img_redirect(cache: &Cache, img_path: &str) -> Result<rouille::Response> {
+    // img_path:
+    //
+    // RWApodcast/113/1
+    // ^          ^   ^
+    // |          |   |
+    // |          |   image (0-indexed)
+    // |          post ID
+    // username
+
+    let img_parts: Vec<&str> = img_path.split('/').collect();
+
+    if img_parts.len() != 3 {
+        println!("invalid image link: {}", img_path);
+        return Err(anyhow!("not a valid image link: {}", img_path));
+    }
+
+    let link = TgLink {
+        username: img_parts[0].into(),
+        message_id: img_parts[1].parse().context("failed to parse message_id")?,
+        translated: false,
+    };
+
+    let img_idx: usize = img_parts[2].parse().context("failed to parse img_idx")?;
+    let post = fetch_with_cache(cache, &link)?;
+
+    if img_idx >= post.media.len() {
+        return Err(anyhow!(
+            "there is no {}. image in {}/{}",
+            img_idx,
+            link.username,
+            link.message_id
+        ));
+    }
+
+    Ok(rouille::Response::redirect_303(post.media[img_idx].clone()))
+}
+
+fn handle_tg_link(cache: &Cache, link: &TgLink) -> Result<rouille::Response> {
+    let post = fetch_with_cache(cache, link)?;
+    Ok(rouille::Response::text(post.bbcode))
+}
+
+fn main() {
+    crimp::init();
+
+    let cache: Cache = RwLock::new(HashMap::new());
+
+    rouille::start_server("0.0.0.0:8472", move |request| {
+        let mut raw_url = request.raw_url();
+        let mut translate = false;
+
+        let response = loop {
+            if raw_url.starts_with("/img/") {
+                break handle_img_redirect(&cache, &raw_url[5..]);
+            }
+
+            if raw_url.starts_with("/translate/") {
+                translate = true;
+                raw_url = &raw_url[10..];
+            }
+
+            break match TgLink::parse(raw_url, translate) {
+                None => Ok(rouille::Response::text(
+                    r#"tgsa
+----
+
+this is a stupid program that lets you turn telegram message links
+into BBcode suitable for pasting on somethingawful dot com
+
+you can use it by putting a valid telegram message link in the url and
+waiting for some bbcode to show up.
+
+for example:
+
+  https://tgsa.tazj.in/https://t.me/RWApodcast/113
+
+yes, that looks stupid, but it works
+
+if you see this message and think you did the above correctly, you
+didn't. try again. idiot.
+
+it can also translate posts from russian, ukrainian or whatever other
+dumb language you speak into english by adding `/translate/`, for
+example:
+
+  https://tgsa.tazj.in/translate/https://t.me/strelkovii/4329
+
+expect this to be slow though. that's the price to pay for translating
+shitty slang.
+
+pm me on the forums if any of this makes you mad or something.
+"#,
+                )),
+                Some(link) => handle_tg_link(&cache, &link),
+            };
+        };
+
+        match response {
+            Ok(resp) => resp,
+            Err(err) => {
+                println!("something failed: {}", err);
+                rouille::Response::text(format!(
+                    r#"ugh, something broke: {}
+
+nobody has been alerted about this and it has probably not been
+logged. pm me on the forums if you think it's important."#,
+                    err
+                ))
+            }
+        }
+    });
+}
diff --git a/users/tazjin/tgsa/src/translate.rs b/users/tazjin/tgsa/src/translate.rs
new file mode 100644
index 0000000000..35d7b35ca8
--- /dev/null
+++ b/users/tazjin/tgsa/src/translate.rs
@@ -0,0 +1,191 @@
+//! integration with yandex cloud translate api, for automatically
+//! translating telegram posts.
+//!
+//! most of this module is concerned with handling the authentication
+//! tokens for yandex cloud, as jwt signing needs to be handled
+//! manually (none of the rust jwt libraries that i tried actually
+//! work).
+
+use anyhow::{anyhow, Context, Result};
+use base64::prelude::BASE64_URL_SAFE_NO_PAD as B64;
+use base64::Engine;
+use lazy_static::lazy_static;
+use ring::signature as sig;
+use serde::Deserialize;
+use serde_json::{json, Value};
+use std::sync::Mutex;
+use std::time::{Duration, SystemTime};
+
+/// token exchange url (exchanging a signed jwt for an iam token
+/// understood by the translation service)
+const TOKEN_URL: &str = "https://iam.api.cloud.yandex.net/iam/v1/tokens";
+
+/// translation endpoint
+const TRANSLATE_URL: &str = "https://translate.api.cloud.yandex.net/translate/v2/translate";
+
+/// describes the private key as downloaded from yandex, pem-encoded.
+#[derive(Deserialize)]
+struct AuthorizedKey {
+    id: String,
+    service_account_id: String,
+    private_key: String,
+}
+
+/// cached iam token for yandex cloud
+struct Token {
+    token: String,
+    expiry: SystemTime,
+}
+
+impl Token {
+    fn is_expired(&self) -> bool {
+        self.expiry < SystemTime::now()
+    }
+}
+
+lazy_static! {
+    static ref KEY_FILE: String =
+        std::env::var("YANDEX_KEY_FILE").expect("`YANDEX_KEY_FILE` variable should be set");
+    static ref CACHED_TOKEN: Mutex<Token> = {
+        let token = refresh_token().expect("fetching initial translation token must not fail");
+        Mutex::new(token)
+    };
+}
+
+/// wrap all the authentication logic below into a single function.
+fn refresh_token() -> Result<Token> {
+    let file = std::fs::File::open(KEY_FILE.as_str())?;
+    let key: AuthorizedKey = serde_json::from_reader(file)?;
+    let jwt = sign_yandex_jwt(&key)?;
+    let token = fetch_iam_token(&jwt)?;
+
+    Ok(Token {
+        token,
+        expiry: SystemTime::now() + Duration::from_secs(3600),
+    })
+}
+
+/// wrapper around the cached token that refreshes if required.
+fn current_token() -> Result<String> {
+    let mut token = CACHED_TOKEN
+        .lock()
+        .expect("thread operating on token should never fail");
+
+    if token.is_expired() {
+        println!("refreshing translation token");
+        *token = refresh_token().context("refreshing translation token")?;
+    }
+
+    Ok(token.token.clone())
+}
+
+/// use openssl to read the pem-encoded key, as ring itself is not
+/// capable of this.
+fn read_pem_key(key: &AuthorizedKey) -> Result<sig::RsaKeyPair> {
+    let rsa = openssl::rsa::Rsa::private_key_from_pem(key.private_key.as_bytes())
+        .context("parsing RSA key")?;
+
+    let der = rsa
+        .private_key_to_der()
+        .context("encoding key as DER for ring")?;
+
+    sig::RsaKeyPair::from_der(&der).map_err(|err| anyhow!("decoding DER key in ring: {}", err))
+}
+
+/// manually construct and sign the jwt required to perform the
+/// iam-token key exchange with yandex.
+fn sign_yandex_jwt(key: &AuthorizedKey) -> Result<String> {
+    let iat = SystemTime::now()
+        .duration_since(SystemTime::UNIX_EPOCH)?
+        .as_secs();
+
+    let header = json!({
+        "typ": "JWT",
+        "alg": "PS256",
+        "kid": key.id,
+    })
+    .to_string();
+
+    let payload = json!({
+        "iss": key.service_account_id,
+        "aud": TOKEN_URL,
+        "iat": iat,
+        "exp": iat + 60,
+    })
+    .to_string();
+
+    let unsigned = format!("{}.{}", B64.encode(header), B64.encode(payload));
+    let key_pair = read_pem_key(key)?;
+
+    let rng = ring::rand::SystemRandom::new();
+    let mut signature = vec![0; key_pair.public_modulus_len()];
+    key_pair
+        .sign(
+            &sig::RSA_PSS_SHA256,
+            &rng,
+            unsigned.as_bytes(),
+            &mut signature,
+        )
+        .map_err(|err| anyhow!("while signing JWT: {}", err))?;
+
+    Ok(format!("{}.{}", unsigned, B64.encode(&signature)))
+}
+
+/// exchange the jwt for an iam token
+fn fetch_iam_token(token: &str) -> Result<String> {
+    #[derive(Deserialize)]
+    #[serde(rename_all = "camelCase")]
+    struct TokenResponse {
+        iam_token: String,
+    }
+
+    let response = crimp::Request::post(TOKEN_URL)
+        .json(&json!({
+            "jwt": token,
+        }))?
+        .send()?
+        .error_for_status(|resp| {
+            anyhow::anyhow!("{} ({})", String::from_utf8_lossy(&resp.body), resp.status)
+        })?
+        .as_json::<TokenResponse>()
+        .context("deserialising IAM token")?;
+
+    Ok(response.body.iam_token)
+}
+
+pub fn fetch_translation(message: &str) -> Result<String> {
+    let request_body = json!({
+        "folderId": "b1g5k8f0tgimg06i6p5h",
+        "texts": [ message ],
+        "targetLanguageCode": "en",
+    });
+
+    let response = crimp::Request::post(TRANSLATE_URL)
+        .bearer_auth(&current_token()?)
+        .context("adding 'Bearer' token")?
+        .json(&request_body)
+        .context("preparing JSON body")?
+        .send()
+        .context("failed to fetch translation from yandex")?
+        .error_for_status(|resp| {
+            anyhow!(
+                "translation request failed: {} ({})",
+                String::from_utf8_lossy(&resp.body),
+                resp.status
+            )
+        })?
+        .as_json::<Value>()?
+        .body;
+
+    let translation = response
+        .get("translations")
+        .ok_or_else(|| anyhow!("missing 'translations' key"))?
+        .get(0)
+        .ok_or_else(|| anyhow!("translations list is empty"))?
+        .get("text")
+        .ok_or_else(|| anyhow!("translation missing 'text' key"))?
+        .as_str()
+        .ok_or_else(|| anyhow!("'text' was not a string"))?;
+
+    Ok(translation.to_string())
+}
diff --git a/users/tazjin/tvix-eval-value.d2 b/users/tazjin/tvix-eval-value.d2
new file mode 100644
index 0000000000..dad2dbcef2
--- /dev/null
+++ b/users/tazjin/tvix-eval-value.d2
@@ -0,0 +1,98 @@
+# D2 diagram of tvix-eval's `Value` type.
+#
+# can be rendered at https://play.d2lang.com/
+#
+# colours have meanings:
+#
+# yellow: recurses
+# orange: heap allocation
+# red: refcount
+#
+# this intentionally does *not* include some internal variants
+
+Value -> Null
+Value -> Bool
+Value -> Integer
+Value -> Float
+
+Box*.style.fill: "lightsalmon"
+Rc*.style.fill: "salmon"
+Vec\<*.style.fill: "salmon"
+
+Value -> String -> NixString -> "Box<str>"
+
+Value -> Path -> "Box<PathBuf>" -> PathBuf
+PathBuf.style.fill: "lightsalmon"
+
+# attribute sets are kinda complicated
+Value -> Attrs -> "Box<NixAttrs>" -> NixAttrs
+NixAttrs -> Empty
+NixAttrs -> KV
+KV.style.fill: "LemonChiffon"
+KV -> Value
+KV -> Value
+NixAttrs -> Map
+Map -> "OrdMap<NixString, Value>" -> "MapEntry<NixString, Value>"
+"OrdMap<NixString, Value>".style.fill: "lightsalmon"
+"MapEntry<NixString, Value>".style.fill: "salmon"
+"MapEntry<NixString, Value>".style.multiple: true
+"MapEntry<NixString, Value>" -> NixString
+"MapEntry<NixString, Value>" -> Value
+"MapEntry<NixString, Value>".style.stroke-width: 15
+"MapEntry<NixString, Value>".style.stroke: "lemonchiffon"
+
+Value -> List -> NixList -> "Rc<imbl::Vector<Value>>"
+"Rc<imbl::Vector<Value>>" -> "VecEntry<Value>" -> Value
+"VecEntry<Value>".style.multiple: true
+"VecEntry<Value>".style.fill: "salmon"
+"VecEntry<Value>".style.stroke-width: 15
+"VecEntry<Value>".style.stroke: "lemonchiffon"
+
+# closures
+
+Value -> Closure -> "Rc<Closure>" -> Closure
+Closure -> "Rc<Lambda>" -> Lambda
+
+Lambda -> Chunk
+Lambda -> SmolStr: sometimes allocates
+SmolStr.style.fill: "lightsalmon"
+Lambda -> usize
+Lambda -> "Option<Formals>" -> Formals
+
+Formals -> "HashMap<NixString, bool>" -> "MapEntry<NixString, bool>"
+"HashMap<NixString, bool>".style.fill: "lightsalmon"
+"MapEntry<NixString, bool>".style.fill: "salmon"
+"MapEntry<NixString, bool>".style.multiple: true
+"MapEntry<NixString, bool>" -> NixString
+
+Closure -> "Rc<Upvalues>" -> Upvalues
+
+Upvalues -> "Vec<Value>"
+"Vec<Value>" -> Value
+"Vec<Value>".style.stroke-width: 15
+"Vec<Value>".style.stroke: "lemonchiffon"
+Upvalues -> "Option<Vec<Value>>"
+"Option<Vec<Value>>" -> Value
+"Option<Vec<Value>>".style.fill: "lightsalmon"
+"Option<Vec<Value>>".style.stroke-width: 15
+"Option<Vec<Value>>".style.stroke: "lemonchiffon"
+
+Value -> Blueprint -> "Rc<Lambda>"
+
+# builtins
+
+Value -> Builtin -> "Box<BuiltinRepr>" -> BuiltinRepr
+BuiltinRepr -> "Rc<dyn BuiltinGen>"
+BuiltinRepr -> "Vec<Value>"
+
+# thunks
+
+Value -> Thunk -> "Rc<RefCell<ThunkRepr>>" -> ThunkRepr
+ThunkRepr -> Suspended
+Suspended -> "Rc<Lambda>"
+Suspended -> "Rc<Upvalues>"
+
+ThunkRepr -> Native -> "Box<dyn Fn() -> Result<Value, ErrorKind>>"
+ThunkRepr -> Blackhole
+ThunkRepr -> Evaluated -> Value
+Evaluated.style.fill: "lemonchiffon"
diff --git a/users/tazjin/yddns/.gitignore b/users/tazjin/yddns/.gitignore
new file mode 100644
index 0000000000..2f7896d1d1
--- /dev/null
+++ b/users/tazjin/yddns/.gitignore
@@ -0,0 +1 @@
+target/
diff --git a/users/tazjin/yddns/Cargo.lock b/users/tazjin/yddns/Cargo.lock
new file mode 100644
index 0000000000..58b37d553b
--- /dev/null
+++ b/users/tazjin/yddns/Cargo.lock
@@ -0,0 +1,1425 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "addr2line"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
+[[package]]
+name = "async-stream"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51"
+dependencies = [
+ "async-stream-impl",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-stream-impl"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "axum"
+version = "0.6.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bitflags 1.3.2",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "sync_wrapper",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "mime",
+ "rustversion",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "backtrace"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
+dependencies = [
+ "addr2line",
+ "cc",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "base64"
+version = "0.21.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+
+[[package]]
+name = "bytes"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
+
+[[package]]
+name = "cc"
+version = "1.0.84"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+
+[[package]]
+name = "crc32fast"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crimp"
+version = "4087.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ead2c83f7d1f9b8e5a6f7a25985d0d1759ccd2cd72abb1eee2db65d05e12b39"
+dependencies = [
+ "curl",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "curl"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
+dependencies = [
+ "curl-sys",
+ "libc",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "socket2 0.4.10",
+ "winapi",
+]
+
+[[package]]
+name = "curl-sys"
+version = "0.4.68+curl-8.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+ "windows-sys",
+]
+
+[[package]]
+name = "either"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "flate2"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
+
+[[package]]
+name = "futures-task"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
+
+[[package]]
+name = "futures-util"
+version = "0.3.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+
+[[package]]
+name = "h2"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http",
+ "indexmap 1.9.3",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "hashbrown"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "home"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "http"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f95b9abcae896730d42b78e09c155ed4ddf82c07b4de772c64aee5b2d8b7c150"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
+dependencies = [
+ "bytes",
+ "http",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "0.14.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2 0.4.10",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
+dependencies = [
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+ "tokio-io-timeout",
+]
+
+[[package]]
+name = "indexmap"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+dependencies = [
+ "autocfg",
+ "hashbrown 0.12.3",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.2",
+]
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libc"
+version = "0.2.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+
+[[package]]
+name = "libz-sys"
+version = "1.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
+
+[[package]]
+name = "log"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
+[[package]]
+name = "memchr"
+version = "2.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
+dependencies = [
+ "adler",
+]
+
+[[package]]
+name = "mio"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "multimap"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+
+[[package]]
+name = "object"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+
+[[package]]
+name = "petgraph"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
+dependencies = [
+ "fixedbitset",
+ "indexmap 2.1.0",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "prettyplease"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+dependencies = [
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prost"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
+dependencies = [
+ "bytes",
+ "heck",
+ "itertools",
+ "lazy_static",
+ "log",
+ "multimap",
+ "petgraph",
+ "prettyplease",
+ "prost",
+ "prost-types",
+ "regex",
+ "syn 1.0.109",
+ "tempfile",
+ "which",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
+dependencies = [
+ "prost",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "regex"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+
+[[package]]
+name = "ring"
+version = "0.17.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
+dependencies = [
+ "cc",
+ "getrandom",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+
+[[package]]
+name = "rustix"
+version = "0.38.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+dependencies = [
+ "bitflags 2.4.1",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustls"
+version = "0.21.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c"
+dependencies = [
+ "log",
+ "ring",
+ "rustls-webpki",
+ "sct",
+]
+
+[[package]]
+name = "rustls-native-certs"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
+dependencies = [
+ "openssl-probe",
+ "rustls-pemfile",
+ "schannel",
+ "security-framework",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
+dependencies = [
+ "base64",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.101.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "sct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
+dependencies = [
+ "ring",
+ "untrusted",
+]
+
+[[package]]
+name = "security-framework"
+version = "2.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.192"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.108"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "socket2"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "tempfile"
+version = "3.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "redox_syscall",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio"
+version = "1.34.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2 0.5.5",
+ "tokio-macros",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-io-timeout"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
+dependencies = [
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tonic"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a"
+dependencies = [
+ "async-stream",
+ "async-trait",
+ "axum",
+ "base64",
+ "bytes",
+ "flate2",
+ "futures-core",
+ "futures-util",
+ "h2",
+ "http",
+ "http-body",
+ "hyper",
+ "hyper-timeout",
+ "percent-encoding",
+ "pin-project",
+ "prost",
+ "rustls-native-certs",
+ "rustls-pemfile",
+ "tokio",
+ "tokio-rustls",
+ "tokio-stream",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tonic-build"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6fdaae4c2c638bb70fe42803a26fbd6fc6ac8c72f5c59f67ecc2a2dcabf4b07"
+dependencies = [
+ "prettyplease",
+ "proc-macro2",
+ "prost-build",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "indexmap 1.9.3",
+ "pin-project",
+ "pin-project-lite",
+ "rand",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
+[[package]]
+name = "tower-service"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
+
+[[package]]
+name = "tracing"
+version = "0.1.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "walkdir"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "yandex-cloud"
+version = "2023.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98f67140264e8e090e26b70925096adf295569c057a8b2ad2cd7e0f10c01cfae"
+dependencies = [
+ "prost",
+ "prost-types",
+ "tonic",
+ "tonic-build",
+ "walkdir",
+]
+
+[[package]]
+name = "yddns"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "crimp",
+ "tokio",
+ "yandex-cloud",
+]
diff --git a/users/tazjin/yddns/Cargo.toml b/users/tazjin/yddns/Cargo.toml
new file mode 100644
index 0000000000..78691f303d
--- /dev/null
+++ b/users/tazjin/yddns/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "yddns"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0"
+crimp = "4087.0"
+tokio = "*" # pulled in by yandex-cloud
+yandex-cloud = "2023.6.13"
diff --git a/users/tazjin/yddns/default.nix b/users/tazjin/yddns/default.nix
new file mode 100644
index 0000000000..40b0d1c24e
--- /dev/null
+++ b/users/tazjin/yddns/default.nix
@@ -0,0 +1,9 @@
+{ depot, pkgs, ... }:
+
+depot.third_party.naersk.buildPackage {
+  src = ./.;
+  buildInputs = with pkgs; [
+    pkg-config
+    openssl
+  ];
+}
diff --git a/users/tazjin/yddns/src/main.rs b/users/tazjin/yddns/src/main.rs
new file mode 100644
index 0000000000..2e2f9fe02f
--- /dev/null
+++ b/users/tazjin/yddns/src/main.rs
@@ -0,0 +1,142 @@
+use anyhow::{anyhow, bail, Context, Result};
+use crimp::Request;
+use std::env;
+use std::net::Ipv4Addr;
+use tokio::runtime;
+use yandex_cloud::tonic_exports::{Channel, Endpoint, InterceptedService};
+use yandex_cloud::yandex::cloud::dns::v1 as dns;
+use yandex_cloud::yandex::cloud::dns::v1::dns_zone_service_client::DnsZoneServiceClient;
+use yandex_cloud::{AuthInterceptor, TokenProvider};
+
+type DnsClient<T> = DnsZoneServiceClient<InterceptedService<Channel, AuthInterceptor<T>>>;
+
+/// Fetch the current IP from the given URL. It should be the URL of a
+/// site that responds only with the IP in plain text, and nothing else.
+fn get_current_ip(source: &str) -> Result<Ipv4Addr> {
+    let response = Request::get(source)
+        .send()
+        .context("failed to fetch current IP")?
+        .error_for_status(|resp| anyhow!("error response ({})", resp.status))
+        .context("received error response for IP")?
+        .as_string()?
+        .body;
+
+    Ok(response.trim().parse().with_context(|| {
+        format!(
+            "failed to parse IP address from response body: {}",
+            response
+        )
+    })?)
+}
+
+/// Fetch the current address of the target record.
+async fn fetch_current_record_addr<T: TokenProvider>(
+    client: &mut DnsClient<T>,
+    zone_id: &str,
+    record_name: &str,
+) -> Result<Ipv4Addr> {
+    let req = dns::GetDnsZoneRecordSetRequest {
+        dns_zone_id: zone_id.into(),
+        name: record_name.into(),
+        r#type: "A".into(),
+    };
+
+    let response = client
+        .get_record_set(req)
+        .await
+        .context("failed to fetch current record set")?
+        .into_inner();
+
+    if response.data.len() != 1 {
+        bail!(
+            "expected exactly one record for 'A {}', but found {}",
+            record_name,
+            response.data.len()
+        );
+    }
+
+    Ok(response.data[0]
+        .parse()
+        .context("failed to parse returned record")?)
+}
+
+/// Update the record with the new address, if required.
+async fn update_record<T: TokenProvider>(
+    client: &mut DnsClient<T>,
+    zone_id: &str,
+    record_name: &str,
+    new_address: Ipv4Addr,
+) -> Result<()> {
+    let request = dns::UpsertRecordSetsRequest {
+        dns_zone_id: zone_id.into(),
+        replacements: vec![dns::RecordSet {
+            name: record_name.into(),
+            r#type: "A".into(),
+            ttl: 3600, // 1 hour
+            data: vec![new_address.to_string()],
+        }],
+        ..Default::default()
+    };
+
+    client
+        .upsert_record_sets(request)
+        .await
+        .context("failed to update record")?;
+
+    Ok(())
+}
+
+/// Compare the record with the expected value, and issue an update if
+/// necessary.
+async fn compare_update_record<T: TokenProvider>(
+    client: &mut DnsClient<T>,
+    zone_id: &str,
+    record_name: &str,
+    new_ip: Ipv4Addr,
+) -> Result<()> {
+    let old_ip = fetch_current_record_addr(client, zone_id, record_name).await?;
+
+    if old_ip == new_ip {
+        println!("IP address unchanged ({})", old_ip);
+        return Ok(());
+    }
+
+    println!(
+        "IP address changed: current record points to {}, but address is {}",
+        old_ip, new_ip
+    );
+
+    update_record(client, zone_id, record_name, new_ip).await?;
+    println!("successfully updated '{}' to 'A {}'", record_name, new_ip);
+
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    let token =
+        env::var("YANDEX_CLOUD_TOKEN").context("Yandex Cloud authentication token unset")?;
+    let target_zone_id =
+        env::var("TARGET_ZONE").unwrap_or_else(|_| "dnsd0tif5mokfu0mg8i5".to_string());
+    let target_record = env::var("TARGET_RECORD").unwrap_or_else(|_| "khtrsk".to_string());
+
+    let current_ip = get_current_ip("http://ifconfig.me")?;
+    println!("current IP address is '{}'", current_ip);
+
+    let rt = runtime::Builder::new_current_thread()
+        .enable_time()
+        .enable_io()
+        .build()?;
+
+    rt.block_on(async move {
+        let channel = Endpoint::from_static("https://dns.api.cloud.yandex.net")
+            .connect()
+            .await?;
+
+        let mut client =
+            DnsZoneServiceClient::with_interceptor(channel, AuthInterceptor::new(token));
+
+        compare_update_record(&mut client, &target_zone_id, &target_record, current_ip).await
+    })?;
+
+    Ok(())
+}
diff --git a/users/wpcarro/.envrc b/users/wpcarro/.envrc
new file mode 100644
index 0000000000..3e1e1a35f0
--- /dev/null
+++ b/users/wpcarro/.envrc
@@ -0,0 +1,5 @@
+source_up
+
+export PATH="${PWD}/bin:${PATH}"
+export REPO_ROOT=$(git rev-parse --show-toplevel)
+export WPCARRO="${REPO_ROOT}/users/wpcarro"
diff --git a/users/wpcarro/.gitignore b/users/wpcarro/.gitignore
new file mode 100644
index 0000000000..64703ed129
--- /dev/null
+++ b/users/wpcarro/.gitignore
@@ -0,0 +1,35 @@
+.vim
+./configs/secrets
+**/*/.emacs.d/quelpa/**/*
+**/*/.emacs.d/elpa/**/*
+**/*/.emacs.d/emojis
+**/*/.emacs.d/auto-save-list/**/*
+**/*/.emacs.d/eshell/
+**/*/.emacs.d/var/**/*
+**/*/.emacs.d/.cache/**/*
+**/*/.emacs.d/request
+**/*/.emacs.d/network-security.data
+**/*/.emacs.d/smex-items
+**/*/.gnupg/random_seed
+**/*/.gnupg/private-keys-v1.d
+.netrwhist
+Vundle.vim
+**/*/.emacs.d/custom.el
+**/*/.emacs.d/projectile-bookmarks.eld
+**/*/.emacs.d/bookmarks
+**/*/transient/history.el
+*.hi
+*.o
+__pycache__
+*.class
+node_modules/
+/configs/.config/fish/config.fish
+/configs/.config/fish/fish_variables
+/website/blog/public/
+/emacs/.emacs.d/tramp
+.gitsecret/keys/random_seed
+!*.secret
+secrets.json
+
+# Nix gcroots symlinks created by .envrc
+/.gcroots/*
diff --git a/users/wpcarro/.gitsecret/keys/pubring.kbx b/users/wpcarro/.gitsecret/keys/pubring.kbx
new file mode 100644
index 0000000000..692d5c67b0
--- /dev/null
+++ b/users/wpcarro/.gitsecret/keys/pubring.kbx
Binary files differdiff --git a/users/wpcarro/.gitsecret/keys/pubring.kbx~ b/users/wpcarro/.gitsecret/keys/pubring.kbx~
new file mode 100644
index 0000000000..c0a748ce2c
--- /dev/null
+++ b/users/wpcarro/.gitsecret/keys/pubring.kbx~
Binary files differdiff --git a/users/wpcarro/.gitsecret/keys/trustdb.gpg b/users/wpcarro/.gitsecret/keys/trustdb.gpg
new file mode 100644
index 0000000000..369485be06
--- /dev/null
+++ b/users/wpcarro/.gitsecret/keys/trustdb.gpg
Binary files differdiff --git a/users/wpcarro/.gitsecret/paths/mapping.cfg b/users/wpcarro/.gitsecret/paths/mapping.cfg
new file mode 100644
index 0000000000..fda2c84fb3
--- /dev/null
+++ b/users/wpcarro/.gitsecret/paths/mapping.cfg
@@ -0,0 +1 @@
+secrets.json:7d596a3ed16403040d89dd7e033a2af58e7aaabb6f246f44751b80a1863a2949
diff --git a/users/wpcarro/OWNERS b/users/wpcarro/OWNERS
new file mode 100644
index 0000000000..ac1283f56d
--- /dev/null
+++ b/users/wpcarro/OWNERS
@@ -0,0 +1 @@
+wpcarro
diff --git a/users/wpcarro/README.md b/users/wpcarro/README.md
new file mode 100644
index 0000000000..be0aacf247
--- /dev/null
+++ b/users/wpcarro/README.md
@@ -0,0 +1,47 @@
+# wpcarro
+
+Welcome to my monorepo.
+
+Herein you will find a variety of libraries, packages, and documents. Some of
+this work in finished and other work is incomplete or just a sketch for a
+future project.
+
+Where applicable, I try to include `README.md` files in some of the
+subdirectories to help orient both myself and any onlookers.
+
+## Sign posts
+
+Below I have outlined a few projects that you might find interesting.
+
+- `boilerplate`: scaffolding for projects. Boilerplate's goal is to reduce the
+  startup costs of a project.
+- `configs`: my dotfiles (e.g. `config.fish`, `init.vim`).
+- `emacs`: Emacs is both my preferred text editor and my window manager; with
+  tens of thousands of lines of Emacs Lisp, you can safely assume that this
+  directory hosts a lot of libraries and packages.
+- `monzo_ynab`: `systemd` timer unit that imports my Monzo (i.e. a U.K.-based
+  online bank) transactions into the personal finance tool YNAB (i.e.
+  youneedabudget.com).
+- `nixos`: my declarative configuration for my NixOS machines. If you are
+  unfamiliar with Nix, I recommend reading about the NixOS project.
+- `tools`: some scripts and projects that simplify my life.
+- `website`: everything required to build my website, https://wpcarro.dev.
+
+## Installation
+
+### Google Machine
+
+- ensure `/google-briefcase` exists
+- read `/google-briefcase/README.md`
+
+### NixOS Machine
+
+```shell
+$ nix-shell -p nixos.{git,direnv}
+$ git clone https://code.tvl.fyi/depot.git /depot
+$ cd /depot
+$ eval "$(direnv hook bash)"
+$ HOSTNAME=base rebuild-system
+$ sudo tailscale up
+$ git clone 'user@host:~/.passage' ~/.passage
+```
diff --git a/users/wpcarro/assessments/brilliant/.ghci b/users/wpcarro/assessments/brilliant/.ghci
new file mode 100644
index 0000000000..efc88e630c
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/.ghci
@@ -0,0 +1,2 @@
+:set prompt "> "
+:set -Wall
diff --git a/users/wpcarro/assessments/brilliant/App.hs b/users/wpcarro/assessments/brilliant/App.hs
new file mode 100644
index 0000000000..0272988f37
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/App.hs
@@ -0,0 +1,41 @@
+--------------------------------------------------------------------------------
+module App where
+--------------------------------------------------------------------------------
+import Keyboard (Keyboard(..))
+import Transforms (Transform(..))
+import Utils ((|>))
+
+import qualified Data.Char as Char
+import qualified Utils
+import qualified Data.List.Split as Split
+import qualified Keyboard
+import qualified Data.HashMap.Strict as HM
+--------------------------------------------------------------------------------
+
+transform :: Keyboard -> Transform -> Keyboard
+
+transform (Keyboard xs) xform =
+  case xform of
+    HorizontalFlip ->
+      xs
+      |> fmap reverse
+      |> Keyboard
+
+    VerticalFlip ->
+      xs
+      |> reverse
+      |> Keyboard
+
+    Shift n ->
+      xs
+      |> concat
+      |> Utils.rotate n
+      |> Split.chunksOf 10
+      |> Keyboard
+
+retypePassage :: String -> Keyboard -> Maybe String
+retypePassage passage newKeyboard =
+  passage
+  |> fmap Char.toUpper
+  |> traverse (\c -> HM.lookup c Keyboard.charToCoord)
+  >>= traverse (Keyboard.coordToChar newKeyboard)
diff --git a/users/wpcarro/assessments/brilliant/Keyboard.hs b/users/wpcarro/assessments/brilliant/Keyboard.hs
new file mode 100644
index 0000000000..13b5de0145
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/Keyboard.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+--------------------------------------------------------------------------------
+module Keyboard where
+--------------------------------------------------------------------------------
+import Utils
+import Data.Coerce
+import Data.Hashable (Hashable)
+import GHC.Generics (Generic)
+
+import qualified Data.List as List
+import qualified Data.HashMap.Strict as HM
+--------------------------------------------------------------------------------
+
+newtype Keyboard = Keyboard [[Char]]
+  deriving (Eq)
+
+instance Show Keyboard where
+  show (Keyboard xxs) =
+    xxs |> fmap printRow |> List.intercalate "\n"
+    where
+      printRow :: [Char] -> String
+      printRow xs =
+        xs |> fmap (\x -> '[':x:']':"") |> List.intercalate ""
+
+data Coord = Coord
+  { row :: Int
+  , col :: Int
+  } deriving (Eq, Show, Generic)
+
+instance Hashable Coord
+
+-- | List of characters to their QWERTY coordinatees.
+coords :: [(Char, Coord)]
+coords =
+  qwerty
+  |> coerce
+  |> fmap (zip [0..])
+  |> zip [0..]
+  |> fmap (\(row, xs) -> xs |> fmap (\(col, char) -> (char, Coord row col)))
+  |> mconcat
+
+-- | Mapping of characters to their coordinates on a QWERTY keyboard with the
+-- top-left corner as 0,0.
+charToCoord :: HM.HashMap Char Coord
+charToCoord = HM.fromList coords
+
+coordToChar :: Keyboard -> Coord -> Maybe Char
+coordToChar (Keyboard xxs) Coord{..} =
+  Just $ xxs !! row !! col
+
+qwerty :: Keyboard
+qwerty = Keyboard [ ['1','2','3','4','5','6','7','8','9','0']
+                  , ['Q','W','E','R','T','Y','U','I','O','P']
+                  , ['A','S','D','F','G','H','J','K','L',';']
+                  , ['Z','X','C','V','B','N','M',',','.','/']
+                  ]
diff --git a/users/wpcarro/assessments/brilliant/Main.hs b/users/wpcarro/assessments/brilliant/Main.hs
new file mode 100644
index 0000000000..e94c73bea2
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/Main.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE RecordWildCards #-}
+--------------------------------------------------------------------------------
+module Main where
+--------------------------------------------------------------------------------
+import Options.Applicative
+import Data.Semigroup ((<>))
+
+import qualified Transforms
+import qualified Keyboard
+import qualified App
+--------------------------------------------------------------------------------
+
+data CommandArgs = CommandArgs
+  { transforms :: String
+  , passage :: String
+  } deriving (Eq, Show)
+
+parseArgs :: Parser CommandArgs
+parseArgs =
+  CommandArgs <$> strOption
+                  ( long "transforms"
+                 <> short 't'
+                 <> help "String of transforms where (e.g. \"HHVS12VHVHS3\")" )
+              <*> strOption
+                  ( long "passage"
+                 <> short 'p'
+                 <> help "Input text to re-type" )
+
+main :: IO ()
+main = do
+  CommandArgs{..} <- execParser opts
+  case Transforms.fromString transforms of
+    Nothing -> putStrLn "You must provide valid input (e.g. \"HHVS12VHVHS3\")"
+    Just xs -> do
+      let keyboard = foldl App.transform Keyboard.qwerty (Transforms.optimize xs)
+      putStrLn $ "Typing: \"" ++ passage ++ "\"\nOn this keyboard:\n" ++ show keyboard
+      case App.retypePassage passage keyboard of
+        Nothing -> putStrLn $ "Looks like at least one of the characters in your input passage doesn't fit on our QWERTY keyboard: \n" ++ show Keyboard.qwerty
+        Just result -> putStrLn $ "Result: " ++ result
+  where
+    opts = info (parseArgs <**> helper)
+      ( fullDesc
+     <> progDesc "Transform a QWERTY keyboard using a string of commands")
diff --git a/users/wpcarro/assessments/brilliant/README.md b/users/wpcarro/assessments/brilliant/README.md
new file mode 100644
index 0000000000..60d7de4e25
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/README.md
@@ -0,0 +1,82 @@
+# Transform QWERTY
+
+Apply a series of transforms to a QWERTY keyboard then use the new keyboard to
+re-type a passage of text.
+
+## Environment
+
+You will need [Nix][nix] to build this program on your machine. The good news is
+that you won't need any Haskell-specific dependencies like `ghc`, `cabal`, or
+`stack`: just Nix.
+
+Once you have Nix installed, to build the program, run the following from this
+project's top-level directory:
+
+```shell
+$ nix-build
+```
+
+This should output an executable named `transform-keyboard` within a `result`
+directory:
+
+```shell
+$ tree result
+result
+โ””โ”€โ”€ transform-keyboard
+```
+
+### Testing
+
+To run the test suite, run the following from the project's top-level directory:
+
+```shell
+$ nix-shell
+$ runhaskell Spec.hs
+```
+
+[nix]: https://nixos.org/download.html
+
+## Usage
+
+Here are some `--help` and usage examples:
+
+```shell
+$ ./result/transform-keyboard --help
+Usage: transform-keyboard (-t|--transforms ARG) (-p|--passage ARG)
+  Transform a QWERTY keyboard using a string of commands
+
+Available options:
+  -t,--transforms ARG      String of transforms where (e.g. "HHVS12VHVHS3")
+  -p,--passage ARG         Input text to re-type
+  -h,--help                Show this help text
+```
+
+Now a working example:
+
+```shell
+$ ./result/transform-keyboard --transforms=HHVS12VHVHS3 --passage='Hello,Brilliant.'
+Typing: "Hello,Brilliant."
+On this keyboard:
+[H][J][K][L][;][Q][W][E][R][T]
+[Y][U][I][O][P][1][2][3][4][5]
+[6][7][8][9][0][Z][X][C][V][B]
+[N][M][,][.][/][A][S][D][F][G]
+Result: ZIVV4D/O3VV36APF
+```
+
+...and an example with an erroneous input (i.e. `!`):
+
+```shell
+$ ./result/transform-keyboard --transforms=HHVS12VHVHS3 --passage='Hello,Brilliant!'
+Typing: "Hello,Brilliant!"
+On this keyboard:
+[H][J][K][L][;][Q][W][E][R][T]
+[Y][U][I][O][P][1][2][3][4][5]
+[6][7][8][9][0][Z][X][C][V][B]
+[N][M][,][.][/][A][S][D][F][G]
+Looks like at least one of the characters in your input passage doesn't fit on our QWERTY keyboard:
+[1][2][3][4][5][6][7][8][9][0]
+[Q][W][E][R][T][Y][U][I][O][P]
+[A][S][D][F][G][H][J][K][L][;]
+[Z][X][C][V][B][N][M][,][.][/]
+```
diff --git a/users/wpcarro/assessments/brilliant/Spec.hs b/users/wpcarro/assessments/brilliant/Spec.hs
new file mode 100644
index 0000000000..e99e025641
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/Spec.hs
@@ -0,0 +1,103 @@
+--------------------------------------------------------------------------------
+module Spec where
+--------------------------------------------------------------------------------
+import Test.Hspec
+import Test.QuickCheck
+import Keyboard (Keyboard(..))
+import Transforms (Transform(..))
+import Data.Coerce
+import Utils
+
+import qualified App
+import qualified Keyboard
+import qualified Transforms
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = hspec $ do
+  describe "Keyboard.print" $ do
+    it "pretty-prints the keyboard" $ do
+      show Keyboard.qwerty == "[1][2][3][4][5][6][7][8][9][0]\n[Q][W][E][R][T][Y][U][I][O][P]\n[A][S][D][F][G][H][J][K][L][;]\n[Z][X][C][V][B][N][M][,][.][/]"
+
+  describe "Transforms.fromString" $ do
+    it "successfully parses a string of commands" $ do
+      Transforms.fromString "HHVS-12VHVHS3" ==
+        Just [ HorizontalFlip
+             , HorizontalFlip
+             , VerticalFlip
+             , Shift (-12)
+             , VerticalFlip
+             , HorizontalFlip
+             , VerticalFlip
+             , HorizontalFlip
+             , Shift 3
+             ]
+
+    it "returns Nothing when the input is invalid" $ do
+      Transforms.fromString "potato" == Nothing
+
+    it "return Nothing when the input is valid except for the end" $ do
+      Transforms.fromString "HVS10potato" == Nothing
+
+  describe "App.transform" $ do
+    it "flips any keyboard horizontally" $ do
+      property $ \first second third fourth ->
+        App.transform (Keyboard [first, second, third, fourth]) HorizontalFlip == do
+          Keyboard [ reverse first
+                   , reverse second
+                   , reverse third
+                   , reverse fourth
+                   ]
+
+    it "flips any keyboard vertically" $ do
+      property $ \first second third fourth ->
+        App.transform (Keyboard [first, second, third, fourth]) VerticalFlip == do
+          Keyboard $ reverse [first, second, third, fourth]
+
+    it "shifts any keyboard" $ do
+      property $ \first second third fourth n ->
+        App.transform (Keyboard [first, second, third, fourth]) (Shift n)
+        |> (coerce :: Keyboard -> [[Char]])
+        |> concat ==
+          [first, second, third, fourth]
+            |> concat
+            |> Utils.rotate n
+
+    it "flips a QWERTY keyboard horizontally" $ do
+      App.transform Keyboard.qwerty HorizontalFlip == do
+        Keyboard [ ['0','9','8','7','6','5','4','3','2','1']
+                 , ['P','O','I','U','Y','T','R','E','W','Q']
+                 , [';','L','K','J','H','G','F','D','S','A']
+                 , ['/','.',',','M','N','B','V','C','X','Z']
+                 ]
+
+    it "flips a keyboard vertically" $ do
+      App.transform Keyboard.qwerty VerticalFlip == do
+        Keyboard [ ['Z','X','C','V','B','N','M',',','.','/']
+                 , ['A','S','D','F','G','H','J','K','L',';']
+                 , ['Q','W','E','R','T','Y','U','I','O','P']
+                 , ['1','2','3','4','5','6','7','8','9','0']
+                 ]
+
+    it "shifts a keyboard left N times" $ do
+      App.transform Keyboard.qwerty (Shift 2) == do
+        Keyboard [ ['3','4','5','6','7','8','9','0','Q','W']
+                 , ['E','R','T','Y','U','I','O','P','A','S']
+                 , ['D','F','G','H','J','K','L',';','Z','X']
+                 , ['C','V','B','N','M',',','.','/','1','2']
+                 ]
+
+    it "shifts right negative amounts" $ do
+      App.transform Keyboard.qwerty (Shift (-3)) == do
+        Keyboard [ [',','.','/','1','2','3','4','5','6','7']
+                 , ['8','9','0','Q','W','E','R','T','Y','U']
+                 , ['I','O','P','A','S','D','F','G','H','J']
+                 , ['K','L',';','Z','X','C','V','B','N','M']
+                 ]
+
+  describe "Transforms.optimize" $ do
+    it "removes superfluous horizontal transformations" $ do
+      Transforms.optimize [HorizontalFlip, HorizontalFlip] == []
+
+    it "removes superfluous vertical transformations" $ do
+      Transforms.optimize [VerticalFlip, VerticalFlip] == []
diff --git a/users/wpcarro/assessments/brilliant/Transforms.hs b/users/wpcarro/assessments/brilliant/Transforms.hs
new file mode 100644
index 0000000000..d8df8f8372
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/Transforms.hs
@@ -0,0 +1,52 @@
+--------------------------------------------------------------------------------
+module Transforms where
+--------------------------------------------------------------------------------
+import Control.Applicative ((<|>))
+import Text.ParserCombinators.ReadP
+--------------------------------------------------------------------------------
+
+data Transform = VerticalFlip
+               | HorizontalFlip
+               | Shift Int
+               deriving (Eq, Show)
+
+digit :: ReadP Char
+digit =
+  satisfy (\c -> c >= '0' && c <= '9')
+
+command :: ReadP Transform
+command = vertical
+      <|> horizontal
+      <|> shift
+  where
+    vertical =
+      char 'V' >> pure VerticalFlip
+
+    horizontal =
+      char 'H' >> pure HorizontalFlip
+
+    shift = do
+      _ <- char 'S'
+      negative <- option Nothing $ fmap Just (satisfy (== '-'))
+      n <- read <$> many1 digit
+      case negative of
+        Nothing -> pure $ Shift n
+        Just _  -> pure $ Shift (-1 * n)
+
+-- | Attempt to remove redundant transformations.
+-- | Here are some rules that I'd like to support but may not have time for:
+-- | - All even-numbered flips (w/o intermittent shifts) can become zero
+-- | - All odd-numbered flips (w/o intermittent shifts) can become 1
+-- | - All shifts can be be reduce to the absolute value of shifts
+optimize :: [Transform] -> [Transform]
+optimize [] = []
+optimize [x] = [x]
+optimize (VerticalFlip:VerticalFlip:xs) = optimize xs
+optimize (HorizontalFlip:HorizontalFlip:xs) = optimize xs
+optimize xs = xs
+
+fromString :: String -> Maybe [Transform]
+fromString x =
+  case readP_to_S (manyTill command eof) x of
+   [(res, "")] -> Just res
+   _           -> Nothing
diff --git a/users/wpcarro/assessments/brilliant/Utils.hs b/users/wpcarro/assessments/brilliant/Utils.hs
new file mode 100644
index 0000000000..c69d00333b
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/Utils.hs
@@ -0,0 +1,13 @@
+--------------------------------------------------------------------------------
+module Utils where
+--------------------------------------------------------------------------------
+import Data.Function ((&))
+--------------------------------------------------------------------------------
+
+(|>) :: a -> (a -> b) -> b
+(|>) = (&)
+
+-- | Rotate `xs` as a cycle `n` times.
+rotate :: Int -> [a] -> [a]
+rotate n xs = take size . drop (n `mod` size) . cycle $ xs
+  where size = length xs
diff --git a/users/wpcarro/assessments/brilliant/default.nix b/users/wpcarro/assessments/brilliant/default.nix
new file mode 100644
index 0000000000..0628679c01
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/default.nix
@@ -0,0 +1,16 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.program {
+  name = "transform-keyboard";
+  srcs = builtins.path {
+    path = ./.;
+    name = "transform-keyboard-src";
+  };
+  deps = hpkgs: with hpkgs; [
+    optparse-applicative
+    unordered-containers
+    split
+    rio
+  ];
+  ghcExtensions = [ ];
+}
diff --git a/users/wpcarro/assessments/brilliant/shell.nix b/users/wpcarro/assessments/brilliant/shell.nix
new file mode 100644
index 0000000000..e08399c093
--- /dev/null
+++ b/users/wpcarro/assessments/brilliant/shell.nix
@@ -0,0 +1,12 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    (haskellPackages.ghcWithPackages (hpkgs: with hpkgs; [
+      hspec
+      optparse-applicative
+      unordered-containers
+      split
+    ]))
+  ];
+}
diff --git a/users/wpcarro/assessments/dotted-squares/.envrc b/users/wpcarro/assessments/dotted-squares/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/assessments/dotted-squares/.ghci b/users/wpcarro/assessments/dotted-squares/.ghci
new file mode 100644
index 0000000000..b100af4432
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/.ghci
@@ -0,0 +1 @@
+:set -Wall
diff --git a/users/wpcarro/assessments/dotted-squares/Main.hs b/users/wpcarro/assessments/dotted-squares/Main.hs
new file mode 100644
index 0000000000..44f91e2b23
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/Main.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE DeriveGeneric #-}
+--------------------------------------------------------------------------------
+module Main where
+--------------------------------------------------------------------------------
+import Data.Hashable
+import Data.Function ((&))
+import GHC.Generics
+import Text.ParserCombinators.ReadP
+import Control.Applicative
+
+import qualified Data.HashSet as HS
+--------------------------------------------------------------------------------
+
+data Direction
+  = DirLeft
+  | DirRight
+  | DirUp
+  | DirDown
+  deriving (Eq, Show)
+
+data Point = Point Int Int
+  deriving (Eq, Show, Ord, Generic)
+instance Hashable Point
+
+data Orientation
+  = Horizontal
+  | Vertical
+  deriving (Eq, Show)
+
+data Anchor
+  = Beg
+  | End
+  deriving (Eq, Show)
+
+data Rotation
+  = CW
+  | CCW
+  deriving (Eq, Show)
+
+data Line = Line Point Point
+  deriving (Show, Generic)
+instance Hashable Line
+
+instance Eq Line where
+  Line begA endA == Line begB endB =
+    (begA == begB && endA == endB) ||
+    (begA == endB && endA == begB)
+
+data Game = Game (HS.HashSet Line) [Line]
+  deriving (Eq, Show)
+
+data Scoreboard = Scoreboard Int Int
+  deriving (Eq)
+
+instance Semigroup Scoreboard where
+  (Scoreboard a b) <> (Scoreboard x y) =
+    Scoreboard (a + x) (b + y)
+
+instance Monoid Scoreboard where
+  mempty = Scoreboard 0 0
+
+data Turn
+  = Player1
+  | Player2
+  deriving (Eq, Show)
+
+next :: Turn -> Turn
+next Player1 = Player2
+next Player2 = Player1
+
+instance Show Scoreboard where
+  show (Scoreboard p1 p2) =
+    "Player 1: " ++ show (p1) ++ " Player 2: " ++ show (p2)
+
+digit :: ReadP Char
+digit = satisfy (\c -> c >= '0' && c <= '9')
+
+int :: ReadP Int
+int = read <$> many1 digit
+
+inputLine :: ReadP String
+inputLine = manyTill get (char '\n')
+
+direction :: ReadP Direction
+direction = do
+  c <- char 'L' <|> char 'R' <|> char 'U' <|> char 'D'
+  case c of
+    'L' -> pure DirLeft
+    'R' -> pure DirRight
+    'U' -> pure DirUp
+    'D' -> pure DirDown
+    _   -> fail $ "Unexpected direction: " ++ show c
+
+validMove :: Int -> Int -> ReadP Line
+validMove w h = do
+  x <- int
+  skipSpaces
+  y <- int
+  skipSpaces
+  dir <- direction
+  _ <- char '\n'
+  if x >= 0 && x <= w &&  y >= 0 && y <= h then do
+    let beg = Point x y
+    pure $ mkLine beg (shiftPoint dir beg)
+  else
+    fail "Expected a move on the game board"
+
+game :: ReadP Game
+game = do
+  w <- read <$> inputLine
+  h <- read <$> inputLine
+  locs <- read <$> inputLine
+  moves <- count locs (validMove w h)
+  eof
+  pure $ Game mempty moves
+
+parseInput :: String -> Maybe Game
+parseInput x = do
+  case readP_to_S game x of
+    [(res, "")] -> Just res
+    _ -> Nothing
+
+-- | Smart constructor to ensure that beg is always < end.
+mkLine :: Point -> Point -> Line
+mkLine beg end =
+  if beg < end then Line beg end else Line end beg
+
+mkLineDir :: Int -> Int -> Direction -> Line
+mkLineDir x y dir =
+  let beg = Point x y
+  in mkLine beg (shiftPoint dir beg)
+
+mkLineDir' :: Point -> Direction -> Line
+mkLineDir' (Point x y) dir = mkLineDir x y dir
+
+shiftPoint :: Direction -> Point -> Point
+shiftPoint DirLeft  (Point x y) = Point (x - 1) y
+shiftPoint DirRight (Point x y) = Point (x + 1) y
+shiftPoint DirUp    (Point x y) = Point x (y + 1)
+shiftPoint DirDown  (Point x y) = Point x (y - 1)
+
+shiftLine :: Direction -> Line -> Line
+shiftLine dir (Line beg end) =
+  mkLine (shiftPoint dir beg) (shiftPoint dir end)
+
+rotateLine :: Anchor -> Rotation -> Line -> Line
+rotateLine anchor rotation line =
+  doRotateLine (classifyOrientation line) anchor rotation line
+
+doRotateLine :: Orientation -> Anchor -> Rotation -> Line -> Line
+doRotateLine Horizontal Beg CW  (Line beg _) = mkLineDir' beg DirDown
+doRotateLine Horizontal Beg CCW (Line beg _) = mkLineDir' beg DirUp
+doRotateLine Horizontal End CW  (Line _ end) = mkLineDir' end DirUp
+doRotateLine Horizontal End CCW (Line _ end) = mkLineDir' end DirDown
+doRotateLine Vertical   Beg CW  (Line beg _) = mkLineDir' beg DirRight
+doRotateLine Vertical   Beg CCW (Line beg _) = mkLineDir' beg DirLeft
+doRotateLine Vertical   End CW  (Line _ end) = mkLineDir' end DirLeft
+doRotateLine Vertical   End CCW (Line _ end) = mkLineDir' end DirRight
+
+classifyOrientation :: Line -> Orientation
+classifyOrientation (Line (Point _ y1) (Point _ y2)) =
+  if y1 == y2 then Horizontal else Vertical
+
+closesAnySquare :: HS.HashSet Line -> Line -> Bool
+closesAnySquare allMoves line = do
+  let alreadyDrawn x = HS.member x allMoves
+  case classifyOrientation line of
+    Horizontal ->
+      all alreadyDrawn
+        [ shiftLine DirUp line
+        , rotateLine Beg CCW line
+        , rotateLine End CW line
+        ] ||
+      all alreadyDrawn
+        [ shiftLine DirDown line
+        , rotateLine Beg CW line
+        , rotateLine End CCW line
+        ]
+    Vertical ->
+      all alreadyDrawn
+        [ shiftLine DirLeft line
+        , rotateLine Beg CCW line
+        , rotateLine End CW line
+        ] ||
+      all alreadyDrawn
+        [ shiftLine DirRight line
+        , rotateLine Beg CW line
+        , rotateLine End CCW line
+        ]
+
+incScoreboard :: Turn -> Scoreboard -> Scoreboard
+incScoreboard Player1 score = score <> Scoreboard 1 0
+incScoreboard Player2 score = score <> Scoreboard 0 1
+
+scoreGame :: Turn -> Game -> Scoreboard -> Maybe Scoreboard
+scoreGame _ (Game _ []) score = Just $ score
+scoreGame player (Game allMoves (line:rest)) score =
+  if HS.member line allMoves then
+    Nothing
+  else do
+    let allMoves' = HS.insert line allMoves
+        score' = if closesAnySquare allMoves line then
+                   incScoreboard player score
+                 else score
+    scoreGame (next player) (Game allMoves' rest) score'
+
+(|>) :: a -> (a -> b) -> b
+(|>) = (&)
+
+main :: IO ()
+main = do
+  input <- readFile "game.txt"
+  case parseInput input of
+    Nothing -> putStrLn "invalid"
+    Just parsedGame ->
+      case scoreGame Player1 parsedGame mempty of
+        Nothing -> putStrLn "invalid"
+        Just score -> print score
diff --git a/users/wpcarro/assessments/dotted-squares/README.md b/users/wpcarro/assessments/dotted-squares/README.md
new file mode 100644
index 0000000000..3d13da1cb1
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/README.md
@@ -0,0 +1,21 @@
+# Dotted Squares
+
+This is my second attempt at solving this problem. I had an hour to solve it the
+first time, and I unfortunately came up short although I made good progress.
+
+The problem asks to read input from a text file that looks like this:
+
+```
+1     -- board width
+1     -- board height
+4     -- number of lines of "moves" (below)
+0 0 R -- create a unit vector (0,0) facing right
+0 0 U -- create a unit vector (0,0) facing up
+0 1 L -- create a unit vector (0,1) facing left
+1 1 D -- create a unit vector (1,1) facing down
+```
+
+After parsing and validating the input, score the outcome a game where players
+one and two alternatively take turns drawing lines on a board. Anytime one of
+the players draws a line that creates a square from existing lines, they get a
+point.
diff --git a/users/wpcarro/assessments/dotted-squares/Spec.hs b/users/wpcarro/assessments/dotted-squares/Spec.hs
new file mode 100644
index 0000000000..b5d604085b
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/Spec.hs
@@ -0,0 +1,80 @@
+--------------------------------------------------------------------------------
+module Spec where
+--------------------------------------------------------------------------------
+import Test.Hspec
+import Main hiding (main)
+import qualified Data.HashSet as HS
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = hspec $ do
+  describe "dotted-squares" $ do
+    describe "parseInput" $ do
+      it "works as expected" $ do
+        input <- readFile "input-a.txt"
+        parseInput input `shouldBe` Just (Game mempty [ mkLine (Point 0 0) (Point 1 0)
+                                                      , mkLine (Point 0 0) (Point 0 1)
+                                                      ])
+
+      it "fails when the game has too many user moves" $ do
+        input <- readFile "too-many-moves.txt"
+        parseInput input `shouldBe` Nothing
+
+      it "fails when the game has too few user moves" $ do
+        input <- readFile "too-few-moves.txt"
+        parseInput input `shouldBe` Nothing
+
+    describe "shiftLine" $ do
+      let horizontal = mkLineDir 1 1 DirRight
+          vertical   = mkLineDir 1 1 DirUp
+      it "can move a horizontal line up" $
+        shiftLine DirUp horizontal `shouldBe` mkLineDir 1 2 DirRight
+      it "can move a horizontal line down" $
+        shiftLine DirDown horizontal `shouldBe` mkLineDir 1 0 DirRight
+      it "can move a horizontal line left" $
+        shiftLine DirLeft horizontal `shouldBe` mkLineDir 0 1 DirRight
+      it "can move a horizontal line right" $
+        shiftLine DirRight horizontal `shouldBe` mkLineDir 2 1 DirRight
+      it "can move a vertical line up" $
+        shiftLine DirUp vertical `shouldBe` mkLineDir 1 2 DirUp
+      it "can move a vertical line down" $
+        shiftLine DirDown vertical `shouldBe` mkLineDir 1 0 DirUp
+      it "can move a vertical line left" $
+        shiftLine DirLeft vertical `shouldBe` mkLineDir 0 1 DirUp
+      it "can move a vertical line right" $
+        shiftLine DirRight vertical `shouldBe` mkLineDir 2 1 DirUp
+
+    describe "rotateLine" $ do
+      let horizontal = mkLineDir 1 1 DirRight -- 1,1;2,1
+          vertical   = mkLineDir 1 1 DirUp    -- 1,1;1,2
+      it "can rotate a horizontal line CW anchored at its beginning" $
+        rotateLine Beg CW horizontal `shouldBe` mkLineDir 1 1 DirDown
+      it "can rotate a horizontal line CCW anchored at its beginning" $
+        rotateLine Beg CCW horizontal `shouldBe` mkLineDir 1 1 DirUp
+      it "can rotate a horizontal line CW anchored at its end" $
+        rotateLine End CW horizontal `shouldBe` mkLineDir 2 1 DirUp
+      it "can rotate a horizontal line CCW anchored at its end" $
+        rotateLine End CCW horizontal `shouldBe` mkLineDir 2 1 DirDown
+
+      it "can rotate a vertical line CW anchored at its beginning" $
+        rotateLine Beg CW vertical `shouldBe` mkLineDir 1 1 DirRight
+      it "can rotate a vertical line CCW anchored at its beginning" $
+        rotateLine Beg CCW vertical `shouldBe` mkLineDir 1 1 DirLeft
+      it "can rotate a vertical line CW anchored at its end" $
+        rotateLine End CW vertical `shouldBe` mkLineDir 1 2 DirLeft
+      it "can rotate a vertical line CCW anchored at its end" $
+        rotateLine End CCW vertical `shouldBe` mkLineDir 1 2 DirRight
+
+    describe "closesAnySquare" $ do
+      let threeSides = [ (0, 0, DirRight)
+                       , (0, 0, DirUp)
+                       , (0, 1, DirRight)
+                       ]
+                       |> fmap (\(x, y, dir) -> mkLineDir x y dir)
+                       |> HS.fromList
+      it "returns true the line we supply makes a square" $
+        closesAnySquare threeSides (mkLineDir 1 1 DirDown) `shouldBe` True
+      it "returns false the line we supply doesn't make a square" $
+        closesAnySquare threeSides (mkLineDir 1 1 DirUp) `shouldBe` False
+      it "returns false when we have no existing lines" $
+        closesAnySquare mempty (mkLineDir 1 1 DirUp) `shouldBe` False
diff --git a/users/wpcarro/assessments/dotted-squares/colliding-moves.txt b/users/wpcarro/assessments/dotted-squares/colliding-moves.txt
new file mode 100644
index 0000000000..a831fa95c0
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/colliding-moves.txt
@@ -0,0 +1,7 @@
+1
+1
+4
+0 0 R
+0 0 R
+0 1 R
+0 1 R
diff --git a/users/wpcarro/assessments/dotted-squares/game.txt b/users/wpcarro/assessments/dotted-squares/game.txt
new file mode 100644
index 0000000000..0af71d1f5b
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/game.txt
@@ -0,0 +1,7 @@
+1
+1
+4
+0 0 R
+0 0 U
+0 1 R
+1 1 D
diff --git a/users/wpcarro/assessments/dotted-squares/input-a.txt b/users/wpcarro/assessments/dotted-squares/input-a.txt
new file mode 100644
index 0000000000..b9e871eced
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/input-a.txt
@@ -0,0 +1,5 @@
+1
+1
+2
+0 0 R
+0 0 U
diff --git a/users/wpcarro/assessments/dotted-squares/shell.nix b/users/wpcarro/assessments/dotted-squares/shell.nix
new file mode 100644
index 0000000000..868668ca50
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/shell.nix
@@ -0,0 +1,8 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.shell {
+  deps = hpkgs: with hpkgs; [
+    hspec
+    unordered-containers
+  ];
+}
diff --git a/users/wpcarro/assessments/dotted-squares/too-few-moves.txt b/users/wpcarro/assessments/dotted-squares/too-few-moves.txt
new file mode 100644
index 0000000000..d684679d26
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/too-few-moves.txt
@@ -0,0 +1,6 @@
+1
+1
+4
+0 0 R
+0 0 U
+0 1 R
diff --git a/users/wpcarro/assessments/dotted-squares/too-many-moves.txt b/users/wpcarro/assessments/dotted-squares/too-many-moves.txt
new file mode 100644
index 0000000000..bfcced43b9
--- /dev/null
+++ b/users/wpcarro/assessments/dotted-squares/too-many-moves.txt
@@ -0,0 +1,7 @@
+1
+1
+3
+0 0 R
+0 0 U
+0 1 R
+1 1 D
diff --git a/users/wpcarro/assessments/ramp/solution-emacs-elixir-format.py b/users/wpcarro/assessments/ramp/solution-emacs-elixir-format.py
new file mode 100644
index 0000000000..d0d9484020
--- /dev/null
+++ b/users/wpcarro/assessments/ramp/solution-emacs-elixir-format.py
@@ -0,0 +1,29 @@
+# The file '2010.census.txt' contains summary statistics from the 2010 United
+# States census including household income. The data is in an unspecified
+# format.
+
+# Find the average of the column called:
+
+#     'MEDIAN HOUSEHOLD INCOME'
+
+# Ideally the solution should be a command line script, of the form:
+
+#     $ ./solution [options] [file...]
+
+# The solution may be written in any language, Python is preferred but not
+# required.
+
+# Google, stack overflow, etc. usage is allowed.
+
+import requests
+
+url = "https://assets.tryramp.com/interview/census/2010.census.txt"
+
+def main():
+    res = requests.get(url)
+    if res.status not in {200}:
+        raise Exception("Unexpected status code: {}".format(res.status_code))
+    # download the content
+    # parse row
+    # select 'MEDIAN HOUSEHOLD INCOME' column
+    pass
diff --git a/users/wpcarro/assessments/ramp/solution.py b/users/wpcarro/assessments/ramp/solution.py
new file mode 100644
index 0000000000..28060bfb3c
--- /dev/null
+++ b/users/wpcarro/assessments/ramp/solution.py
@@ -0,0 +1,87 @@
+# The file '2010.census.txt' contains summary statistics from the 2010 United
+# States census including household income. The data is in an unspecified
+# format.
+
+# Find the average of the column called:
+
+#     'MEDIAN HOUSEHOLD INCOME'
+
+# Ideally the solution should be a command line script, of the form:
+
+#     $ ./solution [options] [file...]
+
+# The solution may be written in any language, Python is preferred but not
+# required.
+
+# Google, stack overflow, etc. usage is allowed.
+
+import requests
+import csv
+
+url = "https://assets.tryramp.com/interview/census/2010.census.txt"
+column = 'MEDIAN HOUSEHOLD INCOME'
+columns = [
+    'CENSUS YEAR',
+    'TRACT',
+    'BLOCK GROUP',
+    'FIPS ID',
+    'TOTAL POPULATION',
+    'POPULATION WHITE',
+    'POPULATION BLACK',
+    'POPULATION ASIAN',
+    'POPULATION OTHER',
+    'POPULATION AMERICAN INDIAN',
+    'POPULATION PACIFIC ISLANDER',
+    'POPULATION ONE RACE',
+    'POPULATION MULTI RACE',
+    'POPULATION 25 OLDER',
+    'MEDIAN AGE',
+    'MEDIAN HOUSEHOLD INCOME',
+    'HIGH SCHOOL MALE',
+    'HIGH SCHOOL MORE MALE',
+    'COLLEGE 1 YR LESS MALE',
+    'COLLEGE 1 YR MORE MALE',
+    'ASSOCIATES DEGREE MALE',
+    'BACHELORS DEGREE MALE',
+    'MASTERS DEGREE MALE',
+    'PROFESSIONAL DEGREE MALE',
+    'DOCTORAL DEGREE MALE',
+    'HIGH SCHOOL FEMALE',
+    'HIGH SCHOOL MORE FEMALE',
+    'COLLEGE 1 YR LESS FEMALE',
+    'COLLEGE 1 YR MORE FEMALE',
+    'ASSOCIATES DEGREE FEMALE',
+    'BACHELORS DEGREE FEMALE',
+    'MASTERS DEGREE FEMALE',
+    'PROFESSIONAL DEGREE FEMALE',
+    'DOCTORAL DEGREE FEMALE',
+    'PERCENT 25 YR OVER HIGH SCHOOL MORE',
+    'HOUSING UNITS',
+    'OCCUPIED HOUSING UNITS',
+    'OWNER OCCUPIED HOUSING',
+    'RENTER OCCUPIED HOUSING',
+    'PERCENT OWNER OCCUPIED',
+    'PERCENT RENTER OCCUPIED',
+    'MEDIAN HOUSE VALUE OWNER OCCUPIED',
+    'MEDIAN YEAR BUILT',
+    'VACANCY RATES',
+]
+
+
+def average(xs):
+    return sum(xs) / len(xs)
+
+
+def parse_body(body):
+    return list(csv.DictReader(body.split('\n')[1:], delimiter='|', fieldnames=columns))
+
+
+def main():
+    res = requests.get(url)
+    if res.status_code not in {200}:
+        raise Exception("Unexpected status code: {}".format(res.status_code))
+    return average([int(d.get(column))
+                    for d in parse_body(res.text)
+                    if int(d.get(column)) >= 0])
+
+print(main())
diff --git a/users/wpcarro/assessments/semiprimes/.gitignore b/users/wpcarro/assessments/semiprimes/.gitignore
new file mode 100644
index 0000000000..b5b25bd648
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/.gitignore
@@ -0,0 +1 @@
+default.nix
diff --git a/users/wpcarro/assessments/semiprimes/README.md b/users/wpcarro/assessments/semiprimes/README.md
new file mode 100644
index 0000000000..7d5a15482a
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/README.md
@@ -0,0 +1,44 @@
+# Semiprimes Service
+
+## Introduction
+
+A **composite** is a number containing at least two prime factors. For example:
+
+```
+15 = 3 ร— 5
+9 = 3 ร— 3
+12 = 2 ร— 2 ร— 3
+```
+
+There are ten composites below thirty containing precisely two, not necessarily
+distinct, prime factors: `4, 6, 9, 10, 14, 15, 21, 22, 25, 26`. Letโ€™s call such
+numbers *Semiprimes*.
+
+## Task
+
+- Write a module which provides a function to tell whether a given number, `N`,
+  is a semiprime. `N` will be less than 100,000
+- Please implement an API (RESTful or GraphQL) to factor a given number into two
+  prime numbers if itโ€™s a semiprime, otherwise, return an error message.
+
+## Stretch Goals
+
+- Handle the invalid inputs.
+- Support batch requests: i.e. users could provide 100 numbers, and the API
+  return the answer for all.
+- Considering this module will be used by a long running service, could you
+  optimize it to give answers faster?
+
+## Usage
+
+To run the application you'll need to have `elixir` installed. Assuming `elixir`
+is already installed, consult the following steps to start the application:
+
+```shell
+$ cd server
+$ mix deps.get
+$ iex -S mix
+```
+
+Now open a web browser and visit `http://localhost:8080`!
+
diff --git a/users/wpcarro/assessments/semiprimes/server/.formatter.exs b/users/wpcarro/assessments/semiprimes/server/.formatter.exs
new file mode 100644
index 0000000000..d2cda26edd
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/.formatter.exs
@@ -0,0 +1,4 @@
+# Used by "mix format"
+[
+  inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
+]
diff --git a/users/wpcarro/assessments/semiprimes/server/.gitignore b/users/wpcarro/assessments/semiprimes/server/.gitignore
new file mode 100644
index 0000000000..db9704a85f
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/.gitignore
@@ -0,0 +1,24 @@
+# The directory Mix will write compiled artifacts to.
+/_build/
+
+# If you run "mix test --cover", coverage assets end up here.
+/cover/
+
+# The directory Mix downloads your dependencies sources to.
+/deps/
+
+# Where third-party dependencies like ExDoc output generated docs.
+/doc/
+
+# Ignore .fetch files in case you like to edit your project deps locally.
+/.fetch
+
+# If the VM crashes, it generates a dump, let's ignore it too.
+erl_crash.dump
+
+# Also ignore archive artifacts (built via "mix archive.build").
+*.ez
+
+# Ignore package tarball (built via "mix hex.build").
+server-*.tar
+
diff --git a/users/wpcarro/assessments/semiprimes/server/lib/app.ex b/users/wpcarro/assessments/semiprimes/server/lib/app.ex
new file mode 100644
index 0000000000..7a6fa5ea24
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/lib/app.ex
@@ -0,0 +1,8 @@
+defmodule App do
+  use Application
+
+  @impl true
+  def start(_type, _args) do
+    Sup.start_link()
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/lib/cache.ex b/users/wpcarro/assessments/semiprimes/server/lib/cache.ex
new file mode 100644
index 0000000000..cd064cc1ae
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/lib/cache.ex
@@ -0,0 +1,41 @@
+defmodule Cache do
+  @moduledoc """
+  Cache is an in-memory key-value store.
+  """
+  use Agent
+
+  @doc """
+  Inititalize the key-value store.
+  """
+  def start_link(_) do
+    Agent.start_link(fn -> %{} end, name: __MODULE__)
+  end
+
+  @doc """
+  Attempt to return the value stored at `key`
+  """
+  def get(key) do
+    Agent.get(__MODULE__, &Map.get(&1, key))
+  end
+
+  @doc """
+  Write the `value` under the `key`. Last writer wins.
+  """
+  def put(key, value) do
+    Agent.update(__MODULE__, &Map.put(&1, key, value))
+  end
+
+  @doc """
+  List the contents of the cache. Useful for debugging purposes.
+  """
+  def list() do
+    Agent.get(__MODULE__, & &1)
+  end
+
+  @doc """
+  Invalidate the entire cache.
+  """
+  def clear() do
+    Agent.update(__MODULE__, fn _ -> %{} end)
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/lib/extras.ex b/users/wpcarro/assessments/semiprimes/server/lib/extras.ex
new file mode 100644
index 0000000000..f0c2ea4b9e
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/lib/extras.ex
@@ -0,0 +1,22 @@
+defmodule Extras do
+  @moduledoc """
+  Hosts utility functions intended to supplement the standard library.
+  """
+
+  @doc """
+  Return an ascending range starting at `a` and ending at `b` (exclusive).
+
+  ## Examples
+
+      iex> Extras.range(2, 5)
+      [2, 3, 4]
+
+  """
+  def range(a, b) do
+    if b <= a do
+      []
+    else
+      [a] ++ range(a + 1, b)
+    end
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/lib/math.ex b/users/wpcarro/assessments/semiprimes/server/lib/math.ex
new file mode 100644
index 0000000000..8a33be4753
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/lib/math.ex
@@ -0,0 +1,26 @@
+defmodule Math do
+  @moduledoc """
+  Math utilities.
+  """
+  alias Extras
+
+  @doc """
+  Returns the prime factors for `n`.
+
+  ## Examples
+
+      iex> Math.factor(15)
+      [3, 5]
+
+  """
+  def factor(1), do: []
+
+  def factor(n) do
+    Extras.range(2, n - 1)
+    |> Enum.find(&(rem(n, &1) == 0))
+    |> case do
+      nil -> [n]
+      x -> [x | factor(div(n, x))]
+    end
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/lib/router.ex b/users/wpcarro/assessments/semiprimes/server/lib/router.ex
new file mode 100644
index 0000000000..cb55520920
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/lib/router.ex
@@ -0,0 +1,86 @@
+defmodule Router do
+  use Plug.Router
+  use Plug.Debugger
+  require Logger
+
+  plug(Plug.Logger, log: :debug)
+  plug(Plug.Parsers, parsers: [:urlencoded])
+  plug(:match)
+  plug(:dispatch)
+
+  @usage """
+  Usage: Try querying some of the following endpoints...
+    GET /
+    GET /help
+    GET /semiprime?number=<integer>
+    GET /semiprimes?numbers=<comma-separated-integers>
+  """
+
+  get "/" do
+    send_resp(conn, 200, "Welcome to Semiprimes Service!\n\n#{@usage}")
+  end
+
+  get "/help" do
+    send_resp(conn, 200, @usage)
+  end
+
+  get "/semiprime" do
+    case conn |> Map.get(:query_params) |> Map.get("number") do
+      nil ->
+        send_resp(conn, 400, "You must pass an integer as a query parameter. #{@usage}")
+
+      val ->
+        case Integer.parse(val) do
+          {n, ""} ->
+            send_resp(conn, 200, semiprime_response(n))
+
+          _ ->
+            send_resp(conn, 400, "We could not parse the number you provided.\n\n#{@usage}")
+        end
+    end
+  end
+
+  get "/semiprimes" do
+    case conn |> Map.get(:query_params) |> Map.get("numbers") do
+      nil ->
+        send_resp(
+          conn,
+          400,
+          "You must pass a comma-separated list of integers as a query parameter.\n\n#{@usage}"
+        )
+
+      xs ->
+        response =
+          xs
+          |> String.split(",")
+          |> Stream.map(&Integer.parse/1)
+          |> Stream.filter(fn
+            {n, ""} -> true
+            _ -> false
+          end)
+          |> Stream.map(fn {n, ""} -> semiprime_response(n) end)
+          |> Enum.join("\n")
+
+        send_resp(conn, 200, response)
+    end
+  end
+
+  match _ do
+    send_resp(conn, 404, "Not found.")
+  end
+
+  ################################################################################
+  # Utils
+  ################################################################################
+
+  defp semiprime_response(n) do
+    case Server.semiprime(n) do
+      nil ->
+        "#{n} is not a semiprime. Try another number!"
+
+      {hit_or_miss, factors} ->
+        response = "#{n} is a semiprime! Its factors are #{Enum.join(factors, " and ")}."
+        "Cache #{Atom.to_string(hit_or_miss)} - #{response}"
+    end
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/lib/server.ex b/users/wpcarro/assessments/semiprimes/server/lib/server.ex
new file mode 100644
index 0000000000..7ab5e905b5
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/lib/server.ex
@@ -0,0 +1,33 @@
+defmodule Server do
+  @moduledoc """
+  Documentation for `Server`.
+  """
+
+  @doc """
+  If `n` contains exactly two prime factors, return those prime factors;
+  otherwise, return nothing.
+  """
+  def semiprime(n) do
+    case Cache.get(n) do
+      nil ->
+        case do_semiprime(n) do
+          nil ->
+            nil
+
+          res ->
+            Cache.put(n, res)
+            {:miss, res}
+        end
+
+      hit ->
+        {:hit, hit}
+    end
+  end
+
+  defp do_semiprime(n) do
+    case Math.factor(n) do
+      [_, _] = res -> res
+      _ -> nil
+    end
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/lib/sup.ex b/users/wpcarro/assessments/semiprimes/server/lib/sup.ex
new file mode 100644
index 0000000000..13a6ab374f
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/lib/sup.ex
@@ -0,0 +1,23 @@
+defmodule Sup do
+  @moduledoc """
+  Top-level supervisor for our OTP application. For now, this supervisor starts
+  and monitors our cache.
+  """
+
+  use Supervisor
+  alias Plug.Adapters.Cowboy
+
+  def start_link(opts \\ []) do
+    Supervisor.start_link(__MODULE__, :ok, opts)
+  end
+
+  @impl true
+  def init(:ok) do
+    children = [
+      Cache,
+      Cowboy.child_spec(scheme: :http, plug: Router, options: [port: 8000])
+    ]
+
+    Supervisor.init(children, strategy: :one_for_one)
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/mix.exs b/users/wpcarro/assessments/semiprimes/server/mix.exs
new file mode 100644
index 0000000000..9062f927e7
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/mix.exs
@@ -0,0 +1,32 @@
+defmodule Server.MixProject do
+  use Mix.Project
+
+  def project do
+    [
+      app: :server,
+      version: "0.1.0",
+      elixir: "~> 1.10",
+      start_permanent: Mix.env() == :prod,
+      deps: deps()
+    ]
+  end
+
+  # Run "mix help compile.app" to learn about applications.
+  def application do
+    [
+      extra_applications: [:logger],
+      mod: {App, []}
+    ]
+  end
+
+  # Run "mix help deps" to learn about dependencies.
+  defp deps do
+    [
+      {:cortex, "~> 0.1", only: [:dev, :test]},
+      {:plug_cowboy, "~> 2.4.1"},
+      {:cowboy, "~> 2.8.0"},
+      {:plug, "~> 1.11.0"},
+      {:poison, "~> 4.0.1"}
+    ]
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/mix.lock b/users/wpcarro/assessments/semiprimes/server/mix.lock
new file mode 100644
index 0000000000..2ae7efbb3f
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/mix.lock
@@ -0,0 +1,14 @@
+%{
+  "cortex": {:hex, :cortex, "0.6.0", "8094830fae266eb0ae34d1a58983c0c49484341f5044fb4dfb81746647bd2993", [:mix], [{:file_system, "~> 0.2", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "d0ef5a2b1269626149118684dc4ea77dbfbc67017f4b4065b71dcefa26cfcc49"},
+  "cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
+  "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"},
+  "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
+  "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
+  "mime": {:hex, :mime, "1.5.0", "203ef35ef3389aae6d361918bf3f952fa17a09e8e43b5aa592b93eba05d0fb8d", [:mix], [], "hexpm", "55a94c0f552249fc1a3dd9cd2d3ab9de9d3c89b559c2bd01121f824834f24746"},
+  "plug": {:hex, :plug, "1.11.0", "f17217525597628298998bc3baed9f8ea1fa3f1160aa9871aee6df47a6e4d38e", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2d9c633f0499f9dc5c2fd069161af4e2e7756890b81adcbb2ceaa074e8308876"},
+  "plug_cowboy": {:hex, :plug_cowboy, "2.4.1", "779ba386c0915027f22e14a48919a9545714f849505fa15af2631a0d298abf0f", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d72113b6dff7b37a7d9b2a5b68892808e3a9a752f2bf7e503240945385b70507"},
+  "plug_crypto": {:hex, :plug_crypto, "1.2.0", "1cb20793aa63a6c619dd18bb33d7a3aa94818e5fd39ad357051a67f26dfa2df6", [:mix], [], "hexpm", "a48b538ae8bf381ffac344520755f3007cc10bd8e90b240af98ea29b69683fc2"},
+  "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"},
+  "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
+  "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
+}
diff --git a/users/wpcarro/assessments/semiprimes/server/test/extras_test.exs b/users/wpcarro/assessments/semiprimes/server/test/extras_test.exs
new file mode 100644
index 0000000000..67d0b8875c
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/test/extras_test.exs
@@ -0,0 +1,18 @@
+defmodule ExtrasTest do
+  use ExUnit.Case
+  doctest Extras
+
+  describe "range" do
+    test "returns an empty list for descending sequences" do
+      assert Extras.range(0, -2) == []
+    end
+
+    test "returns an empty list for non-ascending sequences" do
+      assert Extras.range(8, 8) == []
+    end
+
+    test "returns an exclusive range" do
+      assert Extras.range(3, 6) == [3, 4, 5]
+    end
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/test/math_test.exs b/users/wpcarro/assessments/semiprimes/server/test/math_test.exs
new file mode 100644
index 0000000000..c7186c824a
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/test/math_test.exs
@@ -0,0 +1,30 @@
+defmodule MathTest do
+  use ExUnit.Case
+  doctest Math
+
+  describe "factor" do
+    test "returns the prime factors for an input" do
+      [
+        {15, [3, 5]},
+        {12, [2, 2, 3]},
+        {9, [3, 3]},
+        {21, [3, 7]}
+      ]
+      |> Enum.map(fn {input, expected} ->
+        assert Math.factor(input) == expected
+      end)
+    end
+
+    test "handles large numbers" do
+      assert Math.factor(104_023) == [17, 29, 211]
+    end
+
+    test "returns an empty list for 1" do
+      assert Math.factor(1) == []
+    end
+
+    test "returns the prime number itself when the input is prime" do
+      assert Math.factor(7) == [7]
+    end
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/test/server_test.exs b/users/wpcarro/assessments/semiprimes/server/test/server_test.exs
new file mode 100644
index 0000000000..08d559734b
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/test/server_test.exs
@@ -0,0 +1,34 @@
+defmodule ServerTest do
+  use ExUnit.Case
+  doctest Server
+
+  describe "semiprime" do
+    test "returns the factors when the number is semiprime" do
+      Cache.clear()
+      # Semiprimes below 30
+      [
+        {4, [2, 2]},
+        {6, [2, 3]},
+        {9, [3, 3]},
+        {10, [2, 5]},
+        {14, [2, 7]},
+        {15, [3, 5]},
+        {21, [3, 7]},
+        {22, [2, 11]},
+        {25, [5, 5]},
+        {26, [2, 13]}
+      ]
+      |> Enum.each(fn {input, expected} ->
+        assert Server.semiprime(input) == {:miss, expected}
+      end)
+    end
+
+    test "returns nothing when the number is a composite number" do
+      # Composite numbers below 30
+      [1, 2, 3, 5, 7, 8, 11, 12, 13, 16, 17, 18, 19, 20, 23, 24, 27, 28, 29]
+      |> Enum.each(fn x ->
+        assert Server.semiprime(x) == nil
+      end)
+    end
+  end
+end
diff --git a/users/wpcarro/assessments/semiprimes/server/test/test_helper.exs b/users/wpcarro/assessments/semiprimes/server/test/test_helper.exs
new file mode 100644
index 0000000000..869559e709
--- /dev/null
+++ b/users/wpcarro/assessments/semiprimes/server/test/test_helper.exs
@@ -0,0 +1 @@
+ExUnit.start()
diff --git a/users/wpcarro/assessments/tt/.gitignore b/users/wpcarro/assessments/tt/.gitignore
new file mode 100644
index 0000000000..d4d62d436b
--- /dev/null
+++ b/users/wpcarro/assessments/tt/.gitignore
@@ -0,0 +1,6 @@
+.envrc
+*.db
+*.sqlite3
+!populate.sqlite3
+*.db-shm
+*.db-wal
\ No newline at end of file
diff --git a/users/wpcarro/assessments/tt/README.md b/users/wpcarro/assessments/tt/README.md
new file mode 100644
index 0000000000..0231ef3ab8
--- /dev/null
+++ b/users/wpcarro/assessments/tt/README.md
@@ -0,0 +1,50 @@
+# TT
+
+All of the commands defined herein should be run from the top-level directory of
+this repository (i.e. the directory in which this file exists).
+
+## Server
+
+To create the environment that contains all of this application's dependencies,
+run:
+
+```shell
+$ nix-shell
+```
+
+To run the server interactively, run:
+
+```shell
+$ cd src/
+$ ghci
+```
+
+Now compile and load the server with:
+
+```
+Prelude> :l Main.hs
+*Main> main
+```
+
+## Database
+
+Create a new database named `db.sqlite3` with:
+
+```shell
+$ sqlite3 db.sqlite3
+```
+
+Populate the database with:
+
+```
+sqlite3> .read populate.sqlite3
+```
+
+You can verify that everything is setup with:
+
+```
+sqlite3> .tables
+sqlite3> .schema
+sqlite3> SELECT * FROM Accounts;
+sqlite3> SELECT * FROM Trips;
+```
diff --git a/users/wpcarro/assessments/tt/client/.gitignore b/users/wpcarro/assessments/tt/client/.gitignore
new file mode 100644
index 0000000000..1cb4f3034c
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/.gitignore
@@ -0,0 +1,3 @@
+/elm-stuff
+/Main.min.js
+/output.css
diff --git a/users/wpcarro/assessments/tt/client/README.md b/users/wpcarro/assessments/tt/client/README.md
new file mode 100644
index 0000000000..04804ad94f
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/README.md
@@ -0,0 +1,18 @@
+# Elm
+
+Elm has one of the best developer experiences that I'm aware of. The error
+messages are helpful and the entire experience is optimized to improve the ease
+of writing web applications.
+
+## Developing
+
+If you're interested in contributing, the following will create an environment
+in which you can develop:
+
+```shell
+$ nix-shell
+$ npx tailwindcss build index.css -o output.css
+$ elm-live -- src/Main.elm --output=Main.min.js
+```
+
+You can now view your web client at `http://localhost:8000`!
diff --git a/users/wpcarro/assessments/tt/client/elm.json b/users/wpcarro/assessments/tt/client/elm.json
new file mode 100644
index 0000000000..c4095e118e
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/elm.json
@@ -0,0 +1,40 @@
+{
+    "type": "application",
+    "source-directories": [
+        "src"
+    ],
+    "elm-version": "0.19.1",
+    "dependencies": {
+        "direct": {
+            "CurrySoftware/elm-datepicker": "4.0.0",
+            "elm/browser": "1.0.2",
+            "elm/core": "1.0.5",
+            "elm/html": "1.0.0",
+            "elm/http": "2.0.0",
+            "elm/json": "1.1.3",
+            "elm/random": "1.0.0",
+            "elm/svg": "1.0.1",
+            "elm/time": "1.0.0",
+            "elm/url": "1.0.0",
+            "elm-community/json-extra": "4.2.0",
+            "elm-community/list-extra": "8.2.3",
+            "elm-community/maybe-extra": "5.2.0",
+            "elm-community/random-extra": "3.1.0",
+            "justinmimbs/date": "3.2.1",
+            "krisajenkins/remotedata": "6.0.1",
+            "ryannhg/date-format": "2.3.0"
+        },
+        "indirect": {
+            "elm/bytes": "1.0.8",
+            "elm/file": "1.0.5",
+            "elm/parser": "1.1.0",
+            "elm/virtual-dom": "1.0.2",
+            "owanturist/elm-union-find": "1.0.0",
+            "rtfeldman/elm-iso8601-date-strings": "1.1.3"
+        }
+    },
+    "test-dependencies": {
+        "direct": {},
+        "indirect": {}
+    }
+}
diff --git a/users/wpcarro/assessments/tt/client/index.css b/users/wpcarro/assessments/tt/client/index.css
new file mode 100644
index 0000000000..52114e0e9f
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/index.css
@@ -0,0 +1,142 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+.elm-datepicker--container {
+  position: relative;
+}
+
+.elm-datepicker--input:focus {
+  outline: 0;
+}
+
+.elm-datepicker--picker {
+  position: absolute;
+  border: 1px solid #CCC;
+  z-index: 10;
+  background-color: white;
+}
+
+.elm-datepicker--picker-header,
+.elm-datepicker--weekdays {
+  background: #F2F2F2;
+}
+
+.elm-datepicker--picker-header {
+  display: flex;
+  align-items: center;
+}
+
+.elm-datepicker--prev-container,
+.elm-datepicker--next-container {
+  flex: 0 1 auto;
+  cursor: pointer;
+}
+
+.elm-datepicker--month-container {
+  flex: 1 1 auto;
+  padding: 0.5em;
+  display: flex;
+  flex-direction: column;
+}
+
+.elm-datepicker--month,
+.elm-datepicker--year {
+  flex: 1 1 auto;
+  cursor: default;
+  text-align: center;
+}
+
+.elm-datepicker--year {
+  font-size: 0.6em;
+  font-weight: 700;
+}
+
+.elm-datepicker--prev,
+.elm-datepicker--next {
+  border: 6px solid transparent;
+  background-color: inherit;
+  display: block;
+  width: 0;
+  height: 0;
+  padding: 0 0.2em;
+}
+
+.elm-datepicker--prev {
+  border-right-color: #AAA;
+}
+
+.elm-datepicker--prev:hover {
+  border-right-color: #BBB;
+}
+
+.elm-datepicker--next {
+  border-left-color: #AAA;
+}
+
+.elm-datepicker--next:hover {
+  border-left-color: #BBB;
+}
+
+.elm-datepicker--table {
+  border-spacing: 0;
+  border-collapse: collapse;
+  font-size: 0.8em;
+}
+
+.elm-datepicker--table td {
+  width: 2em;
+  height: 2em;
+  text-align: center;
+}
+
+.elm-datepicker--row {
+  border-top: 1px solid #F2F2F2;
+}
+
+.elm-datepicker--dow {
+  border-bottom: 1px solid #CCC;
+  cursor: default;
+}
+
+.elm-datepicker--day {
+  cursor: pointer;
+}
+
+.elm-datepicker--day:hover {
+  background: #F2F2F2;
+}
+
+.elm-datepicker--disabled {
+  cursor: default;
+  color: #DDD;
+}
+
+.elm-datepicker--disabled:hover {
+  background: inherit;
+}
+
+.elm-datepicker--picked {
+  color: white;
+  background: darkblue;
+}
+
+.elm-datepicker--picked:hover {
+  background: darkblue;
+}
+
+.elm-datepicker--today {
+  font-weight: bold;
+}
+
+.elm-datepicker--other-month {
+  color: #AAA;
+}
+
+.elm-datepicker--other-month.elm-datepicker--disabled {
+  color: #EEE;
+}
+
+.elm-datepicker--other-month.elm-datepicker--picked {
+  color: white;
+}
diff --git a/users/wpcarro/assessments/tt/client/index.html b/users/wpcarro/assessments/tt/client/index.html
new file mode 100644
index 0000000000..9e6cef70db
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/index.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="google-signin-client_id" content="580018768696-4beppspj6cu7rhjnfgok8lbmh9a4n3ok.apps.googleusercontent.com">
+    <title>Elm SPA</title>
+    <link rel="stylesheet" type="text/css" href="./output.css" />
+    <link rel="stylesheet" type="text/css" href="./print.css" media="print" />
+    <script src="https://apis.google.com/js/platform.js" async defer></script>
+    <script src="./Main.min.js"></script>
+  </head>
+  <body class="font-serif">
+    <div id="mount"></div>
+    <script>
+     function onSignIn(googleUser) {
+       console.log(googleUser);
+     }
+
+     var app = Elm.Main.init({node: document.getElementById("mount")});
+
+     app.ports.printPage.subscribe(function() {
+       window.print();
+     });
+
+     app.ports.googleSignIn.subscribe(function() {
+       var auth2 = gapi.auth2.getAuthInstance();
+       var googleUser = auth2.signIn();
+     });
+
+     app.ports.googleSignOut.subscribe(function() {
+       var auth2 = gapi.auth2.getAuthInstance();
+       auth2.signOut().then(function() {
+         console.log('Google user successfully signed out.');
+       });
+     });
+    </script>
+  </body>
+</html>
diff --git a/users/wpcarro/assessments/tt/client/print.css b/users/wpcarro/assessments/tt/client/print.css
new file mode 100644
index 0000000000..3cfb279230
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/print.css
@@ -0,0 +1,3 @@
+.no-print {
+  display: none;
+}
diff --git a/users/wpcarro/assessments/tt/client/shell.nix b/users/wpcarro/assessments/tt/client/shell.nix
new file mode 100644
index 0000000000..78f55385db
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/shell.nix
@@ -0,0 +1,10 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    nodejs
+    elmPackages.elm
+    elmPackages.elm-format
+    elmPackages.elm-live
+  ];
+}
diff --git a/users/wpcarro/assessments/tt/client/src/Admin.elm b/users/wpcarro/assessments/tt/client/src/Admin.elm
new file mode 100644
index 0000000000..d95609ee15
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/Admin.elm
@@ -0,0 +1,189 @@
+module Admin exposing (render)
+
+import Common
+import Date
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Maybe.Extra as ME
+import RemoteData
+import State
+import Tailwind
+import UI
+import Utils
+
+
+roleToggle : State.Model -> State.Role -> Html State.Msg
+roleToggle model role =
+    div [ [ "px-1", "inline" ] |> Tailwind.use |> class ]
+        [ UI.toggleButton
+            { toggled = model.inviteRole == Just role
+            , label = State.roleToString role
+            , handleEnable = State.UpdateInviteRole (Just role)
+            , handleDisable = State.UpdateInviteRole Nothing
+            }
+        ]
+
+
+inviteUser : State.Model -> Html State.Msg
+inviteUser model =
+    div [ [ "pb-6" ] |> Tailwind.use |> class ]
+        [ UI.header 3 "Invite a user"
+        , UI.textField
+            { handleInput = State.UpdateInviteEmail
+            , inputId = "invite-email"
+            , inputValue = model.inviteEmail
+            , pholder = "Email..."
+            }
+        , div [ [ "pt-4" ] |> Tailwind.use |> class ]
+            [ roleToggle model State.User
+            , roleToggle model State.Manager
+            , roleToggle model State.Admin
+            ]
+        , UI.baseButton
+            { enabled =
+                List.all
+                    identity
+                    [ String.length model.inviteEmail > 0
+                    , ME.isJust model.inviteRole
+                    ]
+            , extraClasses = [ "my-4" ]
+            , label =
+                case model.inviteResponseStatus of
+                    RemoteData.Loading ->
+                        "Sending..."
+
+                    _ ->
+                        "Send invitation"
+            , handleClick =
+                case model.inviteRole of
+                    Nothing ->
+                        State.DoNothing
+
+                    Just role ->
+                        State.AttemptInviteUser role
+            }
+        ]
+
+
+allTrips : State.Model -> Html State.Msg
+allTrips model =
+    case model.trips of
+        RemoteData.NotAsked ->
+            UI.absentData { handleFetch = State.AttemptGetTrips }
+
+        RemoteData.Loading ->
+            UI.paragraph "Loading..."
+
+        RemoteData.Failure e ->
+            UI.paragraph ("Error: " ++ Utils.explainHttpError e)
+
+        RemoteData.Success xs ->
+            ul []
+                (xs
+                    |> List.map
+                        (\trip ->
+                            li []
+                                [ UI.paragraph (Date.toIsoString trip.startDate ++ " - " ++ Date.toIsoString trip.endDate ++ ", " ++ trip.username ++ " is going " ++ trip.destination)
+                                , UI.textButton
+                                    { label = "delete"
+                                    , handleClick = State.AttemptDeleteTrip trip
+                                    }
+                                ]
+                        )
+                )
+
+
+allUsers : State.Model -> Html State.Msg
+allUsers model =
+    case model.accounts of
+        RemoteData.NotAsked ->
+            UI.absentData { handleFetch = State.AttemptGetAccounts }
+
+        RemoteData.Loading ->
+            UI.paragraph "Loading..."
+
+        RemoteData.Failure e ->
+            UI.paragraph ("Error: " ++ Utils.explainHttpError e)
+
+        RemoteData.Success xs ->
+            ul []
+                (xs
+                    |> List.map
+                        (\account ->
+                            li []
+                                [ UI.paragraph
+                                    (account.username
+                                        ++ " - "
+                                        ++ State.roleToString account.role
+                                    )
+                                , UI.textButton
+                                    { label = "delete"
+                                    , handleClick = State.AttemptDeleteAccount account.username
+                                    }
+                                ]
+                        )
+                )
+
+
+users : List String -> Html State.Msg
+users xs =
+    ul []
+        (xs
+            |> List.map
+                (\x ->
+                    li [ [ "py-4", "flex" ] |> Tailwind.use |> class ]
+                        [ p [ [ "flex-1" ] |> Tailwind.use |> class ] [ text x ]
+                        , div [ [ "flex-1" ] |> Tailwind.use |> class ]
+                            [ UI.simpleButton
+                                { label = "Delete"
+                                , handleClick = State.AttemptDeleteAccount x
+                                }
+                            ]
+                        ]
+                )
+        )
+
+
+render : State.Model -> Html State.Msg
+render model =
+    div
+        [ [ "container"
+          , "mx-auto"
+          , "text-center"
+          ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ UI.header 2 "Welcome!"
+        , div []
+            [ UI.textButton
+                { label = "Logout"
+                , handleClick = State.AttemptLogout
+                }
+            ]
+        , div [ [ "py-3" ] |> Tailwind.use |> class ]
+            [ case model.adminTab of
+                State.Accounts ->
+                    UI.textButton
+                        { label = "Switch to trips"
+                        , handleClick = State.UpdateAdminTab State.Trips
+                        }
+
+                State.Trips ->
+                    UI.textButton
+                        { label = "Switch to accounts"
+                        , handleClick = State.UpdateAdminTab State.Accounts
+                        }
+            ]
+        , case model.adminTab of
+            State.Accounts ->
+                div []
+                    [ inviteUser model
+                    , allUsers model
+                    ]
+
+            State.Trips ->
+                allTrips model
+        , Common.allErrors model
+        ]
diff --git a/users/wpcarro/assessments/tt/client/src/Common.elm b/users/wpcarro/assessments/tt/client/src/Common.elm
new file mode 100644
index 0000000000..63ba97b794
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/Common.elm
@@ -0,0 +1,37 @@
+module Common exposing (..)
+
+import Html exposing (..)
+import Maybe.Extra as ME
+import State
+import UI
+import Utils
+
+
+allErrors : State.Model -> Html State.Msg
+allErrors model =
+    div []
+        (State.allErrors
+            model
+            |> List.map
+                (\( mError, title ) ->
+                    case mError of
+                        Nothing ->
+                            text ""
+
+                        Just err ->
+                            UI.errorBanner
+                                { title = title
+                                , body = Utils.explainHttpError err
+                                }
+                )
+        )
+
+
+withSession : State.Model -> (State.Session -> Html State.Msg) -> Html State.Msg
+withSession model renderWithSession =
+    case model.session of
+        Nothing ->
+            div [] [ UI.paragraph "You need a valid session to view this page. Please attempt to log in." ]
+
+        Just session ->
+            renderWithSession session
diff --git a/users/wpcarro/assessments/tt/client/src/Login.elm b/users/wpcarro/assessments/tt/client/src/Login.elm
new file mode 100644
index 0000000000..b1a436098a
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/Login.elm
@@ -0,0 +1,199 @@
+module Login exposing (render)
+
+import Common
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import State
+import Tailwind
+import UI
+import Utils
+
+
+googleSignIn : Html State.Msg
+googleSignIn =
+    div
+        [ class "g-signin2"
+        , attribute "onsuccess" "onSignIn"
+        , onClick State.GoogleSignIn
+        ]
+        []
+
+
+loginForm : State.Model -> Html State.Msg
+loginForm model =
+    div
+        [ [ "w-full"
+          , "max-w-xs"
+          , "mx-auto"
+          ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ div
+            [ [ "bg-white"
+              , "shadow-md"
+              , "rounded"
+              , "px-8"
+              , "pt-6"
+              , "pb-8"
+              , "mb-4"
+              , "text-left"
+              ]
+                |> Tailwind.use
+                |> class
+            ]
+            [ div [ [ "text-center", "pb-6" ] |> Tailwind.use |> class ]
+                [ UI.textButton
+                    { handleClick = State.ToggleLoginForm
+                    , label =
+                        case model.loginTab of
+                            State.LoginForm ->
+                                "Switch to sign up"
+
+                            State.SignUpForm ->
+                                "Switch to login"
+                    }
+                ]
+            , div
+                [ [ "mb-4" ] |> Tailwind.use |> class ]
+                [ UI.label_ { for_ = "username", text_ = "Username" }
+                , UI.textField
+                    { inputId = "Username"
+                    , pholder = "Username"
+                    , handleInput = State.UpdateUsername
+                    , inputValue = model.username
+                    }
+                ]
+            , case model.loginTab of
+                State.LoginForm ->
+                    text ""
+
+                State.SignUpForm ->
+                    div
+                        [ [ "mb-4" ] |> Tailwind.use |> class ]
+                        [ UI.label_ { for_ = "email", text_ = "Email" }
+                        , input
+                            [ [ "shadow"
+                              , "appearance-none"
+                              , "border"
+                              , "rounded"
+                              , "w-full"
+                              , "py-2"
+                              , "px-3"
+                              , "text-gray-700"
+                              , "leading-tight"
+                              , "focus:outline-none"
+                              , "focus:shadow-outline"
+                              ]
+                                |> Tailwind.use
+                                |> class
+                            , id "email"
+                            , placeholder "who@domain.tld"
+                            , onInput State.UpdateEmail
+                            ]
+                            []
+                        ]
+            , div
+                [ [ "mb-4" ] |> Tailwind.use |> class ]
+                [ UI.label_ { for_ = "password", text_ = "Password" }
+                , input
+                    [ [ "shadow"
+                      , "appearance-none"
+                      , "border"
+                      , "rounded"
+                      , "w-full"
+                      , "py-2"
+                      , "px-3"
+                      , "text-gray-700"
+                      , "leading-tight"
+                      , "focus:outline-none"
+                      , "focus:shadow-outline"
+                      ]
+                        |> Tailwind.use
+                        |> class
+                    , id "password"
+                    , type_ "password"
+                    , placeholder "******************"
+                    , onInput State.UpdatePassword
+                    ]
+                    []
+                ]
+            , case model.loginTab of
+                State.LoginForm ->
+                    div [ [ "flex", "space-around" ] |> Tailwind.use |> class ]
+                        [ UI.simpleButton
+                            { handleClick = State.AttemptLogin
+                            , label = "Login"
+                            }
+                        , div [ [ "pl-4" ] |> Tailwind.use |> class ] [ googleSignIn ]
+                        ]
+
+                State.SignUpForm ->
+                    if
+                        List.all identity
+                            [ String.length model.username > 0
+                            , String.length model.email > 0
+                            , String.length model.password > 0
+                            ]
+                    then
+                        div []
+                            [ UI.simpleButton
+                                { handleClick = State.AttemptSignUp
+                                , label = "Sign up"
+                                }
+                            ]
+
+                    else
+                        UI.disabledButton { label = "Sign up" }
+            ]
+        ]
+
+
+login :
+    State.Model
+    -> Html State.Msg
+login model =
+    div
+        [ [ "text-center"
+          , "py-20"
+          , "bg-gray-200"
+          , "h-screen"
+          ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ UI.header 3 "Welcome to Trip Planner"
+        , loginForm model
+        , Common.allErrors model
+        ]
+
+
+logout : State.Model -> Html State.Msg
+logout model =
+    div
+        [ [ "text-center"
+          , "py-20"
+          , "bg-gray-200"
+          , "h-screen"
+          ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ UI.header 3 "Looks like you're already signed in..."
+        , UI.simpleButton
+            { label = "Logout"
+            , handleClick = State.AttemptLogout
+            }
+        , Common.allErrors model
+        ]
+
+
+render : State.Model -> Html State.Msg
+render model =
+    case model.session of
+        Nothing ->
+            login model
+
+        Just x ->
+            logout model
diff --git a/users/wpcarro/assessments/tt/client/src/Main.elm b/users/wpcarro/assessments/tt/client/src/Main.elm
new file mode 100644
index 0000000000..de71a72db0
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/Main.elm
@@ -0,0 +1,62 @@
+module Main exposing (main)
+
+import Admin
+import Browser
+import Html exposing (..)
+import Login
+import Manager
+import State
+import Url
+import User
+
+
+viewForRoute : State.Route -> (State.Model -> Html State.Msg)
+viewForRoute route =
+    case route of
+        State.Login ->
+            Login.render
+
+        State.UserHome ->
+            User.render
+
+        State.ManagerHome ->
+            Manager.render
+
+        State.AdminHome ->
+            Admin.render
+
+
+view : State.Model -> Browser.Document State.Msg
+view model =
+    { title = "TripPlanner"
+    , body =
+        [ case ( model.session, model.route ) of
+            -- Redirect to /login when someone is not authenticated.
+            -- TODO(wpcarro): We should ensure that /login shows in the URL
+            -- bar.
+            ( Nothing, _ ) ->
+                Login.render model
+
+            ( Just session, Nothing ) ->
+                Login.render model
+
+            -- Authenticated
+            ( Just session, Just route ) ->
+                if State.isAuthorized session.role route then
+                    viewForRoute route model
+
+                else
+                    text "Access denied. You are not authorized to be here. Evacuate the area immediately"
+        ]
+    }
+
+
+main =
+    Browser.application
+        { init = State.init
+        , onUrlChange = State.UrlChanged
+        , onUrlRequest = State.LinkClicked
+        , subscriptions = \_ -> Sub.none
+        , update = State.update
+        , view = view
+        }
diff --git a/users/wpcarro/assessments/tt/client/src/Manager.elm b/users/wpcarro/assessments/tt/client/src/Manager.elm
new file mode 100644
index 0000000000..cd15c99a34
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/Manager.elm
@@ -0,0 +1,70 @@
+module Manager exposing (render)
+
+import Array
+import Common
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import RemoteData
+import State
+import Tailwind
+import UI
+import Utils
+
+
+allUsers : State.Model -> Html State.Msg
+allUsers model =
+    case model.accounts of
+        RemoteData.NotAsked ->
+            UI.absentData { handleFetch = State.AttemptGetAccounts }
+
+        RemoteData.Loading ->
+            UI.paragraph "Loading..."
+
+        RemoteData.Failure e ->
+            UI.paragraph ("Error: " ++ Utils.explainHttpError e)
+
+        RemoteData.Success xs ->
+            ul []
+                (xs
+                    |> List.map
+                        (\account ->
+                            li []
+                                [ UI.paragraph
+                                    (account.username
+                                        ++ " - "
+                                        ++ State.roleToString account.role
+                                    )
+                                , UI.textButton
+                                    { label = "delete"
+                                    , handleClick = State.AttemptDeleteAccount account.username
+                                    }
+                                ]
+                        )
+                )
+
+
+render : State.Model -> Html State.Msg
+render model =
+    Common.withSession model
+        (\session ->
+            div
+                [ class
+                    ([ "container"
+                     , "mx-auto"
+                     , "text-center"
+                     ]
+                        |> Tailwind.use
+                    )
+                ]
+                [ h1 []
+                    [ UI.header 2 ("Welcome back, " ++ session.username ++ "!")
+                    , UI.textButton
+                        { label = "Logout"
+                        , handleClick = State.AttemptLogout
+                        }
+                    , allUsers model
+                    , Common.allErrors model
+                    ]
+                ]
+        )
diff --git a/users/wpcarro/assessments/tt/client/src/Shared.elm b/users/wpcarro/assessments/tt/client/src/Shared.elm
new file mode 100644
index 0000000000..addb0a4ffd
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/Shared.elm
@@ -0,0 +1,7 @@
+module Shared exposing (..)
+
+clientOrigin =
+    "http://localhost:8000"
+
+serverOrigin =
+    "http://localhost:3000"
diff --git a/users/wpcarro/assessments/tt/client/src/State.elm b/users/wpcarro/assessments/tt/client/src/State.elm
new file mode 100644
index 0000000000..b3f78bb169
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/State.elm
@@ -0,0 +1,1014 @@
+port module State exposing (..)
+
+import Array exposing (Array)
+import Browser
+import Browser.Navigation as Nav
+import Date
+import DatePicker
+import Http
+import Json.Decode as JD
+import Json.Decode.Extra as JDE
+import Json.Encode as JE
+import Json.Encode.Extra as JEE
+import Process
+import RemoteData exposing (WebData)
+import Shared
+import Task
+import Time
+import Url
+import Url.Builder as UrlBuilder
+import Url.Parser exposing ((</>), Parser, int, map, oneOf, s, string)
+import Utils
+
+
+
+--------------------------------------------------------------------------------
+-- Types
+--------------------------------------------------------------------------------
+
+
+type Msg
+    = DoNothing
+    | UpdateUsername String
+    | UpdateEmail String
+    | UpdatePassword String
+    | UpdateRole String
+    | UpdateAdminTab AdminTab
+    | UpdateTripDestination String
+    | UpdateTripStartDate DatePicker.Msg
+    | UpdateTripEndDate DatePicker.Msg
+    | UpdateTripComment String
+    | UpdateEditTripDestination String
+    | UpdateEditTripComment String
+    | ClearErrors
+    | ToggleLoginForm
+    | PrintPage
+    | GoogleSignIn
+    | GoogleSignOut
+    | UpdateInviteEmail String
+    | UpdateInviteRole (Maybe Role)
+    | ReceiveTodaysDate Date.Date
+    | EditTrip Trip
+    | CancelEditTrip
+      -- SPA
+    | LinkClicked Browser.UrlRequest
+    | UrlChanged Url.Url
+      -- Outbound network
+    | AttemptGetAccounts
+    | AttemptGetTrips
+    | AttemptSignUp
+    | AttemptLogin
+    | AttemptLogout
+    | AttemptDeleteAccount String
+    | AttemptCreateTrip Date.Date Date.Date
+    | AttemptDeleteTrip Trip
+    | AttemptInviteUser Role
+    | AttemptUpdateTrip TripPK Trip
+      -- Inbound network
+    | GotAccounts (WebData (List Account))
+    | GotTrips (WebData (List Trip))
+    | GotSignUp (Result Http.Error Session)
+    | GotLogin (Result Http.Error Session)
+    | GotLogout (Result Http.Error String)
+    | GotDeleteAccount (Result Http.Error String)
+    | GotCreateTrip (Result Http.Error ())
+    | GotDeleteTrip (Result Http.Error ())
+    | GotInviteUser (Result Http.Error ())
+    | GotUpdateTrip (Result Http.Error ())
+
+
+type Route
+    = Login
+    | UserHome
+    | ManagerHome
+    | AdminHome
+
+
+type Role
+    = User
+    | Manager
+    | Admin
+
+
+type alias Account =
+    { username : String
+    , role : Role
+    }
+
+
+type alias Session =
+    { role : Role
+    , username : String
+    }
+
+
+type alias Review =
+    { rowid : Int
+    , content : String
+    , rating : Int
+    , user : String
+    , dateOfVisit : String
+    }
+
+
+type AdminTab
+    = Accounts
+    | Trips
+
+
+type LoginTab
+    = LoginForm
+    | SignUpForm
+
+
+type alias Trip =
+    { username : String
+    , destination : String
+    , startDate : Date.Date
+    , endDate : Date.Date
+    , comment : String
+    }
+
+
+type alias TripPK =
+    { username : String
+    , destination : String
+    , startDate : Date.Date
+    }
+
+
+type alias Model =
+    { route : Maybe Route
+    , url : Url.Url
+    , key : Nav.Key
+    , session : Maybe Session
+    , todaysDate : Maybe Date.Date
+    , username : String
+    , email : String
+    , password : String
+    , role : Maybe Role
+    , accounts : WebData (List Account)
+    , startDatePicker : DatePicker.DatePicker
+    , endDatePicker : DatePicker.DatePicker
+    , tripDestination : String
+    , tripStartDate : Maybe Date.Date
+    , tripEndDate : Maybe Date.Date
+    , tripComment : String
+    , trips : WebData (List Trip)
+    , editingTrip : Maybe Trip
+    , editTripDestination : String
+    , editTripComment : String
+    , adminTab : AdminTab
+    , loginTab : LoginTab
+    , inviteEmail : String
+    , inviteRole : Maybe Role
+    , inviteResponseStatus : WebData ()
+    , updateTripStatus : WebData ()
+    , loginError : Maybe Http.Error
+    , logoutError : Maybe Http.Error
+    , signUpError : Maybe Http.Error
+    , deleteUserError : Maybe Http.Error
+    , createTripError : Maybe Http.Error
+    , deleteTripError : Maybe Http.Error
+    , inviteUserError : Maybe Http.Error
+    }
+
+
+allErrors : Model -> List ( Maybe Http.Error, String )
+allErrors model =
+    [ ( model.loginError, "Error attempting to authenticate" )
+    , ( model.logoutError, "Error attempting to log out" )
+    , ( model.signUpError, "Error attempting to create your account" )
+    , ( model.deleteUserError, "Error attempting to delete a user" )
+    , ( model.createTripError, "Error attempting to create a trip" )
+    , ( model.inviteUserError, "Error attempting to invite a user" )
+    ]
+
+
+
+--------------------------------------------------------------------------------
+-- Functions
+--------------------------------------------------------------------------------
+
+
+roleToString : Role -> String
+roleToString role =
+    case role of
+        User ->
+            "user"
+
+        Manager ->
+            "manager"
+
+        Admin ->
+            "admin"
+
+
+endpoint : List String -> List UrlBuilder.QueryParameter -> String
+endpoint =
+    UrlBuilder.crossOrigin Shared.serverOrigin
+
+
+encodeRole : Role -> JE.Value
+encodeRole x =
+    case x of
+        User ->
+            JE.string "user"
+
+        Manager ->
+            JE.string "manager"
+
+        Admin ->
+            JE.string "admin"
+
+
+decodeRole : JD.Decoder Role
+decodeRole =
+    let
+        toRole : String -> JD.Decoder Role
+        toRole s =
+            case s of
+                "user" ->
+                    JD.succeed User
+
+                "manager" ->
+                    JD.succeed Manager
+
+                "admin" ->
+                    JD.succeed Admin
+
+                x ->
+                    JD.fail ("Invalid input: " ++ x)
+    in
+    JD.string |> JD.andThen toRole
+
+
+decodeSession : JD.Decoder Session
+decodeSession =
+    JD.map2
+        Session
+        (JD.field "role" decodeRole)
+        (JD.field "username" JD.string)
+
+
+encodeLoginRequest : String -> String -> JE.Value
+encodeLoginRequest username password =
+    JE.object
+        [ ( "username", JE.string username )
+        , ( "password", JE.string password )
+        ]
+
+
+login : String -> String -> Cmd Msg
+login username password =
+    Utils.postWithCredentials
+        { url = endpoint [ "login" ] []
+        , body = Http.jsonBody (encodeLoginRequest username password)
+        , expect = Http.expectJson GotLogin decodeSession
+        }
+
+
+logout : Cmd Msg
+logout =
+    Utils.getWithCredentials
+        { url = endpoint [ "logout" ] []
+        , expect = Http.expectString GotLogout
+        }
+
+
+signUp :
+    { username : String
+    , email : String
+    , password : String
+    }
+    -> Cmd Msg
+signUp { username, email, password } =
+    Utils.postWithCredentials
+        { url = endpoint [ "accounts" ] []
+        , body =
+            Http.jsonBody
+                (JE.object
+                    [ ( "username", JE.string username )
+                    , ( "email", JE.string username )
+                    , ( "password", JE.string password )
+                    , ( "role", JE.string "user" )
+                    ]
+                )
+        , expect = Http.expectJson GotSignUp decodeSession
+        }
+
+
+updateTrip : TripPK -> Trip -> Cmd Msg
+updateTrip tripKey trip =
+    Utils.putWithCredentials
+        { url = endpoint [ "trips" ] []
+        , body =
+            Http.jsonBody
+                (JE.object
+                    [ ( "tripKey", encodeTripKey tripKey )
+                    , ( "destination", JE.string trip.destination )
+                    , ( "startDate", encodeDate trip.startDate )
+                    , ( "endDate", encodeDate trip.endDate )
+                    , ( "comment", JE.string trip.comment )
+                    ]
+                )
+        , expect = Http.expectWhatever GotUpdateTrip
+        }
+
+
+inviteUser : { email : String, role : Role } -> Cmd Msg
+inviteUser { email, role } =
+    Utils.postWithCredentials
+        { url = endpoint [ "invite" ] []
+        , body =
+            Http.jsonBody
+                (JE.object
+                    [ ( "email", JE.string email )
+                    , ( "role", encodeRole role )
+                    ]
+                )
+        , expect = Http.expectWhatever GotInviteUser
+        }
+
+
+createTrip :
+    { username : String
+    , destination : String
+    , startDate : Date.Date
+    , endDate : Date.Date
+    , comment : String
+    }
+    -> Cmd Msg
+createTrip { username, destination, startDate, endDate, comment } =
+    Utils.postWithCredentials
+        { url = endpoint [ "trips" ] []
+        , body =
+            Http.jsonBody
+                (JE.object
+                    [ ( "username", JE.string username )
+                    , ( "destination", JE.string destination )
+                    , ( "startDate", encodeDate startDate )
+                    , ( "endDate", encodeDate endDate )
+                    , ( "comment", JE.string comment )
+                    ]
+                )
+        , expect = Http.expectWhatever GotCreateTrip
+        }
+
+
+deleteTrip :
+    { username : String
+    , destination : String
+    , startDate : Date.Date
+    }
+    -> Cmd Msg
+deleteTrip { username, destination, startDate } =
+    Utils.deleteWithCredentials
+        { url = endpoint [ "trips" ] []
+        , body =
+            Http.jsonBody
+                (JE.object
+                    [ ( "username", JE.string username )
+                    , ( "destination", JE.string destination )
+                    , ( "startDate", encodeDate startDate )
+                    ]
+                )
+        , expect = Http.expectWhatever GotDeleteTrip
+        }
+
+
+deleteAccount : String -> Cmd Msg
+deleteAccount username =
+    Utils.deleteWithCredentials
+        { url = endpoint [ "accounts" ] [ UrlBuilder.string "username" username ]
+        , body = Http.emptyBody
+        , expect = Http.expectString GotDeleteAccount
+        }
+
+
+decodeReview : JD.Decoder Review
+decodeReview =
+    JD.map5
+        Review
+        (JD.field "rowid" JD.int)
+        (JD.field "content" JD.string)
+        (JD.field "rating" JD.int)
+        (JD.field "user" JD.string)
+        (JD.field "timestamp" JD.string)
+
+
+encodeTripKey : TripPK -> JE.Value
+encodeTripKey tripKey =
+    JE.object
+        [ ( "username", JE.string tripKey.username )
+        , ( "destination", JE.string tripKey.destination )
+        , ( "startDate", encodeDate tripKey.startDate )
+        ]
+
+
+encodeDate : Date.Date -> JE.Value
+encodeDate date =
+    date |> Date.toIsoString |> JE.string
+
+
+decodeDate : JD.Decoder Date.Date
+decodeDate =
+    JD.string |> JD.andThen (Date.fromIsoString >> JDE.fromResult)
+
+
+fetchTrips : Cmd Msg
+fetchTrips =
+    Utils.getWithCredentials
+        { url = endpoint [ "trips" ] []
+        , expect =
+            Http.expectJson
+                (RemoteData.fromResult >> GotTrips)
+                (JD.list
+                    (JD.map5
+                        Trip
+                        (JD.field "username" JD.string)
+                        (JD.field "destination" JD.string)
+                        (JD.field "startDate" decodeDate)
+                        (JD.field "endDate" decodeDate)
+                        (JD.field "comment" JD.string)
+                    )
+                )
+        }
+
+
+fetchAccounts : Cmd Msg
+fetchAccounts =
+    Utils.getWithCredentials
+        { url = endpoint [ "accounts" ] []
+        , expect =
+            Http.expectJson
+                (RemoteData.fromResult >> GotAccounts)
+                (JD.list
+                    (JD.map2
+                        Account
+                        (JD.field "username" JD.string)
+                        (JD.field "role" decodeRole)
+                    )
+                )
+        }
+
+
+sleepAndClearErrors : Cmd Msg
+sleepAndClearErrors =
+    Process.sleep 4000
+        |> Task.perform (\_ -> ClearErrors)
+
+
+isAuthorized : Role -> Route -> Bool
+isAuthorized role route =
+    case ( role, route ) of
+        ( User, _ ) ->
+            True
+
+        ( Manager, _ ) ->
+            True
+
+        ( Admin, _ ) ->
+            True
+
+
+homeRouteForRole : Role -> String
+homeRouteForRole role =
+    case role of
+        User ->
+            "/user"
+
+        Manager ->
+            "/manager"
+
+        Admin ->
+            "/admin"
+
+
+routeParser : Parser (Route -> a) a
+routeParser =
+    oneOf
+        [ map Login (s "topic")
+        , map UserHome (s "user")
+        , map ManagerHome (s "manager")
+        , map AdminHome (s "admin")
+        ]
+
+
+{-| Set init to `prod` when going live.
+-}
+prod : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
+prod _ url key =
+    let
+        ( startDatePicker, startDatePickerCmd ) =
+            DatePicker.init
+
+        ( endDatePicker, endDatePickerCmd ) =
+            DatePicker.init
+    in
+    ( { route = Nothing
+      , url = url
+      , key = key
+      , session = Nothing
+      , todaysDate = Nothing
+      , username = ""
+      , email = ""
+      , password = ""
+      , role = Nothing
+      , accounts = RemoteData.NotAsked
+      , tripDestination = ""
+      , tripStartDate = Nothing
+      , tripEndDate = Nothing
+      , tripComment = ""
+      , trips = RemoteData.NotAsked
+      , editingTrip = Nothing
+      , editTripDestination = ""
+      , editTripComment = ""
+      , startDatePicker = startDatePicker
+      , endDatePicker = endDatePicker
+      , adminTab = Accounts
+      , loginTab = LoginForm
+      , inviteEmail = ""
+      , inviteRole = Nothing
+      , inviteResponseStatus = RemoteData.NotAsked
+      , updateTripStatus = RemoteData.NotAsked
+      , loginError = Nothing
+      , logoutError = Nothing
+      , signUpError = Nothing
+      , deleteUserError = Nothing
+      , createTripError = Nothing
+      , deleteTripError = Nothing
+      , inviteUserError = Nothing
+      }
+    , Cmd.batch
+        [ Cmd.map UpdateTripStartDate startDatePickerCmd
+        , Cmd.map UpdateTripEndDate endDatePickerCmd
+        , Date.today |> Task.perform ReceiveTodaysDate
+        ]
+    )
+
+
+{-| When working on a feature for the UserHome, use this.
+-}
+userHome : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
+userHome flags url key =
+    let
+        ( model, cmd ) =
+            prod flags url key
+    in
+    ( { model
+        | route = Just UserHome
+        , session = Just { username = "mimi", role = User }
+        , trips =
+            RemoteData.Success
+                [ { username = "mimi"
+                  , destination = "Barcelona"
+                  , startDate = Date.fromCalendarDate 2020 Time.Sep 25
+                  , endDate = Date.fromCalendarDate 2020 Time.Oct 5
+                  , comment = "Blah"
+                  }
+                , { username = "mimi"
+                  , destination = "Paris"
+                  , startDate = Date.fromCalendarDate 2021 Time.Jan 1
+                  , endDate = Date.fromCalendarDate 2021 Time.Feb 1
+                  , comment = "Bon voyage!"
+                  }
+                ]
+      }
+    , cmd
+    )
+
+
+managerHome : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
+managerHome flags url key =
+    let
+        ( model, cmd ) =
+            prod flags url key
+    in
+    ( { model
+        | route = Just ManagerHome
+        , session = Just { username = "bill", role = Manager }
+      }
+    , cmd
+    )
+
+
+adminHome : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
+adminHome flags url key =
+    let
+        ( model, cmd ) =
+            prod flags url key
+    in
+    ( { model
+        | route = Just AdminHome
+        , session = Just { username = "wpcarro", role = Admin }
+      }
+    , cmd
+    )
+
+
+port printPage : () -> Cmd msg
+
+
+port googleSignIn : () -> Cmd msg
+
+
+port googleSignOut : () -> Cmd msg
+
+
+{-| The initial state for the application.
+-}
+init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
+init flags url key =
+    prod flags url key
+
+
+{-| Now that we have state, we need a function to change the state.
+-}
+update : Msg -> Model -> ( Model, Cmd Msg )
+update msg model =
+    case msg of
+        DoNothing ->
+            ( model, Cmd.none )
+
+        UpdateUsername x ->
+            ( { model | username = x }, Cmd.none )
+
+        UpdatePassword x ->
+            ( { model | password = x }, Cmd.none )
+
+        UpdateEmail x ->
+            ( { model | email = x }, Cmd.none )
+
+        UpdateAdminTab x ->
+            ( { model | adminTab = x }, Cmd.none )
+
+        UpdateRole x ->
+            let
+                maybeRole =
+                    case x of
+                        "user" ->
+                            Just User
+
+                        "manager" ->
+                            Just Manager
+
+                        "admin" ->
+                            Just Admin
+
+                        _ ->
+                            Nothing
+            in
+            ( { model | role = maybeRole }, Cmd.none )
+
+        UpdateTripDestination x ->
+            ( { model | tripDestination = x }, Cmd.none )
+
+        UpdateTripStartDate dpMsg ->
+            let
+                ( newDatePicker, dateEvent ) =
+                    DatePicker.update DatePicker.defaultSettings dpMsg model.startDatePicker
+
+                newDate =
+                    case dateEvent of
+                        DatePicker.Picked changedDate ->
+                            Just changedDate
+
+                        _ ->
+                            model.tripStartDate
+            in
+            ( { model
+                | tripStartDate = newDate
+                , startDatePicker = newDatePicker
+              }
+            , Cmd.none
+            )
+
+        UpdateTripEndDate dpMsg ->
+            let
+                ( newDatePicker, dateEvent ) =
+                    DatePicker.update DatePicker.defaultSettings dpMsg model.endDatePicker
+
+                newDate =
+                    case dateEvent of
+                        DatePicker.Picked changedDate ->
+                            Just changedDate
+
+                        _ ->
+                            model.tripEndDate
+            in
+            ( { model
+                | tripEndDate = newDate
+                , endDatePicker = newDatePicker
+              }
+            , Cmd.none
+            )
+
+        UpdateTripComment x ->
+            ( { model | tripComment = x }, Cmd.none )
+
+        UpdateEditTripDestination x ->
+            ( { model | editTripDestination = x }, Cmd.none )
+
+        UpdateEditTripComment x ->
+            ( { model | editTripComment = x }, Cmd.none )
+
+        ClearErrors ->
+            ( { model
+                | loginError = Nothing
+                , logoutError = Nothing
+                , signUpError = Nothing
+                , deleteUserError = Nothing
+                , createTripError = Nothing
+              }
+            , Cmd.none
+            )
+
+        ToggleLoginForm ->
+            ( { model
+                | loginTab =
+                    case model.loginTab of
+                        LoginForm ->
+                            SignUpForm
+
+                        SignUpForm ->
+                            LoginForm
+              }
+            , Cmd.none
+            )
+
+        PrintPage ->
+            ( model, printPage () )
+
+        GoogleSignIn ->
+            ( model, googleSignIn () )
+
+        GoogleSignOut ->
+            ( model, googleSignOut () )
+
+        UpdateInviteEmail x ->
+            ( { model | inviteEmail = x }, Cmd.none )
+
+        UpdateInviteRole mRole ->
+            ( { model | inviteRole = mRole }, Cmd.none )
+
+        ReceiveTodaysDate date ->
+            ( { model | todaysDate = Just date }, Cmd.none )
+
+        EditTrip trip ->
+            ( { model
+                | editingTrip = Just trip
+                , editTripDestination = trip.destination
+                , editTripComment = trip.comment
+              }
+            , Cmd.none
+            )
+
+        CancelEditTrip ->
+            ( { model
+                | editingTrip = Nothing
+                , editTripDestination = ""
+                , editTripComment = ""
+              }
+            , Cmd.none
+            )
+
+        LinkClicked urlRequest ->
+            case urlRequest of
+                Browser.Internal url ->
+                    ( model, Nav.pushUrl model.key (Url.toString url) )
+
+                Browser.External href ->
+                    ( model, Nav.load href )
+
+        UrlChanged url ->
+            let
+                route =
+                    Url.Parser.parse routeParser url
+            in
+            case route of
+                Just UserHome ->
+                    ( { model
+                        | url = url
+                        , route = route
+                        , trips = RemoteData.Loading
+                      }
+                    , fetchTrips
+                    )
+
+                Just ManagerHome ->
+                    ( { model
+                        | url = url
+                        , route = route
+                        , accounts = RemoteData.Loading
+                      }
+                    , fetchAccounts
+                    )
+
+                Just AdminHome ->
+                    ( { model
+                        | url = url
+                        , route = route
+                        , accounts = RemoteData.Loading
+                        , trips = RemoteData.Loading
+                      }
+                    , Cmd.batch
+                        [ fetchAccounts
+                        , fetchTrips
+                        ]
+                    )
+
+                _ ->
+                    ( { model
+                        | url = url
+                        , route = route
+                      }
+                    , Cmd.none
+                    )
+
+        -- GET /accounts
+        AttemptGetAccounts ->
+            ( { model | accounts = RemoteData.Loading }, fetchAccounts )
+
+        GotAccounts xs ->
+            ( { model | accounts = xs }, Cmd.none )
+
+        -- DELETE /accounts
+        AttemptDeleteAccount username ->
+            ( model, deleteAccount username )
+
+        GotDeleteAccount result ->
+            case result of
+                Ok _ ->
+                    ( model, fetchAccounts )
+
+                Err e ->
+                    ( { model | deleteUserError = Just e }
+                    , sleepAndClearErrors
+                    )
+
+        -- POST /trips
+        AttemptCreateTrip startDate endDate ->
+            ( model
+            , case model.session of
+                Nothing ->
+                    Cmd.none
+
+                Just session ->
+                    createTrip
+                        { username = session.username
+                        , destination = model.tripDestination
+                        , startDate = startDate
+                        , endDate = endDate
+                        , comment = model.tripComment
+                        }
+            )
+
+        GotCreateTrip result ->
+            case result of
+                Ok _ ->
+                    ( { model
+                        | tripDestination = ""
+                        , tripStartDate = Nothing
+                        , tripEndDate = Nothing
+                        , tripComment = ""
+                      }
+                    , fetchTrips
+                    )
+
+                Err e ->
+                    ( { model
+                        | createTripError = Just e
+                        , tripDestination = ""
+                        , tripStartDate = Nothing
+                        , tripEndDate = Nothing
+                        , tripComment = ""
+                      }
+                    , sleepAndClearErrors
+                    )
+
+        -- DELETE /trips
+        AttemptDeleteTrip trip ->
+            ( model
+            , deleteTrip
+                { username = trip.username
+                , destination = trip.destination
+                , startDate = trip.startDate
+                }
+            )
+
+        GotDeleteTrip result ->
+            case result of
+                Ok _ ->
+                    ( model, fetchTrips )
+
+                Err e ->
+                    ( { model | deleteTripError = Just e }
+                    , sleepAndClearErrors
+                    )
+
+        AttemptInviteUser role ->
+            ( { model | inviteResponseStatus = RemoteData.Loading }
+            , inviteUser
+                { email = model.inviteEmail
+                , role = role
+                }
+            )
+
+        GotInviteUser result ->
+            case result of
+                Ok _ ->
+                    ( { model
+                        | inviteEmail = ""
+                        , inviteRole = Nothing
+                        , inviteResponseStatus = RemoteData.Success ()
+                      }
+                    , Cmd.none
+                    )
+
+                Err e ->
+                    ( { model
+                        | inviteUserError = Just e
+                        , inviteResponseStatus = RemoteData.Failure e
+                      }
+                    , sleepAndClearErrors
+                    )
+
+        -- PATCH /trips
+        AttemptUpdateTrip tripKey trip ->
+            ( { model | updateTripStatus = RemoteData.Loading }
+            , updateTrip tripKey trip
+            )
+
+        GotUpdateTrip result ->
+            case result of
+                Ok _ ->
+                    ( { model | updateTripStatus = RemoteData.Success () }
+                    , fetchTrips
+                    )
+
+                Err e ->
+                    ( { model | updateTripStatus = RemoteData.Failure e }
+                    , Cmd.none
+                    )
+
+        -- POST /accounts
+        AttemptSignUp ->
+            ( model
+            , signUp
+                { username = model.username
+                , email = model.email
+                , password = model.password
+                }
+            )
+
+        GotSignUp result ->
+            case result of
+                Ok session ->
+                    ( { model | session = Just session }
+                    , Nav.pushUrl model.key (homeRouteForRole session.role)
+                    )
+
+                Err x ->
+                    ( { model | signUpError = Just x }
+                    , sleepAndClearErrors
+                    )
+
+        -- GET /trips
+        AttemptGetTrips ->
+            ( { model | trips = RemoteData.Loading }, fetchTrips )
+
+        GotTrips xs ->
+            ( { model | trips = xs }, Cmd.none )
+
+        -- POST /login
+        AttemptLogin ->
+            ( model, login model.username model.password )
+
+        GotLogin result ->
+            case result of
+                Ok session ->
+                    ( { model | session = Just session }
+                    , Nav.pushUrl model.key (homeRouteForRole session.role)
+                    )
+
+                Err x ->
+                    ( { model | loginError = Just x }
+                    , sleepAndClearErrors
+                    )
+
+        -- GET /logout
+        AttemptLogout ->
+            ( model, logout )
+
+        GotLogout result ->
+            case result of
+                Ok _ ->
+                    ( { model | session = Nothing }
+                    , Nav.pushUrl model.key "/login"
+                    )
+
+                Err e ->
+                    ( { model | logoutError = Just e }
+                    , sleepAndClearErrors
+                    )
diff --git a/users/wpcarro/assessments/tt/client/src/Tailwind.elm b/users/wpcarro/assessments/tt/client/src/Tailwind.elm
new file mode 100644
index 0000000000..57d419db5a
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/Tailwind.elm
@@ -0,0 +1,29 @@
+module Tailwind exposing (..)
+
+{-| Functions to make Tailwind development in Elm even more pleasant.
+-}
+
+
+{-| Conditionally use `class` selection when `condition` is true.
+-}
+when : Bool -> String -> String
+when condition class =
+    if condition then
+        class
+
+    else
+        ""
+
+
+if_ : Bool -> String -> String -> String
+if_ condition whenTrue whenFalse =
+    if condition then
+        whenTrue
+
+    else
+        whenFalse
+
+
+use : List String -> String
+use styles =
+    String.join " " styles
diff --git a/users/wpcarro/assessments/tt/client/src/UI.elm b/users/wpcarro/assessments/tt/client/src/UI.elm
new file mode 100644
index 0000000000..7f8f379795
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/UI.elm
@@ -0,0 +1,318 @@
+module UI exposing (..)
+
+import Date
+import DatePicker exposing (defaultSettings)
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import State
+import Tailwind
+
+
+label_ : { for_ : String, text_ : String } -> Html msg
+label_ { for_, text_ } =
+    label
+        [ [ "block"
+          , "text-gray-700"
+          , "text-sm"
+          , "font-bold"
+          , "mb-2"
+          ]
+            |> Tailwind.use
+            |> class
+        , for for_
+        ]
+        [ text text_ ]
+
+
+errorBanner : { title : String, body : String } -> Html msg
+errorBanner { title, body } =
+    div
+        [ [ "text-left"
+          , "fixed"
+          , "container"
+          , "top-0"
+          , "mt-6"
+          ]
+            |> Tailwind.use
+            |> class
+        , style "left" "50%"
+
+        -- TODO(wpcarro): Consider supporting breakpoints, but for now
+        -- don't.
+        , style "width" "800px"
+        , style "margin-left" "-400px"
+        ]
+        [ div
+            [ [ "bg-red-500"
+              , "text-white"
+              , "font-bold"
+              , "rounded-t"
+              , "px-4"
+              , "py-2"
+              ]
+                |> Tailwind.use
+                |> class
+            ]
+            [ text title ]
+        , div
+            [ [ "border"
+              , "border-t-0"
+              , "border-red-400"
+              , "rounded-b"
+              , "bg-red-100"
+              , "px-4"
+              , "py-3"
+              , "text-red-700"
+              ]
+                |> Tailwind.use
+                |> class
+            ]
+            [ p [] [ text body ] ]
+        ]
+
+
+baseButton :
+    { label : String
+    , enabled : Bool
+    , handleClick : msg
+    , extraClasses : List String
+    }
+    -> Html msg
+baseButton { label, enabled, handleClick, extraClasses } =
+    button
+        [ [ if enabled then
+                "bg-blue-500"
+
+            else
+                "bg-gray-500"
+          , if enabled then
+                "hover:bg-blue-700"
+
+            else
+                ""
+          , if enabled then
+                ""
+
+            else
+                "cursor-not-allowed"
+          , "text-white"
+          , "font-bold"
+          , "py-1"
+          , "shadow-lg"
+          , "px-4"
+          , "rounded"
+          , "focus:outline-none"
+          , "focus:shadow-outline"
+          ]
+            ++ extraClasses
+            |> Tailwind.use
+            |> class
+        , onClick handleClick
+        , disabled (not enabled)
+        ]
+        [ text label ]
+
+
+simpleButton :
+    { label : String
+    , handleClick : msg
+    }
+    -> Html msg
+simpleButton { label, handleClick } =
+    baseButton
+        { label = label
+        , enabled = True
+        , handleClick = handleClick
+        , extraClasses = []
+        }
+
+
+disabledButton :
+    { label : String }
+    -> Html State.Msg
+disabledButton { label } =
+    baseButton
+        { label = label
+        , enabled = False
+        , handleClick = State.DoNothing
+        , extraClasses = []
+        }
+
+
+textButton :
+    { label : String
+    , handleClick : msg
+    }
+    -> Html msg
+textButton { label, handleClick } =
+    button
+        [ [ "text-blue-600"
+          , "hover:text-blue-500"
+          , "font-bold"
+          , "hover:underline"
+          , "focus:outline-none"
+          ]
+            |> Tailwind.use
+            |> class
+        , onClick handleClick
+        ]
+        [ text label ]
+
+
+textField :
+    { pholder : String
+    , inputId : String
+    , handleInput : String -> msg
+    , inputValue : String
+    }
+    -> Html msg
+textField { pholder, inputId, handleInput, inputValue } =
+    input
+        [ [ "shadow"
+          , "appearance-none"
+          , "border"
+          , "rounded"
+          , "w-full"
+          , "py-2"
+          , "px-3"
+          , "text-gray-700"
+          , "leading-tight"
+          , "focus:outline-none"
+          , "focus:shadow-outline"
+          ]
+            |> Tailwind.use
+            |> class
+        , id inputId
+        , value inputValue
+        , placeholder pholder
+        , onInput handleInput
+        ]
+        []
+
+
+toggleButton :
+    { toggled : Bool
+    , label : String
+    , handleEnable : msg
+    , handleDisable : msg
+    }
+    -> Html msg
+toggleButton { toggled, label, handleEnable, handleDisable } =
+    button
+        [ [ if toggled then
+                "bg-blue-700"
+
+            else
+                "bg-blue-500"
+          , "hover:bg-blue-700"
+          , "text-white"
+          , "font-bold"
+          , "py-2"
+          , "px-4"
+          , "rounded"
+          , "focus:outline-none"
+          , "focus:shadow-outline"
+          ]
+            |> Tailwind.use
+            |> class
+        , onClick
+            (if toggled then
+                handleDisable
+
+             else
+                handleEnable
+            )
+        ]
+        [ text label ]
+
+
+paragraph : String -> Html msg
+paragraph x =
+    p [ [ "text-xl" ] |> Tailwind.use |> class ] [ text x ]
+
+
+header : Int -> String -> Html msg
+header which x =
+    let
+        hStyles =
+            case which of
+                1 ->
+                    [ "text-6xl"
+                    , "py-12"
+                    ]
+
+                2 ->
+                    [ "text-3xl"
+                    , "py-6"
+                    ]
+
+                _ ->
+                    [ "text-2xl"
+                    , "py-2"
+                    ]
+    in
+    h1
+        [ hStyles
+            ++ [ "font-bold"
+               , "text-gray-700"
+               ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ text x ]
+
+
+link : String -> String -> Html msg
+link path label =
+    a
+        [ href path
+        , [ "underline"
+          , "text-blue-600"
+          , "text-xl"
+          ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ text label ]
+
+
+absentData : { handleFetch : msg } -> Html msg
+absentData { handleFetch } =
+    div []
+        [ paragraph "Welp... it looks like you've caught us in a state that we considered impossible: we did not fetch the data upon which this page depends. Maybe you can help us out by clicking the super secret, highly privileged \"Fetch data\" button below (we don't normally show people this)."
+        , div [ [ "py-4" ] |> Tailwind.use |> class ]
+            [ simpleButton
+                { label = "Fetch data"
+                , handleClick = handleFetch
+                }
+            ]
+        ]
+
+
+datePicker :
+    { mDate : Maybe Date.Date
+    , prompt : String
+    , prefix : String
+    , picker : DatePicker.DatePicker
+    , onUpdate : DatePicker.Msg -> State.Msg
+    }
+    -> Html State.Msg
+datePicker { mDate, prompt, prefix, picker, onUpdate } =
+    let
+        settings =
+            { defaultSettings
+                | placeholder = prompt
+                , inputClassList =
+                    [ ( "text-center", True )
+                    , ( "py-2", True )
+                    ]
+            }
+    in
+    div [ [ "w-1/2", "py-4", "mx-auto" ] |> Tailwind.use |> class ]
+        [ DatePicker.view mDate settings picker |> Html.map onUpdate ]
+
+
+wrapNoPrint : Html State.Msg -> Html State.Msg
+wrapNoPrint component =
+    div [ [ "no-print" ] |> Tailwind.use |> class ] [ component ]
diff --git a/users/wpcarro/assessments/tt/client/src/User.elm b/users/wpcarro/assessments/tt/client/src/User.elm
new file mode 100644
index 0000000000..87871b78db
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/User.elm
@@ -0,0 +1,245 @@
+module User exposing (render)
+
+import Common
+import Date
+import DatePicker
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Maybe.Extra as ME
+import RemoteData
+import State
+import Tailwind
+import UI
+import Utils
+
+
+createTrip : State.Model -> Html State.Msg
+createTrip model =
+    div []
+        [ UI.header 3 "Plan Upcoming Trip"
+        , UI.textField
+            { pholder = "Where are you going?"
+            , inputId = "destination"
+            , handleInput = State.UpdateTripDestination
+            , inputValue = model.tripDestination
+            }
+        , div [ [ "flex" ] |> Tailwind.use |> class ]
+            [ UI.datePicker
+                { mDate = model.tripStartDate
+                , prompt = "Set departure date"
+                , prefix = "Departure: "
+                , picker = model.startDatePicker
+                , onUpdate = State.UpdateTripStartDate
+                }
+            , UI.datePicker
+                { mDate = model.tripEndDate
+                , prompt = "Set return date"
+                , prefix = "Return: "
+                , picker = model.endDatePicker
+                , onUpdate = State.UpdateTripEndDate
+                }
+            ]
+        , UI.textField
+            { pholder = "Comments?"
+            , inputId = "comment"
+            , handleInput = State.UpdateTripComment
+            , inputValue = model.tripComment
+            }
+        , UI.baseButton
+            { enabled =
+                List.all
+                    identity
+                    [ String.length model.tripDestination > 0
+                    , String.length model.tripComment > 0
+                    , ME.isJust model.tripStartDate
+                    , ME.isJust model.tripEndDate
+                    ]
+            , extraClasses = [ "my-4" ]
+            , handleClick =
+                case ( model.tripStartDate, model.tripEndDate ) of
+                    ( Nothing, _ ) ->
+                        State.DoNothing
+
+                    ( _, Nothing ) ->
+                        State.DoNothing
+
+                    ( Just startDate, Just endDate ) ->
+                        State.AttemptCreateTrip startDate endDate
+            , label = "Schedule trip"
+            }
+        ]
+
+
+renderEditTrip : State.Model -> State.Trip -> Html State.Msg
+renderEditTrip model trip =
+    li []
+        [ div []
+            [ UI.textField
+                { handleInput = State.UpdateEditTripDestination
+                , inputId = "edit-trip-destination"
+                , inputValue = model.editTripDestination
+                , pholder = "Destination"
+                }
+            , UI.textField
+                { handleInput = State.UpdateEditTripComment
+                , inputId = "edit-trip-comment"
+                , inputValue = model.editTripComment
+                , pholder = "Comment"
+                }
+            ]
+        , div []
+            [ UI.baseButton
+                { enabled =
+                    case model.updateTripStatus of
+                        RemoteData.Loading ->
+                            False
+
+                        _ ->
+                            True
+                , extraClasses = []
+                , label =
+                    case model.updateTripStatus of
+                        RemoteData.Loading ->
+                            "Saving..."
+
+                        _ ->
+                            "Save"
+                , handleClick =
+                    State.AttemptUpdateTrip
+                        { username = trip.username
+                        , destination = trip.destination
+                        , startDate = trip.startDate
+                        }
+                        { username = trip.username
+                        , destination = model.editTripDestination
+                        , startDate = trip.startDate
+                        , endDate = trip.endDate
+                        , comment = model.editTripComment
+                        }
+                }
+            , UI.simpleButton
+                { label = "Cancel"
+                , handleClick = State.CancelEditTrip
+                }
+            ]
+        ]
+
+
+renderTrip : Date.Date -> State.Trip -> Html State.Msg
+renderTrip today trip =
+    li
+        [ [ "py-2" ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ if Date.compare today trip.startDate == GT then
+            UI.paragraph
+                (String.fromInt (Date.diff Date.Days trip.startDate today)
+                    ++ " days until you're travelling to "
+                    ++ trip.destination
+                    ++ " for "
+                    ++ String.fromInt
+                        (Date.diff
+                            Date.Days
+                            trip.startDate
+                            trip.endDate
+                        )
+                    ++ " days."
+                )
+
+          else
+            UI.paragraph
+                (String.fromInt (Date.diff Date.Days today trip.endDate)
+                    ++ " days ago you returned from your trip to "
+                    ++ trip.destination
+                )
+        , UI.paragraph ("\"" ++ trip.comment ++ "\"")
+        , UI.wrapNoPrint
+            (UI.textButton
+                { label = "Edit"
+                , handleClick = State.EditTrip trip
+                }
+            )
+        , UI.wrapNoPrint
+            (UI.textButton
+                { label = "Delete"
+                , handleClick = State.AttemptDeleteTrip trip
+                }
+            )
+        ]
+
+
+trips : State.Model -> Html State.Msg
+trips model =
+    div []
+        [ UI.header 3 "Your Trips"
+        , case model.trips of
+            RemoteData.NotAsked ->
+                UI.paragraph "Somehow we've reached the user home page without requesting your trips data. Please report this to our engineering team at bugs@tripplaner.tld"
+
+            RemoteData.Loading ->
+                UI.paragraph "Loading your trips..."
+
+            RemoteData.Failure e ->
+                UI.paragraph ("Error: " ++ Utils.explainHttpError e)
+
+            RemoteData.Success xs ->
+                case model.todaysDate of
+                    Nothing ->
+                        text ""
+
+                    Just today ->
+                        div [ [ "mb-10" ] |> Tailwind.use |> class ]
+                            [ ul [ [ "my-4" ] |> Tailwind.use |> class ]
+                                (xs
+                                    |> List.sortWith (\x y -> Date.compare y.startDate x.startDate)
+                                    |> List.map
+                                        (\trip ->
+                                            case model.editingTrip of
+                                                Nothing ->
+                                                    renderTrip today trip
+
+                                                Just x ->
+                                                    if x == trip then
+                                                        renderEditTrip model trip
+
+                                                    else
+                                                        renderTrip today trip
+                                        )
+                                )
+                            , UI.wrapNoPrint
+                                (UI.simpleButton
+                                    { label = "Print iternary"
+                                    , handleClick = State.PrintPage
+                                    }
+                                )
+                            ]
+        ]
+
+
+render : State.Model -> Html State.Msg
+render model =
+    Common.withSession model
+        (\session ->
+            div
+                [ class
+                    ([ "container"
+                     , "mx-auto"
+                     , "text-center"
+                     ]
+                        |> Tailwind.use
+                    )
+                ]
+                [ UI.wrapNoPrint (UI.header 2 ("Welcome, " ++ session.username ++ "!"))
+                , UI.wrapNoPrint (createTrip model)
+                , trips model
+                , UI.wrapNoPrint
+                    (UI.textButton
+                        { label = "Logout"
+                        , handleClick = State.AttemptLogout
+                        }
+                    )
+                , Common.allErrors model
+                ]
+        )
diff --git a/users/wpcarro/assessments/tt/client/src/Utils.elm b/users/wpcarro/assessments/tt/client/src/Utils.elm
new file mode 100644
index 0000000000..60343cd870
--- /dev/null
+++ b/users/wpcarro/assessments/tt/client/src/Utils.elm
@@ -0,0 +1,109 @@
+module Utils exposing (..)
+
+import DateFormat
+import Http
+import Time
+import Shared
+
+
+explainHttpError : Http.Error -> String
+explainHttpError e =
+    case e of
+        Http.BadUrl _ ->
+            "Bad URL: you may have supplied an improperly formatted URL"
+
+        Http.Timeout ->
+            "Timeout: the resource you requested did not arrive within the interval of time that you claimed it should"
+
+        Http.BadStatus s ->
+            "Bad Status: the server returned a bad status code: " ++ String.fromInt s
+
+        Http.BadBody b ->
+            "Bad Body: our application had trouble decoding the body of the response from the server: " ++ b
+
+        Http.NetworkError ->
+            "Network Error: something went awry in the network stack. I recommend checking the server logs if you can."
+
+
+getWithCredentials :
+    { url : String
+    , expect : Http.Expect msg
+    }
+    -> Cmd msg
+getWithCredentials { url, expect } =
+    Http.riskyRequest
+        { url = url
+        , headers = [ Http.header "Origin" Shared.clientOrigin ]
+        , method = "GET"
+        , timeout = Nothing
+        , tracker = Nothing
+        , body = Http.emptyBody
+        , expect = expect
+        }
+
+
+postWithCredentials :
+    { url : String
+    , body : Http.Body
+    , expect : Http.Expect msg
+    }
+    -> Cmd msg
+postWithCredentials { url, body, expect } =
+    Http.riskyRequest
+        { url = url
+        , headers = [ Http.header "Origin" Shared.clientOrigin ]
+        , method = "POST"
+        , timeout = Nothing
+        , tracker = Nothing
+        , body = body
+        , expect = expect
+        }
+
+
+deleteWithCredentials :
+    { url : String
+    , body : Http.Body
+    , expect : Http.Expect msg
+    }
+    -> Cmd msg
+deleteWithCredentials { url, body, expect } =
+    Http.riskyRequest
+        { url = url
+        , headers = [ Http.header "Origin" Shared.clientOrigin ]
+        , method = "DELETE"
+        , timeout = Nothing
+        , tracker = Nothing
+        , body = body
+        , expect = expect
+        }
+
+putWithCredentials :
+    { url : String
+    , body : Http.Body
+    , expect : Http.Expect msg
+    }
+    -> Cmd msg
+putWithCredentials { url, body, expect } =
+    Http.riskyRequest
+        { url = url
+        , headers = [ Http.header "Origin" Shared.clientOrigin ]
+        , method = "PUT"
+        , timeout = Nothing
+        , tracker = Nothing
+        , body = body
+        , expect = expect
+        }
+
+
+
+formatTime : Time.Posix -> String
+formatTime ts =
+    DateFormat.format
+        [ DateFormat.monthNameFull
+        , DateFormat.text " "
+        , DateFormat.dayOfMonthSuffix
+        , DateFormat.text ", "
+        , DateFormat.yearNumber
+        ]
+        Time.utc
+        ts
diff --git a/users/wpcarro/assessments/tt/data/accounts.csv b/users/wpcarro/assessments/tt/data/accounts.csv
new file mode 100644
index 0000000000..f5fc77b6d7
--- /dev/null
+++ b/users/wpcarro/assessments/tt/data/accounts.csv
@@ -0,0 +1,2 @@
+mimi,$2b$12$LynoGCNbe2RA1WWSiBEMVudJKs5dxnssY16rYmUyiwlSBIhHBOLbu,miriamwright@google.com,user,
+wpcarro,$2b$12$3wbi4xfQmksLsu6GOKTbj.5WHywESATnXB4R8FJ55RSRLy6X9xA7u,wpcarro@google.com,admin,
\ No newline at end of file
diff --git a/users/wpcarro/assessments/tt/data/trips.csv b/users/wpcarro/assessments/tt/data/trips.csv
new file mode 100644
index 0000000000..a583c750f7
--- /dev/null
+++ b/users/wpcarro/assessments/tt/data/trips.csv
@@ -0,0 +1,3 @@
+mimi,Rome,2020-08-10,2020-08-12,Heading home before the upcoming trip with Panarea.
+mimi,Panarea,2020-08-15,2020-08-28,Exciting upcoming trip with Matt and Sarah!
+mimi,London,2020-08-30,2020-09-15,Heading back to London...
\ No newline at end of file
diff --git a/users/wpcarro/assessments/tt/populate.sqlite3 b/users/wpcarro/assessments/tt/populate.sqlite3
new file mode 100644
index 0000000000..e200d2b49c
--- /dev/null
+++ b/users/wpcarro/assessments/tt/populate.sqlite3
@@ -0,0 +1,7 @@
+PRAGMA foreign_keys = on;
+.read src/init.sql
+.mode csv
+.import data/accounts.csv Accounts
+.import data/trips.csv Trips
+.mode column
+.headers on
\ No newline at end of file
diff --git a/users/wpcarro/assessments/tt/shell.nix b/users/wpcarro/assessments/tt/shell.nix
new file mode 100644
index 0000000000..bf8486ba1d
--- /dev/null
+++ b/users/wpcarro/assessments/tt/shell.nix
@@ -0,0 +1,18 @@
+{ pkgs, depot, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    (haskellPackages.ghcWithPackages (hpkgs: with hpkgs; [
+      hpkgs.aeson
+      hpkgs.cryptonite
+      hpkgs.envy
+      hpkgs.hailgun
+      hpkgs.resource-pool
+      hpkgs.servant-server
+      hpkgs.sqlite-simple
+      hpkgs.uuid
+      hpkgs.wai-cors
+      hpkgs.warp
+    ]))
+  ];
+}
diff --git a/users/wpcarro/assessments/tt/src/.ghci b/users/wpcarro/assessments/tt/src/.ghci
new file mode 100644
index 0000000000..efc88e630c
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/.ghci
@@ -0,0 +1,2 @@
+:set prompt "> "
+:set -Wall
diff --git a/users/wpcarro/assessments/tt/src/API.hs b/users/wpcarro/assessments/tt/src/API.hs
new file mode 100644
index 0000000000..471fa761e0
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/API.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeOperators #-}
+--------------------------------------------------------------------------------
+module API where
+--------------------------------------------------------------------------------
+import Data.Text
+import Servant.API
+import Web.Cookie
+
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+-- | Once authenticated, users receive a SessionCookie.
+type SessionCookie = Header' '[Required] "Cookie" T.SessionCookie
+
+type API =
+      -- accounts: Create
+           "accounts"
+           :> Header "Cookie" T.SessionCookie
+           :> ReqBody '[JSON] T.CreateAccountRequest
+           :> Post '[JSON] NoContent
+      :<|> "verify"
+           :> ReqBody '[JSON] T.VerifyAccountRequest
+           :> Post '[JSON] NoContent
+      -- accounts: Read
+      -- accounts: Update
+      -- accounts: Delete
+      :<|> "accounts"
+           :> SessionCookie
+           :> QueryParam' '[Required] "username" Text
+           :> Delete '[JSON] NoContent
+      -- accounts: List
+      :<|> "accounts"
+           :> SessionCookie
+           :> Get '[JSON] [T.User]
+
+      -- trips: Create
+      :<|> "trips"
+           :> SessionCookie
+           :> ReqBody '[JSON] T.Trip
+           :> Post '[JSON] NoContent
+      -- trips: Read
+      -- trips: Update
+      :<|> "trips"
+           :> SessionCookie
+           :> ReqBody '[JSON] T.UpdateTripRequest
+           :> Put '[JSON] NoContent
+      -- trips: Delete
+      :<|> "trips"
+           :> SessionCookie
+           :> ReqBody '[JSON] T.TripPK
+           :> Delete '[JSON] NoContent
+      -- trips: List
+      :<|> "trips"
+           :> SessionCookie
+           :> Get '[JSON] [T.Trip]
+
+      -- Miscellaneous
+      :<|> "login"
+           :> ReqBody '[JSON] T.AccountCredentials
+           :> Post '[JSON] (Headers '[Header "Set-Cookie" SetCookie] T.Session)
+      :<|> "logout"
+           :> SessionCookie
+           :> Get '[JSON] (Headers '[Header "Set-Cookie" SetCookie] NoContent)
+      :<|> "unfreeze"
+           :> SessionCookie
+           :> ReqBody '[JSON] T.UnfreezeAccountRequest
+           :> Post '[JSON] NoContent
+      :<|> "invite"
+           :> SessionCookie
+           :> ReqBody '[JSON] T.InviteUserRequest
+           :> Post '[JSON] NoContent
+      :<|> "accept-invitation"
+           :> ReqBody '[JSON] T.AcceptInvitationRequest
+           :> Post '[JSON] NoContent
diff --git a/users/wpcarro/assessments/tt/src/Accounts.hs b/users/wpcarro/assessments/tt/src/Accounts.hs
new file mode 100644
index 0000000000..c7ab7a2f13
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/Accounts.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE OverloadedStrings #-}
+--------------------------------------------------------------------------------
+module Accounts where
+--------------------------------------------------------------------------------
+import Database.SQLite.Simple
+
+import qualified PendingAccounts
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+-- | Delete the account in PendingAccounts and create on in Accounts.
+transferFromPending :: FilePath -> T.PendingAccount -> IO ()
+transferFromPending dbFile T.PendingAccount{..} = withConnection dbFile $
+  \conn -> withTransaction conn $ do
+    PendingAccounts.delete dbFile pendingAccountUsername
+    execute conn "INSERT INTO Accounts (username,password,email,role) VALUES (?,?,?,?)"
+      ( pendingAccountUsername
+      , pendingAccountPassword
+      , pendingAccountEmail
+      , pendingAccountRole
+      )
+
+-- | Create a new account in the Accounts table.
+create :: FilePath -> T.Username -> T.ClearTextPassword -> T.Email -> T.Role -> IO ()
+create dbFile username password email role = withConnection dbFile $ \conn -> do
+  hashed <- T.hashPassword password
+  execute conn "INSERT INTO Accounts (username,password,email,role) VALUES (?,?,?,?)"
+    (username, hashed, email, role)
+
+-- | Delete `username` from `dbFile`.
+delete :: FilePath -> T.Username -> IO ()
+delete dbFile username = withConnection dbFile $ \conn -> do
+  execute conn "DELETE FROM Accounts WHERE username = ?"
+    (Only username)
+
+-- | Attempt to find `username` in the Account table of `dbFile`.
+lookup :: FilePath -> T.Username -> IO (Maybe T.Account)
+lookup dbFile username = withConnection dbFile $ \conn -> do
+  res <- query conn "SELECT username,password,email,role,profilePicture FROM Accounts WHERE username = ?" (Only username)
+  case res of
+    [x] -> pure (Just x)
+    _ -> pure Nothing
+
+-- | Return a list of accounts with the sensitive data removed.
+list :: FilePath -> IO [T.User]
+list dbFile = withConnection dbFile $ \conn -> do
+  accounts <- query_ conn "SELECT username,password,email,role,profilePicture FROM Accounts"
+  pure $ T.userFromAccount <$> accounts
diff --git a/users/wpcarro/assessments/tt/src/App.hs b/users/wpcarro/assessments/tt/src/App.hs
new file mode 100644
index 0000000000..742bc962dc
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/App.hs
@@ -0,0 +1,270 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE TypeApplications #-}
+--------------------------------------------------------------------------------
+module App where
+--------------------------------------------------------------------------------
+import Control.Monad.IO.Class (liftIO)
+import Data.String.Conversions (cs)
+import Data.Text (Text)
+import Servant
+import API
+import Utils
+import Web.Cookie
+
+import qualified Network.Wai.Handler.Warp as Warp
+import qualified Network.Wai.Middleware.Cors as Cors
+import qualified System.Random as Random
+import qualified Email as Email
+import qualified Data.UUID as UUID
+import qualified Types as T
+import qualified Accounts as Accounts
+import qualified Auth as Auth
+import qualified Trips as Trips
+import qualified Sessions as Sessions
+import qualified Invitations as Invitations
+import qualified LoginAttempts as LoginAttempts
+import qualified PendingAccounts as PendingAccounts
+--------------------------------------------------------------------------------
+
+err429 :: ServerError
+err429 = ServerError
+  { errHTTPCode = 429
+  , errReasonPhrase = "Too many requests"
+  , errBody = ""
+  , errHeaders = []
+  }
+
+-- | Send an email to recipient, `to`, with a secret code.
+sendVerifyEmail :: T.Config
+                -> T.Username
+                -> T.Email
+                -> T.RegistrationSecret
+                -> IO (Either Email.SendError Email.SendSuccess)
+sendVerifyEmail T.Config{..} (T.Username username) email (T.RegistrationSecret secretUUID) = do
+  Email.send mailgunAPIKey subject (cs body) email
+  where
+    subject = "Please confirm your account"
+    body =
+      let secret = secretUUID |> UUID.toString in
+        "To verify your account: POST /verify username=" ++ cs username ++ " secret=" ++ secret
+
+-- | Send an invitation email to recipient, `to`, with a secret code.
+sendInviteEmail :: T.Config
+                -> T.Email
+                -> T.InvitationSecret
+                -> IO (Either Email.SendError Email.SendSuccess)
+sendInviteEmail T.Config{..} email@(T.Email to) (T.InvitationSecret secretUUID) = do
+  Email.send mailgunAPIKey subject (cs body) email
+  where
+    subject = "You've been invited!"
+    body =
+      let secret = secretUUID |> UUID.toString in
+        "To accept the invitation: POST /accept-invitation username=<username> password=<password> email=" ++ cs to ++ " secret=" ++ secret
+
+server :: T.Config -> Server API
+server config@T.Config{..} = createAccount
+                        :<|> verifyAccount
+                        :<|> deleteAccount
+                        :<|> listAccounts
+                        :<|> createTrip
+                        :<|> updateTrip
+                        :<|> deleteTrip
+                        :<|> listTrips
+                        :<|> login
+                        :<|> logout
+                        :<|> unfreezeAccount
+                        :<|> inviteUser
+                        :<|> acceptInvitation
+  where
+    -- Admit Admins + whatever the predicate `p` passes.
+    adminsAnd cookie p = Auth.assert dbFile cookie (\acct@T.Account{..} -> accountRole == T.Admin || p acct)
+    -- Admit Admins only.
+    adminsOnly cookie = adminsAnd cookie (const True)
+
+    -- TODO(wpcarro): Handle failed CONSTRAINTs instead of sending 500s
+    createAccount :: Maybe T.SessionCookie
+                  -> T.CreateAccountRequest
+                  -> Handler NoContent
+    createAccount mCookie T.CreateAccountRequest{..} =
+      case (mCookie, createAccountRequestRole) of
+        (_, T.RegularUser) ->
+          doCreateAccount
+        (Nothing, T.Manager) ->
+          throwError err401 { errBody = "Only admins can create Manager accounts" }
+        (Nothing, T.Admin) ->
+          throwError err401 { errBody = "Only admins can create Admin accounts" }
+        (Just cookie, _) ->
+          adminsAnd cookie (\T.Account{..} -> accountRole == T.Manager) doCreateAccount
+      where
+        doCreateAccount :: Handler NoContent
+        doCreateAccount = do
+          secretUUID <- liftIO $ T.RegistrationSecret <$> Random.randomIO
+          liftIO $ PendingAccounts.create dbFile
+            secretUUID
+            createAccountRequestUsername
+            createAccountRequestPassword
+            createAccountRequestRole
+            createAccountRequestEmail
+          res <- liftIO $ sendVerifyEmail config
+            createAccountRequestUsername
+            createAccountRequestEmail
+            secretUUID
+          case res of
+            Left _ -> undefined
+            Right _ -> pure NoContent
+
+    verifyAccount :: T.VerifyAccountRequest -> Handler NoContent
+    verifyAccount T.VerifyAccountRequest{..} = do
+      mPendingAccount <- liftIO $ PendingAccounts.get dbFile verifyAccountRequestUsername
+      case mPendingAccount of
+        Nothing ->
+          throwError err401 { errBody = "Either your secret or your username (or both) is invalid" }
+        Just pendingAccount@T.PendingAccount{..} ->
+          if pendingAccountSecret == verifyAccountRequestSecret then do
+            liftIO $ Accounts.transferFromPending dbFile pendingAccount
+            pure NoContent
+          else
+            throwError err401 { errBody = "The secret you provided is invalid" }
+
+    deleteAccount :: T.SessionCookie -> Text -> Handler NoContent
+    deleteAccount cookie username = adminsOnly cookie $ do
+      liftIO $ Accounts.delete dbFile (T.Username username)
+      pure NoContent
+
+    listAccounts :: T.SessionCookie -> Handler [T.User]
+    listAccounts cookie = adminsOnly cookie $ do
+      liftIO $ Accounts.list dbFile
+
+    createTrip :: T.SessionCookie -> T.Trip -> Handler NoContent
+    createTrip cookie trip@T.Trip{..} =
+      adminsAnd cookie (\T.Account{..} -> accountUsername == tripUsername) $ do
+        liftIO $ Trips.create dbFile trip
+        pure NoContent
+
+    updateTrip :: T.SessionCookie -> T.UpdateTripRequest -> Handler NoContent
+    updateTrip cookie updates@T.UpdateTripRequest{..} =
+      adminsAnd cookie (\T.Account{..} -> accountUsername == T.tripPKUsername updateTripRequestTripPK) $ do
+        mTrip <- liftIO $ Trips.get dbFile updateTripRequestTripPK
+        case mTrip of
+          Nothing -> throwError err400 { errBody = "tripKey is invalid" }
+          Just trip@T.Trip{..} -> do
+            -- TODO(wpcarro): Prefer function in Trips module that does this in a
+            -- DB transaction.
+            liftIO $ Trips.delete dbFile updateTripRequestTripPK
+            liftIO $ Trips.create dbFile (T.updateTrip updates trip)
+            pure NoContent
+
+    deleteTrip :: T.SessionCookie -> T.TripPK -> Handler NoContent
+    deleteTrip cookie tripPK@T.TripPK{..} =
+      adminsAnd cookie (\T.Account{..} -> accountUsername == tripPKUsername) $ do
+      liftIO $ Trips.delete dbFile tripPK
+      pure NoContent
+
+    listTrips :: T.SessionCookie -> Handler [T.Trip]
+    listTrips cookie = do
+      mAccount <- liftIO $ Auth.accountFromCookie dbFile cookie
+      case mAccount of
+        Nothing -> throwError err401 { errBody = "Your session cookie is invalid. Try logging out and logging back in." }
+        Just T.Account{..} ->
+          case accountRole of
+            T.Admin -> liftIO $ Trips.listAll dbFile
+            _ -> liftIO $ Trips.list dbFile accountUsername
+
+    login :: T.AccountCredentials
+          -> Handler (Headers '[Header "Set-Cookie" SetCookie] T.Session)
+    login (T.AccountCredentials username password) = do
+      mAccount <- liftIO $ Accounts.lookup dbFile username
+      case mAccount of
+        Just account@T.Account{..} -> do
+          mAttempts <- liftIO $ LoginAttempts.forUsername dbFile accountUsername
+          case mAttempts of
+            Nothing ->
+              if T.passwordsMatch password accountPassword then do
+                uuid <- liftIO $ Sessions.findOrCreate dbFile account
+                pure $ addHeader (Auth.mkCookie uuid)
+                  T.Session{ sessionUsername = accountUsername
+                           , sessionRole = accountRole
+                           }
+              else do
+                liftIO $ LoginAttempts.increment dbFile username
+                throwError err401 { errBody = "Your credentials are invalid" }
+            Just attempts ->
+              if attempts >= 3 then
+                throwError err429
+              else if T.passwordsMatch password accountPassword then do
+                uuid <- liftIO $ Sessions.findOrCreate dbFile account
+                pure $ addHeader (Auth.mkCookie uuid)
+                  T.Session{ sessionUsername = accountUsername
+                           , sessionRole = accountRole
+                           }
+              else do
+                liftIO $ LoginAttempts.increment dbFile username
+                throwError err401 { errBody = "Your credentials are invalid" }
+
+        -- In this branch, the user didn't supply a known username.
+        Nothing -> throwError err401 { errBody = "Your credentials are invalid" }
+
+    logout :: T.SessionCookie
+           -> Handler (Headers '[Header "Set-Cookie" SetCookie] NoContent)
+    logout cookie = do
+      case Auth.uuidFromCookie cookie of
+        Nothing ->
+          pure $ addHeader Auth.emptyCookie NoContent
+        Just uuid -> do
+          liftIO $ Sessions.delete dbFile uuid
+          pure $ addHeader Auth.emptyCookie NoContent
+
+    unfreezeAccount :: T.SessionCookie
+                    -> T.UnfreezeAccountRequest
+                    -> Handler NoContent
+    unfreezeAccount cookie T.UnfreezeAccountRequest{..} =
+      adminsAnd cookie (\T.Account{..} -> accountRole == T.Manager) $ do
+        liftIO $ LoginAttempts.reset dbFile unfreezeAccountRequestUsername
+        pure NoContent
+
+    inviteUser :: T.SessionCookie
+               -> T.InviteUserRequest
+               -> Handler NoContent
+    inviteUser cookie T.InviteUserRequest{..} = adminsOnly cookie $ do
+      secretUUID <- liftIO $ T.InvitationSecret <$> Random.randomIO
+      liftIO $ Invitations.create dbFile
+        secretUUID
+        inviteUserRequestEmail
+        inviteUserRequestRole
+      res <- liftIO $ sendInviteEmail config inviteUserRequestEmail secretUUID
+      case res of
+        Left _ -> undefined
+        Right _ -> pure NoContent
+
+    acceptInvitation :: T.AcceptInvitationRequest -> Handler NoContent
+    acceptInvitation T.AcceptInvitationRequest{..} = do
+      mInvitation <- liftIO $ Invitations.get dbFile acceptInvitationRequestEmail
+      case mInvitation of
+        Nothing -> throwError err404 { errBody = "No invitation for email" }
+        Just T.Invitation{..} ->
+          if invitationSecret == acceptInvitationRequestSecret then do
+            liftIO $ Accounts.create dbFile
+              acceptInvitationRequestUsername
+              acceptInvitationRequestPassword
+              invitationEmail
+              invitationRole
+            pure NoContent
+          else
+            throwError err401 { errBody = "You are not providing a valid secret" }
+
+run :: T.Config -> IO ()
+run config@T.Config{..} =
+  Warp.run 3000 (enforceCors $ serve (Proxy @ API) $ server config)
+  where
+    enforceCors = Cors.cors (const $ Just corsPolicy)
+    corsPolicy :: Cors.CorsResourcePolicy
+    corsPolicy =
+      Cors.simpleCorsResourcePolicy
+        { Cors.corsOrigins = Just ([cs configClient], True)
+        , Cors.corsMethods = Cors.simpleMethods ++ ["PUT", "PATCH", "DELETE", "OPTIONS"]
+        , Cors.corsRequestHeaders = Cors.simpleHeaders ++ ["Content-Type", "Authorization"]
+        }
diff --git a/users/wpcarro/assessments/tt/src/Auth.hs b/users/wpcarro/assessments/tt/src/Auth.hs
new file mode 100644
index 0000000000..f1bff23257
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/Auth.hs
@@ -0,0 +1,64 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+--------------------------------------------------------------------------------
+module Auth where
+--------------------------------------------------------------------------------
+import Control.Monad.IO.Class (liftIO)
+import Web.Cookie
+import Servant
+
+import qualified Data.UUID as UUID
+import qualified Sessions as Sessions
+import qualified Accounts as Accounts
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+-- | Return the UUID from a Session cookie.
+uuidFromCookie :: T.SessionCookie -> Maybe T.SessionUUID
+uuidFromCookie (T.SessionCookie cookies) = do
+  auth <- lookup "auth" cookies
+  uuid <- UUID.fromASCIIBytes auth
+  pure $ T.SessionUUID uuid
+
+-- | Attempt to return the account associated with `cookie`.
+accountFromCookie :: FilePath -> T.SessionCookie -> IO (Maybe T.Account)
+accountFromCookie dbFile cookie =
+  case uuidFromCookie cookie of
+    Nothing -> pure Nothing
+    Just uuid -> do
+      mSession <- Sessions.get dbFile uuid
+      case mSession of
+        Nothing -> pure Nothing
+        Just T.StoredSession{..} -> do
+          mAccount <- Accounts.lookup dbFile storedSessionUsername
+          case mAccount of
+            Nothing -> pure Nothing
+            Just x -> pure (Just x)
+
+-- | Create a new session cookie.
+mkCookie :: T.SessionUUID -> SetCookie
+mkCookie (T.SessionUUID uuid) =
+  defaultSetCookie
+    { setCookieName = "auth"
+    , setCookieValue = UUID.toASCIIBytes uuid
+    }
+
+-- | Use this to clear out the session cookie.
+emptyCookie :: SetCookie
+emptyCookie =
+  defaultSetCookie
+    { setCookieName = "auth"
+    , setCookieValue = ""
+    }
+
+-- | Throw a 401 error if the `predicate` fails.
+assert :: FilePath -> T.SessionCookie -> (T.Account -> Bool) -> Handler a -> Handler a
+assert dbFile cookie predicate handler = do
+  mRole <- liftIO $ accountFromCookie dbFile cookie
+  case mRole of
+    Nothing -> throwError err401 { errBody = "Missing valid session cookie" }
+    Just account ->
+      if predicate account then
+        handler
+      else
+        throwError err401 { errBody = "You are not authorized to access this resource" }
diff --git a/users/wpcarro/assessments/tt/src/Email.hs b/users/wpcarro/assessments/tt/src/Email.hs
new file mode 100644
index 0000000000..2dac0973ba
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/Email.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE OverloadedStrings #-}
+--------------------------------------------------------------------------------
+module Email where
+--------------------------------------------------------------------------------
+import Data.Text
+import Data.String.Conversions (cs)
+import Utils
+
+import qualified Mail.Hailgun as MG
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+newtype SendSuccess = SendSuccess MG.HailgunSendResponse
+
+data SendError
+  = MessageError MG.HailgunErrorMessage
+  | ResponseError MG.HailgunErrorResponse
+
+-- | Attempt to send an email with `subject` and with message, `body`.
+send :: Text
+     -> Text
+     -> Text
+     -> T.Email
+     -> IO (Either SendError SendSuccess)
+send apiKey subject body (T.Email to) = do
+  case mkMsg of
+    Left e -> pure $ Left (MessageError e)
+    Right x -> do
+      res <- MG.sendEmail ctx x
+      case res of
+        Left e -> pure $ Left (ResponseError e)
+        Right y -> pure $ Right (SendSuccess y)
+  where
+    ctx = MG.HailgunContext { MG.hailgunDomain = "sandboxda5038873f924b50af2f82a0f05cffdf.mailgun.org"
+                            , MG.hailgunApiKey = cs apiKey
+                            , MG.hailgunProxy = Nothing
+                            }
+    mkMsg = MG.hailgunMessage
+            subject
+            (body |> cs |> MG.TextOnly)
+            "mailgun@sandboxda5038873f924b50af2f82a0f05cffdf.mailgun.org"
+            (MG.MessageRecipients { MG.recipientsTo = [cs to]
+                                  , MG.recipientsCC = []
+                                  , MG.recipientsBCC = []
+                                  })
+            []
diff --git a/users/wpcarro/assessments/tt/src/Invitations.hs b/users/wpcarro/assessments/tt/src/Invitations.hs
new file mode 100644
index 0000000000..0c700470f3
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/Invitations.hs
@@ -0,0 +1,21 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+--------------------------------------------------------------------------------
+module Invitations where
+--------------------------------------------------------------------------------
+import Database.SQLite.Simple
+
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+create :: FilePath -> T.InvitationSecret -> T.Email -> T.Role -> IO ()
+create dbFile secret email role = withConnection dbFile $ \conn -> do
+  execute conn "INSERT INTO Invitations (email,role,secret) VALUES (?,?,?)"
+    (email, role, secret)
+
+get :: FilePath -> T.Email -> IO (Maybe T.Invitation)
+get dbFile email = withConnection dbFile $ \conn -> do
+  res <- query conn "SELECT email,role,secret FROM Invitations WHERE email = ?" (Only email)
+  case res of
+    [x] -> pure (Just x)
+    _ -> pure Nothing
diff --git a/users/wpcarro/assessments/tt/src/LoginAttempts.hs b/users/wpcarro/assessments/tt/src/LoginAttempts.hs
new file mode 100644
index 0000000000..d78e12e3fd
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/LoginAttempts.hs
@@ -0,0 +1,30 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+--------------------------------------------------------------------------------
+module LoginAttempts where
+--------------------------------------------------------------------------------
+import Database.SQLite.Simple
+
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+reset :: FilePath -> T.Username -> IO ()
+reset dbFile username = withConnection dbFile $ \conn ->
+  execute conn "UPDATE LoginAttempts SET numAttempts = 0 WHERE username = ?"
+    (Only username)
+
+-- | Attempt to return the number of failed login attempts for
+-- `username`. Returns a Maybe in case `username` doesn't exist.
+forUsername :: FilePath -> T.Username -> IO (Maybe Integer)
+forUsername dbFile username = withConnection dbFile $ \conn -> do
+  res <- query conn "SELECT username,numAttempts FROM LoginAttempts WHERE username = ?"
+    (Only username)
+  case res of
+    [T.LoginAttempt{..}] -> pure (Just loginAttemptNumAttempts)
+    _  -> pure Nothing
+
+-- | INSERT a failed login attempt for `username` or UPDATE an existing entry.
+increment :: FilePath -> T.Username -> IO ()
+increment dbFile username = withConnection dbFile $ \conn ->
+  execute conn "INSERT INTO LoginAttempts (username,numAttempts) VALUES (?,?) ON CONFLICT (username) DO UPDATE SET numAttempts = numAttempts + 1"
+    (username, 1 :: Integer)
diff --git a/users/wpcarro/assessments/tt/src/Main.hs b/users/wpcarro/assessments/tt/src/Main.hs
new file mode 100644
index 0000000000..9df4232066
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/Main.hs
@@ -0,0 +1,13 @@
+--------------------------------------------------------------------------------
+module Main where
+--------------------------------------------------------------------------------
+import qualified App
+import qualified System.Envy as Envy
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = do
+  mEnv <- Envy.decodeEnv
+  case mEnv of
+    Left err -> putStrLn err
+    Right env -> App.run env
diff --git a/users/wpcarro/assessments/tt/src/PendingAccounts.hs b/users/wpcarro/assessments/tt/src/PendingAccounts.hs
new file mode 100644
index 0000000000..a555185fa7
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/PendingAccounts.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+--------------------------------------------------------------------------------
+module PendingAccounts where
+--------------------------------------------------------------------------------
+import Database.SQLite.Simple
+
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+create :: FilePath
+       -> T.RegistrationSecret
+       -> T.Username
+       -> T.ClearTextPassword
+       -> T.Role
+       -> T.Email
+       -> IO ()
+create dbFile secret username password role email = withConnection dbFile $ \conn -> do
+  hashed <- T.hashPassword password
+  execute conn "INSERT INTO PendingAccounts (secret,username,password,role,email) VALUES (?,?,?,?,?)"
+    (secret, username, hashed, role, email)
+
+get :: FilePath -> T.Username -> IO (Maybe T.PendingAccount)
+get dbFile username = withConnection dbFile $ \conn -> do
+  res <- query conn "SELECT secret,username,password,role,email FROM PendingAccounts WHERE username = ?" (Only username)
+  case res of
+    [x] -> pure (Just x)
+    _ -> pure Nothing
+
+delete :: FilePath -> T.Username -> IO ()
+delete dbFile username = withConnection dbFile $ \conn ->
+  execute conn "DELETE FROM PendingAccounts WHERE username = ?" (Only username)
diff --git a/users/wpcarro/assessments/tt/src/Sessions.hs b/users/wpcarro/assessments/tt/src/Sessions.hs
new file mode 100644
index 0000000000..713059a383
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/Sessions.hs
@@ -0,0 +1,74 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+--------------------------------------------------------------------------------
+module Sessions where
+--------------------------------------------------------------------------------
+import Database.SQLite.Simple
+
+import qualified Data.Time.Clock as Clock
+import qualified Types as T
+import qualified System.Random as Random
+--------------------------------------------------------------------------------
+
+-- | Return True if `session` was created at most three hours ago.
+isValid :: T.StoredSession -> IO Bool
+isValid session = do
+  t1 <- Clock.getCurrentTime
+  let t0 = T.storedSessionTsCreated session in
+    pure $ Clock.diffUTCTime t1 t0 <= 3 * 60 * 60
+
+-- | Lookup the session by UUID.
+get :: FilePath -> T.SessionUUID -> IO (Maybe T.StoredSession)
+get dbFile uuid = withConnection dbFile $ \conn -> do
+  res <- query conn "SELECT uuid,username,tsCreated FROM Sessions WHERE uuid = ?" (Only uuid)
+  case res of
+    [x] -> pure (Just x)
+    _ -> pure Nothing
+
+-- | Lookup the session stored under `username` in `dbFile`.
+find :: FilePath -> T.Username -> IO (Maybe T.StoredSession)
+find dbFile username = withConnection dbFile $ \conn -> do
+  res <- query conn "SELECT uuid,username,tsCreated FROM Sessions WHERE username = ?" (Only username)
+  case res of
+    [x] -> pure (Just x)
+    _ -> pure Nothing
+
+-- | Create a session under the `username` key in `dbFile`.
+create :: FilePath -> T.Username -> IO T.SessionUUID
+create dbFile username = withConnection dbFile $ \conn -> do
+  now <- Clock.getCurrentTime
+  uuid <- Random.randomIO
+  execute conn "INSERT INTO Sessions (uuid,username,tsCreated) VALUES (?,?,?)"
+    (T.SessionUUID uuid, username, now)
+  pure (T.SessionUUID uuid)
+
+-- | Reset the tsCreated field to the current time to ensure the token is valid.
+refresh :: FilePath -> T.SessionUUID -> IO ()
+refresh dbFile uuid = withConnection dbFile $ \conn -> do
+  now <- Clock.getCurrentTime
+  execute conn "UPDATE Sessions SET tsCreated = ? WHERE uuid = ?"
+    (now, uuid)
+  pure ()
+
+-- | Delete the session under `username` from `dbFile`.
+delete :: FilePath -> T.SessionUUID -> IO ()
+delete dbFile uuid = withConnection dbFile $ \conn ->
+  execute conn "DELETE FROM Sessions WHERE uuid = ?" (Only uuid)
+
+-- | Find or create a session in the Sessions table. If a session exists,
+-- refresh the token's validity.
+findOrCreate :: FilePath -> T.Account -> IO T.SessionUUID
+findOrCreate dbFile account =
+  let username = T.accountUsername account in do
+    mSession <- find dbFile username
+    case mSession of
+      Nothing -> create dbFile username
+      Just session ->
+        let uuid = T.storedSessionUUID session in do
+          refresh dbFile uuid
+          pure uuid
+
+-- | Return a list of all sessions in the Sessions table.
+list :: FilePath -> IO [T.StoredSession]
+list dbFile = withConnection dbFile $ \conn ->
+  query_ conn "SELECT uuid,username,tsCreated FROM Sessions"
diff --git a/users/wpcarro/assessments/tt/src/Trips.hs b/users/wpcarro/assessments/tt/src/Trips.hs
new file mode 100644
index 0000000000..f90740363c
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/Trips.hs
@@ -0,0 +1,42 @@
+{-# LANGUAGE OverloadedStrings #-}
+--------------------------------------------------------------------------------
+module Trips where
+--------------------------------------------------------------------------------
+import Database.SQLite.Simple
+import Utils
+
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+-- | Create a new `trip` in `dbFile`.
+create :: FilePath -> T.Trip -> IO ()
+create dbFile trip = withConnection dbFile $ \conn ->
+  execute conn "INSERT INTO Trips (username,destination,startDate,endDate,comment) VALUES (?,?,?,?,?)"
+    (trip |> T.tripFields)
+
+-- | Attempt to get the trip record from `dbFile` under `tripKey`.
+get :: FilePath -> T.TripPK -> IO (Maybe T.Trip)
+get dbFile tripKey = withConnection dbFile $ \conn -> do
+  res <- query conn "SELECT username,destination,startDate,endDate,comment FROM Trips WHERE username = ? AND destination = ? AND startDate = ? LIMIT 1"
+    (T.tripPKFields tripKey)
+  case res of
+    [x] -> pure (Just x)
+    _ -> pure Nothing
+
+-- | Delete a trip from `dbFile` using its `tripKey` Primary Key.
+delete :: FilePath -> T.TripPK -> IO ()
+delete dbFile tripKey =
+  withConnection dbFile $ \conn -> do
+    execute conn "DELETE FROM Trips WHERE username = ? AND destination = ? and startDate = ?"
+      (T.tripPKFields tripKey)
+
+-- | Return a list of all of the trips in `dbFile`.
+listAll :: FilePath -> IO [T.Trip]
+listAll dbFile = withConnection dbFile $ \conn ->
+  query_ conn "SELECT username,destination,startDate,endDate,comment FROM Trips ORDER BY date(startDate) ASC"
+
+-- | Return a list of all of the trips in `dbFile`.
+list :: FilePath -> T.Username -> IO [T.Trip]
+list dbFile username = withConnection dbFile $ \conn ->
+  query conn "SELECT username,destination,startDate,endDate,comment FROM Trips WHERE username = ? ORDER BY date(startDate) ASC"
+    (Only username)
diff --git a/users/wpcarro/assessments/tt/src/Types.hs b/users/wpcarro/assessments/tt/src/Types.hs
new file mode 100644
index 0000000000..6b06a39694
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/Types.hs
@@ -0,0 +1,544 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE NamedFieldPuns #-}
+--------------------------------------------------------------------------------
+module Types where
+--------------------------------------------------------------------------------
+import Data.Aeson
+import Utils
+import Data.Text
+import Data.Typeable
+import Database.SQLite.Simple
+import Database.SQLite.Simple.Ok
+import Database.SQLite.Simple.FromField
+import Database.SQLite.Simple.ToField
+import GHC.Generics
+import Web.Cookie
+import Servant.API
+import System.Envy (FromEnv, fromEnv, env)
+import Crypto.Random.Types (MonadRandom)
+
+import qualified Data.Time.Calendar as Calendar
+import qualified Crypto.KDF.BCrypt as BC
+import qualified Data.Time.Clock as Clock
+import qualified Data.ByteString.Char8 as B
+import qualified Data.ByteString as BS
+import qualified Data.Text.Encoding as TE
+import qualified Data.Maybe as M
+import qualified Data.UUID as UUID
+--------------------------------------------------------------------------------
+
+-- | Top-level application configuration.
+data Config = Config
+  { mailgunAPIKey :: Text
+  , dbFile :: FilePath
+  , configClient :: Text
+  , configServer :: Text
+  } deriving (Eq, Show)
+
+instance FromEnv Config where
+  fromEnv _ = do
+    mailgunAPIKey <- env "MAILGUN_API_KEY"
+    dbFile <- env "DB_FILE"
+    configClient <- env "CLIENT"
+    configServer <- env "SERVER"
+    pure Config {..}
+
+-- TODO(wpcarro): Properly handle NULL for columns like profilePicture.
+forNewtype :: (Typeable b) => (Text -> b) -> FieldParser b
+forNewtype wrapper y =
+  case fieldData y of
+    (SQLText x) -> Ok (wrapper x)
+    x -> returnError ConversionFailed y ("We expected SQLText, but we received: " ++ show x)
+
+newtype Username = Username Text
+  deriving (Eq, Show, Generic)
+
+instance ToJSON Username
+instance FromJSON Username
+
+instance ToField Username where
+  toField (Username x) = SQLText x
+
+instance FromField Username where
+  fromField = forNewtype Username
+
+newtype HashedPassword = HashedPassword BS.ByteString
+  deriving (Eq, Show, Generic)
+
+instance ToField HashedPassword where
+  toField (HashedPassword x) = SQLText (TE.decodeUtf8 x)
+
+instance FromField HashedPassword where
+  fromField y =
+    case fieldData y of
+      (SQLText x) -> x |> TE.encodeUtf8 |> HashedPassword |> Ok
+      x -> returnError ConversionFailed y ("We expected SQLText, but we received: " ++ show x)
+
+newtype ClearTextPassword = ClearTextPassword Text
+  deriving (Eq, Show, Generic)
+
+instance ToJSON ClearTextPassword
+instance FromJSON ClearTextPassword
+
+instance ToField ClearTextPassword where
+  toField (ClearTextPassword x) = SQLText x
+
+instance FromField ClearTextPassword where
+  fromField = forNewtype ClearTextPassword
+
+newtype Email = Email Text
+  deriving (Eq, Show, Generic)
+
+instance ToJSON Email
+instance FromJSON Email
+
+instance ToField Email where
+  toField (Email x) = SQLText x
+
+instance FromField Email where
+  fromField = forNewtype Email
+
+data Role = RegularUser | Manager | Admin
+  deriving (Eq, Show, Generic)
+
+instance ToJSON Role where
+  toJSON RegularUser = "user"
+  toJSON Manager = "manager"
+  toJSON Admin = "admin"
+
+instance FromJSON Role where
+  parseJSON = withText "Role" $ \x ->
+    case x of
+      "user" -> pure RegularUser
+      "manager" -> pure Manager
+      "admin" -> pure Admin
+      _ -> fail "Expected \"user\" or \"manager\" or \"admin\""
+
+instance ToField Role where
+  toField RegularUser = SQLText "user"
+  toField Manager = SQLText "manager"
+  toField Admin = SQLText "admin"
+
+instance FromField Role where
+  fromField y =
+    case fieldData y of
+      (SQLText "user") -> Ok RegularUser
+      (SQLText "manager") -> Ok Manager
+      (SQLText "admin") -> Ok Admin
+      x -> returnError ConversionFailed y ("We expected user, manager, admin, but we received: " ++ show x)
+
+-- TODO(wpcarro): Prefer Data.ByteString instead of Text
+newtype ProfilePicture = ProfilePicture Text
+  deriving (Eq, Show, Generic)
+
+instance ToJSON ProfilePicture
+instance FromJSON ProfilePicture
+
+instance ToField ProfilePicture where
+  toField (ProfilePicture x) = SQLText x
+
+instance FromField ProfilePicture where
+  fromField = forNewtype ProfilePicture
+
+data Account = Account
+  { accountUsername :: Username
+  , accountPassword :: HashedPassword
+  , accountEmail :: Email
+  , accountRole :: Role
+  , accountProfilePicture :: Maybe ProfilePicture
+  } deriving (Eq, Show, Generic)
+
+-- | Return a tuple with all of the fields for an Account record to use for SQL.
+accountFields :: Account -> (Username, HashedPassword, Email, Role, Maybe ProfilePicture)
+accountFields (Account {..})
+  = ( accountUsername
+    , accountPassword
+    , accountEmail
+    , accountRole
+    , accountProfilePicture
+    )
+
+instance FromRow Account where
+  fromRow = do
+    accountUsername <- field
+    accountPassword <- field
+    accountEmail <- field
+    accountRole <- field
+    accountProfilePicture <- field
+    pure Account{..}
+
+data Session = Session
+  { sessionUsername :: Username
+  , sessionRole :: Role
+  } deriving (Eq, Show)
+
+instance ToJSON Session where
+  toJSON (Session username role) =
+    object [ "username" .= username
+           , "role" .= role
+           ]
+
+newtype Comment = Comment Text
+  deriving (Eq, Show, Generic)
+
+instance ToJSON Comment
+instance FromJSON Comment
+
+instance ToField Comment where
+  toField (Comment x) = SQLText x
+
+instance FromField Comment where
+  fromField = forNewtype Comment
+
+newtype Destination = Destination Text
+  deriving (Eq, Show, Generic)
+
+instance ToJSON Destination
+instance FromJSON Destination
+
+instance ToField Destination where
+  toField (Destination x) = SQLText x
+
+instance FromField Destination where
+  fromField = forNewtype Destination
+
+newtype Year = Year Integer deriving (Eq, Show)
+newtype Month = Month Integer deriving (Eq, Show)
+newtype Day = Day Integer deriving (Eq, Show)
+data Date = Date
+  { dateYear :: Year
+  , dateMonth :: Month
+  , dateDay :: Day
+  } deriving (Eq, Show)
+
+data Trip = Trip
+  { tripUsername :: Username
+  , tripDestination :: Destination
+  , tripStartDate :: Calendar.Day
+  , tripEndDate :: Calendar.Day
+  , tripComment :: Comment
+  } deriving (Eq, Show, Generic)
+
+instance FromRow Trip where
+  fromRow = do
+    tripUsername <- field
+    tripDestination <- field
+    tripStartDate <- field
+    tripEndDate <- field
+    tripComment <- field
+    pure Trip{..}
+
+-- | The fields used as the Primary Key for a Trip entry.
+data TripPK = TripPK
+  { tripPKUsername :: Username
+  , tripPKDestination :: Destination
+  , tripPKStartDate :: Calendar.Day
+  } deriving (Eq, Show, Generic)
+
+tripPKFields :: TripPK -> (Username, Destination, Calendar.Day)
+tripPKFields (TripPK{..})
+  = (tripPKUsername, tripPKDestination, tripPKStartDate)
+
+instance FromJSON TripPK where
+  parseJSON = withObject "TripPK" $ \x -> do
+    tripPKUsername    <- x .: "username"
+    tripPKDestination <- x .: "destination"
+    tripPKStartDate   <- x .: "startDate"
+    pure TripPK{..}
+
+-- | Return the tuple representation of a Trip record for SQL.
+tripFields :: Trip
+           -> (Username, Destination, Calendar.Day, Calendar.Day, Comment)
+tripFields (Trip{..})
+  = ( tripUsername
+    , tripDestination
+    , tripStartDate
+    , tripEndDate
+    , tripComment
+    )
+
+instance ToJSON Trip where
+  toJSON (Trip username destination startDate endDate comment) =
+    object [ "username" .= username
+           , "destination" .= destination
+           , "startDate" .= startDate
+           , "endDate" .= endDate
+           , "comment" .= comment
+           ]
+
+instance FromJSON Trip where
+  parseJSON = withObject "Trip" $ \x -> do
+    tripUsername    <- x .: "username"
+    tripDestination <- x .: "destination"
+    tripStartDate   <- x .: "startDate"
+    tripEndDate     <- x .: "endDate"
+    tripComment     <- x .: "comment"
+    pure Trip{..}
+
+-- | Users and Accounts both refer to the same underlying entities; however,
+-- Users model the user-facing Account details, hiding sensitive details like
+-- passwords and emails.
+data User = User
+  { userUsername :: Username
+  , userProfilePicture :: Maybe ProfilePicture
+  , userRole :: Role
+  } deriving (Eq, Show, Generic)
+
+instance ToJSON User where
+  toJSON (User username profilePicture role) =
+    object [ "username" .= username
+           , "profilePicture" .= profilePicture
+           , "role" .= role
+           ]
+
+userFromAccount :: Account -> User
+userFromAccount account =
+  User { userUsername = accountUsername account
+       , userProfilePicture = accountProfilePicture account
+       , userRole = accountRole account
+       }
+
+-- | This is the data that a user needs to supply to authenticate with the
+-- application.
+data AccountCredentials = AccountCredentials
+  { accountCredentialsUsername :: Username
+  , accountCredentialsPassword :: ClearTextPassword
+  } deriving (Eq, Show, Generic)
+
+instance FromJSON AccountCredentials where
+  parseJSON = withObject "AccountCredentials" $ \x -> do
+    accountCredentialsUsername <- x.: "username"
+    accountCredentialsPassword <- x.: "password"
+    pure AccountCredentials{..}
+
+
+-- | Hash password `x`.
+hashPassword :: (MonadRandom m) => ClearTextPassword -> m HashedPassword
+hashPassword (ClearTextPassword x) = do
+  hashed <- BC.hashPassword 12 (x |> unpack |> B.pack)
+  pure $ HashedPassword hashed
+
+-- | Return True if the cleartext password matches the hashed password.
+passwordsMatch :: ClearTextPassword -> HashedPassword -> Bool
+passwordsMatch (ClearTextPassword clear) (HashedPassword hashed) =
+  BC.validatePassword (clear |> unpack |> B.pack) hashed
+
+data CreateAccountRequest = CreateAccountRequest
+  { createAccountRequestUsername :: Username
+  , createAccountRequestPassword :: ClearTextPassword
+  , createAccountRequestEmail :: Email
+  , createAccountRequestRole :: Role
+  } deriving (Eq, Show)
+
+instance FromJSON CreateAccountRequest where
+  parseJSON = withObject "CreateAccountRequest" $ \x -> do
+    createAccountRequestUsername <- x .: "username"
+    createAccountRequestPassword <- x .: "password"
+    createAccountRequestEmail <- x .: "email"
+    createAccountRequestRole <- x .: "role"
+    pure $ CreateAccountRequest{..}
+
+createAccountRequestFields :: CreateAccountRequest
+                           -> (Username, ClearTextPassword, Email, Role)
+createAccountRequestFields CreateAccountRequest{..} =
+  ( createAccountRequestUsername
+  , createAccountRequestPassword
+  , createAccountRequestEmail
+  , createAccountRequestRole
+  )
+
+newtype SessionUUID = SessionUUID UUID.UUID
+  deriving (Eq, Show, Generic)
+
+instance FromField SessionUUID where
+  fromField y =
+    case fieldData y of
+      (SQLText x) ->
+        case UUID.fromText x of
+          Nothing -> returnError ConversionFailed y ("Could not convert to UUID: " ++ show x)
+          Just uuid -> Ok $ SessionUUID uuid
+      _ -> returnError ConversionFailed y "Expected SQLText for SessionUUID, but we received"
+
+instance ToField SessionUUID where
+  toField (SessionUUID uuid) =
+    uuid |> UUID.toText |> SQLText
+
+data StoredSession = StoredSession
+  { storedSessionUUID :: SessionUUID
+  , storedSessionUsername :: Username
+  , storedSessionTsCreated :: Clock.UTCTime
+  } deriving (Eq, Show, Generic)
+
+instance FromRow StoredSession where
+  fromRow = do
+    storedSessionUUID <- field
+    storedSessionUsername <- field
+    storedSessionTsCreated <- field
+    pure StoredSession {..}
+
+data LoginAttempt = LoginAttempt
+  { loginAttemptUsername :: Username
+  , loginAttemptNumAttempts :: Integer
+  } deriving (Eq, Show)
+
+instance FromRow LoginAttempt where
+  fromRow = do
+    loginAttemptUsername <- field
+    loginAttemptNumAttempts <- field
+    pure LoginAttempt {..}
+
+newtype SessionCookie = SessionCookie Cookies
+
+instance FromHttpApiData SessionCookie where
+  parseHeader x =
+    x |> parseCookies |> SessionCookie |> pure
+  parseQueryParam x =
+    x |> TE.encodeUtf8 |> parseCookies |> SessionCookie |> pure
+
+newtype RegistrationSecret = RegistrationSecret UUID.UUID
+  deriving (Eq, Show, Generic)
+
+instance FromHttpApiData RegistrationSecret where
+  parseQueryParam x =
+    case UUID.fromText x of
+      Nothing -> Left x
+      Just uuid -> Right (RegistrationSecret uuid)
+
+instance FromField RegistrationSecret where
+  fromField y =
+    case fieldData y of
+      (SQLText x) ->
+        case UUID.fromText x of
+          Nothing -> returnError ConversionFailed y ("Could not convert text to UUID: " ++ show x)
+          Just uuid -> Ok $ RegistrationSecret uuid
+      _ -> returnError ConversionFailed y "Field data is not SQLText, which is what we expect"
+
+instance ToField RegistrationSecret where
+  toField (RegistrationSecret secretUUID) =
+    secretUUID |> UUID.toText |> SQLText
+
+instance FromJSON RegistrationSecret
+
+data VerifyAccountRequest = VerifyAccountRequest
+  { verifyAccountRequestUsername :: Username
+  , verifyAccountRequestSecret :: RegistrationSecret
+  } deriving (Eq, Show)
+
+instance FromJSON VerifyAccountRequest where
+  parseJSON = withObject "VerifyAccountRequest" $ \x -> do
+    verifyAccountRequestUsername <- x .: "username"
+    verifyAccountRequestSecret   <- x .: "secret"
+    pure VerifyAccountRequest{..}
+
+data PendingAccount = PendingAccount
+  { pendingAccountSecret :: RegistrationSecret
+  , pendingAccountUsername :: Username
+  , pendingAccountPassword :: HashedPassword
+  , pendingAccountRole :: Role
+  , pendingAccountEmail :: Email
+  } deriving (Eq, Show)
+
+instance FromRow PendingAccount where
+  fromRow = do
+    pendingAccountSecret <- field
+    pendingAccountUsername <- field
+    pendingAccountPassword <- field
+    pendingAccountRole <- field
+    pendingAccountEmail <- field
+    pure PendingAccount {..}
+
+data UpdateTripRequest = UpdateTripRequest
+  { updateTripRequestTripPK :: TripPK
+  , updateTripRequestDestination :: Maybe Destination
+  , updateTripRequestStartDate :: Maybe Calendar.Day
+  , updateTripRequestEndDate :: Maybe Calendar.Day
+  , updateTripRequestComment :: Maybe Comment
+  } deriving (Eq, Show)
+
+instance FromJSON UpdateTripRequest where
+  parseJSON = withObject "UpdateTripRequest" $ \x -> do
+    updateTripRequestTripPK <- x .: "tripKey"
+    -- the following four fields might not be present
+    updateTripRequestDestination <- x .:? "destination"
+    updateTripRequestStartDate   <- x .:? "startDate"
+    updateTripRequestEndDate     <- x .:? "endDate"
+    updateTripRequestComment     <- x .:? "comment"
+    pure UpdateTripRequest{..}
+
+-- | Apply the updates in the UpdateTripRequest to Trip.
+updateTrip :: UpdateTripRequest -> Trip -> Trip
+updateTrip UpdateTripRequest{..} Trip{..} = Trip
+  { tripUsername    = tripUsername
+  , tripDestination = M.fromMaybe tripDestination updateTripRequestDestination
+  , tripStartDate   = M.fromMaybe tripStartDate updateTripRequestStartDate
+  , tripEndDate     = M.fromMaybe tripEndDate updateTripRequestEndDate
+  , tripComment     = M.fromMaybe tripComment updateTripRequestComment
+  }
+
+data UnfreezeAccountRequest = UnfreezeAccountRequest
+  { unfreezeAccountRequestUsername :: Username
+  } deriving (Eq, Show)
+
+instance FromJSON UnfreezeAccountRequest where
+  parseJSON = withObject "UnfreezeAccountRequest" $ \x -> do
+    unfreezeAccountRequestUsername <- x .: "username"
+    pure UnfreezeAccountRequest{..}
+
+data InviteUserRequest = InviteUserRequest
+  { inviteUserRequestEmail :: Email
+  , inviteUserRequestRole :: Role
+  } deriving (Eq, Show)
+
+instance FromJSON InviteUserRequest where
+  parseJSON = withObject "InviteUserRequest" $ \x -> do
+    inviteUserRequestEmail <- x .: "email"
+    inviteUserRequestRole <- x .: "role"
+    pure InviteUserRequest{..}
+
+newtype InvitationSecret = InvitationSecret UUID.UUID
+  deriving (Eq, Show, Generic)
+
+instance ToJSON InvitationSecret
+instance FromJSON InvitationSecret
+
+instance ToField InvitationSecret where
+  toField (InvitationSecret secretUUID) =
+    secretUUID |> UUID.toText |> SQLText
+
+instance FromField InvitationSecret where
+  fromField y =
+    case fieldData y of
+      (SQLText x) ->
+        case UUID.fromText x of
+          Nothing -> returnError ConversionFailed y ("Could not convert text to UUID: " ++ show x)
+          Just z -> Ok $ InvitationSecret z
+      _ -> returnError ConversionFailed y "Field data is not SQLText, which is what we expect"
+
+data Invitation = Invitation
+  { invitationEmail :: Email
+  , invitationRole :: Role
+  , invitationSecret :: InvitationSecret
+  } deriving (Eq, Show)
+
+instance FromRow Invitation where
+  fromRow = Invitation <$> field
+                       <*> field
+                       <*> field
+
+data AcceptInvitationRequest = AcceptInvitationRequest
+  { acceptInvitationRequestUsername :: Username
+  , acceptInvitationRequestPassword :: ClearTextPassword
+  , acceptInvitationRequestEmail :: Email
+  , acceptInvitationRequestSecret :: InvitationSecret
+  } deriving (Eq, Show)
+
+instance FromJSON AcceptInvitationRequest where
+  parseJSON = withObject "AcceptInvitationRequest" $ \x -> do
+    acceptInvitationRequestUsername <- x .: "username"
+    acceptInvitationRequestPassword <- x .: "password"
+    acceptInvitationRequestEmail <- x .: "email"
+    acceptInvitationRequestSecret <- x .: "secret"
+    pure AcceptInvitationRequest{..}
diff --git a/users/wpcarro/assessments/tt/src/Utils.hs b/users/wpcarro/assessments/tt/src/Utils.hs
new file mode 100644
index 0000000000..48c33af079
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/Utils.hs
@@ -0,0 +1,9 @@
+--------------------------------------------------------------------------------
+module Utils where
+--------------------------------------------------------------------------------
+import Data.Function ((&))
+--------------------------------------------------------------------------------
+
+-- | Prefer this operator to the ampersand for stylistic reasons.
+(|>) :: a -> (a -> b) -> b
+(|>) = (&)
diff --git a/users/wpcarro/assessments/tt/src/init.sql b/users/wpcarro/assessments/tt/src/init.sql
new file mode 100644
index 0000000000..b42753ae5d
--- /dev/null
+++ b/users/wpcarro/assessments/tt/src/init.sql
@@ -0,0 +1,67 @@
+-- Run `.read init.sql` from within a SQLite3 REPL to initialize the tables we
+-- need for this application. This will erase all current entries, so use with
+-- caution.
+-- Make sure to set `PRAGMA foreign_keys = on;` when transacting with the
+-- database.
+
+BEGIN TRANSACTION;
+
+DROP TABLE IF EXISTS Accounts;
+DROP TABLE IF EXISTS Trips;
+DROP TABLE IF EXISTS Sessions;
+DROP TABLE IF EXISTS LoginAttempts;
+DROP TABLE IF EXISTS PendingAccounts;
+DROP TABLE IF EXISTS Invitations;
+
+CREATE TABLE Accounts (
+  username TEXT CHECK(LENGTH(username) > 0) NOT NULL,
+  password TEXT CHECK(LENGTH(password) > 0) NOT NULL,
+  email TEXT CHECK(LENGTH(email) > 0) NOT NULL UNIQUE,
+  role TEXT CHECK(role IN ('user', 'manager', 'admin')) NOT NULL,
+  profilePicture BLOB,
+  PRIMARY KEY (username)
+);
+
+CREATE TABLE Trips (
+  username TEXT NOT NULL,
+  destination TEXT CHECK(LENGTH(destination) > 0) NOT NULL,
+  startDate TEXT CHECK(LENGTH(startDate) == 10) NOT NULL, -- 'YYYY-MM-DD'
+  endDate TEXT CHECK(LENGTH(endDate) == 10) NOT NULL, -- 'YYYY-MM-DD'
+  comment TEXT NOT NULL,
+  PRIMARY KEY (username, destination, startDate),
+  FOREIGN KEY (username) REFERENCES Accounts ON DELETE CASCADE
+);
+
+CREATE TABLE Sessions (
+  uuid TEXT CHECK(LENGTH(uuid) == 36) NOT NULL,
+  username TEXT NOT NULL UNIQUE,
+  -- TODO(wpcarro): Add a LENGTH CHECK here
+  tsCreated TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM:SS'
+  PRIMARY KEY (uuid),
+  FOREIGN KEY (username) REFERENCES Accounts ON DELETE CASCADE
+);
+
+CREATE TABLE LoginAttempts (
+  username TEXT NOT NULL UNIQUE,
+  numAttempts INTEGER NOT NULL,
+  PRIMARY KEY (username),
+  FOREIGN KEY (username) REFERENCES Accounts ON DELETE CASCADE
+);
+
+CREATE TABLE PendingAccounts (
+  secret TEXT CHECK(LENGTH(secret) == 36) NOT NULL,
+  username TEXT CHECK(LENGTH(username) > 0) NOT NULL,
+  password TEXT CHECK(LENGTH(password) > 0) NOT NULL,
+  role TEXT CHECK(role IN ('user', 'manager', 'admin')) NOT NULL,
+  email TEXT CHECK(LENGTH(email) > 0) NOT NULL UNIQUE,
+  PRIMARY KEY (username)
+);
+
+CREATE TABLE Invitations (
+  email TEXT CHECK(LENGTH(email) > 0) NOT NULL UNIQUE,
+  role TEXT CHECK(role IN ('user', 'manager', 'admin')) NOT NULL,
+  secret TEXT CHECK(LENGTH(secret) == 36) NOT NULL,
+  PRIMARY KEY (email)
+);
+
+COMMIT;
diff --git a/users/wpcarro/assessments/tt/tests/create-accounts.sh b/users/wpcarro/assessments/tt/tests/create-accounts.sh
new file mode 100755
index 0000000000..8c2a66bc8b
--- /dev/null
+++ b/users/wpcarro/assessments/tt/tests/create-accounts.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env sh
+
+# This script populates the Accounts table over HTTP.
+
+http POST :3000/accounts \
+  username=mimi \
+  password=testing \
+  email=miriamwright@google.com \
+  role=user
+
+http POST :3000/accounts \
+  username=bill \
+  password=testing \
+  email=wpcarro@gmail.com \
+  role=manager
+
+http POST :3000/accounts \
+  username=wpcarro \
+  password=testing \
+  email=wpcarro@google.com \
+  role=admin
diff --git a/users/wpcarro/assessments/tt/todo.org b/users/wpcarro/assessments/tt/todo.org
new file mode 100644
index 0000000000..39592d0482
--- /dev/null
+++ b/users/wpcarro/assessments/tt/todo.org
@@ -0,0 +1,18 @@
+* TODO Users must be able to create an account
+* TODO Users must verify their account by email
+* TODO Support federated login with Google
+* TODO Users must be able to authenticate and login
+* TODO Define three roles: user, manager, admin
+* TODO Users can add trips
+* TODO Users can edit trips
+* TODO Users can delete trips
+* TODO Users can filter trips
+* TODO Support all actions via the REST API
+* TODO Block users after three failed authentication attempts
+* TODO Only admins and managers can unblock blocked login attempts
+* TODO Add unit tests
+* TODO Add E2E tests
+* TODO Pull user profile pictures using Gravatar
+* TODO Allow users to change their profile picture
+* TODO Admins should be allowed to invite new users via email
+* TODO Allow users to print their travel itineraries
diff --git a/users/wpcarro/boilerplate/README.md b/users/wpcarro/boilerplate/README.md
new file mode 100644
index 0000000000..aa72266a33
--- /dev/null
+++ b/users/wpcarro/boilerplate/README.md
@@ -0,0 +1,21 @@
+# Boilerplate
+
+Storing some boilerplate code to help me reduce the time it takes me to develop
+and deploy applications.
+
+## Usage
+
+Let's say that you would like to create a game for
+`sandbox.wpcarro.dev/game`. We will create a new TypeScript project with the
+following:
+
+```shell
+$ cp -r typescript path/to/new-project
+```
+
+This initializes the project. To start developing, run:
+
+```shell
+$ nix-shell
+$ yarn run dev
+```
diff --git a/users/wpcarro/boilerplate/clojure/.envrc b/users/wpcarro/boilerplate/clojure/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/boilerplate/clojure/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/boilerplate/clojure/.gitignore b/users/wpcarro/boilerplate/clojure/.gitignore
new file mode 100644
index 0000000000..f24c5e393a
--- /dev/null
+++ b/users/wpcarro/boilerplate/clojure/.gitignore
@@ -0,0 +1,4 @@
+/.lein-repl-history
+/target
+/?
+/.nrepl-port
\ No newline at end of file
diff --git a/users/wpcarro/boilerplate/clojure/README.md b/users/wpcarro/boilerplate/clojure/README.md
new file mode 100644
index 0000000000..53590850bc
--- /dev/null
+++ b/users/wpcarro/boilerplate/clojure/README.md
@@ -0,0 +1,33 @@
+# Clojure Boilerplate
+
+This boilerplate uses `lein` to manage the project.
+
+## Files to change
+
+To use this boilerplate, run the following in a shell:
+
+```shell
+$ cp -r . path/to/new-project
+```
+
+After running the above command, change the following files to remove the
+placeholder values:
+
+- `README.md`: Change the title; change the description; drop "Files to change";
+  keep "Getting started"
+- `project.clj`: Change title
+- `src/main.clj`: Change `:doc`; drop `main/foo`
+
+## Getting started
+
+From a shell, run:
+
+```shell
+$ lein repl
+```
+
+From Emacs, navigate to a source code buffer and run:
+
+```
+M-x cider-jack-in
+```
diff --git a/users/wpcarro/boilerplate/clojure/project.clj b/users/wpcarro/boilerplate/clojure/project.clj
new file mode 100644
index 0000000000..54e34eab7a
--- /dev/null
+++ b/users/wpcarro/boilerplate/clojure/project.clj
@@ -0,0 +1,2 @@
+(defproject boilerplate "0.0.1"
+  :dependencies [[org.clojure/clojure "1.8.0"]])
diff --git a/users/wpcarro/boilerplate/clojure/shell.nix b/users/wpcarro/boilerplate/clojure/shell.nix
new file mode 100644
index 0000000000..8b92b592e1
--- /dev/null
+++ b/users/wpcarro/boilerplate/clojure/shell.nix
@@ -0,0 +1,7 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    leiningen
+  ];
+}
diff --git a/users/wpcarro/boilerplate/clojure/src/main.clj b/users/wpcarro/boilerplate/clojure/src/main.clj
new file mode 100644
index 0000000000..f6b60dba40
--- /dev/null
+++ b/users/wpcarro/boilerplate/clojure/src/main.clj
@@ -0,0 +1,8 @@
+(ns ^{:doc "Top-level module."
+      :author "William Carroll"}
+    main)
+
+(declare main)
+
+(defn foo [a b]
+  (+ a b))
diff --git a/users/wpcarro/boilerplate/elm/.envrc b/users/wpcarro/boilerplate/elm/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/boilerplate/elm/.gitignore b/users/wpcarro/boilerplate/elm/.gitignore
new file mode 100644
index 0000000000..1cb4f3034c
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/.gitignore
@@ -0,0 +1,3 @@
+/elm-stuff
+/Main.min.js
+/output.css
diff --git a/users/wpcarro/boilerplate/elm/README.md b/users/wpcarro/boilerplate/elm/README.md
new file mode 100644
index 0000000000..04804ad94f
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/README.md
@@ -0,0 +1,18 @@
+# Elm
+
+Elm has one of the best developer experiences that I'm aware of. The error
+messages are helpful and the entire experience is optimized to improve the ease
+of writing web applications.
+
+## Developing
+
+If you're interested in contributing, the following will create an environment
+in which you can develop:
+
+```shell
+$ nix-shell
+$ npx tailwindcss build index.css -o output.css
+$ elm-live -- src/Main.elm --output=Main.min.js
+```
+
+You can now view your web client at `http://localhost:8000`!
diff --git a/users/wpcarro/boilerplate/elm/elm.json b/users/wpcarro/boilerplate/elm/elm.json
new file mode 100644
index 0000000000..a95f80408e
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/elm.json
@@ -0,0 +1,30 @@
+{
+    "type": "application",
+    "source-directories": [
+        "src"
+    ],
+    "elm-version": "0.19.1",
+    "dependencies": {
+        "direct": {
+            "elm/browser": "1.0.2",
+            "elm/core": "1.0.5",
+            "elm/html": "1.0.0",
+            "elm/random": "1.0.0",
+            "elm/svg": "1.0.1",
+            "elm/time": "1.0.0",
+            "elm-community/list-extra": "8.2.3",
+            "elm-community/maybe-extra": "5.2.0",
+            "elm-community/random-extra": "3.1.0"
+        },
+        "indirect": {
+            "elm/json": "1.1.3",
+            "elm/url": "1.0.0",
+            "elm/virtual-dom": "1.0.2",
+            "owanturist/elm-union-find": "1.0.0"
+        }
+    },
+    "test-dependencies": {
+        "direct": {},
+        "indirect": {}
+    }
+}
diff --git a/users/wpcarro/boilerplate/elm/index.css b/users/wpcarro/boilerplate/elm/index.css
new file mode 100644
index 0000000000..b5c61c9567
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/index.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/users/wpcarro/boilerplate/elm/index.html b/users/wpcarro/boilerplate/elm/index.html
new file mode 100644
index 0000000000..ce8f727b6f
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>Elm SPA</title>
+    <link rel="stylesheet" href="./output.css" />
+    <script src="./Main.min.js"></script>
+  </head>
+  <body class="font-serif">
+    <div id="mount"></div>
+    <script>
+     Elm.Main.init({node: document.getElementById("mount")});
+    </script>
+  </body>
+</html>
diff --git a/users/wpcarro/boilerplate/elm/shell.nix b/users/wpcarro/boilerplate/elm/shell.nix
new file mode 100644
index 0000000000..afcc0f4d36
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/shell.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs.elmPackages; [
+    elm
+    elm-format
+    elm-live
+  ];
+}
diff --git a/users/wpcarro/boilerplate/elm/src/Landing.elm b/users/wpcarro/boilerplate/elm/src/Landing.elm
new file mode 100644
index 0000000000..00bb9e281a
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/src/Landing.elm
@@ -0,0 +1,13 @@
+module Landing exposing (render)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import State
+
+
+render : State.Model -> Html State.Msg
+render model =
+    div [ class "pt-10 pb-20 px-10" ]
+        [ p [] [ text "Welcome to the landing page!" ]
+        ]
diff --git a/users/wpcarro/boilerplate/elm/src/Login.elm b/users/wpcarro/boilerplate/elm/src/Login.elm
new file mode 100644
index 0000000000..27f1d811a8
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/src/Login.elm
@@ -0,0 +1,13 @@
+module Login exposing (render)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import State
+
+
+render : State.Model -> Html State.Msg
+render model =
+    div [ class "pt-10 pb-20 px-10" ]
+        [ p [] [ text "Please authenticate" ]
+        ]
diff --git a/users/wpcarro/boilerplate/elm/src/Main.elm b/users/wpcarro/boilerplate/elm/src/Main.elm
new file mode 100644
index 0000000000..30006460cd
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/src/Main.elm
@@ -0,0 +1,31 @@
+module Main exposing (main)
+
+import Browser
+import Html exposing (..)
+import Landing
+import Login
+import State
+
+
+subscriptions : State.Model -> Sub State.Msg
+subscriptions model =
+    Sub.none
+
+
+view : State.Model -> Html State.Msg
+view model =
+    case model.view of
+        State.Landing ->
+            Landing.render model
+
+        State.Login ->
+            Login.render model
+
+
+main =
+    Browser.element
+        { init = \() -> ( State.init, Cmd.none )
+        , subscriptions = subscriptions
+        , update = State.update
+        , view = view
+        }
diff --git a/users/wpcarro/boilerplate/elm/src/State.elm b/users/wpcarro/boilerplate/elm/src/State.elm
new file mode 100644
index 0000000000..c1edae8bb6
--- /dev/null
+++ b/users/wpcarro/boilerplate/elm/src/State.elm
@@ -0,0 +1,43 @@
+module State exposing (..)
+
+
+type Msg
+    = DoNothing
+    | SetView View
+
+
+type View
+    = Landing
+    | Login
+
+
+type alias Model =
+    { isLoading : Bool
+    , view : View
+    }
+
+
+{-| The initial state for the application.
+-}
+init : Model
+init =
+    { isLoading = False
+    , view = Landing
+    }
+
+
+{-| Now that we have state, we need a function to change the state.
+-}
+update : Msg -> Model -> ( Model, Cmd Msg )
+update msg model =
+    case msg of
+        DoNothing ->
+            ( model, Cmd.none )
+
+        SetView x ->
+            ( { model
+                | view = x
+                , isLoading = True
+              }
+            , Cmd.none
+            )
diff --git a/users/wpcarro/boilerplate/typescript/.envrc b/users/wpcarro/boilerplate/typescript/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/boilerplate/typescript/.gitignore b/users/wpcarro/boilerplate/typescript/.gitignore
new file mode 100644
index 0000000000..ebea22e071
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/.gitignore
@@ -0,0 +1,3 @@
+/.cache
+/dist
+/node_modules
\ No newline at end of file
diff --git a/users/wpcarro/boilerplate/typescript/README.md b/users/wpcarro/boilerplate/typescript/README.md
new file mode 100644
index 0000000000..a54186a9f2
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/README.md
@@ -0,0 +1,26 @@
+# Frontend Boilerplate
+
+While many times I prefer using alt-languages like ReasonML, ClojureScript, or
+Elm, sometimes I prefer to write an application using TypeScript. This directory
+contains the necessary starter code to create these applications.
+
+- React: Maps application state to UI
+- React-Router: Stateful routing for SPAs
+- Redux: Application state management
+- TypeScript: Type-safety
+- TailwindCSS: Styling library using utility classes
+- Prettier: Source code formatting
+- Jest: Test runner
+
+## Developing
+
+```shell
+$ nix-shell
+$ yarn run dev
+```
+
+## Building
+
+```shell
+$ nix-build
+```
diff --git a/users/wpcarro/boilerplate/typescript/default.nix b/users/wpcarro/boilerplate/typescript/default.nix
new file mode 100644
index 0000000000..84949cae7f
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/default.nix
@@ -0,0 +1,23 @@
+{ pkgs, ... }:
+
+pkgs.stdenv.mkDerivation {
+  name = "typescript";
+  srcs = builtins.path { path = ./.; name = "typescript"; };
+  buildInputs = with pkgs; [
+    nodejs
+    # Exposes lscpu for parcel.js
+    utillinux
+  ];
+  # parcel.js needs number of CPUs
+  PARCEL_WORKERS = "1";
+  buildPhase = ''
+    export HOME="."
+    npx parcel build src/index.html --public-url ./
+  '';
+  installPhase = ''
+    mv dist $out
+  '';
+
+  # TODO(wpcarro): This doesn't build at all.
+  meta.ci.skip = true;
+}
diff --git a/users/wpcarro/boilerplate/typescript/package.json b/users/wpcarro/boilerplate/typescript/package.json
new file mode 100644
index 0000000000..104e7272da
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/package.json
@@ -0,0 +1,27 @@
+{
+  "name": "tailwindcss",
+  "version": "1.0.0",
+  "main": "index.js",
+  "license": "MIT",
+  "scripts": {
+    "dev": "parcel src/index.html & npx tsc --watch --noEmit",
+    "prettier": "prettier --ignore-path .gitignore --write \"**/*.{js,ts,jsx,tsx,html,css.json}\""
+  },
+  "devDependencies": {
+    "@types/node": "^13.9.3",
+    "parcel-bundler": "^1.12.4",
+    "prettier": "^2.0.2",
+    "tailwindcss": "^1.2.0",
+    "typescript": "^3.8.3"
+  },
+  "dependencies": {
+    "@reduxjs/toolkit": "^1.2.5",
+    "@types/react-dom": "^16.9.5",
+    "@types/react-redux": "^7.1.7",
+    "@types/react-router-dom": "^5.1.3",
+    "react": "^16.13.1",
+    "react-dom": "^16.13.1",
+    "react-redux": "^7.2.0",
+    "react-router-dom": "^5.1.2"
+  }
+}
diff --git a/users/wpcarro/boilerplate/typescript/postcss.config.js b/users/wpcarro/boilerplate/typescript/postcss.config.js
new file mode 100644
index 0000000000..d68fa61866
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/postcss.config.js
@@ -0,0 +1,7 @@
+const tailwindcss = require('tailwindcss')
+
+module.exports = {
+  plugins: [
+    tailwindcss('./tailwind.config.js')
+  ]
+}
diff --git a/users/wpcarro/boilerplate/typescript/shell.nix b/users/wpcarro/boilerplate/typescript/shell.nix
new file mode 100644
index 0000000000..a3ae929ef4
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/shell.nix
@@ -0,0 +1,8 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    nodejs
+    yarn
+  ];
+}
diff --git a/users/wpcarro/boilerplate/typescript/src/App.tsx b/users/wpcarro/boilerplate/typescript/src/App.tsx
new file mode 100644
index 0000000000..4fae1b36ac
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/src/App.tsx
@@ -0,0 +1,52 @@
+import React, { useEffect } from "react";
+import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
+import { useDispatch } from "react-redux";
+import { actions, useTypedSelector } from "./store";
+import { Link } from "react-router-dom";
+
+const App: React.FC = () => {
+  const dispatch = useDispatch();
+  const { isLoading } = useTypedSelector(state => ({
+    isLoading: state.isLoading,
+  }));
+
+  return (
+    <Router>
+      <nav className="bg-blue-400">
+        <ul className="container mx-auto justify-between flex py-6 text-white">
+          <li>
+            <Link to="/">Home</Link>
+          </li>
+          <li>
+            <Link to="/about">About</Link>
+          </li>
+          <li>
+            <Link to="/contact">Contact</Link>
+          </li>
+        </ul>
+      </nav>
+      <Switch>
+        <Route exact path="/">
+          <div className="container mx-auto">
+            <h1>Welcome to the home page. Loading: {isLoading ? "true" : "false"}</h1>
+            <button
+              className="bg-gray-300 py-4 px-6"
+              onClick={() => dispatch(actions.toggleIsLoading())}>isLoading</button>
+          </div>
+        </Route>
+        <Route exact path="/about">
+          <div className="container mx-auto">
+            <h1>Here is the about page.</h1>
+          </div>
+        </Route>
+        <Route exact path="/contact">
+          <div className="container mx-auto">
+            <h1>Here is the contact page.</h1>
+          </div>
+        </Route>
+      </Switch>
+    </Router>
+  );
+};
+
+export default App;
diff --git a/users/wpcarro/boilerplate/typescript/src/index.css b/users/wpcarro/boilerplate/typescript/src/index.css
new file mode 100644
index 0000000000..b5c61c9567
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/src/index.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/users/wpcarro/boilerplate/typescript/src/index.html b/users/wpcarro/boilerplate/typescript/src/index.html
new file mode 100644
index 0000000000..91752af916
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/src/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="stylesheet" href="./index.css" />
+  </head>
+  <body>
+    <div id="mount"></div>
+    <script src="./index.tsx"></script>
+  </body>
+</html>
diff --git a/users/wpcarro/boilerplate/typescript/src/index.tsx b/users/wpcarro/boilerplate/typescript/src/index.tsx
new file mode 100644
index 0000000000..dc28dc4a9c
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/src/index.tsx
@@ -0,0 +1,12 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import App from "./App";
+import { Provider } from "react-redux";
+import store from "./store";
+
+ReactDOM.render(
+  <Provider store={store}>
+    <App />
+  </Provider>,
+  document.getElementById("mount")
+);
diff --git a/users/wpcarro/boilerplate/typescript/src/store.ts b/users/wpcarro/boilerplate/typescript/src/store.ts
new file mode 100644
index 0000000000..03e980a491
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/src/store.ts
@@ -0,0 +1,26 @@
+import { createSlice, configureStore, PayloadAction } from "@reduxjs/toolkit";
+import { useSelector, TypedUseSelectorHook } from "react-redux";
+
+export interface State {
+  isLoading: boolean;
+}
+
+const initialState: State = {
+  isLoading: true,
+};
+
+export const { actions, reducer } = createSlice({
+  name: "application",
+  initialState,
+  reducers: {
+    toggleIsLoading: state => ({ ...state, isLoading: !state.isLoading }),
+  }
+});
+
+/**
+ * Defining and consuming this allows us to avoid annotating State in all of our
+ * selectors.
+ */
+export const useTypedSelector: TypedUseSelectorHook<State> = useSelector;
+
+export default configureStore({ reducer });
diff --git a/users/wpcarro/boilerplate/typescript/tailwind.config.js b/users/wpcarro/boilerplate/typescript/tailwind.config.js
new file mode 100644
index 0000000000..af829e20f9
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/tailwind.config.js
@@ -0,0 +1,7 @@
+module.exports = {
+  theme: {
+    extend: {},
+  },
+  variants: {},
+  plugins: [],
+}
diff --git a/users/wpcarro/boilerplate/typescript/tsconfig.json b/users/wpcarro/boilerplate/typescript/tsconfig.json
new file mode 100644
index 0000000000..013f34fdf0
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/tsconfig.json
@@ -0,0 +1,25 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "lib": [
+      "dom",
+      "dom.iterable",
+      "esnext"
+    ],
+    "allowJs": true,
+    "skipLibCheck": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "strict": true,
+    "forceConsistentCasingInFileNames": true,
+    "module": "esnext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "react"
+  },
+  "include": [
+    "src/**/*"
+  ]
+}
diff --git a/users/wpcarro/boilerplate/typescript/yarn.lock b/users/wpcarro/boilerplate/typescript/yarn.lock
new file mode 100644
index 0000000000..0e16fe80a4
--- /dev/null
+++ b/users/wpcarro/boilerplate/typescript/yarn.lock
@@ -0,0 +1,5670 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
+  integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==
+  dependencies:
+    "@babel/highlight" "^7.8.3"
+
+"@babel/compat-data@^7.8.6", "@babel/compat-data@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.0.tgz#04815556fc90b0c174abd2c0c1bb966faa036a6c"
+  integrity sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==
+  dependencies:
+    browserslist "^4.9.1"
+    invariant "^2.2.4"
+    semver "^5.5.0"
+
+"@babel/core@^7.4.4":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e"
+  integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==
+  dependencies:
+    "@babel/code-frame" "^7.8.3"
+    "@babel/generator" "^7.9.0"
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helpers" "^7.9.0"
+    "@babel/parser" "^7.9.0"
+    "@babel/template" "^7.8.6"
+    "@babel/traverse" "^7.9.0"
+    "@babel/types" "^7.9.0"
+    convert-source-map "^1.7.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.1"
+    json5 "^2.1.2"
+    lodash "^4.17.13"
+    resolve "^1.3.2"
+    semver "^5.4.1"
+    source-map "^0.5.0"
+
+"@babel/generator@^7.4.4", "@babel/generator@^7.9.0":
+  version "7.9.3"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.3.tgz#7c8b2956c6f68b3ab732bd16305916fbba521d94"
+  integrity sha512-RpxM252EYsz9qLUIq6F7YJyK1sv0wWDBFuztfDGWaQKzHjqDHysxSiRUpA/X9jmfqo+WzkAVKFaUily5h+gDCQ==
+  dependencies:
+    "@babel/types" "^7.9.0"
+    jsesc "^2.5.1"
+    lodash "^4.17.13"
+    source-map "^0.5.0"
+
+"@babel/helper-annotate-as-pure@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee"
+  integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503"
+  integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==
+  dependencies:
+    "@babel/helper-explode-assignable-expression" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-builder-react-jsx-experimental@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.0.tgz#066d80262ade488f9c1b1823ce5db88a4cedaa43"
+  integrity sha512-3xJEiyuYU4Q/Ar9BsHisgdxZsRlsShMe90URZ0e6przL26CCs8NJbDoxH94kKT17PcxlMhsCAwZd90evCo26VQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-module-imports" "^7.8.3"
+    "@babel/types" "^7.9.0"
+
+"@babel/helper-builder-react-jsx@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.9.0.tgz#16bf391990b57732700a3278d4d9a81231ea8d32"
+  integrity sha512-weiIo4gaoGgnhff54GQ3P5wsUQmnSwpkvU0r6ZHq6TzoSzKy4JxHEgnxNytaKbov2a9z/CVNyzliuCOUPEX3Jw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/types" "^7.9.0"
+
+"@babel/helper-compilation-targets@^7.8.7":
+  version "7.8.7"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz#dac1eea159c0e4bd46e309b5a1b04a66b53c1dde"
+  integrity sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==
+  dependencies:
+    "@babel/compat-data" "^7.8.6"
+    browserslist "^4.9.1"
+    invariant "^2.2.4"
+    levenary "^1.1.1"
+    semver "^5.5.0"
+
+"@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8":
+  version "7.8.8"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087"
+  integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-regex" "^7.8.3"
+    regexpu-core "^4.7.0"
+
+"@babel/helper-define-map@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15"
+  integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==
+  dependencies:
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/types" "^7.8.3"
+    lodash "^4.17.13"
+
+"@babel/helper-explode-assignable-expression@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982"
+  integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==
+  dependencies:
+    "@babel/traverse" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-function-name@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca"
+  integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.8.3"
+    "@babel/template" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-get-function-arity@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5"
+  integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-hoist-variables@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134"
+  integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-member-expression-to-functions@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c"
+  integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-module-imports@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498"
+  integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-module-transforms@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5"
+  integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==
+  dependencies:
+    "@babel/helper-module-imports" "^7.8.3"
+    "@babel/helper-replace-supers" "^7.8.6"
+    "@babel/helper-simple-access" "^7.8.3"
+    "@babel/helper-split-export-declaration" "^7.8.3"
+    "@babel/template" "^7.8.6"
+    "@babel/types" "^7.9.0"
+    lodash "^4.17.13"
+
+"@babel/helper-optimise-call-expression@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9"
+  integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
+  integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==
+
+"@babel/helper-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965"
+  integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==
+  dependencies:
+    lodash "^4.17.13"
+
+"@babel/helper-remap-async-to-generator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86"
+  integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-wrap-function" "^7.8.3"
+    "@babel/template" "^7.8.3"
+    "@babel/traverse" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6":
+  version "7.8.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8"
+  integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.8.3"
+    "@babel/helper-optimise-call-expression" "^7.8.3"
+    "@babel/traverse" "^7.8.6"
+    "@babel/types" "^7.8.6"
+
+"@babel/helper-simple-access@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae"
+  integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==
+  dependencies:
+    "@babel/template" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-split-export-declaration@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9"
+  integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-validator-identifier@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed"
+  integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==
+
+"@babel/helper-wrap-function@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610"
+  integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==
+  dependencies:
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/template" "^7.8.3"
+    "@babel/traverse" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helpers@^7.9.0":
+  version "7.9.2"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f"
+  integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==
+  dependencies:
+    "@babel/template" "^7.8.3"
+    "@babel/traverse" "^7.9.0"
+    "@babel/types" "^7.9.0"
+
+"@babel/highlight@^7.8.3":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
+  integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.9.0"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
+"@babel/parser@^7.4.4", "@babel/parser@^7.8.6", "@babel/parser@^7.9.0":
+  version "7.9.3"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.3.tgz#043a5fc2ad8b7ea9facddc4e802a1f0f25da7255"
+  integrity sha512-E6SpIDJZ0cZAKoCNk+qSDd0ChfTnpiJN9FfNf3RZ20dzwA2vL2oq5IX1XTVT+4vDmRlta2nGk5HGMMskJAR+4A==
+
+"@babel/plugin-proposal-async-generator-functions@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f"
+  integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-remap-async-to-generator" "^7.8.3"
+    "@babel/plugin-syntax-async-generators" "^7.8.0"
+
+"@babel/plugin-proposal-dynamic-import@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054"
+  integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+
+"@babel/plugin-proposal-json-strings@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b"
+  integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-json-strings" "^7.8.0"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2"
+  integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+
+"@babel/plugin-proposal-numeric-separator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8"
+  integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-numeric-separator" "^7.8.3"
+
+"@babel/plugin-proposal-object-rest-spread@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz#a28993699fc13df165995362693962ba6b061d6f"
+  integrity sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9"
+  integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+
+"@babel/plugin-proposal-optional-chaining@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58"
+  integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3":
+  version "7.8.8"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d"
+  integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.8.8"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-async-generators@^7.8.0":
+  version "7.8.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
+  integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-dynamic-import@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
+  integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-flow@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.8.3.tgz#f2c883bd61a6316f2c89380ae5122f923ba4527f"
+  integrity sha512-innAx3bUbA0KSYj2E2MNFSn9hiCeowOFLxlsuhXzw8hMQnzkDomUr9QCD7E9VF60NmnG1sNTuuv6Qf4f8INYsg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-json-strings@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
+  integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-jsx@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz#521b06c83c40480f1e58b4fd33b92eceb1d6ea94"
+  integrity sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+  integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f"
+  integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-object-rest-spread@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
+  integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
+  integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+  integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-top-level-await@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391"
+  integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-arrow-functions@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6"
+  integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-async-to-generator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086"
+  integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==
+  dependencies:
+    "@babel/helper-module-imports" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-remap-async-to-generator" "^7.8.3"
+
+"@babel/plugin-transform-block-scoped-functions@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3"
+  integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-block-scoping@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a"
+  integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    lodash "^4.17.13"
+
+"@babel/plugin-transform-classes@^7.9.0":
+  version "7.9.2"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz#8603fc3cc449e31fdbdbc257f67717536a11af8d"
+  integrity sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-define-map" "^7.8.3"
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/helper-optimise-call-expression" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-replace-supers" "^7.8.6"
+    "@babel/helper-split-export-declaration" "^7.8.3"
+    globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b"
+  integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-destructuring@^7.8.3":
+  version "7.8.8"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz#fadb2bc8e90ccaf5658de6f8d4d22ff6272a2f4b"
+  integrity sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e"
+  integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-duplicate-keys@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1"
+  integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-exponentiation-operator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7"
+  integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==
+  dependencies:
+    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-flow-strip-types@^7.4.4":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz#8a3538aa40434e000b8f44a3c5c9ac7229bd2392"
+  integrity sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-flow" "^7.8.3"
+
+"@babel/plugin-transform-for-of@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e"
+  integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-function-name@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b"
+  integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==
+  dependencies:
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-literals@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1"
+  integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-member-expression-literals@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410"
+  integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-modules-amd@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz#19755ee721912cf5bb04c07d50280af3484efef4"
+  integrity sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-commonjs@^7.4.4", "@babel/plugin-transform-modules-commonjs@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz#e3e72f4cbc9b4a260e30be0ea59bdf5a39748940"
+  integrity sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-simple-access" "^7.8.3"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-systemjs@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz#e9fd46a296fc91e009b64e07ddaa86d6f0edeb90"
+  integrity sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.8.3"
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-umd@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697"
+  integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c"
+  integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.8.3"
+
+"@babel/plugin-transform-new-target@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43"
+  integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-object-super@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725"
+  integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-replace-supers" "^7.8.3"
+
+"@babel/plugin-transform-parameters@^7.8.7":
+  version "7.9.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.3.tgz#3028d0cc20ddc733166c6e9c8534559cee09f54a"
+  integrity sha512-fzrQFQhp7mIhOzmOtPiKffvCYQSK10NR8t6BBz2yPbeUHb9OLW8RZGtgDRBn8z2hGcwvKDL3vC7ojPTLNxmqEg==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-property-literals@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263"
+  integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-react-jsx@^7.0.0":
+  version "7.9.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.9.1.tgz#d03af29396a6dc51bfa24eefd8005a9fd381152a"
+  integrity sha512-+xIZ6fPoix7h57CNO/ZeYADchg1tFyX9NDsnmNFFua8e1JNPln156mzS+8AQe1On2X2GLlANHJWHIXbMCqWDkQ==
+  dependencies:
+    "@babel/helper-builder-react-jsx" "^7.9.0"
+    "@babel/helper-builder-react-jsx-experimental" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-jsx" "^7.8.3"
+
+"@babel/plugin-transform-regenerator@^7.8.7":
+  version "7.8.7"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8"
+  integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==
+  dependencies:
+    regenerator-transform "^0.14.2"
+
+"@babel/plugin-transform-reserved-words@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5"
+  integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-shorthand-properties@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8"
+  integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-spread@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8"
+  integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-sticky-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100"
+  integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-regex" "^7.8.3"
+
+"@babel/plugin-transform-template-literals@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80"
+  integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-typeof-symbol@^7.8.4":
+  version "7.8.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412"
+  integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-unicode-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad"
+  integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/preset-env@^7.4.4":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.0.tgz#a5fc42480e950ae8f5d9f8f2bbc03f52722df3a8"
+  integrity sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ==
+  dependencies:
+    "@babel/compat-data" "^7.9.0"
+    "@babel/helper-compilation-targets" "^7.8.7"
+    "@babel/helper-module-imports" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-proposal-async-generator-functions" "^7.8.3"
+    "@babel/plugin-proposal-dynamic-import" "^7.8.3"
+    "@babel/plugin-proposal-json-strings" "^7.8.3"
+    "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3"
+    "@babel/plugin-proposal-numeric-separator" "^7.8.3"
+    "@babel/plugin-proposal-object-rest-spread" "^7.9.0"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.8.3"
+    "@babel/plugin-proposal-optional-chaining" "^7.9.0"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.8.3"
+    "@babel/plugin-syntax-async-generators" "^7.8.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+    "@babel/plugin-syntax-json-strings" "^7.8.0"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+    "@babel/plugin-syntax-numeric-separator" "^7.8.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+    "@babel/plugin-syntax-top-level-await" "^7.8.3"
+    "@babel/plugin-transform-arrow-functions" "^7.8.3"
+    "@babel/plugin-transform-async-to-generator" "^7.8.3"
+    "@babel/plugin-transform-block-scoped-functions" "^7.8.3"
+    "@babel/plugin-transform-block-scoping" "^7.8.3"
+    "@babel/plugin-transform-classes" "^7.9.0"
+    "@babel/plugin-transform-computed-properties" "^7.8.3"
+    "@babel/plugin-transform-destructuring" "^7.8.3"
+    "@babel/plugin-transform-dotall-regex" "^7.8.3"
+    "@babel/plugin-transform-duplicate-keys" "^7.8.3"
+    "@babel/plugin-transform-exponentiation-operator" "^7.8.3"
+    "@babel/plugin-transform-for-of" "^7.9.0"
+    "@babel/plugin-transform-function-name" "^7.8.3"
+    "@babel/plugin-transform-literals" "^7.8.3"
+    "@babel/plugin-transform-member-expression-literals" "^7.8.3"
+    "@babel/plugin-transform-modules-amd" "^7.9.0"
+    "@babel/plugin-transform-modules-commonjs" "^7.9.0"
+    "@babel/plugin-transform-modules-systemjs" "^7.9.0"
+    "@babel/plugin-transform-modules-umd" "^7.9.0"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3"
+    "@babel/plugin-transform-new-target" "^7.8.3"
+    "@babel/plugin-transform-object-super" "^7.8.3"
+    "@babel/plugin-transform-parameters" "^7.8.7"
+    "@babel/plugin-transform-property-literals" "^7.8.3"
+    "@babel/plugin-transform-regenerator" "^7.8.7"
+    "@babel/plugin-transform-reserved-words" "^7.8.3"
+    "@babel/plugin-transform-shorthand-properties" "^7.8.3"
+    "@babel/plugin-transform-spread" "^7.8.3"
+    "@babel/plugin-transform-sticky-regex" "^7.8.3"
+    "@babel/plugin-transform-template-literals" "^7.8.3"
+    "@babel/plugin-transform-typeof-symbol" "^7.8.4"
+    "@babel/plugin-transform-unicode-regex" "^7.8.3"
+    "@babel/preset-modules" "^0.1.3"
+    "@babel/types" "^7.9.0"
+    browserslist "^4.9.1"
+    core-js-compat "^3.6.2"
+    invariant "^2.2.2"
+    levenary "^1.1.1"
+    semver "^5.5.0"
+
+"@babel/preset-modules@^0.1.3":
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72"
+  integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
+    "@babel/plugin-transform-dotall-regex" "^7.4.4"
+    "@babel/types" "^7.4.4"
+    esutils "^2.0.2"
+
+"@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4":
+  version "7.9.2"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06"
+  integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
+"@babel/template@^7.4.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
+  version "7.8.6"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
+  integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==
+  dependencies:
+    "@babel/code-frame" "^7.8.3"
+    "@babel/parser" "^7.8.6"
+    "@babel/types" "^7.8.6"
+
+"@babel/traverse@^7.4.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892"
+  integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==
+  dependencies:
+    "@babel/code-frame" "^7.8.3"
+    "@babel/generator" "^7.9.0"
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/helper-split-export-declaration" "^7.8.3"
+    "@babel/parser" "^7.9.0"
+    "@babel/types" "^7.9.0"
+    debug "^4.1.0"
+    globals "^11.1.0"
+    lodash "^4.17.13"
+
+"@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5"
+  integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.9.0"
+    lodash "^4.17.13"
+    to-fast-properties "^2.0.0"
+
+"@iarna/toml@^2.2.0":
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.3.tgz#f060bf6eaafae4d56a7dac618980838b0696e2ab"
+  integrity sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==
+
+"@mrmlnc/readdir-enhanced@^2.2.1":
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
+  integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==
+  dependencies:
+    call-me-maybe "^1.0.1"
+    glob-to-regexp "^0.3.0"
+
+"@nodelib/fs.stat@^1.1.2":
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
+  integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
+
+"@parcel/fs@^1.11.0":
+  version "1.11.0"
+  resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-1.11.0.tgz#fb8a2be038c454ad46a50dc0554c1805f13535cd"
+  integrity sha512-86RyEqULbbVoeo8OLcv+LQ1Vq2PKBAvWTU9fCgALxuCTbbs5Ppcvll4Vr+Ko1AnmMzja/k++SzNAwJfeQXVlpA==
+  dependencies:
+    "@parcel/utils" "^1.11.0"
+    mkdirp "^0.5.1"
+    rimraf "^2.6.2"
+
+"@parcel/logger@^1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-1.11.1.tgz#c55b0744bcbe84ebc291155627f0ec406a23e2e6"
+  integrity sha512-9NF3M6UVeP2udOBDILuoEHd8VrF4vQqoWHEafymO1pfSoOMfxrSJZw1MfyAAIUN/IFp9qjcpDCUbDZB+ioVevA==
+  dependencies:
+    "@parcel/workers" "^1.11.0"
+    chalk "^2.1.0"
+    grapheme-breaker "^0.3.2"
+    ora "^2.1.0"
+    strip-ansi "^4.0.0"
+
+"@parcel/utils@^1.11.0":
+  version "1.11.0"
+  resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-1.11.0.tgz#539e08fff8af3b26eca11302be80b522674b51ea"
+  integrity sha512-cA3p4jTlaMeOtAKR/6AadanOPvKeg8VwgnHhOyfi0yClD0TZS/hi9xu12w4EzA/8NtHu0g6o4RDfcNjqN8l1AQ==
+
+"@parcel/watcher@^1.12.1":
+  version "1.12.1"
+  resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-1.12.1.tgz#b98b3df309fcab93451b5583fc38e40826696dad"
+  integrity sha512-od+uCtCxC/KoNQAIE1vWx1YTyKYY+7CTrxBJPRh3cDWw/C0tCtlBMVlrbplscGoEpt6B27KhJDCv82PBxOERNA==
+  dependencies:
+    "@parcel/utils" "^1.11.0"
+    chokidar "^2.1.5"
+
+"@parcel/workers@^1.11.0":
+  version "1.11.0"
+  resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-1.11.0.tgz#7b8dcf992806f4ad2b6cecf629839c41c2336c59"
+  integrity sha512-USSjRAAQYsZFlv43FUPdD+jEGML5/8oLF0rUzPQTtK4q9kvaXr49F5ZplyLz5lox78cLZ0TxN2bIDQ1xhOkulQ==
+  dependencies:
+    "@parcel/utils" "^1.11.0"
+    physical-cpu-count "^2.0.0"
+
+"@reduxjs/toolkit@^1.2.5":
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.2.5.tgz#149aa62da12a18a67a30495cb63fd897003f2272"
+  integrity sha512-/OWoW5mniUXAomw4+3ZhhWodcs1/SRvK2HKyxLXdW6vKgmJhiBiSHe/huHARlKWujEmGaJrkafx548GE494bCQ==
+  dependencies:
+    immer "^4.0.1"
+    redux "^4.0.0"
+    redux-devtools-extension "^2.13.8"
+    redux-immutable-state-invariant "^2.1.0"
+    redux-thunk "^2.3.0"
+    reselect "^4.0.0"
+
+"@types/color-name@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
+  integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
+
+"@types/history@*":
+  version "4.7.5"
+  resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.5.tgz#527d20ef68571a4af02ed74350164e7a67544860"
+  integrity sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw==
+
+"@types/hoist-non-react-statics@^3.3.0":
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
+  integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
+  dependencies:
+    "@types/react" "*"
+    hoist-non-react-statics "^3.3.0"
+
+"@types/node@^13.9.3":
+  version "13.9.3"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.3.tgz#6356df2647de9eac569f9a52eda3480fa9e70b4d"
+  integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==
+
+"@types/prop-types@*":
+  version "15.7.3"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
+  integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
+
+"@types/q@^1.5.1":
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
+  integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
+
+"@types/react-dom@^16.9.5":
+  version "16.9.5"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7"
+  integrity sha512-BX6RQ8s9D+2/gDhxrj8OW+YD4R+8hj7FEM/OJHGNR0KipE1h1mSsf39YeyC81qafkq+N3rU3h3RFbLSwE5VqUg==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react-redux@^7.1.7":
+  version "7.1.7"
+  resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.7.tgz#12a0c529aba660696947384a059c5c6e08185c7a"
+  integrity sha512-U+WrzeFfI83+evZE2dkZ/oF/1vjIYgqrb5dGgedkqVV8HEfDFujNgWCwHL89TDuWKb47U0nTBT6PLGq4IIogWg==
+  dependencies:
+    "@types/hoist-non-react-statics" "^3.3.0"
+    "@types/react" "*"
+    hoist-non-react-statics "^3.3.0"
+    redux "^4.0.0"
+
+"@types/react-router-dom@^5.1.3":
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.3.tgz#b5d28e7850bd274d944c0fbbe5d57e6b30d71196"
+  integrity sha512-pCq7AkOvjE65jkGS5fQwQhvUp4+4PVD9g39gXLZViP2UqFiFzsEpB3PKf0O6mdbKsewSK8N14/eegisa/0CwnA==
+  dependencies:
+    "@types/history" "*"
+    "@types/react" "*"
+    "@types/react-router" "*"
+
+"@types/react-router@*":
+  version "5.1.4"
+  resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.4.tgz#7d70bd905543cb6bcbdcc6bd98902332054f31a6"
+  integrity sha512-PZtnBuyfL07sqCJvGg3z+0+kt6fobc/xmle08jBiezLS8FrmGeiGkJnuxL/8Zgy9L83ypUhniV5atZn/L8n9MQ==
+  dependencies:
+    "@types/history" "*"
+    "@types/react" "*"
+
+"@types/react@*":
+  version "16.9.25"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.25.tgz#6ae2159b40138c792058a23c3c04fd3db49e929e"
+  integrity sha512-Dlj2V72cfYLPNscIG3/SMUOzhzj7GK3bpSrfefwt2YT9GLynvLCCZjbhyF6VsT0q0+aRACRX03TDJGb7cA0cqg==
+  dependencies:
+    "@types/prop-types" "*"
+    csstype "^2.2.0"
+
+abab@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a"
+  integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==
+
+acorn-globals@^4.3.0:
+  version "4.3.4"
+  resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7"
+  integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==
+  dependencies:
+    acorn "^6.0.1"
+    acorn-walk "^6.0.1"
+
+acorn-node@^1.6.1:
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
+  integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==
+  dependencies:
+    acorn "^7.0.0"
+    acorn-walk "^7.0.0"
+    xtend "^4.0.2"
+
+acorn-walk@^6.0.1:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
+  integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
+
+acorn-walk@^7.0.0:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e"
+  integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==
+
+acorn@^6.0.1, acorn@^6.0.4:
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
+  integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
+
+acorn@^7.0.0, acorn@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
+  integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==
+
+ajv@^6.5.5:
+  version "6.12.0"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
+  integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
+alphanum-sort@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
+  integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+  integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+ansi-regex@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
+  integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+
+ansi-styles@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+  integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
+
+ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+ansi-styles@^4.1.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
+  integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
+  dependencies:
+    "@types/color-name" "^1.1.1"
+    color-convert "^2.0.1"
+
+ansi-to-html@^0.6.4:
+  version "0.6.14"
+  resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.14.tgz#65fe6d08bba5dd9db33f44a20aec331e0010dad8"
+  integrity sha512-7ZslfB1+EnFSDO5Ju+ue5Y6It19DRnZXWv8jrGHgIlPna5Mh4jz7BV5jCbQneXNFurQcKoolaaAjHtgSBfOIuA==
+  dependencies:
+    entities "^1.1.2"
+
+anymatch@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
+  integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
+  dependencies:
+    micromatch "^3.1.4"
+    normalize-path "^2.1.1"
+
+argparse@^1.0.7:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+  dependencies:
+    sprintf-js "~1.0.2"
+
+arr-diff@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+  integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+  integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+  integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-equal@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
+  integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
+
+array-unique@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+  integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+asn1.js@^4.0.0:
+  version "4.10.1"
+  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
+  integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
+  dependencies:
+    bn.js "^4.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+asn1@~0.2.3:
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
+  integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
+  dependencies:
+    safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+  integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
+
+assert@^1.1.1:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
+  integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
+  dependencies:
+    object-assign "^4.1.1"
+    util "0.10.3"
+
+assign-symbols@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+  integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+async-each@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
+  integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
+
+async-limiter@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
+  integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+
+atob@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+autoprefixer@^9.4.5:
+  version "9.7.4"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378"
+  integrity sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==
+  dependencies:
+    browserslist "^4.8.3"
+    caniuse-lite "^1.0.30001020"
+    chalk "^2.4.2"
+    normalize-range "^0.1.2"
+    num2fraction "^1.2.2"
+    postcss "^7.0.26"
+    postcss-value-parser "^4.0.2"
+
+aws-sign2@~0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+  integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
+
+aws4@^1.8.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
+  integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
+
+babel-plugin-dynamic-import-node@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f"
+  integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==
+  dependencies:
+    object.assign "^4.1.0"
+
+babel-runtime@^6.11.6, babel-runtime@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.11.0"
+
+babel-types@^6.15.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
+  integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
+  dependencies:
+    babel-runtime "^6.26.0"
+    esutils "^2.0.2"
+    lodash "^4.17.4"
+    to-fast-properties "^1.0.3"
+
+babylon-walk@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/babylon-walk/-/babylon-walk-1.0.2.tgz#3b15a5ddbb482a78b4ce9c01c8ba181702d9d6ce"
+  integrity sha1-OxWl3btIKni0zpwByLoYFwLZ1s4=
+  dependencies:
+    babel-runtime "^6.11.6"
+    babel-types "^6.15.0"
+    lodash.clone "^4.5.0"
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base64-js@^1.0.2:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
+  integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
+
+base@^0.11.1:
+  version "0.11.2"
+  resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+  integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+  dependencies:
+    cache-base "^1.0.1"
+    class-utils "^0.3.5"
+    component-emitter "^1.2.1"
+    define-property "^1.0.0"
+    isobject "^3.0.1"
+    mixin-deep "^1.2.0"
+    pascalcase "^0.1.1"
+
+bcrypt-pbkdf@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+  integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
+  dependencies:
+    tweetnacl "^0.14.3"
+
+binary-extensions@^1.0.0:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
+  integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
+
+bindings@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
+  integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
+  dependencies:
+    file-uri-to-path "1.0.0"
+
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
+  version "4.11.8"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
+  integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
+
+boolbase@^1.0.0, boolbase@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+braces@^2.3.1, braces@^2.3.2:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
+  integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+  dependencies:
+    arr-flatten "^1.1.0"
+    array-unique "^0.3.2"
+    extend-shallow "^2.0.1"
+    fill-range "^4.0.0"
+    isobject "^3.0.1"
+    repeat-element "^1.1.2"
+    snapdragon "^0.8.1"
+    snapdragon-node "^2.0.1"
+    split-string "^3.0.2"
+    to-regex "^3.0.1"
+
+brfs@^1.2.0:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/brfs/-/brfs-1.6.1.tgz#b78ce2336d818e25eea04a0947cba6d4fb8849c3"
+  integrity sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==
+  dependencies:
+    quote-stream "^1.0.1"
+    resolve "^1.1.5"
+    static-module "^2.2.0"
+    through2 "^2.0.0"
+
+brorand@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+  integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
+
+browser-process-hrtime@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
+  integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
+
+browserify-aes@^1.0.0, browserify-aes@^1.0.4:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
+  integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
+  dependencies:
+    buffer-xor "^1.0.3"
+    cipher-base "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.3"
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+browserify-cipher@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
+  integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
+  dependencies:
+    browserify-aes "^1.0.4"
+    browserify-des "^1.0.0"
+    evp_bytestokey "^1.0.0"
+
+browserify-des@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c"
+  integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==
+  dependencies:
+    cipher-base "^1.0.1"
+    des.js "^1.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+browserify-rsa@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
+  integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=
+  dependencies:
+    bn.js "^4.1.0"
+    randombytes "^2.0.1"
+
+browserify-sign@^4.0.0:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
+  integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=
+  dependencies:
+    bn.js "^4.1.1"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.2"
+    elliptic "^6.0.0"
+    inherits "^2.0.1"
+    parse-asn1 "^5.0.0"
+
+browserify-zlib@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
+  integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==
+  dependencies:
+    pako "~1.0.5"
+
+browserslist@^4.0.0, browserslist@^4.1.0, browserslist@^4.8.3, browserslist@^4.9.1:
+  version "4.11.0"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.11.0.tgz#aef4357b10a8abda00f97aac7cd587b2082ba1ad"
+  integrity sha512-WqEC7Yr5wUH5sg6ruR++v2SGOQYpyUdYYd4tZoAq1F7y+QXoLoYGXVbxhtaIqWmAJjtNTRjVD3HuJc1OXTel2A==
+  dependencies:
+    caniuse-lite "^1.0.30001035"
+    electron-to-chromium "^1.3.380"
+    node-releases "^1.1.52"
+    pkg-up "^3.1.0"
+
+buffer-equal@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b"
+  integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=
+
+buffer-from@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+  integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
+buffer-xor@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
+  integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
+
+buffer@^4.3.0:
+  version "4.9.2"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
+  integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
+  dependencies:
+    base64-js "^1.0.2"
+    ieee754 "^1.1.4"
+    isarray "^1.0.0"
+
+builtin-status-codes@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
+  integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
+
+bytes@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
+  integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
+
+cache-base@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+  integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+  dependencies:
+    collection-visit "^1.0.0"
+    component-emitter "^1.2.1"
+    get-value "^2.0.6"
+    has-value "^1.0.0"
+    isobject "^3.0.1"
+    set-value "^2.0.0"
+    to-object-path "^0.3.0"
+    union-value "^1.0.0"
+    unset-value "^1.0.0"
+
+call-me-maybe@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
+  integrity sha1-JtII6onje1y95gJQoV8DHBak1ms=
+
+caller-callsite@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+  integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
+  dependencies:
+    callsites "^2.0.0"
+
+caller-path@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+  integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
+  dependencies:
+    caller-callsite "^2.0.0"
+
+callsites@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+  integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+
+camelcase-css@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
+  integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
+
+camelcase@^5.0.0:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+caniuse-api@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
+  integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==
+  dependencies:
+    browserslist "^4.0.0"
+    caniuse-lite "^1.0.0"
+    lodash.memoize "^4.1.2"
+    lodash.uniq "^4.5.0"
+
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001035:
+  version "1.0.30001036"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001036.tgz#930ea5272010d8bf190d859159d757c0b398caf0"
+  integrity sha512-jU8CIFIj2oR7r4W+5AKcsvWNVIb6Q6OZE3UsrXrZBHFtreT4YgTeOJtTucp+zSedEpTi3L5wASSP0LYIE3if6w==
+
+caseless@~0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+  integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
+
+chalk@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+  integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
+  dependencies:
+    ansi-styles "^2.2.1"
+    escape-string-regexp "^1.0.2"
+    has-ansi "^2.0.0"
+    strip-ansi "^3.0.0"
+    supports-color "^2.0.0"
+
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
+  integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chokidar@^2.1.5:
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
+  integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==
+  dependencies:
+    anymatch "^2.0.0"
+    async-each "^1.0.1"
+    braces "^2.3.2"
+    glob-parent "^3.1.0"
+    inherits "^2.0.3"
+    is-binary-path "^1.0.0"
+    is-glob "^4.0.0"
+    normalize-path "^3.0.0"
+    path-is-absolute "^1.0.0"
+    readdirp "^2.2.1"
+    upath "^1.1.1"
+  optionalDependencies:
+    fsevents "^1.2.7"
+
+cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
+  integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+class-utils@^0.3.5:
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+  integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+  dependencies:
+    arr-union "^3.1.0"
+    define-property "^0.2.5"
+    isobject "^3.0.0"
+    static-extend "^0.1.1"
+
+cli-cursor@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+  integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
+  dependencies:
+    restore-cursor "^2.0.0"
+
+cli-spinners@^1.1.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a"
+  integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==
+
+cliui@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
+  integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
+  dependencies:
+    string-width "^3.1.0"
+    strip-ansi "^5.2.0"
+    wrap-ansi "^5.1.0"
+
+clone@^1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+  integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+
+clone@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
+  integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
+
+coa@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
+  integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
+  dependencies:
+    "@types/q" "^1.5.1"
+    chalk "^2.4.1"
+    q "^1.1.2"
+
+collection-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+  integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+  dependencies:
+    map-visit "^1.0.0"
+    object-visit "^1.0.0"
+
+color-convert@^1.9.0, color-convert@^1.9.1:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+color-name@^1.0.0, color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-string@^1.5.2:
+  version "1.5.3"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
+  integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
+  dependencies:
+    color-name "^1.0.0"
+    simple-swizzle "^0.2.2"
+
+color@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10"
+  integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==
+  dependencies:
+    color-convert "^1.9.1"
+    color-string "^1.5.2"
+
+combined-stream@^1.0.6, combined-stream@~1.0.6:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
+command-exists@^1.2.6:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.8.tgz#715acefdd1223b9c9b37110a149c6392c2852291"
+  integrity sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw==
+
+commander@^2.11.0, commander@^2.19.0, commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+component-emitter@^1.2.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
+  integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+concat-stream@~1.6.0:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+  integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
+  dependencies:
+    buffer-from "^1.0.0"
+    inherits "^2.0.3"
+    readable-stream "^2.2.2"
+    typedarray "^0.0.6"
+
+console-browserify@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
+  integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
+
+constants-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
+  integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
+
+convert-source-map@^1.5.1, convert-source-map@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
+  integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
+  dependencies:
+    safe-buffer "~5.1.1"
+
+copy-descriptor@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+  integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+core-js-compat@^3.6.2:
+  version "3.6.4"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17"
+  integrity sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==
+  dependencies:
+    browserslist "^4.8.3"
+    semver "7.0.0"
+
+core-js@^2.4.0, core-js@^2.6.5:
+  version "2.6.11"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
+  integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
+
+core-util-is@1.0.2, core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+cosmiconfig@^5.0.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+  integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
+  dependencies:
+    import-fresh "^2.0.0"
+    is-directory "^0.3.1"
+    js-yaml "^3.13.1"
+    parse-json "^4.0.0"
+
+create-ecdh@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
+  integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==
+  dependencies:
+    bn.js "^4.1.0"
+    elliptic "^6.0.0"
+
+create-hash@^1.1.0, create-hash@^1.1.2:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
+  integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
+  dependencies:
+    cipher-base "^1.0.1"
+    inherits "^2.0.1"
+    md5.js "^1.3.4"
+    ripemd160 "^2.0.1"
+    sha.js "^2.4.0"
+
+create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
+  integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
+  dependencies:
+    cipher-base "^1.0.3"
+    create-hash "^1.1.0"
+    inherits "^2.0.1"
+    ripemd160 "^2.0.0"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+cross-spawn@^6.0.4:
+  version "6.0.5"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+  integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+  dependencies:
+    nice-try "^1.0.4"
+    path-key "^2.0.1"
+    semver "^5.5.0"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
+crypto-browserify@^3.11.0:
+  version "3.12.0"
+  resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
+  integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
+  dependencies:
+    browserify-cipher "^1.0.0"
+    browserify-sign "^4.0.0"
+    create-ecdh "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.0"
+    diffie-hellman "^5.0.0"
+    inherits "^2.0.1"
+    pbkdf2 "^3.0.3"
+    public-encrypt "^4.0.0"
+    randombytes "^2.0.0"
+    randomfill "^1.0.3"
+
+css-color-names@0.0.4, css-color-names@^0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
+  integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
+
+css-declaration-sorter@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22"
+  integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==
+  dependencies:
+    postcss "^7.0.1"
+    timsort "^0.3.0"
+
+css-modules-loader-core@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz#5908668294a1becd261ae0a4ce21b0b551f21d16"
+  integrity sha1-WQhmgpShvs0mGuCkziGwtVHyHRY=
+  dependencies:
+    icss-replace-symbols "1.1.0"
+    postcss "6.0.1"
+    postcss-modules-extract-imports "1.1.0"
+    postcss-modules-local-by-default "1.2.0"
+    postcss-modules-scope "1.1.0"
+    postcss-modules-values "1.3.0"
+
+css-select-base-adapter@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7"
+  integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
+
+css-select@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
+  integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^3.2.1"
+    domutils "^1.7.0"
+    nth-check "^1.0.2"
+
+css-selector-tokenizer@^0.7.0:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz#11e5e27c9a48d90284f22d45061c303d7a25ad87"
+  integrity sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==
+  dependencies:
+    cssesc "^3.0.0"
+    fastparse "^1.1.2"
+    regexpu-core "^4.6.0"
+
+css-tree@1.0.0-alpha.37:
+  version "1.0.0-alpha.37"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
+  integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
+  dependencies:
+    mdn-data "2.0.4"
+    source-map "^0.6.1"
+
+css-unit-converter@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996"
+  integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=
+
+css-what@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1"
+  integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==
+
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+cssnano-preset-default@^4.0.7:
+  version "4.0.7"
+  resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76"
+  integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==
+  dependencies:
+    css-declaration-sorter "^4.0.1"
+    cssnano-util-raw-cache "^4.0.1"
+    postcss "^7.0.0"
+    postcss-calc "^7.0.1"
+    postcss-colormin "^4.0.3"
+    postcss-convert-values "^4.0.1"
+    postcss-discard-comments "^4.0.2"
+    postcss-discard-duplicates "^4.0.2"
+    postcss-discard-empty "^4.0.1"
+    postcss-discard-overridden "^4.0.1"
+    postcss-merge-longhand "^4.0.11"
+    postcss-merge-rules "^4.0.3"
+    postcss-minify-font-values "^4.0.2"
+    postcss-minify-gradients "^4.0.2"
+    postcss-minify-params "^4.0.2"
+    postcss-minify-selectors "^4.0.2"
+    postcss-normalize-charset "^4.0.1"
+    postcss-normalize-display-values "^4.0.2"
+    postcss-normalize-positions "^4.0.2"
+    postcss-normalize-repeat-style "^4.0.2"
+    postcss-normalize-string "^4.0.2"
+    postcss-normalize-timing-functions "^4.0.2"
+    postcss-normalize-unicode "^4.0.1"
+    postcss-normalize-url "^4.0.1"
+    postcss-normalize-whitespace "^4.0.2"
+    postcss-ordered-values "^4.1.2"
+    postcss-reduce-initial "^4.0.3"
+    postcss-reduce-transforms "^4.0.2"
+    postcss-svgo "^4.0.2"
+    postcss-unique-selectors "^4.0.1"
+
+cssnano-util-get-arguments@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f"
+  integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=
+
+cssnano-util-get-match@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d"
+  integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=
+
+cssnano-util-raw-cache@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282"
+  integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==
+  dependencies:
+    postcss "^7.0.0"
+
+cssnano-util-same-parent@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3"
+  integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==
+
+cssnano@^4.0.0, cssnano@^4.1.10:
+  version "4.1.10"
+  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2"
+  integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==
+  dependencies:
+    cosmiconfig "^5.0.0"
+    cssnano-preset-default "^4.0.7"
+    is-resolvable "^1.0.0"
+    postcss "^7.0.0"
+
+csso@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d"
+  integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg==
+  dependencies:
+    css-tree "1.0.0-alpha.37"
+
+cssom@0.3.x, cssom@^0.3.4:
+  version "0.3.8"
+  resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
+  integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
+
+cssstyle@^1.1.1:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1"
+  integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==
+  dependencies:
+    cssom "0.3.x"
+
+csstype@^2.2.0:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098"
+  integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==
+
+dashdash@^1.12.0:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+  integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
+  dependencies:
+    assert-plus "^1.0.0"
+
+data-urls@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe"
+  integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==
+  dependencies:
+    abab "^2.0.0"
+    whatwg-mimetype "^2.2.0"
+    whatwg-url "^7.0.0"
+
+deasync@^0.1.14:
+  version "0.1.19"
+  resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.19.tgz#e7ea89fcc9ad483367e8a48fe78f508ca86286e8"
+  integrity sha512-oh3MRktfnPlLysCPpBpKZZzb4cUC/p0aA3SyRGp15lN30juJBTo/CiD0d4fR+f1kBtUQoJj1NE9RPNWQ7BQ9Mg==
+  dependencies:
+    bindings "^1.5.0"
+    node-addon-api "^1.7.1"
+
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+  dependencies:
+    ms "2.0.0"
+
+debug@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
+  integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+  dependencies:
+    ms "^2.1.1"
+
+decamelize@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+  integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
+
+decode-uri-component@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+  integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+deep-is@~0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+  integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+
+defaults@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
+  integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
+  dependencies:
+    clone "^1.0.2"
+
+define-properties@^1.1.2, define-properties@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+  integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
+  dependencies:
+    object-keys "^1.0.12"
+
+define-property@^0.2.5:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+  integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+  dependencies:
+    is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+  integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+  dependencies:
+    is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+  integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+  dependencies:
+    is-descriptor "^1.0.2"
+    isobject "^3.0.1"
+
+defined@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
+  integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
+
+depd@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+  integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
+
+des.js@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
+  integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==
+  dependencies:
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+destroy@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
+  integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
+
+detective@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b"
+  integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==
+  dependencies:
+    acorn-node "^1.6.1"
+    defined "^1.0.0"
+    minimist "^1.1.1"
+
+diffie-hellman@^5.0.0:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
+  integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
+  dependencies:
+    bn.js "^4.1.0"
+    miller-rabin "^4.0.0"
+    randombytes "^2.0.0"
+
+dom-serializer@0:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
+  integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
+  dependencies:
+    domelementtype "^2.0.1"
+    entities "^2.0.0"
+
+domain-browser@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
+  integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
+
+domelementtype@1, domelementtype@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
+  integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
+
+domelementtype@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
+  integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
+
+domexception@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
+  integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
+  dependencies:
+    webidl-conversions "^4.0.2"
+
+domhandler@^2.3.0:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
+  integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==
+  dependencies:
+    domelementtype "1"
+
+domutils@^1.5.1, domutils@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
+  integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+dot-prop@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
+  integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
+  dependencies:
+    is-obj "^2.0.0"
+
+dotenv-expand@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
+  integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
+
+dotenv@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef"
+  integrity sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==
+
+duplexer2@~0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
+  integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=
+  dependencies:
+    readable-stream "^2.0.2"
+
+ecc-jsbn@~0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+  integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
+  dependencies:
+    jsbn "~0.1.0"
+    safer-buffer "^2.1.0"
+
+ee-first@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+  integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
+
+electron-to-chromium@^1.3.380:
+  version "1.3.381"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.381.tgz#952678ff91a5f36175a3832358a6dd2de3bf62b7"
+  integrity sha512-JQBpVUr83l+QOqPQpj2SbOve1bBE4ACpmwcMNqWlZmfib7jccxJ02qFNichDpZ5LS4Zsqc985NIPKegBIZjK8Q==
+
+elliptic@^6.0.0:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762"
+  integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==
+  dependencies:
+    bn.js "^4.4.0"
+    brorand "^1.0.1"
+    hash.js "^1.0.0"
+    hmac-drbg "^1.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.0"
+
+emoji-regex@^7.0.1:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
+  integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+
+encodeurl@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+  integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+
+entities@^1.1.1, entities@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
+  integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
+
+entities@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
+  integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
+
+envinfo@^7.3.1:
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.0.tgz#91410bb6db262fb4f1409bd506e9ff57e91023f4"
+  integrity sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ==
+
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+es-abstract@^1.17.0-next.1, es-abstract@^1.17.2:
+  version "1.17.5"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
+  integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==
+  dependencies:
+    es-to-primitive "^1.2.1"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.1"
+    is-callable "^1.1.5"
+    is-regex "^1.0.5"
+    object-inspect "^1.7.0"
+    object-keys "^1.1.1"
+    object.assign "^4.1.0"
+    string.prototype.trimleft "^2.1.1"
+    string.prototype.trimright "^2.1.1"
+
+es-to-primitive@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+  integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
+  dependencies:
+    is-callable "^1.1.4"
+    is-date-object "^1.0.1"
+    is-symbol "^1.0.2"
+
+escape-html@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+escodegen@^1.11.0, escodegen@^1.11.1:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457"
+  integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==
+  dependencies:
+    esprima "^4.0.1"
+    estraverse "^4.2.0"
+    esutils "^2.0.2"
+    optionator "^0.8.1"
+  optionalDependencies:
+    source-map "~0.6.1"
+
+escodegen@~1.9.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2"
+  integrity sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==
+  dependencies:
+    esprima "^3.1.3"
+    estraverse "^4.2.0"
+    esutils "^2.0.2"
+    optionator "^0.8.1"
+  optionalDependencies:
+    source-map "~0.6.1"
+
+esprima@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+  integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=
+
+esprima@^4.0.0, esprima@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+  integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+estraverse@^4.2.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+  integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+esutils@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+  integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
+
+events@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59"
+  integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==
+
+evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
+  integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
+  dependencies:
+    md5.js "^1.3.4"
+    safe-buffer "^5.1.1"
+
+expand-brackets@^2.1.4:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+  integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+  dependencies:
+    debug "^2.3.3"
+    define-property "^0.2.5"
+    extend-shallow "^2.0.1"
+    posix-character-classes "^0.1.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+extend-shallow@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+  integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+  dependencies:
+    is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+  integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+  dependencies:
+    assign-symbols "^1.0.0"
+    is-extendable "^1.0.1"
+
+extend@~3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+  integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+extglob@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+  integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+  dependencies:
+    array-unique "^0.3.2"
+    define-property "^1.0.0"
+    expand-brackets "^2.1.4"
+    extend-shallow "^2.0.1"
+    fragment-cache "^0.2.1"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+extsprintf@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+  integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
+
+extsprintf@^1.2.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
+  integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
+
+falafel@^2.1.0:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.2.4.tgz#b5d86c060c2412a43166243cb1bce44d1abd2819"
+  integrity sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ==
+  dependencies:
+    acorn "^7.1.1"
+    foreach "^2.0.5"
+    isarray "^2.0.1"
+    object-keys "^1.0.6"
+
+fast-deep-equal@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
+  integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
+
+fast-glob@^2.2.2:
+  version "2.2.7"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
+  integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==
+  dependencies:
+    "@mrmlnc/readdir-enhanced" "^2.2.1"
+    "@nodelib/fs.stat" "^1.1.2"
+    glob-parent "^3.1.0"
+    is-glob "^4.0.0"
+    merge2 "^1.2.3"
+    micromatch "^3.1.10"
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@~2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+fastparse@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
+  integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
+
+file-uri-to-path@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+  integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
+
+filesize@^3.6.0:
+  version "3.6.1"
+  resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317"
+  integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==
+
+fill-range@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+  integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+    to-regex-range "^2.1.0"
+
+find-up@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+  integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+  dependencies:
+    locate-path "^3.0.0"
+
+for-in@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+  integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+foreach@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
+  integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
+
+forever-agent@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+  integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+
+form-data@~2.3.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+  integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.6"
+    mime-types "^2.1.12"
+
+fragment-cache@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+  integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+  dependencies:
+    map-cache "^0.2.2"
+
+fresh@0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+  integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
+
+fs-extra@^8.0.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+fsevents@^1.2.7:
+  version "1.2.12"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.12.tgz#db7e0d8ec3b0b45724fd4d83d43554a8f1f0de5c"
+  integrity sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==
+  dependencies:
+    bindings "^1.5.0"
+    nan "^2.12.1"
+
+function-bind@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+gensync@^1.0.0-beta.1:
+  version "1.0.0-beta.1"
+  resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
+  integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==
+
+get-caller-file@^2.0.1:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-port@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
+  integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=
+
+get-value@^2.0.3, get-value@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+  integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+getpass@^0.1.1:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+  integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
+  dependencies:
+    assert-plus "^1.0.0"
+
+glob-parent@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
+  integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=
+  dependencies:
+    is-glob "^3.1.0"
+    path-dirname "^1.0.0"
+
+glob-to-regexp@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
+  integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
+
+glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
+  version "7.1.6"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+  integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+globals@^11.1.0:
+  version "11.12.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
+  integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
+
+grapheme-breaker@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/grapheme-breaker/-/grapheme-breaker-0.3.2.tgz#5b9e6b78c3832452d2ba2bb1cb830f96276410ac"
+  integrity sha1-W55reMODJFLSuiuxy4MPlidkEKw=
+  dependencies:
+    brfs "^1.2.0"
+    unicode-trie "^0.3.1"
+
+gud@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
+  integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
+
+har-schema@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+  integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
+
+har-validator@~5.1.3:
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
+  integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
+  dependencies:
+    ajv "^6.5.5"
+    har-schema "^2.0.0"
+
+has-ansi@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+  integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+has-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+  integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-symbols@^1.0.0, has-symbols@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
+  integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
+
+has-value@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+  integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+  dependencies:
+    get-value "^2.0.3"
+    has-values "^0.1.4"
+    isobject "^2.0.0"
+
+has-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+  integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+  dependencies:
+    get-value "^2.0.6"
+    has-values "^1.0.0"
+    isobject "^3.0.0"
+
+has-values@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+  integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+  integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+  dependencies:
+    is-number "^3.0.0"
+    kind-of "^4.0.0"
+
+has@^1.0.0, has@^1.0.1, has@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+  dependencies:
+    function-bind "^1.1.1"
+
+hash-base@^3.0.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
+  integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+hash.js@^1.0.0, hash.js@^1.0.3:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
+  integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
+  dependencies:
+    inherits "^2.0.3"
+    minimalistic-assert "^1.0.1"
+
+hex-color-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
+  integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
+
+history@^4.9.0:
+  version "4.10.1"
+  resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
+  integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
+  dependencies:
+    "@babel/runtime" "^7.1.2"
+    loose-envify "^1.2.0"
+    resolve-pathname "^3.0.0"
+    tiny-invariant "^1.0.2"
+    tiny-warning "^1.0.0"
+    value-equal "^1.0.1"
+
+hmac-drbg@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+  integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
+  dependencies:
+    hash.js "^1.0.3"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.1"
+
+hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
+  integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
+  dependencies:
+    react-is "^16.7.0"
+
+hsl-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e"
+  integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=
+
+hsla-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38"
+  integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg=
+
+html-comment-regex@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
+  integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
+
+html-encoding-sniffer@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
+  integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==
+  dependencies:
+    whatwg-encoding "^1.0.1"
+
+html-tags@^1.0.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-1.2.0.tgz#c78de65b5663aa597989dd2b7ab49200d7e4db98"
+  integrity sha1-x43mW1Zjqll5id0rerSSANfk25g=
+
+htmlnano@^0.2.2:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-0.2.5.tgz#134fd9548c7cbe51c8508ce434a3f9488cff1b0b"
+  integrity sha512-X1iPSwXG/iF9bVs+/obt2n6F64uH0ETkA8zp7qFDmLW9/+A6ueHGeb/+qD67T21qUY22owZPMdawljN50ajkqA==
+  dependencies:
+    cssnano "^4.1.10"
+    normalize-html-whitespace "^1.0.0"
+    posthtml "^0.12.0"
+    posthtml-render "^1.1.5"
+    purgecss "^1.4.0"
+    svgo "^1.3.2"
+    terser "^4.3.9"
+    uncss "^0.17.2"
+
+htmlparser2@^3.9.2:
+  version "3.10.1"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
+  integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
+  dependencies:
+    domelementtype "^1.3.1"
+    domhandler "^2.3.0"
+    domutils "^1.5.1"
+    entities "^1.1.1"
+    inherits "^2.0.1"
+    readable-stream "^3.1.1"
+
+http-errors@~1.7.2:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
+  integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.4"
+    setprototypeof "1.1.1"
+    statuses ">= 1.5.0 < 2"
+    toidentifier "1.0.0"
+
+http-signature@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+  integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+  dependencies:
+    assert-plus "^1.0.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
+https-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
+  integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
+
+iconv-lite@0.4.24:
+  version "0.4.24"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
+  integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
+
+ieee754@^1.1.4:
+  version "1.1.13"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
+  integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
+
+immer@^4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/immer/-/immer-4.0.2.tgz#9ff0fcdf88e06f92618a5978ceecb5884e633559"
+  integrity sha512-Q/tm+yKqnKy4RIBmmtISBlhXuSDrB69e9EKTYiIenIKQkXBQir43w+kN/eGiax3wt1J0O1b2fYcNqLSbEcXA7w==
+
+import-fresh@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+  integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
+  dependencies:
+    caller-path "^2.0.0"
+    resolve-from "^3.0.0"
+
+indexes-of@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
+  integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inherits@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
+  integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
+
+inherits@2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+  integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
+invariant@^2.1.0, invariant@^2.2.2, invariant@^2.2.4:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+  integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+  dependencies:
+    loose-envify "^1.0.0"
+
+is-absolute-url@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
+  integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
+
+is-absolute-url@^3.0.1:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698"
+  integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==
+
+is-accessor-descriptor@^0.1.6:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+  integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+  integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
+
+is-arrayish@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+  integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
+
+is-binary-path@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+  integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=
+  dependencies:
+    binary-extensions "^1.0.0"
+
+is-buffer@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+  integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-callable@^1.1.4, is-callable@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
+  integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==
+
+is-color-stop@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345"
+  integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=
+  dependencies:
+    css-color-names "^0.0.4"
+    hex-color-regex "^1.1.0"
+    hsl-regex "^1.0.0"
+    hsla-regex "^1.0.0"
+    rgb-regex "^1.0.1"
+    rgba-regex "^1.0.0"
+
+is-data-descriptor@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+  integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+  integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-date-object@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
+  integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
+
+is-descriptor@^0.1.0:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+  integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+  dependencies:
+    is-accessor-descriptor "^0.1.6"
+    is-data-descriptor "^0.1.4"
+    kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+  integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+  dependencies:
+    is-accessor-descriptor "^1.0.0"
+    is-data-descriptor "^1.0.0"
+    kind-of "^6.0.2"
+
+is-directory@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
+  integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+  integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+  integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+  dependencies:
+    is-plain-object "^2.0.4"
+
+is-extglob@^2.1.0, is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+  integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-glob@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
+  integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=
+  dependencies:
+    is-extglob "^2.1.0"
+
+is-glob@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+  integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-html@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-html/-/is-html-1.1.0.tgz#e04f1c18d39485111396f9a0273eab51af218464"
+  integrity sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ=
+  dependencies:
+    html-tags "^1.0.0"
+
+is-number@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+  integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-obj@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
+  integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
+
+is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+  integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+  dependencies:
+    isobject "^3.0.1"
+
+is-regex@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
+  integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==
+  dependencies:
+    has "^1.0.3"
+
+is-resolvable@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
+  integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
+
+is-svg@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75"
+  integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==
+  dependencies:
+    html-comment-regex "^1.1.0"
+
+is-symbol@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
+  integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
+  dependencies:
+    has-symbols "^1.0.1"
+
+is-typedarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+  integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+
+is-url@^1.2.2:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
+  integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
+
+is-windows@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+  integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+is-wsl@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+  integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
+
+isarray@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+  integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
+
+isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isarray@^2.0.1:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
+  integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+  integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+  dependencies:
+    isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+  integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+  integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^3.10.0, js-yaml@^3.13.1:
+  version "3.13.1"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
+  integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
+jsbn@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+  integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
+
+jsdom@^14.1.0:
+  version "14.1.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b"
+  integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==
+  dependencies:
+    abab "^2.0.0"
+    acorn "^6.0.4"
+    acorn-globals "^4.3.0"
+    array-equal "^1.0.0"
+    cssom "^0.3.4"
+    cssstyle "^1.1.1"
+    data-urls "^1.1.0"
+    domexception "^1.0.1"
+    escodegen "^1.11.0"
+    html-encoding-sniffer "^1.0.2"
+    nwsapi "^2.1.3"
+    parse5 "5.1.0"
+    pn "^1.1.0"
+    request "^2.88.0"
+    request-promise-native "^1.0.5"
+    saxes "^3.1.9"
+    symbol-tree "^3.2.2"
+    tough-cookie "^2.5.0"
+    w3c-hr-time "^1.0.1"
+    w3c-xmlserializer "^1.1.2"
+    webidl-conversions "^4.0.2"
+    whatwg-encoding "^1.0.5"
+    whatwg-mimetype "^2.3.0"
+    whatwg-url "^7.0.0"
+    ws "^6.1.2"
+    xml-name-validator "^3.0.0"
+
+jsesc@^2.5.1:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+  integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+jsesc@~0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+  integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
+
+json-parse-better-errors@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+  integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema@0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+  integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
+
+json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+  integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
+
+json5@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
+  integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
+  dependencies:
+    minimist "^1.2.0"
+
+json5@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e"
+  integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==
+  dependencies:
+    minimist "^1.2.5"
+
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+jsprim@^1.2.2:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
+  integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
+  dependencies:
+    assert-plus "1.0.0"
+    extsprintf "1.3.0"
+    json-schema "0.2.3"
+    verror "1.10.0"
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+  integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+  integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+  integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+  integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+leven@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
+  integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+
+levenary@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77"
+  integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==
+  dependencies:
+    leven "^3.1.0"
+
+levn@~0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+  integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
+  dependencies:
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+
+locate-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+  integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+  dependencies:
+    p-locate "^3.0.0"
+    path-exists "^3.0.0"
+
+lodash.clone@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6"
+  integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=
+
+lodash.memoize@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+  integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
+
+lodash.sortby@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
+  integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
+
+lodash.toarray@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
+  integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE=
+
+lodash.uniq@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+  integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
+
+lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.4:
+  version "4.17.15"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+  integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+log-symbols@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
+  integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
+  dependencies:
+    chalk "^2.0.1"
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+magic-string@^0.22.4:
+  version "0.22.5"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e"
+  integrity sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==
+  dependencies:
+    vlq "^0.2.2"
+
+map-cache@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+  integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+  integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+  dependencies:
+    object-visit "^1.0.0"
+
+md5.js@^1.3.4:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
+  integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+mdn-data@2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
+  integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
+
+merge-source-map@1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.0.4.tgz#a5de46538dae84d4114cc5ea02b4772a6346701f"
+  integrity sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=
+  dependencies:
+    source-map "^0.5.6"
+
+merge2@^1.2.3:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81"
+  integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==
+
+micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4:
+  version "3.1.10"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+  integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+  dependencies:
+    arr-diff "^4.0.0"
+    array-unique "^0.3.2"
+    braces "^2.3.1"
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    extglob "^2.0.4"
+    fragment-cache "^0.2.1"
+    kind-of "^6.0.2"
+    nanomatch "^1.2.9"
+    object.pick "^1.3.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.2"
+
+miller-rabin@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
+  integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
+  dependencies:
+    bn.js "^4.0.0"
+    brorand "^1.0.1"
+
+mime-db@1.43.0:
+  version "1.43.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
+  integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
+
+mime-types@^2.1.12, mime-types@~2.1.19:
+  version "2.1.26"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
+  integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
+  dependencies:
+    mime-db "1.43.0"
+
+mime@1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mimic-fn@^1.0.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
+  integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
+
+mini-create-react-context@^0.3.0:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189"
+  integrity sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==
+  dependencies:
+    "@babel/runtime" "^7.4.0"
+    gud "^1.0.0"
+    tiny-warning "^1.0.2"
+
+minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+  integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
+
+minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+  integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
+
+minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+  integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
+mixin-deep@^1.2.0:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
+  integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
+  dependencies:
+    for-in "^1.0.2"
+    is-extendable "^1.0.1"
+
+mkdirp@^0.5.1, mkdirp@~0.5.1:
+  version "0.5.4"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512"
+  integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==
+  dependencies:
+    minimist "^1.2.5"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+  integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
+
+ms@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+nan@^2.12.1:
+  version "2.14.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
+  integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
+
+nanomatch@^1.2.9:
+  version "1.2.13"
+  resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
+  integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+  dependencies:
+    arr-diff "^4.0.0"
+    array-unique "^0.3.2"
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    fragment-cache "^0.2.1"
+    is-windows "^1.0.2"
+    kind-of "^6.0.2"
+    object.pick "^1.3.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+nice-try@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+  integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+
+node-addon-api@^1.7.1:
+  version "1.7.1"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.1.tgz#cf813cd69bb8d9100f6bdca6755fc268f54ac492"
+  integrity sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ==
+
+node-emoji@^1.8.1:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da"
+  integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==
+  dependencies:
+    lodash.toarray "^4.4.0"
+
+node-forge@^0.7.1:
+  version "0.7.6"
+  resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
+  integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==
+
+node-libs-browser@^2.0.0:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
+  integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
+  dependencies:
+    assert "^1.1.1"
+    browserify-zlib "^0.2.0"
+    buffer "^4.3.0"
+    console-browserify "^1.1.0"
+    constants-browserify "^1.0.0"
+    crypto-browserify "^3.11.0"
+    domain-browser "^1.1.1"
+    events "^3.0.0"
+    https-browserify "^1.0.0"
+    os-browserify "^0.3.0"
+    path-browserify "0.0.1"
+    process "^0.11.10"
+    punycode "^1.2.4"
+    querystring-es3 "^0.2.0"
+    readable-stream "^2.3.3"
+    stream-browserify "^2.0.1"
+    stream-http "^2.7.2"
+    string_decoder "^1.0.0"
+    timers-browserify "^2.0.4"
+    tty-browserify "0.0.0"
+    url "^0.11.0"
+    util "^0.11.0"
+    vm-browserify "^1.0.1"
+
+node-releases@^1.1.52:
+  version "1.1.52"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9"
+  integrity sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ==
+  dependencies:
+    semver "^6.3.0"
+
+normalize-html-whitespace@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz#5e3c8e192f1b06c3b9eee4b7e7f28854c7601e34"
+  integrity sha512-9ui7CGtOOlehQu0t/OhhlmDyc71mKVlv+4vF+me4iZLPrNtRL2xoquEdfZxasC/bdQi/Hr3iTrpyRKIG+ocabA==
+
+normalize-path@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+  integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
+  dependencies:
+    remove-trailing-separator "^1.0.1"
+
+normalize-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+normalize-range@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+  integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
+
+normalize-url@^3.0.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
+  integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
+
+normalize.css@^8.0.1:
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3"
+  integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==
+
+nth-check@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
+  integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
+  dependencies:
+    boolbase "~1.0.0"
+
+num2fraction@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
+  integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
+
+nwsapi@^2.1.3:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
+  integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
+
+oauth-sign@~0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+  integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+
+object-assign@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-copy@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+  integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+  dependencies:
+    copy-descriptor "^0.1.0"
+    define-property "^0.2.5"
+    kind-of "^3.0.3"
+
+object-inspect@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
+  integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
+
+object-inspect@~1.4.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.4.1.tgz#37ffb10e71adaf3748d05f713b4c9452f402cbc4"
+  integrity sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw==
+
+object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+  integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object-visit@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+  integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+  dependencies:
+    isobject "^3.0.0"
+
+object.assign@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+  integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.1"
+    has-symbols "^1.0.0"
+    object-keys "^1.0.11"
+
+object.getownpropertydescriptors@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
+  integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.0-next.1"
+
+object.pick@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+  integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+  dependencies:
+    isobject "^3.0.1"
+
+object.values@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
+  integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.0-next.1"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+
+on-finished@~2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+  integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
+  dependencies:
+    ee-first "1.1.1"
+
+once@^1.3.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
+onetime@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+  integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
+  dependencies:
+    mimic-fn "^1.0.0"
+
+opn@^5.1.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc"
+  integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==
+  dependencies:
+    is-wsl "^1.1.0"
+
+optionator@^0.8.1:
+  version "0.8.3"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
+  integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
+  dependencies:
+    deep-is "~0.1.3"
+    fast-levenshtein "~2.0.6"
+    levn "~0.3.0"
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+    word-wrap "~1.2.3"
+
+ora@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b"
+  integrity sha512-hNNlAd3gfv/iPmsNxYoAPLvxg7HuPozww7fFonMZvL84tP6Ox5igfk5j/+a9rtJJwqMgKK+JgWsAQik5o0HTLA==
+  dependencies:
+    chalk "^2.3.1"
+    cli-cursor "^2.1.0"
+    cli-spinners "^1.1.0"
+    log-symbols "^2.2.0"
+    strip-ansi "^4.0.0"
+    wcwidth "^1.0.1"
+
+os-browserify@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
+  integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
+
+p-limit@^2.0.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
+  integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
+  dependencies:
+    p-try "^2.0.0"
+
+p-locate@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+  integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+  dependencies:
+    p-limit "^2.0.0"
+
+p-try@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+  integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+pako@^0.2.5:
+  version "0.2.9"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
+  integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=
+
+pako@~1.0.5:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
+  integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
+
+parcel-bundler@^1.12.4:
+  version "1.12.4"
+  resolved "https://registry.yarnpkg.com/parcel-bundler/-/parcel-bundler-1.12.4.tgz#31223f4ab4d00323a109fce28d5e46775409a9ee"
+  integrity sha512-G+iZGGiPEXcRzw0fiRxWYCKxdt/F7l9a0xkiU4XbcVRJCSlBnioWEwJMutOCCpoQmaQtjB4RBHDGIHN85AIhLQ==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/core" "^7.4.4"
+    "@babel/generator" "^7.4.4"
+    "@babel/parser" "^7.4.4"
+    "@babel/plugin-transform-flow-strip-types" "^7.4.4"
+    "@babel/plugin-transform-modules-commonjs" "^7.4.4"
+    "@babel/plugin-transform-react-jsx" "^7.0.0"
+    "@babel/preset-env" "^7.4.4"
+    "@babel/runtime" "^7.4.4"
+    "@babel/template" "^7.4.4"
+    "@babel/traverse" "^7.4.4"
+    "@babel/types" "^7.4.4"
+    "@iarna/toml" "^2.2.0"
+    "@parcel/fs" "^1.11.0"
+    "@parcel/logger" "^1.11.1"
+    "@parcel/utils" "^1.11.0"
+    "@parcel/watcher" "^1.12.1"
+    "@parcel/workers" "^1.11.0"
+    ansi-to-html "^0.6.4"
+    babylon-walk "^1.0.2"
+    browserslist "^4.1.0"
+    chalk "^2.1.0"
+    clone "^2.1.1"
+    command-exists "^1.2.6"
+    commander "^2.11.0"
+    core-js "^2.6.5"
+    cross-spawn "^6.0.4"
+    css-modules-loader-core "^1.1.0"
+    cssnano "^4.0.0"
+    deasync "^0.1.14"
+    dotenv "^5.0.0"
+    dotenv-expand "^5.1.0"
+    envinfo "^7.3.1"
+    fast-glob "^2.2.2"
+    filesize "^3.6.0"
+    get-port "^3.2.0"
+    htmlnano "^0.2.2"
+    is-glob "^4.0.0"
+    is-url "^1.2.2"
+    js-yaml "^3.10.0"
+    json5 "^1.0.1"
+    micromatch "^3.0.4"
+    mkdirp "^0.5.1"
+    node-forge "^0.7.1"
+    node-libs-browser "^2.0.0"
+    opn "^5.1.0"
+    postcss "^7.0.11"
+    postcss-value-parser "^3.3.1"
+    posthtml "^0.11.2"
+    posthtml-parser "^0.4.0"
+    posthtml-render "^1.1.3"
+    resolve "^1.4.0"
+    semver "^5.4.1"
+    serialize-to-js "^3.0.0"
+    serve-static "^1.12.4"
+    source-map "0.6.1"
+    terser "^3.7.3"
+    v8-compile-cache "^2.0.0"
+    ws "^5.1.1"
+
+parse-asn1@^5.0.0:
+  version "5.1.5"
+  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e"
+  integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==
+  dependencies:
+    asn1.js "^4.0.0"
+    browserify-aes "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.0"
+    pbkdf2 "^3.0.3"
+    safe-buffer "^5.1.1"
+
+parse-json@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+  integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+  dependencies:
+    error-ex "^1.3.1"
+    json-parse-better-errors "^1.0.1"
+
+parse5@5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2"
+  integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==
+
+parseurl@~1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+  integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+pascalcase@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+  integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-browserify@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
+  integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
+
+path-dirname@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
+  integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
+
+path-exists@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+  integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-key@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+  integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
+path-parse@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+  integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+path-to-regexp@^1.7.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
+  integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
+  dependencies:
+    isarray "0.0.1"
+
+pbkdf2@^3.0.3:
+  version "3.0.17"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
+  integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==
+  dependencies:
+    create-hash "^1.1.2"
+    create-hmac "^1.1.4"
+    ripemd160 "^2.0.1"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+performance-now@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+  integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+
+physical-cpu-count@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz#18de2f97e4bf7a9551ad7511942b5496f7aba660"
+  integrity sha1-GN4vl+S/epVRrXURlCtUlverpmA=
+
+pkg-up@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
+  integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
+  dependencies:
+    find-up "^3.0.0"
+
+pn@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
+  integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
+
+posix-character-classes@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+  integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+postcss-calc@^7.0.1:
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.2.tgz#504efcd008ca0273120568b0792b16cdcde8aac1"
+  integrity sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==
+  dependencies:
+    postcss "^7.0.27"
+    postcss-selector-parser "^6.0.2"
+    postcss-value-parser "^4.0.2"
+
+postcss-colormin@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381"
+  integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==
+  dependencies:
+    browserslist "^4.0.0"
+    color "^3.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-convert-values@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f"
+  integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==
+  dependencies:
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-discard-comments@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033"
+  integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-discard-duplicates@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb"
+  integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-discard-empty@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765"
+  integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-discard-overridden@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57"
+  integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-functions@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-functions/-/postcss-functions-3.0.0.tgz#0e94d01444700a481de20de4d55fb2640564250e"
+  integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=
+  dependencies:
+    glob "^7.1.2"
+    object-assign "^4.1.1"
+    postcss "^6.0.9"
+    postcss-value-parser "^3.3.0"
+
+postcss-js@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-2.0.3.tgz#a96f0f23ff3d08cec7dc5b11bf11c5f8077cdab9"
+  integrity sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==
+  dependencies:
+    camelcase-css "^2.0.1"
+    postcss "^7.0.18"
+
+postcss-merge-longhand@^4.0.11:
+  version "4.0.11"
+  resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24"
+  integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==
+  dependencies:
+    css-color-names "0.0.4"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    stylehacks "^4.0.0"
+
+postcss-merge-rules@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650"
+  integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==
+  dependencies:
+    browserslist "^4.0.0"
+    caniuse-api "^3.0.0"
+    cssnano-util-same-parent "^4.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
+    vendors "^1.0.0"
+
+postcss-minify-font-values@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6"
+  integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==
+  dependencies:
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-minify-gradients@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471"
+  integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    is-color-stop "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-minify-params@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874"
+  integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    browserslist "^4.0.0"
+    cssnano-util-get-arguments "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    uniqs "^2.0.0"
+
+postcss-minify-selectors@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8"
+  integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
+
+postcss-modules-extract-imports@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb"
+  integrity sha1-thTJcgvmgW6u41+zpfqh26agXds=
+  dependencies:
+    postcss "^6.0.1"
+
+postcss-modules-local-by-default@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
+  integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=
+  dependencies:
+    css-selector-tokenizer "^0.7.0"
+    postcss "^6.0.1"
+
+postcss-modules-scope@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
+  integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A=
+  dependencies:
+    css-selector-tokenizer "^0.7.0"
+    postcss "^6.0.1"
+
+postcss-modules-values@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
+  integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=
+  dependencies:
+    icss-replace-symbols "^1.1.0"
+    postcss "^6.0.1"
+
+postcss-nested@^4.1.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-4.2.1.tgz#4bc2e5b35e3b1e481ff81e23b700da7f82a8b248"
+  integrity sha512-AMayXX8tS0HCp4O4lolp4ygj9wBn32DJWXvG6gCv+ZvJrEa00GUxJcJEEzMh87BIe6FrWdYkpR2cuyqHKrxmXw==
+  dependencies:
+    postcss "^7.0.21"
+    postcss-selector-parser "^6.0.2"
+
+postcss-normalize-charset@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4"
+  integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-normalize-display-values@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a"
+  integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-positions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f"
+  integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-repeat-style@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c"
+  integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-string@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c"
+  integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==
+  dependencies:
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-timing-functions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9"
+  integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-unicode@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb"
+  integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==
+  dependencies:
+    browserslist "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-url@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1"
+  integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==
+  dependencies:
+    is-absolute-url "^2.0.0"
+    normalize-url "^3.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-whitespace@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82"
+  integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==
+  dependencies:
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-ordered-values@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee"
+  integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-reduce-initial@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df"
+  integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==
+  dependencies:
+    browserslist "^4.0.0"
+    caniuse-api "^3.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+
+postcss-reduce-transforms@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29"
+  integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-selector-parser@6.0.2, postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
+  integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
+  dependencies:
+    cssesc "^3.0.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-selector-parser@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270"
+  integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==
+  dependencies:
+    dot-prop "^5.2.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-svgo@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258"
+  integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==
+  dependencies:
+    is-svg "^3.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    svgo "^1.0.0"
+
+postcss-unique-selectors@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac"
+  integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    postcss "^7.0.0"
+    uniqs "^2.0.0"
+
+postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
+  integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
+
+postcss-value-parser@^4.0.2:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d"
+  integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==
+
+postcss@6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2"
+  integrity sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=
+  dependencies:
+    chalk "^1.1.3"
+    source-map "^0.5.6"
+    supports-color "^3.2.3"
+
+postcss@^6.0.1, postcss@^6.0.9:
+  version "6.0.23"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
+  integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
+  dependencies:
+    chalk "^2.4.1"
+    source-map "^0.6.1"
+    supports-color "^5.4.0"
+
+postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.11, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.18, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.27:
+  version "7.0.27"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
+  integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==
+  dependencies:
+    chalk "^2.4.2"
+    source-map "^0.6.1"
+    supports-color "^6.1.0"
+
+posthtml-parser@^0.4.0, posthtml-parser@^0.4.1:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.4.2.tgz#a132bbdf0cd4bc199d34f322f5c1599385d7c6c1"
+  integrity sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg==
+  dependencies:
+    htmlparser2 "^3.9.2"
+
+posthtml-render@^1.1.3, posthtml-render@^1.1.5:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-1.2.0.tgz#3df0c800a8bbb95af583a94748520469477addf4"
+  integrity sha512-dQB+hoAKDtnI94RZm/wxBUH9My8OJcXd0uhWmGh2c7tVtQ85A+OS3yCN3LNbFtPz3bViwBJXAeoi+CBGMXM0DA==
+
+posthtml@^0.11.2:
+  version "0.11.6"
+  resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.11.6.tgz#e349d51af7929d0683b9d8c3abd8166beecc90a8"
+  integrity sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw==
+  dependencies:
+    posthtml-parser "^0.4.1"
+    posthtml-render "^1.1.5"
+
+posthtml@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.12.0.tgz#6e2a2fcd774eaed1a419a95c5cc3a92b676a40a6"
+  integrity sha512-aNUEP/SfKUXAt+ghG51LC5MmafChBZeslVe/SSdfKIgLGUVRE68mrMF4V8XbH07ZifM91tCSuxY3eHIFLlecQw==
+  dependencies:
+    posthtml-parser "^0.4.1"
+    posthtml-render "^1.1.5"
+
+prelude-ls@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+  integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
+
+prettier@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.2.tgz#1ba8f3eb92231e769b7fcd7cb73ae1b6b74ade08"
+  integrity sha512-5xJQIPT8BraI7ZnaDwSbu5zLrB6vvi8hVV58yHQ+QK64qrY40dULy0HSRlQ2/2IdzeBpjhDkqdcFBnFeDEMVdg==
+
+pretty-hrtime@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
+  integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
+
+private@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+  integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
+
+process-nextick-args@~2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+  integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
+
+prop-types@^15.6.2, prop-types@^15.7.2:
+  version "15.7.2"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
+  integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
+  dependencies:
+    loose-envify "^1.4.0"
+    object-assign "^4.1.1"
+    react-is "^16.8.1"
+
+psl@^1.1.28:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
+  integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
+
+public-encrypt@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
+  integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==
+  dependencies:
+    bn.js "^4.1.0"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    parse-asn1 "^5.0.0"
+    randombytes "^2.0.1"
+    safe-buffer "^5.1.2"
+
+punycode@1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+  integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
+
+punycode@^1.2.4:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+  integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
+
+punycode@^2.1.0, punycode@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+purgecss@^1.4.0:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-1.4.2.tgz#67ab50cb4f5c163fcefde56002467c974e577f41"
+  integrity sha512-hkOreFTgiyMHMmC2BxzdIw5DuC6kxAbP/gGOGd3MEsF3+5m69rIvUEPaxrnoUtfODTFKe9hcXjGwC6jcjoyhOw==
+  dependencies:
+    glob "^7.1.3"
+    postcss "^7.0.14"
+    postcss-selector-parser "^6.0.0"
+    yargs "^14.0.0"
+
+q@^1.1.2:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
+  integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
+
+qs@~6.5.2:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+  integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+
+querystring-es3@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
+  integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
+
+querystring@0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
+  integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
+
+quote-stream@^1.0.1, quote-stream@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2"
+  integrity sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=
+  dependencies:
+    buffer-equal "0.0.1"
+    minimist "^1.1.3"
+    through2 "^2.0.0"
+
+randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+  integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+  dependencies:
+    safe-buffer "^5.1.0"
+
+randomfill@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
+  integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
+  dependencies:
+    randombytes "^2.0.5"
+    safe-buffer "^5.1.0"
+
+range-parser@~1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+  integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+react-dom@^16.13.1:
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
+  integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.2"
+    scheduler "^0.19.1"
+
+react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.9.0:
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+react-redux@^7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
+  integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==
+  dependencies:
+    "@babel/runtime" "^7.5.5"
+    hoist-non-react-statics "^3.3.0"
+    loose-envify "^1.4.0"
+    prop-types "^15.7.2"
+    react-is "^16.9.0"
+
+react-router-dom@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18"
+  integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==
+  dependencies:
+    "@babel/runtime" "^7.1.2"
+    history "^4.9.0"
+    loose-envify "^1.3.1"
+    prop-types "^15.6.2"
+    react-router "5.1.2"
+    tiny-invariant "^1.0.2"
+    tiny-warning "^1.0.0"
+
+react-router@5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418"
+  integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==
+  dependencies:
+    "@babel/runtime" "^7.1.2"
+    history "^4.9.0"
+    hoist-non-react-statics "^3.1.0"
+    loose-envify "^1.3.1"
+    mini-create-react-context "^0.3.0"
+    path-to-regexp "^1.7.0"
+    prop-types "^15.6.2"
+    react-is "^16.6.0"
+    tiny-invariant "^1.0.2"
+    tiny-warning "^1.0.0"
+
+react@^16.13.1:
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
+  integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.2"
+
+readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6:
+  version "2.3.7"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
+  integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~2.0.0"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.1.1"
+    util-deprecate "~1.0.1"
+
+readable-stream@^3.1.1:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+  integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
+readdirp@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
+  integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==
+  dependencies:
+    graceful-fs "^4.1.11"
+    micromatch "^3.1.10"
+    readable-stream "^2.0.2"
+
+reduce-css-calc@^2.1.6:
+  version "2.1.7"
+  resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz#1ace2e02c286d78abcd01fd92bfe8097ab0602c2"
+  integrity sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA==
+  dependencies:
+    css-unit-converter "^1.1.1"
+    postcss-value-parser "^3.3.0"
+
+redux-devtools-extension@^2.13.8:
+  version "2.13.8"
+  resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1"
+  integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==
+
+redux-immutable-state-invariant@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/redux-immutable-state-invariant/-/redux-immutable-state-invariant-2.1.0.tgz#308fd3cc7415a0e7f11f51ec997b6379c7055ce1"
+  integrity sha512-3czbDKs35FwiBRsx/3KabUk5zSOoTXC+cgVofGkpBNv3jQcqIe5JrHcF5AmVt7B/4hyJ8MijBIpCJ8cife6yJg==
+  dependencies:
+    invariant "^2.1.0"
+    json-stringify-safe "^5.0.1"
+
+redux-thunk@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
+  integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
+
+redux@^4.0.0:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
+  integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
+  dependencies:
+    loose-envify "^1.4.0"
+    symbol-observable "^1.2.0"
+
+regenerate-unicode-properties@^8.2.0:
+  version "8.2.0"
+  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
+  integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==
+  dependencies:
+    regenerate "^1.4.0"
+
+regenerate@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
+  integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
+
+regenerator-runtime@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+  integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
+regenerator-runtime@^0.13.4:
+  version "0.13.5"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
+  integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
+
+regenerator-transform@^0.14.2:
+  version "0.14.4"
+  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7"
+  integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==
+  dependencies:
+    "@babel/runtime" "^7.8.4"
+    private "^0.1.8"
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+  integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+  dependencies:
+    extend-shallow "^3.0.2"
+    safe-regex "^1.1.0"
+
+regexpu-core@^4.6.0, regexpu-core@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938"
+  integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==
+  dependencies:
+    regenerate "^1.4.0"
+    regenerate-unicode-properties "^8.2.0"
+    regjsgen "^0.5.1"
+    regjsparser "^0.6.4"
+    unicode-match-property-ecmascript "^1.0.4"
+    unicode-match-property-value-ecmascript "^1.2.0"
+
+regjsgen@^0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c"
+  integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==
+
+regjsparser@^0.6.4:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272"
+  integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==
+  dependencies:
+    jsesc "~0.5.0"
+
+remove-trailing-separator@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
+  integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
+
+repeat-element@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+  integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+  integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+request-promise-core@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9"
+  integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==
+  dependencies:
+    lodash "^4.17.15"
+
+request-promise-native@^1.0.5:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36"
+  integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==
+  dependencies:
+    request-promise-core "1.1.3"
+    stealthy-require "^1.1.1"
+    tough-cookie "^2.3.3"
+
+request@^2.88.0:
+  version "2.88.2"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
+  integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
+  dependencies:
+    aws-sign2 "~0.7.0"
+    aws4 "^1.8.0"
+    caseless "~0.12.0"
+    combined-stream "~1.0.6"
+    extend "~3.0.2"
+    forever-agent "~0.6.1"
+    form-data "~2.3.2"
+    har-validator "~5.1.3"
+    http-signature "~1.2.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.19"
+    oauth-sign "~0.9.0"
+    performance-now "^2.1.0"
+    qs "~6.5.2"
+    safe-buffer "^5.1.2"
+    tough-cookie "~2.5.0"
+    tunnel-agent "^0.6.0"
+    uuid "^3.3.2"
+
+require-directory@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+  integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+
+require-main-filename@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+  integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
+reselect@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
+  integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==
+
+resolve-from@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+  integrity sha1-six699nWiBvItuZTM17rywoYh0g=
+
+resolve-pathname@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
+  integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
+
+resolve-url@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+  integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+resolve@^1.1.5, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.4.0:
+  version "1.15.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
+  integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
+  dependencies:
+    path-parse "^1.0.6"
+
+restore-cursor@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+  integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
+  dependencies:
+    onetime "^2.0.0"
+    signal-exit "^3.0.2"
+
+ret@~0.1.10:
+  version "0.1.15"
+  resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+  integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rgb-regex@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
+  integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE=
+
+rgba-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
+  integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
+
+rimraf@^2.6.2:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+  integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+  dependencies:
+    glob "^7.1.3"
+
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
+  integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+
+safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
+  integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
+
+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+  integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+  dependencies:
+    ret "~0.1.10"
+
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sax@~1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+  integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+saxes@^3.1.9:
+  version "3.1.11"
+  resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b"
+  integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==
+  dependencies:
+    xmlchars "^2.1.1"
+
+scheduler@^0.19.1:
+  version "0.19.1"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
+  integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+
+semver@7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
+  integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
+
+semver@^5.4.1, semver@^5.5.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+semver@^6.3.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+send@0.17.1:
+  version "0.17.1"
+  resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
+  integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
+  dependencies:
+    debug "2.6.9"
+    depd "~1.1.2"
+    destroy "~1.0.4"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    etag "~1.8.1"
+    fresh "0.5.2"
+    http-errors "~1.7.2"
+    mime "1.6.0"
+    ms "2.1.1"
+    on-finished "~2.3.0"
+    range-parser "~1.2.1"
+    statuses "~1.5.0"
+
+serialize-to-js@^3.0.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/serialize-to-js/-/serialize-to-js-3.1.1.tgz#b3e77d0568ee4a60bfe66287f991e104d3a1a4ac"
+  integrity sha512-F+NGU0UHMBO4Q965tjw7rvieNVjlH6Lqi2emq/Lc9LUURYJbiCzmpi4Cy1OOjjVPtxu0c+NE85LU6968Wko5ZA==
+
+serve-static@^1.12.4:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
+  integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
+  dependencies:
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    parseurl "~1.3.3"
+    send "0.17.1"
+
+set-blocking@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+  integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+set-value@^2.0.0, set-value@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
+  integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.3"
+    split-string "^3.0.1"
+
+setimmediate@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+  integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
+
+setprototypeof@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
+  integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
+
+sha.js@^2.4.0, sha.js@^2.4.8:
+  version "2.4.11"
+  resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
+  integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+shallow-copy@~0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170"
+  integrity sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=
+
+shebang-command@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+  integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+  dependencies:
+    shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+  integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
+signal-exit@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+
+simple-swizzle@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
+  integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=
+  dependencies:
+    is-arrayish "^0.3.1"
+
+snapdragon-node@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+  integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+  dependencies:
+    define-property "^1.0.0"
+    isobject "^3.0.0"
+    snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+  integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+  dependencies:
+    kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+  integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+  dependencies:
+    base "^0.11.1"
+    debug "^2.2.0"
+    define-property "^0.2.5"
+    extend-shallow "^2.0.1"
+    map-cache "^0.2.2"
+    source-map "^0.5.6"
+    source-map-resolve "^0.5.0"
+    use "^3.1.0"
+
+source-map-resolve@^0.5.0:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
+  integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==
+  dependencies:
+    atob "^2.1.2"
+    decode-uri-component "^0.2.0"
+    resolve-url "^0.2.1"
+    source-map-url "^0.4.0"
+    urix "^0.1.0"
+
+source-map-support@~0.5.10, source-map-support@~0.5.12:
+  version "0.5.16"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
+  integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map-url@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+  integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+source-map@^0.5.0, source-map@^0.5.6:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+  integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+split-string@^3.0.1, split-string@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+  integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+  dependencies:
+    extend-shallow "^3.0.0"
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+  integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+
+sshpk@^1.7.0:
+  version "1.16.1"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
+  integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
+  dependencies:
+    asn1 "~0.2.3"
+    assert-plus "^1.0.0"
+    bcrypt-pbkdf "^1.0.0"
+    dashdash "^1.12.0"
+    ecc-jsbn "~0.1.1"
+    getpass "^0.1.1"
+    jsbn "~0.1.0"
+    safer-buffer "^2.0.2"
+    tweetnacl "~0.14.0"
+
+stable@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+static-eval@^2.0.0:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.5.tgz#f0782e66999c4b3651cda99d9ce59c507d188f71"
+  integrity sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA==
+  dependencies:
+    escodegen "^1.11.1"
+
+static-extend@^0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+  integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+  dependencies:
+    define-property "^0.2.5"
+    object-copy "^0.1.0"
+
+static-module@^2.2.0:
+  version "2.2.5"
+  resolved "https://registry.yarnpkg.com/static-module/-/static-module-2.2.5.tgz#bd40abceae33da6b7afb84a0e4329ff8852bfbbf"
+  integrity sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ==
+  dependencies:
+    concat-stream "~1.6.0"
+    convert-source-map "^1.5.1"
+    duplexer2 "~0.1.4"
+    escodegen "~1.9.0"
+    falafel "^2.1.0"
+    has "^1.0.1"
+    magic-string "^0.22.4"
+    merge-source-map "1.0.4"
+    object-inspect "~1.4.0"
+    quote-stream "~1.0.2"
+    readable-stream "~2.3.3"
+    shallow-copy "~0.0.1"
+    static-eval "^2.0.0"
+    through2 "~2.0.3"
+
+"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+  integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+
+stealthy-require@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
+  integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
+
+stream-browserify@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
+  integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "^2.0.2"
+
+stream-http@^2.7.2:
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
+  integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==
+  dependencies:
+    builtin-status-codes "^3.0.0"
+    inherits "^2.0.1"
+    readable-stream "^2.3.6"
+    to-arraybuffer "^1.0.0"
+    xtend "^4.0.0"
+
+string-width@^3.0.0, string-width@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
+  integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
+  dependencies:
+    emoji-regex "^7.0.1"
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^5.1.0"
+
+string.prototype.trimleft@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"
+  integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==
+  dependencies:
+    define-properties "^1.1.3"
+    function-bind "^1.1.1"
+
+string.prototype.trimright@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9"
+  integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==
+  dependencies:
+    define-properties "^1.1.3"
+    function-bind "^1.1.1"
+
+string_decoder@^1.0.0, string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
+strip-ansi@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+  integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+  integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+  dependencies:
+    ansi-regex "^3.0.0"
+
+strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
+  integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
+  dependencies:
+    ansi-regex "^4.1.0"
+
+stylehacks@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
+  integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==
+  dependencies:
+    browserslist "^4.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
+
+supports-color@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+  integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
+
+supports-color@^3.2.3:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+  integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
+  dependencies:
+    has-flag "^1.0.0"
+
+supports-color@^5.3.0, supports-color@^5.4.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
+  integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
+  integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
+  dependencies:
+    has-flag "^4.0.0"
+
+svgo@^1.0.0, svgo@^1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
+  integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
+  dependencies:
+    chalk "^2.4.1"
+    coa "^2.0.2"
+    css-select "^2.0.0"
+    css-select-base-adapter "^0.1.1"
+    css-tree "1.0.0-alpha.37"
+    csso "^4.0.2"
+    js-yaml "^3.13.1"
+    mkdirp "~0.5.1"
+    object.values "^1.1.0"
+    sax "~1.2.4"
+    stable "^0.1.8"
+    unquote "~1.1.1"
+    util.promisify "~1.0.0"
+
+symbol-observable@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+  integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
+symbol-tree@^3.2.2:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
+  integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+
+tailwindcss@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.2.0.tgz#5df317cebac4f3131f275d258a39da1ba3a0f291"
+  integrity sha512-CKvY0ytB3ze5qvynG7qv4XSpQtFNGPbu9pUn8qFdkqgD8Yo/vGss8mhzbqls44YCXTl4G62p3qVZBj45qrd6FQ==
+  dependencies:
+    autoprefixer "^9.4.5"
+    bytes "^3.0.0"
+    chalk "^3.0.0"
+    detective "^5.2.0"
+    fs-extra "^8.0.0"
+    lodash "^4.17.15"
+    node-emoji "^1.8.1"
+    normalize.css "^8.0.1"
+    postcss "^7.0.11"
+    postcss-functions "^3.0.0"
+    postcss-js "^2.0.0"
+    postcss-nested "^4.1.1"
+    postcss-selector-parser "^6.0.0"
+    pretty-hrtime "^1.0.3"
+    reduce-css-calc "^2.1.6"
+    resolve "^1.14.2"
+
+terser@^3.7.3:
+  version "3.17.0"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2"
+  integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==
+  dependencies:
+    commander "^2.19.0"
+    source-map "~0.6.1"
+    source-map-support "~0.5.10"
+
+terser@^4.3.9:
+  version "4.6.7"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.7.tgz#478d7f9394ec1907f0e488c5f6a6a9a2bad55e72"
+  integrity sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==
+  dependencies:
+    commander "^2.20.0"
+    source-map "~0.6.1"
+    source-map-support "~0.5.12"
+
+through2@^2.0.0, through2@~2.0.3:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
+  integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
+  dependencies:
+    readable-stream "~2.3.6"
+    xtend "~4.0.1"
+
+timers-browserify@^2.0.4:
+  version "2.0.11"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f"
+  integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==
+  dependencies:
+    setimmediate "^1.0.4"
+
+timsort@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
+  integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
+
+tiny-inflate@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4"
+  integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==
+
+tiny-invariant@^1.0.2:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
+  integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
+
+tiny-warning@^1.0.0, tiny-warning@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
+  integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
+
+to-arraybuffer@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
+  integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
+
+to-fast-properties@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+  integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
+
+to-fast-properties@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+  integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+to-object-path@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+  integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+  dependencies:
+    kind-of "^3.0.2"
+
+to-regex-range@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+  integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+  dependencies:
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+  integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+  dependencies:
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    regex-not "^1.0.2"
+    safe-regex "^1.1.0"
+
+toidentifier@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
+  integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
+
+tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
+  integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
+  dependencies:
+    psl "^1.1.28"
+    punycode "^2.1.1"
+
+tr46@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
+  integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
+  dependencies:
+    punycode "^2.1.0"
+
+tty-browserify@0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
+  integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
+
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
+  dependencies:
+    safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+  integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
+
+type-check@~0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+  integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
+  dependencies:
+    prelude-ls "~1.1.2"
+
+typedarray@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+  integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+
+typescript@^3.8.3:
+  version "3.8.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
+  integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==
+
+uncss@^0.17.2:
+  version "0.17.3"
+  resolved "https://registry.yarnpkg.com/uncss/-/uncss-0.17.3.tgz#50fc1eb4ed573ffff763458d801cd86e4d69ea11"
+  integrity sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog==
+  dependencies:
+    commander "^2.20.0"
+    glob "^7.1.4"
+    is-absolute-url "^3.0.1"
+    is-html "^1.1.0"
+    jsdom "^14.1.0"
+    lodash "^4.17.15"
+    postcss "^7.0.17"
+    postcss-selector-parser "6.0.2"
+    request "^2.88.0"
+
+unicode-canonical-property-names-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
+  integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
+
+unicode-match-property-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
+  integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
+  dependencies:
+    unicode-canonical-property-names-ecmascript "^1.0.4"
+    unicode-property-aliases-ecmascript "^1.0.4"
+
+unicode-match-property-value-ecmascript@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531"
+  integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==
+
+unicode-property-aliases-ecmascript@^1.0.4:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4"
+  integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==
+
+unicode-trie@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/unicode-trie/-/unicode-trie-0.3.1.tgz#d671dddd89101a08bac37b6a5161010602052085"
+  integrity sha1-1nHd3YkQGgi6w3tqUWEBBgIFIIU=
+  dependencies:
+    pako "^0.2.5"
+    tiny-inflate "^1.0.0"
+
+union-value@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
+  integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==
+  dependencies:
+    arr-union "^3.1.0"
+    get-value "^2.0.6"
+    is-extendable "^0.1.1"
+    set-value "^2.0.1"
+
+uniq@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+  integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
+
+uniqs@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
+  integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
+
+universalify@^0.1.0:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+  integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+unquote@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
+  integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
+
+unset-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+  integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+  dependencies:
+    has-value "^0.3.1"
+    isobject "^3.0.0"
+
+upath@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
+  integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
+
+uri-js@^4.2.2:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
+  integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+  dependencies:
+    punycode "^2.1.0"
+
+urix@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+  integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+url@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
+  integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=
+  dependencies:
+    punycode "1.3.2"
+    querystring "0.2.0"
+
+use@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+  integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+util-deprecate@^1.0.1, util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+util.promisify@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
+  integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.2"
+    has-symbols "^1.0.1"
+    object.getownpropertydescriptors "^2.1.0"
+
+util@0.10.3:
+  version "0.10.3"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
+  integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
+  dependencies:
+    inherits "2.0.1"
+
+util@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
+  integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==
+  dependencies:
+    inherits "2.0.3"
+
+uuid@^3.3.2:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+  integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+
+v8-compile-cache@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
+  integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==
+
+value-equal@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
+  integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
+
+vendors@^1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e"
+  integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==
+
+verror@1.10.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+  integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
+  dependencies:
+    assert-plus "^1.0.0"
+    core-util-is "1.0.2"
+    extsprintf "^1.2.0"
+
+vlq@^0.2.2:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26"
+  integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==
+
+vm-browserify@^1.0.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
+  integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
+
+w3c-hr-time@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
+  integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
+  dependencies:
+    browser-process-hrtime "^1.0.0"
+
+w3c-xmlserializer@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794"
+  integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==
+  dependencies:
+    domexception "^1.0.1"
+    webidl-conversions "^4.0.2"
+    xml-name-validator "^3.0.0"
+
+wcwidth@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+  integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
+  dependencies:
+    defaults "^1.0.3"
+
+webidl-conversions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
+  integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
+
+whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
+  integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
+  dependencies:
+    iconv-lite "0.4.24"
+
+whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
+  integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
+
+whatwg-url@^7.0.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
+  integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
+  dependencies:
+    lodash.sortby "^4.7.0"
+    tr46 "^1.0.1"
+    webidl-conversions "^4.0.2"
+
+which-module@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+  integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+
+which@^1.2.9:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+  integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+  dependencies:
+    isexe "^2.0.0"
+
+word-wrap@~1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+  integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wrap-ansi@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
+  integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
+  dependencies:
+    ansi-styles "^3.2.0"
+    string-width "^3.0.0"
+    strip-ansi "^5.0.0"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+ws@^5.1.1:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
+  integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==
+  dependencies:
+    async-limiter "~1.0.0"
+
+ws@^6.1.2:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
+  integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
+  dependencies:
+    async-limiter "~1.0.0"
+
+xml-name-validator@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
+  integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
+
+xmlchars@^2.1.1:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
+  integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
+
+xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+  integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+
+y18n@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+  integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+
+yargs-parser@^15.0.1:
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3"
+  integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
+yargs@^14.0.0:
+  version "14.2.3"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414"
+  integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==
+  dependencies:
+    cliui "^5.0.0"
+    decamelize "^1.2.0"
+    find-up "^3.0.0"
+    get-caller-file "^2.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^2.0.0"
+    set-blocking "^2.0.0"
+    string-width "^3.0.0"
+    which-module "^2.0.0"
+    y18n "^4.0.0"
+    yargs-parser "^15.0.1"
diff --git a/users/wpcarro/buildHaskell/default.nix b/users/wpcarro/buildHaskell/default.nix
new file mode 100644
index 0000000000..2f0fd9e1c2
--- /dev/null
+++ b/users/wpcarro/buildHaskell/default.nix
@@ -0,0 +1,35 @@
+{ pkgs, ... }:
+
+{
+  # Create a nix-shell for Haskell development.
+  shell = { deps }:
+    let
+      ghc = pkgs.haskellPackages.ghcWithPackages (hpkgs: deps hpkgs);
+    in
+    pkgs.mkShell {
+      buildInputs = [ ghc ];
+    };
+
+  # Build a Haskell executable. This assumes a project directory with a
+  # top-level Main.hs.
+  # - `name`: You can find the result at ./result/$name
+  # - `srcs`: Will be passed to `srcs` field of `pkgs.stdenv.mkDerivation`.
+  # - `deps`: A function that accepts `hpkgs` and returns a list of Haskell
+  # - `ghcExtensions`: A list of strings representing the language extensions to
+  #   use.
+  program = { name, srcs, deps, ghcExtensions }:
+    let
+      ghc = pkgs.haskellPackages.ghcWithPackages (hpkgs: deps hpkgs);
+    in
+    pkgs.stdenv.mkDerivation {
+      name = name;
+      buildInputs = [ ];
+      srcs = srcs;
+      buildPhase = ''
+        ${ghc}/bin/ghc -Wall Main.hs ${pkgs.lib.concatMapStrings (x: "-X${x} ") ghcExtensions}
+      '';
+      installPhase = ''
+        mkdir -p $out && mv Main $out/${name}
+      '';
+    };
+}
diff --git a/users/wpcarro/ci/pipelines/post-receive.nix b/users/wpcarro/ci/pipelines/post-receive.nix
new file mode 100644
index 0000000000..09b8990e13
--- /dev/null
+++ b/users/wpcarro/ci/pipelines/post-receive.nix
@@ -0,0 +1,14 @@
+{ pkgs, depot, ... }:
+
+let
+  inherit (builtins) path toJSON;
+
+  pipeline.steps = [
+    {
+      key = "lint-secrets";
+      command = "${pkgs.git-secrets}/bin/git-secrets --scan-history";
+      label = ":broom: lint secrets";
+    }
+  ];
+in
+pkgs.writeText "pipeline.yaml" (toJSON pipeline)
diff --git a/users/wpcarro/ci/secret-patterns.txt b/users/wpcarro/ci/secret-patterns.txt
new file mode 100644
index 0000000000..cbf58a1e74
--- /dev/null
+++ b/users/wpcarro/ci/secret-patterns.txt
@@ -0,0 +1,9 @@
+(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}
+("|')?(AWS|aws|Aws)?_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)("|')?\s*(:|=>|=)\s*("|')?[A-Za-z0-9/\+=]{40}("|')?
+("|')?(AWS|aws|Aws)?_?(ACCOUNT|account|Account)_?(ID|id|Id)?("|')?\s*(:|=>|=)\s*("|')?[0-9]{4}\-?[0-9]{4}\-?[0-9]{4}("|')?
+AIza[0-9A-Za-z_-]{35}
+[0-9]+-[0-9A-Za-z_]{32}\.apps\.googleusercontent\.com
+(^|[^0-9A-Za-z/+])1/[0-9A-Za-z_-]{43}
+(^|[^0-9A-Za-z/+])1/[0-9A-Za-z_-]{64}
+ya29\.[0-9A-Za-z_-]+
+(sk|pk)_(test|live)_[a-zA-Z0-9]{99}
diff --git a/users/wpcarro/common.nix b/users/wpcarro/common.nix
new file mode 100644
index 0000000000..582b63fc4c
--- /dev/null
+++ b/users/wpcarro/common.nix
@@ -0,0 +1,83 @@
+{ depot, pkgs, ... }:
+
+let
+  inherit (depot.users) wpcarro;
+in
+{
+  programs = {
+    fish.enable = true;
+
+    gnupg.agent.enable = true;
+
+    ssh = {
+      startAgent = true;
+      extraConfig = ''
+        AddKeysToAgent yes
+      '';
+    };
+
+    git = {
+      enable = true;
+      config = {
+        user.name = "William Carroll";
+        user.email = "wpcarro@gmail.com";
+      };
+    };
+  };
+
+  services = {
+    # Remodel the system clipboard as a list instead of a point.
+    clipmenu.enable = true;
+
+    # TODO(wpcarro): broken in nixpkgs as of 2023-10-04
+    locate.enable = false;
+
+    depot.automatic-gc = {
+      enable = true;
+      interval = "1 hour";
+      diskThreshold = 16; # GiB
+      maxFreed = 10; # GiB
+      preserveGenerations = "14d";
+    };
+  };
+
+  # Command-line tools I commonly used and want available on most (or all) of my
+  # machines.
+  shell-utils = with pkgs; [
+    age
+    bat
+    coreutils
+    direnv
+    diskus
+    emacs
+    fd
+    fzf
+    git
+    gnupg
+    htop
+    jq
+    nmap
+    passage
+    python3
+    rink
+    ripgrep
+    tldr
+    tokei
+    tree
+    vim
+    whois
+    # TODO(wpcarro): Debug this failing build.
+    # wpcarro.tools.simple_vim
+    xclip
+    zip
+  ] ++
+  (if pkgs.stdenv.isLinux then [
+    mkpasswd
+    sysz
+    # This depends on compiler-rt-libc-10.0.1, which is marked as broken on
+    # aarch64-darwin, but depot sets `allowBroken = true`, which means any
+    # build that depends on dig will fail on OSX (e.g. emacs).
+    # https://cs.tvl.fyi/github.com/NixOS/nixpkgs@e9b195248c6cd7961a453b10294aea9ab58e01b4/-/blob/pkgs/development/compilers/llvm/10/compiler-rt/default.nix?L122
+    dig
+  ] else [ ]);
+}
diff --git a/users/wpcarro/configs/.config/nixpkgs/config.nix b/users/wpcarro/configs/.config/nixpkgs/config.nix
new file mode 100644
index 0000000000..1dd1750ae0
--- /dev/null
+++ b/users/wpcarro/configs/.config/nixpkgs/config.nix
@@ -0,0 +1,3 @@
+{
+  allowUnfree = true;
+}
diff --git a/users/wpcarro/configs/.config/nvim/init.vim b/users/wpcarro/configs/.config/nvim/init.vim
new file mode 100644
index 0000000000..57cfe7ea6a
--- /dev/null
+++ b/users/wpcarro/configs/.config/nvim/init.vim
@@ -0,0 +1,668 @@
+" -- BEGIN: Vundle config --
+set nocompatible              " be iMproved, required
+filetype off                  " required
+
+" set the runtime path to include Vundle and initialize
+" share Vundle between vim and neovim
+set rtp+=~/.vim/bundle/Vundle.vim
+set rtp+=~/.config/nvim/bundle/Vundle.vim
+call vundle#begin()
+" alternatively, pass a path where Vundle should install plugins
+"call vundle#begin('~/some/path/here')
+
+" let Vundle manage Vundle, required
+Plugin 'VundleVim/Vundle.vim'
+
+" Rust IDE features
+Plugin 'racer-rust/vim-racer'
+
+set hidden
+let g:racer_experimental_completer = 1
+autocmd FileType rust nmap         gd <Plug>(rust-def)
+autocmd FileType rust nmap         gs <Plug>(rust-def-split)
+autocmd FileType rust nmap         gx <Plug>(rust-def-vertical)
+autocmd FileType rust nmap <leader>gd <Plug>(rust-doc)
+
+Plugin 'xolox/vim-misc'
+
+" The following are examples of different formats supported.
+" Keep Plugin commands between vundle#begin/end.
+
+" Displays git information in airline.
+Plugin 'tpope/vim-fugitive'
+
+" easier file navigation
+Plugin 'tpope/vim-vinegar'
+
+" Displays git-tracked C*UD ops within gutter.
+Plugin 'airblade/vim-gitgutter'
+
+" Fuzzy-finder
+Plugin 'kien/ctrlp.vim'
+
+" Grep file contents
+Plugin 'mileszs/ack.vim'
+
+" Syntax and other light-weight suppor for a variety of languages
+Plugin 'sheerun/vim-polyglot'
+
+" Themes
+Plugin 'deviantfero/wpgtk.vim'
+Plugin 'rainglow/vim'
+
+
+" Executes shell commands and pipes output into new Vim buffer.
+Plugin 'sjl/clam.vim'
+
+" Multiple cursors for simultaneous edits.
+" NOTE: use <C-n> to run miltiple cursors not <C-d>
+Plugin 'terryma/vim-multiple-cursors'
+
+" Visualize buffers
+Plugin 'vim-airline/vim-airline'
+Plugin 'vim-airline/vim-airline-themes'
+
+" Visually align assignments
+Plugin 'godlygeek/tabular'
+
+" Visually Highlight and comment code.
+Plugin 'tpope/vim-commentary'
+
+" Macros for quotes, parens, etc.
+Plugin 'tpope/vim-surround'
+
+" Allows Plugins to be repeated with `.` character
+Plugin 'tpope/vim-repeat'
+
+" Pairs of mappings
+Plugin 'tpope/vim-unimpaired'
+
+" LISPs support
+Plugin 'guns/vim-sexp'
+Plugin 'tpope/vim-sexp-mappings-for-regular-people'
+let g:sexp_enable_insert_mode_mappings = 0
+let g:sexp_filetypes = ''
+
+" Seamlessly navigate Vim and Tmux with similar bindings.
+Plugin 'christoomey/vim-tmux-navigator'
+
+" Async `:make` for code linting etc.
+Plugin 'neomake/neomake'
+
+" Better buffer mgt than CtrlP
+Plugin 'yegappan/mru'
+
+Plugin 'zanglg/nova.vim'
+
+" Emulates Emacs's Helm Swoop search
+Plugin 'pelodelfuego/vim-swoop'
+
+" Transparent encryption + decryption
+Plugin 'jamessan/vim-gnupg'
+
+" Javascript auto-formatting
+" Plugin 'prettier/vim-prettier', {
+"   \ 'do': 'yarn install',
+  " \ 'for': ['javascript', 'typescript', 'css', 'less', 'scss', 'json', 'graphql', 'markdown'] }
+
+" Support Org mode
+Plugin 'jceb/vim-orgmode'
+
+" Autocompletion
+Plugin 'junegunn/fzf'
+
+" Text objects made easy
+Plugin 'kana/vim-textobj-user'
+
+" Elixir text objects
+Plugin 'andyl/vim-textobj-elixir'
+
+" Making HTML editing faster
+Plugin 'mattn/emmet-vim'
+
+" Snippets for all languages
+Plugin 'honza/vim-snippets'
+
+" Automatic bracket insertion
+Plugin 'jiangmiao/auto-pairs'
+
+" Linting & error warnings
+Plugin 'vim-syntastic/syntastic'
+
+" Angular.js support
+Plugin 'burnettk/vim-angular'
+
+" Asynchronous Linting Engine
+Plugin 'w0rp/ale'
+
+call vundle#end()            " required
+filetype plugin indent on    " required
+" Put your non-Plugin stuff after this line
+" -- END: Vundle config --
+
+" Changes <leader> to <space> character.
+let mapleader = " "
+
+
+" Highlight column width
+set textwidth=80
+set colorcolumn=+0
+
+" autoreload a file when it changes on disk
+set autoread
+
+" default to case-insensitive searching
+set ignorecase
+
+" JSX configuration
+let g:jsx_ext_required = 0
+
+
+autocmd FileType reason nnoremap <buffer> gd :call LanguageClient_textDocument_definition()<CR>
+autocmd FileType reason nnoremap <buffer> gf :call LanguageClient_textDocument_formatting()<CR>
+autocmd FileType reason nnoremap <buffer> gh :call LanguageClient_textDocument_hover()<CR>
+autocmd FileType reason nnoremap <buffer> gr :call LanguageClient_textDocument_rename()<CR>
+
+" Replace <CR> with G for faster navigation
+nnoremap <CR> G
+onoremap <CR> G
+vnoremap <CR> G
+
+" Mirror ZLE KBD
+inoremap <M-'> :echo "Working"<CR>
+
+" Syntastic configuration
+set statusline+=%#warningmsg#
+set statusline+=%{SyntasticStatuslineFlag()}
+set statusline+=%*
+
+let g:syntastic_always_populate_loc_list = 1
+let g:syntastic_auto_loc_list = 1
+let g:syntastic_check_on_open = 1
+let g:syntastic_check_on_wq = 0
+" let g:syntastic_javascript_checkers = ['eslint']
+let g:syntastic_javascript_eslint_generic = 1
+" this is a hack to prevent a false negative
+" https://github.com/vim-syntastic/syntastic/issues/1692
+" let g:syntastic_javascript_eslint_exec = '/bin/ls'
+" let g:syntastic_javascript_eslint_exe = 'npx eslint'
+" let g:syntastic_javascript_eslint_args = '-f compact'
+
+" javascript autocompletion
+" autocmd FileType javascript setlocal omnifunc=javascriptcomplete#CompleteJS
+" autocmd FileType javascript nnoremap <buffer> gf :Prettier<CR>
+
+" Maximize the current window
+" Similar to Tmux mapping alt-z in my tmux.conf
+nnoremap t% :tab sp<CR>
+
+" Allow C-g to act like C-c the way it does in Emacs
+cnoremap <C-g> <C-c>
+
+" Prettier configuration
+" let g:prettier#exec_cmd_async = 1
+" force Prettier to run on files even without the @format pragma
+" let g:prettier#autoformat = 0
+
+
+" Basic settings
+" Thin cursor on INSERT mode
+if has('nvim')
+  let $NVIM_TUI_ENABLE_CURSOR_SHAPE = 1
+endif
+
+set number
+set nowrap
+set tabstop=2
+set expandtab
+set shiftwidth=2
+set background=dark
+
+syntax enable
+colorscheme peacock
+
+" Vim in terminal cannot have a different font from the one set within your
+" terminal. However, this setting will set the font for the GUI version.
+if has('gui_running')
+  set guifont=Operator\ Mono:h12
+endif
+
+if has('termguicolors')
+  set termguicolors
+endif
+
+if &term =~# '^screen'
+  let &t_8f = "\<Esc>[38;2;%lu;%lu;%lum"
+  let &t_8b = "\<Esc>[48;2;%lu;%lu;%lum"
+endif
+
+set history=1000
+set undolevels=1000
+
+set t_Co=255
+
+" Support italics
+highlight Comment cterm=italic
+
+
+" quickly edit popular configuration files
+nnoremap <leader>ev :vsplit $MYVIMRC<CR>
+nnoremap <leader>ee :vsplit ~/.emacs.d/init.el<CR>
+nnoremap <leader>ez :vsplit ~/.zshrc<CR>
+nnoremap <leader>ea :vsplit ~/aliases.zsh<CR>
+nnoremap <leader>ef :vsplit ~/functions.zsh<CR>
+nnoremap <leader>el :vsplit ~/variables.zsh<CR>
+nnoremap <leader>ex :vsplit ~/.Xresources<CR>
+
+" quickly source your vimrc
+nnoremap <leader>sv :source $MYVIMRC<CR>
+
+" quickly edit your snippets
+nnoremap <leader>es :vsplit<CR>:edit ~/.vim/bundle/vim-snippets/snippets/reason.snippets<CR>
+
+
+" Auto resize window splits
+autocmd VimResized * wincmd =
+
+
+" Neomake Settings
+autocmd! BufWritePost * Neomake
+
+" Elixir linting
+let g:neomake_elixir_credo_maker = {
+      \ 'exe': 'mix',
+      \ 'args': ['credo', 'list', '%:p', '--format=oneline'],
+      \ 'errorformat':
+      \   '%W[F] %. %f:%l:%c %m,' .
+      \   '%W[F] %. %f:%l %m,' .
+      \   '%W[R] %. %f:%l:%c %m,' .
+      \   '%W[R] %. %f:%l %m,' .
+      \   '%I[C] %. %f:%l:%c %m,' .
+      \   '%I[C] %. %f:%l %m,' .
+      \   '%-Z%.%#'
+      \ }
+
+
+let g:neomake_elixir_enabled_makers = ['mix', 'credo']
+
+augroup my_error_signs
+  au!
+  autocmd ColorScheme * hi NeomakeErrorSign ctermfg=203 guifg=#ff5f5f
+  autocmd ColorScheme * hi NeomakeWarningSign ctermfg=209 guifg=#ffaf00
+  autocmd ColorScheme * hi NeomakeInfoSign ctermfg=183 guifg=#dfafff
+  autocmd ColorScheme * hi NeomakeMessageSign ctermfg=27 guifg=#0087ff
+augroup END
+
+
+" templates
+if has("autocmd")
+  autocmd BufNewFile *.c  0r ~/.config/nvim/templates/boilerplate.c
+  autocmd BufNewFile *.rs 0r ~/.config/nvim/templates/boilerplate.rs
+endif
+
+let g:neomake_error_sign = {
+            \ 'text': '>>',
+            \ 'texthl': 'NeoMakeErrorSign',
+            \ }
+
+let g:neomake_warning_sign = {
+            \ 'text': '>>',
+            \ 'texthl': 'NeoMakeWarningSign',
+            \ }
+
+let g:neomake_info_sign = {
+            \ 'text': '>>',
+            \ 'texthl': 'NeoMakeInfoSign',
+            \ }
+
+let g:neomake_message_sign = {
+            \ 'text': '>>',
+            \ 'texthl': 'NeoMakeMessageSign',
+            \ }
+
+function! <SID>LocationPrevious()
+  try
+    lprev
+  catch /^Vim\%((\a\+)\)\=:E553/
+    llast
+  endtry
+endfunction
+
+function! <SID>LocationNext()
+  try
+    lnext
+  catch /^Vim\%((\a\+)\)\=:E553/
+    lfirst
+  endtry
+endfunction
+
+nnoremap <Leader>[ :call <SID>LocationPrevious()<CR>
+nnoremap <Leader>] :call <SID>LocationNext()<CR>
+
+
+" Alchemist settings
+let g:alchemist#elixir_erlang_src = '/usr/local/share/src'
+
+
+" Airline Settings
+" Enables the list of buffers.
+let g:airline#extensions#tabline#enabled = 0
+
+" Buffer numbers alongside files
+let g:airline#extensions#tabline#buffer_nr_show = 0
+
+" Shows the filename only.
+let g:airline#extensions#tabline#fnamemod = ':t'
+
+" Allow glyphs in airline
+let g:airline_powerline_fonts = 1
+
+" Change Airline theme
+let g:airline_theme = 'hybrid'
+
+
+" Vim-Swoop Settings
+" Edits colorscheme
+let g:swoopHighlight = ["hi! link SwoopBufferLineHi Warning", "hi! link SwoopPatternHi Error"]
+
+
+" Jump to buffers.
+nmap <F1> :1b<CR>
+nmap <F2> :2b<CR>
+nmap <F3> :3b<CR>
+nmap <F4> :4b<CR>
+nmap <F5> :5b<CR>
+nmap <F6> :6b<CR>
+nmap <F7> :7b<CR>
+nmap <F8> :8b<CR>
+nmap <F9> :9b<CR>
+
+
+" It's the twenty-first century...no swaps.
+set noswapfile
+
+
+" Allow visual tab completion in command mode
+set wildmenu
+
+
+" Show Vim commands as they're being input.
+set showcmd
+
+
+" Code folding
+" set foldmethod=indent
+" set foldnestmax=10
+" set nofoldenable
+" set foldlevel=4
+
+
+" emulate ci" and ci' behavior
+nnoremap ci( f(%ci(
+nnoremap ci[ f[%ci[
+
+
+" extend functionality of <C-e> & <C-y> scrolling
+nnoremap <C-e> <C-e>j
+vnoremap <C-e> <C-e>j
+nnoremap <C-y> <C-y>k
+vnoremap <C-y> <C-y>k
+
+
+" Opens all folds within the buffer
+" nnoremap ZZ zR
+
+" Closes all folds within the buffer
+" nnoremap zz zM
+
+" Opens all folds beneath the cursor
+" NOTE: j is the character to go down
+" nnoremap zJ zO
+
+" Opens single fold beneath the cursor
+" NOTE: j is the character to go down
+" nnoremap zj zo
+
+" Opens single fold beneath the cursor
+" NOTE: k is the character to go down
+" nnoremap zK zC
+
+" Opens single fold beneath the cursor
+" NOTE: k is the character to go down
+" nnoremap zk zc
+
+
+" Save shortcut
+nnoremap <C-s> :w<CR>
+
+
+" Switch to MRU'd buffer
+nnoremap <leader><leader> <C-^>
+
+
+" Alternative MRU to CtrlP MRU
+nnoremap <leader>b :MRU<CR>
+
+
+" Supports mouse interaction.
+set mouse=a
+
+
+" Highlights matches during a search.
+set hlsearch
+
+" Clear highlight
+noremap <silent> <leader>h :nohlsearch<bar>:echo<CR>
+
+
+" backspace settings
+set backspace=2
+set backspace=indent,eol,start
+
+
+" Javascript specific variables
+let g:javascript_plugin_jsdoc = 1
+
+" GlobalListchars
+set list
+set listchars=tab:ยทยท,trail:ยท,nbsp:ยท
+
+
+" Keeps everything concealed at all times. Even when cursor is on the word.
+set conceallevel=1
+set concealcursor=nvic
+
+
+" map jk to <Esc>
+inoremap jk <Esc>
+
+
+" Hybrid mode for Vim
+inoremap <C-a> <Esc>I
+inoremap <C-e> <Esc>A
+
+inoremap <M-b> <S-Left>
+inoremap <M-f> <S-Right>
+
+inoremap <C-b> <Left>
+inoremap <C-f> <Right>
+inoremap <C-p> <Up>
+inoremap <C-n> <Down>
+
+" temporarily disable <C-p> in normal mode so it doesn't attempt to index all of
+" Google3.
+nnoremap <C-p> :echo "You are attempting to index all of Google3. Aborting..."<CR>
+
+" tab maintenence
+nnoremap <C-t> :tabnew<CR>
+nnoremap <C-w> :tabclose<CR>
+nnoremap <Tab> :tabnext<CR>
+nnoremap <S-Tab> :tabprevious<CR>
+
+" Manage Vertical and Horizontal splits
+nnoremap sl <Esc>:vs<CR><C-w>l
+nnoremap sh <Esc>:vs<CR>
+nnoremap sj <Esc>:sp<CR><C-w>j
+nnoremap sk <Esc>:sp<CR>
+
+
+" Delete (i.e. "close") the currently opened buffer
+" TODO: unless it's a split window, which should be :q
+nnoremap <leader>q :bdelete<CR>
+
+
+" Set CtrlP runtime path
+set runtimepath^=~/.vim/bundle/ctrlp.vim
+
+
+" Pane movement
+let g:tmux_navigator_no_mappings = 1
+
+nnoremap <silent> <M-h> :TmuxNavigateLeft<CR>
+nnoremap <silent> <M-j> :TmuxNavigateDown<CR>
+nnoremap <silent> <M-k> :TmuxNavigateUp<CR>
+nnoremap <silent> <M-l> :TmuxNavigateRight<CR>
+nnoremap <silent> <M-q> :q<CR>
+
+" make Y do what is intuitive given:
+"   D: deletes until EOL
+"   C: changes until EOL
+"   Y: (should) yank until EOL
+nnoremap Y y$
+
+
+" scrolling and maintaing mouse position
+" nnoremap <C-j> j<C-e>
+" nnoremap <C-k> k<C-y>
+
+
+" remap redo key that is eclipsed by `rotate` currently
+nnoremap U :redo<CR>
+
+
+" Define highlighting groups
+" NOTE: The ANSII aliases for colors will change when iTerm2 settings are
+" changed.
+highlight InterestingWord1 ctermbg=Magenta ctermfg=Black
+highlight InterestingWord2 ctermbg=Blue ctermfg=Black
+
+" h1 highlighting
+nnoremap <silent> <leader>1 :execute '2match InterestingWord1 /\<<c-r><c-w>\>/'<CR>
+nnoremap <silent> <leader>x1 :execute '2match none'<CR>
+vnoremap <silent> <leader>1 :execute '2match InterestingWord1 /\<<c-r><c-w>\>/'<CR>
+
+" h2 highlighting
+nnoremap <silent> <leader>2 :execute '3match InterestingWord2 /\<<c-r><c-w>\>/'<CR>
+nnoremap <silent> <leader>x2 :execute '3match none'<CR>
+
+"clear all highlighted groups
+nnoremap <silent> <leader>xx :execute '2match none'<CR> :execute '3match none'<CR> hh
+
+
+" pasteboard copy & paste
+set clipboard+=unnamedplus
+
+
+" Manage 80 char line limits
+highlight OverLength1 ctermbg=Magenta ctermfg=Black
+highlight OverLength2 ctermbg=LightMagenta ctermfg=Black
+highlight OverLength3 ctermbg=White ctermfg=Black
+" match OverLength3 /\%81v.\+/
+match OverLength2 /\%91v.\+/
+" match OverLength3 /\%101v.\+/
+
+nnoremap <leader>w :w<CR>
+
+
+" Resize split to 10,20,...,100 chars
+" Uncomment the next lines for support at those sizes.
+" These bindings interfere with the highlight groups, however.
+" Increases the width of a vertical split.
+" nnoremap <leader>1 :vertical resize 10<CR>
+" nnoremap <leader>2 :vertical resize 20<CR>
+nnoremap <leader>3 :vertical resize 30<CR>
+nnoremap <leader>4 :vertical resize 40<CR>
+nnoremap <leader>5 :vertical resize 50<CR>
+nnoremap <leader>6 :vertical resize 60<CR>
+nnoremap <leader>7 :vertical resize 70<CR>
+nnoremap <leader>8 :vertical resize 80<CR>
+nnoremap <leader>9 :vertical resize 90<CR>
+nnoremap <leader>0 :vertical resize 100<CR>
+
+
+" Increases the height of a horizontal split.
+nnoremap <leader>v1 :resize 5<CR>
+nnoremap <leader>v2 :resize 10<CR>
+nnoremap <leader>v3 :resize 15<CR>
+nnoremap <leader>v4 :resize 20<CR>
+nnoremap <leader>v5 :resize 25<CR>
+nnoremap <leader>v6 :resize 30<CR>
+nnoremap <leader>v7 :resize 35<CR>
+nnoremap <leader>v8 :resize 40<CR>
+nnoremap <leader>v9 :resize 45<CR>
+nnoremap <leader>v0 :resize 50<CR>
+
+
+" BOL and EOL
+nnoremap H ^
+vnoremap H ^
+nnoremap L $
+vnoremap L $
+
+
+" Search for visually selected text
+vnoremap // y/<C-r>"<CR>N
+
+
+" trim trailing whitespace on save
+" Are there any file type where I wouldn't want this?
+autocmd BufWritePre *.{js,py,tpl,less,html,ex,exs,txt,hs,java,rs,ml} :%s/\s\+$//e
+
+
+" Use .gitignore file to populate Ctrl-P
+let g:ctrlp_user_command = ['.git', 'cd %s && git ls-files . -co --exclude-standard', 'find %s -type f']
+
+
+" Ignores dirs and files
+let g:ctrlp_custom_ignore = {
+  \ 'dir':  'node_modules',
+  \ 'file': '\v\.(exe|dll|png|jpg|jpeg)$'
+\}
+
+
+" WIP: Run elixir tests on that line
+" TODO: only register binding in *.exs? file extensions
+nnoremap <leader>t :call ExTestToggle()<CR>
+
+
+" Jumps from an Elixir module file to an Elixir test file.
+fun! ExTestToggle()
+  if expand('%:e') == "ex"
+
+    let test_file_name = expand('%:t:r') . "_test.exs"
+    let test_file_dir = substitute(expand('%:p:h'), "/lib/", "/test/", "")
+    let full_test_path = join([test_file_dir, test_file_name], "/")
+
+    e `=full_test_path`
+
+  elseif match(expand('%:t'), "_test.exs") != -1
+
+    let test_file_name = expand('%:t:r')
+    let offset_amt = strlen(test_file_name) - strlen("_test")
+    let module_file_name = strpart(test_file_name, 0, offset_amt) . ".ex"
+    let module_file_dir = substitute(expand('%:p:h'), "/test/", "/lib/", "")
+    let full_module_path = join([module_file_dir, module_file_name], "/")
+
+    e `=full_module_path`
+
+  endif
+endfun
+
+
+" Creates intermediate directories and file to match current buffer's filepath
+fun! CreateNonExistingDirsAndFile()
+  ! echo "Creating directory..." && mkdir -p %:p:h && echo "Created directory." && echo "Creating file..." && touch %:t:p && echo "Created file."
+
+  " Write the buffer to the recently created file.
+  w
+endfun
diff --git a/users/wpcarro/configs/.config/nvim/templates/boilerplate.c b/users/wpcarro/configs/.config/nvim/templates/boilerplate.c
new file mode 100644
index 0000000000..949743d725
--- /dev/null
+++ b/users/wpcarro/configs/.config/nvim/templates/boilerplate.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main() {
+  printf("Hello, world!");
+  return 0;
+}
diff --git a/users/wpcarro/configs/.config/nvim/templates/boilerplate.rs b/users/wpcarro/configs/.config/nvim/templates/boilerplate.rs
new file mode 100644
index 0000000000..c83adbc69f
--- /dev/null
+++ b/users/wpcarro/configs/.config/nvim/templates/boilerplate.rs
@@ -0,0 +1,5 @@
+fn main() {
+    // The statements here will be executed when the compiled binary is called.
+
+    println!("Hello, world!");
+}
diff --git a/users/wpcarro/configs/.config/systemd/user/clipmenud.service b/users/wpcarro/configs/.config/systemd/user/clipmenud.service
new file mode 100644
index 0000000000..fac317f3f0
--- /dev/null
+++ b/users/wpcarro/configs/.config/systemd/user/clipmenud.service
@@ -0,0 +1,18 @@
+[Unit]
+Description=Clipmenu daemon
+
+[Service]
+ExecStart=clipmenud
+Restart=always
+RestartSec=500ms
+Environment=DISPLAY=:0
+
+MemoryDenyWriteExecute=yes
+NoNewPrivileges=yes
+ProtectControlGroups=yes
+ProtectKernelTunables=yes
+RestrictAddressFamilies=
+RestrictRealtime=yes
+
+[Install]
+WantedBy=default.target
diff --git a/users/wpcarro/configs/.config/systemd/user/default.target.wants/clipmenud.service b/users/wpcarro/configs/.config/systemd/user/default.target.wants/clipmenud.service
new file mode 120000
index 0000000000..387f2023d2
--- /dev/null
+++ b/users/wpcarro/configs/.config/systemd/user/default.target.wants/clipmenud.service
@@ -0,0 +1 @@
+/usr/local/google/home/wpcarro/.config/systemd/user/clipmenud.service
\ No newline at end of file
diff --git a/users/wpcarro/configs/.config/systemd/user/lieer-google.service b/users/wpcarro/configs/.config/systemd/user/lieer-google.service
new file mode 100644
index 0000000000..2f79ed6cca
--- /dev/null
+++ b/users/wpcarro/configs/.config/systemd/user/lieer-google.service
@@ -0,0 +1,7 @@
+[Unit]
+Description=Lieer sync for account 'google'
+
+[Service]
+Type=oneshot
+ExecStart=/nix/store/n6c4pr4fyrsjfksspkapb7yqc6fzl166-corp-lieer/bin/gmi sync
+WorkingDirectory=%h/mail/account.google
diff --git a/users/wpcarro/configs/.config/systemd/user/lieer-google.timer b/users/wpcarro/configs/.config/systemd/user/lieer-google.timer
new file mode 100644
index 0000000000..a073da25ea
--- /dev/null
+++ b/users/wpcarro/configs/.config/systemd/user/lieer-google.timer
@@ -0,0 +1,9 @@
+[Unit]
+Description=Run lieer sync for account 'google'
+
+[Timer]
+OnActiveSec=1
+OnUnitActiveSec=120
+
+[Install]
+WantedBy=timers.target
diff --git a/users/wpcarro/configs/.config/systemd/user/timers.target.wants/lieer-google.timer b/users/wpcarro/configs/.config/systemd/user/timers.target.wants/lieer-google.timer
new file mode 120000
index 0000000000..e9f2cab3bc
--- /dev/null
+++ b/users/wpcarro/configs/.config/systemd/user/timers.target.wants/lieer-google.timer
@@ -0,0 +1 @@
+/usr/local/google/home/wpcarro/.config/systemd/user/lieer-google.timer
\ No newline at end of file
diff --git a/users/wpcarro/configs/.gnupg/crls.d/DIR.txt b/users/wpcarro/configs/.gnupg/crls.d/DIR.txt
new file mode 100644
index 0000000000..2a29a47b8d
--- /dev/null
+++ b/users/wpcarro/configs/.gnupg/crls.d/DIR.txt
@@ -0,0 +1 @@
+v:1:
diff --git a/users/wpcarro/configs/.gnupg/exported/ownertrust.txt b/users/wpcarro/configs/.gnupg/exported/ownertrust.txt
new file mode 100644
index 0000000000..79b727914f
--- /dev/null
+++ b/users/wpcarro/configs/.gnupg/exported/ownertrust.txt
@@ -0,0 +1,3 @@
+# List of assigned trustvalues, created Mon 29 Jul 2019 15:01:24 BST
+# (Use "gpg --import-ownertrust" to restore them)
+7E87921AAC9C514E9341C4F1C7A53CC58D3B1F8C:6:
diff --git a/users/wpcarro/configs/.gnupg/exported/public.asc b/users/wpcarro/configs/.gnupg/exported/public.asc
new file mode 100644
index 0000000000..8b5547f4c3
--- /dev/null
+++ b/users/wpcarro/configs/.gnupg/exported/public.asc
@@ -0,0 +1,225 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBFk52cEBEADW2uF8AjpGxbd/yrtCguVzl7fWCCo/vZYGTomoHy7K3ru7bQEN
+upIBj1ElcsLGxbNLqdEqb17blTOUpaLLxWhEUw38rTpRyepBH0y2u5INDiw9GlpU
+uXKnkvaAF2f7DJH24jQA2mLI5Jcgc2M0Kzmuh1Q1foAy3frORBnYlrd9TlSPU7Og
+Jj0T20jtZIsIORov2TFC2cEpwa+9jHkNaBK2Bdg5c0SyI2r3TSJq+L7X8Vkf3Hmb
+NEWJj286+ElcFP/FyVgRCtSJPjBg/MF0ucukm96cel5qYfK5RkMA/HCyv6xI8iNn
+eZj8sJnozDY4rMxFwNkTxIjwH9cCTW0CR9FMsc1wlIe6Zx0ic8Fu7PZCS5MjM8cQ
+LnruOunVnb0YodQ+cLde6FlKu7kNUlLJrH5NnuFxjWPxzC63u+/K6CcRV9ilWe5r
+so/ImtNfGO1JiCvisYeOqlTYBKceQgjvu5tZtLJoGxH0UzoJARLCLRwyHN8dGqgp
+STRd0Ze6LtzYLG0uuedyPNXDKci7GyrVdAmxVIo+eLA1a7n3YCcluGKZlM0IBWx8
+fTKJ16ASTXpK7Hqr3XSf5V7tUcwxiFFtxh5C7kXglyd4QI6Jk6Xp8HlPLvYXNSNj
+VYRMHi/ueFI92jlt3kCodD26btgIEfD3e3JxKfHhOtwSoA2i1Hr43qdvtQARAQAB
+tCNXaWxsaWFtIENhcnJvbGwgPHdwY2Fycm9AZ21haWwuY29tPokCVAQTAQgAPhYh
+BH6HkhqsnFFOk0HE8celPMWNOx+MBQJZOdnBAhsDBQkJZgGABQsJCAcCBhUICQoL
+AgQWAgMBAh4BAheAAAoJEMelPMWNOx+MGm8P/RYqv5mnneRbyJ6CgisYn2iIBQvz
++rmpdGDfkFqsd2YqDoGjzEJLVkan+I1oLnKSv5QJqPw1gG7fSv6X7Trov9J+Cma3
+h1bSn2BBiq8L9paWTILYmsrBe7kU9bQjNKFu0qjfvPqkGX6HXO6c81N00Qgie574
+MCByWgPtJTcbPLJodIxu6+aibwNBc3XInL/d0ZbXLs8Fc0+z2/dO7cmzAdE77d5Q
+QaG9fGyztiYlZoUS9g3xT4ZulpPqs9zFa04fPvOXWVl+RQjZOYVYW/T8aVRnXohz
+3y7tnxOWs8cmCFd91DDR099DZXstAesWllPsdSld18aMjeM2XrzuaWVDYktaraiY
+RUdz6ZRPcaCpsIA2RHn++xEg+3q6QRz1J4bsbqEOKys+KlQO9uIgPMIkiMaLFoe5
+nu63XI4EMezrti86ETUxFPFL107P9KZ0gitjUXP0KSbnGQ7jt5FmuZgSAkbCSlis
+Ulm8PZ/cmKj9OZysezDKkXFGtyskAELkpToIy48GtyEVIMk+CXcgNydUXDiLnWI8
+VwgmR1Q+hClLYMPvrk7OR6zK8txXsglJItCRUF5fmAn4Q7loh6i/BCfQpHdylO7G
+nn7BOEJ0CJ8Hrr4Y785dtswAX8hWMIuzS4mxAHCjqkkfsOObBfLi+XpkZ6lDkfrQ
+jAt2KuAjQR58dHDSiQEzBBABCAAdFiEEDxGpiYeei7v9weI2RO9bXoYcCacFAlmu
+vnAACgkQRO9bXoYcCacQ4AgAmjDO+8Sd8d+cezwIjZgq1nPPb+/K0KTGsALe7jdF
+MDOKwwPKd75mKbAVyJRu8CMEfgFW04YKbkeVp9bLeD2lpMYsIgpNYy5bU6DNCgi5
+QO501sTqeaWc/rlm7Ng5AlF8GIK6FagrPS31eUexxJ62VFozi3EiibKYepgeIUHR
+3ukw6+PWBkvOYSQjZ0Uc08nci8UsewDQaQvuDABR+6WbLDYX6PuyUEzV7MPbyzME
+QOvGuYpgcAq5gGB6NNe9zFQK0xAQob1UhDlaa1p8VSZyH/RLnyYCdlq0Bmf95PNq
+eE8YkmqFCPKNoWwzOa6pJOk/Y2mXhJm/eD4Avmlo122LN4kCMwQQAQgAHRYhBBP5
+Ly1Y78tmaShOtNywW0z4mvxmBQJZvs4vAAoJENywW0z4mvxmIAoQAMM6QLexK9fK
+88EZxYC6x+qYkb3rzjaGyO3dzGhfRQTFJ8HtFrWTR5s/m+1ACKFnbf1xo7AWbsYq
+jVIxXsqUt9a4jserpaczlzDQLojFCKSGFmfCVV40FwQHL3W+C40xLHLOq93bpHLD
+knp38daRzSryW11ev9y0J3to0qX03WpgFKKwT3fQMT5+V4wZNuzOFmWGaPUsuCQh
+SQj5VU+p9Q4soIIzu2gaal4vW3/qZgIlkAmkg0FV5iW7mwScWPcF/kPlkHFMj9pG
+aTNVgegxtUQBJEXx+VF0vDiOtRnBjE2woVLq1FWGkn7feX4Jvnajqpf6A8dKeNcv
+Egfm00HHhhR5f7LTOYTSXWCqhCmIBKGpYlZo/PDGHdlVoRSR5+qN/1kyP7WZg35u
++7XR0paCTR71RwO+oHZHiv97u6P/iPvVWE7aqCJe6kBW6Q8hB4zjLfH6AbjcIN/9
+Vy0k3ALBfNxatkJZAyl/PQpgSpfpAkbk5upBPvOKVGCWsYA5sYRH4MMiwrOuSgCh
+nibnUT4jJjgU7hK7iOTJB1mbNEvyMSksgxtbdA4XWee4iv5qS3ZCX+1RUspZk3IS
+8NePnaycg+OlI5gMbSEVEmLCat2V3l5KNZRFWpbmNTCo+Mi4t1i8kgJAHRtn0r6t
+sIS9beklhQz1p7KZAphYWjl6kO9p80cnuQINBFk52cEBEAC8e3b6SN4t5I/RRmRt
+/YbPFyC81yElaPzBM+OFsbRDr9MrtfeDUp/wgcihQIw01HUDlm4F2WjbGwth/8Zs
+tEML3CFwtv4V+sYhKqfg+sS5YqzFrFWfZYod8ppFKNMaw9Pcjl70td2egcBDt1SR
+51ni4SdhyMt/KOm4mym/Lf4UNyzlYwykbjtb3nsmvxYI/uVdceDv+7vZoW1rapSw
+Zj4ZS8+jhgHrO2p5B5TCHdsDJEYQ2SOYeIm8tfqb4oQlTWwjG0frl5eOp/+9HfK1
+q7R049FMAzmd6fbm0jpzxDveDe58qWWq2aj+7HwTZhmvr9l7uFyR+3TL6s9hEALl
+B3RGpOy5hGmvwLIAi4qylZuRJzW0tMveDcaDEdMhtyEKF9DYpk1Ug/01uG+PzK8e
+TiFyTCN6OAawWIe7pIxRhlk4+CIqRPEprMTfDJxKEUS7RnUYZ76E94FSV9nEqIJj
+UWFLq3aTMqSiSN5LAgT6AJKHrR2+cJVHM1cEILvCIugeuw1GUC+Bxs1qaxIpp4SS
+xi9Givx8PkKuCukp1J4B8Alx0ZpRELsBEuhZdEN2LP+Hpz7uyD875r8BvEJ+hU20
+Qfnj2wHmKVF+6jBPwrpzZ/gbXuQzfstmrSJCY6p6izfcQSJmbSNpaTsrOvgmaUev
+Wuss4bjuG4loSRwFb/7fsPsX9wARAQABiQI8BBgBCAAmFiEEfoeSGqycUU6TQcTx
+x6U8xY07H4wFAlk52cECGwwFCQlmAYAACgkQx6U8xY07H4xvTg/+MbWyGFmLe9b8
+AMJtqwX+3EyP44Mo2CeafvmbPSqxoXh1NdOezlEESZU9fHMDY50IA2hpariO/Le3
+Ck8py5NAznCc4avS+gnahcPyhvUaMCskmN4UaRsohMvxKrdAGyRfXZcQqE5Tu6zM
+6TxycQkT00qe+PTSQnV2dvGXE4iBFV5kj3NHV7RBC+7sDZ7cYuLHrw1gOaMWeCcF
+oQ0l+DW3CNh3klFds/PIfPALjV25+niwOYcenxqp8GVjooWj7xkASkFyZAqusFTa
++/XY2y7+jvdSmm36gfWiPXWdpPiesekPK1NqPGdAtyv1/EKJd+7cYCbkSH6qPJpb
+FnpfK/ItVm39OIe7OVUuZYd4lGeFvKK/nDqSQ+9STVar2+n8Wths4C8KJbLdZxGE
+QYWKw5aL09tpQRy/skReAt9hVDq3qflODyuWPqS/oDbSGERA2NndkV3LIkU+ZCXI
+xsin4IP8XzC8yJjv11PAzM8wmhlXWOdKDIZXMV/2wO+cyM/t9WtfeXUOuk2NyWO4
+nQ+gD3cDPTJS+t8ReKb/bEXSeHiRfgiYuZXcwT5vGmx4HiYgJ7d9i+8Ikcew75zw
+EvR+OLXYVICsI9PiQPzqVBfp+2u5saEvmWORrNLLUBQt24R2CF4Y63pLumsCB3gv
+n+cUG+sEsct2sSKhQEEU7Yra9tppHlO5Ag0EWT7OXAEQANUBV3/GFcAtsM9XiSGL
+GuWs+S8np+A7WRIISsR5BU22u9XqF7P8/5o/ZJIvoNu9EwTkFZYP9pAxx7F+I/62
+x8YbXCU5byiOG0X/RKRafW1j9zJdZHK2jdga2pRUiCexpk43knTMyYUxyNlOFM+r
+UKJlMErTaN8PJldD9f7qq+assxFN+rLW+tWwxtcL9WxdCIBBMKE7ldyaIRzKNMbZ
+b+mckdP412Ht05sn2BijgHj/co07M0zw+MLWNc9c50wI+/CYBZXerDp7TZoB2HxD
+JH9FqQ+ypjZQkNArieMj8IB6o8nZRbOqs+FO3LJf4A2WuHSLb187LpTLGoL/WaGG
+YK5ZE/0DuR7lSZmVYt2qaKqzDUJbbETLNJlhEykKI0f4bkhjI9UPjxlFgarDuFOe
+V7KkZXJoapda6lwy+W8GDAcrtMasMIgN5lMQdYJllIOJVs0wTJyMbLFN1hcr8oGc
+09tLEPr0FO5lxygMHqcNiia1SO34IJF6HaRHZQeX6Hv2M+FHWpZ3fcu9dkY9i9JK
+RM0W3x0OLFBNfCAvBVFxat9xmYYJuRqomPAFIlt3hik/Dl3dWOkPLdVoE97T/r1/
+5DDNBpaO/g7XU24bH6ja9/WO8T9g0L4nTLCtdKSaGFxwT0No7jmgTJxJQan7hHDZ
++0CHj8jCUsMNSK587HTWiZFZABEBAAGJBHIEGAEIACYWIQR+h5IarJxRTpNBxPHH
+pTzFjTsfjAUCWT7OXAIbAgUJCWYBgAJACRDHpTzFjTsfjMF0IAQZAQgAHRYhBOpX
+PxAcncyJjCSg9h7cm2rmkT65BQJZPs5cAAoJEB7cm2rmkT65H2sQAMlGuoA6pKlA
+W9L+Mdn3aHaGHzxiLU0mxJZHLPxLru9YNkEF2uDiSzHRMSSbJCujF8O2Z8jg08f2
++r/ZVHK1wWd/J7zikh/1pMVj9KVNG0JdyqiHuQ02i5vWdBg6lZZku4uUvU616Ynh
+PMDVoEQ6QXQ24BhrSBH9B1tgSnIRc9EHzzW5lTF3+qttA1tJETJODzEZGmSlBene
+kUxBZVuH7daEa1pRPGNVSa4TmCTkxgYZUIdnIDC7CeDREldAftCvEll37Ewy8QTi
+goAPYYZQd9jJ7ywq1qWFiTt6n6IYvjfVm2ttO/3PBtla5FdCW3U0MZyxOiIIpHjh
+IgVFWknvOEsKQhaN4rbq7YSMLLZW5Y0ukHk8c3OsRpO5Clc/yl0hHURGtjhvHgSg
+kL8z80WhnP+XiMa/vaWIsats1/LGc+uvstmohI4np9+jF/6Byk9Y85ki1ilQszVP
+/JEIj2IvH6/OsjcXlgAs8In7EBeixlERLneK9F0D+rb99m21rhkXmy/EQ75yeSV/
+z7sy9suK3SuULqYSuTKuw+mbAY3KOF3JLjQdW1NXCZhqRaxYF+5nMq1f6VDwGcpO
+pPSF+9fY6/R5/cyEk8LOY4kS9O9rqc4PIi1ieYoyxezTBdX1aWpZEsAzVDWuJRCd
+pwaKM8YF7ofZxEo5QAnfOO/gc1opPVIUMzAP/2md27adc9Qn0AkWf4Gbr5HSbArq
+urGjqUa8HCUcishzW/TrWbDtuy0ZplvWv4z1KgqoAAr+U2rZwck8mk02UCmjpaDL
+KliTiBAuRei6wgW0y7rLE0S1q9wjoJZemhVrI5C0hu7jIPzl1r/ZRNEqcKtsl6UG
++2zlo0loGiLt+G+p5KpcwuscYGEU0Ekasu+68sual5UUdRXpu8J9ePnqSIZUHanj
+mrARW5iv17NGG3ZtGeX+rDZ2G23Ymnc/IOK1Qeyz07GE3OcxaikTk4EvNDJcl33E
+uI7Dcauz+1msBxGwkib/BUx686ZI8e3Qa9cxzmzGBAwJGqtuOv0BrKbmT6dJ1FG2
+XtRCKsYDGttvoEM8fnhAeVXLIEidiMg3ri8cE/uhIKQlTCoel/hr6yM0BztxQRIk
+PIkFDGSpOq6pknv0KgAxhymJlHmC002e3FAl+B+q71FUthwjs7h110CrI6yZTwbC
+La9FZEODAFWkWLghV3iLP0D8HD+rsBxDttpJOC0lndsONIMkb2Xf4ue8pceUehEN
+zf+mkS6B5ilfHOhrcY3vkfV/cuF2Zv1kBpjayCbanweeyEIok720eP4RoTu8NJGZ
+gD0qLo2iJBW65FRr557CWET+X22k2vDO92PeB6MXdZ+PGNgPWw+SZfY5nUN6hsbm
+73dNlPTad6zfMtHRuQINBFk+zuMBEADd0yUpELWiFKezO/GLaqBs5iI5fRvO97pk
+5yhROIjaM4xz2tmZMvenO9AdVSchgh6w1CCNMvhbE9MkEakh8qN5hWl0XeN+UarF
+XvIx1ARfzmI+Xwz40wNRNHkGMwZipvHQ0oFW2NI+qQaxu1QzX7eGIF1uQPhyw5wg
+dQeO3fbAKR7G1YNOiBM5KyEPSFj29fSyPVjhqM5orHyrD3rtHir+978hA0W4yFY3
+0lY7OqaHs2crU4txy30bc2oYL93J5uMDeXmDg+K7NEGeih1vJnh3lF73yh3i6d+t
+MxrMyzkhLf8GBD+38QqJ4npkcd4E66g0kB3Q1EYfh4R2eMkYCCcTuRuQVrtIyC81
+r490Kx3WFB2ioIOARk/0NUrnqO0tze4KuLKc4wVpKFCeRkBpN5ZxoF+NJ2DLodg8
+w5G5xkMluLpcUWU+D/LEf52Nu8JBro4BVOv8h+D3MwC8icaQaL4xTVqCBtOvhA3/
+0gxrEJ98g5jpR/Puspa/zQVpY2MP632m6Ctfw6mdHtcq3PKX2sJIF8UKLGhJsAYg
+9bUSD4ka+IV1BLXY71b2DFZoTA9WaprG4GDTV4x3PcERq1/LSUBIKl9kHw9DvGzW
+uBcQQOwUR564ghGPt4Lxq8fc5G03h2Oou6LZx1lfYHuwQs8iJtiX6EtkVhZxxQX5
+TClQMdVgEwARAQABiQI8BBgBCAAmFiEEfoeSGqycUU6TQcTxx6U8xY07H4wFAlk+
+zuMCGwwFCQlmAYAACgkQx6U8xY07H4yNohAApKlliONc+s6PMtwAOJ3j3NzOCPDy
+MOiA24kKHMg4yCUiJDJ+xX2tQs6Jf99IcCIF625nnsUqyRDgdHyDeu4ZTneo1aFB
+YMf4fgxqUkEiV7VNxvw2idDfW2Wy0fmyGCdS8UOw9UOjSMURNjfvY/pQlFNG+cWx
+ZUfrU2HgXzdAchTlYQnpPwaDaMQE7xsV/Q+VVtWNe9gAuycrGNgPhh4zNAmjqiGN
+a+YS0vW4v+TSaA7Y/jMTEJYEz9+60dYy4I64Wv1NWODT03KExQoINrROLwhn/wD8
+AZhyJKBNuAbSZpMXNMD+2QKtqeNxE7HbTQY7Bqx5feBvDkDgr7ox+KyzR3NuXOHQ
+CSbmSEQPN3miiGglHGASctp3Fd93PhXzVtiiRAnqfBw7zGDSgdpaNC8z9DAG6iUY
+ZtcNz5UoiCHSOqE1vxV38poWDtZLkKuQRXvXy/uNyPcPx2efaDNf/FxH3gM6L7+6
+gfJ9vDMKUI9Xa9A5u3BMR/1Xiehx/GL7kZ16fZDWhJH1iCUcPwu/wSPDCmzGaX3B
+O/FT+H1m/Fql8oKWOy82K1zBVMx7cx+b+3/Qlkbx3wGPGtNLPH1m5QFKoPV7z2zP
+tM9VziUSLPPlIEbsT3I4lXYdhFsHbGBk++ZbY9kRUTXkNRciqX2NFcFtNSo9RH8F
+KOmVBBI+ZQ28HDGJAh8EKAEIAAkFAlm/5kACHQMACgkQx6U8xY07H4yS/xAAiHYW
+T+wgYgFR0e0DYNOlz3KeYZwhORc5/ED07qCxUMFkChpBnXbKLzGViiKK3H9FyaFy
+fOhuIqb0GgXX4TTYdHShvceBtMfTfeYMLXQC+WaJgIydbjRK978mDBgIDs96ylEj
+ErtgP3J/GXTk616nv9VYYjGGNKQVJNxDGCRzfZks3m/gWH/whbctFQXaBOshstra
+nGpoR6EEZERpDkDMdLqd1JEhCPK8YUSPwT0LKm2yQpeR3ly5phZiJC7uZVmq518P
+f8UoHYhtb6P18kJeMVbrNpEzDTdGCZ6eKC3B+1tfoft8MXF3fEINFzZKqAXKqsHD
+sQtVWPshg49J8HNpc0NbQi90+8ph4oVrWDHoDulhGg9xUTlY1fXUye1uDXhVn8gL
+rAeLqz6WP2i7jPnNCJgTXw5+e2kAye0rCvKH0pw8a1Aq2iaxvxr0L1MzAgtKaTh/
+AU3K5j8r7YRUdOMUHMGS5CwwdhwNkABM2Sm7FmlZL/BNwmgxekhJSivLL3M6qPY3
+LjcxxJBfe4gk9RRX9/YCgSkKTwvx1Ko9368G4WcxYOSTP3eVol6o0yBqd0rV/P+l
+CCgiAZ/ZoVOvi5jmTy2I9flafPzGp51EdH+RS/rGwW1feP5JMg+NULwc1y4kTru6
+pkHTUu3Ol+M2616HU32p8XJi4mDV5qMRWmLn+UCZAQ0EVKyAeQEIALyGS95q8aCp
+8rjM35kpabNOhr9hAcdq0DrxwjOWZd5u569X1sS81VjPqoj1jpA+/GgheWeYrNxm
+RbMT1fdtd22W5yiNd5TNXF+RMhZYvnT4Mxm3NNggZoriHsnrG4XbtLZZmMTXwF/6
+a/CsaCXYHp4J3YvYnDc/B0fssj37OXQH0SjpBQnU7U1m7mvLXfm7Mh+zi7VTSz45
+WcFyr7Lg1HRN6OzDtbBjn8kuWWMzYIlg+EUZPuHLHoCkjhN6g7AM5eDhQqXvzOcy
+lSIk/TIPy7n8EeKrrgfijGQOJF6c6d5n0Hq+6lejT6uL3iHUOKtv8PYGr8vF01ao
+vP/EOVTkYQEAEQEAAbQrR3JpZmZpbiBTbWl0aCA8Z3NtaXRoQHNlY3VyaXR5c2Nv
+cmVjYXJkLmlvPokBNwQTAQgAIQUCWCuGZQIbAwULCQgHAgYVCAkKCwIEFgIDAQIe
+AQIXgAAKCRBE71tehhwJp2lCCACiTsLoGbq44A11+k24oWItbJTrg5pISwKUwfwt
+hvik0oQPWfQoz/sr0w/Ie0rUnCuSyOVUXuJZSgzFOjEcwmw1dDv0hsanyt+NZ3SC
+r/hSasAOMIeXS7+hyL894E6NKIGDi25+Yhpj1AFneCu9cOoxlEXqynVaiBJbpHIw
+atwB5i7ZvUz+krTBjf6wwgLzBi1EHw7IJYhgS1Ye9A/+h+iur7d/4/C6cC31IgBd
+r8d8iNbMhqyk66+fhdZ5Vd2QO4DUq4CUgFoakO9X383Jf8azR0zXIPPphJ2QpQzD
+sfriUT0J18bP546tknwOsNYlt1XPYwlLvXKljXr1YkRyTdPTtCdHcmlmZmluIFNt
+aXRoIDx3aWxkZ3JpZmZpbjQ1QGdtYWlsLmNvbT6JATcEEwEIACEFAlSsgHkCGwMF
+CwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQRO9bXoYcCaed+Qf+JSDZ3odMwlnr
+bb2kwslduAt9VhRm+dfdIm25nAgxJUxIju8uIgE9v+8dRdGFwrV8pKBYYOCMi8MF
+NYuu9zS66wXS4opd/DeYDj2yaN0wBYEfeXMCwVLVDHU7AHrsxQWRSxbcUOi2Mm2s
+ig70ZSq2iNicX2f6eUSr/4CjocTP6jOqcHd6Di4odEy/hK6ukCCW8ia1Uujh7JYC
+U7quHnuE1N184W2Jf6hUieFC2kE+Nmhix0LsYYe6c1InembHRZ85BpOsWWuE9cS6
+IuVO/jbNZcgS7NkuCHkG7CubPnSZX/EDwmyr9Pd57tr9BANuDNvTGgcbaXhJj5nl
+Ix/usDsrRLQxR3JpZmZpbiBTbWl0aCAoS2V5YmFzZSkgPGdsaXR0ZXJzaGFya0Br
+ZXliYXNlLmlvPokBNwQTAQgAIQUCWCuHlgIbAwULCQgHAgYVCAkKCwIEFgIDAQIe
+AQIXgAAKCRBE71tehhwJpxWTB/0ehZJ1Bjkf7AvtWYn7PEwr1y9aAWHLhAxNNXOE
+M5IhXjnpL5o3Pic8DonUrzDVxRsNxaGU8jvAvbQpWgtQXJFi0qgDxS6b1hf5CSlS
+kcjqtkcMMqyi7XAydSyCXr5s0sZ2ZBn0tri0AKN7JW4Wd0aXJrP/RmmXNeTTARI/
+LGy5Em/PBFogDPTHRWwJQ5uCaddwev3pcOzNvrSvR0m1JXG+ZtP/Z+c4QQA3YGdT
+TSxanK2w9NXTQVToJKO8Lig3ivYNgpbscE0ywrbXVfu3pzB1+9uTa3zd9MmQ0QL9
+mX3RiJeExNE+Vxj5jG+kE8GhcRxXKefXkg+UweaYfkcX8vEEuQENBFSsgHkBCADI
+E/6vQg1OW9aGffzp3atrHtCjEHU6ZONE6unlez4CGHZXIZYTAbA0Nmgd3d3JA7wd
+d0p48whI/tREFHlBD4lxQBN3wrpmDFVq0OiSLuMSAZaTXrX5ctY4CiHJVOIJUK16
+6zsoQFqvTBW7hYTsmFml1frOZrnyeYD9Hyj1Kkk1kaUkf+JrtnZzcftqD0hFzYHe
+645YsLS2ub/ZoXrlV1hznDdIH64TYwlvabvBcZR6Exn6+hByMSbem1nNqB4PN2GV
+/dO2OrkolThctGaxVoChDoauA+vfUQRWbpxzMJQHAJ3/PtKMKyMjv0+TTSIO1zsp
+i2mayI7XUyXLu5fcTfQxABEBAAGJAR8EGAEIAAkFAlSsgHkCGwwACgkQRO9bXoYc
+Cae2+Qf/QWJ+sVhFHNHUjPWSL1o+dSUMIv6qseCGyojGLZxAl9z6IKUng638XMrV
+kgAy1aoy91N+HY0IPg45huTvU36uFoD2Hr9dd+ZVftO38jfviiowqu+iPt16sfZq
+f9VUTDTJpsLzoxiwq+x5FbJYt2iqDqK30JyQD2EMn5Li0qtR1ohunxR2CE5byNRA
+1ymk1BKMDb0tDHl87fCY5+bHZBrG0svqyDsxxK0T4bqRl0gSUfhVA67xcL1C7wG1
+MmtNZM+Ks9AepFlxmRDJnX0XNdaw7P6QtK4igLw5hSiFpVYmdfEyL9W0yn4No4Pj
+rJnQvrCvIJqJ+1ANxY9H9ArCl3iF/5kCDQRZvrPlARAA1DjXoVu6jU9Rfojm7iFu
+XJm2Suq7W3v4HjsycExn3ZBh2Lh5Jc7EdncPbP3UWnNBI5MlerHS5VMfC1OFzG/Z
+IRXZyWIVOu1ajRH39i/8pIxOfcCQ1Y4msN0QntL79Z1qAtOdUGqppViiywgTA7XC
+yGU8lvVEx7TlgXdmviRSI2Mm2McbeD/YLdTgqD8+8J00sIW5DUk1n/gUkyU2z4mk
+4rwvGLJR2bGv1KIndZzfg7c+fNd2UsXjcJV8+eMRJCjE+xlrviBnJHnrNj75Ps9v
+xX4WDX2PQZycS4NEictsFOmWmyiAHWcW8ZOrqsrDbd2QGUxjKrh6kdrZvVhCogsO
+StwdVm39TfBykLt3K5jdhU5QK4AyNBbVoikoBtZFyVg2G6e0vbVAYF5NDoy+uDyM
+jK9cHeJfMHRPCW+rBkaNh4+i0uj97SML9F1G1s3+dlPSNIofvJvZ69VxJi5w0OCT
+vwdgmR/na7DQTBikZK+F0hoZRJAmKgxh0yUMzExYUq9rvdgNeEyIWs3nh6GVPs15
+iZfcRITiGFZsi/BWeSH/ce96qYG+c5UkN1QOvFqMZdneF/uUfUI/qOc3KGmhjW+f
+lcO/qqtROO3UisckjvUZL1/80YQISMn96iJpG8mOUrDHiHndSMwErMyyxz3XXYzQ
+ys7W2oihWL5iKU+SHmisNA0AEQEAAbQqSnVzdGluIERlTWFyaXMgKGh0dHA0MDIp
+IDxqdXN0aW5AZGVtYXIuaXM+iQI+BBMBAgAoBQJZvrPlAhsDBQkDwmcABgsJCAcD
+AgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDcsFtM+Jr8ZiuBD/9dOkCFWDZWAj1LKBc6
+nKci16H8tazePpvjYeZFxJw2w5NEGgwg1iGsdhKm60EOA8Okh8cEmmfq+AriFPEH
+nAbnSePGNeXTRFy7njaApxnRQGaVTV+++B/J/zQTA+2iJXh4gWR2/ip6gmyAGQJK
+u63jA/fwoeWqcQDI1FnHqpGEHb6BLeeyRA7iXd3TYTOYpuFJGx57yhZflFssbmwY
+3MW3NyWdOYXkWiH1OfujYHuh5du5txiEMvN78q5F18byIaLSkoa3eOM6osGOn8kj
+X3iJEAOk+HzMONQd2O59OWmozzyxHicr4rv7LIOeAvL9gi+gpEflT65/AJbgLa5N
+JLcvQwAD/iRRv9fs7CSsOlwLyZEVGy0huZ2iyxSguBwkDsHd6yFAr90F8eehV4z6
+DlW3g5UREVQEcUIKW4FEg+E0XMe7tILOcqTzhsMrd3PwMmC/RDPoyOOhJLCLFAg9
+hN+xaFEr4cDUPT1PwudHqQ9u9uqyeH47O3Qi0KJ2IsrWgcjTy3z0setCZDh3APlv
+Y9o+Go4ykvMNV/iHwnui/CS/sIX64VKrBq5L+0cq3PnbJfeqxi/Q7CRBko4Zf2h1
+A5SkSM3lwSuLk+zLNu+erS13EjwfainK4eOgFism7lN5CD4Z7VrKxtOTKjozidG0
+N3Ez7edUYQ28NGWJBIMEb6qn/7kCDQRZvrPlARAAoWI61RD8wjDINkiXAtX0jcoG
+dvO9oMXvVFWqsGEGivqciifdA5VvB/9jK0YfFQbLvQtkfvcqITuGflBExCK47CDg
+lv4AxI0xNkj1jKwgvm/tU6y+Oe1mrw+b64Z/V5naptNnIU4VgVSNsWvZkH2EKxgq
+6k+fAoCCwxlctw2JMmbnmUNOiu2miwoiq/Agl8Jfd4xSrAGZn77ZHM+XNLgabKiJ
+782E3alCFOXbIftOXIcxgOQWbiiPEUjzCJ5llMdjVnOkn7uP+ZXm3/h7IsrC9/GP
+DqSsebGPbNuxgNrDj9HigYPNK6jjZ95bLImaADfd2h/OXA9FYz+HwJ2kBZRNGtDj
+FxsmLvqNolu8WZKSjiw7SNK0Ya+55y8KU2iO/G3T5ilAB6nRPliP9aE6IIEzNWgK
+9nNM92S34CZQMhOnVjRqH4WEi9i3j/rlSAX4mJLbe2pi6cueSBc53qisBs6H8p5Z
+hqPQJfUlVeRxF4ZNKF7wt6dhQcEbi3/IxoABBizIt5DSBybOMLOAB65A5GQkPlJ7
+VyHhzlIoS5RqzwOqg6TCQ4UUtifQqtFXuwVHlHAPhi56U10IiCWJd4hy635Eei6C
+WDmGr4+eXTK14h93f79JxIqqve5y7cZgcQ+dQPSBVl19FZnvQUA4/5E8UPI1X3cQ
+o8I1542PpL74CcXBZ1kAEQEAAYkCJQQYAQIADwUCWb6z5QIbDAUJA8JnAAAKCRDc
+sFtM+Jr8ZqSQEACFW1xrxu8mmXGXpLlXpGx9CFWBfJSFtWBYNjbLZ8Rcdu8FweBt
+HVIAmdwYbBHYgT/xQd9Gxg+Z+JmnaAZFi88pN5Pmh2dy53nysLsjYZS8G7p2lKdu
+alXrM90PxGpwugNNPVEr//+Bb6ahQgnJQLDY6wz3DuA3L1vk+sBN+00iuGbaW992
+kPRcz2KSDXY0jR3lh3938qBXJR6jbYN2YxHMhfCeK8y9hpNSP5UVUYlLeEjyEIT7
+HgbMwsX8WX8OvL+uacwSzwC1JE8Vn98pIEQgMveZn9ylwuJZp1zv5eSulDsDRWA8
+S8Agjb/fjdQGsck4REiahw3DIPqcIvUFr3yDybB8dTLp509UqK+HLw/bf8QMmpc8
+YazIquk0/HVm0tdijDCgAIw+Dh8LEP1gmCVxynlrHs9ItCjlipuTao8LopjwOiGE
+9LnYy19ESF7kUbtyFenmp+FX0WAvlUfhrSfeeM+vR1yD8dJSa1XDI/WafkxBQRuV
+sd3cfQIxDn7JGlhwytRkxl7oabxHokc3wCSzu5Sb4ok6A91HPbSNqJ2nKSkbAwQE
+u6d5vx6SSO02stvyHPUFLM7zTTNa2B5Vrz9e7eItKEm6taXqzx1e7A8w+kOlP/0p
+4oLlBDWgklpqVZcPWtFDsldyNZlxwo5xw9czlTZ+hVuaHdSqwP2NNQegYw==
+=5XsB
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/users/wpcarro/configs/.gnupg/pubring.kbx b/users/wpcarro/configs/.gnupg/pubring.kbx
new file mode 100644
index 0000000000..208fad71b7
--- /dev/null
+++ b/users/wpcarro/configs/.gnupg/pubring.kbx
Binary files differdiff --git a/users/wpcarro/configs/.gnupg/trustdb.gpg b/users/wpcarro/configs/.gnupg/trustdb.gpg
new file mode 100644
index 0000000000..8781b2ad9b
--- /dev/null
+++ b/users/wpcarro/configs/.gnupg/trustdb.gpg
Binary files differdiff --git a/users/wpcarro/configs/.sqliterc b/users/wpcarro/configs/.sqliterc
new file mode 100644
index 0000000000..7e8b3e3fb4
--- /dev/null
+++ b/users/wpcarro/configs/.sqliterc
@@ -0,0 +1,2 @@
+.mode column
+.headers on
\ No newline at end of file
diff --git a/users/wpcarro/configs/.xsecurelockrc b/users/wpcarro/configs/.xsecurelockrc
new file mode 100644
index 0000000000..101495c3ef
--- /dev/null
+++ b/users/wpcarro/configs/.xsecurelockrc
@@ -0,0 +1,5 @@
+# Replace the gLinux penguin with a custom image.
+XSECURELOCK_LOGO_IMAGE=~/.local/share/static/pickle-rick.jpg
+
+# Turn this off on laptop (not on desktop).
+XSECURELOCK_BLANK_DPMS_STATE=on
diff --git a/users/wpcarro/configs/default.nix b/users/wpcarro/configs/default.nix
new file mode 100644
index 0000000000..681f976052
--- /dev/null
+++ b/users/wpcarro/configs/default.nix
@@ -0,0 +1,73 @@
+{ pkgs, ... }:
+
+let
+  inherit (pkgs) writeShellScript;
+  inherit (pkgs.lib.strings) makeBinPath;
+in
+{
+  install = writeShellScript "install-configs" ''
+    cd "$WPCARRO/configs" && ${pkgs.stow}/bin/stow --target="$HOME" .
+  '';
+
+  uninstall = writeShellScript "uninstall-configs" ''
+    cd "$WPCARRO/configs" && ${pkgs.stow}/bin/stow --delete --target="$HOME" .
+  '';
+
+  # Run this script to import all of the information exported by `export.sh`.
+  # Usage: import-gpg path/to/export.zip
+  import-gpg = writeShellScript "import-gpg" ''
+    set -euo pipefail
+
+    if [ -z "''${1+x}" ]; then
+      echo "You must specify the path to export.zip. Exiting..."
+      exit 1
+    fi
+
+    PATH="${makeBinPath (with pkgs; [ busybox gnupg ])}"
+    destination="$(mktemp -d)"
+
+    function cleanup() {
+      rm -rf "$destination"
+    }
+    trap cleanup EXIT
+
+    unzip "$1" -d "$destination" >/dev/null
+
+    gpg --import "$destination/public.asc"
+    gpg --import "$destination/secret.asc"
+    gpg --import-ownertrust "$destination/ownertrust.txt"
+
+    # Run this at the end to output some verification
+    gpg --list-keys
+    gpg --list-secret-keys
+  '';
+
+  # Run this script to export all the information required to transport your GPG
+  # information to a zip file.
+  # Usage: export-gpg
+  export-gpg = writeShellScript "export-gpg" ''
+    set -euo pipefail
+
+    PATH="${makeBinPath (with pkgs; [ busybox gnupg zip ])}"
+    output="$(pwd)/export.zip"
+    destination="$(mktemp -d)"
+
+    function cleanup() {
+      rm -rf "$destination"
+    }
+    trap cleanup EXIT
+
+    gpg --armor --export >"$destination/public.asc"
+    gpg --armor --export-secret-keys >"$destination/secret.asc"
+    gpg --armor --export-ownertrust >"$destination/ownertrust.txt"
+
+    # Strangely enough this appears to be the only way to create a zip of a
+    # directory that doesn't contain the (noisy) full paths of each item from
+    # the source filesystem. (i.e. -j doesn't cooperate with -r).
+    pushd "$destination"
+    zip -r "$output" ./*
+    popd
+
+    echo "$(realpath $output)"
+  '';
+}
diff --git a/users/wpcarro/configs/install b/users/wpcarro/configs/install
new file mode 100755
index 0000000000..a3f3ec328e
--- /dev/null
+++ b/users/wpcarro/configs/install
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+configs="$WPCARRO/configs"
+
+(cd "$configs" && stow --target="$HOME" .)
diff --git a/users/wpcarro/configs/uninstall b/users/wpcarro/configs/uninstall
new file mode 100755
index 0000000000..9650479c42
--- /dev/null
+++ b/users/wpcarro/configs/uninstall
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+configs="$WPCARRO/configs"
+
+(cd "$configs" && stow --delete --target="$HOME" .)
diff --git a/users/wpcarro/dotfiles/config.fish b/users/wpcarro/dotfiles/config.fish
new file mode 100644
index 0000000000..3be8fefd9c
--- /dev/null
+++ b/users/wpcarro/dotfiles/config.fish
@@ -0,0 +1,44 @@
+alias c 'xclip -selection clipboard -i'
+alias p 'xclip -selection clipboard -o'
+alias cat 'bat --theme="Monokai Extended Light"'
+alias rgh 'rg --hidden'
+alias fdh 'fd --hidden'
+alias tpr 'tput reset'
+alias ls 'exa --sort=type'
+alias ll 'exa --long --sort=type'
+alias la 'exa --long --all --sort=type'
+alias gcan 'git commit --amend --no-edit'
+alias gco 'git checkout'
+alias gd 'git diff'
+alias gds 'git diff --staged'
+alias glp 'git log --pretty --oneline --graph'
+alias gpf 'git push --force'
+alias gsh 'git show HEAD'
+alias gst 'git status'
+alias gprom 'git pull --rebase origin main'
+alias gfom 'git fetch origin main'
+alias grh 'git reset --hard'
+alias gproc 'git pull --rebase origin canon'
+alias edit 'emacsclient -n'
+alias h 'cd /hadrian'
+alias d 'cd /depot'
+alias hw 'cd /hadrian/users/wpcarro'
+alias dw 'cd /depot/users/wpcarro'
+alias sc 'systemctl'
+alias ef 'edit ~/.config/fish/config.fish'
+alias sf 'source ~/.config/fish/config.fish'
+alias tf 'terraform'
+
+# environment variables
+set -gx EDITOR "emacsclient"
+set -gx ALTERNATE_EDITOR "emacs -q -nw"
+set -gx VISUAL "emacsclient"
+
+# Use my custom fish prompt
+source /depot/users/wpcarro/dotfiles/prompt.fish
+
+# Configure fuzzy history, file, directory searching
+source (fzf-share)/key-bindings.fish && fzf_key_bindings
+
+# Install direnv
+eval (direnv hook fish)
diff --git a/users/wpcarro/dotfiles/default.nix b/users/wpcarro/dotfiles/default.nix
new file mode 100644
index 0000000000..8150f53706
--- /dev/null
+++ b/users/wpcarro/dotfiles/default.nix
@@ -0,0 +1,5 @@
+{ ... }:
+
+{
+  dunstrc = ./dunstrc;
+}
diff --git a/users/wpcarro/dotfiles/dunstrc b/users/wpcarro/dotfiles/dunstrc
new file mode 100644
index 0000000000..a17533f073
--- /dev/null
+++ b/users/wpcarro/dotfiles/dunstrc
@@ -0,0 +1,53 @@
+[global]
+font = JetBrains Mono
+origin = top-right
+markup = yes
+plain_text = no
+format = "<b>%s</b>\n%b"
+sort = no
+indicate_hidden = yes
+alignment = center
+bounce_freq = 0
+show_age_threshold = -1
+word_wrap = yes
+ignore_newline = no
+stack_duplicates = yes
+hide_duplicate_count = yes
+geometry = "300x50-15+49"
+shrink = no
+transparency = 5
+idle_threshold = 0
+monitor = 0
+follow = keyboard
+sticky_history = yes
+history_length = 15
+show_indicators = no
+line_height = 3
+separator_height = 2
+padding = 6
+horizontal_padding = 6
+separator_color = frame
+startup_notification = false
+browser = xdg-open
+icon_position = off
+max_icon_size = 80
+frame_width = 3
+frame_color = "#8EC07C"
+
+[urgency_low]
+frame_color = "#3B7C87"
+foreground = "#3B7C87"
+background = "#191311"
+timeout = 4
+
+[urgency_normal]
+frame_color = "#5B8234"
+foreground = "#5B8234"
+background = "#191311"
+timeout = 6
+
+[urgency_critical]
+frame_color = "#B7472A"
+foreground = "#B7472A"
+background = "#191311"
+timeout = 8
\ No newline at end of file
diff --git a/users/wpcarro/dotfiles/gitconfig b/users/wpcarro/dotfiles/gitconfig
new file mode 100644
index 0000000000..f81c0c40f7
--- /dev/null
+++ b/users/wpcarro/dotfiles/gitconfig
@@ -0,0 +1,9 @@
+[user]
+	name = "William Carroll"
+	email = "wpcarro@gmail.com"
+[diff]
+  external = difft
+[push]
+	default = current
+[rebase]
+	autoStash = true
diff --git a/users/wpcarro/dotfiles/prompt.fish b/users/wpcarro/dotfiles/prompt.fish
new file mode 100644
index 0000000000..58d22dab5e
--- /dev/null
+++ b/users/wpcarro/dotfiles/prompt.fish
@@ -0,0 +1,87 @@
+# When the Emacs SSH client, Tramp, connects to a remote host that uses Fish,
+# it's important to keep the shell prompt simple so that Tramp can parse it.
+if test "$TERM" = "dumb"
+    function fish_prompt
+        echo "\$ "
+    end
+    function fish_right_prompt; end
+    function fish_greeting; end
+    function fish_title; end
+else
+    function fish_prompt
+        # My custom prompt.
+        #
+        # Design objectives:
+        # - max-length <= 80 characters
+        # - minimal
+        # - no dependencies (well, you know what I mean)
+        #
+        # Components
+        # - ssh connection
+        # - user
+        # - host
+        # - git repo
+        # - git branch
+        # - lambda character as prompt
+
+        # Cache status before we overwrite it.
+        set -l last_status $status
+
+        # Colors
+        set -l color_inactive (set_color red --bold)
+        set -l color_active (set_color green --bold)
+        set -l color_normal (set_color normal)
+
+        # SSH information
+        if set -q SSH_CLIENT; or set -q SSH_TTY
+            echo -en "$color_active \bssh โœ“ [$color_normal$USER@"(hostname)"$color_active]$color_normal"
+        else
+            echo -en "$color_inactive \bssh โœ— [$color_normal$USER@"(hostname)"$color_inactive]$color_normal"
+        end
+
+        # Separator
+        echo -n " "
+
+        # Git information
+        set -l git_repo (git rev-parse --show-toplevel 2>/dev/null)
+        set -l git_status $status
+
+        if [ (realpath .) = "/" ]
+            set -g dir_path (realpath .)
+        else if [ (realpath ..) = "/" ]
+            set -g dir_path (realpath .)
+        else
+            set -g dir_path (echo (basename (realpath ..))"/"(basename (realpath .)))
+        end
+
+        if test $git_status -eq 0
+            set -l git_repo_name (basename (git rev-parse --show-toplevel))
+            set -l git_branch (git branch 2>/dev/null | grep '^\*' | cut -d' ' -f2-)
+            echo -en "$color_active \bgit โœ“ [$color_normal$git_branch$color_active|$color_normal$git_repo_name$color_active|$color_normal$dir_path$color_active]$color_normal"
+        else
+            echo -en "$color_inactive \bgit โœ— [$color_normal$dir_path$color_inactive]$color_normal"
+        end
+
+        # Newline
+        echo
+
+        # Handle root vs non-root
+        if [ "$USER" = "root" ]
+            set -g prompt_sigil "#"
+        else
+            set -g prompt_sigil "ฮป"
+        end
+
+        set -l time (date +"%T")
+        if test $last_status -eq 0
+            set -l color_prompt (set_color white --bold)
+            echo -n "$time$color_prompt $prompt_sigil$color_normal "
+        else
+            set -l color_prompt (set_color red --bold)
+            echo -n "$time$color_prompt $prompt_sigil$color_normal "
+        end
+    end
+    function fish_right_prompt; end
+    function fish_greeting; end
+    function fish_title; end
+end
diff --git a/users/wpcarro/emacs/.emacs.d/init.el b/users/wpcarro/emacs/.emacs.d/init.el
new file mode 100644
index 0000000000..5db74d36c7
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/init.el
@@ -0,0 +1,15 @@
+;; load order is intentional
+(setq-default debug-on-error t)
+(require 'wpc-package)
+(require 'wpc-misc)
+(require 'ssh)
+(require 'keyboard)
+(require 'email)
+(require 'keybindings)
+(require 'window-manager)
+(require 'wpc-ui)
+(require 'wpc-dired)
+(require 'wpc-org)
+(require 'wpc-company)
+(require 'wpc-shell)
+(require 'wpc-language-support)
diff --git a/users/wpcarro/emacs/.emacs.d/opam-user-setup.el b/users/wpcarro/emacs/.emacs.d/opam-user-setup.el
new file mode 100644
index 0000000000..a23addefaf
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/opam-user-setup.el
@@ -0,0 +1,145 @@
+;; ## added by OPAM user-setup for emacs / base ## cfd3c9b7837c85cffd0c59de521990f0 ## you can edit, but keep this line
+(provide 'opam-user-setup)
+
+;; Base configuration for OPAM
+
+(defun opam-shell-command-to-string (command)
+  "Similar to shell-command-to-string, but returns nil unless the process
+  returned 0, and ignores stderr (shell-command-to-string ignores return value)"
+  (let* ((return-value 0)
+         (return-string
+          (with-output-to-string
+            (setq return-value
+                  (with-current-buffer standard-output
+                    (process-file shell-file-name nil '(t nil) nil
+                                  shell-command-switch command))))))
+    (if (= return-value 0) return-string nil)))
+
+(defun opam-update-env (switch)
+  "Update the environment to follow current OPAM switch configuration"
+  (interactive
+   (list
+    (let ((default
+            (car (split-string (opam-shell-command-to-string "opam switch show --safe")))))
+      (completing-read
+       (concat "opam switch (" default "): ")
+       (split-string (opam-shell-command-to-string "opam switch list -s --safe") "\n")
+       nil t nil nil default))))
+  (let* ((switch-arg (if (= 0 (length switch)) "" (concat "--switch " switch)))
+         (command (concat "opam config env --safe --sexp " switch-arg))
+         (env (opam-shell-command-to-string command)))
+    (when (and env (not (string= env "")))
+      (dolist (var (car (read-from-string env)))
+        (setenv (car var) (cadr var))
+        (when (string= (car var) "PATH")
+          (setq exec-path (split-string (cadr var) path-separator)))))))
+
+(opam-update-env nil)
+
+(defvar opam-share
+  (let ((reply (opam-shell-command-to-string "opam config var share --safe")))
+    (when reply (substring reply 0 -1))))
+
+(add-to-list 'load-path (concat opam-share "/emacs/site-lisp"))
+;; OPAM-installed tools automated detection and initialisation
+
+(defun opam-setup-tuareg ()
+  (add-to-list 'load-path (concat opam-share "/tuareg") t)
+  (load "tuareg-site-file"))
+
+(defun opam-setup-add-ocaml-hook (h)
+  (add-hook 'tuareg-mode-hook h t)
+  (add-hook 'caml-mode-hook h t))
+
+(defun opam-setup-complete ()
+  (if (require 'company nil t)
+    (opam-setup-add-ocaml-hook
+      (lambda ()
+         (company-mode)
+         (defalias 'auto-complete 'company-complete)))
+    (require 'auto-complete nil t)))
+
+(defun opam-setup-ocp-indent ()
+  (opam-setup-complete)
+  (autoload 'ocp-setup-indent "ocp-indent" "Improved indentation for Tuareg mode")
+  (autoload 'ocp-indent-caml-mode-setup "ocp-indent" "Improved indentation for Caml mode")
+  (add-hook 'tuareg-mode-hook 'ocp-setup-indent t)
+  (add-hook 'caml-mode-hook 'ocp-indent-caml-mode-setup  t))
+
+(defun opam-setup-ocp-index ()
+  (autoload 'ocp-index-mode "ocp-index" "OCaml code browsing, documentation and completion based on build artefacts")
+  (opam-setup-add-ocaml-hook 'ocp-index-mode))
+
+(defun opam-setup-merlin ()
+  (opam-setup-complete)
+  (require 'merlin)
+  (opam-setup-add-ocaml-hook 'merlin-mode)
+
+  (defcustom ocp-index-use-auto-complete nil
+    "Use auto-complete with ocp-index (disabled by default by opam-user-setup because merlin is in use)"
+    :group 'ocp_index)
+  (defcustom merlin-ac-setup 'easy
+    "Use auto-complete with merlin (enabled by default by opam-user-setup)"
+    :group 'merlin-ac)
+
+  ;; So you can do it on a mac, where `C-<up>` and `C-<down>` are used
+  ;; by spaces.
+  (define-key merlin-mode-map
+    (kbd "C-c <up>") 'merlin-type-enclosing-go-up)
+  (define-key merlin-mode-map
+    (kbd "C-c <down>") 'merlin-type-enclosing-go-down)
+  (set-face-background 'merlin-type-face "skyblue"))
+
+(defun opam-setup-utop ()
+  (autoload 'utop "utop" "Toplevel for OCaml" t)
+  (autoload 'utop-minor-mode "utop" "Minor mode for utop" t)
+  (add-hook 'tuareg-mode-hook 'utop-minor-mode))
+
+(defvar opam-tools
+  '(("tuareg" . opam-setup-tuareg)
+    ("ocp-indent" . opam-setup-ocp-indent)
+    ("ocp-index" . opam-setup-ocp-index)
+    ("merlin" . opam-setup-merlin)
+    ("utop" . opam-setup-utop)))
+
+(defun opam-detect-installed-tools ()
+  (let*
+      ((command "opam list --installed --short --safe --color=never")
+       (names (mapcar 'car opam-tools))
+       (command-string (mapconcat 'identity (cons command names) " "))
+       (reply (opam-shell-command-to-string command-string)))
+    (when reply (split-string reply))))
+
+(defvar opam-tools-installed (opam-detect-installed-tools))
+
+(defun opam-auto-tools-setup ()
+  (interactive)
+  (dolist (tool opam-tools)
+    (when (member (car tool) opam-tools-installed)
+     (funcall (symbol-function (cdr tool))))))
+
+(opam-auto-tools-setup)
+;; ## end of OPAM user-setup addition for emacs / base ## keep this line
+;; ## added by OPAM user-setup for emacs / tuareg ## b10f42abebd2259b784b70d1a7f7e426 ## you can edit, but keep this line
+;; Set to autoload tuareg from its original switch when not found in current
+;; switch (don't load tuareg-site-file as it adds unwanted load-paths)
+(defun opam-tuareg-autoload (fct file doc args)
+  (let ((load-path (cons "/home/wpcarro/.opam/default/share/emacs/site-lisp" load-path)))
+    (load file))
+  (apply fct args))
+(when (not (member "tuareg" opam-tools-installed))
+  (defun tuareg-mode (&rest args)
+    (opam-tuareg-autoload 'tuareg-mode "tuareg" "Major mode for editing OCaml code" args))
+  (defun tuareg-run-ocaml (&rest args)
+    (opam-tuareg-autoload 'tuareg-run-ocaml "tuareg" "Run an OCaml toplevel process" args))
+  (defun ocamldebug (&rest args)
+    (opam-tuareg-autoload 'ocamldebug "ocamldebug" "Run the OCaml debugger" args))
+  (defalias 'run-ocaml 'tuareg-run-ocaml)
+  (defalias 'camldebug 'ocamldebug)
+  (add-to-list 'auto-mode-alist '("\\.ml[iylp]?\\'" . tuareg-mode))
+  (add-to-list 'auto-mode-alist '("\\.eliomi?\\'" . tuareg-mode))
+  (add-to-list 'interpreter-mode-alist '("ocamlrun" . tuareg-mode))
+  (add-to-list 'interpreter-mode-alist '("ocaml" . tuareg-mode))
+  (dolist (ext '(".cmo" ".cmx" ".cma" ".cmxa" ".cmxs" ".cmt" ".cmti" ".cmi" ".annot"))
+    (add-to-list 'completion-ignored-extensions ext)))
+;; ## end of OPAM user-setup addition for emacs / tuareg ## keep this line
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/c-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/c-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/c-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/c-mode/stdio b/users/wpcarro/emacs/.emacs.d/snippets/c-mode/stdio
new file mode 100644
index 0000000000..52bc717e47
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/c-mode/stdio
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: <stdio.h>
+# key: sio
+# --
+#include <stdio.h>
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/c-mode/stdlib b/users/wpcarro/emacs/.emacs.d/snippets/c-mode/stdlib
new file mode 100644
index 0000000000..5d44e8ed79
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/c-mode/stdlib
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: <stdlib.h>
+# key: slb
+# --
+#include <stdlib.h>
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/c-mode/struct b/users/wpcarro/emacs/.emacs.d/snippets/c-mode/struct
new file mode 100644
index 0000000000..6e9282f83c
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/c-mode/struct
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: struct
+# key: struct
+# --
+typedef struct $1 {
+  $2
+} $1_t;
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/elisp-module-docs b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/elisp-module-docs
new file mode 100644
index 0000000000..8ea7b8f077
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/elisp-module-docs
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: Elisp module docs
+# key: emd
+# --
+;;; `(-> (buffer-file-name) f-filename)` --- $2 -*- lexical-binding: t -*-
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; $3
+
+;;; Code:
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/function b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/function
new file mode 100644
index 0000000000..bfa888d526
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/function
@@ -0,0 +1,8 @@
+# -*- mode: snippet -*-
+# name: Function
+# key: fn
+# expand-env: ((yas-indent-line 'fixed))
+# --
+(defun $1 ($2)
+  "$3"
+  $4)
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/generic-header b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/generic-header
new file mode 100644
index 0000000000..bf6e525f8c
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/generic-header
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Header
+# key: hdr
+# --
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; $1
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/library-header b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/library-header
new file mode 100644
index 0000000000..0f0ad5c4fc
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/library-header
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Library header
+# key: lib
+# --
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/provide-footer b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/provide-footer
new file mode 100644
index 0000000000..2a0bcc33f7
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/emacs-lisp-mode/provide-footer
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: Provide footer
+# key: elf
+# --
+(provide '`(-> (buffer-file-name) f-filename f-no-ext)`)
+;;; `(-> (buffer-file-name) f-filename)` ends here
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/derive-safe-copy b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/derive-safe-copy
new file mode 100644
index 0000000000..95f7d9deec
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/derive-safe-copy
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Derive Safe Copy
+# key: dsc
+# --
+deriveSafeCopy 0 'base ''$1
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/import-qualified b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/import-qualified
new file mode 100644
index 0000000000..4c4db62a8a
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/import-qualified
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Import qualified
+# key: iq
+# --
+import qualified $1 as $2
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/instance-defn b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/instance-defn
new file mode 100644
index 0000000000..10d194ce41
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/instance-defn
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: Instance
+# key: inst
+# --
+instance $1 where
+  $2 = $3
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/language-extension b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/language-extension
new file mode 100644
index 0000000000..9d6084acb4
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/language-extension
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: language extension
+# key: lang
+# --
+{-# LANGUAGE $1 #-}
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/separator b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/separator
new file mode 100644
index 0000000000..1ab0d762b6
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/separator
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Separator
+# key: -
+# --
+--------------------------------------------------------------------------------
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/undefined b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/undefined
new file mode 100644
index 0000000000..7609f801f2
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/haskell-mode/undefined
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Undefiend
+# key: nd
+# --
+undefined
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/html-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/html-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/html-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/html-mode/index-boilerplate b/users/wpcarro/emacs/.emacs.d/snippets/html-mode/index-boilerplate
new file mode 100644
index 0000000000..3cea6ce003
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/html-mode/index-boilerplate
@@ -0,0 +1,18 @@
+# -*- mode: snippet -*-
+# name: HTML index.html starter
+# key: html
+# --
+<!doctype html>
+
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>$1</title>
+  <meta name="description" content="$2">
+  <meta name="author" content="William Carroll">
+  <link rel="stylesheet" href="index.css">
+</head>
+<body>
+  <script src="index.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/java-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/java-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/java-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/java-mode/public-static-void-main b/users/wpcarro/emacs/.emacs.d/snippets/java-mode/public-static-void-main
new file mode 100644
index 0000000000..1839a27eb5
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/java-mode/public-static-void-main
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: public static void main
+# key: psvm
+# --
+public static void main(String[] args) {
+    $1
+}
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/defpackage b/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/defpackage
new file mode 100644
index 0000000000..7f110a9718
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/defpackage
@@ -0,0 +1,9 @@
+# -*- mode: snippet -*-
+# name: Define package
+# key: defp
+# --
+(in-package #:cl-user)
+(defpackage #:$1
+  (:documentation "$2")
+  (:use #:cl))
+(in-package #:$1)
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/function b/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/function
new file mode 100644
index 0000000000..b1769cd3d1
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/function
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Function
+# key: fn
+# --
+(defun $1 ($2)
+  "$3"
+  $4)
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/typed-function b/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/typed-function
new file mode 100644
index 0000000000..a3c236821e
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/lisp-mode/typed-function
@@ -0,0 +1,8 @@
+# -*- mode: snippet -*-
+# name: Typed function
+# key: tfn
+# --
+(type $1 ($3) $4)
+(defun $1 ($2)
+  "$5"
+  $6)
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/nix-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/nix-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/nix-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/nix-mode/shell-nix b/users/wpcarro/emacs/.emacs.d/snippets/nix-mode/shell-nix
new file mode 100644
index 0000000000..b5eb5a2447
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/nix-mode/shell-nix
@@ -0,0 +1,12 @@
+# -*- mode: snippet -*-
+# name: shell.nix boilerplate
+# key: import
+# --
+{ pkgs, ... }:
+
+pkgs.stdenv.mkDerivation {
+  name = "$1";
+  buildInputs = [
+    $2
+  ];
+}
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/org-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/org-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/org-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/org-mode/code-snippet b/users/wpcarro/emacs/.emacs.d/snippets/org-mode/code-snippet
new file mode 100644
index 0000000000..4215b15992
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/org-mode/code-snippet
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Code Snippet
+# key: src
+# --
+#+BEGIN_SRC $1
+$2
+#+END_SRC
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/org-mode/href b/users/wpcarro/emacs/.emacs.d/snippets/org-mode/href
new file mode 100644
index 0000000000..ac65ea2e49
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/org-mode/href
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Org mode URL
+# key: href
+# --
+[[$1][$2]]
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/python-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/python-mode/dunder-main b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/dunder-main
new file mode 100644
index 0000000000..4dd22dc0b2
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/dunder-main
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: Dunder main (__main__)
+# key: mn
+# --
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/python-mode/function b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/function
new file mode 100644
index 0000000000..379ceda1a3
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/function
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: Function
+# key: fn
+# --
+def $1($2):
+    $3
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/python-mode/header b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/header
new file mode 100644
index 0000000000..db48adfec7
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/header
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Header
+# key: hdr
+# --
+################################################################################
+# $1
+################################################################################
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/python-mode/init b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/init
new file mode 100644
index 0000000000..5c407495f5
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/init
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: dunder init
+# key: ctor
+# --
+def __init__(self$1):
+    $2
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/python-mode/shebang b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/shebang
new file mode 100644
index 0000000000..0f45ae782d
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/shebang
@@ -0,0 +1,6 @@
+# -*- mode: snippet -*-
+# name: shebang
+# key: shb
+# --
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/python-mode/utf-8 b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/utf-8
new file mode 100644
index 0000000000..3babc73030
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/python-mode/utf-8
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: utf-8
+# key: utf
+# --
+# -*- coding: utf-8 -*-
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/function b/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/function
new file mode 100644
index 0000000000..882c48ded3
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/function
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Function
+# key: fn
+# --
+(define ($1) $2)
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/lambda b/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/lambda
new file mode 100644
index 0000000000..b9a684588b
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/lambda
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Lambda function
+# key: ld
+# --
+(ฮป ($1) $2)
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/lambda-symbol b/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/lambda-symbol
new file mode 100644
index 0000000000..254b9fd96b
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/racket-mode/lambda-symbol
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Lambda symbol
+# key: l
+# --
+ฮป
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/reason-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/reason-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/reason-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/reason-mode/function b/users/wpcarro/emacs/.emacs.d/snippets/reason-mode/function
new file mode 100644
index 0000000000..6b4b6a5db2
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/reason-mode/function
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Function
+# key: fn
+# --
+let $1 = (~$2:$3) => {
+  $4
+};
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/reason-mode/switch b/users/wpcarro/emacs/.emacs.d/snippets/reason-mode/switch
new file mode 100644
index 0000000000..40f34ff8d1
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/reason-mode/switch
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Switch statement
+# key: sw
+# --
+switch ($1) {
+| $2 =>
+}
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/action-extractor b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/action-extractor
new file mode 100644
index 0000000000..62834a29ab
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/action-extractor
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: exactness
+# key: $x
+# --
+$Exact<$Call<typeof $1>>
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/console-log b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/console-log
new file mode 100644
index 0000000000..82ec3fd8e3
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/console-log
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Console.log helper
+# key: clg
+# --
+console.log($1)
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/const-defn b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/const-defn
new file mode 100644
index 0000000000..8e35e61fc2
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/const-defn
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: const definition
+# key: cn
+# --
+const $1 = '$2'
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/const-function b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/const-function
new file mode 100644
index 0000000000..13f2018f22
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/const-function
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: const function
+# key: cfn
+# --
+const $1 = ($2) => {
+  $3
+}
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/destructure-const b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/destructure-const
new file mode 100644
index 0000000000..2a52c57c75
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/destructure-const
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Destructuring a const
+# key: cds
+# --
+const { $1 } = $2
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/fat-arrow b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/fat-arrow
new file mode 100644
index 0000000000..187a2efc5a
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/fat-arrow
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Fat arrow function
+# key: fa
+# --
+=>
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/fat-arrow-function b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/fat-arrow-function
new file mode 100644
index 0000000000..694914a83c
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/fat-arrow-function
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Fat arrow function
+# key: faf
+# --
+() => {
+  $1
+}
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-destructured b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-destructured
new file mode 100644
index 0000000000..ded3ce163a
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-destructured
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Import destructured
+# key: ids
+# --
+import { $1 } from '$2'
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-react b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-react
new file mode 100644
index 0000000000..0463f5cd55
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-react
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Import React dependency (ES6)
+# key: ir
+# --
+import React from 'react'
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-type b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-type
new file mode 100644
index 0000000000..fcd51f687b
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-type
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: import type
+# key: ixt
+# --
+import type { $1 } from '$2'
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-x-from-y b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-x-from-y
new file mode 100644
index 0000000000..09fa6df505
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-x-from-y
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: import x from y
+# key: ix
+# --
+import $1 from '$2'
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-y b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-y
new file mode 100644
index 0000000000..9f550e300d
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/import-y
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: import y
+# key: iy
+# --
+import '$1'
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/jest-describe-test b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/jest-describe-test
new file mode 100644
index 0000000000..ed382d4f74
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/jest-describe-test
@@ -0,0 +1,10 @@
+# -*- mode: snippet -*-
+# name: Jest describe/test block
+# key: dsc
+# --
+describe('$1', () => {
+  test('$2', () => {
+
+    expect($3).toEqual($4)
+  })
+})
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/jest-test b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/jest-test
new file mode 100644
index 0000000000..12ca2e786d
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/jest-test
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Jest / Jasmine test
+# key: tst
+# --
+test('$1', () => {
+  expect($2).toBe($3)
+})
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/react-class-component b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/react-class-component
new file mode 100644
index 0000000000..f2a93a31d9
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/react-class-component
@@ -0,0 +1,11 @@
+# -*- mode: snippet -*-
+# name: React class extends
+# key: clz
+# --
+class $1 extends React.Component {
+  render() {
+    $2
+  }
+}
+
+export default $1
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/redux-action b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/redux-action
new file mode 100644
index 0000000000..681c5d0dfd
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/redux-action
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: redux-action
+# key: rax
+# --
+export const ${1:$$(string-lower->caps yas-text)} = '`(downcase (functions-buffer-dirname))`/${1:$(string-caps->kebab yas-text)}'
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/typed-redux-action b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/typed-redux-action
new file mode 100644
index 0000000000..53c6e5fc5a
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rjsx-mode/typed-redux-action
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: typed-redux-action
+# key: trax
+# --
+export const ${1:$$(string-lower->caps yas-text)}: '`(downcase (functions-buffer-dirname))`/${1:$(string-caps->kebab yas-text)}' = '`(downcase (buffer-dirname))`/${1:$(string-caps->kebab yas-text)}'
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rust-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/rust-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rust-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rust-mode/for-loop b/users/wpcarro/emacs/.emacs.d/snippets/rust-mode/for-loop
new file mode 100644
index 0000000000..4d8e0e3bbd
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rust-mode/for-loop
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: for-loop
+# key: for
+# --
+for $1 in $2 {
+    $3
+}
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/rust-mode/match b/users/wpcarro/emacs/.emacs.d/snippets/rust-mode/match
new file mode 100644
index 0000000000..bf0e876e2b
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/rust-mode/match
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: match
+# key: match
+# --
+match $1 {
+    $2 => $3,
+}
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/sh-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/sh-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/sh-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/sh-mode/function b/users/wpcarro/emacs/.emacs.d/snippets/sh-mode/function
new file mode 100644
index 0000000000..efa946bb27
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/sh-mode/function
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Create function
+# key: fn
+# --
+$1() {
+  $2
+}
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/text-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/text-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/text-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/text-mode/check-mark b/users/wpcarro/emacs/.emacs.d/snippets/text-mode/check-mark
new file mode 100644
index 0000000000..7977819688
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/text-mode/check-mark
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Unicode checkmark
+# key: uck
+# --
+โœ“
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/text-mode/x-mark b/users/wpcarro/emacs/.emacs.d/snippets/text-mode/x-mark
new file mode 100644
index 0000000000..bc3c356a61
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/text-mode/x-mark
@@ -0,0 +1,5 @@
+# -*- mode: snippet -*-
+# name: Unicode ex-mark
+# key: ux
+# --
+โœ—
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/web-mode/.yas-parents b/users/wpcarro/emacs/.emacs.d/snippets/web-mode/.yas-parents
new file mode 100644
index 0000000000..d58dacb7a0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/web-mode/.yas-parents
@@ -0,0 +1 @@
+text-mode
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/web-mode/header b/users/wpcarro/emacs/.emacs.d/snippets/web-mode/header
new file mode 100644
index 0000000000..ae59c7a50f
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/web-mode/header
@@ -0,0 +1,7 @@
+# -*- mode: snippet -*-
+# name: Header
+# key: hdr
+# --
+/*******************************************************************************
+ * $1
+ ******************************************************************************/
\ No newline at end of file
diff --git a/users/wpcarro/emacs/.emacs.d/snippets/web-mode/index-boilerplate b/users/wpcarro/emacs/.emacs.d/snippets/web-mode/index-boilerplate
new file mode 100644
index 0000000000..b791cdf86f
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/snippets/web-mode/index-boilerplate
@@ -0,0 +1,18 @@
+# -*- mode: snippet -*-
+# name: HTML index.html starter
+# key: html
+# --
+<!doctype html>
+
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>$1</title>
+  <meta name="description" content="$2">
+  <meta name="author" content="William Carroll">
+  <link rel="stylesheet" href="index.css">
+</head>
+<body>
+  <script src="index.js"></script>
+</body>
+</html>
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/>.el b/users/wpcarro/emacs/.emacs.d/wpc/>.el
new file mode 100644
index 0000000000..6d5f86f8b4
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/>.el
@@ -0,0 +1,28 @@
+;;; >.el --- Small utility functions -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Originally I stored the `>>` macro in macros.el, but after setting up linting
+;; for my Elisp in CI, `>>` failed because it didn't have the `macros-`
+;; namespace.  I created this module to establish a `>-` namespace under which I
+;; can store some utilities that would be best kept without a cumbersome
+;; namespace.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro >-> (&rest forms)
+  "Compose a new, point-free function by composing FORMS together."
+  (let ((sym (gensym)))
+    `(lambda (,sym)
+       (->> ,sym ,@forms))))
+
+
+(provide '>)
+;;; >.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/buffer.el b/users/wpcarro/emacs/.emacs.d/wpc/buffer.el
new file mode 100644
index 0000000000..0f86f7f811
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/buffer.el
@@ -0,0 +1,174 @@
+;;; buffer.el --- Working with buffers -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Utilities for CRUDing buffers in Emacs.
+;;
+;; Many of these functions may seem unnecessary especially when you consider
+;; there implementations.  In general I believe that Elisp suffers from a
+;; library disorganization problem.  Providing simple wrapper functions that
+;; rename functions or reorder parameters is worth the effort in my opinion if
+;; it improves discoverability (via intuition) and improve composability.
+;;
+;; I support three ways for switching between what I'm calling "source code
+;; buffers":
+;; 1. Toggling previous: <SPC><SPC>
+;; 2. Using `ivy-read': <SPC>b
+;; TODO: These obscure evil KBDs.  Maybe a hydra definition would be best?
+;; 3. Cycling (forwards/backwards): C-f, C-b
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'maybe)
+(require 'set)
+(require 'cycle)
+(require 'struct)
+(require 'ts)
+(require 'general)
+(require 'list)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst buffer-source-code-blacklist
+  (set-new 'dired-mode
+           'erc-mode
+           'vterm-mode
+           'magit-status-mode
+           'magit-process-mode
+           'magit-log-mode
+           'magit-diff-mode
+           'org-mode
+           'fundamental-mode)
+  "A blacklist of major-modes to ignore for listing source code buffers.")
+
+(defconst buffer-ivy-source-code-whitelist '("*scratch*" "*Messages*")
+  "A whitelist of buffers to include when listing source code buffers.")
+
+(defconst buffer-source-code-timeout 2
+  "Number of seconds to wait before invalidating the cycle.")
+
+(cl-defstruct source-code-cycle cycle last-called)
+
+(defun buffer-emacs-generated? (name)
+  "Return t if buffer, NAME, is an Emacs-generated buffer.
+Some buffers are Emacs-generated but are surrounded by whitespace."
+  (let ((trimmed (s-trim name)))
+    (and (s-starts-with? "*" trimmed))))
+
+(defun buffer-find (buffer-or-name)
+  "Find a buffer by its BUFFER-OR-NAME."
+  (get-buffer buffer-or-name))
+
+(defun buffer-major-mode (name)
+  "Return the active `major-mode' in buffer, NAME."
+  (with-current-buffer (buffer-find name)
+    major-mode))
+
+(defun buffer-source-code-buffers ()
+  "Return a list of source code buffers.
+This will ignore Emacs-generated buffers, like *Messages*.  It will also ignore
+  any buffer whose major mode is defined in `buffer-source-code-blacklist'."
+  (->> (buffer-list)
+       (list-map #'buffer-name)
+       (list-reject #'buffer-emacs-generated?)
+       (list-reject (lambda (name)
+                      (set-contains? (buffer-major-mode name)
+                                     buffer-source-code-blacklist)))))
+
+(defvar buffer-source-code-cycle-state
+  (make-source-code-cycle
+   :cycle (cycle-from-list (buffer-source-code-buffers))
+   :last-called (ts-now))
+  "State used to manage cycling between source code buffers.")
+
+(defun buffer-exists? (name)
+  "Return t if buffer, NAME, exists."
+  (maybe-some? (buffer-find name)))
+
+(defun buffer-new (name)
+  "Return a newly created buffer NAME."
+  (generate-new-buffer name))
+
+(defun buffer-find-or-create (name)
+  "Find or create buffer, NAME.
+Return a reference to that buffer."
+  (let ((x (buffer-find name)))
+    (if (maybe-some? x)
+        x
+      (buffer-new name))))
+
+;; TODO: Should this consume: `display-buffer' or `switch-to-buffer'?
+(defun buffer-show (buffer-or-name)
+  "Display the BUFFER-OR-NAME, which is either a buffer reference or its name."
+  (display-buffer buffer-or-name))
+
+;; TODO: Move this and `buffer-cycle-prev' into a separate module that
+;; encapsulates all of this behavior.
+
+(defun buffer-cycle (cycle-fn)
+  "Using CYCLE-FN, move through `buffer-source-code-buffers'."
+  (let ((last-called (source-code-cycle-last-called
+                      buffer-source-code-cycle-state))
+        (cycle (source-code-cycle-cycle
+                buffer-source-code-cycle-state)))
+    (if (> (ts-diff (ts-now) last-called)
+           buffer-source-code-timeout)
+        (progn
+          (struct-set! source-code-cycle
+                       cycle
+                       (cycle-from-list (buffer-source-code-buffers))
+                       buffer-source-code-cycle-state)
+          (let ((cycle (source-code-cycle-cycle
+                        buffer-source-code-cycle-state)))
+            (funcall cycle-fn cycle)
+            (switch-to-buffer (cycle-current cycle)))
+          (struct-set! source-code-cycle
+                       last-called
+                       (ts-now)
+                       buffer-source-code-cycle-state))
+      (progn
+        (funcall cycle-fn cycle)
+        (switch-to-buffer (cycle-current cycle))))))
+
+(defun buffer-cycle-next ()
+  "Cycle forward through the `buffer-source-code-buffers'."
+  (interactive)
+  (buffer-cycle #'cycle-next!))
+
+(defun buffer-cycle-prev ()
+  "Cycle backward through the `buffer-source-code-buffers'."
+  (interactive)
+  (buffer-cycle #'cycle-prev!))
+
+(defun buffer-ivy-source-code ()
+  "Use `ivy-read' to choose among all open source code buffers."
+  (interactive)
+  (ivy-read "Source code buffer: "
+            (-concat buffer-ivy-source-code-whitelist
+                     (-drop 1 (buffer-source-code-buffers)))
+            :sort nil
+            :action #'switch-to-buffer))
+
+(defun buffer-show-previous ()
+  "Call `switch-to-buffer' on the previously visited buffer.
+This function ignores Emacs-generated buffers, i.e. the ones that look like
+  this: *Buffer*.  It also ignores buffers that are `dired-mode' or `erc-mode'.
+  This blacklist can easily be changed."
+  (interactive)
+  (let* ((xs (buffer-source-code-buffers))
+         (candidate (list-get 1 xs)))
+    (prelude-assert (maybe-some? candidate))
+    (switch-to-buffer candidate)))
+
+(provide 'buffer)
+;;; buffer.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/clipboard.el b/users/wpcarro/emacs/.emacs.d/wpc/clipboard.el
new file mode 100644
index 0000000000..ec2a46f540
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/clipboard.el
@@ -0,0 +1,40 @@
+;;; clipboard.el --- Working with X11's pasteboard -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Simple functions for copying and pasting.
+;;
+;; Integrate with bburns/clipmon so that System Clipboard can integrate with
+;; Emacs's kill-ring.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'cl-lib)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defun clipboard-copy (x &key (message "[clipboard.el] Copied!"))
+  "Copy string, X, to X11's clipboard and `message' MESSAGE."
+  (kill-new x)
+  (message message))
+
+(cl-defun clipboard-paste (&key (message "[clipboard.el] Pasted!"))
+  "Paste contents of X11 clipboard and `message' MESSAGE."
+  (yank)
+  (message message))
+
+(defun clipboard-contents ()
+  "Return the contents of the clipboard as a string."
+  (substring-no-properties (current-kill 0)))
+
+(provide 'clipboard)
+;;; clipboard.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/constants.el b/users/wpcarro/emacs/.emacs.d/wpc/constants.el
new file mode 100644
index 0000000000..48bcd9042f
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/constants.el
@@ -0,0 +1,29 @@
+;;; constants.el --- Constants for organizing my Elisp -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; This file contains constants that are shared across my configuration.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'maybe)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst constants-ci? (maybe-some? (getenv "CI"))
+  "Defined as t when Emacs is running in CI.")
+
+(defconst constants-osx? (eq system-type 'darwin)
+  "Defined as t when OSX is running.")
+
+(provide 'constants)
+;;; constants.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/display.el b/users/wpcarro/emacs/.emacs.d/wpc/display.el
new file mode 100644
index 0000000000..69dae6939e
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/display.el
@@ -0,0 +1,103 @@
+;;; display.el --- Working with single or multiple displays -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Mostly wrappers around xrandr.
+;;
+;; Troubleshooting:
+;; The following commands help me when I (infrequently) interact with xrandr.
+;; - xrandr --listmonitors
+;; - xrandr --query
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'dash)
+(require 's)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defmacro display-register (name &key
+                                    output
+                                    primary
+                                    coords
+                                    size
+                                    rate
+                                    dpi
+                                    rotate)
+  "Macro to define constants and two functions for {en,dis}abling a display.
+
+NAME    - the human-readable identifier for the display
+OUTPUT  - the xrandr identifier for the display
+PRIMARY - if true, send --primary flag to xrandr
+COORDS  - X and Y offsets
+SIZE    - the pixel resolution of the display (width height)
+RATE    - the refresh rate
+DPI     - the pixel density in dots per square inch
+rotate  - one of {normal,left,right,inverted}
+
+See the man-page for xrandr for more details."
+  `(progn
+     (defconst ,(intern (format "display-%s" name)) ,output
+       ,(format "The xrandr identifier for %s" name))
+     (defconst ,(intern (format "display-%s-args" name))
+       ,(replace-regexp-in-string
+         "\s+" " "
+         (s-format "--output ${output} ${primary-flag} --auto \
+                    --size ${size-x}x${size-y} --rate ${rate} --dpi ${dpi} \
+                    --rotate ${rotate} ${pos-flag}"
+                   #'aget
+                   `(("output" . ,output)
+                     ("primary-flag" . ,(if primary "--primary" "--noprimary"))
+                     ("pos-flag" . ,(if coords
+                                        (format "--pos %dx%d"
+                                                (car coords)
+                                                (cadr coords))
+                                      ""))
+                     ("size-x" . ,(car size))
+                     ("size-y" . ,(cadr size))
+                     ("rate" . ,rate)
+                     ("dpi" . ,dpi)
+                     ("rotate" . ,rotate))))
+       ,(format "The arguments we pass to xrandr for display-%s." name))
+     (defconst ,(intern (format "display-%s-command" name))
+       (format "xrandr %s" ,(intern (format "display-%s-args" name)))
+       ,(format "The command we run to configure %s" name))
+     (defun ,(intern (format "display-enable-%s" name)) ()
+       ,(format "Attempt to enable my %s monitor" name)
+       (interactive)
+       (prelude-start-process
+        :name ,(format "display-enable-%s" name)
+        :command ,(intern (format "display-%s-command" name))))
+     (defun ,(intern (format "display-disable-%s" name)) ()
+       ,(format "Attempt to disable my %s monitor." name)
+       (interactive)
+       (prelude-start-process
+        :name ,(format "display-disable-%s" name)
+        :command ,(format
+                   "xrandr --output %s --off"
+                   output)))))
+
+(defmacro display-arrangement (name &key displays)
+  "Create a function, display-arrange-<NAME>, to enable all your DISPLAYS."
+  `(defun ,(intern (format "display-arrange-%s" name)) ()
+     (interactive)
+     (prelude-start-process
+      :name ,(format "display-configure-%s" name)
+      :command ,(format "xrandr %s"
+                        (->> displays
+                             (-map (lambda (x)
+                                     (eval (intern (format "display-%s-args" x)))))
+                             (s-join " "))))))
+
+(provide 'display)
+;;; display.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/email.el b/users/wpcarro/emacs/.emacs.d/wpc/email.el
new file mode 100644
index 0000000000..a83ca25e6c
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/email.el
@@ -0,0 +1,76 @@
+;;; email.el --- My email settings -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Attempting to configure to `notmuch' for my personal use.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'notmuch)
+(require 'list)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq notmuch-saved-searches
+      '((:name "inbox" :query "tag:inbox" :key "i")
+        (:name "direct"
+         :query "tag:direct and tag:unread and not tag:sent"
+         :key "d")
+        (:name "action" :query "tag:action" :key "a")
+        (:name "review" :query "tag:review" :key "r")
+        (:name "waiting" :query "tag:waiting" :key "w")
+        (:name "broadcast" :query "tag:/broadcast\/.+/ and tag:unread" :key "b")
+        (:name "systems" :query "tag:/systems\/.+/ and tag:unread" :key "s")
+        (:name "sent" :query "tag:sent" :key "t")
+        (:name "drafts" :query "tag:draft" :key "D")))
+
+;; Sort results from newest-to-oldest.
+(setq notmuch-search-oldest-first nil)
+
+;; Discard noisy email signatures.
+(setq notmuch-mua-cite-function #'message-cite-original-without-signature)
+
+;; By default, this is just '("-inbox")
+(setq notmuch-archive-tags '("-inbox" "-unread" "+archive"))
+
+;; Show saved searches even when they're empty.
+(setq notmuch-show-empty-saved-searches t)
+
+;; Currently the sendmail executable on my system is symlinked to msmtp.
+(setq send-mail-function #'sendmail-send-it)
+
+;; I'm not sure if I need this or not. Copying it from tazjin@'s monorepo.
+(setq notmuch-always-prompt-for-sender nil)
+
+;; Add the "User-Agent" header to my emails and ensure that it includes Emacs
+;; and notmuch information.
+(setq notmuch-mua-user-agent-function
+      (lambda ()
+        (format "Emacs %s; notmuch.el %s" emacs-version notmuch-emacs-version)))
+
+;; I was informed that Gmail does this server-side
+(setq notmuch-fcc-dirs nil)
+
+;; Ensure buffers are closed after sending mail.
+(setq message-kill-buffer-on-exit t)
+
+;; Ensure sender is correctly passed to msmtp.
+(setq mail-specify-envelope-from t
+      message-sendmail-envelope-from 'header
+      mail-envelope-from 'header)
+
+;; Assert that no two saved searches share share a KBD
+(prelude-assert
+ (list-xs-distinct-by? (lambda (x) (plist-get x :key)) notmuch-saved-searches))
+
+(provide 'email)
+;;; email.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/fonts.el b/users/wpcarro/emacs/.emacs.d/wpc/fonts.el
new file mode 100644
index 0000000000..0f70f69c2b
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/fonts.el
@@ -0,0 +1,99 @@
+;;; fonts.el --- Font preferences -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Control my font preferences with ELisp.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'maybe)
+(require 'cl-lib)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defgroup fonts nil
+  "Customize group for fonts configuration.")
+
+(defcustom fonts-size "10"
+  "My preferred default font-size."
+  :group 'fonts)
+
+(defcustom fonts-size-step 10
+  "The amount (%) by which to increase or decrease a font."
+  :group 'fonts)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun fonts-set (font &optional size)
+  "Change the font to `FONT' with option integer, SIZE, in pixels."
+  (if (maybe-some? size)
+      (set-frame-font (string-format "%s %s" font size) nil t)
+    (set-frame-font font nil t)))
+
+(defun fonts-current ()
+  "Return the currently enabled font."
+  (symbol-name (font-get (face-attribute 'default :font) :family)))
+
+(defun fonts-increase-size ()
+  "Increase font size."
+  (interactive)
+  (->> (face-attribute 'default :height)
+       (+ fonts-size-step)
+       (set-face-attribute 'default (selected-frame) :height)))
+
+(defun fonts-decrease-size ()
+  "Decrease font size."
+  (interactive)
+  (->> (face-attribute 'default :height)
+       (+ (- fonts-size-step))
+       (set-face-attribute 'default (selected-frame) :height)))
+
+(defun fonts-reset-size ()
+  "Restore font size to its default value."
+  (interactive)
+  (fonts-set (fonts-current) fonts-size))
+
+(defun fonts-enable-ligatures ()
+  "Call this function to enable ligatures."
+  (interactive)
+  (let ((alist '((33 . ".\\(?:\\(?:==\\|!!\\)\\|[!=]\\)")
+                 (35 . ".\\(?:###\\|##\\|_(\\|[#(?[_{]\\)") ;;
+                 (36 . ".\\(?:>\\)")
+                 (37 . ".\\(?:\\(?:%%\\)\\|%\\)")
+                 (38 . ".\\(?:\\(?:&&\\)\\|&\\)")
+                 (42 . ".\\(?:\\(?:\\*\\*/\\)\\|\\(?:\\*[*/]\\)\\|[*/>]\\)") ;;
+                 (43 . ".\\(?:\\(?:\\+\\+\\)\\|[+>]\\)")
+                 (45 . ".\\(?:\\(?:-[>-]\\|<<\\|>>\\)\\|[<>}~-]\\)")
+                 (46 . ".\\(?:\\(?:\\.[.<]\\)\\|[.=-]\\)") ;;
+                 (47 . ".\\(?:\\(?:\\*\\*\\|//\\|==\\)\\|[*/=>]\\)")
+                 (48 . ".\\(?:x[a-zA-Z]\\)")
+                 (58 . ".\\(?:::\\|[:=]\\)")
+                 (59 . ".\\(?:;;\\|;\\)")
+                 (60 . ".\\(?:\\(?:!--\\)\\|\\(?:~~\\|->\\|\\$>\\|\\*>\\|\\+>\\|--\\|<[<=-]\\|=[<=>]\\||>\\)\\|[*$+~/<=>|-]\\)")
+                 (61 . ".\\(?:\\(?:/=\\|:=\\|<<\\|=[=>]\\|>>\\)\\|[<=>~]\\)")
+                 (62 . ".\\(?:\\(?:=>\\|>[=>-]\\)\\|[=>-]\\)")
+                 (63 . ".\\(?:\\(\\?\\?\\)\\|[:=?]\\)")
+                 (91 . ".\\(?:]\\)")
+                 (92 . ".\\(?:\\(?:\\\\\\\\\\)\\|\\\\\\)")
+                 (94 . ".\\(?:=\\)")
+                 (119 . ".\\(?:ww\\)")
+                 (123 . ".\\(?:-\\)")
+                 (124 . ".\\(?:\\(?:|[=|]\\)\\|[=>|]\\)")
+                 (126 . ".\\(?:~>\\|~~\\|[>=@~-]\\)"))))
+    (dolist (char-regexp alist)
+      (set-char-table-range composition-function-table (car char-regexp)
+                            `([,(cdr char-regexp) 0 font-shape-gstring])))))
+
+(provide 'fonts)
+;;; fonts.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/ivy-helpers.el b/users/wpcarro/emacs/.emacs.d/wpc/ivy-helpers.el
new file mode 100644
index 0000000000..3303237d52
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/ivy-helpers.el
@@ -0,0 +1,67 @@
+;;; ivy-helpers.el --- More interfaces to ivy -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Hopefully to improve my workflows.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'tuple)
+(require 'string)
+(require 'cl-lib)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defun ivy-helpers-kv (prompt kv f)
+  "PROMPT users with the keys in KV and return its corresponding value.
+
+Apply key and value from KV to F."
+  (ivy-read
+   prompt
+   kv
+   :require-match t
+   :action (lambda (entry)
+             (funcall f (car entry) (cdr entry)))))
+
+(defun ivy-helpers-do-run-external-command (cmd)
+  "Execute the specified CMD and notify the user when it finishes."
+  (message "Starting %s..." cmd)
+  (set-process-sentinel
+   (start-process-shell-command cmd nil cmd)
+   (lambda (process event)
+     (when (string= event "finished\n")
+       (message "%s process finished." process)))))
+
+(defun ivy-helpers-list-external-commands ()
+  "Create a list of all external commands available on $PATH."
+  (cl-loop
+   for dir in (split-string (getenv "PATH") path-separator)
+   when (and (file-exists-p dir) (file-accessible-directory-p dir))
+   for lsdir = (cl-loop for i in (directory-files dir t)
+                        for bn = (file-name-nondirectory i)
+                        when (and (not (s-contains? "-wrapped" i))
+                                  (not (member bn completions))
+                                  (not (file-directory-p i))
+                                  (file-executable-p i))
+                        collect bn)
+   append lsdir into completions
+   finally return (sort completions 'string-lessp)))
+
+(defun ivy-helpers-run-external-command ()
+  "Prompts the user with a list of all installed applications to launch."
+  (interactive)
+  (let ((external-commands-list (ivy-helpers-list-external-commands)))
+    (ivy-read "Command:" external-commands-list
+              :require-match t
+              :action #'ivy-helpers-do-run-external-command)))
+
+;;; Code:
+(provide 'ivy-helpers)
+;;; ivy-helpers.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/kbd.el b/users/wpcarro/emacs/.emacs.d/wpc/kbd.el
new file mode 100644
index 0000000000..7defc3d08f
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/kbd.el
@@ -0,0 +1,85 @@
+;;; kbd.el --- Elisp keybinding -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; In order to stay organized, I'm attempting to dedicate KBD prefixes to
+;; specific functions.  I'm hoping I can be more deliberate with my keybinding
+;; choices this way.
+;;
+;; Terminology:
+;; For a more thorough overview of the terminology refer to `keybindings.md'
+;; file.  Here's a brief overview:
+;; - workspace: Anything concerning EXWM workspaces.
+;; - x11: Anything concerning X11 applications.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'al)
+(require 'set)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst kbd-prefixes
+  '((workspace . "s")
+    (x11 . "C-s"))
+  "Mapping of functions to designated keybinding prefixes to stay organized.")
+
+;; Assert that no keybindings are colliding.
+(prelude-assert
+ (= (al-count kbd-prefixes)
+    (->> kbd-prefixes
+         al-values
+         set-from-list
+         set-count)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun kbd-raw (f x)
+  "Return the string keybinding for function F and appendage X.
+Values for F include:
+- workspace
+- x11"
+  (prelude-assert (al-has-key? f kbd-prefixes))
+  (string-format
+   "%s-%s"
+   (al-get f kbd-prefixes)
+   x))
+
+(defun kbd-for (f x)
+  "Return the `kbd' for function F and appendage X.
+Values for F include:
+- workspace
+- x11"
+  (kbd (kbd-raw f x)))
+
+;; TODO: Prefer copying human-readable versions to the clipboard.  Right now
+;; this isn't too useful.
+(defun kbd-copy-keycode ()
+  "Copy the pressed key to the system clipboard."
+  (interactive)
+  (message "[kbd] Awaiting keypress...")
+  (let ((key (read-key)))
+    (clipboard-copy (string-format "%s" key))
+    (message (string-format "[kbd] \"%s\" copied!" key))))
+
+(defun kbd-print-keycode ()
+  "Prints the pressed keybinding."
+  (interactive)
+  (message "[kbd] Awaiting keypress...")
+  (message (string-format "[kbd] keycode: %s" (read-key))))
+
+(provide 'kbd)
+;;; kbd.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/keybindings.el b/users/wpcarro/emacs/.emacs.d/wpc/keybindings.el
new file mode 100644
index 0000000000..a55bf27330
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/keybindings.el
@@ -0,0 +1,495 @@
+;;; keybindings.el --- Centralizing my keybindings -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; Attempting to centralize my keybindings to simplify my configuration.
+;;
+;; I have some expectations about my keybindings.  Here are some of those
+;; defined:
+;; - In insert mode:
+;;   - C-a: beginning-of-line
+;;   - C-e: end-of-line
+;;   - C-b: backwards-char
+;;   - C-f: forwards-char
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'screen-brightness)
+(require 'pulse-audio)
+(require 'scrot)
+(require 'ivy)
+(require 'ivy-clipmenu)
+(require 'ivy-helpers)
+(require 'general)
+(require 'exwm)
+(require 'vterm-mgt)
+(require 'buffer)
+(require 'fonts)
+(require 'bookmark)
+(require 'tvl)
+(require 'window-manager)
+
+;; Note: The following lines must be sorted this way.
+(setq evil-want-integration t)
+(setq evil-want-keybinding nil)
+(general-evil-setup)
+(require 'evil)
+(require 'evil-collection)
+(require 'evil-commentary)
+(require 'evil-surround)
+(require 'key-chord)
+(require 'edebug)
+(require 'avy)
+(require 'passage)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helper Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun keybindings--window-vsplit-right ()
+  "Split the window vertically and focus the right half."
+  (interactive)
+  (evil-window-vsplit)
+  (windmove-right))
+
+(defun keybindings--window-split-down ()
+  "Split the window horizontal and focus the bottom half."
+  (interactive)
+  (evil-window-split)
+  (windmove-down))
+
+(defun keybindings--create-snippet ()
+  "Create a window split and then opens the Yasnippet editor."
+  (interactive)
+  (evil-window-vsplit)
+  (call-interactively #'yas-new-snippet))
+
+(defun keybindings--replace-under-point ()
+  "Faster than typing %s//thing/g."
+  (interactive)
+  (let ((term (s-replace "/" "\\/" (symbol-to-string (symbol-at-point)))))
+    (save-excursion
+      (evil-ex (concat "%s/\\b" term "\\b/")))))
+
+(defun keybindings--evil-ex-define-cmd-local (cmd f)
+  "Define CMD to F locally to a buffer."
+  (unless (local-variable-p 'evil-ex-commands)
+    (setq-local evil-ex-commands (copy-alist evil-ex-commands)))
+  (evil-ex-define-cmd cmd f))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; General Keybindings
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Ensure that evil's command mode behaves with readline bindings.
+(general-define-key
+ :keymaps 'evil-ex-completion-map
+ "C-a" #'move-beginning-of-line
+ "C-e" #'move-end-of-line
+ "C-k" #'kill-line
+ "C-u" #'evil-delete-whole-line
+ "C-v" #'evil-paste-after
+ "C-d" #'delete-char
+ "C-f" #'forward-char
+ "M-b" #'backward-word
+ "M-f" #'forward-word
+ "M-d" #'kill-word
+ "M-DEL" #'backward-kill-word
+ "C-b" #'backward-char)
+
+(general-mmap
+  :keymaps 'override
+  "RET" #'evil-goto-line
+  "H"   #'evil-first-non-blank
+  "L"   #'evil-end-of-line
+  "_"   #'ranger
+  "-"   #'dired-jump
+  "sl"  #'keybindings--window-vsplit-right
+  "sh"  #'evil-window-vsplit
+  "sk"  #'evil-window-split
+  "sj"  #'keybindings--window-split-down)
+
+(general-nmap
+  :keymaps 'override
+  "gu" #'browse-url-at-point
+  "gd" #'xref-find-definitions
+  ;; Wrapping `xref-find-references' in the `let' binding to prevent xref from
+  ;; prompting.  There are other ways to handle this variable, such as setting
+  ;; it globally with `setq' or buffer-locally with `setq-local'.  For now, I
+  ;; prefer setting it with `let', which should bind it in the dynamic scope
+  ;; for the duration of the `xref-find-references' function call.
+  "gx" (lambda ()
+         (interactive)
+         (let ((xref-prompt-for-identifier nil))
+           (call-interactively #'xref-find-references))))
+
+(general-unbind 'motion "M-." "C-p" "<SPC>")
+(general-unbind 'normal "s"   "M-." "C-p" "C-n")
+(general-unbind 'insert "C-v" "C-d" "C-a" "C-e" "C-n" "C-p" "C-k")
+
+(customize-set-variable 'evil-symbol-word-search t)
+(evil-mode 1)
+(evil-collection-init)
+(evil-commentary-mode)
+(global-evil-surround-mode 1)
+
+;; Ensure the Evil search results get centered vertically.
+;; When Emacs is run from a terminal, this forces Emacs to redraw itself, which
+;; is visually disruptive.
+(when window-system
+  (progn
+    (defadvice isearch-update
+        (before advice-for-isearch-update activate)
+      (evil-scroll-line-to-center (line-number-at-pos)))
+    (defadvice evil-search-next
+        (after advice-for-evil-search-next activate)
+      (evil-scroll-line-to-center (line-number-at-pos)))
+    (defadvice evil-search-previous
+        (after advice-for-evil-search-previous activate)
+      (evil-scroll-line-to-center (line-number-at-pos)))))
+
+(general-define-key
+ :keymaps '(isearch-mode-map)
+ "C-p" #'isearch-ring-retreat
+ "C-n" #'isearch-ring-advance
+ "<up>" #'isearch-ring-retreat
+ "<down>" #'isearch-ring-advance)
+
+(general-define-key
+ :keymaps '(minibuffer-local-isearch-map)
+ "C-p" #'previous-line-or-history-element
+ "C-n" #'next-line-or-history-element
+ "<up>" #'previous-line-or-history-element
+ "<down>" #'next-line-or-history-element)
+
+(key-chord-mode 1)
+(key-chord-define evil-insert-state-map "jk" 'evil-normal-state)
+
+;; This may be contraversial, but I never use the prefix key, and I'd prefer to
+;; have to bound to the readline function that deletes the entire line.
+(general-unbind "C-u")
+
+(defmacro keybindings-exwm (c fn)
+  "Bind C to FN using `exwm-input-set-key' with `kbd' applied to C."
+  `(exwm-input-set-key (kbd ,c) ,fn))
+
+(keybindings-exwm "C-M-v" #'ivy-clipmenu-copy)
+(keybindings-exwm "<XF86MonBrightnessUp>" #'screen-brightness-increase)
+(keybindings-exwm "<XF86MonBrightnessDown>" #'screen-brightness-decrease)
+(keybindings-exwm "<XF86AudioMute>" #'pulse-audio-toggle-mute)
+(keybindings-exwm "<XF86AudioLowerVolume>" #'pulse-audio-decrease-volume)
+(keybindings-exwm "<XF86AudioRaiseVolume>" #'pulse-audio-increase-volume)
+(keybindings-exwm "<XF86AudioMicMute>" #'pulse-audio-toggle-microphone)
+(keybindings-exwm (kbd-raw 'x11 "s") #'scrot-select)
+(keybindings-exwm "<C-M-tab>" #'window-manager-switch-to-exwm-buffer)
+(keybindings-exwm (kbd-raw 'workspace "k") #'fonts-increase-size)
+(keybindings-exwm (kbd-raw 'workspace "j") #'fonts-decrease-size)
+(keybindings-exwm (kbd-raw 'workspace "0") #'fonts-reset-size)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Window sizing
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(keybindings-exwm "C-M-=" #'balance-windows)
+(keybindings-exwm "C-M-j" #'shrink-window)
+(keybindings-exwm "C-M-k" #'enlarge-window)
+(keybindings-exwm "C-M-h" #'shrink-window-horizontally)
+(keybindings-exwm "C-M-l" #'enlarge-window-horizontally)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Window Management
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(keybindings-exwm "M-h" #'windmove-left)
+(keybindings-exwm "M-j" #'windmove-down)
+(keybindings-exwm "M-k" #'windmove-up)
+(keybindings-exwm "M-l" #'windmove-right)
+(keybindings-exwm "M-\\" #'evil-window-vsplit)
+(keybindings-exwm "M--" #'evil-window-split)
+(keybindings-exwm "M-q" #'delete-window)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Miscellaneous
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(keybindings-exwm "M-:" #'eval-expression)
+(keybindings-exwm "M-SPC" #'ivy-helpers-run-external-command)
+(keybindings-exwm "M-x" #'counsel-M-x)
+(keybindings-exwm "<M-tab>" #'window-manager-next-workspace)
+(keybindings-exwm "<M-S-iso-lefttab>" #'window-manager-prev-workspace)
+(keybindings-exwm "C-S-f" #'window-manager-toggle-previous)
+(keybindings-exwm "C-M-\\" #'passage-select)
+
+(defun keybindings-copy-emoji ()
+  "Select an emoji from the completing-read menu."
+  (interactive)
+  (clipboard-copy (emojify-completing-read "Copy: ")))
+
+(keybindings-exwm "s-e" #'keybindings-copy-emoji)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Workspaces
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(keybindings-exwm (kbd-raw 'workspace "l")
+                  (lambda ()
+                    (interactive)
+                    (shell-command window-manager-screenlocker)))
+
+(general-define-key
+ :keymaps 'override
+ "M-q" #'delete-window
+ "<s-return>" #'toggle-frame-fullscreen
+ "M-h" #'windmove-left
+ "M-l" #'windmove-right
+ "M-k" #'windmove-up
+ "M-j" #'windmove-down
+ "M-q" #'delete-window)
+
+;; Support pasting in M-:.
+(general-define-key
+ :keymaps 'read-expression-map
+ "C-v"   #'clipboard-yank
+ "C-S-v" #'clipboard-yank)
+
+(general-define-key
+ :prefix "<SPC>"
+ :states '(normal)
+ "." #'ffap
+ "gn" #'notmuch
+ "i" #'counsel-semantic-or-imenu
+ "I" #'ibuffer
+ "hk" #'helpful-callable
+ "hf" #'helpful-function
+ "hm" #'helpful-macro
+ "hc" #'helpful-command
+ "hk" #'helpful-key
+ "hv" #'helpful-variable
+ "hp" #'helpful-at-point
+ "hi" #'info-apropos
+ "s" #'flyspell-mode
+ "S" #'sort-lines
+ "=" #'align
+ "p" #'flycheck-previous-error
+ "f" #'project-find-file
+ "n" #'flycheck-next-error
+ "N" #'smerge-next
+ "W" #'balance-windows
+ "gss" #'magit-status
+ "gsd" #'tvl-depot-status
+ "E" #'refine
+ "es" #'keybindings--create-snippet
+ "l" #'linum-mode
+ "B" #'magit-blame
+ "w" #'save-buffer
+ "r" #'keybindings--replace-under-point
+ "R" #'deadgrep)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Vterm
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Show or hide a vterm buffer.  I'm intentionally not defining this in
+;; vterm-mgt.el because it consumes `buffer-show-previous', and I'd like to
+;; avoid bloating vterm-mgt.el with dependencies that others may not want.
+(general-define-key (kbd-raw 'x11 "t")
+                    (lambda ()
+                      (interactive)
+                      (if (vterm-mgt--instance? (current-buffer))
+                          (switch-to-buffer (first (buffer-source-code-buffers)))
+                        (call-interactively #'vterm-mgt-find-or-create))))
+
+(general-define-key
+ :keymaps '(vterm-mode-map)
+ ;; For some reason vterm captures this KBD instead of EXWM
+ "C-S-f" nil
+ "s-x" #'vterm-mgt-select
+ "C-S-n" #'vterm-mgt-instantiate
+ "C-S-w" #'vterm-mgt-kill
+ "<C-tab>" #'vterm-mgt-next
+ "<C-S-iso-lefttab>" #'vterm-mgt-prev
+ "<s-backspace>" #'vterm-mgt-rename-buffer
+ ;; Without this, typing "+" is effectively no-op. Try for yourself:
+ ;; (vterm-send-key "<kp-add>")
+ "<kp-add>" "+"
+ "M--" #'evil-window-split
+ "M-\\" #'evil-window-vsplit)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; notmuch
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; evil-collection adds many KBDs to notmuch modes. Some of these I find
+;; disruptive.
+(general-define-key
+ :states '(normal)
+ :keymaps '(notmuch-show-mode-map)
+ "M-j" nil
+ "M-k" nil
+ "<C-S-iso-lefttab>" #'notmuch-show-previous-thread-show
+ "<C-tab>" #'notmuch-show-next-thread-show
+ "e" #'notmuch-show-archive-message-then-next-or-next-thread)
+
+(add-hook 'notmuch-message-mode-hook
+          (lambda ()
+            (keybindings--evil-ex-define-cmd-local "x" #'notmuch-mua-send-and-exit)))
+
+;; For now, I'm mimmicking Gmail KBDs that I have memorized and enjoy
+(general-define-key
+ :states '(normal visual)
+ :keymaps '(notmuch-search-mode-map)
+ "M"  (lambda ()
+        (interactive)
+        (notmuch-search-tag '("-inbox" "+muted")))
+ "mi" (lambda ()
+        (interactive)
+        (notmuch-search-tag '("+inbox" "-action" "-review" "-waiting" "-muted")))
+ "ma" (lambda ()
+        (interactive)
+        (notmuch-search-tag '("-inbox" "+action" "-review" "-waiting")))
+ "mr" (lambda ()
+        (interactive)
+        (notmuch-search-tag '("-inbox" "-action" "+review" "-waiting")))
+ "mw" (lambda ()
+        (interactive)
+        (notmuch-search-tag '("-inbox" "-action" "-review" "+waiting")))
+ "e" #'notmuch-search-archive-thread)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; magit
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(general-define-key
+ :states '(normal)
+ :keymaps '(magit-status-mode-map
+            magit-log-mode-map
+            magit-revision-mode-map)
+ "l" #'evil-forward-char
+ "L" #'magit-log)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Info-mode
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; NOTE: I find some of the following, existing KBDs useful:
+;;   M-x info-apropos
+;;   u   Info-up
+;;   M-n clone-buffer
+(general-define-key
+ :states '(normal)
+ :keymaps '(Info-mode-map)
+ "SPC" nil
+ "g SPC" #'Info-scroll-up
+ "RET" #'Info-follow-nearest-node
+ "<C-tab>" #'Info-next
+ "<C-S-iso-lefttab>" #'Info-prev
+ "g l" #'Info-history-back
+ "g t" #'Info-toc)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; ibuffer
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(general-define-key
+ :states '(normal)
+ :keymaps '(ibuffer-mode-map)
+ "M-j" nil
+ "K" #'ibuffer-do-delete)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; buffers
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(general-define-key
+ :states '(normal)
+ "C-f" #'buffer-cycle-next
+ "C-b" #'buffer-cycle-prev)
+
+(general-define-key
+ :prefix "<SPC>"
+ :states '(normal)
+ "b" #'buffer-ivy-source-code
+ "<SPC>" #'buffer-show-previous
+ "k" #'kill-buffer)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; edebug
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(general-define-key
+ :states '(normal)
+ :keymaps '(edebug-mode-map)
+ ;; this restores my ability to move-left while debugging
+ "h" nil)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; deadgrep
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(general-define-key
+ :states '(normal)
+ :keymaps '(deadgrep-mode-map)
+ "<tab>" #'deadgrep-forward
+ "<backtab>" #'deadgrep-backward)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; bookmarks
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(bookmark-install-kbd
+ (make-bookmark :label "wpcarro"
+                :path (f-join tvl-depot-path "users/wpcarro")
+                :kbd "w"))
+
+(bookmark-install-kbd
+ (make-bookmark :label "depot"
+                :path tvl-depot-path
+                :kbd "d"))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; refine
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(general-define-key
+ :keymaps '(refine-mode-map)
+ :states '(normal)
+ "K" #'refine-delete
+ "q" #'kill-this-buffer)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; avy
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(global-set-key (kbd "C-;") #'avy-goto-char)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; ivy
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; restore the ability to paste in ivy
+(general-define-key
+ :keymaps '(ivy-minibuffer-map)
+ "C-k" #'kill-line
+ "C-u" (lambda () (interactive) (kill-line 0))
+ "C-v" #'clipboard-yank
+ "C-S-v" #'clipboard-yank)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Rust
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(general-define-key
+ :keymaps '(rust-mode-map)
+ :states '(normal)
+ "gd" #'lsp-find-definition
+ "gr" #'lsp-find-references)
+
+(general-define-key
+ :keymaps '(rust-mode-map)
+ "TAB" #'company-indent-or-complete-common)
+
+(provide 'keybindings)
+;;; keybindings.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/keyboard.el b/users/wpcarro/emacs/.emacs.d/wpc/keyboard.el
new file mode 100644
index 0000000000..0ee00e1b84
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/keyboard.el
@@ -0,0 +1,138 @@
+;;; keyboard.el --- Managing keyboard preferences with Elisp -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Setting key repeat and other values.
+;;
+;; Be wary of suspiciously round numbers.  Especially those divisible by ten!
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'cl-lib)
+(require 'prelude)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support clamping functions for repeat-{rate,delay} to ensure only valid
+;; values are sent to xset.
+(defcustom keyboard-repeat-rate 80
+  "The number of key repeat signals sent per second.")
+
+(defcustom keyboard-repeat-delay 170
+  "The number of milliseconds before autorepeat starts.")
+
+(defconst keyboard-repeat-rate-copy keyboard-repeat-rate
+  "Copy of `keyboard-repeat-rate' to support `keyboard-reset-key-repeat'.")
+
+(defconst keyboard-repeat-delay-copy keyboard-repeat-delay
+  "Copy of `keyboard-repeat-delay' to support `keyboard-reset-key-repeat'.")
+
+(defcustom keyboard-install-preferences? t
+  "When t, install keyboard preferences.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun keyboard-message (x)
+  "Message X in a structured way."
+  (message (format "[keyboard.el] %s" x)))
+
+(cl-defun keyboard-set-key-repeat (&key
+                                   (rate keyboard-repeat-rate)
+                                   (delay keyboard-repeat-delay))
+  "Use xset to set the key-repeat RATE and DELAY."
+  (prelude-start-process
+   :name "keyboard-set-key-repeat"
+   :command (format "xset r rate %s %s" delay rate)))
+
+;; NOTE: Settings like this are machine-dependent. For instance I only need to
+;; do this on my laptop and other devices where I don't have access to my split
+;; keyboard.
+;; NOTE: Running keysym Caps_Lock is not idempotent.  If this is called more
+;; than once, xmodmap will start to error about non-existent Caps_Lock symbol.
+;; For more information see here:
+;; https://unix.stackexchange.com/questions/108207/how-to-map-caps-lock-as-the-compose-key-using-xmodmap-portably-and-idempotently
+(defun keyboard-swap-caps-lock-and-escape ()
+  "Swaps the caps lock and escape keys using xmodmap."
+  (interactive)
+  ;; TODO: Ensure these work once the tokenizing in prelude-start-process works
+  ;; as expected.
+  (start-process "keyboard-swap-caps-lock-and-escape"
+                 nil "/usr/bin/xmodmap" "-e" "remove Lock = Caps_Lock")
+  (start-process "keyboard-swap-caps-lock-and-escape"
+                 nil "/usr/bin/xmodmap" "-e" "keysym Caps_Lock = Escape"))
+
+(defun keyboard-inc-repeat-rate ()
+  "Increment `keyboard-repeat-rate'."
+  (interactive)
+  (setq keyboard-repeat-rate (1+ keyboard-repeat-rate))
+  (keyboard-set-key-repeat :rate keyboard-repeat-rate)
+  (keyboard-message
+   (format "Rate: %s" keyboard-repeat-rate)))
+
+(defun keyboard-dec-repeat-rate ()
+  "Decrement `keyboard-repeat-rate'."
+  (interactive)
+  (setq keyboard-repeat-rate (1- keyboard-repeat-rate))
+  (keyboard-set-key-repeat :rate keyboard-repeat-rate)
+  (keyboard-message
+   (format "Rate: %s" keyboard-repeat-rate)))
+
+(defun keyboard-inc-repeat-delay ()
+  "Increment `keyboard-repeat-delay'."
+  (interactive)
+  (setq keyboard-repeat-delay (1+ keyboard-repeat-delay))
+  (keyboard-set-key-repeat :delay keyboard-repeat-delay)
+  (keyboard-message
+   (format "Delay: %s" keyboard-repeat-delay)))
+
+(defun keyboard-dec-repeat-delay ()
+  "Decrement `keyboard-repeat-delay'."
+  (interactive)
+  (setq keyboard-repeat-delay (1- keyboard-repeat-delay))
+  (keyboard-set-key-repeat :delay keyboard-repeat-delay)
+  (keyboard-message
+   (format "Delay: %s" keyboard-repeat-delay)))
+
+(defun keyboard-print-key-repeat ()
+  "Print the currently set values for key repeat."
+  (interactive)
+  (keyboard-message
+   (format "Rate: %s. Delay: %s"
+           keyboard-repeat-rate
+           keyboard-repeat-delay)))
+
+(defun keyboard-set-preferences ()
+  "Reset the keyboard preferences to their default values.
+NOTE: This function exists because occasionally I unplug and re-plug in a
+  keyboard and all of the preferences that I set using xset disappear."
+  (interactive)
+  (keyboard-swap-caps-lock-and-escape)
+  (keyboard-set-key-repeat :rate keyboard-repeat-rate
+                           :delay keyboard-repeat-delay)
+  ;; TODO: Implement this message function as a macro that pulls the current
+  ;; file name.
+  (keyboard-message "Keyboard preferences set!"))
+
+(defun keyboard-reset-key-repeat ()
+  "Set key repeat rate and delay to original values."
+  (interactive)
+  (keyboard-set-key-repeat :rate keyboard-repeat-rate-copy
+                           :delay keyboard-repeat-delay-copy)
+  (keyboard-message "Key repeat preferences reset."))
+
+(when keyboard-install-preferences?
+  (keyboard-set-preferences))
+
+(provide 'keyboard)
+;;; keyboard.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/modeline.el b/users/wpcarro/emacs/.emacs.d/wpc/modeline.el
new file mode 100644
index 0000000000..df1cddec9d
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/modeline.el
@@ -0,0 +1,68 @@
+;;; modeline.el --- Customize my mode-line -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; Because I use EXWM, I treat my Emacs mode-line like my system bar: I need to
+;; quickly check the system time, and I expect it to be at the bottom-right of
+;; my Emacs frame.  I used doom-modeline for awhile, which is an impressive
+;; package, but it conditionally colorizes on the modeline for the active
+;; buffer.  So if my bottom-right window is inactive, I cannot see the time.
+;;
+;; My friend, @tazjin, has a modeline setup that I think is more compatible with
+;; EXWM, so I'm going to base my setup off of his.
+
+;;; Code:
+
+(use-package telephone-line)
+
+(defun modeline-bottom-right-window? ()
+  "Determines whether the last (i.e.
+bottom-right) window of the
+active frame is showing the buffer in which this function is
+  executed."
+  (let* ((frame (selected-frame))
+         (right-windows (window-at-side-list frame 'right))
+         (bottom-windows (window-at-side-list frame 'bottom))
+         (last-window (car (seq-intersection right-windows bottom-windows))))
+    (eq (current-buffer) (window-buffer last-window))))
+
+(defun modeline-maybe-render-time ()
+  "Conditionally renders the `mode-line-misc-info' string.
+
+  The idea is to not display information like the current time,
+  load, battery levels on all buffers."
+  (when (modeline-bottom-right-window?)
+    (telephone-line-raw mode-line-misc-info t)))
+
+(defun modeline-setup ()
+  "Render my custom modeline."
+  (telephone-line-defsegment telephone-line-last-window-segment ()
+    (modeline-maybe-render-time))
+  ;; Display the current EXWM workspace index in the mode-line
+  (telephone-line-defsegment telephone-line-exwm-workspace-index ()
+    (when (modeline-bottom-right-window?)
+      (format "[%s]" exwm-workspace-current-index)))
+  ;; Define a highlight font for ~ important ~ information in the last
+  ;; window.
+  (defface special-highlight
+    '((t (:foreground "white" :background "#5f627f"))) "")
+  (add-to-list 'telephone-line-faces
+               '(highlight . (special-highlight . special-highlight)))
+  (setq telephone-line-lhs
+        '((nil . (telephone-line-position-segment))
+          (accent . (telephone-line-buffer-segment))))
+  (setq telephone-line-rhs
+        '((accent . (telephone-line-major-mode-segment))
+          (nil . (telephone-line-last-window-segment
+                  telephone-line-exwm-workspace-index))))
+  (setq telephone-line-primary-left-separator 'telephone-line-tan-left
+        telephone-line-primary-right-separator 'telephone-line-tan-right
+        telephone-line-secondary-left-separator 'telephone-line-tan-hollow-left
+        telephone-line-secondary-right-separator 'telephone-line-tan-hollow-right)
+  (telephone-line-mode 1))
+
+(provide 'modeline)
+;;; modeline.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/prelude.el b/users/wpcarro/emacs/.emacs.d/wpc/prelude.el
new file mode 100644
index 0000000000..4a332cb8ca
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/prelude.el
@@ -0,0 +1,144 @@
+;;; prelude.el --- My attempt at augmenting Elisp stdlib -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Some of these ideas are scattered across other modules like `fs',
+;; `string-functions', etc.  I'd like to keep everything modular.  I still don't
+;; have an answer for which items belond in `misc'; I don't want that to become
+;; a dumping grounds.  Ideally this file will `require' all other modules and
+;; define just a handful of functions.
+
+;; TODO: Consider removing all dependencies from prelude.el.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'dash)
+(require 's)
+(require 'f)
+(require 'cl-lib)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Utilities
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun prelude-to-string (x)
+  "Convert X to a string."
+  (format "%s" x))
+
+(defun prelude-inspect (&rest args)
+  "Message ARGS where ARGS are any type."
+  (->> args
+       (-map #'prelude-to-string)
+       (apply #'s-concat)
+       message))
+
+(defmacro prelude-call-process-to-string (cmd &rest args)
+  "Return the string output of CMD called with ARGS."
+  `(with-temp-buffer
+     (call-process ,cmd nil (current-buffer) nil ,@args)
+     (buffer-string)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Assertions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Should I `throw' instead of `error' here?
+(defmacro prelude-assert (x)
+  "Errors unless X is t.
+These are strict assertions and purposely do not rely on truthiness."
+  (let ((as-string (prelude-to-string x)))
+    `(unless (equal t ,x)
+       (error (s-concat "Assertion failed: " ,as-string)))))
+
+(defmacro prelude-refute (x)
+  "Errors unless X is nil."
+  (let ((as-string (prelude-to-string x)))
+    `(unless (equal nil ,x)
+       (error (s-concat "Refutation failed: " ,as-string)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Adapter functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun prelude-identity (x)
+  "Return X unchanged."
+  x)
+
+(defun prelude-const (x)
+  "Return a variadic lambda that will return X."
+  (lambda (&rest _) x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Miscellaneous
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Consider packaging these into a linum-color.el package.
+;; TODO: Generate the color used here from the theme.
+(defvar prelude--linum-safe? nil
+  "Flag indicating whether it is safe to work with function `linum-mode'.")
+
+(defvar prelude--linum-mru-color nil
+  "Stores the color most recently attempted to be applied.")
+
+(add-hook 'linum-mode-hook
+          (lambda ()
+            (setq prelude--linum-safe? t)
+            (when (maybe-some? prelude--linum-mru-color)
+              (set-face-foreground 'linum prelude--linum-mru-color))))
+
+(defun prelude-set-line-number-color (color)
+  "Safely set linum color to `COLOR'.
+
+If this is called before Emacs initializes, the color will be stored in
+`prelude--linum-mru-color' and applied once initialization completes.
+
+Why is this safe?
+If `(set-face-foreground 'linum)' is called before initialization completes,
+Emacs will silently fail.  Without this function, it is easy to introduce
+difficult to troubleshoot bugs in your init files."
+  (if prelude--linum-safe?
+      (set-face-foreground 'linum color)
+    (setq prelude--linum-mru-color color)))
+
+(defun prelude-prompt (prompt)
+  "Read input from user with PROMPT."
+  (read-string prompt))
+
+(cl-defun prelude-start-process (&key name command)
+  "Pass command string, COMMAND, and the function name, NAME.
+This is a wrapper around `start-process' that has an API that resembles
+`shell-command'."
+  ;; TODO: Fix the bug with tokenizing here, since it will split any whitespace
+  ;; character, even though it shouldn't in the case of quoted string in shell.
+  ;; e.g. - "xmodmap -e 'one two three'" => '("xmodmap" "-e" "'one two three'")
+  (prelude-refute (s-contains? "'" command))
+  (let* ((tokens (s-split " " command))
+         (program-name (nth 0 tokens))
+         (program-args (cdr tokens)))
+    (apply #'start-process
+           `(,(format "*%s<%s>*" program-name name)
+             ,nil
+             ,program-name
+             ,@program-args))))
+
+(defun prelude-executable-exists? (name)
+  "Return t if CLI tool NAME exists according to the variable `exec-path'."
+  (let ((file (locate-file name exec-path)))
+    (require 'maybe)
+    (if (maybe-some? file)
+        (f-exists? file)
+      nil)))
+
+(defmacro prelude-time (x)
+  "Print the time it takes to evaluate X."
+  `(benchmark 1 ',x))
+
+(provide 'prelude)
+;;; prelude.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/pulse-audio.el b/users/wpcarro/emacs/.emacs.d/wpc/pulse-audio.el
new file mode 100644
index 0000000000..eaa6106590
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/pulse-audio.el
@@ -0,0 +1,69 @@
+;;; pulse-audio.el --- Control audio with Elisp -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Because everything in my configuration is turning into Elisp these days.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst pulse-audio--step-size 5
+  "The size by which to increase or decrease the volume.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun pulse-audio--message (x)
+  "Output X to *Messages*."
+  (message (string-format "[pulse-audio.el] %s" x)))
+
+(defun pulse-audio-toggle-mute ()
+  "Mute the default sink."
+  (interactive)
+  (prelude-start-process
+   :name "pulse-audio-toggle-mute"
+   :command "pactl set-sink-mute @DEFAULT_SINK@ toggle")
+  (pulse-audio--message "Mute toggled."))
+
+(defun pulse-audio-toggle-microphone ()
+  "Mute the default sink."
+  (interactive)
+  (prelude-start-process
+   :name "pulse-audio-toggle-microphone"
+   :command "pactl set-source-mute @DEFAULT_SOURCE@ toggle")
+  (pulse-audio--message "Microphone toggled."))
+
+(defun pulse-audio-decrease-volume ()
+  "Low the volume output of the default sink."
+  (interactive)
+  (prelude-start-process
+   :name "pulse-audio-decrease-volume"
+   :command (string-format "pactl set-sink-volume @DEFAULT_SINK@ -%s%%"
+                           pulse-audio--step-size))
+  (pulse-audio--message "Volume decreased."))
+
+(defun pulse-audio-increase-volume ()
+  "Raise the volume output of the default sink."
+  (interactive)
+  (prelude-start-process
+   :name "pulse-audio-increase-volume"
+   :command (string-format "pactl set-sink-volume @DEFAULT_SINK@ +%s%%"
+                           pulse-audio--step-size))
+  (pulse-audio--message "Volume increased."))
+
+(provide 'pulse-audio)
+;;; pulse-audio.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/region.el b/users/wpcarro/emacs/.emacs.d/wpc/region.el
new file mode 100644
index 0000000000..0b692981f8
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/region.el
@@ -0,0 +1,23 @@
+;;; region.el --- Functions for working with regions -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Sometimes Emacs's function names and argument ordering is great; other times,
+;; it isn't.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun region-to-string ()
+  "Return the string in the active region."
+  (buffer-substring-no-properties (region-beginning)
+                                  (region-end)))
+
+(provide 'region)
+;;; region.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/screen-brightness.el b/users/wpcarro/emacs/.emacs.d/wpc/screen-brightness.el
new file mode 100644
index 0000000000..851be9f99f
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/screen-brightness.el
@@ -0,0 +1,57 @@
+;;; screen-brightness.el --- Control laptop screen brightness -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Control your laptop's screen brightness.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defgroup screen-brightness nil "Configuration for screen-brightness.")
+
+(defcustom screen-brightness-increase-cmd
+  "light -A 3"
+  "The shell command to run to increase screen brightness."
+  :group 'screen-brightness
+  :type 'string)
+
+(defcustom screen-brightness-decrease-cmd
+  "light -U 3"
+  "The shell command to run to decrease screen brightness."
+  :group 'screen-brightness
+  :type 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun screen-brightness-increase ()
+  "Increase the screen brightness."
+  (interactive)
+  (prelude-start-process
+   :name "screen-brightness-increase"
+   :command screen-brightness-increase-cmd)
+  (message "[screen-brightness.el] Increased screen brightness."))
+
+(defun screen-brightness-decrease ()
+  "Decrease the screen brightness."
+  (interactive)
+  (prelude-start-process
+   :name "screen-brightness-decrease"
+   :command screen-brightness-decrease-cmd)
+  (message "[screen-brightness.el] Decreased screen brightness."))
+
+(provide 'screen-brightness)
+;;; screen-brightness.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/scrot.el b/users/wpcarro/emacs/.emacs.d/wpc/scrot.el
new file mode 100644
index 0000000000..08994fea5f
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/scrot.el
@@ -0,0 +1,54 @@
+;;; scrot.el --- Screenshot functions -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; scrot is a Linux utility for taking screenshots.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'f)
+(require 'string)
+(require 'ts)
+(require 'clipboard)
+(require 'kbd)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst scrot-screenshot-directory "~/Downloads"
+  "The default directory for screenshot outputs.")
+
+(defconst scrot-output-format "screenshot_%H:%M:%S_%Y-%m-%d.png"
+  "The format string for the output screenshot file.
+See scrot's man page for more information.")
+
+(defun scrot--copy-image (path)
+  "Use xclip to copy the image at PATH to the clipboard.
+This currently only works for PNG files because that's what I'm outputting"
+  (call-process "xclip" nil nil nil
+                "-selection" "clipboard" "-t" "image/png" path)
+  (message (string-format "[scrot.el] Image copied to clipboard!")))
+
+(defun scrot-select ()
+  "Click-and-drag to screenshot a region.
+The output path is copied to the user's clipboard."
+  (interactive)
+  (let ((screenshot-path (f-join scrot-screenshot-directory
+                                 (ts-format scrot-output-format (ts-now)))))
+    (make-process
+     :name "scrot-select"
+     :command `("scrot" "--select" ,screenshot-path)
+     :sentinel (lambda (proc _err)
+                 (when (= 0 (process-exit-status proc))
+                   (scrot--copy-image screenshot-path))))))
+
+(provide 'scrot)
+;;; scrot.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/ssh.el b/users/wpcarro/emacs/.emacs.d/wpc/ssh.el
new file mode 100644
index 0000000000..1179e90363
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/ssh.el
@@ -0,0 +1,67 @@
+;;; ssh.el --- When working remotely -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Configuration to make remote work easier.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'tramp)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Is "ssh" preferable to "scp"?
+(setq tramp-default-method "ssh")
+
+;; Taken from: https://superuser.com/questions/179313/tramp-waiting-for-prompts-from-remote-shell
+(setq tramp-shell-prompt-pattern "^[^$>\n]*[#$%>] *\\(\[[0-9;]*[a-zA-Z] *\\)*")
+
+;; Sets the value of the TERM variable to "dumb" when logging into the remote
+;; host. This allows me to check for the value of "dumb" in my shell's init file
+;; and control the startup accordingly. You can see in the (shamefully large)
+;; commit, 0b4ef0e, that I added a check like this to my ~/.zshrc. I've since
+;; switched from z-shell to fish. I don't currently have this check in
+;; config.fish, but I may need to add it one day soon.
+(setq tramp-terminal-type "dumb")
+
+;; Maximizes the tramp debugging noisiness while I'm still learning about tramp.
+(setq tramp-verbose 10)
+
+;; As confusing as this may seem, this forces Tramp to use *my* .ssh/config
+;; options, which enable ControlMaster. In other words, disabling this actually
+;; enables ControlMaster.
+(setq tramp-use-ssh-controlmaster-options nil)
+
+(defcustom ssh-hosts '("wpcarro@wpcarro.dev"
+                       "foundation"
+                       "edge")
+  "List of hosts to which I commonly connect.")
+
+(defun ssh-sudo-buffer ()
+  "Open the current buffer with sudo rights."
+  (interactive)
+  (with-current-buffer (current-buffer)
+    (if (s-starts-with? "/ssh:" buffer-file-name)
+        (pcase (s-split ":" buffer-file-name)
+          (`(,one ,two ,three) (find-file (format "/ssh:%s|sudo:%s:%s" two two three))))
+        (find-file
+         (s-join ":" (-insert-at 2 "|sudo" (s-split ":" buffer-file-name))))
+      (find-file (format "/sudo::%s" buffer-file-name)))))
+
+(defun ssh-cd-home ()
+  "Prompt for an SSH host and open a dired buffer for wpcarro on that machine."
+  (interactive)
+  (let ((machine (completing-read "Machine: " ssh-hosts)))
+    (find-file (format "/ssh:%s:~" machine))))
+
+(provide 'ssh)
+;;; ssh.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/window-manager.el b/users/wpcarro/emacs/.emacs.d/wpc/window-manager.el
new file mode 100644
index 0000000000..94fb99d427
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/window-manager.el
@@ -0,0 +1,228 @@
+;;; window-manager.el --- Functions augmenting my usage of EXWM -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; I switched to EXWM from i3, and I haven't looked back.  One day I may write a
+;; poem declaring my love for Emacs and EXWM.  For now, I haven't the time.
+
+;; Wist List:
+;; - TODO: Consider supporting MRU cache of worksapces.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'alert)
+(require 'cycle)
+(require 'dash)
+(require 'kbd)
+(require 's)
+(require 'exwm)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Variables
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defgroup window-manager nil
+  "Customization options for `window-manager'.")
+
+(cl-defstruct window-manager-named-workspace
+  label kbd display)
+
+(defcustom window-manager-named-workspaces nil
+  "List of `window-manager-named-workspace' structs."
+  :group 'window-manager
+  :type (list 'window-manager-named-workspace))
+
+(defcustom window-manager-screenlocker "xsecurelock"
+  "Reference to a screen-locking executable."
+  :group 'window-manager
+  :type 'string)
+
+(defvar window-manager--workspaces nil
+  "Cycle of the my EXWM workspaces.")
+
+(defconst window-manager--modes
+  (cycle-from-list (list #'window-manager--char-mode
+                         #'window-manager--line-mode))
+  "Functions to switch exwm modes.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun window-manager--alert (x)
+  "Message X with a structured format."
+  (alert (s-concat "[exwm] " x)))
+
+(cl-defun window-manager-init (&key init-hook)
+  "Call `exwm-enable' alongside other bootstrapping functions."
+  (require 'exwm-config)
+  (require 'exwm-randr)
+  (setq exwm-randr-workspace-monitor-plist
+        (->> window-manager-named-workspaces
+             (-map-indexed (lambda (i x)
+                             (list i (window-manager-named-workspace-display x))))
+             -flatten))
+  (setq exwm-workspace-number (length window-manager-named-workspaces))
+  (setq exwm-input-simulation-keys
+        '(([?\C-b] . [left])
+          ([?\M-b] . [C-left])
+          ([?\C-f] . [right])
+          ([?\M-f] . [C-right])
+          ([?\C-p] . [up])
+          ([?\C-n] . [down])
+          ([?\C-a] . [home])
+          ([?\C-e] . [end])
+          ([?\C-d] . [delete])
+          ([?\C-c] . [C-c])))
+  ;; Install workspace KBDs
+  (progn
+    (->> window-manager-named-workspaces
+         (list-map #'window-manager--register-kbd))
+    (window-manager--alert "Registered workspace KBDs!"))
+  ;; Ensure exwm apps open in char-mode.
+  (add-hook 'exwm-manage-finish-hook #'window-manager--char-mode)
+  (add-hook 'exwm-init-hook init-hook)
+  (setq window-manager--workspaces
+        (cycle-from-list window-manager-named-workspaces))
+  (exwm-randr-enable)
+  (exwm-enable))
+
+(defun window-manager-next-workspace ()
+  "Cycle forwards to the next workspace."
+  (interactive)
+  (window-manager--change-workspace (cycle-next! window-manager--workspaces)))
+
+(defun window-manager-prev-workspace ()
+  "Cycle backwards to the previous workspace."
+  (interactive)
+  (window-manager--change-workspace (cycle-prev! window-manager--workspaces)))
+
+;; Here is the code required to toggle EXWM's modes.
+(defun window-manager--line-mode ()
+  "Switch exwm to line-mode."
+  (call-interactively #'exwm-input-grab-keyboard)
+  (window-manager--alert "Switched to line-mode"))
+
+(defun window-manager--char-mode ()
+  "Switch exwm to char-mode."
+  (call-interactively #'exwm-input-release-keyboard)
+  (window-manager--alert "Switched to char-mode"))
+
+(defun window-manager-toggle-mode ()
+  "Switch between line- and char- mode."
+  (interactive)
+  (with-current-buffer (window-buffer)
+    (when (eq major-mode 'exwm-mode)
+      (funcall (cycle-next! window-manager--modes)))))
+
+(defun window-manager--label->index (label workspaces)
+  "Return the index of the workspace in WORKSPACES named LABEL."
+  (let ((index (-elem-index label (-map #'window-manager-named-workspace-label
+                                        workspaces))))
+    (if index index (error (format "No workspace found for label: %s" label)))))
+
+(defun window-manager--register-kbd (workspace)
+  "Registers a keybinding for WORKSPACE struct.
+Currently using super- as the prefix for switching workspaces."
+  (let ((handler (lambda ()
+                   (interactive)
+                   (window-manager--switch
+                    (window-manager-named-workspace-label workspace))))
+        (key (window-manager-named-workspace-kbd workspace)))
+    (exwm-input-set-key
+     (kbd-for 'workspace key)
+     handler)))
+
+(defun window-manager--change-workspace (workspace)
+  "Switch EXWM workspaces to the WORKSPACE struct."
+  (exwm-workspace-switch
+   (window-manager--label->index
+    (window-manager-named-workspace-label workspace)
+    window-manager-named-workspaces))
+  (window-manager--alert
+   (format "Switched to: %s"
+           (window-manager-named-workspace-label workspace))))
+
+(defun window-manager--switch (label)
+  "Switch to a named workspaces using LABEL."
+  (cycle-focus! (lambda (x)
+                  (equal label
+                         (window-manager-named-workspace-label x)))
+                window-manager--workspaces)
+  (window-manager--change-workspace (cycle-current window-manager--workspaces)))
+
+(defun window-manager-toggle-previous ()
+  "Focus the previously active EXWM workspace."
+  (interactive)
+  (window-manager--change-workspace
+   (cycle-focus-previous! window-manager--workspaces)))
+
+(defun window-manager--exwm-buffer? (x)
+  "Return t if buffer X is an EXWM buffer."
+  (equal 'exwm-mode (buffer-local-value 'major-mode x)))
+
+(defun window-manager--application-name (buffer)
+  "Return the name of the application running in the EXWM BUFFER.
+This function asssumes that BUFFER passes the `window-manager--exwm-buffer?'
+predicate."
+  (with-current-buffer buffer exwm-class-name))
+
+;; TODO: Support disambiguating between two or more instances of the same
+;; application. For instance if two `exwm-class-name' values are
+;; "Google-chrome", find a encode this information in the `buffer-alist'.
+(defun window-manager-switch-to-exwm-buffer ()
+  "Use `completing-read' to focus an EXWM buffer."
+  (interactive)
+  (let* ((buffer-alist (->> (buffer-list)
+                            (-filter #'window-manager--exwm-buffer?)
+                            (-map
+                             (lambda (buffer)
+                               (cons (window-manager--application-name buffer)
+                                     buffer)))))
+         (label (completing-read "Switch to EXWM buffer: " buffer-alist)))
+    (exwm-workspace-switch-to-buffer
+     (al-get label buffer-alist))))
+
+(defun window-manager-current-workspace ()
+  "Output the label of the currently active workspace."
+  (->> window-manager--workspaces
+       cycle-current
+       window-manager-named-workspace-label))
+
+(defun window-manager-workspace-move ()
+  "Prompt the user to move the current workspace to another."
+  (interactive)
+  (exwm-workspace-move
+   exwm-workspace--current
+   (window-manager--label->index
+    (completing-read "Move current workspace to: "
+                     (->> window-manager-named-workspaces
+                          (-map #'window-manager-named-workspace-label))
+                     nil
+                     t)
+    window-manager-named-workspaces)))
+
+(defun window-manager-move-window ()
+  "Prompt the user to move the current window to another workspace."
+  (interactive)
+  (let ((window (get-buffer-window))
+        (dest (completing-read "Move current window to: "
+                               (->> window-manager-named-workspaces
+                                    (-map #'window-manager-named-workspace-label))
+                               nil
+                               t)))
+    (exwm-workspace-move-window
+     (exwm-workspace--workspace-from-frame-or-index
+      (window-manager--label->index dest window-manager-named-workspaces))
+     (exwm--buffer->id window))
+    (window-manager--switch dest)))
+
+(provide 'window-manager)
+;;; window-manager.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-clojure.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-clojure.el
new file mode 100644
index 0000000000..5582641b3f
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-clojure.el
@@ -0,0 +1,71 @@
+;;; wpc-clojure.el --- My Clojure preferences -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; Hosting my Clojure tooling preferences
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'general)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package clojure-mode
+  :config
+  ;; from Ryan Schmukler:
+  (setq cljr-magic-require-namespaces
+        '(("io" . "clojure.java.io")
+          ("sh" . "clojure.java.shell")
+          ("jdbc" . "clojure.java.jdbc")
+          ("set" . "clojure.set")
+          ("time" . "java-time")
+          ("str" . "cuerdas.core")
+          ("path" . "pathetic.core")
+          ("walk" . "clojure.walk")
+          ("zip" . "clojure.zip")
+          ("async" . "clojure.core.async")
+          ("component" . "com.stuartsierra.component")
+          ("http" . "clj-http.client")
+          ("url" . "cemerick.url")
+          ("sql" . "honeysql.core")
+          ("csv" . "clojure.data.csv")
+          ("json" . "cheshire.core")
+          ("s" . "clojure.spec.alpha")
+          ("fs" . "me.raynes.fs")
+          ("ig" . "integrant.core")
+          ("cp" . "com.climate.claypoole")
+          ("re-frame" . "re-frame.core")
+          ("rf" . "re-frame.core")
+          ("re" . "reagent.core")
+          ("reagent" . "reagent.core")
+          ("u.core" . "utopia.core")
+          ("gen" . "clojure.spec.gen.alpha"))))
+
+(use-package cider
+  :config
+  (general-define-key
+    :keymaps 'cider-repl-mode-map
+    "C-l"    #'cider-repl-clear-buffer
+    "C-u"    #'kill-whole-line
+    "<up>"   #'cider-repl-previous-input
+    "<down>" #'cider-repl-next-input)
+  (general-define-key
+   :keymaps 'clojure-mode-map
+   :states '(normal)
+   :prefix "<SPC>"
+   "x" #'cider-eval-defun-at-point
+   "X" #'cider-eval-buffer
+   "d" #'cider-symbol-at-point)
+  (setq cider-prompt-for-symbol nil))
+
+(provide 'wpc-clojure)
+;;; wpc-clojure.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-company.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-company.el
new file mode 100644
index 0000000000..89fde4b6a1
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-company.el
@@ -0,0 +1,41 @@
+;;; wpc-company.el --- Autocompletion package, company, preferences -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; Hosts my company mode preferences
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'general)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; autocompletion client
+(use-package company
+  :config
+  (general-define-key
+    :keymaps 'company-active-map
+    "C-j" #'company-select-next
+    "C-n" #'company-select-next
+    "C-k" #'company-select-previous
+    "C-p" #'company-select-previous
+    "C-d" #'company-show-doc-buffer)
+  (setq company-tooltip-align-annotations t)
+  (setq company-idle-delay 0)
+  (setq company-show-numbers t)
+  (setq company-minimum-prefix-length 2)
+  (setq company-dabbrev-downcase nil
+        company-dabbrev-ignore-case t)
+  (global-company-mode))
+
+(provide 'wpc-company)
+;;; wpc-company.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-dired.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-dired.el
new file mode 100644
index 0000000000..7c22a4657f
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-dired.el
@@ -0,0 +1,51 @@
+;;; wpc-dired.el --- My dired preferences -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; File management in Emacs, if learned and configured properly, should be
+;; capable to reduce my dependency on the terminal.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'macros)
+(require 'general)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(progn
+  (require 'dired)
+  (setq dired-recursive-copies 'always
+        dired-recursive-deletes 'top)
+  (setq dired-listing-switches "-la --group-directories-first")
+  (general-define-key
+   :keymaps 'dired-mode-map
+   :states '(normal)
+   ;; Overriding some KBDs defined in the evil-collection module.
+   "o" #'dired-find-file-other-window
+   "<SPC>" nil ;; This unblocks some of my leader-prefixed KBDs.
+   "s" nil ;; This unblocks my window-splitting KBDs.
+   "c" #'find-file
+   "f" #'project-find-file
+   "-" (lambda () (interactive) (find-alternate-file "..")))
+  (general-add-hook 'dired-mode-hook
+                    (list (macros-enable dired-hide-details-mode)
+                          #'auto-revert-mode)))
+
+(progn
+  (require 'locate)
+  (general-define-key
+   :keymaps 'locate-mode-map
+   :states 'normal
+   "o" #'dired-find-file-other-window))
+
+(provide 'wpc-dired)
+;;; wpc-dired.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-dotnet.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-dotnet.el
new file mode 100644
index 0000000000..03fc430e48
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-dotnet.el
@@ -0,0 +1,16 @@
+;;; wpc-dotnet.el --- C# and company -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+
+;;; Commentary:
+;; Windows things v0v.
+
+;;; Code:
+
+(require 'macros)
+
+(use-package csharp-mode)
+(macros-support-file-extension "csproj" xml-mode)
+
+(provide 'wpc-dotnet)
+;;; wpc-dotnet.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-elixir.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-elixir.el
new file mode 100644
index 0000000000..69259274c8
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-elixir.el
@@ -0,0 +1,27 @@
+;;; wpc-elixir.el --- Elixir / Erland configuration -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; My preferences for working with Elixir / Erlang projects
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'macros)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package elixir-mode
+  :config
+  (macros-add-hook-before-save 'elixir-mode-hook #'elixir-format))
+
+(provide 'wpc-elixir)
+;;; wpc-elixir.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-flycheck.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-flycheck.el
new file mode 100644
index 0000000000..c32c5daeff
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-flycheck.el
@@ -0,0 +1,17 @@
+;;; wpc-flycheck.el --- My flycheck configuration -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Hosts my Flycheck preferences
+
+;;; Code:
+
+(use-package flycheck
+  :config
+  (global-flycheck-mode))
+
+(provide 'wpc-flycheck)
+;;; wpc-flycheck.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-golang.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-golang.el
new file mode 100644
index 0000000000..47198c8e02
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-golang.el
@@ -0,0 +1,42 @@
+;;; wpc-golang.el --- Tooling preferences for Go -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Tooling support for golang development.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'macros)
+(require 'general)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support jumping to go source code for fmt.Println, etc.
+
+(use-package go-mode
+  :config
+  (setq gofmt-command "goimports")
+  ;; TODO: Consider configuring `xref-find-definitions' to use `godef-jump'
+  ;; instead of shadowing the KBD here.
+  (general-define-key
+   :states '(normal)
+   :keymaps '(go-mode-map)
+   "M-." #'godef-jump)
+  ;; Support calling M-x `compile'.
+  (add-hook 'go-mode-hook (lambda ()
+                            (setq-local tab-width 2)
+                            (setq-local compile-command "go build -v")))
+  (macros-add-hook-before-save 'go-mode-hook #'gofmt-before-save))
+
+(provide 'wpc-golang)
+;;; wpc-golang.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-haskell.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-haskell.el
new file mode 100644
index 0000000000..536790e36d
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-haskell.el
@@ -0,0 +1,53 @@
+;;; wpc-haskell.el --- My Haskell preferences -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Hosts my Haskell development preferences
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'macros)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; font-locking, glyph support, etc
+(use-package haskell-mode
+  :config
+  (macros-add-hook-before-save 'haskell-mode #'haskell-align-imports))
+
+;; Test toggling
+(defun wpc-haskell-module->test ()
+  "Jump from a module to a test."
+  (let ((filename (->> buffer-file-name
+                       (s-replace "/src/" "/test/")
+                       (s-replace ".hs" "Test.hs")
+                       find-file)))
+    (make-directory (f-dirname filename) t)
+    (find-file filename)))
+
+(defun wpc-haskell-test->module ()
+  "Jump from a test to a module."
+  (let ((filename (->> buffer-file-name
+                       (s-replace "/test/" "/src/")
+                       (s-replace "Test.hs" ".hs"))))
+    (make-directory (f-dirname filename) t)
+    (find-file filename)))
+
+(defun wpc-haskell-test<->module ()
+  "Toggle between test and module in Haskell."
+  (interactive)
+  (if (s-contains? "/src/" buffer-file-name)
+      (wpc-haskell-module->test)
+    (wpc-haskell-test->module)))
+
+(provide 'wpc-haskell)
+;;; wpc-haskell.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-javascript.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-javascript.el
new file mode 100644
index 0000000000..9e137ad880
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-javascript.el
@@ -0,0 +1,93 @@
+;;; wpc-javascript.el --- My Javascript preferences -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; This module hosts my Javascript tooling preferences.  This also includes
+;; tooling for TypeScript and other frontend tooling.  Perhaps this module will
+;; change names to more accurately reflect that.
+;;
+;; Depends
+;; - yarn global add prettier
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'general)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Constants
+(defconst wpc-javascript--js-hooks
+  '(js-mode-hook
+    web-mode-hook
+    typescript-mode-hook
+    js2-mode-hook
+    rjsx-mode-hook)
+  "All of the commonly used hooks for Javascript buffers.")
+
+(defconst wpc-javascript--frontend-hooks
+  (-insert-at 0 'css-mode-hook wpc-javascript--js-hooks)
+  "All of the commonly user hooks for frontend development.")
+
+;; frontend indentation settings
+(setq typescript-indent-level 2
+      js-indent-level 2
+      css-indent-offset 2)
+
+(use-package web-mode
+  :mode "\\.html\\'"
+  :config
+  (setq web-mode-css-indent-offset 2)
+  (setq web-mode-code-indent-offset 2)
+  (setq web-mode-markup-indent-offset 2))
+
+;; JSX highlighting
+(use-package rjsx-mode
+  :config
+  (general-unbind rjsx-mode-map "<" ">" "C-d")
+  (general-nmap
+    :keymaps 'rjsx-mode-map
+    "K" #'flow-minor-type-at-pos)
+  (setq js2-mode-show-parse-errors nil
+        js2-mode-show-strict-warnings nil))
+
+(progn
+  (defun wpc-javascript-tide-setup ()
+    (interactive)
+    (tide-setup)
+    (flycheck-mode 1)
+    (setq flycheck-check-syntax-automatically '(save mode-enabled))
+    (eldoc-mode 1)
+    (tide-hl-identifier-mode 1)
+    (company-mode 1))
+  (use-package tide
+    :config
+    (add-hook 'typescript-mode-hook #'wpc-javascript-tide-setup))
+  (require 'web-mode)
+  (add-to-list 'auto-mode-alist '("\\.tsx\\'" . web-mode))
+  (add-hook 'web-mode-hook
+            (lambda ()
+              (when (string-equal "tsx" (f-ext buffer-file-name))
+                (wpc-javascript-tide-setup))))
+  (flycheck-add-mode 'typescript-tslint 'web-mode))
+
+;; JS autoformatting
+(use-package prettier-js
+  :config
+  (general-add-hook wpc-javascript--frontend-hooks #'prettier-js-mode))
+
+;; Support Elm
+(use-package elm-mode
+  :config
+  (add-hook 'elm-mode-hook #'elm-format-on-save-mode))
+
+(provide 'wpc-javascript)
+;;; wpc-javascript.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-language-support.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-language-support.el
new file mode 100644
index 0000000000..8363e3c08e
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-language-support.el
@@ -0,0 +1,36 @@
+;;; wpc-language-support.el --- Support for miscellaneous programming languages -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; I defined this module to declutter my init.el.
+;;
+;; When a particular programming-language's configuration gets too complicated,
+;; I break it out into a dedicated module. Everything else gets dumped in
+;; "Miscellaneous Configuration".
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dedicated Modules
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'wpc-lisp)
+(require 'wpc-haskell)
+(require 'wpc-elixir)
+(require 'wpc-nix)
+(require 'wpc-rust)
+(require 'wpc-clojure)
+(require 'wpc-prolog)
+(require 'wpc-dotnet)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Miscellaneous Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package terraform-mode)
+
+(provide 'wpc-language-support)
+;;; wpc-language-support.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-lisp.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-lisp.el
new file mode 100644
index 0000000000..599d426204
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-lisp.el
@@ -0,0 +1,123 @@
+;;; wpc-lisp.el --- Generic LISP preferences -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; parent (up)
+;; child (down)
+;; prev-sibling (left)
+;; next-sibling (right)
+
+;;; Code:
+
+;; TODO: Consider having a separate module for each LISP dialect.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'general)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst wpc-lisp--hooks
+  '(lisp-mode-hook
+    emacs-lisp-mode-hook
+    clojure-mode-hook
+    clojurescript-mode-hook
+    racket-mode-hook)
+  "List of LISP modes.")
+
+(use-package sly
+  :config
+  (setq inferior-lisp-program "sbcl")
+  (general-define-key
+   :keymaps 'sly-mode-map
+   :states '(normal)
+   :prefix "<SPC>"
+   "x" #'sly-eval-defun
+   "X" #'sly-eval-buffer
+   "d" #'sly-describe-symbol))
+
+(use-package rainbow-delimiters
+  :config
+  (general-add-hook wpc-lisp--hooks #'rainbow-delimiters-mode))
+
+(use-package racket-mode
+  :config
+  (general-define-key
+   :keymaps 'racket-mode-map
+   :states 'normal
+   :prefix "<SPC>"
+   "x" #'racket-send-definition
+   "X" #'racket-run
+   "d" #'racket-describe)
+  (setq racket-program "~/.nix-profile/bin/racket"))
+
+(use-package lispyville
+  :init
+  (defconst wpc-lisp--lispyville-key-themes
+    '(c-w
+      operators
+      text-objects
+      prettify
+      commentary
+      slurp/barf-cp
+      wrap
+      additional
+      additional-insert
+      additional-wrap
+      escape)
+    "All available key-themes in Lispyville.")
+  :config
+  (general-add-hook wpc-lisp--hooks #'lispyville-mode)
+  (lispyville-set-key-theme wpc-lisp--lispyville-key-themes)
+  (progn
+    (general-define-key
+     :keymaps 'lispyville-mode-map
+     :states 'motion
+     ;; first unbind
+     "M-h" nil
+     "M-l" nil)
+    (general-define-key
+     :keymaps 'lispyville-mode-map
+     :states 'normal
+     ;; first unbind
+     "M-j" nil
+     "M-k" nil
+     ;; second rebind
+     "C-s-h" #'lispyville-drag-backward
+     "C-s-l" #'lispyville-drag-forward
+     "C-s-e" #'lispyville-end-of-defun
+     "C-s-a" #'lispyville-beginning-of-defun)))
+
+;; Elisp
+(use-package elisp-slime-nav
+  :config
+  (general-add-hook 'emacs-lisp-mode #'ielm-mode))
+
+(defun wpc-lisp-copy-elisp-eval-output ()
+  "Copy the output of the elisp evaluation"
+  (interactive)
+  (call-interactively 'eval-last-sexp)
+  (clipboard-copy (current-message)
+                  :message (format "%s - copied!" (current-message))))
+
+(general-define-key
+ :keymaps 'emacs-lisp-mode-map
+ :prefix "<SPC>"
+ :states 'normal
+ "c" #'wpc-lisp-copy-elisp-eval-output
+ "x" #'eval-defun
+ "X" #'eval-buffer
+ "d" (lambda ()
+       (interactive)
+       (with-current-buffer (current-buffer)
+         (helpful-function (symbol-at-point)))))
+
+(provide 'wpc-lisp)
+;;; wpc-lisp.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-misc.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-misc.el
new file mode 100644
index 0000000000..36fbf8b12c
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-misc.el
@@ -0,0 +1,330 @@
+;;; wpc-misc.el --- Hosting miscellaneous configuration -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; This is the home of any configuration that couldn't find a better home.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'project)
+(require 'f)
+(require 'dash)
+(require 'tvl)
+(require 'region)
+(require 'general)
+(require 'constants)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq display-time-string-forms
+      '((format-time-string "%H:%M %a %b %d")))
+(display-time-mode 1)
+
+;; Remove the boilerplate in the *scratch* buffer
+(setq initial-scratch-message "")
+
+;; disable custom variable entries from being written to ~/.emacs.d/init.el
+(setq custom-file (f-join user-emacs-directory "custom.el"))
+(load custom-file 'noerror)
+
+;; integrate Emacs with X11 clipboard
+(customize-set-variable 'select-enable-primary t)
+(customize-set-variable 'select-enable-clipboard t)
+(customize-set-variable 'evil-visual-update-x-selection-p nil)
+(general-def 'insert
+  "s-v" #'clipboard-yank
+  "C-S-v" #'clipboard-yank)
+
+;; transparently edit compressed files
+(auto-compression-mode t)
+
+;; autowrap when over the fill-column
+(setq-default auto-fill-function #'do-auto-fill)
+
+;; link to Emacs source code
+;; TODO: Update this link.
+(setq find-function-C-source-directory
+      "~/Dropbox/programming/emacs/src")
+
+;; change emacs prompts from "yes or no" -> "y or n"
+(fset 'yes-or-no-p 'y-or-n-p)
+
+;; open photos in Emacs
+(auto-image-file-mode 1)
+
+;; disable line-wrapping
+(setq-default truncate-lines 1)
+
+;; shell file indentation
+(setq sh-basic-offset 2)
+(setq sh-indentation 2)
+
+(use-package vterm
+  :config
+  (general-define-key
+   :keymaps '(vterm-mode-map)
+   :states '(insert)
+   "C-S-v" #'vterm-yank)
+  (general-define-key
+   :keymaps '(vterm-mode-map)
+   :states '(normal)
+   "K" #'evil-scroll-line-up
+   "J" #'evil-scroll-line-down
+   "C-b" #'evil-scroll-page-up
+   "C-f" #'evil-scroll-page-down))
+
+;; Use en Emacs buffer as a REST client.
+;; For more information: http://emacsrocks.com/e15.html
+(use-package restclient)
+
+;; Run `package-lint' before publishing to MELPA.
+(use-package package-lint)
+
+;; Parser combinators in Elisp.
+(use-package parsec)
+
+;; disable company mode when editing markdown
+;; TODO: move this out of wpc-misc.el and into a later file to call
+;; `(disable company-mode)'
+(use-package markdown-mode
+  :config
+  ;; TODO: Add assertion that pandoc is installed and it is accessible from
+  ;; Emacs.
+  (setq markdown-command "pandoc")
+  (setq markdown-split-window-direction 'right)
+  ;; (add-hook 'markdown-mode-hook #'markdown-live-preview-mode)
+  ;; Use mode-specific syntax highlighting for code blocks.
+  (setq markdown-fontify-code-blocks-natively t)
+  ;; Prevent Emacs from adding a space after the leading 3x-backticks.
+  (setq markdown-spaces-after-code-fence 0))
+
+(use-package alert)
+
+(use-package refine)
+
+;; Required by some google-emacs package commands.
+(use-package deferred)
+
+;; git integration
+(use-package magit
+  :config
+  (add-hook 'git-commit-setup-hook
+            (lambda ()
+              (company-mode -1)
+              (flyspell-mode 1)))
+  (setq magit-display-buffer-function
+        #'magit-display-buffer-same-window-except-diff-v1))
+
+(use-package magit-popup)
+
+;; http
+(use-package request)
+
+;; TVL depot stuff
+(use-package tvl)
+
+;; perl-compatible regular expressions
+(use-package pcre2el)
+
+;; alternative to help
+(use-package helpful)
+
+;; If called from an existing helpful-mode buffer, reuse that buffer; otherwise,
+;; call `pop-to-buffer'.
+(setq helpful-switch-buffer-function
+      (lambda (buffer-or-name)
+        (if (eq major-mode 'helpful-mode)
+            (switch-to-buffer buffer-or-name)
+          (pop-to-buffer buffer-or-name))))
+
+;; Emacs integration with direnv
+(use-package direnv
+  :config
+  (direnv-mode))
+
+;; Superior Elisp library for working with dates and times.
+;; TODO: Put this where my other installations for dash.el, s.el, a.el, and
+;; other utility Elisp libraries are located.
+(use-package ts)
+
+;; persist history etc b/w Emacs sessions
+(setq desktop-save 'if-exists)
+(desktop-save-mode 1)
+(setq desktop-globals-to-save
+      (append '((extended-command-history . 30)
+                (file-name-history        . 100)
+                (grep-history             . 30)
+                (compile-history          . 30)
+                (minibuffer-history       . 50)
+                (query-replace-history    . 60)
+                (read-expression-history  . 60)
+                (regexp-history           . 60)
+                (regexp-search-ring       . 20)
+                (search-ring              . 20)
+                (shell-command-history    . 50)
+                tags-file-name
+                register-alist)))
+
+;; configure ibuffer
+(setq ibuffer-default-sorting-mode 'major-mode)
+
+;; Emacs autosave, backup, interlocking files
+(setq auto-save-default nil
+      make-backup-files nil
+      create-lockfiles nil)
+
+;; ensure code wraps at 80 characters by default
+(setq-default fill-column 80)
+
+;; render tabs 2x-chars wide
+(setq tab-width 2)
+
+(put 'narrow-to-region 'disabled nil)
+
+;; trim whitespace on save
+(add-hook 'before-save-hook #'delete-trailing-whitespace)
+
+;; call `git secret hide` after saving secrets.json
+(add-hook 'after-save-hook
+          (lambda ()
+            (when (f-equal? (buffer-file-name)
+                            (f-join tvl-depot-path
+                                    "users"
+                                    "wpcarro"
+                                    "secrets.json"))
+              (shell-command "git secret hide"))))
+
+;; use tabs instead of spaces
+(setq-default indent-tabs-mode nil)
+
+;; prefer shorter tab-widths (e.g. writing Go code)
+(setq-default tab-width 2)
+
+;; automatically follow symlinks
+(setq vc-follow-symlinks t)
+
+;; fullscreen settings
+(setq ns-use-native-fullscreen nil)
+
+(use-package yasnippet
+  :config
+  (unless constants-ci?
+    (setq yas-snippet-dirs (list (f-join user-emacs-directory "snippets")))
+    (yas-global-mode 1)))
+
+(use-package projectile
+  :config
+  (projectile-mode t))
+
+;; TODO(wpcarro): Consider replacing this with a TVL version if it exists.
+(defun wpc-misc--depot-find (dir)
+  "Find the default.nix nearest to DIR."
+  ;; I use 'vc only at the root of my monorepo because 'transient doesn't use my
+  ;; .gitignore, which slows things down. Ideally, I could write a version that
+  ;; behaves like 'transient but also respects my monorepo's .gitignore and any
+  ;; ancestor .gitignore files.
+  (if (f-equal? tvl-depot-path dir)
+      (cons 'vc dir)
+    (when (f-ancestor-of? tvl-depot-path dir)
+      (if (f-exists? (f-join dir "default.nix"))
+          (cons 'transient dir)
+        (wpc-misc--depot-find (f-parent dir))))))
+
+(add-to-list 'project-find-functions #'wpc-misc--depot-find)
+
+(defun wpc-misc-pkill (name)
+  "Call the pkill executable using NAME as its argument."
+  (interactive "sProcess name: ")
+  (call-process "pkill" nil nil nil name))
+
+(use-package deadgrep
+  :config
+  (general-define-key
+   :keymaps 'deadgrep-mode-map
+   :states 'normal
+   "o" #'deadgrep-visit-result-other-window)
+  (setq-default deadgrep--context '(0 . 3))
+  (defun wpc-misc-deadgrep-region ()
+    "Run a ripgrep search on the active region."
+    (interactive)
+    (deadgrep (region-to-string)))
+  (defun wpc-misc-deadgrep-dwim ()
+    "If a region is active, use that as the search, otherwise don't."
+    (interactive)
+    (with-current-buffer (current-buffer)
+      (if (region-active-p)
+          (setq deadgrep--additional-flags '("--multiline"))
+          (wpc-misc-deadgrep-region)
+        (call-interactively #'deadgrep))))
+  (advice-add 'deadgrep--arguments
+              :filter-return
+              (lambda (args)
+                (push "--hidden" args)
+                (push "--follow" args))))
+
+;; TODO: Do I need this when I have swiper?
+(use-package counsel)
+
+(use-package counsel-projectile)
+
+;; search Google, Stackoverflow from within Emacs
+(use-package engine-mode
+  :config
+  (defengine google
+    "http://www.google.com/search?ie=utf-8&oe=utf-8&q=%s"
+    :keybinding "g")
+  (defengine stack-overflow
+    "https://stackoverflow.com/search?q=%s"
+    :keybinding "s"))
+
+;; EGlot (another LSP client)
+(use-package eglot)
+
+;; Microsoft's Debug Adapter Protocol (DAP)
+(use-package dap-mode
+  :after lsp-mode
+  :config
+  (dap-mode 1)
+  (dap-ui-mode 1))
+
+;; Microsoft's Language Server Protocol (LSP)
+(use-package lsp-ui
+  :config
+  (add-hook 'lsp-mode-hook #'lsp-ui-mode))
+
+;; Wilfred/suggest.el - Tool for discovering functions basesd on declaring your
+;; desired inputs and outputs.
+(use-package suggest)
+
+;; Malabarba/paradox - Enhances the `list-packages' view.
+(use-package paradox
+  :config
+  (paradox-enable))
+
+;; render emojis in Emacs ๐Ÿ•บ
+(use-package emojify
+  :config
+  (add-hook 'after-init-hook #'global-emojify-mode)
+  ;; Disable the default styles of:
+  ;; - ascii  :P (When this is enabled, the vim command, :x, renders as ๐Ÿ˜ถ)
+  ;; - github :smile:
+  (setq emojify-emoji-styles '(unicode)))
+
+;; Always auto-close parantheses and other pairs
+(electric-pair-mode)
+
+;; Start the Emacs server
+(when (not (server-running-p))
+  (server-start))
+
+(provide 'wpc-misc)
+;;; wpc-misc.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-nix.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-nix.el
new file mode 100644
index 0000000000..e9dc203691
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-nix.el
@@ -0,0 +1,37 @@
+;;; wpc-nix.el --- Nix support -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; Configuration to support working with Nix.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'tvl)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package nix-mode
+  :mode "\\.nix\\'")
+
+(defun wpc-nix-rebuild-emacs ()
+  "Use nix-env to rebuild wpcarros-emacs."
+  (interactive)
+  (let* ((pname (format "nix-env -iA users.wpcarro.emacs.nixos"))
+         (bname (format "*%s*" pname)))
+    (start-process pname bname
+                   "nix-env"
+                   "-f" tvl-depot-path
+                   "-iA" "users.wpcarro.emacs.nixos")
+    (display-buffer bname)))
+
+(provide 'wpc-nix)
+;;; wpc-nix.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-org.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-org.el
new file mode 100644
index 0000000000..229177220b
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-org.el
@@ -0,0 +1,39 @@
+;;; wpc-org.el --- My org preferences -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.1"))
+
+;;; Commentary:
+;; Hosts my org mode preferences
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'f)
+(require 'macros)
+(require 'general)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package org
+  :config
+  (evil-set-initial-state 'org-mode 'normal)
+  (general-add-hook 'org-mode-hook
+                    (list (macros-disable linum-mode)
+                          (macros-disable company-mode)))
+  (setq org-startup-folded nil)
+  (setq org-todo-keywords '((sequence "TODO" "BLOCKED" "DONE")))
+  (general-unbind 'normal org-mode-map "M-h" "M-j" "M-k" "M-l"))
+
+(use-package org-bullets
+  :config
+  (general-add-hook 'org-mode-hook (macros-enable org-bullets-mode)))
+
+(provide 'wpc-org)
+;;; wpc-org.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-package.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-package.el
new file mode 100644
index 0000000000..9c57bb4270
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-package.el
@@ -0,0 +1,32 @@
+;;; wpc-package.el --- My package configuration -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.1"))
+
+;;; Commentary:
+;; This module hosts all of the settings required to work with ELPA,
+;; MELPA, QUELPA, and co.
+
+;;; Code:
+
+(require 'package)
+
+;; Even though we're packaging our Emacs with Nix, having MELPA registered is
+;; helpful to ad-hoc test out packages before declaratively adding them to
+;; emacs/default.nix.
+(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/"))
+(package-initialize)
+
+(unless (package-installed-p 'use-package)
+  ;; TODO: Consider removing this to improve initialization speed.
+  (package-refresh-contents)
+  (package-install 'use-package))
+(eval-when-compile
+  (require 'use-package))
+;; TODO: Consider removing this, since I'm requiring general.el in individual
+;; modules.
+(use-package general)
+
+(provide 'wpc-package)
+;;; wpc-package.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-prolog.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-prolog.el
new file mode 100644
index 0000000000..6779431c12
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-prolog.el
@@ -0,0 +1,19 @@
+;;; wpc-prolog.el --- For Prologging things -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Code configuring my Prolog work.
+
+;;; Code:
+
+(require 'macros)
+
+;; TODO: Notice that the .pl extension conflicts with Perl files. This may
+;; become a problem should I start working with Perl.
+(macros-support-file-extension "pl" prolog-mode)
+
+(provide 'wpc-prolog)
+;;; wpc-prolog.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-python.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-python.el
new file mode 100644
index 0000000000..9ffb7c4c8a
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-python.el
@@ -0,0 +1,24 @@
+;;; wpc-python.el --- Python configuration -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; My Python configuration settings
+;;
+;; Depends
+;; - `apti yapf`
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package py-yapf
+  :config
+  (add-hook 'python-mode-hook #'py-yapf-enable-on-save))
+
+(provide 'wpc-python)
+;;; wpc-python.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-rust.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-rust.el
new file mode 100644
index 0000000000..b609efb431
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-rust.el
@@ -0,0 +1,30 @@
+;;; wpc-rust.el --- Support Rust language -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Supports my Rust work.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'lsp)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package rust-mode
+  :config
+  (setq lsp-rust-server #'rust-analyzer)
+  (setq rust-format-show-buffer nil)
+  (setq rust-format-on-save t)
+  (add-hook 'rust-mode-hook #'lsp))
+
+(provide 'wpc-rust)
+;;; wpc-rust.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-shell.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-shell.el
new file mode 100644
index 0000000000..f4229ed328
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-shell.el
@@ -0,0 +1,31 @@
+;;; wpc-shell.el --- POSIX Shell scripting support -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Helpers for my shell scripting.  Includes bash, zsh, etc.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'zle)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Code
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(use-package flymake-shellcheck
+  :commands flymake-shellcheck-load
+  :init
+  (add-hook 'sh-mode-hook #'flymake-shellcheck-load)
+  (add-hook 'sh-mode-hook #'zle-minor-mode))
+
+(use-package fish-mode)
+
+(provide 'wpc-shell)
+;;; wpc-shell.el ends here
diff --git a/users/wpcarro/emacs/.emacs.d/wpc/wpc-ui.el b/users/wpcarro/emacs/.emacs.d/wpc/wpc-ui.el
new file mode 100644
index 0000000000..a2f533cec0
--- /dev/null
+++ b/users/wpcarro/emacs/.emacs.d/wpc/wpc-ui.el
@@ -0,0 +1,186 @@
+;;; wpc-ui.el --- Any related to the UI/UX goes here -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Hosts font settings, scrolling, color schemes.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require '>)
+(require 'al)
+(require 'constants)
+(require 'dash)
+(require 'fonts)
+(require 'general)
+(require 'modeline)
+(require 'prelude)
+(require 'theme)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; line height
+(setq-default line-spacing 0)
+
+(when window-system
+  (setq frame-title-format '(buffer-file-name "%f" ("%b"))))
+
+;; Ensure that buffers update when their contents change on disk.
+(global-auto-revert-mode t)
+
+;; smooth scrolling settings
+(setq scroll-step 1
+      scroll-conservatively 10000)
+
+;; clean up modeline
+(use-package diminish
+  :config
+  (diminish 'emacs-lisp-mode "elisp")
+  (diminish 'evil-commentary-mode)
+  (diminish 'flycheck-mode)
+  (diminish 'auto-revert-mode)
+  (diminish 'which-key-mode)
+  (diminish 'yas-minor-mode)
+  (diminish 'lispyville-mode)
+  (diminish 'undo-tree-mode)
+  (diminish 'company-mode)
+  (diminish 'projectile-mode)
+  (diminish 'eldoc-mode)
+  ;; This is how to diminish `auto-fill-mode'.
+  (diminish 'auto-fill-function)
+  (diminish 'counsel-mode)
+  (diminish 'ivy-mode))
+
+;; TODO: Further customize `mode-line-format' variable.
+(delete 'mode-line-modes mode-line-format)
+(delete '(vc-mode vc-mode) mode-line-format)
+
+;; disable startup screen
+(setq inhibit-startup-screen t)
+
+;; disable toolbar
+(tool-bar-mode -1)
+
+;; premium Emacs themes
+(use-package doom-themes
+  :config
+  (setq doom-themes-enable-bold t
+        doom-themes-enable-italic t)
+  (doom-themes-visual-bell-config)
+  (doom-themes-org-config))
+
+;; kbd discovery
+(use-package which-key
+  :config
+  (setq which-key-idle-delay 0.25)
+  (which-key-mode))
+
+;; completion framework
+(use-package ivy
+  :config
+  (counsel-mode t)
+  (ivy-mode t)
+  ;; Remove preceding "^" from ivy prompts
+  (setq ivy-initial-inputs-alist nil)
+  ;; prefer using `helpful' variants
+  (progn
+    (setq counsel-describe-function-function #'helpful-callable)
+    (setq counsel-describe-variable-function #'helpful-variable))
+  (general-define-key
+   :keymaps '(ivy-minibuffer-map ivy-switch-buffer-map)
+   ;; prev
+   "C-k" #'ivy-previous-line
+   "<backtab>" #'ivy-previous-line
+   ;; next
+   "C-j" #'ivy-next-line
+   "<tab>" #'ivy-next-line))
+
+(use-package ivy-prescient
+  :config
+  (ivy-prescient-mode 1)
+  (unless constants-ci?
+    (prescient-persist-mode 1)))
+
+;; all-the-icons
+(use-package all-the-icons
+  :config
+  (unless (or constants-ci?
+              (f-exists? "~/.local/share/fonts/all-the-icons.ttf")
+              (f-exists? "~/Library/Fonts/all-the-icons.ttf"))
+    (all-the-icons-install-fonts t)))
+
+;; icons for Ivy
+(use-package all-the-icons-ivy
+  :after (ivy all-the-icons)
+  :config
+  (all-the-icons-ivy-setup))
+
+;; disable menubar
+(menu-bar-mode -1)
+
+;; reduce noisiness of auto-revert-mode
+(setq auto-revert-verbose nil)
+
+;; highlight lines that are over 80 characters long
+(use-package whitespace
+  :config
+  ;; TODO: This should change depending on the language and project. For
+  ;; example, Google Java projects prefer 100 character width instead of 80
+  ;; character width.
+  (setq whitespace-line-column 80)
+  (setq whitespace-style '(face lines-tail tabs))
+  (global-whitespace-mode t))
+
+;; dirname/filename instead of filename<dirname>
+(setq uniquify-buffer-name-style 'forward)
+
+;; highlight matching parens, brackets, etc
+(show-paren-mode 1)
+
+;; hide the scroll-bars in the GUI
+(scroll-bar-mode -1)
+
+;; TODO: Learn how to properly integrate this with dunst or another system-level
+;; notification program.
+;; GUI alerts in emacs
+(use-package alert
+  :commands (alert)
+  :config
+  (setq alert-default-style 'notifier))
+
+(display-battery-mode 1)
+
+(setq theme-whitelist
+      (->> (custom-available-themes)
+           (list-map #'symbol-name)
+           (list-filter (>-> (s-starts-with? "doom-")))
+           (list-map #'intern)
+           cycle-from-list))
+(setq theme-linum-color-override "da5478")
+(add-hook 'theme-after-change
+          (lambda () (prelude-set-line-number-color "#da5478")))
+(theme-whitelist-set 'doom-flatwhite)
+
+(when window-system
+  ;; On OSX, JetBrainsMono is installed as "JetBrains Mono", and I'm
+  ;; not sure how to change that.
+  (let ((font (if constants-osx? "JetBrains Mono" "JetBrainsMono")))
+    (fonts-set font)
+    ;; Some themes (e.g. doom-acario-*) change the font for comments. This
+    ;; should prevent that.
+    (set-face-attribute font-lock-comment-face nil
+                        :family font
+                        :slant 'normal)))
+
+(modeline-setup)
+
+(provide 'wpc-ui)
+;;; wpc-ui.el ends here
diff --git a/users/wpcarro/emacs/AppIcon.icns b/users/wpcarro/emacs/AppIcon.icns
new file mode 100644
index 0000000000..b3be251ccf
--- /dev/null
+++ b/users/wpcarro/emacs/AppIcon.icns
Binary files differdiff --git a/users/wpcarro/emacs/README.md b/users/wpcarro/emacs/README.md
new file mode 100644
index 0000000000..16f4fc31f9
--- /dev/null
+++ b/users/wpcarro/emacs/README.md
@@ -0,0 +1,15 @@
+# Emacs
+
+Emacs is one of a handful software projects that I highly value. I consider it
+as central to my workflow as `git` and `nix`.
+
+## Installing
+
+If you already have `depot` on your local file system, run the following from
+the top-level `depot` directory:
+
+```shell
+$ nix-env -iA users.wpcarro.emacs.nixos
+```
+
+Test edit (from depot).
diff --git a/users/wpcarro/emacs/ci.el b/users/wpcarro/emacs/ci.el
new file mode 100644
index 0000000000..9dfaf3056f
--- /dev/null
+++ b/users/wpcarro/emacs/ci.el
@@ -0,0 +1,44 @@
+;; This script initializes Emacs and exits with either a zero or non-zero status
+;; depending on whether or not Emacs initialized without logging warnings or
+;; encountering errors.
+;;
+;; This script reads the location of init.el as the last argument in `argv'.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'prelude)
+(require 'f)
+(require 'dash)
+(require 'buffer)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Script
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar init-el-path (-last-item argv)
+  "Path to the init.el file that this script attempts to load.")
+
+(prelude-assert (f-exists? init-el-path))
+
+(condition-case err
+    (load init-el-path)
+  (error
+   (message "Encountered an error while attempting to load init.el: %s" err)
+   (kill-emacs 1)))
+
+(when (buffer-exists? "*Errors*")
+  (progn
+    (with-current-buffer "*Errors*"
+      (message "Encountered errors in *Errors* buffer: %s" (buffer-string)))
+    (kill-emacs 1)))
+
+(when (buffer-exists? "*Warnings*")
+  (progn
+    (with-current-buffer "*Warnings*"
+      (message "Encountered warnings in *Warnings* buffer: %s" (buffer-string)))
+    (kill-emacs 1)))
+
+(message "Successfully initialized Emacs without errors or warnings!")
+(kill-emacs 0)
diff --git a/users/wpcarro/emacs/default.nix b/users/wpcarro/emacs/default.nix
new file mode 100644
index 0000000000..0b3c5a6e73
--- /dev/null
+++ b/users/wpcarro/emacs/default.nix
@@ -0,0 +1,267 @@
+# My Emacs distribution, which is supporting the following platforms:
+# - Linux
+# - Darwin
+#
+# USAGE:
+#   $ nix-build -A users.wpcarro.emacs.osx -o /Applications/BillsEmacs.app
+{ depot, pkgs, lib, ... }:
+
+# TODO(wpcarro): See if it's possible to expose emacsclient on PATH, so that I
+# don't need to depend on wpcarros-emacs and emacs in my NixOS configurations.
+let
+  inherit (depot.third_party.nixpkgs) emacsPackagesFor emacs28;
+  inherit (depot.users) wpcarro;
+  inherit (lib) mapAttrsToList;
+  inherit (lib.strings) concatStringsSep makeBinPath;
+  inherit (pkgs) runCommand writeShellScriptBin;
+
+  emacsBinPath = makeBinPath (
+    wpcarro.common.shell-utils ++
+    # Rust dependencies
+    (with pkgs; [
+      cargo
+      rust-analyzer
+      rustc
+      rustfmt
+    ]) ++
+    # Misc dependencies
+    (with pkgs; [
+      ispell
+      nix
+      rust-analyzer
+      rustc
+      rustfmt
+      xorg.xset
+    ] ++
+    (if pkgs.stdenv.isLinux then [
+      scrot
+    ] else [ ]))
+  );
+
+  emacsWithPackages = (emacsPackagesFor emacs28).emacsWithPackages;
+
+  wpcarrosEmacs = emacsWithPackages (epkgs:
+    (with wpcarro.emacs.pkgs; [
+      al
+      bookmark
+      cycle
+      list
+      macros
+      maybe
+      passage
+      set
+      string
+      struct
+      symbol
+      theme
+      tuple
+      vterm-mgt
+      zle
+    ]) ++
+
+    (with epkgs.tvlPackages; [
+      tvl
+    ]) ++
+
+    (with epkgs.elpaPackages; [
+      exwm
+    ]) ++
+
+    (with epkgs.melpaPackages; [
+      alert
+      all-the-icons
+      all-the-icons-ivy
+      avy
+      base16-theme
+      cider
+      clojure-mode
+      company
+      counsel
+      counsel-projectile
+      csharp-mode
+      dap-mode
+      dash
+      deadgrep
+      deferred
+      diminish
+      direnv
+      dockerfile-mode
+      # TODO(wpcarro): broken since channel bump cl/10204
+      # doom-themes
+      elisp-slime-nav
+      elixir-mode
+      elm-mode
+      emojify
+      engine-mode
+      evil
+      evil-collection
+      evil-commentary
+      evil-surround
+      f
+      fish-mode
+      flycheck
+      flymake-shellcheck
+      general
+      go-mode
+      haskell-mode
+      helpful
+      ivy
+      ivy-clipmenu
+      ivy-prescient
+      key-chord
+      lispyville
+      lsp-ui
+      magit
+      magit-popup
+      markdown-mode
+      nix-mode
+      notmuch
+      org-bullets
+      package-lint
+      paradox
+      parsec
+      pcre2el
+      prettier-js
+      projectile
+      py-yapf
+      racket-mode
+      rainbow-delimiters
+      reason-mode
+      refine
+      request
+      restclient
+      rjsx-mode
+      rust-mode
+      sly
+      suggest
+      telephone-line
+      terraform-mode
+      tide
+      ts
+      tuareg
+      use-package
+      vterm
+      web-mode
+      which-key
+      yaml-mode
+      yasnippet
+    ]) ++
+
+    [
+      epkgs.eglot # from elpa devel
+    ]);
+
+  loadPath = concatStringsSep ":" [
+    ./.emacs.d/wpc
+    # TODO(wpcarro): Explain why the trailing ":" is needed.
+    "${wpcarrosEmacs.deps}/share/emacs/site-lisp:"
+  ];
+
+  # Transform an attrset into "export k=v" statements.
+  makeEnvVars = env: concatStringsSep "\n"
+    (mapAttrsToList (k: v: "export ${k}=\"${v}\"") env);
+
+  withEmacsPath = { emacsBin, env ? { }, load ? [ ] }:
+    writeShellScriptBin "wpcarros-emacs" ''
+      export XMODIFIERS=emacs
+      export PATH="${emacsBinPath}:$PATH"
+      export EMACSLOADPATH="${loadPath}"
+      ${makeEnvVars env}
+      exec ${emacsBin} \
+        --debug-init \
+        --no-init-file \
+        --no-site-file \
+        --no-site-lisp \
+        --load ${./.emacs.d/init.el} \
+        ${concatStringsSep "\n  " (map (el: "--load ${el} \\") load)}
+        "$@"
+    '';
+
+  # I can't figure out how to augment LSEnvironment.PATH such that it inherits
+  # the default $PATH and adds the things that I need as well, so let's
+  # hardcode the desired outcome in the meantime.
+  osxDefaultPath = builtins.concatStringsSep ":" [
+    "/Users/bill/.nix-profile/bin"
+    "/nix/var/nix/profiles/default/bin"
+    "/opt/homebrew/bin"
+    "/opt/homebrew/sbin"
+    "/usr/local/bin"
+    "/usr/bin"
+    "/bin"
+    "/usr/sbin"
+    "/sbin"
+    "/opt/X11/bin"
+  ];
+
+  infoPlist = pkgs.writeText "Info.plist" (pkgs.lib.generators.toPlist { } {
+    LSEnvironment = {
+      PATH = "${emacsBinPath}:${osxDefaultPath}";
+    };
+    CFBundleExecutable = "BillsEmacs";
+    CFBundleDisplayName = "BillsEmacs";
+    CFBundleIconFile = "AppIcon";
+    CFBundleIconName = "AppIcon";
+  });
+
+  versionPlist = pkgs.writeText "version.plist" (pkgs.lib.generators.toPlist { } {
+    ProjectName = "OSXPlatformSupport";
+  });
+in
+{
+  # TODO(wpcarro): Support this with base.overrideAttrs or something similar.
+  nixos = { load ? [ ] }: withEmacsPath {
+    inherit load;
+    emacsBin = "${wpcarrosEmacs}/bin/emacs";
+  };
+
+  # To install GUI:
+  # $ nix-build -A users.wpcarro.emacs.osx -o /Applications/BillsEmacs.app
+  osx = pkgs.stdenv.mkDerivation {
+    pname = "bills-emacs";
+    version = "0.0.1";
+    src = ./.;
+    dontFixup = true;
+    installPhase = ''
+      runHook preInstall
+      APP="$out"
+      mkdir -p "$APP/Contents/MacOS"
+      mkdir -p "$APP/Contents/Resources"
+      cp ${infoPlist}      "$APP/Contents/Info.plist"
+      cp ${versionPlist}   "$APP/Contents/version.plist"
+      cp ${./AppIcon.icns} "$APP/Contents/Resources/AppIcon.icns"
+      echo "APPL????"  > "$APP/Contents/PkgInfo"
+      cat << EOF > "$APP/Contents/MacOS/BillsEmacs"
+      #!${pkgs.stdenvNoCC.shell}
+      export EMACSLOADPATH="${loadPath}"
+      exec ${wpcarrosEmacs}/bin/emacs \
+        --debug-init \
+        --no-init-file \
+        --no-site-file \
+        --no-site-lisp \
+        --load ${./.emacs.d/init.el}
+      EOF
+      chmod +x "$APP/Contents/MacOS/BillsEmacs"
+      runHook postInstall
+    '';
+    meta.platforms = [ "aarch64-darwin" ];
+  };
+
+  # Script that asserts my Emacs can initialize without warnings or errors.
+  check = runCommand "check-emacs" { } ''
+    # Even though Buildkite defines this, I'd still like still be able to test
+    # this locally without depending on my ability to remember to set CI=true.
+    export CI=true
+    export PATH="${emacsBinPath}:$PATH"
+    export EMACSLOADPATH="${loadPath}"
+    ${wpcarrosEmacs}/bin/emacs \
+      --no-site-file \
+      --no-site-lisp \
+      --no-init-file \
+      --script ${./ci.el} \
+      ${./.emacs.d/init.el} && \
+    touch $out
+  '';
+
+  # TODO(wpcarro): commented out because of doom-themes breakage; cl/10204
+  # meta.ci.targets = [ "check" ];
+}
diff --git a/users/wpcarro/emacs/elisp-conventions.md b/users/wpcarro/emacs/elisp-conventions.md
new file mode 100644
index 0000000000..0e39c3069d
--- /dev/null
+++ b/users/wpcarro/emacs/elisp-conventions.md
@@ -0,0 +1,20 @@
+# Elisp Conventions
+
+Some of this aligns with existing style guides. Some of it does not.
+
+In general, prefer functions with fixed arities instead of variadic
+alternatives.
+
+- Namespace functions with `namespace/function-name`
+- Use `ensure`, `assert`, `refute` whenever possible.
+- When talking about encoding and decoding, let's use the words "encoding" and
+  "decoding" rather than the myriad of other variants that appear like:
+  - `marshalling` and `unmarshalling`
+  - `parse` and `deparse`, `serialize`, `stringify`
+  - `unpickle` and `pickle` (Python)
+  - `from-string` and `to-string`
+  - TODO: Add more examples of these; there should be close to a dozen.
+- Annotate assertions with `!` endings.
+- Prefer the Scheme style of `predicate?`
+- Variadic functions *should* encode this by appending * onto their
+  name. E.g. `maybe/nil?*`
diff --git a/users/wpcarro/emacs/keybindings.md b/users/wpcarro/emacs/keybindings.md
new file mode 100644
index 0000000000..96ba7c9645
--- /dev/null
+++ b/users/wpcarro/emacs/keybindings.md
@@ -0,0 +1,47 @@
+# Keybindings
+
+Since I'm using Emacs to manage most of my workflow, all of the keybindings
+should be defined herein and -- in order to scale -- order must be imposed. This
+can help avoid KBD collisions and improve my ability to remember each KBD.
+
+See `kbd.el` for the programmatic encoding of these principles.
+
+## Troubleshooting
+
+When in doubt, use Emacs's `read-key` and `read-event` to learn what signal
+you're sending Emacs.
+
+### Super-
+
+- EXWM X11 windows are not processing `s-`.
+- EXWM X11 windows are not processing `<M-ESC>`.
+
+### Super-Ctrl-
+
+I'm reserving `C-s-` for opening X11 applications.
+
+- `terminator`: `t`
+- `google-chrome`: `c`
+
+## Emacs nouns
+
+Most of my keybindings should be organized according to their function, which in
+turn should be related to the following Emacs nouns.
+
+- `workspace`: As defined by EXWM.
+- `frame`: What non-Emacs users would call a "window". Currently my workflow
+  doesn't use or rely on Emacs frames.
+- `window`: A vertical or horizontal split within an Emacs frame.
+- `buffer`: Anything storing text in memory.
+
+## Prefixes and their meanings
+
+TODO: Have a system for leader-prefixed KBDs, chords, and prefixed chords.
+
+- `s-`: Switching between named workspaces. Right now, super is too overloaded
+  and would benefit from having more deliberate keybindings.
+- `C-M-`: Window sizing
+- `M-{h,j,k,l}`: Window traversing
+- `M-{\,-}`: Window splitting
+- `M-q`: Window deletion
+- `<leader>-q`: Window deletion
diff --git a/users/wpcarro/emacs/pkgs/al/al.el b/users/wpcarro/emacs/pkgs/al/al.el
new file mode 100644
index 0000000000..4c37526c64
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/al/al.el
@@ -0,0 +1,227 @@
+;;; al.el --- Interface for working with associative lists -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; Firstly, a rant:
+;; In most cases, I find Elisp's APIs to be confusing.  There's a mixture of
+;; overloaded functions that leak the implementation details (TODO: provide an
+;; example of this.) of the abstract data type, which I find privileges those
+;; "insiders" who spend disproportionately large amounts of time in Elisp land,
+;; and other functions with little-to-no pattern about the order in which
+;; arguments should be applied.  In theory, however, most of these APIs could
+;; and should be much simpler.  This module represents a step in that direction.
+;;
+;; I'm modelling these APIs after Elixir's APIs.
+;;
+;; On my wishlist is to create protocols that will allow generic interfaces like
+;; Enum protocols, etc.  Would be nice to abstract over...
+;; - associative lists (i.e. alists)
+;; - property lists (i.e. plists)
+;; - hash tables
+;; ...with some dictionary or map-like interface.  This will probably end up
+;; being quite similar to the kv.el project but with differences at the API
+;; layer.
+;;
+;; Similar libraries:
+;; - map.el: Comes bundled with recent versions of Emacs.
+;; - asoc.el: Helpers for working with alists.  asoc.el is similar to alist.el
+;;   because it uses the "!" convention for signalling that a function mutates
+;;   the underlying data structure.
+;; - ht.el: Hash table library.
+;; - kv.el: Library for dealing with key-value collections.  Note that map.el
+;;   has a similar typeclass because it works with lists, hash-tables, or
+;;   arrays.
+;; - a.el: Clojure-inspired way of working with key-value data structures in
+;; Elisp.  Works with alists, hash-tables, and sometimes vectors.
+;;
+;; Some API design principles:
+;; - The "noun" (i.e. alist) of the "verb" (i.e. function) comes last to improve
+;; composability with the threading macro (i.e. `->>') and to improve consumers'
+;; intuition with the APIs.  Learn this once, know it always.
+;;
+;; - Every function avoids mutating the alist unless it ends with !.
+;;
+;; - CRUD operations will be named according to the following table:
+;;   - "create" *and* "set"
+;;   - "read"   *and* "get"
+;;   - "update"
+;;   - "delete" *and* "remove"
+;;
+;; For better or worse, all of this code expects alists in the form of:
+;; ((first-name . "William") (last-name . "Carroll"))
+;;
+;; Special thanks to github.com/alphapapa/emacs-package-dev-handbook for some of
+;; the idiomatic ways to update alists.
+;;
+;; TODO: Include a section that compares alist.el to a.el from
+;; github.com/plexus/a.el.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies:
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'dash)
+(require 'list)
+(require 'map)
+
+;; TODO: Support function aliases for:
+;; - create/set
+;; - read/get
+;; - update
+;; - delete/remove
+
+;; Support mutative variants of functions with an ! appendage to their name.
+
+;; Ensure that the same message about only updating the first occurrence of a
+;; key is consistent throughout documentation using string interpolation or some
+;; other mechanism.
+
+;; TODO: Consider wrapping all of this with `(cl-defstruct alist xs)'.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support a variadic version of this to easily construct alists.
+(defun al-new ()
+  "Return a new, empty alist."
+  '())
+
+;; Create
+;; TODO: See if this mutates.
+(defun al-set (k v xs)
+  "Set K to V in XS."
+  (if (al-has-key? k xs)
+      (progn
+        ;; Note: this is intentional `alist-get' and not `al-get'.
+        (setf (alist-get k xs) v)
+        xs)
+    (list-cons `(,k . ,v) xs)))
+
+(defun al-set! (k v xs)
+  "Set K to V in XS mutatively.
+Note that this doesn't append to the alist in the way that most alists handle
+  writing.  If the k already exists in XS, it is overwritten."
+  (map-delete xs k)
+  (map-put! xs k v))
+
+;; Read
+(defun al-get (k xs &optional default)
+  "Return the value at K in XS; otherwise, return nil or DEFAULT (if set).
+Returns the first occurrence of K in XS since alists support multiple entries."
+  (if (not (al-has-key? k xs))
+      default
+    (cdr (assoc k xs))))
+
+(defun al-get-entry (k xs)
+  "Return the first key-value pair at K in XS."
+  (assoc k xs))
+
+;; Update
+;; TODO: Add warning about only the first occurrence being updated in the
+;; documentation.
+(defun al-update (k f xs)
+  "Apply F to the value stored at K in XS.
+If `K' is not in `XS', this function errors.  Use `al-upsert' if you're
+interested in inserting a value when a key doesn't already exist."
+  (if (not (al-has-key? k xs))
+      (error "Refusing to update: key does not exist in alist")
+    (al-set k (funcall f (al-get k xs)) xs)))
+
+(defun al-update! (k f xs)
+  "Call F on the entry at K in XS.
+Mutative variant of `al-update'."
+  (al-set! k (funcall f (al-get k xs))xs))
+
+;; TODO: Support this.
+(defun al-upsert (k v f xs)
+  "If K exists in `XS' call `F' on the value otherwise insert `V'."
+  (if (al-has-key? k xs)
+      (al-update k f xs)
+    (al-set k v xs)))
+
+;; Delete
+;; TODO: Make sure `delete' and `remove' behave as advertised in the Elisp docs.
+(defun al-delete (k xs)
+  "Deletes the entry of K from XS.
+This only removes the first occurrence of K, since alists support multiple
+  key-value entries.  See `al-delete-all' and `al-dedupe'."
+  (remove (assoc k xs) xs))
+
+(defun al-delete! (k xs)
+  "Delete the entry of K from XS.
+Mutative variant of `al-delete'."
+  (delete (assoc k xs) xs))
+
+;; Additions to the CRUD API
+;; TODO: Implement this function.
+(defun al-dedupe-keys (xs)
+  "Remove the entries in XS where the keys are `equal'.")
+
+(defun al-dedupe-entries (xs)
+  "Remove the entries in XS where the key-value pair are `equal'."
+  (delete-dups xs))
+
+(defun al-keys (xs)
+  "Return a list of the keys in XS."
+  (mapcar 'car xs))
+
+(defun al-values (xs)
+  "Return a list of the values in XS."
+  (mapcar 'cdr xs))
+
+(defun al-has-key? (k xs)
+  "Return t if XS has a key `equal' to K."
+  (not (eq nil (assoc k xs))))
+
+(defun al-has-value? (v xs)
+  "Return t if XS has a value of V."
+  (not (eq nil (rassoc v xs))))
+
+(defun al-count (xs)
+  "Return the number of entries in XS."
+  (length xs))
+
+;; TODO: Should I support `al-find-key' and `al-find-value' variants?
+(defun al-find (p xs)
+  "Find an element in XS.
+
+Apply a predicate fn, P, to each key and value in XS and return the key of the
+first element that returns t."
+  (let ((result (list-find (lambda (x) (funcall p (car x) (cdr x))) xs)))
+    (if result
+        (car result)
+      nil)))
+
+(defun al-map-keys (f xs)
+  "Call F on the values in XS, returning a new alist."
+  (list-map (lambda (x)
+              `(,(funcall f (car x)) . ,(cdr x)))
+            xs))
+
+(defun al-map-values (f xs)
+  "Call F on the values in XS, returning a new alist."
+  (list-map (lambda (x)
+              `(,(car x) . ,(funcall f (cdr x))))
+            xs))
+
+(defun al-reduce (acc f xs)
+  "Return a new alist by calling F on k v and ACC from XS.
+F should return a tuple.  See tuple.el for more information."
+  (->> (al-keys xs)
+       (list-reduce acc
+                    (lambda (k acc)
+                      (funcall f k (al-get k xs) acc)))))
+
+(defun al-merge (a b)
+  "Return a new alist with a merge of alists, A and B.
+In this case, the last writer wins, which is B."
+  (al-reduce a #'al-set b))
+
+(provide 'al)
+;;; al.el ends here
diff --git a/users/wpcarro/emacs/pkgs/al/default.nix b/users/wpcarro/emacs/pkgs/al/default.nix
new file mode 100644
index 0000000000..d88e0757a8
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/al/default.nix
@@ -0,0 +1,28 @@
+{ pkgs, depot, ... }:
+
+let
+  al = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "al";
+        version = "1.0.0";
+        src = ./al.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+          ]) ++
+          (with depot.users.wpcarro.emacs.pkgs; [
+            list
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ al ]);
+in
+al.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/al/tests.el b/users/wpcarro/emacs/pkgs/al/tests.el
new file mode 100644
index 0000000000..04fe4dcbb5
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/al/tests.el
@@ -0,0 +1,53 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'al)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest al-has-key? ()
+  (should (al-has-key? 'fname '((fname . "William"))))
+  (should (not (al-has-key? 'lname '((fname . "William"))))))
+
+(ert-deftest al-get ()
+  (let ((xs (->> (al-new)
+                 (al-set 'fname "John")
+                 (al-set 'employed? nil))))
+    (should (string= "John" (al-get 'fname xs)))
+    (should (string= "Cleese" (al-get 'lname xs "Cleese")))
+    ;; Test that the value of nil is returned even when a default is defined,
+    ;; which could be a subtle bug in the typical Elisp pattern of supporting
+    ;; defaults with: (or foo default).
+    (should (eq nil (al-get 'employed? xs)))
+    (should (eq nil (al-get 'employed? xs "default")))))
+
+(ert-deftest al-has-value? ()
+  (should (al-has-value? "William" '((fname . "William"))))
+  (should (not (al-has-key? "John" '((fname . "William"))))))
+
+(ert-deftest al-map-keys ()
+  (should
+   (equal '((2 . one)
+            (3 . two))
+          (al-map-keys #'1+
+                       '((1 . one)
+                         (2 . two))))))
+
+(ert-deftest al-map-values ()
+  (should (equal '((one . 2)
+                   (two . 3))
+                 (al-map-values #'1+
+                                '((one . 1)
+                                  (two . 2))))))
+
+(ert-deftest al-delete ()
+  (let ((person (->> (al-new)
+                     (al-set "fname" "John")
+                     (al-set "lname" "Cleese")
+                     (al-set "age" 82))))
+    (should (al-has-key? "age" person))
+    (should (not (al-has-key? "age" (al-delete "age" person))))))
diff --git a/users/wpcarro/emacs/pkgs/bag/bag.el b/users/wpcarro/emacs/pkgs/bag/bag.el
new file mode 100644
index 0000000000..502f567253
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bag/bag.el
@@ -0,0 +1,78 @@
+;;; bag.el --- Working with bags (aka multi-sets) -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; What is a bag?  A bag should be thought of as a frequency table.  It's a way
+;; to convert a list of something into a set that allows duplicates.  Isn't
+;; allowing duplicates the whole thing with Sets?  Kind of.  But the interface
+;; of Sets is something that bags resemble, so multi-set isn't as bad of a name
+;; as it may first seem.
+;;
+;; If you've used Python's collections.Counter, the concept of a bag should be
+;; familiar already.
+;;
+;; Interface:
+;; - add        :: x -> Bag(x) -> Bag(x)
+;; - remove     :: x -> Bag(x) -> Bag(x)
+;; - union      :: Bag(x) -> Bag(x) -> Bag(x)
+;; - difference :: Bag(x) -> Bag(x) -> Bag(x)
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'al)
+(require 'list)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct bag xs)
+
+(defun bag-new ()
+  "Create an empty bag."
+  (make-bag :xs (al-new)))
+
+(defun bag-from-list (xs)
+  "Map a list of `XS' into a bag."
+  (->> xs
+       (list-reduce (bag-new) #'bag-add)))
+
+(defun bag-add (x xs)
+  "Add X to XS."
+  (if (bag-contains? x xs)
+      (struct-update
+       bag xs (lambda (xs) (al-update x (lambda (x) (+ 1 x)) xs)) xs)
+    (struct-update bag xs (lambda (xs) (al-set x 1 xs)) xs)))
+
+(defun bag-remove (x xs)
+  "Remove X from XS.
+This is a no-op is X doesn't exist in XS."
+  (when (bag-contains? x xs)
+    (struct-update bag xs (lambda (xs) (al-delete x xs)) xs)))
+
+(defun bag-count (x xs)
+  "Return the number of occurrences of X in XS."
+  (al-get x (bag-xs xs) 0))
+
+(defun bag-total (xs)
+  "Return the total number of elements in XS."
+  (->> (bag-xs xs)
+       (al-reduce 0 (lambda (_key v acc) (+ acc v)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun bag-contains? (x xs)
+  "Return t if XS has X."
+  (al-has-key? x (bag-xs xs)))
+
+(provide 'bag)
+;;; bag.el ends here
diff --git a/users/wpcarro/emacs/pkgs/bag/default.nix b/users/wpcarro/emacs/pkgs/bag/default.nix
new file mode 100644
index 0000000000..3dedc27286
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bag/default.nix
@@ -0,0 +1,26 @@
+{ pkgs, depot, ... }:
+
+let
+  bag = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "bag";
+        version = "1.0.0";
+        src = ./bag.el;
+        packageRequires =
+          (with depot.users.wpcarro.emacs.pkgs; [
+            al
+            list
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ bag ]);
+in
+bag.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/bag/tests.el b/users/wpcarro/emacs/pkgs/bag/tests.el
new file mode 100644
index 0000000000..4970f70815
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bag/tests.el
@@ -0,0 +1,32 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'bag)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq fixture (bag-from-list '(1 1 1 2 2 3)))
+
+(ert-deftest bag-add ()
+  (should (not (bag-contains? 4 fixture)))
+  (should (bag-contains? 4 (bag-add 4 fixture))))
+
+(ert-deftest bag-remove ()
+  (should (bag-contains? 1 fixture))
+  (should (not (bag-contains? 3 (bag-remove 3 fixture)))))
+
+(ert-deftest bag-count ()
+  (should (= 3 (bag-count 1 fixture)))
+  (should (= 2 (bag-count 2 fixture)))
+  (should (= 1 (bag-count 3 fixture))))
+
+(ert-deftest bag-total ()
+  (should (= 6 (bag-total fixture))))
+
+(ert-deftest bag-contains? ()
+  (should (bag-contains? 1 fixture))
+  (should (not (bag-contains? 4 fixture))))
diff --git a/users/wpcarro/emacs/pkgs/bookmark/bookmark.el b/users/wpcarro/emacs/pkgs/bookmark/bookmark.el
new file mode 100644
index 0000000000..ab9169a078
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bookmark/bookmark.el
@@ -0,0 +1,50 @@
+;;; bookmark.el --- Saved files and directories on my filesystem -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; A more opinionated version of Emacs's builtin `jump-to-register'.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'project)
+(require 'general)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct bookmark label path kbd)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; API
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun bookmark-open (b)
+  "Open bookmark, B, as either a project directory or a regular directory."
+  (with-temp-buffer
+    (cd (bookmark-path b))
+    (call-interactively #'project-find-file)))
+
+(defun bookmark-install-kbd (b)
+  "Define two functions to explore B and assign them to keybindings."
+  (eval `(defun ,(intern (format "bookmark-visit-%s" (bookmark-label b))) ()
+           (interactive)
+           (find-file ,(bookmark-path b))))
+  (eval `(defun ,(intern (format "bookmark-browse-%s" (bookmark-label b))) ()
+           (interactive)
+           (bookmark-open ,b)))
+  (general-define-key
+   :prefix "<SPC>"
+   :states '(motion)
+   (format "J%s" (bookmark-kbd b)) `,(intern (format "bookmark-visit-%s" (bookmark-label b)))
+   (format "j%s" (bookmark-kbd b)) `,(intern (format "bookmark-browse-%s" (bookmark-label b)))))
+
+(provide 'bookmark)
+;;; bookmark.el ends here
diff --git a/users/wpcarro/emacs/pkgs/bookmark/default.nix b/users/wpcarro/emacs/pkgs/bookmark/default.nix
new file mode 100644
index 0000000000..882481701f
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bookmark/default.nix
@@ -0,0 +1,13 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "bookmark";
+    version = "1.0.0";
+    src = ./bookmark.el;
+    packageRequires = (with pkgs.emacsPackages; [
+      general
+    ]);
+  })
+{ }
diff --git a/users/wpcarro/emacs/pkgs/bytes/bytes.el b/users/wpcarro/emacs/pkgs/bytes/bytes.el
new file mode 100644
index 0000000000..b0d64795a0
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bytes/bytes.el
@@ -0,0 +1,94 @@
+;;; bytes.el --- Working with byte values -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Functions to help with human-readable representations of byte values.
+;;
+;; Usage:
+;; See the test cases for example usage.  Or better yet, I should use a type of
+;; structured documentation that would allow me to expose a view into the test
+;; suite here.  Is this currently possible in Elisp?
+;;
+;; API:
+;; - serialize :: Integer -> String
+;;
+;; Wish list:
+;; - Rounding: e.g. (bytes (* 1024 1.7)) => "2KB"
+
+;;; Code:
+
+;; TODO: Support -ibabyte variants like Gibibyte (GiB).
+
+;; Ranges:
+;;  B: [   0,  1e3)
+;; KB: [ 1e3,  1e6)
+;; MB: [ 1e6,  1e6)
+;; GB: [ 1e9, 1e12)
+;; TB: [1e12, 1e15)
+;; PB: [1e15, 1e18)
+;;
+;; Note: I'm currently not support exabytes because that causes the integer to
+;;  overflow.  I imagine a larger integer type may exist, but for now, I'll
+;;  treat this as a YAGNI.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'tuple)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst bytes-kb (expt 2 10)
+  "Number of bytes in a kilobyte.")
+
+(defconst bytes-mb (expt 2 20)
+  "Number of bytes in a megabytes.")
+
+(defconst bytes-gb (expt 2 30)
+  "Number of bytes in a gigabyte.")
+
+(defconst bytes-tb (expt 2 40)
+  "Number of bytes in a terabyte.")
+
+(defconst bytes-pb (expt 2 50)
+  "Number of bytes in a petabyte.")
+
+(defconst bytes-eb (expt 2 60)
+  "Number of bytes in an exabyte.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun bytes-classify (x)
+  "Return unit that closest fits byte count, X."
+  (cond
+   ((and (>= x 0)        (< x bytes-kb))     'byte)
+   ((and (>= x bytes-kb) (< x bytes-mb)) 'kilobyte)
+   ((and (>= x bytes-mb) (< x bytes-gb)) 'megabyte)
+   ((and (>= x bytes-gb) (< x bytes-tb)) 'gigabyte)
+   ((and (>= x bytes-tb) (< x bytes-pb)) 'terabyte)
+   ((and (>= x bytes-pb) (< x bytes-eb)) 'petabyte)))
+
+(defun bytes-to-string (x)
+  "Convert integer X into a human-readable string."
+  (let ((base-and-unit
+         (pcase (bytes-classify x)
+           ('byte     (tuple-from        1 "B"))
+           ('kilobyte (tuple-from bytes-kb "KB"))
+           ('megabyte (tuple-from bytes-mb "MB"))
+           ('gigabyte (tuple-from bytes-gb "GB"))
+           ('terabyte (tuple-from bytes-tb "TB"))
+           ('petabyte (tuple-from bytes-pb "PB")))))
+    (format "%d%s"
+            (round x (tuple-first base-and-unit))
+            (tuple-second base-and-unit))))
+
+(provide 'bytes)
+;;; bytes.el ends here
diff --git a/users/wpcarro/emacs/pkgs/bytes/default.nix b/users/wpcarro/emacs/pkgs/bytes/default.nix
new file mode 100644
index 0000000000..4e9f52d9b9
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bytes/default.nix
@@ -0,0 +1,25 @@
+{ pkgs, depot, ... }:
+
+let
+  bytes = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "bytes";
+        version = "1.0.0";
+        src = ./bytes.el;
+        packageRequires =
+          (with depot.users.wpcarro.emacs.pkgs; [
+            tuple
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ bytes ]);
+in
+bytes.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/bytes/tests.el b/users/wpcarro/emacs/pkgs/bytes/tests.el
new file mode 100644
index 0000000000..9b71a466c7
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/bytes/tests.el
@@ -0,0 +1,18 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'bytes)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest bytes-to-string ()
+  (should (equal "1000B" (bytes-to-string 1000)))
+  (should (equal "2KB" (bytes-to-string (* 2 bytes-kb))))
+  (should (equal "17MB" (bytes-to-string (* 17 bytes-mb))))
+  (should (equal "419GB" (bytes-to-string (* 419 bytes-gb))))
+  (should (equal "999TB" (bytes-to-string (* 999 bytes-tb))))
+  (should (equal "2PB" (bytes-to-string (* 2 bytes-pb)))))
diff --git a/users/wpcarro/emacs/pkgs/cycle/README.md b/users/wpcarro/emacs/pkgs/cycle/README.md
new file mode 100644
index 0000000000..416900d253
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/cycle/README.md
@@ -0,0 +1,7 @@
+# cycle.el
+
+[![Build status](https://badge.buildkite.com/016bff4b8ae2704a3bbbb0a250784e6692007c582983b6dea7.svg?branch=refs/heads/canon)](https://buildkite.com/tvl/depot)
+
+Cycle data structure exposing mutable and (coming soon!) immutable APIs.
+
+[![asciicast](https://asciinema.org/a/FvFujpRpYjV9qCSGvk3uobOpV.svg)](https://asciinema.org/a/FvFujpRpYjV9qCSGvk3uobOpV)
diff --git a/users/wpcarro/emacs/pkgs/cycle/cycle.el b/users/wpcarro/emacs/pkgs/cycle/cycle.el
new file mode 100644
index 0000000000..2f5b252a0d
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/cycle/cycle.el
@@ -0,0 +1,194 @@
+;;; cycle.el --- Simple module for working with cycles -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Something like this may already exist, but I'm having trouble finding it, and
+;; I think writing my own is a nice exercise for learning more Elisp.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'dash)
+(require 'struct)
+(require 'cl-lib)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Wish list
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; - TODO: Provide immutable variant.
+;; - TODO: Replace mutable consumption with immutable variant.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; `current-index' tracks the current index
+;; `xs' is the original list
+(cl-defstruct cycle current-index previous-index xs)
+
+(defun cycle-from-list (xs)
+  "Create a cycle from a list of `XS'."
+  (if (= 0 (length xs))
+      (make-cycle :current-index nil
+                  :previous-index nil
+                  :xs xs)
+    (make-cycle :current-index 0
+                :previous-index nil
+                :xs xs)))
+
+(defun cycle-new (&rest xs)
+  "Create a cycle with XS as the values."
+  (cycle-from-list xs))
+
+(defun cycle-to-list (xs)
+  "Return the list representation of a cycle, XS."
+  (cycle-xs xs))
+
+(defun cycle-previous-focus (cycle)
+  "Return the previously focused entry in CYCLE."
+  (let ((i (cycle-previous-index cycle)))
+    (when i (nth i (cycle-xs cycle)))))
+
+(defun cycle-focus-previous! (xs)
+  "Jump to the item in XS that was most recently focused; return the cycle.
+This will error when previous-index is nil.  This function mutates the
+underlying struct."
+  (let ((i (cycle-previous-index xs)))
+    (if i
+        (progn (cycle-jump! i xs) (cycle-current xs))
+      (error "Cannot focus the previous element since cycle-previous-index is nil"))))
+
+(defun cycle-next! (xs)
+  "Return the next value in `XS' and update `current-index'."
+  (let* ((current-index (cycle-current-index xs))
+         (next-index (cycle--next-index-> 0 (cycle-count xs) current-index)))
+    (struct-set! cycle previous-index current-index xs)
+    (struct-set! cycle current-index next-index xs)
+    (nth next-index (cycle-xs xs))))
+
+(defun cycle-prev! (xs)
+  "Return the previous value in `XS' and update `current-index'."
+  (let* ((current-index (cycle-current-index xs))
+         (next-index (cycle--next-index<- 0 (cycle-count xs) current-index)))
+    (struct-set! cycle previous-index current-index xs)
+    (struct-set! cycle current-index next-index xs)
+    (nth next-index (cycle-xs xs))))
+
+(defun cycle-current (cycle)
+  "Return the current value in `CYCLE'."
+  (nth (cycle-current-index cycle) (cycle-xs cycle)))
+
+(defun cycle-count (cycle)
+  "Return the length of `xs' in `CYCLE'."
+  (length (cycle-xs cycle)))
+
+(defun cycle-jump! (i xs)
+  "Jump to the I index of XS."
+  (let ((current-index (cycle-current-index xs))
+        (next-index (mod i (cycle-count xs))))
+    (struct-set! cycle previous-index current-index xs)
+    (struct-set! cycle current-index next-index xs))
+  xs)
+
+(defun cycle-focus! (p cycle)
+  "Focus the element in CYCLE for which predicate, P, is t."
+  (let ((i (->> cycle
+                cycle-xs
+                (-find-index p))))
+    (if i
+        (cycle-jump! i cycle)
+      (error "No element in cycle matches predicate"))))
+
+(defun cycle-focus-item! (x xs)
+  "Focus item, X, in cycle XS.
+ITEM is the first item in XS that t for `equal'."
+  (cycle-focus! (lambda (y) (equal x y)) xs))
+
+(defun cycle-append! (x xs)
+  "Add X to the left of the focused element in XS.
+If there is no currently focused item, add X to the beginning of XS."
+  (if (cycle-empty? xs)
+      (progn
+        (struct-set! cycle xs (list x) xs)
+        (struct-set! cycle current-index 0 xs)
+        (struct-set! cycle previous-index nil xs))
+    (let ((curr-i (cycle-current-index xs))
+          (prev-i (cycle-previous-index xs)))
+      (if curr-i
+          (progn
+            (struct-set! cycle xs (-insert-at curr-i x (cycle-xs xs)) xs)
+            (when (and prev-i (>= prev-i curr-i))
+              (struct-set! cycle previous-index (1+ prev-i) xs))
+            (when curr-i (struct-set! cycle current-index (1+ curr-i) xs)))
+        (progn
+          (struct-set! cycle xs (cons x (cycle-xs xs)) xs)
+          (when prev-i (struct-set! cycle previous-index (1+ prev-i) xs))))
+      xs)))
+
+(defun cycle-remove! (x xs)
+  "Attempt to remove X from XS.
+
+X is found using `equal'.
+
+If X is the currently focused value, after it's deleted, current-index will be
+  nil.  If X is the previously value, after it's deleted, previous-index will be
+  nil."
+  (let ((curr-i (cycle-current-index xs))
+        (prev-i (cycle-previous-index xs))
+        (rm-i (-elem-index x (cycle-xs xs))))
+    (struct-set! cycle xs (-remove-at rm-i (cycle-xs xs)) xs)
+    (when prev-i
+      (when (> prev-i rm-i) (struct-set! cycle previous-index (1- prev-i) xs))
+      (when (= prev-i rm-i) (struct-set! cycle previous-index nil xs)))
+    (when curr-i
+      (when (> curr-i rm-i) (struct-set! cycle current-index (1- curr-i) xs))
+      (when (= curr-i rm-i) (struct-set! cycle current-index nil xs)))
+    xs))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun cycle-contains? (x xs)
+  "Return t if cycle, XS, has member X."
+  (not (null (-contains? (cycle-xs xs) x))))
+
+(defun cycle-empty? (xs)
+  "Return t if cycle XS has no elements."
+  (= 0 (length (cycle-xs xs))))
+
+(defun cycle-focused? (xs)
+  "Return t if cycle XS has a non-nil value for current-index."
+  (not (null (cycle-current-index xs))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helper Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun cycle--next-index<- (lo hi x)
+  "Return the next index in a cycle when moving downwards.
+- `LO' is the lower bound.
+- `HI' is the upper bound.
+- `X' is the current index."
+  (if (< (- x 1) lo)
+      (- hi 1)
+    (- x 1)))
+
+(defun cycle--next-index-> (lo hi x)
+  "Return the next index in a cycle when moving upwards.
+- `LO' is the lower bound.
+- `HI' is the upper bound.
+- `X' is the current index."
+  (if (>= (+ 1 x) hi)
+      lo
+    (+ 1 x)))
+
+(provide 'cycle)
+;;; cycle.el ends here
diff --git a/users/wpcarro/emacs/pkgs/cycle/default.nix b/users/wpcarro/emacs/pkgs/cycle/default.nix
new file mode 100644
index 0000000000..7ef3b431ad
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/cycle/default.nix
@@ -0,0 +1,36 @@
+{ pkgs, depot, ... }:
+
+let
+  cycle = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "cycle";
+        version = "1.0.0";
+        src = ./cycle.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+          ]) ++
+          (with depot.users.wpcarro.emacs.pkgs; [
+            struct
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    epkgs.dash
+    cycle
+  ]);
+in
+cycle.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+  passthru.meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+    filter = ":/users/wpcarro/emacs/pkgs/cycle";
+    remote = "git@github.com:wpcarro/cycle.el.git";
+    ref = "refs/heads/canon";
+  };
+})
diff --git a/users/wpcarro/emacs/pkgs/cycle/tests.el b/users/wpcarro/emacs/pkgs/cycle/tests.el
new file mode 100644
index 0000000000..29c0e2a0d5
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/cycle/tests.el
@@ -0,0 +1,79 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'cycle)
+(require 'dash)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq xs (cycle-new 1 2 3))
+
+(ert-deftest cycle-initializes-properly ()
+  (should (= 3 (cycle-count xs)))
+  (should (null (cycle-previous-focus xs)))
+  (should (cycle-contains? 1 xs))
+  (should (cycle-contains? 2 xs))
+  (should (cycle-contains? 3 xs)))
+
+(ert-deftest cycle-contains? ()
+  ;; Returns t or nil
+  (should (eq t (cycle-contains? 1 xs)))
+  (should (eq t (cycle-contains? 2 xs)))
+  (should (eq t (cycle-contains? 3 xs)))
+  (should (eq nil (cycle-contains? 4 xs))))
+
+(ert-deftest cycle-empty? ()
+  (should (eq t (cycle-empty? (cycle-new))))
+  (should (eq nil (cycle-empty? xs))))
+
+(ert-deftest cycle-current ()
+  (should (= 1 (cycle-current xs))))
+
+(ert-deftest cycle-next! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (should (= 2 (cycle-next! xs)))))
+
+(ert-deftest cycle-prev! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (cycle-next! xs)
+    (should (= 1 (cycle-prev! xs)))))
+
+(ert-deftest cycle-previous-focus ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (cycle-focus-item! 2 xs)
+    (cycle-next! xs)
+    (should (= 2 (cycle-previous-focus xs)))))
+
+(ert-deftest cycle-jump! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (should (= 1 (->> xs (cycle-jump! 0) cycle-current)))
+    (should (= 2 (->> xs (cycle-jump! 1) cycle-current)))
+    (should (= 3 (->> xs (cycle-jump! 2) cycle-current)))))
+
+(ert-deftest cycle-focus-previous! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (cycle-focus-item! 2 xs)
+    (cycle-next! xs)
+    (should (= 2 (cycle-previous-focus xs)))
+    (should (= 2 (cycle-focus-previous! xs)))))
+
+(ert-deftest cycle-append! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (cycle-focus-item! 2 xs)
+    (cycle-append! 4 xs)
+    (should (equal '(1 4 2 3) (cycle-xs xs)))))
+
+(ert-deftest cycle-remove! ()
+  (let ((xs (cycle-from-list '(1 2 3))))
+    (should (equal '(1 2) (cycle-xs (cycle-remove! 3 xs))))))
+
+(ert-deftest cycle-misc ()
+  (cycle-focus-item! 3 xs)
+  (cycle-focus-item! 2 xs)
+  (cycle-remove! 1 xs)
+  (should (= 2 (cycle-current xs)))
+  (should (= 3 (cycle-previous-focus xs))))
diff --git a/users/wpcarro/emacs/pkgs/fs/default.nix b/users/wpcarro/emacs/pkgs/fs/default.nix
new file mode 100644
index 0000000000..e6afd107e9
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/fs/default.nix
@@ -0,0 +1,29 @@
+{ pkgs, depot, ... }:
+
+let
+  fs = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "fs";
+        version = "1.0.0";
+        src = ./fs.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+            f
+            s
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    fs
+  ]);
+in
+fs.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/fs/fs.el b/users/wpcarro/emacs/pkgs/fs/fs.el
new file mode 100644
index 0000000000..125c1f1007
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/fs/fs.el
@@ -0,0 +1,47 @@
+;;; fs.el --- Make working with the filesystem easier -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.1"))
+
+;;; Commentary:
+;; Ergonomic alternatives for working with the filesystem.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'dash)
+(require 'f)
+(require 's)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun fs-ensure-file (path)
+  "Ensure that a file and its directories in `PATH' exist.
+Will error for inputs with a trailing slash."
+  (when (s-ends-with? "/" path)
+    (error (format "Input path has trailing slash: %s" path)))
+  (->> path
+       f-dirname
+       fs-ensure-dir)
+  (f-touch path))
+
+(defun fs-ensure-dir (path)
+  "Ensure that a directory and its ancestor directories in `PATH' exist."
+  (->> path
+       f-split
+       (apply #'f-mkdir)))
+
+(defun fs-ls (dir &optional full-path?)
+  "List the files in `DIR' one-level deep.
+Should behave similarly in spirit to the Unix command, ls.
+If `FULL-PATH?' is set, return the full-path of the files."
+  (-drop 2 (directory-files dir full-path?)))
+
+(provide 'fs)
+;;; fs.el ends here
diff --git a/users/wpcarro/emacs/pkgs/fs/tests.el b/users/wpcarro/emacs/pkgs/fs/tests.el
new file mode 100644
index 0000000000..adef11a607
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/fs/tests.el
@@ -0,0 +1,26 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'fs)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest fs-test-ensure-file ()
+  (let ((file "/tmp/file/a/b/c/file.txt"))
+    ;; Ensure this file doesn't exist first to prevent false-positives.
+    (f-delete file t)
+    (fs-ensure-file file)
+    (should (and (f-exists? file)
+                 (f-file? file)))))
+
+(ert-deftest fs-test-ensure-dir ()
+  (let ((dir "/tmp/dir/a/b/c"))
+    ;; Ensure the directory doesn't exist.
+    (f-delete dir t)
+    (fs-ensure-dir dir)
+    (should (and (f-exists? dir)
+                 (f-dir? dir)))))
diff --git a/users/wpcarro/emacs/pkgs/list/README.md b/users/wpcarro/emacs/pkgs/list/README.md
new file mode 100644
index 0000000000..7afa8494fb
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/list/README.md
@@ -0,0 +1,19 @@
+# list.el
+
+Functions for working with lists in Elisp.
+
+## Wish List
+
+Here are some additional functions that I'd like to support.
+
+-  **TODO**: delete_at/2
+-  **TODO**: flatten/1
+-  **TODO**: flatten/2
+-  **TODO**: foldl/3
+-  **TODO**: foldr/3
+-  **TODO**: insert_at/3
+-  **TODO**: pop_at/3
+-  **TODO**: replace_at/3
+-  **TODO**: starts_with?/2
+-  **TODO**: update_at/3
+-  **TODO**: zip/1
diff --git a/users/wpcarro/emacs/pkgs/list/default.nix b/users/wpcarro/emacs/pkgs/list/default.nix
new file mode 100644
index 0000000000..1be0b901eb
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/list/default.nix
@@ -0,0 +1,26 @@
+{ pkgs, depot, ... }:
+
+let
+  list = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "list";
+        version = "1.0.0";
+        src = ./list.el;
+        packageRequires =
+          (with depot.users.wpcarro.emacs.pkgs; [
+            maybe
+            set
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ list ]);
+in
+list.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/list/list.el b/users/wpcarro/emacs/pkgs/list/list.el
new file mode 100644
index 0000000000..18be5f0a71
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/list/list.el
@@ -0,0 +1,219 @@
+;;; list.el --- Functions for working with lists -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Since I prefer having the `list-' namespace, I wrote this module to wrap many
+;; of the functions that are defined in the the global namespace in Elisp.  I
+;; sometimes forget the names of these functions, so it's nice for them to be
+;; organized like this.
+;;
+;; Motivation:
+;; Here are some examples of function names where I prefer more modern
+;; alternatives:
+;; - `car': Return the first element (i.e. "head") of a linked list
+;; - `cdr': Return the tail of a linked list
+
+;; As are most APIs for standard libraries that I write, this is influenced by
+;; Elixir's standard library.
+;;
+;; Similar libraries:
+;; - dash.el: Excellent and widely adopted library for working with lists.
+;; - list-utils.el: Utility library that covers things that dash.el may not
+;;   cover.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'maybe)
+(require 'set)
+(require 'set)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun list-new ()
+  "Return a new, empty list."
+  '())
+
+(defun list-concat (&rest lists)
+  "Joins `LISTS' into on list."
+  (apply #'append lists))
+
+(defun list-duplicate (n x)
+  "Duplicates the given element, X, N times in a list."
+  (list-map (lambda (_) x) (number-sequence 1 n)))
+
+(defun list-join (joint xs)
+  "Join a list of strings, XS, with JOINT."
+  (if (list-empty? xs)
+      ""
+    (list-reduce (list-first xs)
+                 (lambda (x acc)
+                   (format "%s%s%s" acc joint x))
+                 (list-tail xs))))
+
+(defun list-length (xs)
+  "Return the number of elements in `XS'."
+  (length xs))
+
+(defun list-get (i xs)
+  "Return the value in `XS' at `I', or nil."
+  (nth i xs))
+
+(defun list-first (xs &optional default)
+  "Alias for `list-head' for `XS'."
+  (if (list-empty? xs)
+      default
+    (car xs)))
+
+(defun list-last (xs &optional default)
+  "Returns the last element in XS or DEFAULT if empty."
+  (if (list-empty? xs)
+      default
+    (nth (- (length xs) 1) xs)))
+
+(defun list-tail (xs)
+  "Return the tail of `XS'."
+  (cdr xs))
+
+(defun list-reverse (xs)
+  "Reverses `XS'."
+  (reverse xs))
+
+(defun list-cons (x xs)
+  "Add `X' to the head of `XS'."
+  (cons x xs))
+
+(defun list-delete (x xs)
+  "Deletes the given element, X, from XS.
+Returns a new list without X. If X occurs more than once, only the first
+  occurrence is removed."
+  (let ((deleted? nil))
+    (list-reject (lambda (y)
+                   (if deleted? nil
+                     (when (equal x y)
+                       (setq deleted? t) t)))
+                 xs)))
+
+(defun list-filter (p xs)
+  "Return a subset of XS where predicate P returned t."
+  (list--assert-instance xs)
+  (seq-filter p xs))
+
+(defun list-map (f xs)
+  "Call `F' on each element of `XS'."
+  (list--assert-instance xs)
+  (seq-map f xs))
+
+(defun list-reduce (acc f xs)
+  "Return over `XS' calling `F' on an element in `XS'and `ACC'."
+  (list--assert-instance xs)
+  (seq-reduce (lambda (acc x) (funcall f x acc)) xs acc))
+
+(defun list-map-indexed (f xs)
+  "Call `F' on each element of `XS' along with its index."
+  (list-reverse
+   (cdr
+    (list-reduce '(0 . nil)
+                 (lambda (x acc)
+                   (let ((i (car acc))
+                         (result (cdr acc)))
+                     `(,(+ 1 i) . ,(cons (funcall f x i) result))))
+                 xs))))
+
+(defun list-reject (p xs)
+  "Return a subset of XS where predicate of P return nil."
+  (list-filter (lambda (x) (not (funcall p x))) xs))
+
+(defun list-find (p xs)
+  "Return the first x in XS that passes P or nil."
+  (list--assert-instance xs)
+  (seq-find p xs))
+
+(defun list-dedupe-adjacent (xs)
+  "Return XS without adjacent duplicates."
+  (list-reverse
+   (list-reduce (list (list-first xs))
+                (lambda (x acc)
+                  (if (equal x (list-first acc))
+                      acc
+                    (list-cons x acc)))
+                xs)))
+
+(defun list-chunk (n xs)
+  "Chunk XS into lists of size N."
+  (if (> n (length xs))
+      (list xs)
+    (let* ((xs (list-reduce '(:curr () :result ())
+                            (lambda (x acc)
+                              (let ((curr (plist-get acc :curr))
+                                    (result (plist-get acc :result)))
+                                (if (= (- n 1) (length curr))
+                                    `(:curr () :result ,(list-cons (list-reverse (list-cons x curr)) result))
+                                  `(:curr ,(list-cons x curr) :result
+                                          ,result)))) xs))
+           (curr (plist-get xs :curr))
+           (result (plist-get xs :result)))
+      (list-reverse (if curr (list-cons curr result) result)))))
+
+(defun list-wrap (xs)
+  "Wraps XS in a list if it is not a list already."
+  (if (list-instance? xs)
+      xs
+    (list xs)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun list-instance? (xs)
+  "Return t if `XS' is a list.
+Be leery of using this with things like alists.  Many data structures in Elisp
+  are implemented using linked lists."
+  (listp xs))
+
+(defun list-empty? (xs)
+  "Return t if XS are empty."
+  (= 0 (list-length xs)))
+
+(defun list-all? (p xs)
+  "Return t if all `XS' pass the predicate, `P'."
+  (if (list-empty? xs)
+      t
+    (and (maybe-some? (funcall p (car xs)))
+         (list-all? p (cdr xs)))))
+
+(defun list-any? (p xs)
+  "Return t if any `XS' pass the predicate, `P'."
+  (if (list-empty? xs)
+      nil
+    (or (maybe-some? (funcall p (car xs)))
+        (list-any? p (cdr xs)))))
+
+(defun list-contains? (x xs)
+  "Return t if X is in XS using `equal'."
+  (list--assert-instance xs)
+  (maybe-some? (seq-contains-p xs x)))
+
+(defun list-xs-distinct-by? (f xs)
+  "Return t if all elements in XS are distinct after applying F to each."
+  (= (length xs)
+     (set-count (set-from-list (list-map f xs)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helpers
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun list--assert-instance (xs)
+  (unless (list-instance? xs)
+    (error (format "Assertion failed: argument is not a list: %s" xs))))
+
+(provide 'list)
+;;; list.el ends here
diff --git a/users/wpcarro/emacs/pkgs/list/tests.el b/users/wpcarro/emacs/pkgs/list/tests.el
new file mode 100644
index 0000000000..4b45796883
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/list/tests.el
@@ -0,0 +1,107 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'list)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq xs '(1 2 3 4 5))
+
+(ert-deftest list-length ()
+  (should (= 0 (list-length '())))
+  (should (= 5 (list-length xs))))
+
+(ert-deftest list-reduce ()
+  (should (= 16 (list-reduce 1 (lambda (x acc) (+ x acc)) xs))))
+
+(ert-deftest list-map ()
+  (should
+   (equal '(2 4 6 8 10)
+          (list-map (lambda (x) (* x 2)) xs))))
+
+(ert-deftest list-xs-distinct-by? ()
+  (should
+   (equal t (list-xs-distinct-by?
+             (lambda (x) (plist-get x :kbd))
+             '((:kbd "C-a" :name "foo")
+               (:kbd "C-b" :name "foo"))))))
+
+(ert-deftest list-dedupe-adjacent ()
+  (should (equal '(1 2 3 4 3 5)
+                 (list-dedupe-adjacent '(1 1 1 2 2 3 4 4 3 5 5)))))
+
+(ert-deftest list-contains? ()
+  ;; Assert returns t or nil
+  (should (equal t (list-contains? 1 xs)))
+  (should (equal nil (list-contains? 100 xs))))
+
+(ert-deftest list-join ()
+  (should (equal "foo-bar-baz"
+                 (list-join "-" '("foo" "bar" "baz")))))
+
+(ert-deftest list-chunk ()
+  (should (equal '((1 2 3 4 5 6))
+                 (list-chunk 7 '(1 2 3 4 5 6))))
+  (should (equal '((1) (2) (3) (4) (5) (6))
+                 (list-chunk 1 '(1 2 3 4 5 6))))
+  (should (equal '((1 2 3) (4 5 6))
+                 (list-chunk 3 '(1 2 3 4 5 6))))
+  (should (equal '((1 2) (3 4) (5 6))
+                 (list-chunk 2 '(1 2 3 4 5 6)))))
+
+(ert-deftest list-find ()
+  (should (equal 2 (list-find (lambda (x) (= 2 x)) '(1 2 3 4)))))
+
+(ert-deftest list-all? ()
+  (should (equal t (list-all? (lambda (x) (= 2 x)) nil)))
+  (should (null (list-all? (lambda (x) (= 2 x)) '(1 2 3))))
+  (should (equal t (list-all? (lambda (x) (= 2 x)) '(2 2 2 2)))))
+
+(ert-deftest list-any? ()
+  (should (null (list-any? (lambda (x) (= 2 x)) nil)))
+  (should (equal t (list-any? (lambda (x) (= 2 x)) '(1 2 3))))
+  (should (null (list-any? (lambda (x) (= 4 x)) '(1 2 3)))))
+
+(ert-deftest list-duplicate ()
+  (should (equal '() (list-duplicate 0 "hello")))
+  (should (equal '("hi") (list-duplicate 1 "hi")))
+  (should (equal '("bye" "bye") (list-duplicate 2 "bye")))
+  (should (equal '((1 2) (1 2) (1 2)) (list-duplicate 3 '(1 2)))))
+
+(ert-deftest list-first ()
+  (should (null (list-first '())))
+  (should (equal 1 (list-first '() 1)))
+  (should (equal 1 (list-first '(1))))
+  (should (equal 1 (list-first '(1) 2)))
+  (should (equal 1 (list-first '(1 2 3)))))
+
+(ert-deftest list-last ()
+  (should (null (list-last '())))
+  (should (equal 1 (list-last '() 1)))
+  (should (equal 1 (list-last '(1))))
+  (should (equal 1 (list-last '(1) 2)))
+  (should (equal 3 (list-last '(1 2 3)))))
+
+(ert-deftest list-wrap ()
+  (should (equal '("hello") (list-wrap "hello")))
+  (should (equal '(1 2 3) (list-wrap '(1 2 3))))
+  (should (equal '() (list-wrap nil))))
+
+(ert-deftest list-delete ()
+  (should (equal '(b c) (list-delete 'a '(a b c))))
+  (should (equal '(a b c) (list-delete 'd '(a b c))))
+  (should (equal '(a b c) (list-delete 'b '(a b b c))))
+  (should (equal '() (list-delete 'b '()))))
+
+(ert-deftest list-concat ()
+  (should (equal '(1 2 3 4 5) (list-concat '(1) '(2 3) '(4 5))))
+  (should (equal '(1 2 3) (list-concat '() '(1 2 3)))))
+
+;; TODO(wpcarro): Supoprt this.
+;; (ert-deftest list-zip ()
+;;   (should (equal '((1 3 5) (2 4 6)) (list-zip '(1 2) '(3 4) '(5 6))))
+;;   (should (equal '((1 3 5)) (list-zip '(1 2) '(3) '(5 6)))))
diff --git a/users/wpcarro/emacs/pkgs/macros/default.nix b/users/wpcarro/emacs/pkgs/macros/default.nix
new file mode 100644
index 0000000000..d2811ed39f
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/macros/default.nix
@@ -0,0 +1,10 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "macros";
+    version = "1.0.0";
+    src = ./macros.el;
+  })
+{ }
diff --git a/users/wpcarro/emacs/pkgs/macros/macros.el b/users/wpcarro/emacs/pkgs/macros/macros.el
new file mode 100644
index 0000000000..3642686eeb
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/macros/macros.el
@@ -0,0 +1,45 @@
+;;; macros.el --- Helpful variables for making my ELisp life more enjoyable -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; This file contains helpful variables that I use in my ELisp development.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro macros-enable (mode)
+  "Helper for enabling `MODE'.
+Useful in `add-hook' calls.  Some modes, like `linum-mode' need to be called as
+`(linum-mode 1)', so `(add-hook mode #'linum-mode)' won't work."
+  `#'(lambda nil (,mode 1)))
+
+(defmacro macros-disable (mode)
+  "Helper for disabling `MODE'.
+Useful in `add-hook' calls."
+  `#'(lambda nil (,mode -1)))
+
+(defmacro macros-add-hook-before-save (mode f)
+  "Register a hook, `F', for a mode, `MODE' more conveniently.
+Usage: (macros-add-hook-before-save 'reason-mode-hook #'refmt-before-save)"
+  `(add-hook ,mode
+             (lambda ()
+               (add-hook 'before-save-hook ,f))))
+
+(defmacro macros-comment (&rest _)
+  "Empty comment s-expresion where `BODY' is ignored."
+  `nil)
+
+(defmacro macros-support-file-extension (ext mode)
+  "Register MODE to automatically load with files ending with EXT extension.
+Usage: (macros-support-file-extension \"pb\" protobuf-mode)"
+  (let ((extension (format "\\.%s\\'" ext)))
+    `(add-to-list 'auto-mode-alist '(,extension . ,mode))))
+
+(provide 'macros)
+;;; macros.el ends here
diff --git a/users/wpcarro/emacs/pkgs/math/default.nix b/users/wpcarro/emacs/pkgs/math/default.nix
new file mode 100644
index 0000000000..9167d61d4e
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/math/default.nix
@@ -0,0 +1,30 @@
+{ pkgs, depot, ... }:
+
+let
+  math = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "math";
+        version = "1.0.0";
+        src = ./math.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+          ]) ++
+          (with depot.users.wpcarro.emacs.pkgs; [
+            maybe
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    math
+  ]);
+in
+math.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/math/math.el b/users/wpcarro/emacs/pkgs/math/math.el
new file mode 100644
index 0000000000..dbc527928a
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/math/math.el
@@ -0,0 +1,63 @@
+;;; math.el --- Math stuffs -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Containing some useful mathematical functions.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'dash)
+(require 'maybe)
+(require 'cl-lib)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Constants
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defconst math-pi pi
+  "The number pi.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; TODO: Support all three arguments.
+;; Int -> Int -> Int -> Boolean
+(cl-defun math-triangle-of-power (&key base power result)
+  (cond
+   ((-all? #'maybe-some? (list base power result))
+    (error "All three arguments should not be set"))
+   ((-all? #'maybe-some? (list power result))
+    (message "power and result"))
+   ((-all? #'maybe-some? (list base result))
+    (log result base))
+   ((-all? #'maybe-some? (list base power))
+    (expt base power))
+   (t
+    (error "Two of the three arguments must be set"))))
+
+(defun math-mod (x y)
+  "Return X mod Y."
+  (mod x y))
+
+(defun math-exp (x y)
+  "Return X raised to the Y."
+  (expt x y))
+
+(defun math-round (x)
+  "Round X to nearest ones digit."
+  (round x))
+
+(defun math-floor (x)
+  "Floor value X."
+  (floor x))
+
+(provide 'math)
+;;; math.el ends here
diff --git a/users/wpcarro/emacs/pkgs/math/tests.el b/users/wpcarro/emacs/pkgs/math/tests.el
new file mode 100644
index 0000000000..ef3430c913
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/math/tests.el
@@ -0,0 +1,25 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'math)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest math-mod ()
+  (should (= 0 (math-mod 9 3)))
+  (should (= 4 (math-mod 9 5))))
+
+(ert-deftest math-exp ()
+  (should (= 9 (math-exp 3 2)))
+  (should (= 8 (math-exp 2 3))))
+
+(ert-deftest math-round ()
+  (should (= 10 (math-round 9.5)))
+  (should (= 9 (math-round 9.45))))
+
+(ert-deftest math-floor ()
+  (should (= 9 (math-floor 9.5))))
diff --git a/users/wpcarro/emacs/pkgs/maybe/default.nix b/users/wpcarro/emacs/pkgs/maybe/default.nix
new file mode 100644
index 0000000000..68e058b42b
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/maybe/default.nix
@@ -0,0 +1,24 @@
+{ pkgs, depot, ... }:
+
+let
+  maybe = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "maybe";
+        version = "1.0.0";
+        src = ./maybe.el;
+        packageRequires = [ ];
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    maybe
+  ]);
+in
+maybe.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/maybe/maybe.el b/users/wpcarro/emacs/pkgs/maybe/maybe.el
new file mode 100644
index 0000000000..581568d8cc
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/maybe/maybe.el
@@ -0,0 +1,54 @@
+;;; maybe.el --- Library for dealing with nil values -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Inspired by Elm's Maybe library.
+;;
+;; For now, a Nothing value will be defined exclusively as a nil value.  I'm
+;; uninterested in supported falsiness in this module even at risk of going
+;; against the LISP grain.
+;;
+;; I'm avoiding introducing a struct to handle the creation of Just and Nothing
+;; variants of Maybe.  Perhaps this is a mistake in which case this file would
+;; be more aptly named nil.el.  I may change that.  Because of this limitation,
+;; functions in Elm's Maybe library like andThen, which is the monadic bind for
+;; the Maybe type, doesn't have a home here since we cannot compose multiple
+;; Nothing or Just values without a struct or some other construct.
+;;
+;; Possible names for the variants of a Maybe.
+;; None    | Some
+;; Nothing | Something
+;; None    | Just
+;; Nil     | Set
+;;
+;; NOTE: In Elisp, values like '() (i.e. the empty list) are aliases for nil.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun maybe-nil? (x)
+  "Return t if X is nil."
+  (null x))
+
+(defun maybe-some? (x)
+  "Return t when X is non-nil."
+  (not (maybe-nil? x)))
+
+(defun maybe-default (default x)
+  "Return DEFAULT when X is nil."
+  (if (maybe-nil? x) default x))
+
+(defun maybe-map (f x)
+  "Apply F to X if X is not nil."
+  (if (maybe-some? x)
+      (funcall f x)
+    x))
+
+(provide 'maybe)
+;;; maybe.el ends here
diff --git a/users/wpcarro/emacs/pkgs/maybe/tests.el b/users/wpcarro/emacs/pkgs/maybe/tests.el
new file mode 100644
index 0000000000..c0463cc65a
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/maybe/tests.el
@@ -0,0 +1,25 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'maybe)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest maybe-nil? ()
+  (should (maybe-nil? nil))
+  (should (not (maybe-nil? t))))
+
+(ert-deftest maybe-some? ()
+  (should (maybe-some? '(1 2 3)))
+  (should (not (maybe-some? nil))))
+
+(ert-deftest maybe-default ()
+  (should (string= "some" (maybe-default "some" nil)))
+  (should (= 10 (maybe-default 1 10))))
+
+(ert-deftest maybe-map ()
+  (should (eq nil (maybe-map (lambda (x) (* x 2)) nil)))
+  (should (= 4 (maybe-map (lambda (x) (* x 2)) 2))))
diff --git a/users/wpcarro/emacs/pkgs/passage/README.md b/users/wpcarro/emacs/pkgs/passage/README.md
new file mode 100644
index 0000000000..51f7bd6efd
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/passage/README.md
@@ -0,0 +1,12 @@
+# passage.el
+
+Emacs support for `passage`.
+
+## Alternative Packages
+
+If you're looking for more feature-complete, configurable alternatives,
+check-out the following packages:
+
+- `ivy-pass.el`
+- `password-store.el`
+- `pass.el`
diff --git a/users/wpcarro/emacs/pkgs/passage/default.nix b/users/wpcarro/emacs/pkgs/passage/default.nix
new file mode 100644
index 0000000000..ac87f193b4
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/passage/default.nix
@@ -0,0 +1,12 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "passage";
+    version = "1.0.0";
+    src = ./passage.el;
+    packageRequires = (with emacsPackages; [ dash f s ]);
+  }
+  )
+{ }
diff --git a/users/wpcarro/emacs/pkgs/passage/passage.el b/users/wpcarro/emacs/pkgs/passage/passage.el
new file mode 100644
index 0000000000..4a43920e0b
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/passage/passage.el
@@ -0,0 +1,65 @@
+;;; passage.el --- Emacs passage support -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022-2023 William Carroll <wpcarro@gmail.com>
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 1.0.0
+
+;; This file is not part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package provides functions for working with passage.
+
+;;; Code:
+
+(require 'dash)
+(require 'f)
+(require 's)
+
+(defgroup passage nil
+  "Customization options for `passage'."
+  :prefix "passage-"
+  :group 'vterm)
+
+(defcustom passage-store
+  "~/.passage/store"
+  "Path to the passage store directory."
+  :type 'string
+  :group 'passage)
+
+(defcustom passage-executable
+  (or (executable-find "passage")
+      "/nix/store/jgffkfdiiwiqa4zqpxn3691mx9xc6axa-passage-unstable-2022-05-01/bin/passage")
+  "Path to passage executable."
+  :type 'string
+  :group 'passage)
+
+(defun passage-select ()
+  "Select an entry and copy its password to the kill ring."
+  (interactive)
+  (let ((key (completing-read "Copy password of entry: "
+                              (-map (lambda (x)
+                                      (f-no-ext (f-relative x passage-store)))
+                                    (f-files passage-store nil t)))))
+    (kill-new
+     (s-trim-right
+      (shell-command-to-string
+       (format "%s show %s | head -1" passage-executable key))))
+    (message "[passage.el] Copied \"%s\"!" key)))
+
+(provide 'passage)
+;;; passage.el ends here
diff --git a/users/wpcarro/emacs/pkgs/set/default.nix b/users/wpcarro/emacs/pkgs/set/default.nix
new file mode 100644
index 0000000000..319ba92744
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/set/default.nix
@@ -0,0 +1,32 @@
+{ pkgs, depot, ... }:
+
+let
+  set = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "set";
+        version = "1.0.0";
+        src = ./set.el;
+        packageRequires =
+          (with emacsPackages; [
+            dash
+            ht
+          ]) ++
+          (with depot.users.wpcarro.emacs.pkgs; [
+            struct
+          ]);
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    epkgs.dash
+    set
+  ]);
+in
+set.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/set/set.el b/users/wpcarro/emacs/pkgs/set/set.el
new file mode 100644
index 0000000000..2d6e14917a
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/set/set.el
@@ -0,0 +1,116 @@
+;;; set.el --- Working with mathematical sets -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; The set data structure is a collection that deduplicates its elements.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'cl-lib)
+(require 'dash)
+(require 'ht) ;; friendlier API for hash-tables
+(require 'struct)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Wish List
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; - TODO: Support enum protocol for set.
+;; - TODO: Prefer a different hash-table library that doesn't rely on mutative
+;;   code.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct set xs)
+
+(defun set-from-list (xs)
+  "Create a new set from the list XS."
+  (make-set :xs (->> xs
+                     (-map (lambda (x) (cons x nil)))
+                     ht-from-alist)))
+
+(defun set-new (&rest args)
+  "Create a new set from ARGS."
+  (set-from-list args))
+
+(defun set-to-list (xs)
+  "Map set XS into a list."
+  (->> xs
+       set-xs
+       ht-keys))
+
+(defun set-add (x xs)
+  "Add X to set XS."
+  (struct-update set
+                 xs
+                 (lambda (table)
+                   (let ((table-copy (ht-copy table)))
+                     (ht-set table-copy x nil)
+                     table-copy))
+                 xs))
+
+;; TODO: Ensure all `*/reduce' functions share the same API.
+(defun set-reduce (acc f xs)
+  "Return a new set by calling F on each element of XS and ACC."
+  (->> xs
+       set-to-list
+       (-reduce-from (lambda (acc x) (funcall f x acc)) acc)))
+
+(defun set-intersection (a b)
+  "Return the set intersection between A and B."
+  (set-reduce (set-new)
+              (lambda (x acc)
+                (if (set-contains? x b)
+                    (set-add x acc)
+                  acc))
+              a))
+
+(defun set-count (xs)
+  "Return the number of elements in XS."
+  (->> xs
+       set-xs
+       ht-size))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun set-empty? (xs)
+  "Return t if XS has no elements in it."
+  (= 0 (set-count xs)))
+
+(defun set-contains? (x xs)
+  "Return t if set XS has X."
+  (ht-contains? (set-xs xs) x))
+
+;; TODO: Prefer using `ht.el' functions for this.
+(defun set-equal? (a b)
+  "Return t if A and B share the name members."
+  (ht-equal? (set-xs a)
+             (set-xs b)))
+
+(defun set-distinct? (a b)
+  "Return t if A and B have no shared members."
+  (set-empty? (set-intersection a b)))
+
+(defun set-superset? (a b)
+  "Return t if A has all of the members of B."
+  (->> b
+       set-to-list
+       (-all? (lambda (x) (set-contains? x a)))))
+
+(defun set-subset? (a b)
+  "Return t if each member of set A is present in set B."
+  (set-superset? b a))
+
+(provide 'set)
+;;; set.el ends here
diff --git a/users/wpcarro/emacs/pkgs/set/tests.el b/users/wpcarro/emacs/pkgs/set/tests.el
new file mode 100644
index 0000000000..7f5c2ae3ff
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/set/tests.el
@@ -0,0 +1,69 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'dash)
+(require 'set)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest set-from-list ()
+  (should (equal '(1 2 3)
+                 (->> '(1 2 3 1 2 3)
+                      set-from-list
+                      set-to-list))))
+
+(ert-deftest set-distinct? ()
+  (should (set-distinct? (set-new 'one 'two 'three)
+                         (set-new 'a 'b 'c)))
+  (should (not
+           (set-distinct? (set-new 1 2 3)
+                          (set-new 3 4 5))))
+  (should (not
+           (set-distinct? (set-new 1 2 3)
+                          (set-new 1 2 3)))))
+
+(ert-deftest set-equal? ()
+  (should (not (set-equal? (set-new 'a 'b 'c)
+                           (set-new 'x 'y 'z))))
+  (should (not (set-equal? (set-new 'a 'b 'c)
+                           (set-new 'a 'b))))
+  (should (set-equal? (set-new 'a 'b 'c)
+                      (set-new 'a 'b 'c))))
+
+(ert-deftest set-intersection ()
+  (should (set-equal? (set-new 2 3)
+                      (set-intersection (set-new 1 2 3)
+                                        (set-new 2 3 4)))))
+
+(ert-deftest set-to/from-list ()
+  (should (equal '(1 2 3)
+                 (->> '(1 1 2 2 3 3)
+                      set-from-list
+                      set-to-list))))
+
+(ert-deftest set-subset? ()
+  (should (not (set-subset? (set-new "black" "grey")
+                            (set-new "red" "green" "blue"))))
+  (should (set-subset? (set-new "red")
+                       (set-new "red" "green" "blue"))))
+
+(ert-deftest set-superset? ()
+  (let ((primary-colors (set-new "red" "green" "blue")))
+    (should (not (set-superset? primary-colors
+                                (set-new "black" "grey"))))
+    (should (set-superset? primary-colors
+                           (set-new "red" "green" "blue")))
+    (should (set-superset? primary-colors
+                           (set-new "red" "blue")))))
+
+(ert-deftest set-empty? ()
+  (should (set-empty? (set-new)))
+  (should (not (set-empty? (set-new 1 2 3)))))
+
+(ert-deftest set-count ()
+  (should (= 0 (set-count (set-new))))
+  (should (= 2 (set-count (set-new 1 1 2 2)))))
diff --git a/users/wpcarro/emacs/pkgs/string/default.nix b/users/wpcarro/emacs/pkgs/string/default.nix
new file mode 100644
index 0000000000..406cccdfcb
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/string/default.nix
@@ -0,0 +1,27 @@
+{ pkgs, depot, ... }:
+
+let
+  string = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "string";
+        version = "1.0.0";
+        src = ./string.el;
+        packageRequires = [
+          emacsPackages.dash
+          emacsPackages.s
+        ];
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    string
+  ]);
+in
+string.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/string/string.el b/users/wpcarro/emacs/pkgs/string/string.el
new file mode 100644
index 0000000000..30da1805e8
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/string/string.el
@@ -0,0 +1,98 @@
+;;; string.el --- Library for working with strings -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Library for working with strings.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 's)
+(require 'dash)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun string-split (y x)
+  "Map string X into a list of strings that were separated by Y."
+  (s-split y x))
+
+(defun string-format (x &rest args)
+  "Format template string X with ARGS."
+  (apply #'format (cons x args)))
+
+(defun string-concat (&rest strings)
+  "Joins `STRINGS' into onto string."
+  (apply #'s-concat strings))
+
+(defun string-to-symbol (string)
+  "Maps `STRING' to a symbol."
+  (intern string))
+
+(defun string-from-symbol (symbol)
+  "Maps `SYMBOL' into a string."
+  (symbol-name symbol))
+
+(defun string-prepend (prefix x)
+  "Prepend `PREFIX' onto `X'."
+  (s-concat prefix x))
+
+(defun string-append (postfix x)
+  "Appen `POSTFIX' onto `X'."
+  (s-concat x postfix))
+
+(defun string-surround (s x)
+  "Surrounds `X' one each side with `S'."
+  (->> x
+       (string-prepend s)
+       (string-append s)))
+
+;; TODO: Define a macro for defining a function and a test.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Casing
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun string-caps->kebab (x)
+  "Change the casing of `X' from CAP_CASE to kebab-case."
+  (->> x
+       s-downcase
+       (s-replace "_" "-")))
+
+(defun string-kebab->caps (x)
+  "Change the casing of X from CAP_CASE to kebab-case."
+  (->> x
+       s-upcase
+       (s-replace "-" "_")))
+
+(defun string-lower->caps (x)
+  "Change the casing of X from lowercase to CAPS_CASE."
+  (->> x
+       s-upcase
+       (s-replace " " "_")))
+
+(defun string-lower->kebab (x)
+  "Change the casing of `X' from lowercase to kebab-case."
+  (s-replace " " "-" x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun string-instance? (x)
+  "Return t if X is a string."
+  (stringp x))
+
+(defun string-contains? (c x)
+  "Return t if X is in C."
+  (s-contains? c x))
+
+(provide 'string)
+;;; string.el ends here
diff --git a/users/wpcarro/emacs/pkgs/string/tests.el b/users/wpcarro/emacs/pkgs/string/tests.el
new file mode 100644
index 0000000000..351e305466
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/string/tests.el
@@ -0,0 +1,22 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'string)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest string-caps->kebab ()
+  (should (string= "foo-bar-baz" (string-caps->kebab "FOO_BAR_BAZ"))))
+
+(ert-deftest string-kebab->caps ()
+  (should (string= "FOO_BAR_BAZ" (string-kebab->caps "foo-bar-baz"))))
+
+(ert-deftest string-lower->caps ()
+  (should (string= "FOO_BAR_BAZ" (string-lower->caps "foo bar baz"))))
+
+(ert-deftest string-lower->kebab ()
+  (should (string= "foo-bar-baz" (string-lower->kebab "foo bar baz"))))
diff --git a/users/wpcarro/emacs/pkgs/struct/README.md b/users/wpcarro/emacs/pkgs/struct/README.md
new file mode 100644
index 0000000000..34dac6614c
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/struct/README.md
@@ -0,0 +1,6 @@
+# struct.el
+
+[![Build status](https://badge.buildkite.com/016bff4b8ae2704a3bbbb0a250784e6692007c582983b6dea7.svg?branch=refs/heads/canon)](https://buildkite.com/tvl/depot)
+
+Provides new macros exposing immutable and mutable interfaces for working with
+structs in Elisp.
diff --git a/users/wpcarro/emacs/pkgs/struct/default.nix b/users/wpcarro/emacs/pkgs/struct/default.nix
new file mode 100644
index 0000000000..558ebd0a3d
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/struct/default.nix
@@ -0,0 +1,29 @@
+{ pkgs, depot, ... }:
+
+let
+  struct = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "struct";
+        version = "1.0.0";
+        src = ./struct.el;
+        packageRequires = [ ];
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    struct
+  ]);
+in
+struct.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+  passthru.meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+    filter = ":/users/wpcarro/emacs/pkgs/struct";
+    remote = "git@github.com:wpcarro/struct.el.git";
+    ref = "refs/heads/canon";
+  };
+})
diff --git a/users/wpcarro/emacs/pkgs/struct/struct.el b/users/wpcarro/emacs/pkgs/struct/struct.el
new file mode 100644
index 0000000000..5d6572bf6d
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/struct/struct.el
@@ -0,0 +1,65 @@
+;;; struct.el --- Helpers for working with structs -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 1.0.0
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;; Provides new macros for working with structs.  Also provides adapter
+;; interfaces to existing struct macros, that should have more intuitive
+;; interfaces.
+;;
+;; Sometimes `setf' just isn't enough.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro struct-update (type field f xs)
+  "Apply F to FIELD in XS, which is a struct of TYPE.
+This is immutable."
+  (let ((copier (struct--copier-for type))
+        (accessor (struct--accessor-for type field)))
+    `(let ((copy (,copier ,xs)))
+       (setf (,accessor copy) (funcall ,f (,accessor copy)))
+       copy)))
+
+(defmacro struct-update! (type field f xs)
+  "Mutably apply F to FIELD in XS."
+  (let ((accessor (struct--accessor-for type field)))
+    `(progn
+       (setf (,accessor ,xs) (funcall ,f (,accessor ,xs)))
+       ,xs)))
+
+(defmacro struct-set (type field x xs)
+  "Immutably set FIELD in XS (struct TYPE) to X."
+  (let ((copier (struct--copier-for type))
+        (accessor (struct--accessor-for type field)))
+    `(let ((copy (,copier ,xs)))
+       (setf (,accessor copy) ,x)
+       copy)))
+
+(defmacro struct-set! (type field x xs)
+  "Set FIELD in XS (struct TYPE) to X mutably.
+This is an adapter interface to `setf'."
+  (let ((accessor (struct--accessor-for type field)))
+    `(progn
+       (setf (,accessor ,xs) ,x)
+       ,xs)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Helper Functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun struct--copier-for (type)
+  (intern (format "copy-%s" (symbol-name type))))
+
+(defun struct--accessor-for (type field)
+  (intern (format "%s-%s"
+                  (symbol-name type)
+                  (symbol-name field))))
+
+(provide 'struct)
+;;; struct.el ends here
diff --git a/users/wpcarro/emacs/pkgs/struct/tests.el b/users/wpcarro/emacs/pkgs/struct/tests.el
new file mode 100644
index 0000000000..a7ddb52c46
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/struct/tests.el
@@ -0,0 +1,44 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'struct)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct dummy name age)
+
+(ert-deftest struct-update ()
+  (let* ((test (make-dummy :name "Roofus" :age 19))
+         (result (struct-update dummy name #'upcase test)))
+    ;; test
+    (should (string= "Roofus" (dummy-name test)))
+    (should (= 19 (dummy-age test)))
+    ;; result
+    (should (string= "ROOFUS" (dummy-name result)))
+    (should (= 19 (dummy-age result)))))
+
+(ert-deftest struct-update! ()
+  (let ((test (make-dummy :name "Roofus" :age 19)))
+    (struct-update! dummy name #'upcase test)
+    (should (string= "ROOFUS" (dummy-name test)))
+    (should (= 19 (dummy-age test)))))
+
+(ert-deftest struct-set ()
+  (let* ((test (make-dummy :name "Roofus" :age 19))
+         (result (struct-set dummy name "Shoofus" test)))
+    ;; test
+    (should (string= "Roofus" (dummy-name test)))
+    (should (= 19 (dummy-age test)))
+    ;; result
+    (should (string= "Shoofus" (dummy-name result)))
+    (should (= 19 (dummy-age result)))))
+
+(ert-deftest struct-set! ()
+  (let ((test (make-dummy :name "Roofus" :age 19)))
+    (struct-set! dummy name "Doofus" test)
+    (should (string= "Doofus" (dummy-name test)))
+    (should (= 19 (dummy-age test)))))
diff --git a/users/wpcarro/emacs/pkgs/symbol/default.nix b/users/wpcarro/emacs/pkgs/symbol/default.nix
new file mode 100644
index 0000000000..9334697e32
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/symbol/default.nix
@@ -0,0 +1,24 @@
+{ pkgs, depot, ... }:
+
+let
+  symbol = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "symbol";
+        version = "1.0.0";
+        src = ./symbol.el;
+        packageRequires = [ ];
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [
+    symbol
+  ]);
+in
+symbol.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/symbol/symbol.el b/users/wpcarro/emacs/pkgs/symbol/symbol.el
new file mode 100644
index 0000000000..4b16351831
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/symbol/symbol.el
@@ -0,0 +1,38 @@
+;;; symbol.el --- Library for working with symbols -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; Library for working with symbols.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun symbol-to-string (symbol)
+  "Map `SYMBOL' into a string."
+  (symbol-name symbol))
+
+(defun symbol-from-string (string)
+  "Map `STRING' into a symbol."
+  (intern string))
+
+(defun symbol-as-string (f x)
+  "Treat the symbol, X, as a string while applying F to it.
+Coerce back to a symbol on the way out."
+  (symbol-from-string (funcall f (symbol-to-string x))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun symbol-instance? (x)
+  "Return t if X is a symbol."
+  (symbolp x))
+
+(provide 'symbol)
+;;; symbol.el ends here
diff --git a/users/wpcarro/emacs/pkgs/symbol/tests.el b/users/wpcarro/emacs/pkgs/symbol/tests.el
new file mode 100644
index 0000000000..b10362b162
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/symbol/tests.el
@@ -0,0 +1,22 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'symbol)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest symbol-to-string ()
+  (should (string= "foo" (symbol-to-string 'foo))))
+
+(ert-deftest symbol-from-string ()
+  (should (eq 'foo (symbol-from-string "foo"))))
+
+(ert-deftest symbol-as-string ()
+  (should (eq 'foo-hook
+              (symbol-as-string
+               (lambda (x) (format "%s-hook" x))
+               'foo))))
diff --git a/users/wpcarro/emacs/pkgs/theme/default.nix b/users/wpcarro/emacs/pkgs/theme/default.nix
new file mode 100644
index 0000000000..aea6394369
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/theme/default.nix
@@ -0,0 +1,14 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "theme";
+    version = "1.0.0";
+    src = ./theme.el;
+    packageRequires =
+      (with depot.users.wpcarro.emacs.pkgs; [
+        cycle
+      ]);
+  })
+{ }
diff --git a/users/wpcarro/emacs/pkgs/theme/theme.el b/users/wpcarro/emacs/pkgs/theme/theme.el
new file mode 100644
index 0000000000..32f2c89a4d
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/theme/theme.el
@@ -0,0 +1,78 @@
+;;; theme.el --- Colors and stuff -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24.3"))
+
+;;; Commentary:
+;;
+;; Cycle through a whitelist of themes.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'cycle)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defgroup theme nil
+  "Customization options for `theme'."
+  :group 'theme)
+
+(defcustom theme-whitelist
+  (cycle-from-list (custom-available-themes))
+  "The whitelist of themes through which to cycle."
+  :type '(cycle symbol)
+  :group 'theme)
+
+(defcustom theme-after-change
+  nil
+  "Hook invoked after a new theme is loaded"
+  :type 'hook
+  :group 'theme)
+
+(defun theme-whitelist-set (theme)
+  "Focus the THEME in the `theme-whitelist' cycle."
+  (cycle-focus! (lambda (x) (equal x theme)) theme-whitelist)
+  (theme--set (cycle-current theme-whitelist)))
+
+(defun theme-select ()
+  "Load a theme using `completing-read'."
+  (interactive)
+  (let ((theme (completing-read "Theme: " (cycle-to-list theme-whitelist))))
+    (theme--disable-all)
+    (theme--set (intern theme))))
+
+(defun theme-next ()
+  "Disable the currently active theme and load the next theme."
+  (interactive)
+  (disable-theme (cycle-current theme-whitelist))
+  (theme--set (cycle-next! theme-whitelist))
+  (message (format "Active theme: %s" (cycle-current theme-whitelist))))
+
+(defun theme-prev ()
+  "Disable the currently active theme and load the previous theme."
+  (interactive)
+  (disable-theme (cycle-current theme-whitelist))
+  (theme--set (cycle-prev! theme-whitelist))
+  (message (format "Active theme: %s" (cycle-current theme-whitelist))))
+
+(defun theme--disable-all ()
+  "Disable all currently enabled themes."
+  (interactive)
+  (dolist (x custom-enabled-themes)
+    (disable-theme x)))
+
+(defun theme--set (theme)
+    "Call `load-theme' with `THEME', ensuring that the line numbers are bright.
+There is no hook that I'm aware of to handle this more elegantly."
+    (load-theme theme t)
+    (run-hooks 'theme-after-change))
+
+(provide 'theme)
+;;; theme.el ends here
diff --git a/users/wpcarro/emacs/pkgs/tuple/default.nix b/users/wpcarro/emacs/pkgs/tuple/default.nix
new file mode 100644
index 0000000000..0626370e47
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/tuple/default.nix
@@ -0,0 +1,10 @@
+{ pkgs, depot, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "tuple";
+    version = "1.0.0";
+    src = ./tuple.el;
+  })
+{ }
diff --git a/users/wpcarro/emacs/pkgs/tuple/tuple.el b/users/wpcarro/emacs/pkgs/tuple/tuple.el
new file mode 100644
index 0000000000..848c6fa48b
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/tuple/tuple.el
@@ -0,0 +1,93 @@
+;;; tuple.el --- Tuple API for Elisp -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; Work with cons cells with two elements with a familiar API for those who have
+;; worked with tuples before.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(cl-defstruct tuple first second)
+
+;; Create
+(defun tuple-new ()
+  "Return an empty tuple."
+  (make-tuple :first nil
+              :second nil))
+
+(defun tuple-from (a b)
+  "Return a new tuple from A and B."
+  (make-tuple :first a
+              :second b))
+
+(defun tuple-from-dotted (dp)
+  "Convert dotted pair, DP, into a tuple."
+  (tuple-from (car dp) (cdr dp)))
+
+;; Read
+(defun tuple-first (pair)
+  "Return the first element of PAIR."
+  (tuple-first pair))
+
+(defun tuple-second (pair)
+  "Return the second element of PAIR."
+  (tuple-second pair))
+
+;; Update
+(defun tuple-map-each (f g pair)
+  "Apply F to first, G to second in PAIR."
+  (->> pair
+       (tuple-map-first f)
+       (tuple-map-second g)))
+
+(defun tuple-map (f pair)
+  "Apply F to PAIR."
+  (let ((pair-copy (copy-tuple pair)))
+    (funcall f pair-copy)))
+
+(defun tuple-map-first (f pair)
+  "Apply function F to the first element of PAIR."
+  (let ((pair-copy (copy-tuple pair)))
+    (setf (tuple-first pair-copy) (funcall f (tuple-first pair-copy)))
+    pair-copy))
+
+(defun tuple-map-second (f pair)
+  "Apply function F to the second element of PAIR."
+  (let ((pair-copy (copy-tuple pair)))
+    (setf (tuple-second pair-copy) (funcall f (tuple-second pair-copy)))
+    pair-copy))
+
+(defun tuple-set-first (a pair)
+  "Return a new tuple with the first element set as A in PAIR."
+  (tuple-map-first (lambda (_) a) pair))
+
+(defun tuple-set-second (b pair)
+  "Return a new tuple with the second element set as B in PAIR."
+  (tuple-map-second (lambda (_) b) pair))
+
+;; Delete
+(defun tuple-delete-first (pair)
+  "Return PAIR with the first element set to nil."
+  (tuple-set-first nil pair))
+
+(defun tuple-delete-second (pair)
+  "Return PAIR with the second element set to nil."
+  (tuple-set-second nil pair))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Predicates
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun tuple-instance? (x)
+  "Return t if X is a tuple."
+  (tuple-p x))
+
+(provide 'tuple)
+;;; tuple.el ends here
diff --git a/users/wpcarro/emacs/pkgs/vector/default.nix b/users/wpcarro/emacs/pkgs/vector/default.nix
new file mode 100644
index 0000000000..c0a475aaaa
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vector/default.nix
@@ -0,0 +1,21 @@
+{ pkgs, depot, ... }:
+
+let
+  vector = pkgs.callPackage
+    ({ emacsPackages }:
+      emacsPackages.trivialBuild {
+        pname = "vector";
+        version = "1.0.0";
+        src = ./vector.el;
+      })
+    { };
+
+  emacs = (pkgs.emacsPackagesFor pkgs.emacs28).emacsWithPackages (epkgs: [ vector ]);
+in
+vector.overrideAttrs (_old: {
+  doCheck = true;
+  checkPhase = ''
+    ${emacs}/bin/emacs -batch \
+      -l ert -l ${./tests.el} -f ert-run-tests-batch-and-exit
+  '';
+})
diff --git a/users/wpcarro/emacs/pkgs/vector/tests.el b/users/wpcarro/emacs/pkgs/vector/tests.el
new file mode 100644
index 0000000000..ffa9831882
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vector/tests.el
@@ -0,0 +1,20 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'ert)
+(require 'vector)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest vector-misc-tests ()
+  (let ((xs [1 2 3])
+        (ys [1 2 3]))
+    (should (= 1 (vector-get 0 ys)))
+    (vector-set 0 4 ys)
+    (should (= 1 (vector-get 0 ys)))
+    (should (= 1 (vector-get 0 xs)))
+    (vector-set! 0 4 xs)
+    (should (= 4 (vector-get 0 xs)))))
diff --git a/users/wpcarro/emacs/pkgs/vector/vector.el b/users/wpcarro/emacs/pkgs/vector/vector.el
new file mode 100644
index 0000000000..87f38d7d93
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vector/vector.el
@@ -0,0 +1,58 @@
+;;; vector.el --- Working with Elisp's Vector data type -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; It might be best to think of Elisp vectors as tuples in languages like
+;; Haskell or Erlang.
+;;
+;; Not surprisingly, this API is modelled after Elixir's Tuple API.
+;;
+;; Some Elisp trivia:
+;; - "Array": Usually means vector or string.
+;; - "Sequence": Usually means list or "array" (see above).
+;;
+;; It might be a good idea to think of Array and Sequence as typeclasses in
+;; Elisp.  This is perhaps more similar to Elixir's notion of the Enum protocol.
+;;
+;; Intentionally not supporting a to-list function, because tuples can contain
+;; heterogenous types whereas lists should contain homogenous types.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Library
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun vector-concat (&rest args)
+  "Return a new vector composed of all vectors in `ARGS'."
+  (apply #'vconcat args))
+
+(defun vector-prepend (x xs)
+  "Add `X' to the beginning of `XS'."
+  (vector-concat `[,x] xs))
+
+(defun vector-append (x xs)
+  "Add `X' to the end of `XS'."
+  (vector-concat xs `[,x]))
+
+(defun vector-get (i xs)
+  "Return the value in `XS' at index, `I'."
+  (aref xs i))
+
+(defun vector-set (i v xs)
+  "Set index `I' to value `V' in `XS'.
+Returns a copy of `XS' with the updates."
+  (let ((copy (vconcat [] xs)))
+    (aset copy i v)
+    copy))
+
+(defun vector-set! (i v xs)
+  "Set index `I' to value `V' in `XS'.
+This function mutates XS."
+  (aset xs i v))
+
+(provide 'vector)
+;;; vector.el ends here
diff --git a/users/wpcarro/emacs/pkgs/vterm-mgt/README.md b/users/wpcarro/emacs/pkgs/vterm-mgt/README.md
new file mode 100644
index 0000000000..b855826929
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vterm-mgt/README.md
@@ -0,0 +1,17 @@
+# vterm-mgt.el
+
+[![Build status](https://badge.buildkite.com/016bff4b8ae2704a3bbbb0a250784e6692007c582983b6dea7.svg?branch=refs/heads/canon)](https://buildkite.com/tvl/depot)
+
+[emacs-libvterm](https://github.com/akermu/emacs-libvterm) is a feature-complete
+terminal emulator inside Emacs based on libvterm.
+
+`vterm-mgt.el`, adds functionality on top of `vterm` to allow you to:
+
+* find-or-create `vterm` instances
+* fuzzily switch between existing `vterm` buffers
+* cycle through existing `vterm` instances
+* easily rename `vterm` buffers
+
+## Alternatives to vterm-mgt.el
+
+* [multi-vterm](https://github.com/suonlight/multi-vterm)
diff --git a/users/wpcarro/emacs/pkgs/vterm-mgt/default.nix b/users/wpcarro/emacs/pkgs/vterm-mgt/default.nix
new file mode 100644
index 0000000000..88eb550204
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vterm-mgt/default.nix
@@ -0,0 +1,19 @@
+{ pkgs, depot, ... }:
+
+pkgs.emacsPackages.trivialBuild {
+  pname = "vterm-mgt";
+  version = "1.0.0";
+  src = ./vterm-mgt.el;
+  packageRequires =
+    (with pkgs.emacsPackages; [
+      vterm
+    ]) ++
+    (with depot.users.wpcarro.emacs.pkgs; [
+      cycle
+    ]);
+  passthru.meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+    filter = ":/users/wpcarro/emacs/pkgs/vterm-mgt";
+    remote = "git@github.com:wpcarro/vterm-mgt.el.git";
+    ref = "refs/heads/canon";
+  };
+}
diff --git a/users/wpcarro/emacs/pkgs/vterm-mgt/vterm-mgt.el b/users/wpcarro/emacs/pkgs/vterm-mgt/vterm-mgt.el
new file mode 100644
index 0000000000..c082e54a59
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/vterm-mgt/vterm-mgt.el
@@ -0,0 +1,140 @@
+;;; vterm-mgt.el --- Help me manage my vterm instances -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "25.1"))
+
+;;; Commentary:
+;; Supporting functions to instantiate vterm buffers, kill existing vterm
+;; buffers, rename vterm buffers, cycle forwards and backwards through vterm
+;; buffers.
+;;
+;; Many of the functions defined herein are intended to be bound to
+;; `vterm-mode-map'.  Some assertions are made to guard against calling
+;; functions that are intended to be called from outside of a vterm buffer.
+;; These assertions shouldn't error when the functions are bound to
+;; `vterm-mode-map'.  If for some reason, you'd like to bind these functions to
+;; a separate keymap, caveat emptor.
+
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'cycle)
+(require 'vterm)
+(require 'seq)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defgroup vterm-mgt nil
+  "Customization options for `vterm-mgt'."
+  :group 'vterm)
+
+(defcustom vterm-mgt-scroll-on-focus nil
+  "When t, call `end-of-buffer' after focusing a vterm instance."
+  :type '(boolean)
+  :group 'vterm-mgt)
+
+(defconst vterm-mgt--instances (cycle-new)
+  "A cycle tracking all of my vterm instances.")
+
+(defun vterm-mgt--instance? (b)
+  "Return t if the buffer B is a vterm instance."
+  (equal 'vterm-mode (buffer-local-value 'major-mode b)))
+
+(defun vterm-mgt--assert-vterm-buffer ()
+  "Error when the `current-buffer' is not a vterm buffer."
+  (unless (vterm-mgt--instance? (current-buffer))
+    (error "Current buffer is not a vterm buffer")))
+
+(defun vterm-mgt-next ()
+  "Replace the current buffer with the next item in `vterm-mgt--instances'.
+This function should be called from a buffer running vterm."
+  (interactive)
+  (vterm-mgt--assert-vterm-buffer)
+  (vterm-mgt-reconcile-state)
+  (cycle-focus-item! (current-buffer) vterm-mgt--instances)
+  (switch-to-buffer (cycle-next! vterm-mgt--instances))
+  (when vterm-mgt-scroll-on-focus (end-of-buffer)))
+
+(defun vterm-mgt-prev ()
+  "Replace the current buffer with the previous item in `vterm-mgt--instances'.
+This function should be called from a buffer running vterm."
+  (interactive)
+  (vterm-mgt--assert-vterm-buffer)
+  (vterm-mgt-reconcile-state)
+  (cycle-focus-item! (current-buffer) vterm-mgt--instances)
+  (switch-to-buffer (cycle-prev! vterm-mgt--instances))
+  (when vterm-mgt-scroll-on-focus (end-of-buffer)))
+
+(defun vterm-mgt-instantiate ()
+  "Create a new vterm instance.
+
+Prefer calling this function instead of `vterm'.  This function ensures that the
+  newly created instance is added to `vterm-mgt--instances'.
+
+If however you must call `vterm', if you'd like to cycle through vterm
+  instances, make sure you call `vterm-mgt-reconcile-state' to allow vterm-mgt
+  to collect any untracked vterm instances."
+  (interactive)
+  (vterm-mgt-reconcile-state)
+  (let ((buffer (vterm t)))
+    (cycle-append! buffer vterm-mgt--instances)
+    (cycle-focus-item! buffer vterm-mgt--instances)))
+
+(defun vterm-mgt-kill ()
+  "Kill the current buffer and remove it from `vterm-mgt--instances'.
+This function should be called from a buffer running vterm."
+  (interactive)
+  (vterm-mgt--assert-vterm-buffer)
+  (let* ((buffer (current-buffer)))
+    (when (kill-buffer buffer)
+      (vterm-mgt-reconcile-state))))
+
+(defun vterm-mgt-find-or-create ()
+  "Call `switch-to-buffer' on a focused vterm instance if there is one.
+
+When `cycle-focused?' returns nil, focus the first item in the cycle.  When
+there are no items in the cycle, call `vterm-mgt-instantiate' to create a vterm
+instance."
+  (interactive)
+  (vterm-mgt-reconcile-state)
+  (if (cycle-empty? vterm-mgt--instances)
+      (vterm-mgt-instantiate)
+    (if (cycle-focused? vterm-mgt--instances)
+        (switch-to-buffer (cycle-current vterm-mgt--instances))
+      (progn
+        (cycle-jump! 0 vterm-mgt--instances)
+        (switch-to-buffer (cycle-current vterm-mgt--instances))))))
+
+(defun vterm-mgt-rename-buffer (name)
+  "Rename the current buffer ensuring that its NAME is wrapped in *vterm<...>*.
+This function should be called from a buffer running vterm."
+  (interactive "SRename vterm buffer: ")
+  (vterm-mgt--assert-vterm-buffer)
+  (rename-buffer (format "*vterm<%s>*" name)))
+
+(defun vterm-mgt-reconcile-state ()
+  "Fill `vterm-mgt--instances' with the existing vterm buffers.
+
+If for whatever reason, the state of `vterm-mgt--instances' is corrupted and
+  misaligns with the state of vterm buffers in Emacs, use this function to
+  restore the state."
+  (interactive)
+  (setq vterm-mgt--instances
+        (cycle-from-list (seq-filter #'vterm-mgt--instance? (buffer-list)))))
+
+(defun vterm-mgt-select ()
+  "Select a vterm instance by name from the list in `vterm-mgt--instances'."
+  (interactive)
+  (vterm-mgt-reconcile-state)
+  (switch-to-buffer
+   (completing-read "Switch to vterm: "
+                    (seq-map #'buffer-name (cycle-to-list vterm-mgt--instances)))))
+
+(provide 'vterm-mgt)
+;;; vterm-mgt.el ends here
diff --git a/users/wpcarro/emacs/pkgs/zle/default.nix b/users/wpcarro/emacs/pkgs/zle/default.nix
new file mode 100644
index 0000000000..9d4820a944
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/zle/default.nix
@@ -0,0 +1,10 @@
+{ pkgs, ... }:
+
+pkgs.callPackage
+  ({ emacsPackages }:
+  emacsPackages.trivialBuild {
+    pname = "zle";
+    version = "1.0.0";
+    src = ./zle.el;
+  })
+{ }
diff --git a/users/wpcarro/emacs/pkgs/zle/zle.el b/users/wpcarro/emacs/pkgs/zle/zle.el
new file mode 100644
index 0000000000..21a6e35f13
--- /dev/null
+++ b/users/wpcarro/emacs/pkgs/zle/zle.el
@@ -0,0 +1,90 @@
+;;; zle.el --- Functions to mimmick my ZLE KBDs -*- lexical-binding: t -*-
+
+;; Author: William Carroll <wpcarro@gmail.com>
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "24"))
+
+;;; Commentary:
+;; This is primarily for personal use.  The keybindings that I choose are those
+;; that feel slightly mnemonic while also not shadowing important bindings.
+;; It's quite possible that our tastes will differ here.
+;;
+;; All of these keybindings are intended to shave off milliseconds off your
+;; typing.  I don't expect these numbers to sum up to a meaningful amount.  The
+;; primary reason that I wrote this, is that it introduces a small amount of
+;; structural editing to my workflow.  I've been using these exact keybindings
+;; on the command line, and I find them subtely delightful to use.  So much so
+;; that I decided to bring them to my Emacs configuration.
+;;
+;; ZLE is the Z-shell line editor.  I have some KBDs and functions that I often
+;; want in Emacs.
+;;
+;; Usage:
+;; Consider running `(zle-minor-mode)' to run this globally.  Depending on your
+;; configuration, it could be non-disruptive, disruptive, or extremely
+;; disruptive.
+
+;;; Code:
+
+;; subshell (C-j)
+(defun zle-subshell ()
+  "Insert the characters necessary to create a subshell."
+  (interactive)
+  (insert-char ?$)
+  (insert-char ?\()
+  (save-excursion
+    (insert-char ?\))))
+
+;; variable (C-v)
+(defun zle-variable ()
+  "Insert the characters to reference a variable."
+  (interactive)
+  (insert-char ?$)
+  (insert-char ?{)
+  (save-excursion
+    (insert-char ?})))
+
+;; 2x dash (C-M--)
+(defun zle-dash-dash ()
+  "Insert the characters for flags with 2x dashes."
+  (interactive)
+  (insert-char ? )
+  (insert-char ?-)
+  (insert-char ?-))
+
+;; 1x quotes (M-')
+(defun zle-single-quote ()
+  "Insert the characters to quickly create single quotes."
+  (interactive)
+  (insert-char ? )
+  (insert-char ?')
+  (save-excursion
+    (insert-char ?')))
+
+;; 2x quotes (M-")
+(defun zle-double-quote ()
+  "Insert the characters to quickly create double quotes."
+  (interactive)
+  (insert-char ? )
+  (insert-char ?\")
+  (save-excursion
+    (insert-char ?\")))
+
+(defvar zle-kbds
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "C-j") #'zle-subshell)
+    (define-key map (kbd "C-v") #'zle-variable)
+    (define-key map (kbd "C-M--") #'zle-dash-dash)
+    (define-key map (kbd "M-'") #'zle-single-quote)
+    (define-key map (kbd "M-\"") #'zle-double-quote)
+    map)
+  "Keybindings shaving milliseconds off of typing.")
+
+(define-minor-mode zle-minor-mode
+  "A minor mode mirroring my ZLE keybindings."
+  :init-value nil
+  :lighter " zle"
+  :keymap zle-kbds)
+
+(provide 'zle)
+;;; zle.el ends here
diff --git a/users/wpcarro/emacs/snippets.md b/users/wpcarro/emacs/snippets.md
new file mode 100644
index 0000000000..2081b56171
--- /dev/null
+++ b/users/wpcarro/emacs/snippets.md
@@ -0,0 +1,22 @@
+# Snippets
+
+Specifying snippets that I plan on defining for most of the programming
+languages with which I work. I hope this will serve as a checklist of language
+constructs I should support when adopting a new language.
+
+## Shared language features
+
+These are language features that should be available across most of the
+languages that I'm hoping to support.
+
+- `ld`: anonymous functions (i.e. lambdas)
+- `fn`: named function definition
+- `var`: variable definition
+
+## Miscellaneous other language KBDs
+
+Some of this is related to language tool must-haves, which may need to be a
+separate document.
+
+- `<leader>d`: Show documentation
+- `<leader>x`: Evaluate expression (works mostly for LISPs)
diff --git a/users/wpcarro/emacs/workspace.josh b/users/wpcarro/emacs/workspace.josh
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/users/wpcarro/emacs/workspace.josh
diff --git a/users/wpcarro/go/.envrc b/users/wpcarro/go/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/go/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/go/actors.go b/users/wpcarro/go/actors.go
new file mode 100644
index 0000000000..1409db185e
--- /dev/null
+++ b/users/wpcarro/go/actors.go
@@ -0,0 +1,45 @@
+package main
+
+import (
+	"bufio"
+	"fmt"
+	"os"
+)
+
+// Call function `f` in a go-routine, passing a reference to a newly created
+// channel, `c`, as its only argument. Return a reference to `c` to the caller
+// of `act`. When `f` halts, close the channel.
+func act(f func(chan interface{})) chan interface{} {
+	c := make(chan interface{})
+
+	go func() {
+		defer close(c)
+		f(c)
+	}()
+
+	return c
+}
+
+func prompt(msg string) string {
+	reader := bufio.NewReader(os.Stdin)
+	fmt.Print(msg)
+	text, _ := reader.ReadString('\n')
+	// TODO: Trim trailing newline from the rhs of text.
+	return text
+}
+
+func main() {
+	c := act(func(c chan interface{}) {
+		for {
+			x := <-c
+			fmt.Printf("[A] Received value: %v\n", x)
+
+		}
+	})
+
+	for {
+		x := prompt("[B] Enter a value: ")
+		c <- x
+	}
+	os.Exit(0)
+}
diff --git a/users/wpcarro/go/atomic-counters.go b/users/wpcarro/go/atomic-counters.go
new file mode 100644
index 0000000000..6cbcd2ee4e
--- /dev/null
+++ b/users/wpcarro/go/atomic-counters.go
@@ -0,0 +1,26 @@
+// Attempting to apply some of the lessons I learned here:
+// https://gobyexample.com/atomic-counters
+package main
+
+import (
+	"fmt"
+	"sync"
+	"sync/atomic"
+)
+
+func main() {
+	var count uint64
+	var wg sync.WaitGroup
+
+	for i := 0; i < 50; i += 1 {
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			for j := 0; j < 1000; j += 1 {
+				atomic.AddUint64(&count, 1)
+			}
+		}()
+	}
+	wg.Wait()
+	fmt.Println("Count: ", count)
+}
diff --git a/users/wpcarro/go/channels.go b/users/wpcarro/go/channels.go
new file mode 100644
index 0000000000..cba8abfc96
--- /dev/null
+++ b/users/wpcarro/go/channels.go
@@ -0,0 +1,81 @@
+package main
+
+import (
+	"fmt"
+	"math/rand"
+	"sync"
+	"sync/atomic"
+)
+
+type readMsg struct {
+	key    int
+	sender chan int
+}
+
+type writeMsg struct {
+	key    int
+	value  int
+	sender chan bool
+}
+
+func main() {
+	fmt.Println("Hello, go.")
+
+	var readOps uint64
+	var writeOps uint64
+	var wg sync.WaitGroup
+
+	reads := make(chan readMsg)
+	writes := make(chan writeMsg)
+
+	go func() {
+		state := make(map[int]int)
+		for {
+			select {
+			case msg := <-reads:
+				msg.sender <- state[msg.key]
+			case msg := <-writes:
+				state[msg.key] = msg.value
+				msg.sender <- true
+			}
+		}
+	}()
+
+	// Reads
+	for i := 0; i < 100; i += 1 {
+		go func() {
+			wg.Add(1)
+			defer wg.Done()
+			for j := 0; j < 100; j += 1 {
+				msg := readMsg{
+					key:    rand.Intn(5),
+					sender: make(chan int)}
+				reads <- msg
+				val := <-msg.sender
+				fmt.Printf("Received %d.\n", val)
+				atomic.AddUint64(&readOps, 1)
+			}
+		}()
+	}
+
+	// Writes
+	for i := 0; i < 100; i += 1 {
+		go func() {
+			wg.Add(1)
+			defer wg.Done()
+			for j := 0; j < 100; j += 1 {
+				msg := writeMsg{
+					key:    rand.Intn(5),
+					value:  rand.Intn(10),
+					sender: make(chan bool)}
+				writes <- msg
+				<-msg.sender
+				fmt.Printf("Set %d as %d in state\n", msg.key, msg.value)
+				atomic.AddUint64(&writeOps, 1)
+			}
+		}()
+	}
+
+	wg.Wait()
+	fmt.Printf("Read ops: %d\tWrite ops: %d\n", atomic.LoadUint64(&readOps), atomic.LoadUint64(&writeOps))
+}
diff --git a/users/wpcarro/go/mutex.go b/users/wpcarro/go/mutex.go
new file mode 100644
index 0000000000..5cea20754b
--- /dev/null
+++ b/users/wpcarro/go/mutex.go
@@ -0,0 +1,53 @@
+package main
+
+import (
+	"fmt"
+	"math/rand"
+	"sync"
+	"sync/atomic"
+	"time"
+)
+
+func main() {
+	state := make(map[int]int)
+	mux := &sync.Mutex{}
+
+	var readOps uint64
+	var writeOps uint64
+
+	// Read from state
+	for i := 0; i < 1000; i += 1 {
+		for j := 0; j < 100; j += 1 {
+			go func() {
+				key := rand.Intn(5)
+				mux.Lock()
+				fmt.Printf("state[%d] = %d\n", key, state[key])
+				mux.Unlock()
+				atomic.AddUint64(&readOps, 1)
+				time.Sleep(time.Millisecond)
+			}()
+		}
+	}
+
+	// Write to state
+	for i := 0; i < 10; i += 1 {
+		for j := 0; j < 100; j += 1 {
+			go func() {
+				key := rand.Intn(5)
+				mux.Lock()
+				state[key] += 1
+				mux.Unlock()
+				fmt.Printf("Wrote to state[%d].\n", key)
+				atomic.AddUint64(&writeOps, 1)
+				time.Sleep(time.Millisecond)
+			}()
+		}
+	}
+
+	time.Sleep(time.Millisecond)
+
+	mux.Lock()
+	fmt.Printf("State: %v\n", state)
+	mux.Unlock()
+	fmt.Printf("Reads: %d\tWrites: %d\n", atomic.LoadUint64(&readOps), atomic.LoadUint64(&writeOps))
+}
diff --git a/users/wpcarro/go/shell.nix b/users/wpcarro/go/shell.nix
new file mode 100644
index 0000000000..f777c13fef
--- /dev/null
+++ b/users/wpcarro/go/shell.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    go
+    goimports
+    godef
+  ];
+}
diff --git a/users/wpcarro/go/waitgroups.go b/users/wpcarro/go/waitgroups.go
new file mode 100644
index 0000000000..816321b877
--- /dev/null
+++ b/users/wpcarro/go/waitgroups.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+	"fmt"
+	"sync"
+	"time"
+)
+
+func saySomething(x string, wg *sync.WaitGroup) {
+	defer wg.Done()
+	fmt.Println(x)
+	time.Sleep(time.Second)
+	fmt.Printf("Finished saying \"%s\"\n", x)
+}
+
+func main() {
+	var wg sync.WaitGroup
+	var things = [5]string{"chicken", "panini", "cheeseburger", "rice", "bread"}
+	for i := 0; i < 5; i += 1 {
+		wg.Add(1)
+		go saySomething(things[i], &wg)
+	}
+	wg.Wait()
+}
diff --git a/users/wpcarro/gopkgs/kv/default.nix b/users/wpcarro/gopkgs/kv/default.nix
new file mode 100644
index 0000000000..72aae7827b
--- /dev/null
+++ b/users/wpcarro/gopkgs/kv/default.nix
@@ -0,0 +1,8 @@
+{ depot, ... }:
+
+depot.nix.buildGo.package {
+  name = "kv";
+  srcs = [
+    ./kv.go
+  ];
+}
diff --git a/users/wpcarro/gopkgs/kv/kv.go b/users/wpcarro/gopkgs/kv/kv.go
new file mode 100644
index 0000000000..040cc63e0e
--- /dev/null
+++ b/users/wpcarro/gopkgs/kv/kv.go
@@ -0,0 +1,39 @@
+// Supporting reading and writing key-value pairs to disk.
+package kv
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"log"
+	"path"
+)
+
+// Return the decoded store from disk.
+func getStore(storePath string) map[string]interface{} {
+	b, err := ioutil.ReadFile(path.Join(storePath, "kv.json"))
+	if err != nil {
+		log.Fatal("Could not read store: ", err)
+	}
+	var state map[string]interface{}
+	err = json.Unmarshal(b, &state)
+	if err != nil {
+		log.Fatal("Could not decode store as JSON: ", err)
+	}
+	return state
+}
+
+// Set `key` to `value` in the store.
+func Set(storePath string, key string, value interface{}) error {
+	state := getStore(storePath)
+	state[key] = value
+	b, err := json.Marshal(state)
+	if err != nil {
+		log.Fatal("Could not encode state as JSON: ", err)
+	}
+	return ioutil.WriteFile(path.Join(storePath, "kv.json"), b, 0644)
+}
+
+// Get `key` from the store.
+func Get(storePath string, key string) interface{} {
+	return getStore(path.Join(storePath, "kv.json"))[key]
+}
diff --git a/users/wpcarro/gopkgs/utils/default.nix b/users/wpcarro/gopkgs/utils/default.nix
new file mode 100644
index 0000000000..25321f50a0
--- /dev/null
+++ b/users/wpcarro/gopkgs/utils/default.nix
@@ -0,0 +1,8 @@
+{ depot, ... }:
+
+depot.nix.buildGo.package {
+  name = "utils";
+  srcs = [
+    ./utils.go
+  ];
+}
diff --git a/users/wpcarro/gopkgs/utils/utils.go b/users/wpcarro/gopkgs/utils/utils.go
new file mode 100644
index 0000000000..7d662d0866
--- /dev/null
+++ b/users/wpcarro/gopkgs/utils/utils.go
@@ -0,0 +1,131 @@
+// Some utility functions to tidy up my Golang.
+package utils
+
+import (
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"net/http/httputil"
+	"os"
+	"os/user"
+	"path/filepath"
+)
+
+// Return the absolute path to the current uesr's home directory.
+func HomeDir() string {
+	user, err := user.Current()
+	if err != nil {
+		log.Fatal(err)
+	}
+	return user.HomeDir
+}
+
+// Returns true if `info` is a symlink.
+func IsSymlink(info os.FileMode) bool {
+	return info&os.ModeSymlink != 0
+}
+
+// Return true if `path` exists and false otherwise.
+func FileExists(path string) bool {
+	if _, err := os.Stat(path); os.IsNotExist(err) {
+		return false
+	} else {
+		return true
+	}
+}
+
+// Return the absolute file path of `file` using the following resolution
+// strategy:
+// - Traverse and search upwards until you reach the user's home directory
+// - Return the first path in `backupPaths` that exists
+// - Fail
+func Resolve(fileName string, backupPaths []string) string {
+	// TODO(wpcarro): Drop hardcoding when whoami behaves as expected.
+	boundary := "/home"
+	cwd := "."
+	files, _ := ioutil.ReadDir(cwd)
+
+	for {
+		fullCwd, _ := filepath.Abs(cwd)
+		if fullCwd == boundary {
+			break
+		}
+		for _, file := range files {
+			if file.Name() == fileName {
+				path, _ := filepath.Abs(cwd + "/" + file.Name())
+				return path
+			}
+		}
+		cwd += "/.."
+		files, _ = ioutil.ReadDir(cwd)
+	}
+
+	// TODO(wpcarro): Support expanding these paths to allow the consumer to
+	// pass in relative paths, and paths with "~" in them.
+	for _, backup := range backupPaths {
+		if FileExists(backup) {
+			return backup
+		}
+	}
+	log.Fatal("Cannot find a run.json to use.")
+	// This code should be unreachable.
+	return ""
+}
+
+// Call log.Fatal with `err` when it's not nil.
+func FailOn(err error) {
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+// Prints the verbose form of an HTTP request.
+func DebugRequest(req *http.Request) {
+	bytes, _ := httputil.DumpRequest(req, true)
+	fmt.Println(string(bytes))
+}
+
+// Prints out the verbose form of an HTTP response.
+func DebugResponse(res *http.Response) {
+	bytes, _ := httputil.DumpResponse(res, true)
+	fmt.Println(string(bytes))
+}
+
+// Make a simple GET request to `url`. Fail if anything returns an error. I'd
+// like to accumulate a library of these, so that I can write scrappy Go
+// quickly. For now, this function just returns the body of the response back as
+// a string.
+func SimpleGet(url string, headers map[string]string, debug bool) string {
+	client := &http.Client{}
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+	for k, v := range headers {
+		req.Header.Add(k, v)
+	}
+
+	res, err := client.Do(req)
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer res.Body.Close()
+
+	if debug {
+		DebugRequest(req)
+		DebugResponse(res)
+	}
+
+	if res.StatusCode == http.StatusOK {
+		bytes, err := ioutil.ReadAll(res.Body)
+		if err != nil {
+			log.Fatal(err)
+		}
+		return string(bytes)
+	} else {
+		log.Println(res)
+		log.Fatalf("HTTP status code of response not OK: %v\n", res.StatusCode)
+		return ""
+	}
+}
diff --git a/users/wpcarro/haskell-file/.envrc b/users/wpcarro/haskell-file/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/haskell-file/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/haskell-file/README.md b/users/wpcarro/haskell-file/README.md
new file mode 100644
index 0000000000..3f3ac1474b
--- /dev/null
+++ b/users/wpcarro/haskell-file/README.md
@@ -0,0 +1,7 @@
+# haskell-file
+
+This is a half-baked project. I'd like to write a library whose API closely
+resembles some of the more modern filesystem APIs to which I am accustomed:
+notably f.el for Elisp.
+
+I expect more development to come.
diff --git a/users/wpcarro/haskell-file/f-todo.org b/users/wpcarro/haskell-file/f-todo.org
new file mode 100644
index 0000000000..6dd43a9629
--- /dev/null
+++ b/users/wpcarro/haskell-file/f-todo.org
@@ -0,0 +1,67 @@
+* Paths
+** TODO f-join (&rest args)
+** TODO f-split (path)
+** TODO f-expand (path &optional dir)
+** TODO f-filename (path)
+** TODO f-dirname (path)
+** TODO f-common-parent (paths)
+** TODO f-ext (path)
+** TODO f-no-ext (path)
+** TODO f-swap-ext (path ext)
+** TODO f-base (path)
+** TODO f-relative (path &optional dir)
+** TODO f-short (path)
+** TODO f-long (path)
+** TODO f-canonical (path)
+** TODO f-slash (path)
+** TODO f-full (path)
+** TODO f-uniquify (paths)
+** TODO f-uniquify-alist (paths)
+* I/O
+** TODO f-read-bytes (path)
+** TODO f-write-bytes (data path)
+** TODO f-read-text (path &optional coding)
+** TODO f-write-text(text coding path)
+** TODO f-append-text(text coding path)
+** TODO f-append-bytes(text coding path)
+** TODO Destructive
+** TODO f-mkdir (&rest dirs)
+** TODO f-delete (path &optional force)
+** TODO f-symlink (source path)
+** TODO f-move (from to)
+** TODO f-copy (from to)
+** TODO f-copy-contenst (from to)
+** TODO f-touch (path)
+** TODO Predicates
+** TODO f-exists? (path)
+** TODO f-directory? (path)
+** TODO f-file? (path)
+** TODO f-symlink? (path)
+** TODO f-readable? (path)
+** TODO f-writable? (path)
+** TODO f-executable? (path)
+** TODO f-absolute? (path)
+** TODO f-relative? (path)
+** TODO f-root? (path)
+** TODO f-ext? (path ext)
+** TODO f-same? (path-a path-b)
+** TODO f-parent-of? (path-a path-b)
+** TODO f-child-of? (path-a path-b)
+** TODO f-ancestor-of? (path-a path-b)
+** TODO f-descendant-of? (path-a path-b)
+** TODO f-hidden? (path)
+** TODO f-empty? (path)
+** TODO Stats
+** TODO f-size (path)
+** f-depth (path)
+
+* Misc
+** TODO f-this-file ()
+** TODO f-path-separator ()
+** TODO f-glob (pattern &optional path)
+** TODO f-entries (path &optional fn recursive)
+** TODO f-directories (path &optional fn recursive)
+** TODO f-files (path &optional fn recursive)
+** TODO f-root ()
+** TODO f-traverse-upwards (fn &optional path)
+** TODO f-with-sandbox (path-or-paths &rest body)
diff --git a/users/wpcarro/haskell-file/f.hs b/users/wpcarro/haskell-file/f.hs
new file mode 100644
index 0000000000..295575f3f4
--- /dev/null
+++ b/users/wpcarro/haskell-file/f.hs
@@ -0,0 +1,64 @@
+module F
+  ( join
+  , split
+  ) where
+
+--------------------------------------------------------------------------------
+-- Dependencies
+--------------------------------------------------------------------------------
+
+import Data.List (span)
+import System.FilePath (FilePath, pathSeparator)
+import System.FilePath.Posix (FilePath)
+import qualified System.FilePath.Posix as F
+
+-- TODO: Move this to a misc.hs, prelude.hs, operators.hs; somewhere.
+(|>) :: a -> (a -> b) -> b
+(|>) a f = f a
+infixl 1 |>
+
+-- TODO: Move this to a test_utils.hs or elsewhere.
+simpleAssert :: (Eq a) => a -> a -> ()
+simpleAssert x y =
+  if x == y then
+    ()
+  else
+    error "Assertion error"
+
+--------------------------------------------------------------------------------
+-- Library
+--------------------------------------------------------------------------------
+
+join :: [FilePath] -> FilePath
+join = F.joinPath
+
+-- | Split path and return  list containing parts.
+split :: FilePath -> [String]
+split = splitJoin . span (/= pathSeparator)
+  where
+    splitJoin :: (String, String) -> [String]
+    splitJoin ([], []) = []
+    splitJoin (a, []) = [a]
+    splitJoin (a, [_]) = [a]
+    splitJoin (a, _:b) = a : split b
+
+--------------------------------------------------------------------------------
+-- Tests
+--------------------------------------------------------------------------------
+
+expected :: [([FilePath], FilePath)]
+expected = [ (["path"], "path")
+           , (["/path"], "/path")
+           , (["path", "to", "file"], "path/to/file")
+           , (["/path", "to", "file"], "/path/to/file")
+           , (["/"], "/")
+           ]
+
+runTests :: [()]
+runTests =
+  fmap (\(input, expected) -> simpleAssert (join input) expected) expected
+
+main :: IO ()
+main = do
+  print runTests
+  pure ()
diff --git a/users/wpcarro/haskell-file/shell.nix b/users/wpcarro/haskell-file/shell.nix
new file mode 100644
index 0000000000..0c6a298bf2
--- /dev/null
+++ b/users/wpcarro/haskell-file/shell.nix
@@ -0,0 +1,5 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.shell {
+  deps = hpkgs: [ ];
+}
diff --git a/users/wpcarro/haskell-file/tests.hs b/users/wpcarro/haskell-file/tests.hs
new file mode 100644
index 0000000000..e3967b77de
--- /dev/null
+++ b/users/wpcarro/haskell-file/tests.hs
@@ -0,0 +1,39 @@
+module FTest where
+--------------------------------------------------------------------------------
+import Test.Tasty
+import Test.Tasty.Hedgehog
+import Hedgehog
+--------------------------------------------------------------------------------
+import qualified Hedgehog as H
+import qualified Hedgehog.Gen as Gen
+import qualified Hedgehog.Range as Range
+--------------------------------------------------------------------------------
+import Data.List (intercalate)
+import System.FilePath (pathSeparator)
+--------------------------------------------------------------------------------
+import F
+--------------------------------------------------------------------------------
+main :: IO ()
+main
+  = defaultMain
+  . localOption (HedgehogTestLimit $ Just 50)
+  $ testGroup "f functions"
+  [ test_split
+  ]
+--------------------------------------------------------------------------------
+test_split :: TestTree
+test_split
+  = testGroup "split function"
+  [ testProperty "splits parts properly" splitSuccess
+  ]
+splitSuccess :: Property
+splitSuccess = property $ do
+  -- separator
+  --   <- H.forAll
+  --   $ Gen.element ['/', '\\']
+  parts
+    <- H.forAll
+    . Gen.list (Range.linear 0 10)
+    $ Gen.list (Range.linear 1 10) Gen.alphaNum
+  let path = intercalate [pathSeparator] parts
+  F.split path === parts
diff --git a/users/wpcarro/keys.nix b/users/wpcarro/keys.nix
new file mode 100644
index 0000000000..531d110f71
--- /dev/null
+++ b/users/wpcarro/keys.nix
@@ -0,0 +1,20 @@
+# wpcarro's public SSH keys
+{ ... }:
+
+rec {
+  ava = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB/5Fuo7wi8rNXVXgNaCK2X6ePCh9LQs/9h7Tj6UeXrl wpcarro@ava";
+  iphone = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEU1tsRQ/cMxi9Hd7Xo+YpiWB5i6qx24EJLCEFBK4q4W wpcarro@iphone";
+  kyoko = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBFILKdkNqfTP5WeoQAV6K3MdTzsDW65ToXGc6KlQ9yl wpcarro@kyoko";
+  marcus = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJkNQJBXekuSzZJ8+gxT+V1+eXTm3hYsfigllr/ARXkf wpcarro@gmail.com";
+  nathan = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP2NjuP722VUgpSu5bVUPTfdVNPO8fSW0Jlas8L4up13 bill@nathan";
+  tarasco = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOh+wG4f7tI0IwGyF2sLi5mPlh3JKE7KqV2ab0tlcL36 wpcarro@tarasco";
+
+  all = [
+    ava
+    iphone
+    kyoko
+    marcus
+    nathan
+    tarasco
+  ];
+}
diff --git a/users/wpcarro/lib/default.nix b/users/wpcarro/lib/default.nix
new file mode 100644
index 0000000000..6354877dd4
--- /dev/null
+++ b/users/wpcarro/lib/default.nix
@@ -0,0 +1,5 @@
+{ depot, ... }:
+
+{
+  usermod = name: depot.path.origSrc + ("/users/wpcarro/nixos/modules/${name}");
+}
diff --git a/users/wpcarro/lisp/README.md b/users/wpcarro/lisp/README.md
new file mode 100644
index 0000000000..9f8693fa6a
--- /dev/null
+++ b/users/wpcarro/lisp/README.md
@@ -0,0 +1,16 @@
+# Common Lisp
+
+Things that I like about Common Lisp:
+- It's an S-expression based language.
+- It has a powerful macro system
+- It has a unique way of handling-errors
+- It is highly introspectible
+- The tooling integration with Emacs is the best I have ever seen for any language
+
+Things that I don't like about Common Lisp:
+- I find its standard libraries difficult to use and -- compared to modern
+  libraries -- like Golang's or Elixir's standard libraries, Common Lisp's
+  libraries are clunky
+
+As such, I would like to modernize CL's libraries to resemble other libraries
+with which I am more familiar and, therefore, productive.
diff --git a/users/wpcarro/lisp/prelude.lisp b/users/wpcarro/lisp/prelude.lisp
new file mode 100644
index 0000000000..3522567ea0
--- /dev/null
+++ b/users/wpcarro/lisp/prelude.lisp
@@ -0,0 +1,14 @@
+(in-package #:cl-user)
+(defpackage #:prelude
+  (:documentation "Supporting miscellaneous utility functions and macros.")
+  (:use #:cl)
+  (:shadow #:type)
+  (:export #:type #:comment))
+(in-package #:prelude)
+
+;; TODO: Add documentation to these macros.
+
+(defmacro type (name in out)
+  `(declaim (ftype (function ,in ,out) ,name)))
+
+(defmacro comment (&rest _forms) nil)
diff --git a/users/wpcarro/lisp/prelude.nix b/users/wpcarro/lisp/prelude.nix
new file mode 100644
index 0000000000..5fe5d628e0
--- /dev/null
+++ b/users/wpcarro/lisp/prelude.nix
@@ -0,0 +1,8 @@
+{ depot, ... }:
+
+depot.nix.buildLisp.library {
+  name = "prelude";
+  srcs = [
+    ./prelude.lisp
+  ];
+}
diff --git a/users/wpcarro/nixos/ava/ava.el b/users/wpcarro/nixos/ava/ava.el
new file mode 100644
index 0000000000..b0b13746b0
--- /dev/null
+++ b/users/wpcarro/nixos/ava/ava.el
@@ -0,0 +1,61 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'bookmark)
+(require 'display)
+(require 'window-manager)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(bookmark-install-kbd
+ (make-bookmark :label "hadrian"
+                :path "/hadrian"
+                :kbd "h"))
+
+(setq initial-buffer-choice "/hadrian")
+
+(add-to-list 'ssh-hosts "wpcarro@tarasco")
+
+(display-register primary
+                  :output "HDMI-1"
+                  :primary t
+                  :coords (0 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-register secondary
+                  :output "HDMI-2"
+                  :primary nil
+                  :coords (2561 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-arrangement main :displays (primary secondary))
+
+(setq window-manager-named-workspaces
+      (list (make-window-manager-named-workspace
+             :label "Web Browsing"
+             :kbd "c"
+             :display display-secondary)
+            (make-window-manager-named-workspace
+             :label "Coding I"
+             :kbd "1"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Coding II"
+             :kbd "2"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Chatting"
+             :kbd "h"
+             :display display-secondary)))
+
+;; I *think* this needs to be the last statement in this file.
+(window-manager-init :init-hook #'display-arrange-main)
diff --git a/users/wpcarro/nixos/ava/default.nix b/users/wpcarro/nixos/ava/default.nix
new file mode 100644
index 0000000000..25c43c003f
--- /dev/null
+++ b/users/wpcarro/nixos/ava/default.nix
@@ -0,0 +1,150 @@
+{ depot, pkgs, lib, ... }:
+{ ... }:
+
+let
+  inherit (depot.users) wpcarro;
+  inherit (depot.users.wpcarro.lib) usermod;
+
+  wpcarrosEmacs = wpcarro.emacs.nixos {
+    load = [ ./ava.el ];
+  };
+
+  quasselClient = pkgs.quassel.override {
+    client = true;
+    enableDaemon = false;
+    monolithic = false;
+  };
+in
+{
+  imports = [
+    (usermod "hardware/nopn.nix")
+  ];
+
+  # Use the TVL binary cache
+  tvl.cache.enable = true;
+
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  # Support IP forwarding to use this device as a Tailscale exit node.
+  boot.kernel.sysctl."net.ipv4.ip_forward" = true;
+  boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
+  # Additionall exit node settings that Tailscale recommends.
+  networking.firewall.checkReversePath = "loose";
+
+  time.timeZone = "America/Los_Angeles";
+
+  networking = {
+    # The global useDHCP flag is deprecated, therefore explicitly set to false
+    # here.  Per-interface useDHCP will be mandatory in the future, so this
+    # generated config replicates the default behaviour.
+    useDHCP = false;
+    hostName = "ava";
+    networkmanager.enable = true;
+    interfaces.enp1s0.useDHCP = true;
+    interfaces.enp3s0.useDHCP = true;
+    interfaces.wlp2s0.useDHCP = true;
+  };
+
+  services = wpcarro.common.services // {
+    # Check the amount of available memory and free swap a few times per second
+    # and kill the largest process if both are below 10%.
+    earlyoom.enable = true;
+
+    tailscale.enable = true;
+
+    openssh.enable = true;
+
+    printing = {
+      enable = true;
+      drivers = with pkgs; [ gutenprint ];
+    };
+
+    xserver = {
+      enable = true;
+      xkb.layout = "us";
+      xkb.options = "caps:escape";
+      displayManager = {
+        # Give EXWM permission to control the session (from tazjin's setup).
+        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localhost:$USER";
+        lightdm.enable = true;
+      };
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${wpcarrosEmacs}/bin/wpcarros-emacs";
+      };
+    };
+  };
+
+  # Enable sound.
+  sound.enable = true;
+  hardware.pulseaudio.enable = true;
+
+  users.mutableUsers = true;
+  users.users.root.openssh.authorizedKeys.keys = with wpcarro.keys; [
+    iphone
+    nathan
+    tarasco
+  ];
+  users.users.wpcarro = {
+    initialPassword = "password";
+    isNormalUser = true;
+    extraGroups = [
+      "networkmanager"
+      "wheel"
+      "docker"
+    ];
+    shell = pkgs.fish;
+    openssh.authorizedKeys.keys = with wpcarro.keys; [
+      iphone
+      nathan
+      tarasco
+    ];
+  };
+  users.extraGroups.vboxusers.members = [ "wpcarro" ];
+
+  security.sudo.wheelNeedsPassword = false;
+
+  fonts = {
+    packages = with pkgs; [
+      jetbrains-mono
+    ];
+
+    fontconfig = {
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+
+  programs = wpcarro.common.programs // {
+    mosh.enable = true;
+  };
+
+  virtualisation.docker.enable = true;
+  virtualisation.virtualbox.host.enable = true;
+
+  environment.variables = {
+    EDITOR = "emacsclient";
+    ALTERNATE_EDITOR = "emacs -q -nw";
+    VISUAL = "emacsclient";
+  };
+
+  environment.systemPackages =
+    wpcarro.common.shell-utils ++
+    (with pkgs; [
+      alacritty
+      ec2-api-tools
+      firefox
+      google-chrome
+      httpie
+      pavucontrol
+      quasselClient
+      remmina
+      tdesktop
+      wpcarrosEmacs
+      xsecurelock
+    ]);
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/wpcarro/nixos/default.nix b/users/wpcarro/nixos/default.nix
new file mode 100644
index 0000000000..9c8a7e5a79
--- /dev/null
+++ b/users/wpcarro/nixos/default.nix
@@ -0,0 +1,24 @@
+{ depot, pkgs, ... }:
+
+let
+  inherit (depot.users.wpcarro.nixos)
+    ava
+    kyoko
+    marcus
+    tarasco;
+
+  systemFor = sys: (depot.ops.nixos.nixosFor sys).system;
+in
+{
+  avaSystem = systemFor ava;
+  kyokoSystem = systemFor kyoko;
+  marcusSystem = systemFor marcus;
+  tarascoSystem = systemFor tarasco;
+
+  meta.ci.targets = [
+    "avaSystem"
+    "kyokoSystem"
+    "marcusSystem"
+    "tarascoSystem"
+  ];
+}
diff --git a/users/wpcarro/nixos/iso.nix b/users/wpcarro/nixos/iso.nix
new file mode 100644
index 0000000000..8102c98fb8
--- /dev/null
+++ b/users/wpcarro/nixos/iso.nix
@@ -0,0 +1,17 @@
+# TODO(wpcarro): Support the workflow outlined in these docs.
+#
+# Usage:
+#   $ lsblk  # get your USB dev path (e.g. /dev/sdb)
+#   $ create-installer --dev=/dev/sdb //users/wpcarro/nixos/marcus
+
+{ pkgs, ... }:
+
+{
+  imports = [
+    "${pkgs.nixos}/modules/installer/cd-graphical-gnome.nix"
+  ];
+
+  config = {
+    networking.wireless.enable = true;
+  };
+}
diff --git a/users/wpcarro/nixos/kyoko/default.nix b/users/wpcarro/nixos/kyoko/default.nix
new file mode 100644
index 0000000000..0d8907edd2
--- /dev/null
+++ b/users/wpcarro/nixos/kyoko/default.nix
@@ -0,0 +1,153 @@
+{ depot, pkgs, lib, ... }:
+_:
+
+let
+  inherit (depot.users) wpcarro;
+  inherit (depot.users.wpcarro.lib) usermod;
+
+  wpcarrosEmacs = wpcarro.emacs.nixos {
+    load = [ ./kyoko.el ];
+  };
+
+  quasselClient = pkgs.quassel.override {
+    client = true;
+    enableDaemon = false;
+    monolithic = false;
+  };
+in
+{
+  imports = [
+    (usermod "hardware/dell-emc-egw-5200.nix")
+    (usermod "hadrian-cache.nix")
+  ];
+
+  # TVL's Nix binary cache
+  tvl.cache.enable = true;
+
+  # Hadrian's Nix binary cache.
+  hadrian.cache.enable = true;
+
+  nix.settings.trusted-users = [ "@wheel" ];
+
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  # Additionall exit node settings that Tailscale recommends.
+  networking.firewall.checkReversePath = "loose";
+
+  time.timeZone = "America/Los_Angeles";
+
+  networking = {
+    # The global useDHCP flag is deprecated, therefore explicitly set to false
+    # here.  Per-interface useDHCP will be mandatory in the future, so this
+    # generated config replicates the default behaviour.
+    useDHCP = false;
+    hostName = "kyoko";
+    networkmanager.enable = true;
+    interfaces.enp1s0.useDHCP = true;
+    interfaces.enp3s0.useDHCP = true;
+    interfaces.wlp2s0.useDHCP = true;
+  };
+
+  services = wpcarro.common.services // {
+    # Check the amount of available memory and free swap a few times per second
+    # and kill the largest process if both are below 10%.
+    earlyoom.enable = true;
+
+    tailscale.enable = true;
+
+    openssh.enable = true;
+
+    printing = {
+      enable = true;
+      drivers = with pkgs; [ gutenprint ];
+    };
+
+    xserver = {
+      enable = true;
+      xkb.layout = "us";
+      xkb.options = "caps:escape";
+      displayManager = {
+        # Give EXWM permission to control the session (from tazjin's setup).
+        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localhost:$USER";
+        lightdm.enable = true;
+      };
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${wpcarrosEmacs}/bin/wpcarros-emacs";
+      };
+    };
+  };
+
+  # Enable sound.
+  sound.enable = true;
+  hardware.pulseaudio.enable = true;
+
+  users.mutableUsers = true;
+  users.users.root.openssh.authorizedKeys.keys = with wpcarro.keys; [
+    iphone
+    nathan
+    tarasco
+  ];
+  users.users.wpcarro = {
+    initialPassword = "password";
+    isNormalUser = true;
+    extraGroups = [
+      "networkmanager"
+      "wheel"
+      "docker"
+    ];
+    shell = pkgs.fish;
+    openssh.authorizedKeys.keys = with wpcarro.keys; [
+      iphone
+      nathan
+      tarasco
+    ];
+  };
+  users.extraGroups.vboxusers.members = [ "wpcarro" ];
+
+  security.sudo.wheelNeedsPassword = false;
+
+  fonts = {
+    packages = with pkgs; [
+      jetbrains-mono
+    ];
+
+    fontconfig = {
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+
+  programs = wpcarro.common.programs // {
+    mosh.enable = true;
+  };
+
+  virtualisation.docker.enable = true;
+  virtualisation.virtualbox.host.enable = true;
+
+  environment.variables = {
+    EDITOR = "emacsclient";
+    ALTERNATE_EDITOR = "emacs -q -nw";
+    VISUAL = "emacsclient";
+  };
+
+  environment.systemPackages =
+    wpcarro.common.shell-utils ++
+    (with pkgs; [
+      alacritty
+      ec2-api-tools
+      firefox
+      google-chrome
+      httpie
+      pavucontrol
+      quasselClient
+      remmina
+      tdesktop
+      wpcarrosEmacs
+      xsecurelock
+    ]);
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/wpcarro/nixos/kyoko/kyoko.el b/users/wpcarro/nixos/kyoko/kyoko.el
new file mode 100644
index 0000000000..310323688a
--- /dev/null
+++ b/users/wpcarro/nixos/kyoko/kyoko.el
@@ -0,0 +1,61 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'bookmark)
+(require 'display)
+(require 'window-manager)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(bookmark-install-kbd
+ (make-bookmark :label "hadrian"
+                :path "/hadrian"
+                :kbd "h"))
+
+(setq initial-buffer-choice "/hadrian")
+
+(add-to-list 'ssh-hosts "wpcarro@tarasco")
+
+(display-register primary
+                  :output "DP-2"
+                  :primary t
+                  :coords (0 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-register secondary
+                  :output "DP-1"
+                  :primary nil
+                  :coords (2561 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-arrangement main :displays (primary secondary))
+
+(setq window-manager-named-workspaces
+      (list (make-window-manager-named-workspace
+             :label "Web Browsing"
+             :kbd "c"
+             :display display-secondary)
+            (make-window-manager-named-workspace
+             :label "Coding I"
+             :kbd "1"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Coding II"
+             :kbd "2"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Chatting"
+             :kbd "h"
+             :display display-secondary)))
+
+;; I *think* this needs to be the last statement in this file.
+(window-manager-init :init-hook #'display-arrange-main)
diff --git a/users/wpcarro/nixos/marcus/default.nix b/users/wpcarro/nixos/marcus/default.nix
new file mode 100644
index 0000000000..a97d6d264d
--- /dev/null
+++ b/users/wpcarro/nixos/marcus/default.nix
@@ -0,0 +1,169 @@
+{ depot, pkgs, lib, ... }:
+{ ... }:
+
+let
+  inherit (depot.users) wpcarro;
+  inherit (depot.users.wpcarro.lib) usermod;
+
+  wpcarrosEmacs = wpcarro.emacs.nixos {
+    load = [ ./marcus.el ];
+  };
+
+  quasselClient = pkgs.quassel.override {
+    client = true;
+    enableDaemon = false;
+    monolithic = false;
+  };
+in
+{
+  imports = [
+    (depot.path.origSrc + "/users/wpcarro/nixos/marcus/hardware.nix")
+    (pkgs.home-manager.src + "/nixos")
+    (usermod "laptop.nix")
+  ];
+
+  # Use the TVL binary cache
+  tvl.cache.enable = true;
+
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  networking = {
+    # The global useDHCP flag is deprecated, therefore explicitly set to false
+    # here.  Per-interface useDHCP will be mandatory in the future, so this
+    # generated config replicates the default behaviour.
+    useDHCP = false;
+    hostName = "marcus";
+    networkmanager.enable = true;
+    interfaces.enp0s31f6.useDHCP = true;
+    interfaces.wlp0s20f3.useDHCP = true;
+  };
+
+  services = wpcarro.common.services // {
+    # Enable the Tailscale daemon to connect to work and personal Tailnet.
+    tailscale.enable = true;
+
+    tzupdate.enable = true;
+
+    depot.auto-deploy = {
+      enable = true;
+      interval = "1d";
+    };
+
+    xserver = {
+      enable = true;
+      libinput = {
+        enable = true;
+        touchpad.naturalScrolling = false;
+        touchpad.tapping = false;
+      };
+      xkb.layout = "us";
+      xkb.options = "caps:escape";
+      displayManager = {
+        # Give EXWM permission to control the session (from tazjin's setup).
+        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localhost:$USER";
+        lightdm.enable = true;
+      };
+      extraConfig = ''
+        Section "InputClass"
+            Identifier "Touchscreen catchall"
+            MatchIsTouchscreen "on"
+            Option "Ignore" "on"
+        EndSection
+      '';
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${wpcarrosEmacs}/bin/wpcarros-emacs";
+      };
+    };
+  };
+
+  # Enable sound.
+  sound.enable = true;
+  hardware.pulseaudio.enable = true;
+
+  users.mutableUsers = true;
+  users.users.wpcarro = {
+    isNormalUser = true;
+    extraGroups = [
+      "networkmanager"
+      "wheel"
+      "video" # needed to control the screen brightness
+    ];
+    shell = pkgs.fish;
+  };
+
+  security.sudo.wheelNeedsPassword = false;
+
+  fonts = {
+    packages = with pkgs; [
+      jetbrains-mono
+    ];
+
+    fontconfig = {
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+
+  programs = wpcarro.common.programs;
+
+  environment.variables = {
+    EDITOR = "emacsclient";
+    ALTERNATE_EDITOR = "emacs -q -nw";
+    VISUAL = "emacsclient";
+  };
+
+  home-manager.useGlobalPkgs = true;
+  home-manager.users.wpcarro = { config, lib, ... }: {
+    programs.git = {
+      enable = true;
+      userName = "William Carroll";
+      userEmail = "wpcarro@gmail.com";
+      extraConfig = {
+        pull.rebase = true;
+      };
+    };
+
+    services.picom = {
+      enable = true;
+      vSync = true;
+      backend = "glx";
+    };
+
+    services.redshift = {
+      enable = true;
+      latitude = 37.4223931;
+      longitude = -122.0864016;
+    };
+
+    services.dunst.enable = true;
+    xdg.configFile."dunst/dunstrc" = {
+      source = wpcarro.dotfiles.dunstrc;
+      onChange = ''
+        ${pkgs.procps}/bin/pkill -u "$USER" ''${VERBOSE+-e} dunst || true
+      '';
+    };
+
+    systemd.user.startServices = true;
+
+    # Previous default version, see https://github.com/nix-community/home-manager/blob/master/docs/release-notes/rl-2211.adoc
+    home.stateVersion = "18.09";
+  };
+
+  environment.systemPackages =
+    wpcarro.common.shell-utils ++
+    (with pkgs; [
+      alacritty
+      firefox
+      pavucontrol
+      quasselClient
+      tdesktop
+      weechat
+      wpcarrosEmacs
+      xsecurelock
+    ]);
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/wpcarro/nixos/marcus/hardware.nix b/users/wpcarro/nixos/marcus/hardware.nix
new file mode 100644
index 0000000000..8a2672206b
--- /dev/null
+++ b/users/wpcarro/nixos/marcus/hardware.nix
@@ -0,0 +1,29 @@
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+  ];
+
+  boot.initrd.availableKernelModules = [ "xhci_pci" "nvme" "usb_storage" "sd_mod" ];
+  boot.initrd.kernelModules = [ ];
+  boot.kernelModules = [ "kvm-intel" ];
+  boot.extraModulePackages = [ ];
+
+  fileSystems."/" = {
+    device = "/dev/disk/by-label/nixos";
+    fsType = "ext4";
+  };
+
+  fileSystems."/boot" = {
+    device = "/dev/disk/by-label/boot";
+    fsType = "vfat";
+  };
+
+  swapDevices = lib.singleton {
+    device = "/dev/disk/by-label/swap";
+  };
+
+  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
+  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+}
diff --git a/users/wpcarro/nixos/marcus/marcus.el b/users/wpcarro/nixos/marcus/marcus.el
new file mode 100644
index 0000000000..90c04f7ff3
--- /dev/null
+++ b/users/wpcarro/nixos/marcus/marcus.el
@@ -0,0 +1,40 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'tvl)
+(require 'display)
+(require 'window-manager)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Monitor Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(display-register laptop
+                  :output "eDP-1"
+                  :primary t
+                  :coords (0 0)
+                  :size (1920 1080)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-arrangement primary :displays (laptop))
+
+(setq initial-buffer-choice tvl-depot-path)
+
+(setq window-manager-named-workspaces
+      (list (make-window-manager-named-workspace
+             :label "Web Browsing"
+             :kbd "c"
+             :display display-laptop)
+            (make-window-manager-named-workspace
+             :label "Coding"
+             :kbd "d"
+             :display display-laptop)
+            (make-window-manager-named-workspace
+             :label "Chatting"
+             :kbd "h"
+             :display display-laptop)))
+
+(window-manager-init :init-hook #'display-arrange-primary)
diff --git a/users/wpcarro/nixos/modules/.skip-subtree b/users/wpcarro/nixos/modules/.skip-subtree
new file mode 100644
index 0000000000..09520f8c83
--- /dev/null
+++ b/users/wpcarro/nixos/modules/.skip-subtree
@@ -0,0 +1 @@
+NixOS modules are not readTree compatible.
diff --git a/users/wpcarro/nixos/modules/hadrian-cache.nix b/users/wpcarro/nixos/modules/hadrian-cache.nix
new file mode 100644
index 0000000000..033c03c825
--- /dev/null
+++ b/users/wpcarro/nixos/modules/hadrian-cache.nix
@@ -0,0 +1,17 @@
+# If enabled, use Hadrian's Nix cache.
+{ config, lib, pkgs, ... }:
+
+{
+  options = {
+    hadrian.cache.enable = lib.mkEnableOption "Hadrian's binary cache";
+  };
+
+  config = lib.mkIf config.hadrian.cache.enable {
+    nix.settings.trusted-public-keys = [
+      "cache.hadrian.internal:XWdYSn5ZASj6IqZd4nnDBXJmahQEolBrtq9DvSe0UT0="
+    ];
+    nix.settings.substituters = [
+      "http://cache.hadrian.internal"
+    ];
+  };
+}
diff --git a/users/wpcarro/nixos/modules/hardware/dell-emc-egw-5200.nix b/users/wpcarro/nixos/modules/hardware/dell-emc-egw-5200.nix
new file mode 100644
index 0000000000..df46405629
--- /dev/null
+++ b/users/wpcarro/nixos/modules/hardware/dell-emc-egw-5200.nix
@@ -0,0 +1,47 @@
+# In a nutshell, this configuration defines the configuration required to run
+# NixOS on the Dell EMC EGW 5200 (often the config that NixOS put in
+# hardware.nix by default).
+{ config, lib, modulesPath, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+  ];
+
+  boot.initrd.availableKernelModules = [
+    "xhci_pci"
+    "ahci"
+    "usb_storage"
+    "usbhid"
+    "sd_mod"
+  ];
+  boot.initrd.kernelModules = [ ];
+  boot.kernelModules = [ "kvm-intel" ];
+  boot.extraModulePackages = [ ];
+  boot.loader.systemd-boot.enable = true;
+  boot.loader.efi.canTouchEfiVariables = true;
+
+  fileSystems."/" = {
+    device = "/dev/disk/by-label/NIXROOT";
+    fsType = "ext4";
+  };
+
+  fileSystems."/boot" = {
+    device = "/dev/disk/by-label/NIXBOOT";
+    fsType = "vfat";
+  };
+
+  swapDevices = [ ];
+
+  powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
+  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+
+  # Needed for Tailscale subnet routing
+  boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
+  networking.useDHCP = false;
+  networking.interfaces.eno1.useDHCP = true;
+  networking.interfaces.enp3s0.useDHCP = true;
+  networking.interfaces.enp4s0.useDHCP = true;
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/wpcarro/nixos/modules/hardware/nopn.nix b/users/wpcarro/nixos/modules/hardware/nopn.nix
new file mode 100644
index 0000000000..a356954212
--- /dev/null
+++ b/users/wpcarro/nixos/modules/hardware/nopn.nix
@@ -0,0 +1,53 @@
+# I tried looking up the manufacturer, product name, and version, but
+# `dmidecode -t system` reported "To be filled by O.E.M." for each of these
+# fields.
+{ config, lib, pkgs, modulesPath, ... }:
+
+{
+  imports = [
+    (modulesPath + "/installer/scan/not-detected.nix")
+  ];
+
+  fileSystems."/" = {
+    device = "/dev/disk/by-label/NIXROOT";
+    fsType = "ext4";
+  };
+
+  fileSystems."/boot" = {
+    device = "/dev/disk/by-label/NIXBOOT";
+    fsType = "vfat";
+  };
+
+  boot = {
+    initrd.availableKernelModules = [
+      "xhci_pci"
+      "ehci_pci"
+      "ahci"
+      "usb_storage"
+      "usbhid"
+      "sd_mod"
+    ];
+    initrd.kernelModules = [ ];
+    kernelModules = [ "kvm-intel" ];
+    extraModulePackages = [ ];
+
+    # Can verify these settings with:
+    # $ lsmod
+    # ...or:
+    # $ cat /etc/modprobe.d/nixos.conf
+    blacklistedKernelModules = [
+      # Disabling this buggy network driver (and preferring ethernet) to prevent
+      # my machine from becoming unresponsive.
+      # TODO(wpcarro): Consider replacing this module with this fork (if NixOS
+      # isn't already): https://github.com/tomaspinho/rtl8821ce
+      "rtw88_8821ce"
+    ];
+  };
+
+  swapDevices = [ ];
+
+  hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
+  # TODO(wpcarro): https://github.com/NixOS/nixpkgs/issues/222805
+  # high-resolution display
+  # hardware.video.hidpi.enable = lib.mkDefault true;
+}
diff --git a/users/wpcarro/nixos/modules/laptop.nix b/users/wpcarro/nixos/modules/laptop.nix
new file mode 100644
index 0000000000..03dd0f39bb
--- /dev/null
+++ b/users/wpcarro/nixos/modules/laptop.nix
@@ -0,0 +1,15 @@
+# Laptop-specific NixOS configuration.
+_:
+
+{
+  # Automatically detect location for redshift.
+  services.geoclue2.enable = true;
+  location.provider = "geoclue2";
+
+  # Enable power-saving features.
+  powerManagement.powertop.enable = true;
+
+  # Backlight control command.
+  programs.light.enable = true;
+}
+
diff --git a/users/wpcarro/nixos/modules/nginx.nix b/users/wpcarro/nixos/modules/nginx.nix
new file mode 100644
index 0000000000..e6cc6b0feb
--- /dev/null
+++ b/users/wpcarro/nixos/modules/nginx.nix
@@ -0,0 +1,45 @@
+# Common configuration for Nginx.
+{ pkgs, ... }:
+
+{
+  config = {
+    security.acme = {
+      acceptTerms = true;
+      defaults.email = "wpcarro@gmail.com";
+    };
+
+    services.nginx = {
+      enable = true;
+      enableReload = true;
+
+      recommendedTlsSettings = true;
+      recommendedGzipSettings = true;
+
+      # Log errors to journald (i.e. /dev/log) with debug verbosity.
+      logError = "syslog:server=unix:/dev/log debug";
+
+      # for journaldriver
+      commonHttpConfig = ''
+        log_format json_combined escape=json
+        '{'
+            '"remote_addr":"$remote_addr",'
+            '"method":"$request_method",'
+            '"host":"$host",'
+            '"uri":"$request_uri",'
+            '"status":$status,'
+            '"request_size":$request_length,'
+            '"response_size":$body_bytes_sent,'
+            '"response_time":$request_time,'
+            '"referrer":"$http_referer",'
+            '"user_agent":"$http_user_agent"'
+        '}';
+
+        access_log syslog:server=unix:/dev/log,nohostname json_combined;
+      '';
+
+      appendHttpConfig = ''
+        add_header Permissions-Policy "interest-cohort=()";
+      '';
+    };
+  };
+}
diff --git a/users/wpcarro/nixos/tarasco/default.nix b/users/wpcarro/nixos/tarasco/default.nix
new file mode 100644
index 0000000000..7033caa11a
--- /dev/null
+++ b/users/wpcarro/nixos/tarasco/default.nix
@@ -0,0 +1,144 @@
+{ depot, pkgs, lib, ... }:
+{ ... }:
+
+let
+  inherit (depot.users) wpcarro;
+  inherit (depot.users.wpcarro.lib) usermod;
+
+  wpcarrosEmacs = wpcarro.emacs.nixos {
+    load = [ ./tarasco.el ];
+  };
+
+  quasselClient = pkgs.quassel.override {
+    client = true;
+    enableDaemon = false;
+    monolithic = false;
+  };
+in
+{
+  imports = [
+    (usermod "hardware/nopn.nix")
+  ];
+
+  # Use the TVL binary cache
+  tvl.cache.enable = true;
+
+  boot = {
+    loader.systemd-boot.enable = true;
+    loader.efi.canTouchEfiVariables = true;
+
+    # Support IP forwarding to use this device as a Tailscale exit node.
+    kernel.sysctl."net.ipv4.ip_forward" = true;
+    kernel.sysctl."net.ipv6.conf.all.forwarding" = true;
+  };
+
+
+  time.timeZone = "America/Los_Angeles";
+
+  networking = {
+    useDHCP = false;
+    hostName = "tarasco";
+    networkmanager.enable = true;
+    interfaces.enp1s0.useDHCP = true;
+    interfaces.enp3s0.useDHCP = true;
+    firewall.checkReversePath = "loose";
+    # Disabling wifi because the Realtek network card drivers crash. For more
+    # context, see the boot.blacklistedKernelModules configuration.
+    # interfaces.wlp2s0.useDHCP = true;
+  };
+
+  services = wpcarro.common.services // {
+    # Check the amount of available memory and free swap a few times per second
+    # and kill the largest process if both are below 10%.
+    earlyoom.enable = true;
+
+    tailscale.enable = true;
+
+    openssh.enable = true;
+
+    xserver = {
+      enable = true;
+      xkb.layout = "us";
+      xkb.options = "caps:escape";
+      displayManager = {
+        # Give EXWM permission to control the session (from tazjin's setup).
+        sessionCommands = "${pkgs.xorg.xhost}/bin/xhost +SI:localhost:$USER";
+        lightdm.enable = true;
+      };
+      windowManager.session = lib.singleton {
+        name = "exwm";
+        start = "${wpcarrosEmacs}/bin/wpcarros-emacs";
+      };
+    };
+  };
+
+  # Enable sound.
+  sound.enable = true;
+  hardware.pulseaudio.enable = true;
+
+  users.mutableUsers = true;
+  users.users.root.openssh.authorizedKeys.keys = with wpcarro.keys; [
+    ava
+    iphone
+    nathan
+  ];
+  users.users.wpcarro = {
+    isNormalUser = true;
+    extraGroups = [
+      "networkmanager"
+      "wheel"
+      "docker"
+    ];
+    shell = pkgs.fish;
+    openssh.authorizedKeys.keys = with wpcarro.keys; [
+      ava
+      iphone
+      nathan
+    ];
+  };
+  users.extraGroups.vboxusers.members = [ "wpcarro" ];
+
+  security.sudo.wheelNeedsPassword = false;
+
+  fonts = {
+    packages = with pkgs; [
+      jetbrains-mono
+    ];
+
+    fontconfig = {
+      defaultFonts = {
+        monospace = [ "JetBrains Mono" ];
+      };
+    };
+  };
+
+  programs = wpcarro.common.programs // {
+    mosh.enable = true;
+  };
+
+  virtualisation.docker.enable = true;
+  virtualisation.virtualbox.host.enable = true;
+
+  environment.variables = {
+    EDITOR = "emacsclient";
+    ALTERNATE_EDITOR = "emacs -q -nw";
+    VISUAL = "emacsclient";
+  };
+
+  environment.systemPackages =
+    wpcarro.common.shell-utils ++
+    (with pkgs; [
+      alacritty
+      firefox
+      google-chrome
+      httpie
+      pavucontrol
+      quasselClient
+      remmina
+      tdesktop
+      wpcarrosEmacs
+      xsecurelock
+    ]);
+
+  system.stateVersion = "21.11";
+}
diff --git a/users/wpcarro/nixos/tarasco/tarasco.el b/users/wpcarro/nixos/tarasco/tarasco.el
new file mode 100644
index 0000000000..c840493f24
--- /dev/null
+++ b/users/wpcarro/nixos/tarasco/tarasco.el
@@ -0,0 +1,61 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Dependencies
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(require 'bookmark)
+(require 'display)
+(require 'window-manager)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Configuration
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(bookmark-install-kbd
+ (make-bookmark :label "hadrian"
+                :path "/hadrian"
+                :kbd "h"))
+
+(setq initial-buffer-choice "/hadrian")
+
+(add-to-list 'ssh-hosts "wpcarro@ava")
+
+(display-register primary
+                  :output "HDMI-1"
+                  :primary t
+                  :coords (0 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-register secondary
+                  :output "HDMI-2"
+                  :primary nil
+                  :coords (2561 0)
+                  :size (2560 1440)
+                  :rate 30.0
+                  :dpi 96
+                  :rotate normal)
+
+(display-arrangement main :displays (primary secondary))
+
+(setq window-manager-named-workspaces
+      (list (make-window-manager-named-workspace
+             :label "Web Browsing"
+             :kbd "c"
+             :display display-secondary)
+            (make-window-manager-named-workspace
+             :label "Coding I"
+             :kbd "1"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Coding II"
+             :kbd "2"
+             :display display-primary)
+            (make-window-manager-named-workspace
+             :label "Chatting"
+             :kbd "h"
+             :display display-secondary)))
+
+;; I *think* this needs to be the last statement in this file.
+(window-manager-init :init-hook #'display-arrange-main)
diff --git a/users/wpcarro/playbooks/README.md b/users/wpcarro/playbooks/README.md
new file mode 100644
index 0000000000..70a26c8e89
--- /dev/null
+++ b/users/wpcarro/playbooks/README.md
@@ -0,0 +1,3 @@
+# playbooks
+
+Here's the vision: playbooks for everything - not just software.
diff --git a/users/wpcarro/playbooks/first-of-the-month.org b/users/wpcarro/playbooks/first-of-the-month.org
new file mode 100644
index 0000000000..7bce39ca76
--- /dev/null
+++ b/users/wpcarro/playbooks/first-of-the-month.org
@@ -0,0 +1,12 @@
+# In total this should take one hour to complete. This is a substantial amount
+# of time, which may disincentivize me from completing it. This time is
+# amortized over the length of its usefulness (i.e. an entire month), so it
+# should be thought of instead as two-minutes worth of work per day that is all
+# being completed upfront.
+* Tasks
+** TODO [10m] create habit template in journal
+** TODO [30m] assess previous month's performance
+** TODO [10m] book massage for the month
+** TODO [10m] create go/hallpass entries (BJJ, VHP)
+** TODO [10m] expense home internet
+** TODO [10m] buy TSLA through tdameritrade.com
diff --git a/users/wpcarro/playbooks/habits.org b/users/wpcarro/playbooks/habits.org
new file mode 100644
index 0000000000..aac63735d9
--- /dev/null
+++ b/users/wpcarro/playbooks/habits.org
@@ -0,0 +1,49 @@
+* First of the year
+** [1hr] Write a post mortem for the previous year
+* First of the month
+** see ./first-of-the-month.org
+* Payday
+** [10m] Audit Monzo expenses
+** [05m] Review "finances_2020" spreadsheet
+** [05m] Transfer GBP to USD account
+** [10m] Withdraw cash from ATM
+* Morning
+** [00m] Wake up at 7:00
+** [15m] Read
+** [02m] Brush teeth
+** [01m] Make bed
+** [01m] Water plants
+** [10m] 12 rounds of forward folds
+** [05m] 12 rounds Pranayama
+** [30m] Transcendental meditation
+** [10m] Shower
+** [05m] Put on clothes
+* Evening
+** [01m] Layout tomorrow's outfit
+** [01m] Floss
+** [02m] Brush teeth
+** [01m] Mouth wash
+** [30m] Read
+** [01m] Journal daily progress
+* Monday
+** [1hr] Jiu Jitsu
+* Tuesday
+** Work from 6PS
+** [1hr] Jiu Jitsu
+* Wednesday
+** [1hr] Hot Yoga
+** [10m] Shave
+** [15m] Clean apartment sinks
+* Thursday
+* Friday
+** [1hr] Hot Yoga
+* Saturday
+** [10m] Vacuum
+** [30m] Nap
+* Sunday
+** [1hr] Jiu Jitsu
+** [30m] Nap
+** [10m] Shave
+** [05m] Trim nails
+** [05m] Take out trash
+** [05m] Laundry
diff --git a/users/wpcarro/playbooks/hip_opening_challenge/poses.pdf b/users/wpcarro/playbooks/hip_opening_challenge/poses.pdf
new file mode 100644
index 0000000000..d292ef832c
--- /dev/null
+++ b/users/wpcarro/playbooks/hip_opening_challenge/poses.pdf
Binary files differdiff --git a/users/wpcarro/playbooks/hip_opening_challenge/progress.org b/users/wpcarro/playbooks/hip_opening_challenge/progress.org
new file mode 100644
index 0000000000..80749a3c6b
--- /dev/null
+++ b/users/wpcarro/playbooks/hip_opening_challenge/progress.org
@@ -0,0 +1,65 @@
+# From Lucas Rockwood's 21-day hip challenge from yogabody.com
+* DONE day 1
+** pigeon
+** butterfly
+* DONE day 2
+** blaster
+** squat
+* DONE day 3
+** happy baby
+** thread the needle (supine)
+* DONE day 4
+** frog
+** jackknife blaster
+* DONE day 5
+** lightning bolt
+** scissors
+* DONE day 6
+** zorro
+** supine butterfly (w/ strap)
+* TODO day 7
+** thread the needle (wall)
+** prone butterfly
+* TODO day 8
+** ninja squat
+** chair scissors
+** lateral chain stretch
+* TODO day 9
+** psoas blaster (chair)
+** reclined scissors
+* DONE day 10
+** twisted blaster
+** twisted squat
+* TODO day 11
+** double pigeon
+** bound butterfly
+* TODO day 12
+** eagle fold
+** cross-thread
+* DONE day 13
+** swiss army knife
+** saddle
+* TODO day 14
+** butterfly squat
+** half lightning bolt
+* DONE day 15
+** fallen blaster
+** asymmetric baby
+* DONE day 16
+** standing psoas
+** standing pigeon
+* TODO day 17
+** marichi B
+** long butterfly
+* TODO day 18
+** eagle legs
+** chair squat
+* DONE day 19
+** twisted pigeon
+** bound baby
+* DONE day 20
+** seated pigeon
+** railroad squat
+* DONE day 21
+** thunderbolt
+** yogi squat
diff --git a/users/wpcarro/playbooks/nix_gcr/README.md b/users/wpcarro/playbooks/nix_gcr/README.md
new file mode 100644
index 0000000000..9d111cf6bb
--- /dev/null
+++ b/users/wpcarro/playbooks/nix_gcr/README.md
@@ -0,0 +1,62 @@
+# Nix + Google Cloud Run (i.e. GCR)
+
+I'm documenting how I currently deploy projects that I package with Nix on
+Google Cloud Run.
+
+I'd like to automate this workflow as much as possible, and I intend to do just
+that. For now, I'm running things manually until I can design an generalization
+that appeals to me.
+
+## Dependencies
+- `nix-build`
+- `docker`
+- `gcloud`
+
+## Step-by-step
+
+1. Use `nix-build` to create our Docker image for Cloud Run.
+
+```shell
+> nix-build ./cloud_run.nix
+```
+
+This outputs a Docker image at `./result`.
+
+1. Load the built image (i.e. `./result`) into `docker` so that we can tag it
+   and push it to the Google Container Registry (i.e. GCR).
+
+```shell
+> sudo docker load <./result
+```
+
+1. (Optionally) Run the image locally to verify its integrity.
+
+```shell
+> sudo docker run -d -p 8080:4242 <name>:<tag>
+```
+
+1. Tag and push the image to GCR.
+
+```shell
+> sudo docker tag <name>:<label> gcr.io/<google-cloud-project-id>/<name>:<latest>
+```
+
+1. Visit Google Cloud Run; create a new service with "Create Service"; select
+   the uploaded Docker image from the "Container Image URL" field; click
+   "Create" to deploy.
+
+## Notes
+
+You may need to authorize `gcloud` by running the following:
+
+```shell
+> sudo gcloud auth login --no-launch-browser
+```
+
+You must use `sudo` here since the `docker` invocations are prefixed with `sudo`
+as well.
+
+## Todos
+
+- If possible, prefer using a command line tool like `gcloud` to create the
+  Cloud Run service.
diff --git a/users/wpcarro/playbooks/nix_gcr/cloud_run.nix b/users/wpcarro/playbooks/nix_gcr/cloud_run.nix
new file mode 100644
index 0000000000..1f473b5f59
--- /dev/null
+++ b/users/wpcarro/playbooks/nix_gcr/cloud_run.nix
@@ -0,0 +1,14 @@
+{ pkgs, depot, ... }:
+
+pkgs.dockerTools.buildLayeredImage {
+  name = "gemma";
+  tag = "latest";
+  config.ExposedPorts = {
+    "4242" = { };
+  };
+  config.Env = [
+    "GEMMA_CONFIG=${./config.lisp}"
+  ];
+  config.Cmd = [ "${depot.fun.gemma}/bin/gemma" ];
+  maxLayers = 120;
+}
diff --git a/users/wpcarro/playbooks/nix_gcr/config.lisp b/users/wpcarro/playbooks/nix_gcr/config.lisp
new file mode 100644
index 0000000000..54f8e5f344
--- /dev/null
+++ b/users/wpcarro/playbooks/nix_gcr/config.lisp
@@ -0,0 +1,21 @@
+;; Example configuration file for Gemma
+
+(config :port 4242
+        :data-dir "/tmp/gemma/")
+
+(deftask bathroom/wipe-mirror 7)
+(deftask bathroom/wipe-counter 7)
+
+;; Bedroom tasks
+(deftask bedroom/change-sheets 7)
+(deftask bedroom/vacuum 10)
+
+;; Kitchen tasks
+(deftask kitchen/normal-trash 3)
+(deftask kitchen/green-trash 5)
+(deftask kitchen/blue-trash 5)
+(deftask kitchen/wipe-counters 3)
+(deftask kitchen/vacuum 5 "Kitchen has more crumbs and such!")
+
+;; Entire place
+(deftask clean-windows 60)
diff --git a/users/wpcarro/playbooks/shell.md b/users/wpcarro/playbooks/shell.md
new file mode 100644
index 0000000000..5eda417f48
--- /dev/null
+++ b/users/wpcarro/playbooks/shell.md
@@ -0,0 +1,12 @@
+# Shell
+
+I'm making this as an offline reference for some of the commands that I use
+often enough to need to remember but not often enough to *actually* remember.
+
+## Reference
+
+- To kill a process by its port number:
+
+```shell
+$ fuser 8080/tcp
+```
diff --git a/users/wpcarro/playbooks/sqlite3.md b/users/wpcarro/playbooks/sqlite3.md
new file mode 100644
index 0000000000..aec87f0b59
--- /dev/null
+++ b/users/wpcarro/playbooks/sqlite3.md
@@ -0,0 +1,115 @@
+# SQLite3
+
+Creating a reference for SQLite that I can access when I'm offline
+(e.g. traveling in an airplane).
+
+## Benefits
+
+I enjoy using SQLite because it's lightweight and simple. Instead of networking
+microservices, I can oftentimes just create a simple `db.sqlite3` file and get
+significant mileage without much tooling overhead.
+
+## Limitations
+
+SQLite has some limitations; here are some of the limitations that I have encountered.
+
+- SQLite **disables** support for `FOREIGN KEY` by default. Enable it with:
+
+```
+sqlite> PRAGMA foreign_keys = ON;
+```
+
+- SQLite has no `BOOLEAN` type; it uses 0 and 1 instead.
+
+```
+sqlite> SELECT TRUE;
+TRUE
+----------
+1
+sqlite> SELECT FALSE;
+FALSE
+----------
+0
+```
+
+- SQLite has no `DATETIME` type; it uses `TEXT` instead.
+
+```
+sqlite> SELECT datetime('now');
+datetime('now')
+-------------------
+2020-07-26 09:52:32
+```
+
+## Reference
+
+The following should serve as a useful reference for working with SQLite.
+
+### Schema
+
+```sql
+CREATE TABLE IF NOT EXISTS Movies (
+  title TEXT NOT NULL,
+  year INTEGER,
+  PRIMARY KEY (title)
+);
+
+ALTER TABLE Movies ADD COLUMN rating DEFAULT 0.0;
+
+DROP TABLE Movies;
+```
+
+### Queries
+
+The following queries should come in handy as a reference:
+
+```
+sqlite> -- I'm using an intentionally incorrect date here for the subsequent UPDATE.
+sqlite> INSERT INTO Movies (title, year) VALUES ('Toy Story 3', 2100);
+sqlite> SELECT * FROM Movies WHERE year IS NULL;
+sqlite> UPDATE Movies SET year = 2010 WHERE title = 'Toy Story 3';
+sqlite> -- % is like .* in a regex
+sqlite> DELETE FROM Movies WHERE title LIKE 'Toy Story%';
+```
+
+## Command Line
+
+- Create a `~/.sqliterc` file with the following contents:
+
+```
+.mode column
+.headers on
+```
+
+- To start an interactive session:
+
+```shell
+$ sqlite3 db.sqlite3
+```
+
+- To create a SQLite database from a `.sql` file:
+
+```shell
+$ sqlite3 db.sqlite3 <db.sql
+```
+
+- To reload changes to a `.sql` file while in an interactive session:
+
+```
+sqlite> .read db.sql
+```
+
+## Miscellaneous
+
+- For a web-browser-based SQLite viewer, run the following:
+
+```shell
+$ sqlite_web db.sqlite3
+```
+
+- To import a CSV:
+
+```
+sqlite> .mode csv <table-name>
+sqlite> .import path/to/file.csv <table-name>
+```
diff --git a/users/wpcarro/scratch/README.md b/users/wpcarro/scratch/README.md
new file mode 100644
index 0000000000..8259ac70d9
--- /dev/null
+++ b/users/wpcarro/scratch/README.md
@@ -0,0 +1,6 @@
+# Scratch
+
+The purpose of the `scratch` directory is to host practice exercises. Practice
+encompasses things like working on data structures and algorithms problems for
+upcoming coding interviews or general aptitude as well as writing code snippets
+to help me learn a new programming language or understand an unfamiliar concept.
diff --git a/users/wpcarro/scratch/advent-of-code-2019/README.md b/users/wpcarro/scratch/advent-of-code-2019/README.md
new file mode 100644
index 0000000000..e7c105a7f6
--- /dev/null
+++ b/users/wpcarro/scratch/advent-of-code-2019/README.md
@@ -0,0 +1,4 @@
+# 2019 Advent of Code
+
+Here are my attempts at the 2019 Advent of Code challenge before my dedication
+to the effort plummeted.
diff --git a/users/wpcarro/scratch/advent-of-code-2019/day_1.py b/users/wpcarro/scratch/advent-of-code-2019/day_1.py
new file mode 100644
index 0000000000..bd4024e3ec
--- /dev/null
+++ b/users/wpcarro/scratch/advent-of-code-2019/day_1.py
@@ -0,0 +1,119 @@
+from math import floor
+
+xs = [
+    102473,
+    84495,
+    98490,
+    68860,
+    62204,
+    72810,
+    65185,
+    145951,
+    77892,
+    108861,
+    70764,
+    67286,
+    74002,
+    80773,
+    52442,
+    131505,
+    107162,
+    126993,
+    59784,
+    64231,
+    91564,
+    68585,
+    98735,
+    69020,
+    77332,
+    60445,
+    65826,
+    111506,
+    95431,
+    146687,
+    135119,
+    86804,
+    95915,
+    85434,
+    111303,
+    148127,
+    132921,
+    136213,
+    89004,
+    143137,
+    144853,
+    143017,
+    104386,
+    100612,
+    54760,
+    63813,
+    144191,
+    84481,
+    69718,
+    84936,
+    98621,
+    124993,
+    92736,
+    60369,
+    137284,
+    101902,
+    112726,
+    51784,
+    126496,
+    85005,
+    101661,
+    137278,
+    136637,
+    90340,
+    100209,
+    53683,
+    50222,
+    132060,
+    98797,
+    139054,
+    135638,
+    100632,
+    137849,
+    125333,
+    103981,
+    76954,
+    134352,
+    74229,
+    93402,
+    62552,
+    50286,
+    57066,
+    98439,
+    120708,
+    117827,
+    107884,
+    72837,
+    148663,
+    125645,
+    61460,
+    120555,
+    142473,
+    106668,
+    58612,
+    58576,
+    143366,
+    90058,
+    121087,
+    89546,
+    126161,
+]
+
+
+def fuel_for_mass(x):
+    """Return the amount of fuel (in mass) required for a mass of X. The total
+    amount of fuel includes the amount of fuel required for the fuel itself,
+    since fuel also has a mass weights."""
+    mass_fuel = floor(x / 3) - 2
+    if mass_fuel < 0:
+        return 0
+    else:
+        fuel_fuel = fuel_for_mass(mass_fuel)
+        return mass_fuel + fuel_fuel
+
+
+print(sum(fuel_for_mass(x) for x in xs))
diff --git a/users/wpcarro/scratch/advent-of-code-2019/day_2.py b/users/wpcarro/scratch/advent-of-code-2019/day_2.py
new file mode 100644
index 0000000000..77774c1bb5
--- /dev/null
+++ b/users/wpcarro/scratch/advent-of-code-2019/day_2.py
@@ -0,0 +1,32 @@
+from itertools import product
+
+x = [
+    1, 0, 0, 3, 1, 1, 2, 3, 1, 3, 4, 3, 1, 5, 0, 3, 2, 1, 10, 19, 1, 6, 19, 23,
+    2, 23, 6, 27, 2, 6, 27, 31, 2, 13, 31, 35, 1, 10, 35, 39, 2, 39, 13, 43, 1,
+    43, 13, 47, 1, 6, 47, 51, 1, 10, 51, 55, 2, 55, 6, 59, 1, 5, 59, 63, 2, 9,
+    63, 67, 1, 6, 67, 71, 2, 9, 71, 75, 1, 6, 75, 79, 2, 79, 13, 83, 1, 83, 10,
+    87, 1, 13, 87, 91, 1, 91, 10, 95, 2, 9, 95, 99, 1, 5, 99, 103, 2, 10, 103,
+    107, 1, 107, 2, 111, 1, 111, 5, 0, 99, 2, 14, 0, 0
+]
+
+
+def interpret(i, x):
+    op, a, b, out = x[i + 0], x[i + 1], x[i + 2], x[i + 3]
+    if op == 1:
+        x[out] = x[a] + x[b]
+        return interpret(i + 4, x)
+    elif op == 2:
+        x[out] = x[a] * x[b]
+        return interpret(i + 4, x)
+    elif op == 99:
+        return x
+    else:
+        raise Exception('Unsupported opcode: {}. {}, {}'.format(op, a, b))
+
+
+for a, b in product(range(100), range(100)):
+    y = x[:]
+    y[1] = a
+    y[2] = b
+    if interpret(0, y)[0] == 19690720:
+        print(100 * a + b)
diff --git a/users/wpcarro/scratch/advent-of-code-2019/day_3.py b/users/wpcarro/scratch/advent-of-code-2019/day_3.py
new file mode 100644
index 0000000000..6dd863528c
--- /dev/null
+++ b/users/wpcarro/scratch/advent-of-code-2019/day_3.py
@@ -0,0 +1,137 @@
+from math import floor
+from heapq import heappush, heappop
+
+xs = [
+    "R1009", "U993", "L383", "D725", "R163", "D312", "R339", "U650", "R558",
+    "U384", "R329", "D61", "L172", "D555", "R160", "D972", "L550", "D801",
+    "L965", "U818", "L123", "D530", "R176", "D353", "L25", "U694", "L339",
+    "U600", "L681", "D37", "R149", "D742", "R762", "U869", "R826", "U300",
+    "L949", "U978", "L303", "U361", "R136", "D343", "L909", "U551", "R745",
+    "U913", "L566", "D292", "R820", "U886", "R205", "D431", "L93", "D71",
+    "R577", "U872", "L705", "U510", "L698", "U963", "R607", "U527", "L669",
+    "D543", "R690", "U954", "L929", "D218", "R490", "U500", "L589", "D332",
+    "R949", "D538", "R696", "U659", "L188", "U468", "L939", "U833", "L445",
+    "D430", "R78", "D303", "R130", "D649", "R849", "D712", "L511", "U745",
+    "R51", "U973", "R799", "U829", "R605", "D771", "L837", "U204", "L414",
+    "D427", "R538", "U116", "R540", "D168", "R493", "U900", "L679", "U431",
+    "L521", "D500", "L428", "U332", "L954", "U717", "L853", "D339", "L88",
+    "U807", "L607", "D496", "L163", "U468", "L25", "U267", "L759", "D898",
+    "L591", "U445", "L469", "U531", "R596", "D486", "L728", "D677", "R350",
+    "D429", "R39", "U568", "R92", "D875", "L835", "D841", "R877", "U178",
+    "L221", "U88", "R592", "U692", "R455", "U693", "L419", "U90", "R609",
+    "U672", "L293", "U168", "R175", "D456", "R319", "D570", "R504", "D165",
+    "L232", "D624", "L604", "D68", "R807", "D59", "R320", "D281", "L371",
+    "U956", "L788", "D897", "L231", "D829", "R287", "D798", "L443", "U194",
+    "R513", "D925", "L232", "U225", "L919", "U563", "R448", "D889", "R661",
+    "U852", "L950", "D558", "L269", "U186", "L625", "U673", "L995", "U732",
+    "R435", "U849", "L413", "D690", "L158", "D234", "R361", "D458", "L271",
+    "U90", "L781", "U754", "R256", "U162", "L842", "U927", "L144", "D62",
+    "R928", "D238", "R473", "U97", "L745", "U303", "L487", "D349", "L520",
+    "D31", "L825", "U385", "L133", "D948", "L39", "U62", "R801", "D664",
+    "L333", "U134", "R692", "U385", "L658", "U202", "L279", "D374", "R489",
+    "D686", "L182", "U222", "R733", "U177", "R94", "D603", "L376", "U901",
+    "R216", "D851", "L155", "D214", "L460", "U758", "R121", "D746", "L180",
+    "U175", "L943", "U146", "L166", "D251", "L238", "U168", "L642", "D341",
+    "R281", "U182", "R539", "D416", "R553", "D67", "L748", "U272", "R257",
+    "D869", "L340", "U180", "R791", "U138", "L755", "D976", "R731", "U713",
+    "R602", "D284", "L258", "U176", "R509", "U46", "R935", "U576", "R96",
+    "U89", "L913", "U703", "R833"
+]
+ys = [
+    "L1006", "D998", "R94", "D841", "R911", "D381", "R532", "U836", "L299",
+    "U237", "R781", "D597", "L399", "D800", "L775", "D405", "L485", "U636",
+    "R589", "D942", "L878", "D779", "L751", "U711", "L973", "U410", "L151",
+    "U15", "L685", "U417", "L106", "D648", "L105", "D461", "R448", "D743",
+    "L589", "D430", "R883", "U37", "R155", "U350", "L421", "U23", "R337",
+    "U816", "R384", "D671", "R615", "D410", "L910", "U914", "L579", "U385",
+    "R916", "U13", "R268", "D519", "R289", "U410", "L389", "D885", "L894",
+    "U734", "L474", "U707", "L72", "U155", "L237", "U760", "L127", "U806",
+    "L15", "U381", "L557", "D727", "L569", "U320", "L985", "D452", "L8",
+    "D884", "R356", "U732", "L672", "D458", "L485", "U402", "L238", "D30",
+    "R644", "U125", "R753", "U183", "L773", "U487", "R849", "U210", "L164",
+    "D808", "L595", "D668", "L340", "U785", "R313", "D72", "L76", "D263",
+    "R689", "U604", "R471", "U688", "R462", "D915", "R106", "D335", "R869",
+    "U499", "R190", "D916", "R468", "D882", "R56", "D858", "L143", "D741",
+    "L386", "U856", "R50", "U853", "R151", "D114", "L773", "U854", "L290",
+    "D344", "L23", "U796", "L531", "D932", "R314", "U960", "R643", "D303",
+    "L661", "D493", "L82", "D491", "L722", "U848", "L686", "U4", "L985",
+    "D509", "L135", "D452", "R500", "U105", "L326", "D101", "R222", "D944",
+    "L645", "D362", "L628", "U305", "L965", "U356", "L358", "D137", "R787",
+    "U728", "R967", "U404", "R18", "D928", "L695", "D965", "R281", "D597",
+    "L791", "U731", "R746", "U163", "L780", "U41", "L255", "U81", "L530",
+    "D964", "R921", "D297", "R475", "U663", "L226", "U623", "L984", "U943",
+    "L143", "U201", "R926", "U572", "R343", "U839", "R764", "U751", "R128",
+    "U939", "R987", "D108", "R474", "U599", "R412", "D248", "R125", "U797",
+    "L91", "D761", "L840", "U290", "L281", "U779", "R650", "D797", "R185",
+    "D320", "L25", "U378", "L696", "U332", "R75", "D620", "L213", "D667",
+    "R558", "U267", "L846", "U306", "R939", "D220", "R311", "U827", "R345",
+    "U534", "R56", "D679", "R48", "D845", "R898", "U8", "R862", "D960", "R753",
+    "U319", "L886", "D795", "R805", "D265", "R876", "U729", "R894", "D368",
+    "R858", "U744", "R506", "D327", "L903", "U919", "L721", "U507", "L463",
+    "U753", "R775", "D719", "R315", "U128", "R17", "D376", "R999", "D386",
+    "L259", "U181", "L162", "U605", "L265", "D430", "R35", "D968", "R207",
+    "U466", "R796", "D667", "R93", "U749", "L315", "D410", "R312", "U929",
+    "L923", "U260", "R638"
+]
+
+
+def to_coords(xs):
+    row, col = 0, 0
+    coords = []
+    for x in xs:
+        d, amt = x[0], int(x[1:])
+        if d == 'U':
+            for i in range(1, amt + 1):
+                coords.append((row + i, col))
+            row += amt
+        elif d == 'D':
+            for i in range(1, amt + 1):
+                coords.append((row - i, col))
+            row -= amt
+        elif d == 'L':
+            for i in range(1, amt + 1):
+                coords.append((row, col - i))
+            col -= amt
+        elif d == 'R':
+            for i in range(1, amt + 1):
+                coords.append((row, col + i))
+            col += i
+    return coords
+
+
+def contains(row, col, d):
+    if row not in d:
+        return False
+    return col in d[row]
+
+
+def intersections(xs, ys):
+    d = {}
+    ints = set()
+    for row, col in to_coords(xs):
+        if row in d:
+            d[row].add(col)
+        else:
+            d[row] = {col}
+    for row, col in to_coords(ys):
+        if contains(row, col, d):
+            ints.add((row, col))
+    return ints
+
+
+def trace_to(coord, xs):
+    count = 0
+    for coord_x in to_coords(xs):
+        count += 1
+        if coord_x == coord:
+            return count
+    raise Exception("Intersection doesn't exist")
+
+
+answer = []
+for coord in intersections(xs, ys):
+    x = trace_to(coord, xs)
+    y = trace_to(coord, ys)
+    heappush(answer, x + y)
+
+print(heappop(answer))
diff --git a/users/wpcarro/scratch/advent-of-code-2019/day_4.py b/users/wpcarro/scratch/advent-of-code-2019/day_4.py
new file mode 100644
index 0000000000..adef73b452
--- /dev/null
+++ b/users/wpcarro/scratch/advent-of-code-2019/day_4.py
@@ -0,0 +1,35 @@
+import re
+
+start = 134792
+end = 675810
+
+
+def satisfies(x):
+    x = str(x)
+    result = False
+    double, not_decreasing = False, False
+
+    # double and *only* double exists
+    for i in range(len(x) - 1):
+        # double and left-of-a  is BOL or !x
+        #        and right-of-b is EOL or !x
+        a, b = x[i], x[i + 1]
+        bol = i - 1 < 0
+        eol = i + 2 >= len(x)
+        if a == b and (bol or x[i - 1] != a) and (eol or x[i + 2] != a):
+            double = True
+            break
+
+    # not_decreasing
+    prev = int(x[0])
+    for a in x[1:]:
+        a = int(a)
+        if prev > a:
+            return False
+        prev = a
+    not_decreasing = True
+
+    return double and not_decreasing
+
+
+print(len([x for x in range(start, end + 1) if satisfies(x)]))
diff --git a/users/wpcarro/scratch/advent-of-code-2019/day_5.py b/users/wpcarro/scratch/advent-of-code-2019/day_5.py
new file mode 100644
index 0000000000..3d82846e61
--- /dev/null
+++ b/users/wpcarro/scratch/advent-of-code-2019/day_5.py
@@ -0,0 +1,170 @@
+x = [
+    3, 225, 1, 225, 6, 6, 1100, 1, 238, 225, 104, 0, 1102, 31, 68, 225, 1001,
+    13, 87, 224, 1001, 224, -118, 224, 4, 224, 102, 8, 223, 223, 1001, 224, 7,
+    224, 1, 223, 224, 223, 1, 174, 110, 224, 1001, 224, -46, 224, 4, 224, 102,
+    8, 223, 223, 101, 2, 224, 224, 1, 223, 224, 223, 1101, 13, 60, 224, 101,
+    -73, 224, 224, 4, 224, 102, 8, 223, 223, 101, 6, 224, 224, 1, 224, 223,
+    223, 1101, 87, 72, 225, 101, 47, 84, 224, 101, -119, 224, 224, 4, 224,
+    1002, 223, 8, 223, 1001, 224, 6, 224, 1, 223, 224, 223, 1101, 76, 31, 225,
+    1102, 60, 43, 225, 1102, 45, 31, 225, 1102, 63, 9, 225, 2, 170, 122, 224,
+    1001, 224, -486, 224, 4, 224, 102, 8, 223, 223, 101, 2, 224, 224, 1, 223,
+    224, 223, 1102, 29, 17, 224, 101, -493, 224, 224, 4, 224, 102, 8, 223, 223,
+    101, 1, 224, 224, 1, 223, 224, 223, 1102, 52, 54, 225, 1102, 27, 15, 225,
+    102, 26, 113, 224, 1001, 224, -1560, 224, 4, 224, 102, 8, 223, 223, 101, 7,
+    224, 224, 1, 223, 224, 223, 1002, 117, 81, 224, 101, -3645, 224, 224, 4,
+    224, 1002, 223, 8, 223, 101, 6, 224, 224, 1, 223, 224, 223, 4, 223, 99, 0,
+    0, 0, 677, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1105, 0, 99999, 1105, 227, 247,
+    1105, 1, 99999, 1005, 227, 99999, 1005, 0, 256, 1105, 1, 99999, 1106, 227,
+    99999, 1106, 0, 265, 1105, 1, 99999, 1006, 0, 99999, 1006, 227, 274, 1105,
+    1, 99999, 1105, 1, 280, 1105, 1, 99999, 1, 225, 225, 225, 1101, 294, 0, 0,
+    105, 1, 0, 1105, 1, 99999, 1106, 0, 300, 1105, 1, 99999, 1, 225, 225, 225,
+    1101, 314, 0, 0, 106, 0, 0, 1105, 1, 99999, 8, 226, 677, 224, 102, 2, 223,
+    223, 1005, 224, 329, 1001, 223, 1, 223, 1108, 677, 226, 224, 102, 2, 223,
+    223, 1006, 224, 344, 101, 1, 223, 223, 108, 677, 226, 224, 102, 2, 223,
+    223, 1006, 224, 359, 101, 1, 223, 223, 7, 677, 226, 224, 102, 2, 223, 223,
+    1005, 224, 374, 101, 1, 223, 223, 1007, 226, 677, 224, 102, 2, 223, 223,
+    1005, 224, 389, 101, 1, 223, 223, 8, 677, 677, 224, 102, 2, 223, 223, 1006,
+    224, 404, 1001, 223, 1, 223, 1007, 677, 677, 224, 1002, 223, 2, 223, 1006,
+    224, 419, 101, 1, 223, 223, 1108, 677, 677, 224, 1002, 223, 2, 223, 1005,
+    224, 434, 1001, 223, 1, 223, 1107, 226, 677, 224, 102, 2, 223, 223, 1005,
+    224, 449, 101, 1, 223, 223, 107, 226, 226, 224, 102, 2, 223, 223, 1006,
+    224, 464, 101, 1, 223, 223, 1108, 226, 677, 224, 1002, 223, 2, 223, 1005,
+    224, 479, 1001, 223, 1, 223, 7, 677, 677, 224, 102, 2, 223, 223, 1006, 224,
+    494, 1001, 223, 1, 223, 1107, 677, 226, 224, 102, 2, 223, 223, 1005, 224,
+    509, 101, 1, 223, 223, 107, 677, 677, 224, 1002, 223, 2, 223, 1006, 224,
+    524, 101, 1, 223, 223, 1008, 677, 677, 224, 1002, 223, 2, 223, 1006, 224,
+    539, 101, 1, 223, 223, 7, 226, 677, 224, 1002, 223, 2, 223, 1005, 224, 554,
+    101, 1, 223, 223, 108, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 569,
+    101, 1, 223, 223, 1008, 226, 677, 224, 102, 2, 223, 223, 1005, 224, 584,
+    101, 1, 223, 223, 8, 677, 226, 224, 1002, 223, 2, 223, 1005, 224, 599, 101,
+    1, 223, 223, 1007, 226, 226, 224, 1002, 223, 2, 223, 1005, 224, 614, 101,
+    1, 223, 223, 1107, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 629, 101,
+    1, 223, 223, 107, 677, 226, 224, 1002, 223, 2, 223, 1005, 224, 644, 1001,
+    223, 1, 223, 1008, 226, 226, 224, 1002, 223, 2, 223, 1006, 224, 659, 101,
+    1, 223, 223, 108, 677, 677, 224, 1002, 223, 2, 223, 1005, 224, 674, 1001,
+    223, 1, 223, 4, 223, 99, 226
+]
+
+# Interpretter spec:
+# Op-code width: 2
+# ABCDE
+# A:  Mode of 3rd parameter
+# B:  Mode of 2rd parameter
+# C:  Mode of 1st parameter
+# DE: 2-digit op-code
+#
+# Not every op-code has the same arity.
+#
+# Parameter modes:
+# - positional: index of memory. 0
+# - immediate: raw value. 1
+# Assert that you never attempt to write to an "immediate value"
+
+# Parameter modes
+POS = '0'  # positional parameter mode
+VAL = '1'  # immediate parameter mode
+
+
+# Pasted from day-2.py
+# interpretter :: Int -> [Int] -> [Int] -> IO ()
+def interpret(i, x, argv=[], outs=[]):
+    """Values in `argv` will be applied to any `input` fields."""
+    # The widest op-code we'll see is 3 + 2 = 5 for either addition or
+    # multiplication since each of those is a 3-arity function with a two-digit
+    # op-code.
+    instruction = '{:05d}'.format(x[i])
+    op = instruction[-2:]
+
+    if op == '01':
+        a, b, out = x[i + 1], x[i + 2], x[i + 3]
+        mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[
+            0]
+        a = a if mode_a == VAL else x[a]
+        b = b if mode_b == VAL else x[b]
+        assert mode_out == POS
+        x[out] = a + b
+        return interpret(i + 4, x, argv=argv, outs=outs)
+    elif op == '02':
+        a, b, out = x[i + 1], x[i + 2], x[i + 3]
+        mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[
+            0]
+        a = a if mode_a == VAL else x[a]
+        b = b if mode_b == VAL else x[b]
+        assert mode_out == POS
+        x[out] = a * b
+        return interpret(i + 4, x, argv=argv, outs=outs)
+    # input
+    elif op == '03':
+        a = x[i + 1]
+        mode_a = instruction[2]
+        assert mode_a == POS
+        # What's the pythonic way to defensively get this value?
+        if len(argv) and argv[0] is not None:
+            x[a] = argv[0]
+            return interpret(i + 2, x, argv=argv[1:], outs=outs)
+        elif len(outs) and outs[-1] is not None:
+            x[a] = outs[-1]
+            return interpret(i + 2, x, argv=argv, outs=outs)
+        else:
+            # Here we want to block until the user applies input. This could be
+            # done easily with message passing for something similar.
+            x[a] = int(input('Enter: '))
+            return interpret(i + 2, x, argv=argv)
+    # output
+    elif op == '04':
+        a = x[i + 1]
+        mode_a = instruction[2]
+        a = a if mode_a == VAL else x[a]
+        outs.append(a)
+        return interpret(i + 2, x, argv=argv, outs=outs)
+    # jump-if-true
+    elif op == '05':
+        a, b = x[i + 1], x[i + 2]
+        mode_a, mode_b = instruction[2], instruction[1]
+        a = a if mode_a == VAL else x[a]
+        b = b if mode_b == VAL else x[b]
+        if a != 0:
+            return interpret(b, x, argv=argv, outs=outs)
+        else:
+            return interpret(i + 3, x, argv=argv, outs=outs)
+    # jump-if-false
+    elif op == '06':
+        a, b = x[i + 1], x[i + 2]
+        mode_a, mode_b = instruction[2], instruction[1]
+        a = a if mode_a == VAL else x[a]
+        b = b if mode_b == VAL else x[b]
+        if a == 0:
+            return interpret(b, x, argv=argv, outs=outs)
+        else:
+            return interpret(i + 3, x, argv=argv, outs=outs)
+        pass
+    # less than
+    elif op == '07':
+        a, b, out = x[i + 1], x[i + 2], x[i + 3]
+        mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[
+            0]
+        a = a if mode_a == VAL else x[a]
+        b = b if mode_b == VAL else x[b]
+        assert mode_out == POS
+        if a < b:
+            x[out] = 1
+        else:
+            x[out] = 0
+        return interpret(i + 4, x, argv=argv, outs=outs)
+    # equals
+    elif op == '08':
+        a, b, out = x[i + 1], x[i + 2], x[i + 3]
+        mode_a, mode_b, mode_out = instruction[2], instruction[1], instruction[
+            0]
+        a = a if mode_a == VAL else x[a]
+        b = b if mode_b == VAL else x[b]
+        assert mode_out == POS
+        if a == b:
+            x[out] = 1
+        else:
+            x[out] = 0
+        return interpret(i + 4, x, argv=argv, outs=outs)
+    elif op == '99':
+        return x[0]
+    else:
+        raise Exception('Unsupported opcode: {}.'.format(op))
diff --git a/users/wpcarro/scratch/advent-of-code-2019/day_6.py b/users/wpcarro/scratch/advent-of-code-2019/day_6.py
new file mode 100644
index 0000000000..aba99b8239
--- /dev/null
+++ b/users/wpcarro/scratch/advent-of-code-2019/day_6.py
@@ -0,0 +1,155 @@
+from graphviz import Digraph
+
+data = """6WF)DRK 2PT)PSM H42)FN8 1XR)LQD HRK)9KL TD6)H8W 98Z)BJM RCQ)LVG
+RWQ)Q7H 2PS)X94 NHB)25X PXC)W57 L8L)MVX CFK)D8K R1B)43T PDY)QKX FQK)82K JJ6)MQJ
+FB6)6V1 R28)5MZ BN2)5HN 6BQ)JVC W57)22C MQJ)DL2 MTC)84R RH8)CRN Y27)3GN CKQ)31C
+R7V)9BK ZDY)PDY X2Q)Y6S Q8B)SAN 1Z3)PVT R87)57R KCJ)44X PWQ)9CB HLC)VYW HFP)9XS
+X33)MC3 RYS)R7R JRF)VHW 79R)FXZ YQQ)STV 8J6)JWX Q6D)RV6 LL9)B4D 6R1)T1Z VK9)42M
+PQP)17N K6C)HMK GLY)N47 KDW)CDC DQ4)RY5 SND)FDR 7YF)1VN MDT)B3S D3F)98Z 5VH)MR7
+KNR)2L8 CJW)QDL FWY)14X SJD)79R COM)BXW T2B)FPB B2Q)BRJ Z21)HYC VHW)5XR WZ4)2JM
+8HF)342 PYR)X9Y RKF)P43 S1S)9WT 2PB)BSB QF7)M9T HML)HMC 7J9)7Q6 8F1)29K DH1)NDM
+1YC)PXC P32)HR7 PMX)7Y9 STV)SLW NYY)NF1 TG9)998 DMB)DLW XGL)1Z3 GK8)WCS YHR)HQC
+9Q5)B6D R2T)CM5 6KC)J5G ZM9)L8L J8T)F89 3LN)YOU T2T)Z8F SCY)FKG 9W4)195 QLM)DD7
+4QY)JCB WKM)3JF 693)YM8 61M)B6Y DSP)X2M YZ5)DPL BC9)3B1 BDB)JTG 3TJ)TW1 W5M)SF6
+K4Q)X56 5HT)YHX YJG)DM5 68N)X2Q 2YP)DS5 BLK)MY3 6WV)VZ4 2JQ)ZT8 G93)V2W WN1)SBD
+SS7)DY9 X56)8HP JY1)VS4 XQ6)L94 98Z)DMC V6S)NWT D9L)Y44 V6G)GVS JDW)FZW FJT)S38
+L2Z)VPL 7ZX)DKK X2M)8WM YVZ)XWS HMK)P87 47M)TD6 TDZ)21T 19R)95B GD9)Q1L 9QX)DFR
+Y64)XGN CRG)6VY V3L)61D RJ4)C9Z XXG)P53 VJ8)QTF CPQ)2M9 JRN)8V1 KMH)K94 DLW)VQ4
+91W)2QQ G4B)RWQ 4P1)MKS K6G)DZ7 WCS)JR9 LXM)7RY 6ZB)K6G HMC)622 Z21)BLK Q6N)48V
+66S)MK4 PDK)6WV Y6S)GY1 2L8)ZMG 42W)ZN6 6MS)8TZ JBY)STQ NSF)3ZM 5CV)X9N K4V)WFL
+J6R)DT8 N3N)CX4 PTD)YXT F74)4T5 C51)3FW KRW)DS1 NWT)CKQ 195)6G6 HVQ)S18 Q7H)BKM
+SKN)4D4 GK2)MLX MVX)TG9 YPK)RHQ Y9F)Z8W 42M)WNL 84R)6JP KNC)NHF FZW)PGM 3FW)HGX
+DBK)FB6 45T)HLT L11)JVN HB5)K6C QH5)888 BTJ)J55 8BT)8ZS FR1)XGL S87)PS9 C4K)BN2
+N2Q)18C KTF)ZM9 TN2)B2Q DF3)CFK 9T3)TMR P29)3P1 P1W)7SQ 4D4)1DJ LML)ZJ3 Q4L)RKF
+MW2)79T LVG)CPQ BDC)JH5 DNZ)232 998)GTM YGS)4WH GY1)C51 J55)QBT B8Z)34W FJ2)H42
+58J)326 T1Z)DCJ 1ZH)GLV 1YC)JG6 14K)22B RY5)QRY 7V2)2WT 4GQ)XHV ZJ3)TQ8 2G8)SN3
+FPB)HMN SC4)57D 5LQ)R2T LXM)R8Z JQ6)G4B WNL)GK2 42M)P75 LM3)YPK ZN6)753 PN4)835
+C4H)JY1 LR4)VD5 PSM)P1W VWL)C6C G2V)WBC 85M)R24 B1V)QW7 175)2PM Y1V)1ZH 34W)3MJ
+WN7)TTB 3PV)CQD N7Y)9T3 223)8D4 RV6)LJ9 HFP)JRF VMT)DNB GJP)D3F J5G)KMS 7Q6)ZW2
+YCB)JBY XGN)MNL 888)DSP X61)Q6N WT5)X12 SDN)FD1 2QC)54W V98)964 T7S)YVZ MLX)9VZ
+FR8)QH5 TVQ)2PS 2PV)FHY F4S)MPT 3J9)JNB J6M)GDC Q4C)MJN 9VZ)BZK P2P)B69 WBC)M1W
+D97)HPF JKB)9L4 593)6YJ RMB)4Q5 QZB)38C H12)6R1 MKY)DDD HGX)CRG P53)WY7 22B)GMM
+44X)2D8 DT8)L7H 3Y2)D3S FB8)68N 3BC)1XR 4XF)TVQ VPL)R7V Z4V)JSK B3S)FW5 49Z)YQQ
+99V)D13 54Q)SS7 CYC)TXH PQ3)78W X4M)G9H WFL)M99 ZYY)3Y2 12Y)PSW W38)P29 H8W)JJ6
+P66)VPH GK2)45T H5F)FJT JDJ)SNV 14F)96Q JG6)TQ4 2L6)52Q SCY)CBJ 3GN)KNC KLM)XPR
+DH1)QZB DMB)X7G DPL)7SX D97)N3N GNS)T95 53P)GW2 BHR)HNB YHX)XQV 2CR)Y1V C9D)Z7P
+FN8)2PT 6LF)FCQ JNL)LQR SPV)YCB HGX)N83 VS4)8BT 5RH)FTX HYC)X2J 69V)J6S 9XS)PN4
+SD7)5Q3 2RN)82D QRY)FFY K2Y)3X2 79Z)S2Z YN2)Y64 JKB)MDT KJ8)NDH N57)5VH 3XK)1Q1
+SCH)FJ6 17N)GMP QR4)7V2 GLV)GLY NHF)ZDY QDL)S14 QF1)BMC ZLF)DHN 3JF)7TR MKS)GCY
+964)91R 9L4)L5G RRX)6ZB CD7)73M 3X2)PGC HNB)S9Z L94)KLM 8MQ)SCR 18C)3TJ M4Y)BTJ
+BC9)5YR TV5)SCY 2NX)8CC C9Z)MTC B69)3QP HR7)CHJ 8ZS)JRN 31C)TJW D43)4NH 93Q)X9X
+T95)DNZ LQ5)BC9 9T5)S2C RP8)DH1 GCY)SD7 Y44)9B5 VG5)ZYY 7RY)V3L PWV)Q4L NF1)7YF
+DRK)Y8V D13)GYG TW1)2PB ZVZ)2VV BRJ)V2V 9CB)Y7B MK4)9CJ TMR)6XS HWF)GK8 QTF)S1S
+DFW)6LF N3S)WN1 N2Q)MSW CZ5)X61 FXZ)C4H SCQ)MF7 9LY)3LN 5MZ)PMX CN9)WF9 FHY)PR8
+S38)NWH M29)G5S 4NH)GZJ 5YR)54H CLX)MNY TJD)HQL RRZ)4GQ YHB)CZ5 P37)93Q YJG)3Q3
+95B)QMF CMQ)BLZ QD9)45M JSK)R28 YCW)CLX 8K3)JGB N8M)PQW P75)1HL XBS)T2T 22C)PVW
+689)6MS FFY)RWX YHL)2G8 Y8V)4P1 Y7B)62Z YKJ)JDJ 1HL)5LQ PZ3)B1C 52Q)7HB 3Q2)ZV7
+YBF)Z4V J95)SDH NM6)YBF 8YN)J3M J6S)KNR PVT)N4X SDH)RFW RFW)7Y1 JCB)52B 3MJ)H58
+4QF)XHZ F62)DFW 7LJ)KDW JHL)C9D B4D)Q8B 342)YGS PFR)ZQT Z9K)TNS 8F8)WLB 94N)DMB
+QBT)RYS 3VR)KRR 8D4)ST6 X9N)2PV 632)8K3 MX5)XNP 57D)Y27 18D)PQP D3F)RJ4 PLS)PBL
+1JP)YDC 79V)BG2 S14)2NX 4Q5)NCQ FTX)555 2PM)KMH HQC)RMB 9Z9)BNZ XHV)Y94 7ZP)YHR
+BNZ)49Z W6D)LX6 SLS)JL3 PVW)P9W Z1L)HB5 DS5)G2V Z9Q)RV8 DFR)LPJ 836)693 K94)VWL
+HRG)836 J3V)593 52N)LPK 9KL)Y7M LX6)F7D JL3)511 L4G)D97 1RH)Y9F NJ2)LML GW2)9WV
+8KZ)NRC XQV)G6D R8Z)QF7 326)HML R7R)8PM 622)YCW WQY)LGS NF1)FF3 5LQ)QF1 5XR)PTD
+V2V)PFR 9T5)JQ6 CBQ)8KZ VZ4)HVQ TJW)DQT 9WT)5M6 CFK)YHL JR9)1JP Y1K)CF4 8WS)JPY
+VYC)1D6 GKK)7J9 JTG)RRX 6V1)F74 1H5)QR4 SN3)NMG MF7)GQ1 RYK)SCH BNZ)9LY 1DJ)9LP
+L6W)5BK FCQ)BFL DCJ)3RD MXD)8MQ RWX)1RH NBF)WKM K6C)WNH H58)L6W Y7B)BJH PGC)NBF
+96Q)Q2W F7D)BSN 223)Z9K K94)VYC X9X)7M3 Q1M)3J9 QXF)XQ6 DD7)3Q2 Q1L)NHB 79T)LXQ
+8TZ)M29 21T)Q4C B1C)NSF 8D8)FJ2 LJH)HGJ QS2)PS1 5KX)Z2L C6C)6BQ VQ2)2YP P87)N8M
+ST5)L4G 8SP)W5M T4H)69V 9WF)GHS FF3)SND C5G)GKK VQ2)X4M P43)8J6 TD6)384 66V)CN9
+CX4)T9T NCQ)2JQ 29K)K8K RY5)K4Q GQ3)T4H FNH)P32 3BC)PRQ 5HN)4QY M1W)BGT 84R)ST5
+S45)CJW CK4)W7G SGX)19R S2C)7ZX DHN)W5Y 8D9)HM2 BSB)SPV D8K)DFV JHL)2L6 KYP)12Y
+KDN)6X7 Y44)SQZ 6G6)SJD N7D)QGF Q84)8WJ F89)LL9 LYJ)2RN 25X)Q84 HM3)53P JNB)QD9
+SLW)1DQ 384)3BC PR8)NGV 49N)7ZP 65H)LHJ 6XS)S45 ZMG)FR1 X2M)Y86 QD3)QLM P4R)PQ3
+RTK)4M3 4YW)N7D R7V)M4M 73M)CBF DFV)64R Z7P)LMK HRG)Y1K 3ZM)BCZ WY7)QXP DMC)9Q5
+PSW)1H5 8CC)TV5 TTB)S88 BZK)K2Y T2B)CBQ HJB)Y19 DQW)KML Z8W)8ZL PBL)5TK 1D6)MX5
+3MJ)4YW MDT)HJB 62Z)X33 DZ7)BDC 9CJ)FRD 82D)KDN LK7)18D 9QQ)61M Y34)DZG J4T)6KC
+971)QD3 511)GQ3 MJN)F62 RNM)NKG BGW)KJ8 DL2)1YH ZQT)RYZ 1YH)ZJ6 2WT)YYQ 7HB)DYQ
+3BN)WQY 2M9)62D TSK)YR1 N7Y)VJ8 WZ4)FWT MNY)YN2 DYQ)RRZ 3RG)YT3 2SM)VK9 JH5)ZXH
+GYG)K2M PKF)V6G JGB)S87 X94)N57 MSW)L2Z X4N)25G BLZ)4QF JPY)GD9 WLB)V6S KML)2SM
+TXH)9X1 48V)KTR 8PM)WZ4 ZW2)967 PS9)3BN 4WH)9T5 8M1)R6V N7M)VWK S88)978 N4X)8KH
+6VY)PLS NRC)874 QGF)QWJ NMG)J3V B8Z)WPF 45M)2QC KDW)VQ2 FZW)223 BXW)QXF FRD)PWV
+8HP)4G7 KDN)YYL LHJ)SDN P6P)XMC W5Y)RYK HX8)KW3 Z2L)H12 WPF)T2B L7H)BGW MNL)17B
+GHS)66V QKX)XWV FW5)W38 PDK)Y34 FKG)Q6D DQT)YJG 15G)79V 4VK)51Y BJH)LR4 48V)6GC
+DM5)Y1F CM5)VG5 KB8)HRK 5HN)RCQ 6JP)SDQ LGH)NJ2 L94)N7Y 4Y2)ZLF 25G)C4K K8K)SLS
+232)ZVZ GQ1)58J RV8)H5F 78W)565 YCF)8D9 DZG)99V N83)CKR TN2)ZCX NGV)8SP BSN)FTN
+LPJ)94N 3Q3)Q1M JVX)971 54W)LGH 67Y)P66 R24)P37 3QP)QTY YHR)FLT GMP)NM6 NDH)632
+PWV)8D8 LMK)3PV ZWJ)KB8 967)4VK 3B1)WN7 XWS)5CV YR1)FNH 565)4PH 5BK)V98 W5Y)FR8
+PS1)HX8 38C)XXG XWV)1YC M4M)LQ5 S9Z)49N XMC)R1B YYL)VC9 GMM)SCQ LXQ)J95 51Y)RP8
+HLT)XBS 82K)B8Z NR5)7K3 K2M)67Y SF6)W6D CF4)85M MC3)LXM HMN)RNM BFL)4XF MT2)PM4
+VWK)JKB 3JF)ZTZ QWJ)9QQ KRR)TJD VYW)Z9Q CK4)QS2 8NQ)NR5 57R)BHR 8WM)YHB Y86)GNS
+2Y2)Z21 X12)9QX LJ9)YKJ 3RD)8F1 7SQ)CK4 ZXH)3XK DDD)5KX ZCX)PYR GZJ)KXL KC5)52N
+PM4)RYP 14X)ZWJ FJ6)175 17B)689 HQL)14F LQR)DBK LGS)4Y2 2QQ)SGR 2VV)8F8 J6S)LM3
+RTP)YZ5 XDD)14K VQ4)MT2 KMH)KYC CKR)RTP VD5)MRM CM5)KRW BG3)XDD PGM)J4T MY3)JVX
+Z8F)WNP BKM)WT5 FLT)KTF N7D)8M1 Y19)CMQ HPF)WDL 65H)JJP 2MQ)66S 4Q5)54Q Q2W)ZL4
+QTY)659 MRM)9Z9 X2J)SC4 YWH)RB3 FTN)LYJ LMK)N7M SGX)15G KW3)FQK 3VV)JNL JWX)R8R
+9Z3)9MB BMC)N3S W7G)Z1L SD7)MW2 376)RH8 NWT)JHL 7CD)N2Z KTR)HM3 1Q1)TDZ DY9)2CR
+6YJ)14G FWT)JDW C2S)C5G SNV)J6M 5TK)YWH J3M)8HF HM2)GJP P9W)7CD 1VN)SGX KMS)RBK
+64R)B1V 62D)3VV 61D)F4S XPR)SKN FJT)N3P 9WV)D43 TQ8)BDB 46H)K4V 8WJ)MXD NDM)9WF
+8ZL)1QJ SCR)2MQ 7Y9)LJH VPH)MKY YDC)PDK 4G7)65H 2JM)NYY T9T)VMT 8M1)TSK G5S)X4N
+6FH)KYP D98)DQW G6D)C2S 6X7)N2Q 1QJ)T7S ZL4)J8T 5BT)3VR 835)KCJ YM8)3RG Y7M)PWQ
+54W)9W4 CBF)7LJ 4T5)8WS RHQ)HBK CQD)D98 HGJ)J6R JVC)79Z FD1)PKF VC9)5BT C4H)6WF
+D3S)P6P MR7)BG3 R6V)DF3 9X1)NQ5 ZTZ)2Y2 8WM)HFP CDC)376 TQ4)M4Y 9MB)N1R HBK)DQ4
+1DQ)CYC WNP)DM8 CBJ)LK7 ZT8)FWY LQD)PNN 555)9Z3 TNS)D9L QMF)L11 FR8)5RH WF9)R87
+NKG)5HT L5G)91W N2Z)YV9 9B5)CD7 ZV7)8NQ ST6)74T ZJ6)CQV S18)47M 74T)8YN WNH)TN2
+874)46H 3VV)PZ3 Y1F)42W MPT)2LP FDR)HWF X7G)RTK 52B)P4R RYP)G93 NWH)YCF 7TR)FB8
+RWQ)6FH 8F8)HLC CRN)P2P B6D)KC5 PNN)HRG""".split()
+
+# COM is the root in this tree
+
+
+# parent :: Vertex -> [Edge] -> Maybe(Vertex)
+def parent(x, xs):
+    for a, b in xs:
+        if b == x:
+            return a
+    return None
+
+
+# parents :: Vertex -> [Edge] -> [Vertex]
+def parents(x, xs):
+    parents = []
+    p = parent(x, xs)
+    while p:
+        parents.append(p)
+        p = parent(p, xs)
+    return parents
+
+
+# alias Vertex :: String
+# alias Edge :: (String, String)
+# to_edge_list :: [String] -> [(String, String)]
+def to_edge_list(xs):
+    """Returns a list of tuples where (A, B) represents a directed edge from
+    vertex A to vertex B."""
+    return [(x[0:3], x[4:]) for x in xs]
+
+
+# to_graphviz :: [Edge] -> String
+def to_graphviz(xs):
+    d = Digraph()
+    for a, b in xs:
+        d.node(a, label=a)
+        d.edge(a, b)
+    return d.source
+
+
+graph = to_edge_list(data)
+you = parents('YOU', graph)
+san = parents('SAN', graph)
+
+# Distance from YOU to shared point with SAN
+yd = 1
+for i in range(len(you)):
+    if you[i] in san:
+        break
+    yd += 1
+
+# Distance from SAN to shared point with YOU
+sd = 1
+for i in range(len(san)):
+    if san[i] in you:
+        break
+    sd += 1
+
+print('Number of orbital transfers required: {}'.format(yd - 1 + sd - 1))
diff --git a/users/wpcarro/scratch/advent-of-code-2019/day_7.py b/users/wpcarro/scratch/advent-of-code-2019/day_7.py
new file mode 100644
index 0000000000..14597d5104
--- /dev/null
+++ b/users/wpcarro/scratch/advent-of-code-2019/day_7.py
@@ -0,0 +1,49 @@
+from day_5 import interpret
+from itertools import permutations
+
+# TODO: I may need to re-write this in Elixir modelling each amplifier as a
+# `Process` and `Process.send`ing each amplifier the signals.
+
+data = [
+    3, 8, 1001, 8, 10, 8, 105, 1, 0, 0, 21, 38, 59, 76, 89, 106, 187, 268, 349,
+    430, 99999, 3, 9, 1002, 9, 3, 9, 101, 2, 9, 9, 1002, 9, 4, 9, 4, 9, 99, 3,
+    9, 1001, 9, 5, 9, 1002, 9, 5, 9, 1001, 9, 2, 9, 1002, 9, 3, 9, 4, 9, 99, 3,
+    9, 1001, 9, 4, 9, 102, 4, 9, 9, 1001, 9, 3, 9, 4, 9, 99, 3, 9, 101, 4, 9,
+    9, 1002, 9, 5, 9, 4, 9, 99, 3, 9, 1002, 9, 3, 9, 101, 5, 9, 9, 1002, 9, 3,
+    9, 4, 9, 99, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9,
+    1002, 9, 2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9,
+    3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4,
+    9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 99, 3, 9, 1002, 9,
+    2, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101,
+    1, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9,
+    101, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3,
+    9, 1001, 9, 2, 9, 4, 9, 99, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9,
+    4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9,
+    9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 102, 2,
+    9, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 99, 3, 9,
+    1001, 9, 2, 9, 4, 9, 3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9,
+    3, 9, 102, 2, 9, 9, 4, 9, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4,
+    9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 101, 1, 9,
+    9, 4, 9, 3, 9, 101, 1, 9, 9, 4, 9, 99, 3, 9, 101, 2, 9, 9, 4, 9, 3, 9, 102,
+    2, 9, 9, 4, 9, 3, 9, 1002, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9,
+    1001, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 1, 9, 4, 9,
+    3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 1001, 9, 2, 9, 4, 9, 3, 9, 102, 2, 9, 9,
+    4, 9, 99
+]
+
+data_a, data_b, data_c, data_d, data_e = data[:], data[:], data[:], data[:], data[:]
+
+# m = 0
+# for a, b, c, d, e in permutations(range(5, 10)):
+#     answer = None
+#     z = 0
+#     while z is not None:
+#         print(a, b, c, d, e)
+#         print('---')
+#         v = interpret(0, data_a, argv=[a, z])
+#         print(v)
+#         w = interpret(0, data_b, argv=[b, v])
+#         x = interpret(0, data_c, argv=[c, w])
+#         y = interpret(0, data_d, argv=[d, x])
+#         z = interpret(0, data_e, argv=[e, y])
+#         m = max(m, z)
diff --git a/users/wpcarro/scratch/blockchain/default.nix b/users/wpcarro/scratch/blockchain/default.nix
new file mode 100644
index 0000000000..c02f9a9c81
--- /dev/null
+++ b/users/wpcarro/scratch/blockchain/default.nix
@@ -0,0 +1,14 @@
+{ pkgs, ... }:
+
+let
+  pypkgs = pkgs.python3Packages;
+in
+pkgs.python3Packages.buildPythonApplication {
+  pname = "main";
+  src = ./.;
+  version = "0.0.1";
+  propagatedBuildInputs = with pypkgs; [
+    flask
+    requests
+  ];
+}
diff --git a/users/wpcarro/scratch/blockchain/main.py b/users/wpcarro/scratch/blockchain/main.py
new file mode 100644
index 0000000000..e7b6276133
--- /dev/null
+++ b/users/wpcarro/scratch/blockchain/main.py
@@ -0,0 +1,263 @@
+from flask import Flask, jsonify, request
+from hashlib import sha256
+from datetime import datetime
+from urllib.parse import urlparse
+
+import json
+import requests
+import uuid
+
+################################################################################
+# Helper Functions
+################################################################################
+
+def hash(x):
+  return sha256(x).hexdigest()
+
+def is_pow_valid(guess, prev_proof):
+  """
+  Return true if the hash of `guess` + `prev_proof` has 4x leading zeros.
+  """
+  return hash(str(guess + prev_proof).encode("utf8"))[:4] == "0000"
+
+################################################################################
+# Classes
+################################################################################
+
+class Node(object):
+  def __init__(self, host="0.0.0.0", port=8000):
+    self.app = Flask(__name__)
+    self.define_api()
+    self.identifier = str(uuid.uuid4())
+    self.blockchain = Blockchain()
+    self.neighbors = set()
+
+  def add_neighbors(self, urls=None):
+    for url in urls:
+      parsed = urlparse(url)
+      if not parsed.netloc:
+        raise ValueError("Must pass valid URLs for neighbors")
+      self.neighbors.add(parsed.netloc)
+
+  def decode_chain(chain_json):
+    return Blockchain(
+        blocks=[
+            Block(
+                index=block["index"],
+                ts=block["ts"],
+                transactions=[
+                    Transaction(
+                        origin=tx["origin"],
+                        target=tx["target"],
+                        amount=tx["amount"])
+                        for tx in block["ts"]
+                ],
+                proof=block["proof"],
+                prev_hash=block["prev_hash"])
+                for block in chain_json["blocks"]
+        ],
+        transactions=[
+            Transaction(
+                origin=tx["origin"],
+                target=tx["target"],
+                amount=tx["amount"])
+                for tx in chain_json["transactions"]
+        ])
+
+  def resolve_conflicts(self):
+    auth_chain, auth_length = self.blockchain, len(self.blockchain)
+
+    for neighbor in self.neighbors:
+      res = requests.get(f"http://{neighbor}/chain")
+      if res.status_code == 200 and res.json()["length"] > auth_length:
+         decoded_chain = decode_chain(res.json()["chain"])
+         if Blockchain.is_valid(decoded_chain):
+           auth_length = res.json()["length"]
+           auth_chain = decoded_chain
+
+      self.blockchain = auth_chain
+
+  def define_api(self):
+    def msg(x):
+      return jsonify({"message": x})
+
+    ############################################################################
+    # /
+    ############################################################################
+
+    @self.app.route("/healthz", methods={"GET"})
+    def healthz():
+      return "ok"
+
+    @self.app.route("/reset", methods={"GET"})
+    def reset():
+      self.blockchain = Blockchain()
+      return msg("Success")
+
+    @self.app.route("/mine", methods={"GET"})
+    def mine():
+      # calculate POW
+      proof = self.blockchain.prove_work()
+
+      # reward miner
+      self.blockchain.add_transaction(
+          origin="0", # zero signifies that this is a newly minted coin
+          target=self.identifier,
+          amount=1)
+
+      # publish new block
+      self.blockchain.add_block(proof=proof)
+      return msg("Success")
+
+    ############################################################################
+    # /transactions
+    ############################################################################
+
+    @self.app.route("/transactions/new", methods={"POST"})
+    def new_transaction():
+      payload = request.get_json()
+
+      self.blockchain.add_transaction(
+          origin=payload["origin"],
+          target=payload["target"],
+          amount=payload["amount"])
+      return msg("Success")
+
+    ############################################################################
+    # /blocks
+    ############################################################################
+
+    @self.app.route("/chain", methods={"GET"})
+    def view_blocks():
+      return jsonify({
+          "length": len(self.blockchain),
+          "chain": self.blockchain.dictify(),
+      })
+
+    ############################################################################
+    # /nodes
+    ############################################################################
+    @self.app.route("/node/neighbors", methods={"GET"})
+    def view_neighbors():
+      return jsonify({"neighbors": list(self.neighbors)})
+
+    @self.app.route("/node/register", methods={"POST"})
+    def register_nodes():
+      payload = request.get_json()["neighbors"]
+      payload = set(payload) if payload else set()
+      self.add_neighbors(payload)
+      return msg("Success")
+
+    @self.app.route("/node/resolve", methods={"GET"})
+    def resolve_nodes():
+      self.resolve_conflicts()
+      return msg("Success")
+
+  def run(self):
+    self.app.run(host="0.0.0.0", port=8000)
+
+
+class Blockchain(object):
+  def __init__(self, blocks=None, transactions=None):
+    self.blocks = blocks or []
+    self.transactions = transactions or []
+    self.add_block()
+
+  def __len__(self):
+    return len(self.blocks)
+
+  def __iter__(self):
+    for block in self.blocks:
+      yield block
+
+  def prove_work(self):
+    guess, prev_proof = 0, self.blocks[-1].proof or 0
+    while not is_pow_valid(guess, prev_proof):
+      guess += 1
+    return guess
+
+  def add_block(self, prev_hash=None, proof=None):
+    b = Block(
+        index=len(self),
+        transactions=self.transactions,
+        prev_hash=self.blocks[-1].hash() if self.blocks else None,
+        proof=proof)
+    self.blocks.append(b)
+    return b
+
+  def adopt_blocks(self, json_blocks):
+    pass
+
+  def add_transaction(self, origin=None, target=None, amount=None):
+    tx = Transaction(origin=origin, target=target, amount=amount)
+    self.transactions.append(tx)
+
+  @staticmethod
+  def is_valid(chain):
+    prev_block = next(chain)
+
+    for block in chain:
+      if block.prev_hash != prev_block.hash() or not is_pow_valid(prev_block.proof, block.proof):
+        return False
+      prev_block = block
+
+    return True
+
+  def dictify(self):
+    return {
+        "blocks": [block.dictify() for block in self.blocks],
+        "transactions": [tx.dictify() for tx in self.transactions],
+    }
+
+
+class Block(object):
+  def __init__(self, index=None, ts=None, transactions=None, proof=None, prev_hash=None):
+    self.index = index
+    self.ts = ts or str(datetime.now())
+    self.transactions = transactions
+    self.proof = proof
+    self.prev_hash = prev_hash
+
+  def hash(self):
+    return sha256(self.jsonify().encode()).hexdigest()
+
+  def dictify(self):
+    return {
+        "index": self.index,
+        "ts": self.ts,
+        "transactions": [tx.dictify() for tx in self.transactions],
+        "proof": self.proof,
+        "prev_hash": self.prev_hash,
+    }
+
+  def jsonify(self):
+    return json.dumps(self.dictify(), sort_keys=True)
+
+class Transaction(object):
+  def __init__(self, origin=None, target=None, amount=None):
+    if None in {origin, target, amount}:
+      raise ValueError("To create a Transaction, you must provide origin, target, and amount")
+
+    self.origin = origin
+    self.target = target
+    self.amount = amount
+
+  def dictify(self):
+    return {
+        "origin": self.origin,
+        "target": self.target,
+        "amount": self.amount,
+    }
+
+  def jsonify(self):
+    return json.dumps(self.dictify(), sort_keys=True)
+
+################################################################################
+# Main
+################################################################################
+
+def run():
+  Node(host="0.0.0.0", port=8000).run()
+
+if __name__ == "__main__":
+  run()
diff --git a/users/wpcarro/scratch/blockchain/setup.py b/users/wpcarro/scratch/blockchain/setup.py
new file mode 100644
index 0000000000..e5310565db
--- /dev/null
+++ b/users/wpcarro/scratch/blockchain/setup.py
@@ -0,0 +1,10 @@
+from setuptools import setup
+
+setup(
+    name='main',
+    version='0.0.1',
+    py_modules=['main'],
+    entry_points={
+      'console_scripts': ['main = main:run']
+    },
+)
diff --git a/users/wpcarro/scratch/compiler/.envrc b/users/wpcarro/scratch/compiler/.envrc
new file mode 100644
index 0000000000..ff7eea1f7a
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/.envrc
@@ -0,0 +1,3 @@
+source_up
+
+use_nix
diff --git a/users/wpcarro/scratch/compiler/.gitignore b/users/wpcarro/scratch/compiler/.gitignore
new file mode 100644
index 0000000000..96261d3fc7
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/.gitignore
@@ -0,0 +1,5 @@
+a.out
+*.cmi
+*.cmo
+*.cmx
+*.o
\ No newline at end of file
diff --git a/users/wpcarro/scratch/compiler/debug.ml b/users/wpcarro/scratch/compiler/debug.ml
new file mode 100644
index 0000000000..e39ff13742
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/debug.ml
@@ -0,0 +1,66 @@
+open Types
+
+(* Print x prefixed with tag and return x unchanged. *)
+let print (f : 'a -> string) (tag : string) (x : 'a) : 'a =
+  Printf.printf "%s: %s\n" tag (f x);
+  x
+
+let rec ast (tree : Types.value) : string =
+  match tree with
+  | ValueLiteral (LiteralBool x) ->
+     Printf.sprintf "ValueLiteral (LiteralBool %s)" (string_of_bool x)
+  | ValueLiteral (LiteralInt x) ->
+     Printf.sprintf "ValueLiteral (LiteralInt %s)" (string_of_int x)
+  | ValueVariable x ->
+     Printf.sprintf "ValueVariable %s" x
+  | ValueFunction (x, body) ->
+     Printf.sprintf "ValueFunction (%s, %s)" x (ast body)
+  | ValueApplication (f, x) ->
+     Printf.sprintf "ValueApplication (%s, %s)" (ast f) (ast x)
+  | ValueVarApplication (f, x) ->
+     Printf.sprintf "ValueVarApplication (%s, %s)" f (ast x)
+  | ValueBinder (k, v, x) ->
+      Printf.sprintf "ValueBinder (%s, %s, %s)" k (ast v) (ast x)
+
+let rec value (x : value) : string =
+  match x with
+  | ValueLiteral (LiteralInt x) ->
+     Printf.sprintf "Int %d" x
+  | ValueLiteral (LiteralBool x) ->
+     Printf.sprintf "Bool %b" x
+  | ValueVariable x ->
+     Printf.sprintf "Var %s" x
+  | ValueFunction (name, x) ->
+     Printf.sprintf "Fn %s %s" name (value x)
+  | ValueApplication (f, x) ->
+     Printf.sprintf "App %s %s" (value f) (value x)
+  | ValueVarApplication (name, x) ->
+     Printf.sprintf "App %s %s" name (value x)
+  | ValueBinder (name, x, body) ->
+     Printf.sprintf "Bind %s %s %s" name (value x) (value body)
+
+let rec type' (t : _type) : string =
+  match t with
+  | TypeInt -> "Integer"
+  | TypeBool -> "Boolean"
+  | TypeVariable k -> Printf.sprintf "%s" k
+  | TypeArrow (a, b) -> Printf.sprintf "%s -> %s" (type' a) (type' b)
+
+let quantified_type (q : quantified_type) : string =
+  let QuantifiedType (vars, t) = q in
+  if List.length vars == 0 then
+    Printf.sprintf "%s" (type' t)
+  else
+    Printf.sprintf "forall %s. %s" (String.concat "," vars) (type' t)
+
+let substitution (s : substitution) : string =
+  FromString.fold (fun k v acc -> Printf.sprintf "%s\"%s\" |-> %s;" acc k (type' v)) s ""
+  |> Printf.sprintf "{ %s }"
+
+let env (s : env) : string =
+  FromString.fold (fun k v acc -> Printf.sprintf "%s\"%s\" |-> %s;" acc k (quantified_type v)) s ""
+  |> Printf.sprintf "{ %s }"
+
+let inference (Inference (s, t)) =
+  Printf.sprintf "type: %s; sub: %s" (type' t) (substitution s)
+
diff --git a/users/wpcarro/scratch/compiler/expr_parser.ml b/users/wpcarro/scratch/compiler/expr_parser.ml
new file mode 100644
index 0000000000..797592931a
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/expr_parser.ml
@@ -0,0 +1,187 @@
+(*******************************************************************************
+ * CLI REPL for an s-expression Lambda Calculus.
+ *
+ * Lambda Calculus Expression Language:
+ *
+ *   Helpers:
+ *     symbol     -> [-a-z]+
+ *     string     -> '"' [^"]* '"'
+ *     boolean    -> 'true' | 'false'
+ *     integer    -> [1-9][0-9]*
+ *
+ *   Core:
+ *     expression -> funcdef
+ *     binding    -> '(' 'let' symbol expr expr ')'
+ *     funcdef    -> '(' 'fn' symbol expr ')'
+ *     funccall   -> '(' ( symbol | funcdef) expr ')'
+ *     literal    -> string | boolean | integer
+ *     variable   -> symbol
+ *
+ * Example Usage:
+ *   $ ocamlopt types.ml str.cmxa inference.ml parser.ml expr_parser.ml && ./a.out
+ *   repl> true
+ *   tokens: [ "true" ]
+ *   ast: ValueLiteral (LiteralBool true)
+ *   Boolean
+ *   repl>
+ *
+ ******************************************************************************)
+
+open Parser
+open Inference
+open Debug
+open Prettify
+open Vec
+
+type literal = LiteralBool of bool | LiteralInt of int
+
+let ( let* ) = Option.bind
+let map = Option.map
+
+let tokenize (x : string) : token vec =
+  let xs = Vec.create () in
+  let i = ref 0 in
+  while !i < String.length x do
+    match x.[!i] with
+    | ' ' -> i := !i + 1
+    (* strings *)
+    | '"' ->
+      let curr = ref "\"" in 
+      i := !i + 1;
+      while x.[!i] != '"' do
+        curr := !curr ^ "?";
+        i := !i + 1
+      done;
+      curr := !curr ^ "\"";
+      Vec.append !curr xs;
+      i := !i + 1
+    | '(' ->
+        Vec.append "(" xs;
+        i := !i + 1
+    | ')' ->
+        Vec.append ")" xs;
+        i := !i + 1
+    | _ ->
+        let token = ref "" in
+        while !i < String.length x && not (String.contains "() " x.[!i]) do
+          token := !token ^ String.make 1 x.[!i];
+          i := !i + 1
+        done;
+        Vec.append !token xs
+  done;
+  xs
+
+let parse_symbol (p : parser) : string option =
+  let* x = p#curr in
+  if Str.string_match (Str.regexp "[-a-z][0-9]*") x 0 then
+    begin
+      p#advance;
+      Some x
+    end
+  else
+    None
+
+let parse_variable (p : parser) : Types.value option =
+  let* x = parse_symbol p in
+  Some (Types.ValueVariable x)
+
+let parse_literal (p : parser) : Types.value option =
+  match p#curr with
+  | Some "true" ->
+     p#advance;
+     Some (ValueLiteral (LiteralBool true))
+  | Some "false" ->
+     p#advance;
+     Some (ValueLiteral (LiteralBool false))
+  | Some x ->
+     (match int_of_string_opt x with
+      | Some n ->
+         p#advance;
+         Some (ValueLiteral (LiteralInt n))
+      | _ -> 
+        if String.starts_with ~prefix:"\"" x then
+          begin
+            p#advance;
+            Some (ValueLiteral (LiteralString x))
+          end
+        else
+          parse_variable p)
+  | _ -> None
+
+let rec parse_expression (p : parser) : Types.value option =
+  parse_binding p
+
+and parse_funccall (p : parser) : Types.value option =
+  match (p#curr, p#next) with
+  | (Some "(", Some "(") ->
+     p#advance;
+     let* f = parse_funcdef p in
+     let* x = parse_expression p in
+     p#expect ")";
+     Some (Types.ValueApplication (f, x))
+  | (Some "(", _) ->
+     p#advance;
+     let* f = parse_symbol p in
+     let* x = parse_expression p in
+     p#expect ")";
+     Some (Types.ValueVarApplication (f, x))
+  | _ -> parse_literal p
+
+and parse_funcdef (p : parser) : Types.value option =
+  match (p#curr, p#next) with
+  | (Some "(", Some "fn") ->
+     p#advance;
+     p#advance;
+     let* name = parse_symbol p in
+     let* body = parse_expression p in
+     p#expect ")";
+     Some (Types.ValueFunction (name, body))
+  | _ -> parse_funccall p
+
+and parse_binding (p : parser) : Types.value option =
+  match (p#curr, p#next) with
+  | (Some "(", Some "let") ->
+     p#advance;
+     p#advance;
+     let* name = parse_symbol p in
+     let* value = parse_expression p in
+     let* body = parse_expression p in
+     Some (Types.ValueBinder (name, value, body))
+  | _ -> parse_funcdef p
+
+let print_tokens (xs : string vec) : unit =
+  xs 
+  |> Vec.map (Printf.sprintf "\"%s\"")
+  |> Vec.join ", "
+  |> Printf.sprintf "tokens: [ %s ]"
+  |> print_string 
+  |> print_newline
+
+let parse_language (x : string) : Types.value option =
+  let tokens = tokenize x in
+  print_tokens tokens;
+  parse_expression (new parser tokens)
+
+let main =
+  while true do
+    begin
+      print_string "repl> ";
+      let x = read_line () in
+      match parse_language x with
+      | Some ast ->
+         (match ast |> Debug.print Debug.ast "ast" |> do_infer with
+          | None ->
+             "Type-check failed"
+             |> print_string
+             |> print_newline
+          | Some x ->
+             x
+             |> Prettify.type'
+             |> print_string
+             |> print_newline)
+      | None ->
+         "Could not parse"
+         |> print_string
+         |> print_newline
+    end
+  done
diff --git a/users/wpcarro/scratch/compiler/inference.ml b/users/wpcarro/scratch/compiler/inference.ml
new file mode 100644
index 0000000000..e00904a09e
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/inference.ml
@@ -0,0 +1,183 @@
+(*******************************************************************************
+ * WIP implementation of the Hindley-Milner type system primarily for learning
+ * purposes.
+ *
+ * Wish List:
+ * - TODO Debug this inference (let f (fn x x) f)
+ ******************************************************************************)
+
+open Types
+open Debug
+
+(*******************************************************************************
+ * Library
+ ******************************************************************************)
+
+let ( let* ) = Option.bind
+
+let set_from_list (xs : string list) : set =
+  xs |> List.fold_left (fun acc x -> FromString.add x true acc) FromString.empty
+
+(* Map union that favors the rhs values (i.e. "last writer wins"). *)
+let lww (xs : 'a FromString.t) (ys : 'a FromString.t) : 'a FromString.t =
+  FromString.union (fun k x y -> Some y) xs ys
+
+let emptyEnv : env = FromString.empty
+
+let rec free_type_vars (t : _type) : set =
+  match t with
+  | TypeVariable k -> FromString.singleton k true
+  | TypeInt -> FromString.empty
+  | TypeBool -> FromString.empty
+  | TypeString -> FromString.empty
+  | TypeArrow (a, b) -> lww (free_type_vars a) (free_type_vars b)
+
+let i : int ref = ref 0
+
+let make_type_var () : _type =
+  let res = Printf.sprintf "a%d" !i in
+  i := !i + 1;
+  TypeVariable res
+
+exception OccursCheck
+
+let bind_var (k : string) (t : _type) : substitution =
+  if t == TypeVariable k then FromString.empty
+  else if FromString.exists (fun name _ -> name == k) (free_type_vars t) then
+    raise OccursCheck
+  else FromString.singleton k t
+
+let rec instantiate (q : quantified_type) : _type =
+  let (QuantifiedType (names, t)) = q in
+  match t with
+  | TypeInt -> TypeInt
+  | TypeBool -> TypeBool
+  | TypeString -> TypeString
+  | TypeVariable k ->
+      if List.exists (( == ) k) names then make_type_var () else TypeVariable k
+  | TypeArrow (a, b) ->
+      TypeArrow
+        (instantiate (QuantifiedType (names, a)), instantiate (QuantifiedType (names, b)))
+
+let quantified_type_ftvs (q : quantified_type) : set =
+  let (QuantifiedType (names, t)) = q in
+  lww (free_type_vars t) (names |> set_from_list)
+
+let generalize (env : env) (t : _type) : quantified_type =
+  let envftv =
+    env |> FromString.bindings
+    |> List.map (fun (_, v) -> quantified_type_ftvs v)
+    |> List.fold_left lww FromString.empty
+  in
+  let names =
+    lww (free_type_vars t) envftv
+    |> FromString.bindings
+    |> List.map (fun (k, _) -> k)
+  in
+  QuantifiedType (names, t)
+
+let rec substitute_type (s : substitution) (t : _type) : _type =
+  match t with
+  | TypeVariable k as tvar ->
+     (match FromString.find_opt k s with
+      | Some v -> substitute_type s v
+      | None -> tvar)
+  | TypeArrow (a, b) -> TypeArrow (substitute_type s a, substitute_type s b)
+  | TypeInt -> TypeInt
+  | TypeBool -> TypeBool
+  | TypeString -> TypeString
+
+let substitute_quantified_type (s : substitution) (q : quantified_type) : quantified_type =
+  let (QuantifiedType (names, t)) = q in
+  let s1 =
+    FromString.filter (fun k v -> List.exists (fun x -> k != x) names) s
+  in
+  QuantifiedType (names, substitute_type s1 t)
+
+let substitute_env (s : substitution) (env : env) : env =
+  FromString.map (fun q -> substitute_quantified_type s q) env
+
+let compose_substitutions (xs : substitution list) : substitution =
+  let do_compose_substitutions s1 s2 = lww s2 (FromString.map (substitute_type s2) s1) in
+  List.fold_left do_compose_substitutions FromString.empty xs
+
+let rec unify (a : _type) (b : _type) : substitution option =
+  match (a, b) with
+  | TypeInt, TypeInt -> Some FromString.empty
+  | TypeBool, TypeBool -> Some FromString.empty
+  | TypeString, TypeString -> Some FromString.empty
+  | TypeVariable k, _ -> Some (bind_var k b)
+  | _, TypeVariable k -> Some (bind_var k a)
+  | TypeArrow (a, b), TypeArrow (c, d) ->
+      let* s1 = unify a c in
+      let* s2 = unify (substitute_type s1 b) (substitute_type s1 d) in
+      let s3 = compose_substitutions [s1; s2] in
+      s1 |> Debug.substitution |> Printf.sprintf "s1: %s\n" |> print_string;
+      s2 |> Debug.substitution |> Printf.sprintf "s2: %s\n" |> print_string;
+      s3 |> Debug.substitution |> Printf.sprintf "s3: %s\n" |> print_string;
+      Some s3
+  | _ -> None
+
+let print_env (env : env) =
+  Printf.sprintf "env: %s\n" (Debug.env env)
+  |> print_string
+
+let print_val (x : value) =
+  Printf.sprintf "val: %s\n" (Debug.value x)
+  |> print_string
+
+let print_inference (x : inference option) =
+  match x with
+  | None -> "no inference\n" |> print_string
+  | Some x ->
+     Printf.sprintf "inf: %s\n" (Debug.inference x)
+     |> print_string
+
+let rec infer (env : env) (x : value) : inference option =
+  print_env env;
+  print_val x;
+  let res = match x with
+  | ValueLiteral lit -> (
+      match lit with
+      | LiteralInt _ -> Some (Inference (FromString.empty, TypeInt))
+      | LiteralBool _ -> Some (Inference (FromString.empty, TypeBool))
+      | LiteralString _ -> Some (Inference (FromString.empty, TypeString)))
+  | ValueVariable k ->
+      let* v = FromString.find_opt k env in
+      Some (Inference (FromString.empty, instantiate v))
+  | ValueFunction (param, body) ->
+      let typevar = make_type_var () in
+      let env1 = FromString.remove param env in
+      let env2 = lww (FromString.singleton param (QuantifiedType ([], typevar))) env1 in
+      let* (Inference (s1, t1)) = infer env2 body in
+      Some (Inference (s1, TypeArrow (substitute_type s1 typevar, t1)))
+  | ValueApplication (f, x) ->
+      let result = make_type_var () in
+      let* (Inference (s1, t1)) = infer env f in
+      let* (Inference (s2, t2)) = infer (substitute_env s1 env) x in
+      let* s3 = unify (substitute_type s2 t1) (TypeArrow (t2, result)) in
+      Some (Inference
+              ( compose_substitutions [s3; s2; s1],
+                substitute_type s3 result ))
+  | ValueVarApplication (name, x) ->
+      let* v = FromString.find_opt name env in
+      let t1 = instantiate v in
+      let typevar = make_type_var () in
+      let* (Inference (s2, t2)) = infer env x in
+      let* s3 = unify (substitute_type s2 t1) (TypeArrow (t2, typevar)) in
+      Some (Inference
+              ( compose_substitutions [s2; s3],
+                substitute_type s3 typevar ))
+  | ValueBinder (k, v, body) ->
+      let* (Inference (s1, t1)) = infer env v in
+      let env1 = FromString.remove k env in
+      let tg = generalize (substitute_env s1 env) t1 in
+      let env2 = FromString.add k tg env1 in
+      let* (Inference (s2, t2)) = infer (substitute_env s1 env2) body in
+      Some (Inference (compose_substitutions [s1; s2], t2)) in
+  print_inference res;
+  res
+
+let do_infer (x : value) : _type option =
+  let* Inference (_, t) = infer FromString.empty x in
+  Some t
diff --git a/users/wpcarro/scratch/compiler/parser.ml b/users/wpcarro/scratch/compiler/parser.ml
new file mode 100644
index 0000000000..dc66f2506e
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/parser.ml
@@ -0,0 +1,47 @@
+(****************************************************************************** 
+ * Defines a generic parser class.
+ ******************************************************************************)
+
+open Vec
+
+exception ParseError of string
+
+type token = string
+type state = { i : int; tokens : token vec }
+
+class parser (tokens : token vec) =
+  object (self)
+    val mutable tokens = tokens
+    val mutable i = ref 0
+
+    method advance = i := !i + 1
+    method prev : token option = Vec.get (!i - 1) tokens
+    method curr : token option = Vec.get !i tokens
+    method next : token option = Vec.get (!i + 1) tokens
+
+    method consume : token option =
+      match self#curr with
+      | None -> None
+      | Some x as res ->
+          self#advance;
+          res
+
+    method expect (x : token) =
+      match self#curr with
+      | Some y when x = y -> self#advance
+      | _ -> raise (ParseError (Printf.sprintf "Expected %s" x))
+
+    method matches (x : token) : bool =
+      match self#curr with
+      | None -> false
+      | Some y ->
+          if x = y then
+            begin
+              self#advance;
+              true
+            end
+          else false
+
+    method exhausted : bool = !i >= Vec.length tokens
+    method state : state = { i = !i; tokens }
+  end
diff --git a/users/wpcarro/scratch/compiler/prettify.ml b/users/wpcarro/scratch/compiler/prettify.ml
new file mode 100644
index 0000000000..7903ad3694
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/prettify.ml
@@ -0,0 +1,9 @@
+open Types
+
+(* Pretty-print the type, t. *)
+let rec type' (t : _type) : string =
+  match t with
+  | TypeInt -> "Integer"
+  | TypeBool -> "Boolean"
+  | TypeVariable k -> Printf.sprintf "%s" k
+  | TypeArrow (a, b) -> Printf.sprintf "%s -> %s" (type' a) (type' b)
diff --git a/users/wpcarro/scratch/compiler/register_vm.ml b/users/wpcarro/scratch/compiler/register_vm.ml
new file mode 100644
index 0000000000..0a573048e7
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/register_vm.ml
@@ -0,0 +1,129 @@
+(*
+  Rewriting the Python implementation of the register VM in OCaml to see how
+  how much imperative/mutative programming OCaml allows.
+
+  Note: Some of this code is intentionally not written in a functional style
+  because one of the goals was to see how similar this OCaml implementation
+  could be to the Python implementation.
+
+  Conclusion: It's pretty easy to switch between the two languages.
+
+  Usage: Recommended compilation settings I hastily found online:
+  $ ocamlopt -w +A-42-48 -warn-error +A-3-44 ./register_vm.ml && ./a.out
+
+  Formatting:
+  $ ocamlformat --inplace --enable-outside-detected-project ./register_vm.ml
+ *)
+
+open Vec
+
+type reg = X | Y | Res
+type binop = int -> int -> int
+
+type ast =
+  | Const of int
+  | Add of ast * ast
+  | Sub of ast * ast
+  | Mul of ast * ast
+  | Div of ast * ast
+
+type opcode0 =
+  | Op0AssignRegLit of reg * int
+  | Op0AssignRegReg of reg * reg
+  | Op0BinOp of binop * reg * reg * reg
+  | Op0PushReg of reg
+  | Op0PopAndSet of reg
+  | Op0Null
+
+type opcode1 =
+  | Op1AssignRegLit of int * int
+  | Op1AssignRegReg of int * int
+  | Op1BinOp of (int -> int -> int) * int * int * int
+  | Op1PushReg of int
+  | Op1PopAndSet of int
+  | Op1Null
+
+type opcodes0 = opcode0 vec
+type opcodes1 = opcode1 vec
+
+let registers : int vec = Vec.make 8 0
+let stack : int Stack.t = Stack.create ()
+let reg_idx (r : reg) : int = match r with X -> 0 | Y -> 1 | Res -> 2
+
+let reg_name (r : reg) : string =
+  match r with X -> "x" | Y -> "y" | Res -> "res"
+
+let print_opcodes0 (xs : opcodes0) : opcodes0 =
+  let print_opcode x =
+    match x with
+    | Op0AssignRegLit (r, x) -> Printf.printf "%s <- %d\n" (reg_name r) x
+    | Op0AssignRegReg (dst, src) ->
+        Printf.printf "%s <- $%s\n" (reg_name dst) (reg_name src)
+    | Op0PushReg src -> Printf.printf "push $%s\n" (reg_name src)
+    | Op0PopAndSet dst -> Printf.printf "%s <- pop\n" (reg_name dst)
+    | Op0BinOp (_, lhs, rhs, dst) ->
+        Printf.printf "%s <- $%s ? $%s\n" (reg_name dst) (reg_name lhs)
+          (reg_name rhs)
+    | Op0Null -> ()
+  in
+  Vec.iter print_opcode xs;
+  xs
+
+let rec compile (ast : ast) : opcodes0 =
+  let result : opcodes0 = Vec.create () in
+  (match ast with
+   | Const x -> Vec.append (Op0AssignRegLit (Res, x)) result;
+   | Add (lhs, rhs) -> compile_bin_op ( + ) lhs rhs result
+   | Sub (lhs, rhs) -> compile_bin_op ( - ) lhs rhs result
+   | Mul (lhs, rhs) -> compile_bin_op ( * ) lhs rhs result
+   | Div (lhs, rhs) -> compile_bin_op ( / ) lhs rhs result);
+  result
+
+and compile_bin_op (f : binop) (lhs : ast) (rhs : ast) (result : opcodes0) =
+  lhs |> compile |> Vec.append_to result;
+  Vec.append (Op0PushReg Res) result;
+  rhs |> compile |> Vec.append_to result;
+  Vec.append (Op0PopAndSet X) result;
+  Vec.append (Op0AssignRegReg (Y, Res)) result;
+  Vec.append (Op0BinOp (f, X, Y, Res)) result
+
+let compile_registers (xs : opcodes0) : opcodes1 =
+  let do_compile x =
+    match x with
+    | Op0AssignRegLit (dst, x) -> Op1AssignRegLit (reg_idx dst, x)
+    | Op0AssignRegReg (dst, src) -> Op1AssignRegReg (reg_idx dst, reg_idx src)
+    | Op0PushReg src -> Op1PushReg (reg_idx src)
+    | Op0PopAndSet dst -> Op1PopAndSet (reg_idx dst)
+    | Op0BinOp (f, lhs, rhs, dst) -> Op1BinOp (f, reg_idx lhs, reg_idx rhs, reg_idx dst)
+    | Op0Null -> Op1Null
+  in
+  Vec.map do_compile xs
+
+let eval (xs : opcodes1) : int =
+  let ip = ref 0 in
+  while !ip < Vec.length xs do
+    match Vec.get_unsafe !ip xs with
+    | Op1AssignRegLit (dst, x) ->
+        Vec.set dst x registers;
+        ip := !ip + 1
+    | Op1AssignRegReg (dst, src) ->
+        Vec.set dst (Vec.get_unsafe src registers) registers;
+        ip := !ip + 1
+    | Op1PushReg src ->
+        Stack.push (Vec.get_unsafe src registers) stack;
+        ip := !ip + 1
+    | Op1PopAndSet dst ->
+        Vec.set dst (Stack.pop stack) registers;
+        ip := !ip + 1
+    | Op1BinOp (f, lhs, rhs, dst) ->
+        let lhs = Vec.get_unsafe lhs registers in
+        let rhs = Vec.get_unsafe rhs registers in
+        Vec.set dst (f lhs rhs) registers;
+        ip := !ip + 1
+    | Op1Null -> ip := !ip + 1
+  done;
+  Vec.get_unsafe (reg_idx Res) registers
+;;
+
+Add (Mul (Const 2, Div (Const 100, Const 2)), Const 5)
+|> compile |> print_opcodes0 |> compile_registers |> eval |> print_int
diff --git a/users/wpcarro/scratch/compiler/register_vm.py b/users/wpcarro/scratch/compiler/register_vm.py
new file mode 100644
index 0000000000..302bce5a0e
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/register_vm.py
@@ -0,0 +1,161 @@
+# Silly proof-of-concept register VM.
+
+def compile_binary_op(op, ast):
+    result = []
+    for x in compile(ast[1]):
+        result.append(x)
+    result.append(PUSH_REG)
+    result.append(RES)
+    for x in compile(ast[2]):
+        result.append(x)
+    result.append(ASSIGN_REG_REG)
+    result.append(Y)
+    result.append(RES)
+    result.append(POP)
+    result.append(X)
+    result.append(op)
+    return result
+
+def compile(ast):
+    result = []
+
+    if ast[0] == 'CONST':
+        result.append(ASSIGN_REG_LIT)
+        result.append(RES)
+        result.append(ast[1])
+    elif ast[0] == 'ADD':
+        result += compile_binary_op(ADD, ast)
+    elif ast[0] == 'SUB':
+        result += compile_binary_op(SUB, ast)
+    elif ast[0] == 'MUL':
+        result += compile_binary_op(MUL, ast)
+    elif ast[0] == 'DIV':
+        result += compile_binary_op(DIV, ast)
+    elif ast[0] == 'RETURN':
+        result.append(RETURN)
+    else:
+        raise Exception('Cannot compile unknown AST node: {}'.format(ast[0]))
+
+    return result
+
+# opcodes
+ASSIGN_REG_LIT = 0x0
+ASSIGN_REG_REG = 0x1
+ADD = 0x2
+SUB = 0x3
+MUL = 0x4
+DIV = 0x5
+SWAP = 0x6
+RETURN = 0x7
+PUSH_REG = 0x8
+POP = 0x9
+
+# register indices
+X = 0x0
+Y = 0x1
+RES = 0x2
+
+registers = [0x0] * 8
+stack = []
+
+def reg_name(i):
+    if i == X: return 'x'
+    if i == Y: return 'x'
+    if i == RES: return 'res'
+
+def print_instructions(xs):
+    i = 0
+
+    while i < len(xs):
+        if xs[i] == ASSIGN_REG_LIT:
+            # print('ASSIGN_REG_LIT {} {}'.format(reg_name(xs[i + 1]), xs[i + 2]))
+            print('{} <- {}'.format(reg_name(xs[i + 1]), xs[i + 2]))
+            i += 3
+        elif xs[i] == ASSIGN_REG_REG:
+            # print('ASSIGN_REG_REG {} {}'.format(reg_name(xs[i + 1]), reg_name(xs[i + 2])))
+            print('{} <- ${}'.format(reg_name(xs[i + 1]), reg_name(xs[i + 2])))
+            i += 3
+        elif xs[i] == ADD:
+            print('add')
+            i += 1
+        elif xs[i] == SUB:
+            print('sub')
+            i += 1
+        elif xs[i] == MUL:
+            print('mul')
+            i += 1
+        elif xs[i] == DIV:
+            print('div')
+            i += 1
+        elif xs[i] == PUSH_REG:
+            print('push ${}'.format(reg_name(xs[i + 1])))
+            i += 2
+        elif xs[i] == POP:
+            print('{} <- pop'.format(reg_name(xs[i + 1])))
+            i += 2
+        else:
+            raise Exception('Cannot print instruction: {}'.format(xs[i]))
+
+def eval(instructions):
+    print_instructions(instructions)
+    ip = 0
+    cont = True
+    while ip < len(instructions):
+        if instructions[ip] == ASSIGN_REG_LIT:
+            r = instructions[ip + 1]
+            x = instructions[ip + 2]
+            registers[r] = x
+            ip += 3
+        elif instructions[ip] == ASSIGN_REG_REG:
+            r_dst = instructions[ip + 1]
+            r_src = instructions[ip + 2]
+            registers[r_dst] = registers[r_src]
+            ip += 3
+        elif instructions[ip] == ADD:
+            registers[RES] = registers[X] + registers[Y]
+            ip += 1
+        elif instructions[ip] == MUL:
+            registers[RES] = registers[X] * registers[Y]
+            ip += 1
+        elif instructions[ip] == SUB:
+            registers[RES] = registers[X] - registers[Y]
+            ip += 1
+        elif instructions[ip] == MUL:
+            registers[RES] = registers[X] * registers[Y]
+            ip += 1
+        elif instructions[ip] == DIV:
+            registers[RES] = registers[X] / registers[Y]
+            ip += 1
+        elif instructions[ip] == SWAP:
+            r1 = instructions[ip + 1]
+            r2 = instructions[ip + 2]
+            registers[r1], registers[r2] = registers[r2], registers[r1]
+            ip += 3
+        elif instructions[ip] == RETURN:
+            ip += 1
+            cont = False
+            return registers[RES]
+        elif instructions[ip] == PUSH_REG:
+            src = instructions[ip + 1]
+            stack.append(registers[src])
+            ip += 2
+        elif instructions[ip] == POP:
+            dst = instructions[ip + 1]
+            registers[dst] = stack.pop()
+            ip += 2
+        else:
+            raise Exception('Cannot eval instruction: {}'.format(instructions[ip]))
+    return registers[RES]
+
+def main():
+    ast = ['ADD',
+           ['MUL',
+            ['MUL', ['CONST', 2], ['CONST', 3]],
+            ['DIV', ['CONST', 5], ['CONST', 5]]],
+           ['ADD',
+            ['SUB', ['CONST', 10], ['CONST', 1]],
+            ['MUL', ['CONST', 2], ['CONST', 2]]]]
+
+    print('result: {}'.format(eval(compile(ast))))
+
+main()
diff --git a/users/wpcarro/scratch/compiler/shell.nix b/users/wpcarro/scratch/compiler/shell.nix
new file mode 100644
index 0000000000..ec339eb91d
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/shell.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    ocaml
+    ocamlPackages.utop
+    ocamlformat
+  ];
+}
diff --git a/users/wpcarro/scratch/compiler/tests.ml b/users/wpcarro/scratch/compiler/tests.ml
new file mode 100644
index 0000000000..828cbd16f0
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/tests.ml
@@ -0,0 +1,43 @@
+open Expr_parser
+open Type_parser
+open Inference
+
+type test = { input : string; expect : string; }
+(* type sub_test = { s1 : string; s2 : string; s3 : string } *)
+
+let ( let* ) = Option.bind
+
+let tests = [
+    { input = "((fn x x) 10)"; expect = "Integer"; };
+    { input = "(let f (fn x x) f)"; expect = "a -> a"; };
+]
+
+(* let sub_tests = [ *)
+(*     { *)
+(*       s1 = "{b |-> b -> Int}"; *)
+(*       s2 = "{a: Bool, b: Int, c: Bool}"; *)
+(*       s3 = "{a: Bool, b: Int -> Int, c: Bool}"; *)
+(*     } *)
+(* ] *)
+
+exception FailedAssertion
+exception TestError
+
+let main =
+  tests
+  |> List.iter (fun { input; expect } ->
+         Printf.sprintf ":t %s == %s\n" input expect |> print_string;
+         match (parse_language input, parse_input expect) with
+         | Some ast, Some expected ->
+            (match do_infer ast with
+             | Some actual ->
+                if actual != expected then
+                  begin
+                    print_type actual;
+                    raise FailedAssertion
+                  end
+                else
+                  print_string "Test passed.\n"
+             | _ -> raise TestError)
+         | _ -> raise TestError);
+  print_string "All tests pass!"
diff --git a/users/wpcarro/scratch/compiler/type_parser.ml b/users/wpcarro/scratch/compiler/type_parser.ml
new file mode 100644
index 0000000000..99cc8bbc4f
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/type_parser.ml
@@ -0,0 +1,104 @@
+(******************************************************************************
+ * Type Expression Language:
+ *
+ * Helpers:
+ *   symbol   -> [a-z]
+ *
+ * Core:
+ *   type     -> function
+ *   function -> ( variable | literal ) '->' type
+ *   literal  -> 'Integer' | 'Boolean'
+ *   variable -> symbol
+ ******************************************************************************)
+
+open Types
+open Prettify
+open Parser
+open Inference
+open Vec
+
+type side = LHS | RHS
+
+let ( let* ) = Option.bind
+
+let printsub (s : substitution) =
+  s |> Debug.substitution |> print_string |> print_newline
+
+let tokenize (x : string) : token vec =
+  let xs = Vec.create () in
+  let i = ref 0 in
+  while !i < String.length x do
+    match x.[!i] with
+    | ' ' -> i := !i + 1
+    | _ ->
+       let beg = !i in
+       while (!i < String.length x) && (x.[!i] != ' ') do
+         i := !i + 1
+       done;
+       Vec.append (String.sub x beg (!i - beg)) xs
+  done;
+  xs
+
+let rec parse_type (p : parser) : _type option =
+  parse_function p
+and parse_function (p : parser) : _type option =
+  match p#next with
+  | Some "->" ->
+     let* a = parse_literal p in
+     p#advance;
+     let* b = parse_type p in
+     Some (TypeArrow (a, b))
+  | _ -> parse_literal p
+and parse_literal (p : parser) : _type option =
+  match p#curr with
+  | Some "Integer" | Some "Int" -> p#advance; Some TypeInt
+  | Some "Boolean" | Some "Bool" -> p#advance; Some TypeBool
+  | Some _ -> parse_variable p
+  | None -> None
+and parse_variable (p : parser) : _type option =
+  match p#curr with
+  | Some x when String.length x = 1 -> p#advance; Some (TypeVariable x)
+  | _ -> None
+
+let print_tokens (xs : string vec) =
+  xs
+  |> Vec.map (Printf.sprintf "\"%s\"")
+  |> Vec.join ", "
+  |> Printf.sprintf "tokens: [ %s ]"
+  |> print_string 
+  |> print_newline
+
+let print_type (t : _type) =
+  t |> Debug.type' |> Printf.sprintf "type: %s" |> print_string |> print_newline
+
+let parse_input (x : string) : _type option =
+  let tokens = tokenize x in
+  print_tokens tokens;
+  parse_type (new parser tokens)
+
+(* Continually prompt until user provides a parseable type expression *)
+let rec read_type (arg : side) : _type =
+  let prompt = match arg with
+    | LHS -> "lhs> "
+    | RHS -> "rhs> " in
+  print_string prompt;
+  let x = read_line () in
+  match parse_input x with
+  | None ->
+     print_string "Failed to parse input.\n";
+     read_type arg
+  | Some ast ->
+     print_type ast;
+     ast
+
+let main =
+  while true do
+    begin
+      let lhs = read_type LHS in
+      let rhs = read_type RHS in
+      match unify lhs rhs with
+      | None ->
+         Printf.printf "Cannot unify \"%s\" with \"%s\"\n" (Debug.type' lhs) (Debug.type' rhs)
+      | Some x -> printsub x
+    end
+  done
diff --git a/users/wpcarro/scratch/compiler/types.ml b/users/wpcarro/scratch/compiler/types.ml
new file mode 100644
index 0000000000..0acd05737c
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/types.ml
@@ -0,0 +1,31 @@
+type literal 
+  = LiteralInt of int 
+  | LiteralBool of bool
+  | LiteralString of string
+
+(* Lambda Calculus definition *)
+type value =
+  | ValueLiteral of literal
+  | ValueVariable of string
+  | ValueFunction of string * value
+  | ValueApplication of value * value
+  | ValueVarApplication of string * value
+  | ValueBinder of string * value * value
+
+module FromString = Map.Make (String)
+
+type _type =
+  | TypeInt
+  | TypeBool
+  | TypeString
+  | TypeVariable of string
+  | TypeArrow of _type * _type
+
+type quantified_type = QuantifiedType of string list * _type
+
+type set = bool FromString.t
+type substitution = _type FromString.t
+
+type env = quantified_type FromString.t
+
+type inference = Inference of substitution * _type
diff --git a/users/wpcarro/scratch/compiler/vec.ml b/users/wpcarro/scratch/compiler/vec.ml
new file mode 100644
index 0000000000..549078c5d8
--- /dev/null
+++ b/users/wpcarro/scratch/compiler/vec.ml
@@ -0,0 +1,127 @@
+(****************************************************************************** 
+ * Similar to Python's list
+ *
+ * - mutable
+ * - dynamically resized
+ * - O(1) read
+ * - O(1) write
+ * - O(1) append (average case)
+ *
+ ******************************************************************************)
+
+type 'a vec = {
+  mutable length: int;
+  mutable capacity: int;
+  mutable xs: 'a array;
+}
+
+(****************************************************************************** 
+ * Constructors
+ ******************************************************************************)
+
+let make (size : int) (seed : 'a) : 'a vec = { 
+  length = size;
+  capacity = size;
+  xs = Array.make size seed;
+}
+
+let create () = {
+  length = 0;
+  capacity = 0;
+  xs = [||];
+}
+
+let from_array (xs : 'a array) : 'a vec = {
+  length = Array.length xs;
+  capacity = Array.length xs;
+  xs = xs;
+}
+
+let from_list (xs : 'a list) : 'a vec = 
+  match xs with
+  | [] -> create ()
+  | y::ys -> 
+    let result = {
+      length = List.length xs;
+      capacity = List.length xs;
+      xs = Array.make (List.length xs) y;
+    } in
+    List.iteri (fun i x -> Array.set result.xs i x) xs;
+    result
+
+(****************************************************************************** 
+ * Miscellaneous
+ ******************************************************************************)
+
+let append (x : 'a) (v : 'a vec) =
+  if v.capacity = 0 then
+    begin
+      v.length <- 1;
+      v.capacity <- 1;
+      v.xs <- [|x|];
+    end
+  else if v.length = v.capacity then
+    begin
+      (* According to Wikipedia, Python uses 1.25 as the growth factor *)
+      let new_cap = v.capacity |> float_of_int |> Float.mul 1.25 |> ceil |> int_of_float in
+      let new_xs = Array.make new_cap x in
+      Array.iteri (fun i x -> Array.set new_xs i x) v.xs;
+      v.capacity <- new_cap;
+      v.xs <- new_xs;
+      Array.set v.xs v.length x;
+      v.length <- v.length + 1;
+    end
+  else
+    begin
+      Array.set v.xs v.length x;
+      v.length <- v.length + 1;
+    end
+
+let get (i : int) (v : 'a vec) : 'a option =
+  if i >= v.length then
+    None
+  else
+    Some v.xs.(i)
+
+let get_unsafe (i : int) (v : 'a vec) : 'a =
+  v.xs.(i)
+
+let set (i : int) (x : 'a) (v : 'a vec) : unit =
+  if i < v.length then
+    Array.set v.xs i x
+
+let length (v : 'a vec) : int = 
+  v.length
+
+let update (i : int) (f : 'a -> 'a) (v : 'a vec) : unit =
+  match get i v with
+  | None -> ()
+  | Some x -> set i (f x) v
+
+let iter (f : 'a -> unit) (v : 'a vec) : unit =
+  let n = ref 0 in
+  while !n < v.length do
+    f v.xs.(!n);
+    n := !n + 1;
+  done
+
+let join (sep : string) (v : string vec) : string =
+  if length v = 0 then
+    ""
+  else
+    let i = ref 1 in
+    let result = ref v.xs.(0) in
+    while !i < v.length do
+      result := !result ^ sep ^ v.xs.(!i);
+      i := !i + 1;
+    done;
+    !result
+
+let map (f : 'a -> 'b) (v : 'a vec) : 'b vec =
+  let result = create () in
+  iter (fun x -> append (f x) result) v;
+  result
+
+let append_to (dst : 'a vec) (xs : 'a vec) : unit =
+  iter (fun x -> append x dst) xs
+
diff --git a/users/wpcarro/scratch/crack_the_coding_interview/11_1.py b/users/wpcarro/scratch/crack_the_coding_interview/11_1.py
new file mode 100644
index 0000000000..ec7b65dae0
--- /dev/null
+++ b/users/wpcarro/scratch/crack_the_coding_interview/11_1.py
@@ -0,0 +1,40 @@
+# Implementation for a problem from "Crack the Coding Interview".
+#
+# Dependencies:
+# - python 2.7.16
+# - entr 4.1
+#
+# To run the tests, run: `python 11_1.py`
+# For a tight development loop, run: `echo 11_1.py | entr python /_`
+#
+# Author: William Carroll <wpcarro@gmail.com>
+
+################################################################################
+# Implementation
+################################################################################
+def insert_sorted(xs, ys):
+    """
+    Merges `ys` into `xs` and ensures that the result is sorted.
+
+    Assumptions:
+    - `xs` and `ys` are both sorted.
+    - `xs` has enough unused space to accommodate each element in `ys`.
+    """
+    for y in ys:
+        xi = xs.index(None) - 1
+        yi = xs.index(None)
+        xs[yi] = y
+        while xi != -1 and y < xs[xi]:
+            xs[xi], xs[yi] = xs[yi], xs[xi]
+            xi, yi = xi - 1, yi - 1
+    return xs
+
+################################################################################
+# Tests
+################################################################################
+assert insert_sorted([1, 3, 5, None, None], [2, 4]) == [1, 2, 3, 4, 5]
+assert insert_sorted([None, None], [2, 4]) == [2, 4]
+assert insert_sorted([None, None], [2, 4]) == [2, 4]
+assert insert_sorted([1, 1, None, None], [0, 0]) == [0, 0, 1, 1]
+assert insert_sorted([1, 1, None, None], [1, 1]) == [1, 1, 1, 1]
+print('All tests pass!')
diff --git a/users/wpcarro/scratch/crack_the_coding_interview/to_tree.hs b/users/wpcarro/scratch/crack_the_coding_interview/to_tree.hs
new file mode 100644
index 0000000000..8496d88c0c
--- /dev/null
+++ b/users/wpcarro/scratch/crack_the_coding_interview/to_tree.hs
@@ -0,0 +1,11 @@
+data Tree a = Node a [Tree a] deriving (Show)
+
+withRoot :: [a] -> [Tree a]
+withRoot xs = xs |> toThing |> fmap buildTree
+
+buildTree :: (a, [a])
+
+
+toTree :: [a] -> Tree a
+toTree [x]      = Node x []
+toTree [x | xs] = Node x (toTree xs)
diff --git a/users/wpcarro/scratch/cryptopals/.gitignore b/users/wpcarro/scratch/cryptopals/.gitignore
new file mode 100644
index 0000000000..7aa03e126b
--- /dev/null
+++ b/users/wpcarro/scratch/cryptopals/.gitignore
@@ -0,0 +1 @@
+alice.txt
\ No newline at end of file
diff --git a/users/wpcarro/scratch/cryptopals/README.md b/users/wpcarro/scratch/cryptopals/README.md
new file mode 100644
index 0000000000..f4f5719f9f
--- /dev/null
+++ b/users/wpcarro/scratch/cryptopals/README.md
@@ -0,0 +1,3 @@
+# cryptopals
+
+My solutions for some of the questions at https://cryptopals.com.
diff --git a/users/wpcarro/scratch/cryptopals/set1/4.txt b/users/wpcarro/scratch/cryptopals/set1/4.txt
new file mode 100644
index 0000000000..d172b6cff7
--- /dev/null
+++ b/users/wpcarro/scratch/cryptopals/set1/4.txt
@@ -0,0 +1,327 @@
+0e3647e8592d35514a081243582536ed3de6734059001e3f535ce6271032
+334b041de124f73c18011a50e608097ac308ecee501337ec3e100854201d
+40e127f51c10031d0133590b1e490f3514e05a54143d08222c2a4071e351
+45440b171d5c1b21342e021c3a0eee7373215c4024f0eb733cf006e2040c
+22015e420b07ef21164d5935e82338452f42282c1836e42536284c450de3
+043b452e0268e7eb005a080b360f0642e6e342005217ef04a42f3e43113d
+581e0829214202063d70030845e5301f5a5212ed0818e22f120b211b171b
+ea0b342957394717132307133f143a1357e9ed1f5023034147465c052616
+0c300b355c2051373a051851ee154a023723414c023a08171e1b4f17595e
+550c3e13e80246320b0bec09362542243be42d1d5d060e203e1a0c66ef48
+e159464a582a6a0c50471310084f6b1703221d2e7a54502b2b205c433afa
+ec58ea200e3005090e1725005739eda7342aed311001383fff7c58ef1f11
+01305424231c0d2c41f105057f74510d335440332f1038ec17275f5814e1
+05f12f380720ea2b19e24a07e53c142128354e2827f25a08fb401c3126a6
+0d17272f53063954163d050a541b1f1144305ae37d4932431b1f33140b1b
+0b4f070f071fe92c200e1fa05e4b272e50201b5d493110e429482c100730
+100a3148080f227fe60a132f0c10174fe3f63d1a5d38eb414ca8e82f2b05
+0a19e83c58400a023b13234572e6e4272bf67434331631e63b5e0f00175c
+54520c2ceb45530e0f78111d0b0707e01e4bf43b0606073854324421e6f9
+09e7585353ee4a34190de1354e481c373a1b2b0a136127383e271212191f
+0f060d09fb4f2d5024022c5ff6463c390c2b5f1a5532071a31f33503fcea
+371d39121605584f48217235ee1e0602445c162e4942254c071954321d29
+4a0900e63e5f161e15554045f3594c2a6a77e4e52711602beaf53ae53bed
+29011616565d2a372a605bee39eced31183fe068185c3b445b391fe53232
+e4102337000303452a1e2f2b29493f54ed5a037b3e08311b625cfd005009
+2d560d4b0618203249312a310d5f541f295c3f0f25235c2b20037d1600f3
+2c245155e8253708391a7ceb0d05005c3e080f3f0f0e5a16583b111f4448
+493804044d262eec3759594f212d562420105d6a39e70a0f3957f347070c
+e72d1d1f103807590f4339575e00381074485d2d580249f744052605e11d
+e131570ae95307143a71131729552d001057a4540a1f425b190b572dee34
+2c1655342f02581c202b0a5c17a358291e1506f325550f05365e165c1c5f
+e318164df80b043e5406296e5359271d152f552e155a43eda81f23231d1c
+001de0413e174e18192c061e4b3d1b5626f90e3e1429544a20ee150d0c20
+32e902193219033c58191302441a5c1b584825ea140c290927aaea53e23c
+3a36363a732e32ea3f0e430508204b332c382a19292d5b291122e123446a
+1804115614031f5f571f2b143c5d3c1b257a4b37350f18445a3e08341c3d
+21f2fb250b2e55151e77253a3f0e5f4b2030370a4155e720e73914e35a4a
+510a55583a3c491221397c123a2b14a8305b3b09e71b241d0e51202e1a32
+1b51202f4917232b512a141d6812f03c455df05e5a1c2cee14390b3b593a
+5f5731e5203116ee131a4a4b24112cef5d0822f035e6547d3a0014462f26
+0028fb522104f771501a555d3f581e30e9ec3e49e3e63123432f07794145
+1459f6312f000e5a1373e346e40f211e1b0b0e17000f391f170552150500
+7e301e18325717e3412e022f087be30e5641080151357714e0e0eee15e11
+533258e9360f513b083aa51d2824222f40200a470537ecec392d31070b38
+07e32c180dfa56496a461627542115132a4c284050495b23e2245b093159
+2d3c230a1e5a300f6c3e26ed0d1709434950fd6f1e121335054129e4e4ec
+ef22fa2112311b11584ce43434f46f521a215433f9514fe33d313a3e0838
+34e7f336270c08010f2f544f0f1c1e235c0222644c2632efec061de2115f
+121a42395d4c560d213b0c0a26a7e4f4382718153d5e511158a10b2c021e
+e05d414dfa40222f0c382a03235f4d0d04372d4b7855105e26e44f2e0555
+7f3a4f1351f85b0344223e1177e14707190c0e311f4ca633f5f3e9352372
+01424d5d1a322a0d381717130e181d07240c2c19ecee750b1a37085d014c
+16012c5de55a0314a8260e2759e439123ca0c81c321d454e4e0ee14f4c1d
+0b1415512f38580e4e2a227def242643183c224f0ea146443403022fe9fd
+43eb2b1078322a02192d5b5e0c360d584d0b5e2c13072912ee32f03f4155
+002a52553e08361b0be0074b573e201c164c093a5c0f0159333b59770d5b
+38e63c1c5244301a5a01f26930321256143e1ae05e1120a9eaf20a192d58
+7d54140a152ef4035f09083ded531ee04df55848020656a1342e502649eb
+0c211dfe101702015516341136252f3f06f73247133113f5642d083a3417
+015e3d51433f3c003e5e28030b1d413eee186824504b241e0f0d32373e2b
+2d465040ec130c5c0e2704aa17010c40095207223669110f22f45ea155f7
+14552e2b341e5ce0195351066a23e3283e0ee935444b255a1c5c3cef7614
+372b453d5a357c05142be65b3c17f92d2b134853390a312bf92a531b513d
+5658265f4c0ce4440a20322f591a413034292b312206a01be6453a512d21
+1c585c19f31f785324f8583d1ee02620342b10a236263f105011ee5b0e14
+0f522b550818591a752e5fea0e033322ee5e280a4a1b244f5a2b35341255
+39093c1ced331b264127173f1312e2455fa33b31012c1f4d073c553f5d5e
+18f82d5d07e2430b3b3c1b5b49effb0313173f5d4a2e5c134555ff6b1d1a
+550a20234202726341190311295254f4064205aa515ae0145a23071c4e18
+3f2047024e3ce4555a1b39fa145455012c3afb0f2d11134846182e3c575b
+e3e456571937762828065443153b51152e262f09c937024405284f236432
+012f580c3536ec5c021574541d5c41123a4e661d5f0f5f344a083e3a5e4c
+4216252d01eb0a2a4623621b48360d312c29f33e380650447617124b3e71
+54141e59323606390204e95f1206520e5c084510034d30171c5e744f335d
+1e30061401600b342e171059526d1949431a3f412f56594c183711ea4837
+3131254f11e76f550e1e4d26f1391f44363b151c31281ff45259351da0e6
+5def250d0f3505385f22e9f4112633005d272d092e0138275851f943e90e
+0939165718303b445210095c16390cf04f19450e06f4545c0a0c320e3e23
+1e0b0b1f573f3d0fe05d43090fa8482242300819313142325b1f4b19365b
+0d3b2a5d271e463d2203765245065d5d684a051e5815265b52f3171d3004
+6af423303817a43324394af15a5c482e3b16f5a46f1e0b5c1201214b5fe4
+4030544f3f51151e436e04203a5e3b287ee303490a43fb3b28042f36504e
+1a2d5a03fc0e2c04384046242e2b5e1548101825eb2f285f1a210f022141
+122355e90122281deeed3ba05636003826525d5551572d07030d4935201f
+2a3c484a15410d3b16375d4665271b5c4ce7ee37083d3e512b45204f17f6
+03222801255c2c211a7aeb1e042b4e38e8f1293143203139fb202c325f2b
+06542a28041956350e292bf3fe5c32133a2a171b3a3e4e4e3101381529e3
+4a5209ef24e5f3225e503b143d0e5747323fe7ee3d5b1b5110395619e65a
+1fee0a3945563d2b5703701817584b5f5b54702522f5031b561929ea2d1e
+e7271935100e3c31211b23113a3a5524e02241181a251d521ff52f3c5a76
+144a0efee02f0f5f1d353a1c112e1909234f032953ec591e0a58e55d2cf4
+efee0cf00d0955500210015311467543544708eb590d113d30443d080c1e
+1a562c1f7e2b0030094f051c03e30f4d501a0fe22a2817edfc5e470c3843
+1c3df1135321a8e9241a5607f8305d571aa546001e3254555a11511924
+eb1d3f54ec0fea341a097c502ff1111524e24f5b553e49e8576b5b0e1e33
+72413e2f5329e332ec563b5e65185efefd2c3b4e5f0b5133246d214a401d
+352a0ae632183d200a162e5346110552131514e0553e51003e220d47424b
+1d005c58135f3c1b53300c3b49263928f55625454f3be259361ded1f0834
+2d2457524a1e1204255934174d442a1a7d130f350a123c4a075f5be73e30
+0c0518582d131f39575925e0231833370c482b270e183810415d5aec1900
+453b181df1572735380b0446097f00111f1425070b2e1958102ceb592928
+010a4a2d0b0926082d2f1525562d1d070a7a08152f5b4438a4150b132e20
+2b395d0d5d015d41335d21250de33e3d42152d3f557d1e44e4ee22255d2d
+4a1b5c272d0d1c45072639362e402dee2853e51311262b17aa72eb390410
+e7015f0215352030574b4108e44d0e1a204418e62325ff7f34052f234b2d
+1d563c13202346071d39e34055402b0b392c27f552222d3deb3843ee2c16
+29332a521f3c1b0811e33e1a25520e323e75e01c17473f55071226120d3d
+210b35ee1a0a5335222e35033905170c4f3104eb032d425058367d5a2bf2
+1e553809415efb1c460f2f0ffafaec491e4d4e49510452e8245a366a4106
+e1f92cee0e10142514e7ec13155c412fe901092f1f0fa738280c5eee5e04
+3526291e0b2a5f486a3051041f4c16372f5402e6f70b31a03525190b161a
+260e5e1f0c2e4d7528ef11552fefe247201e4752085c1da903563c162a4b
+2a14ff2e3265e604075e523b24455c364a7f284f3a43051d52152f1119e8
+5f02e55a4b1300063640ef10151002565f0b0c010033a1cbef5d3634484a
+1b121c585b495a5e033a09037f2d1754072c2d49084055172a3c220bed4f
+1613400e1632435c0018482aa55b363d26290ae4405ded280f2b0c271536
+4011250ce02119464a1de43113170356342c272d1d3355555e5706245e0a
+16272d5e545953002e10020875e223010719555410f91ce518420e382456
+0d4037320345f945241a1d090a545a310142442131464f4d10562ae4f05a
+07ee4d4ae12e571e313c1636313134233e495459e548317708563c2c1b2f
+e75803294b36565225552c3406304f0201e43323291b5e0e2159025c2f25
+5e63194411490c44494232237e1b323108573d3f391d1f3537e4165a2b35
+51000a3a264c503b5852072a5636f04f5cea58a42838f5fca876415c3521
+3c14130be511275932055a30aa2d03470c51060009f210543002585f5713
+10f0370c5823115200e5015d083e2f1a5df91d68065c1b03f0080855e529
+02ec00f1462d034123151ba6fc07eb3d5e54e85a3f3ee532fb41791a060b
+0c29274232f93efb3d465544e45e491b042ced245100e3f05c14134c254b
+5741235f051e080401a8013c065627e8ee5432205114243d54320e133f2d
+4a4d181635411f5d084e31ed230c16506d5125415e060e4dcd0e5f3708e3
+2d531c3e22065a5eee07310c145305131800063e4a20094b2006ea131240
+e7335c1c4308160be6aa551a0f5a58243e0b10ee470047683c345e1c5b0c
+5434505ee22a18110d20342e4b53062c4d79042a0a02422e225b2523e95a
+3252212407115c07e15eee06391d0519e9271b641330011f383410281f0e
+2cee2b355233292b595d1c69592f483b54584f7154fd4928560752e333a1
+17272b272f110df5e91c560a39104510240b5c4b0c1c570871e422351927
+c32550ec3f132c0c2458503ae5241d3c0d7911480a073826315620403615
+16e11c270d2b010650145de2290b0beb1e120a3a354b2104064f3b533c4e
+505746313d4d2e3455290a281ee81d50007e1148252528025237715a342a
+1c0a13163e404e40242142061d34185421160220fa031f7a423a08f2e01a
+101d303802f51b0c08ef461259315b553823e622a12d565509e23c624139
+0a3d1309e4384c0eed383846545a035a41ee1771513b090a031e15f45159
+2d4944092a1965542507003b23195758403e175a0a450c5c38114de21141
+eb100fe63a031c4b35eb591845e428441c0d5b0037131f5c160a31243619
+c155ef0d19143e24392507a202581a25491b135c27571d5c5b35250f0bef
+0e1d510556485e39557e044e2cf10457523016473f500b1e36370c17591c
+7e5a19250a5e152b46f5130a094cef08e84704ef10197324464b0114017a
+3b56f126390008343d3c400232ed201667211f0b1a1413080202530b08e2
+4912321b61c90a0cf6ef0a0a0c0f17fa62eb385e2616194526701aff5fe6
+2c57114b0400152d4f2aeb18ed41386c2e3a023a281d1a311eefe750ebab
+3a4353282114593b3e36446d2c5e1e582e335337022930331f211604576a
+295f3bfae9271ae8065a3b4417545c3e5b0df11a53351c78530915392d2e
+074a122ee01b17131e4e124e2322a9560ce4120e37582b24e1036fe93f30
+3c08290121090ef72f25e4f220323444532d3fe71f34553c7b2726131009
+12e84a3308590357a719e74c4f2133690a20031a0b045af63551325b1219
+0e3d4fe03f56523cf40f29e4353455120e3a4f2f26f6a30a2b3e0c5b085a
+57f3315c33e41c0f523426232d0651395c1525274e314d0219163b5f181f
+53471622182739e9e25b473d74e1e7023d095a3134e62d1366563004120e
+230a06431935391d5e0b5543223a3bed2b4358f555401e1b3b5c36470d11
+22100330e03b4812e6120f163b1ef6abebe6f602545ef9a459e33d334c2a
+463405faa655563a43532cfe154bec32fe3345eb2c2700340811213e5006
+14241340112b2916017c270a0652732ee8121132385a6c020c040e2be15b
+251119225c573b105d5c0a371c3d421ef23e22377fee334e0228561b2d15
+2e4c2e373b434b0d0b1b340c300e4b195614130ea03c234c292e14530c46
+0d2c3f08560ee32e5a5b6413355215384442563e69ec294a0eef561e3053
+193c100c0b24231c012273e10d2e12552723586120020b02e45632265e5f
+2c175a11553d4b0b16025e2534180964245b125e5d6e595d1d2a0710580b
+213a175ff30855e4001b305000263f5a5c3c5100163cee00114e3518f33a
+10ed33e65b003012e7131e161d5e2e270b4645f358394118330f5a5b241b
+33e80130f45708395457573406422a3b0d03e6e5053d0d2d151c083337a2
+551be2082b1563c4ec2247140400124d4b6508041b5a472256093aea1847
+7b5a4215415d544115415d5015455447414c155c46155f4058455c5b523f
+0864eb4935144c501103a71851370719301bec57093a0929ea3f18060e55
+2d395e57143359e80efffb13330633ea19e323077b4814571e5a3de73a1f
+52e73c1d53330846243c422d3e1b374b5209543903e3195c041c251b7c04
+2f3c2c28273a12520b482f18340d565d1fe84735474f4a012e1a13502523
+23340f39064e306a08194d544647522e1443041d5ee81f5a18415e34a45f
+475a392637565757730a0c4a517b2821040e1709e028071558021f164c54
+100b2135190505264254005618f51152136125370eef27383e45350118ed
+3947452914e0223f1d040943313c193f295b221e573e1b5723391d090d1f
+2c33141859392b04155e3d4e393b322526ee3e581d1b3d6817374d0c085b
+c2ea5821200f1b755b2d13130f04e26625ea3a5b1e37144d3e473c24030d
+ee15025d2019f757305e3f010e2a453a205f1919391e1a04e86d1a350119
+1a5beb4946180fe0002a031a050b41e5164c58795021e1e45c59e2495c20
+1121394f1e381c3647005b7326250514272b55250a49183be5454ba518eb
+1ee55936102a465d5004371f2e382f1d03144f170d2b0eed042ee341eb19
+ec1014ef3ff1272c3408220a41163708140b2e340e505c560c1e4cf82704
+274b341a454a27a0263408292e362c201c0401462049523b2d55e5132d54
+e259032c444b091e2e4920023f1a7ce40908255228e36f0f2424394b3c48
+34130cf8223f23084813e745e006531a1e464b005e0e1ee405413fe22b4e
+4af201080c0928420c2d491f6e5121e451223b070dee54244b3efc470a0e
+771c161f795df81c22101408465ae7ef0c0604733ee03a20560c1512f217
+2f3a142c4155073a200f04166c565634020a59ea04244ff7413c4bc10858
+240d4752e5fa5a4e1ce255505602e55d4c575e2b59f52b4e0c0a0b464019
+21341927f3380232396707232ae424ea123f5b371d4f65e2471dfbede611
+e10e1c3b1d4d28085c091f135b585709332c56134e4844552f45eb41172a
+3f1b5a343f034832193b153c482f1705392f021f5f0953290c4c43312b36
+3810161aea7001fb5d502b285945255d4ef80131572d2c2e59730e2c3035
+4d59052e1f2242403d440a13263e1d2dea0612125e16033b180834030829
+022917180d07474c295f793e42274b0e1e16581036225c1211e41e04042f
+ec2b41054f2a5f56065e5e0e1f56e13e0a702e1b2f2137020e363a2ae2a4
+53085a3b34e75a1caa2e5d031f261f5f044350312f37455d493f131f3746
+0c295f1724e90b001a4e015d27091a0b3256302c303d51a05956e6331531
+e42b315ce21f0def38144d20242845fa3f3b3b0ce8f4fb2d31ed1d54134b
+2957023141335d35372813263b46581af6535a16404d0b4ff12a207648ec
+e4421e301de25c43010c504e0f562f2018421ce137443b41134b5f542047
+0c5600294e085c1d3622292c480d261213e05c1334385108c145f3090612
+062d2e02267404241f4966e6e010052d3224e72856100b1d22f65a30e863
+324950394700e11a01201a0564525706f1013f353319076b4c0d015a2e24
+2a1be80e2013571522483b1e20321a4e03285d211a444d113924e8f41a1f
+27193ae2302208e73010eaa1292001045737013e10e4745aed2c105b25fb
+1b135d46eaef103e1d330a14337a2a4302441c1631ed07e7100c743a0e35
+1a0957115c293b1c0de853245b5b18e2e12d28421b3230245d7b4a55f355
+e7360e2b3846202a2926fa495e3302ed064d127a17343a1f11032b40e8f5
+06e8f90a3118381c5414157d1434050210363e30500511a00a3d56e10438
+30021931f7193e25a0540ef52658350929380974fb035b1a5d2c042959c7
+151b0c24052d0e56025404390e5a3909edec0d03070f040cff710825363e
+2a2328120b2203320810134a0c0a0ef30b25460bec011c1e26e913575a51
+e12d0948ed3c511416151d1c54082b3e385d14f838510bec4e4b5f585321
+1559305c3a49192a010f04ec11001a3d5a5621e5535358353206521f013f
+172c2c155a3a322009505c290516a2c4e4405a1e0a1e353b6e1a5a4e2f09
+552c34e2432b0df1132b130841000d4007232339a2092a593f142b0a0117
+0931432e452d3aea1d02587d3a3e56ed2a3050e2f9363df366331e421947
+0250094823545b20163f1d0a36a92228ed25564d1a304deae8035c32370d
+4314380e264e2359e6a412504a424328e84434ff30236649353315344a00
+25e33540550d3c15135b0eed451cfd1812eaf2063f085d6e214d121c342f
+37513b2d0a4e3e5211372a3a01334c5d51030c46463e3756290c0d0e1222
+132f175e4c4af1120138e1f2085a3804471f5824555d083de6123f533123
+0de11936062d3d2f12193e135f38ff5e1a531d1426523746004e2c063a27
+49241aee1802311611a50de9592009e936270108214a0c4213a01f09545f
+02e14d2babee204a5c4337135821360d021b7831305963ee0737072f0deb
+1512371119050c0c1142245a004f033650481830230a1925085c1a172726
+3be62f230a4b50526ec9345100252aa729eafa59221b3fa517304e500a15
+5e57f231333c3d0c470a47551733511031362a3bed0f334a3f3136104230
+eb24015d051a151f245905061a37ea273d2239fe02463a5e314d565f0457
+23025f415d290a594e3b5940313347a11c5e41531ff15a385a183829780a
+51e0035f2deb3b163eabe8550e2e0414491f573b5419234a28183044e112
+1d54e8390b26585f3aef5f14206672240c4a5e5d31e01b4d406e351401fa
+e555173e242c753b275d4ee50b2f26501402a71b1b5733ec19ee34284aed
+2ee8f023401c09383b084d623ef324ee5a33065a6d5e365b092c5d0d4501
+3f4e024d4b161e144d5e3b140d1e2944465b491d265603a705373c231240
+544f0d4ea6091e00e62d3e130d4f005139f339001a3b480c221b730be75e
+5f1f4f3e0a0dec3b5128e32960e42d0fee02275528154b10e65c36555a2e
+ea3e311b5b0f5f220b1f1b2914f12111f41213e06232224df5ec0114470d
+51203f1e01e5563851284013514a565e53125223052f47100e5011100201
+3f5bee2305217838582be55958a00245265b0308ec56525b5c114c2d5407
+e6e74818e53602160e45372029eb4de72754ec3f49290d2f5901014c0e7f
+08e715e612380a5c1908285a1222073a023c562907384e4f470444483f34
+1110382b5225343ba6092133483e2d683e1e280227084a1e405e3a341513
+415f240f0c53e3f7196e2252fb0105347f345e531f535a344bf439220916
+5722e7f7fa2f4c2e057e2a025e2dec31413439aa12265f5a3458f81a4b15
+135839401856f337a72fec475a060de239a650163a55392a5b303f051415
+56090f18023a2b16e2364407050d48e1541408281d3aa3e84c5b264c1f33
+1725f9540aec5e10ed293e4e5a5a2d2125f053251a55395d1c2044022231
+292d523ff86a180620075f325e02566659f30423525a053a01f0087f4b3b
+17fe493808f25309251e1325596ce32b42311e5d0c2f58652640582a4b17
+67381a5afb7128150a0043e45b173d2111155c49092d2635370a3a201826
+e62d021d36e03b205d5f1f295c094608342a412122583f3bfc34190be62c
+393a055f59060d454a235326e844243a30285c14e316272524f4f0444f51
+352c3c5b2b5845244f55494940194721f80b120f07392b7c2c5a0508111e
+2f1219430151e60f11150b101e295736361b1e053e4d08f83f230e2c383a
+ef5b1d492610e834330f5cf3a2485d324f2822084f41111f582957191b19
+1e3e223704fe1d2e1f592753e5550f15170b231b4234e945301f5605a670
+300d322759ea0337015c662a0e073809543f2741104835512d0624551751
+373727ef1f41084d0b5c0c0137283b1337026aea1c5ae115064ffa183402
+09152b11e1233e5a0e302a521c5a33181e180026463744a82c024b4bf04e
+1df61df1263fee59135c13400950153d3c5c59183b020b1d2d2c492f4968
+e2000c405a01ede30c4c082e2537443c120f38fc57c43651423e5c3beb1d
+1922182420191b293e163d58020b005f454a0621051a38e80b090a463ee9
+39513f2d47042c0fe5134419ec48490f150f323a5ee7a7e0201e193a5e1b
+2037200a2b1013567b35fb4a0f322c2f49435d091920521c302b413f5f35
+775d1a345b483b35a02a4c3e17ee3a3d5a5b57153613264f23041922432f
+35125b3e0a1d2257eb002a26455e1a2f042e1545e92f0b3408032c4f3551
+2d4c392321300a18ed4f3e2c314d20500052aa3917e55d0d29500754282e
+381b2e263758f63c474a1c23110c2d5f1c220412e91043580656080c0427
+081ce1e5350b6a3535f0e6592e5b543432340e38f008e0324102e45a3f25
+30040c181615362e4d1016160a4a5c006eeb1d2422355a3f1028ff192a07
+53f6354d4b5d121974245c14f0225713331f2e381810101428571725e432
+1a2c06372d5b1419742150042d25003c2650512834ef16e51d183f0f0508
+3d191107251100ee2e4125405a44174f061e0e1e5959e606530e06ed245e
+3f592d47512dec5922500e460e1de7183b4c3c2e583942255a0c5d4d2305
+3438001e482a002d56113a1fe13bed542d3508e22f4e22221431121c1539
+ed445a5d28415073eb18022ef836274d573a48090f2a663058194901405d
+215b143954fc313c1e28584b51e729ef31013b232bfb4c52e2322a2d4557
+5244102e1c3d304450ee01761924e62ff2173305e15809102b2125284dfc
+171a3f010f3639056f2be71c2047581de32e05a20833e1221b0e25362459
+2958280de238084f5a1c292e005be71f3b311e1f415809383d3862260238
+361f56ecee120156375862eb3627185c2519545149e2e50b1f3b0c4e3352
+e6115f440634e4005d273611e41c5d383c3814537b3d23362b084024345b
+10370656372e0236eb4f3303e216505f0e465228383729394faa2f205f34
+2e125b2f2c1d0f1f170e0c51331f0c06291610345c0603791f33253f0e0c
+1c2b080526133aeb3e23571d4cfa1e48057a2a010a490a50391b09514f2e
+59383ae11237e5450029162d2e1d3e09221a160e42ea06ea0ca7c7ecf4ea
+3d3024f34d5c07464bea3b185e110d3a10395d3b2632343cf30ca2e6065a
+262f111c0e15441a4825111b185f1e5756243206125f4603e97e79582d27
+2d5801ee2654113e2da00b58e9260d643c10423e1d1f42093b0d0f7d5102
+3649211f210456051e290f1b4c584d0749220c280b2a50531f262901503e
+52053e3e152b5b2b4415580fec57ef5c08e5ed43cc2d2e5b40355d0d2017
+6d3917263f030c4b55f0025d501e57504a122729293c4c5819680d3001ed
+1e313323324e5e177b171cf70c371541395c0e2b7726e42505483014362e
+1910e4f7253f0a012057e03b1e3b4201362b224ff60e0b3a1d115b043957
+200c1e0b242e5e3b4755f61e3be05c040908f1234358e55562711d2efa0f
+0737e0160b1d13132044080d2325f1f0ee2f00354f2106471131020a5d0b
+3f21060de62c052a17576e2ce729242b3e3621300627f01e52580a480050
+1b381a11351f4f5d22040c3c4b3e7d263714e8e61a571d107a34260a4a51
+edf52314e111207c0b23eb482f441d211f306137152407040e08530a783e
+3c054e2d4e2905275e640220f74f1a193f54e1ed5b4e2a290eab27a55147
+33522817335316ea2f3df957e25e02030601514f09f74c2fedee102d3114
+5d05231d03313826164156110c44e4111f4658005e115e300f413b430300
+380bf53a4331f74627492c133fe8eb3141ee39040def040c1a0ae914e3ed
+5b00f0211f0a091e05582e22f05a5d262e0ce352251d25100b102b11e339
+36053935f051f959093252411e2d5af81f360c0fa15d0b373b1d26323b77
+501424184202206215e05944505c4817514540445b0207025de05b050932
+0a5a114515536f553a352c513f0b12f700345fa51d5efb28222676e559ea
+561b0557403f5f534a574638411e2d3b3c133f79555c333215e6f5f9e7ec
+6658f7210218110f00062752e305f21601442c5310162445ed4d175630f3
+0e2154253c4a22f02e1b0933351314071b521513235031250c18120024a1
+e03555453d1e31775f37331823164c341c09e310463438481019fb0b12fa
+37eee654410e4007501f2c0e42faf50125075b2b46164f165a1003097f08
+2a5332145851553926523965582e5b2f530d5d1e292046344feaed461517
+583d2b06251f551d2f5451110911e6034147481a05166e1f241a5817015b
+1f2d3f5c310c315402200010e24135592435f71b4640540a041012ee1b3f
+5b2010060e2f5a4d045e0b36192f79181b0732183b4a261038340032f434
+3a5557340be6f5315c35112912393503320f54065f0e275a3b5853352008
+1c595d183539220eec123478535337110424f90a355af44c267be848173f
+41053f5cef5f6f56e4f5410a5407281600200b2649460a2e3a3c38492a0c
+4c071a57e9356ee415103c5c53e254063f2019340969e30a2e381d5b2555
+32042f46431d2c44607934ed180c1028136a5f2b26092e3b2c4e2930585a
\ No newline at end of file
diff --git a/users/wpcarro/scratch/cryptopals/set1/c1.py b/users/wpcarro/scratch/cryptopals/set1/c1.py
new file mode 100644
index 0000000000..0dfd6bb6d0
--- /dev/null
+++ b/users/wpcarro/scratch/cryptopals/set1/c1.py
@@ -0,0 +1,19 @@
+from base64 import b64encode
+
+################################################################################
+# Challenge 1
+################################################################################
+
+def hex_to_base64(x):
+    parsed = bytearray.fromhex(x)
+    print(parsed.decode()) # easter egg
+    return b64encode(parsed).decode()
+
+run_tests = False
+if run_tests:
+    actual = hex_to_base64("49276d206b696c6c696e6720796f757220627261696e206c696b65206120706f69736f6e6f7573206d757368726f6f6d")
+    expect = "SSdtIGtpbGxpbmcgeW91ciBicmFpbiBsaWtlIGEgcG9pc29ub3VzIG11c2hyb29t"
+
+    print(actual)
+    assert actual == expect
+    print("Success!")
diff --git a/users/wpcarro/scratch/cryptopals/set1/c2.py b/users/wpcarro/scratch/cryptopals/set1/c2.py
new file mode 100644
index 0000000000..badd60503d
--- /dev/null
+++ b/users/wpcarro/scratch/cryptopals/set1/c2.py
@@ -0,0 +1,20 @@
+def fixed_xor(x, y, decode_hex=True, encode_hex=True):
+    if decode_hex:
+        x = bytearray.fromhex(x)
+        y = bytearray.fromhex(y)
+
+    result = bytearray(len(x))
+
+    for i in range(len(x)):
+        result[i] = x[i] ^ y[i]
+
+    return result.hex() if encode_hex else result
+
+run_tests = False
+if run_tests:
+    actual = fixed_xor("1c0111001f010100061a024b53535009181c", "686974207468652062756c6c277320657965")
+    expect = "746865206b696420646f6e277420706c6179"
+
+    print(actual)
+    assert actual == expect
+    print("Success!")
diff --git a/users/wpcarro/scratch/cryptopals/set1/c3.py b/users/wpcarro/scratch/cryptopals/set1/c3.py
new file mode 100644
index 0000000000..2d84026a7b
--- /dev/null
+++ b/users/wpcarro/scratch/cryptopals/set1/c3.py
@@ -0,0 +1,50 @@
+from c2 import fixed_xor
+from collections import Counter
+
+def frequency_table():
+    with open('alice.txt', 'r') as f:
+        chars = {}
+        while True:
+            l = f.readline()
+            if not l: break
+            for c in l:
+                chars[c] = chars.get(c, 0) + 1
+        result = {}
+        for c, n in chars.items():
+            result[c] = n / len(chars)
+        return result
+
+def score(bs, freqs):
+    return sum(freqs.get(b, 0) for b in bs)
+
+def decode_cipher(x):
+    freqs = frequency_table()
+
+    if not freqs:
+        raise Error("Cannot decode cipher without a populated frequency table")
+
+    x = bytearray.fromhex(x)
+    num_bytes = len(x)
+
+    mx, result, key = 0, None, None
+    for b in range(0, 1 << 8):
+        mask = bytearray(b.to_bytes(1, 'big') * num_bytes)
+        try:
+            y = fixed_xor(x, mask, decode_hex=False, encode_hex=False).decode('ascii')
+        except:
+            continue
+        test = score(y, freqs)
+        if test > mx:
+            result = y
+            mx = test
+            key = mask.decode('ascii')
+    return result
+
+run_tests = False
+if run_tests:
+    print(decode_cipher("1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736"))
+
+################################################################################
+# Answer
+################################################################################
+"Cooking MC's like a pound of bacon"
diff --git a/users/wpcarro/scratch/cryptopals/set1/c4.py b/users/wpcarro/scratch/cryptopals/set1/c4.py
new file mode 100644
index 0000000000..c546419a33
--- /dev/null
+++ b/users/wpcarro/scratch/cryptopals/set1/c4.py
@@ -0,0 +1,23 @@
+import c3
+
+content = None
+with open('4.txt', 'r') as f:
+    content = f.read().splitlines()
+if not content:
+    raise Error("Need content to proceed")
+
+xs = []
+for line in content:
+    try:
+        x = c3.decode_cipher(line)
+        if x: xs.append(x)
+    except:
+        continue
+
+freqs = c3.frequency_table()
+print(max(xs, key=lambda x: c3.score(x, freqs)))
+
+################################################################################
+# Answer
+################################################################################
+"Now that the party is jumping"
diff --git a/users/wpcarro/scratch/cryptopals/set1/c5.py b/users/wpcarro/scratch/cryptopals/set1/c5.py
new file mode 100644
index 0000000000..a098dfe74a
--- /dev/null
+++ b/users/wpcarro/scratch/cryptopals/set1/c5.py
@@ -0,0 +1,16 @@
+def encrypt_repeating_key(x, key):
+    result = b""
+    for i in range(len(x)):
+        b = ord(x[i]) ^ ord(key[i % len(key)])
+        result += b.to_bytes(1, 'big')
+    return result.hex()
+
+cleartext = "Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal"
+expected = "0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f"
+
+run_tests = False
+if run_tests:
+    ciphertext = encrypt_repeating_key(cleartext, "ICE")
+    print(ciphertext)
+    assert ciphertext == expected
+    print("Success!")
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/array-traversals.py b/users/wpcarro/scratch/data_structures_and_algorithms/array-traversals.py
new file mode 100644
index 0000000000..35cb439281
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/array-traversals.py
@@ -0,0 +1,87 @@
+# This is practice for various types of list traversals that turn up.
+
+xs = range(10)
+n = len(xs)
+
+print('---')
+# pythonic left-to-right traversal
+result = ''
+for x in xs:
+    result += str(x)
+print(result)
+
+print('---')
+# left-to-right traversal
+result = ''
+for i in range(n):
+    result += str(xs[i])
+print(result)
+
+print('---')
+# right-to-left traversal
+result = ''
+for i in range(n):
+    result += str(xs[n - 1 - i])
+print(result)
+
+print('---')
+# 2x left-to-right traversal
+result = ''
+for i in range(2 * n):
+    result += str(xs[i % n])
+print(result)
+
+print('---')
+# 2x right-to-left traversal
+result = ''
+for i in range(2 * n):
+    result += str(xs[(n - 1 - i) % n])
+print(result)
+
+################################################################################
+# Table traversals
+################################################################################
+
+table = [[row * 10 + i for i in range(10)] for row in range(3)]
+row_ct = len(table)
+col_ct = len(table[0])
+
+print('---')
+# 3x10 table traversal
+result = ''
+for row in table:
+    r = ''
+    for col in row:
+        r += '{:3d}'.format(col)
+    result += r + '\n'
+print(result[0:-1])
+
+print('---')
+# 3x10 table traversal
+result = ''
+for row in range(row_ct):
+    r = ''
+    for col in range(col_ct):
+        r += '{:3d}'.format(table[row][col])
+    result += r + '\n'
+print(result[0:-1])
+
+print('---')
+# 3x10 table traversal (reverse)
+result = ''
+for row in range(row_ct):
+    r = ''
+    for col in range(col_ct):
+        r += '{:3d}'.format(table[row_ct - 1 - row][col_ct - 1 - col])
+    result += r + '\n'
+print(result)
+
+print('---')
+# 3x10 column-row traversal
+result = ''
+for col in range(col_ct):
+    r = ''
+    for row in range(row_ct):
+        r += '{:3d}'.format(table[row][col])
+    result += r + '\n'
+print(result)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/balanced-binary-tree.py b/users/wpcarro/scratch/data_structures_and_algorithms/balanced-binary-tree.py
new file mode 100644
index 0000000000..01fd965fd5
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/balanced-binary-tree.py
@@ -0,0 +1,145 @@
+import unittest
+from itertools import combinations
+
+
+def balanced(xs):
+    """Return True if `xs` contains no two values that differ by more than
+    one."""
+    if len(xs) == 0 or len(xs) == 1:
+        return True
+    if len(xs) == 2:
+        return math.abs(xs[0] - xs[1]) <= 1
+    else:
+        pass
+
+
+def is_leaf(node):
+    return node.left is None and node.right is None
+
+
+def is_balanced(tree_root):
+    """Returns True if the difference between the depths of any two leaf nodes
+    does not exceed 1."""
+    depths = set()
+    populate_depths(tree_root, 0, depths)
+
+    # cartesian product - only the top half
+    for diff in set(abs(a - b) for a, b in combinations(depths, 2)):
+        if diff > 1:
+            return False
+
+    return True
+
+
+def populate_depths(node, depth, depths):
+    if is_leaf(node):
+        depths.add(depth)
+    else:
+        if node.left is not None:
+            populate_depths(node.left, depth + 1, depths)
+        if node.right is not None:
+            populate_depths(node.right, depth + 1, depths)
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    class BinaryTreeNode(object):
+        def __init__(self, value):
+            self.value = value
+            self.left = None
+            self.right = None
+
+        def insert_left(self, value):
+            self.left = Test.BinaryTreeNode(value)
+            return self.left
+
+        def insert_right(self, value):
+            self.right = Test.BinaryTreeNode(value)
+            return self.right
+
+    def test_full_tree(self):
+        tree = Test.BinaryTreeNode(5)
+        left = tree.insert_left(8)
+        right = tree.insert_right(6)
+        left.insert_left(1)
+        left.insert_right(2)
+        right.insert_left(3)
+        right.insert_right(4)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_both_leaves_at_the_same_depth(self):
+        tree = Test.BinaryTreeNode(3)
+        left = tree.insert_left(4)
+        right = tree.insert_right(2)
+        left.insert_left(1)
+        right.insert_right(9)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_leaf_heights_differ_by_one(self):
+        tree = Test.BinaryTreeNode(6)
+        left = tree.insert_left(1)
+        right = tree.insert_right(0)
+        right.insert_right(7)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_leaf_heights_differ_by_two(self):
+        tree = Test.BinaryTreeNode(6)
+        left = tree.insert_left(1)
+        right = tree.insert_right(0)
+        right_right = right.insert_right(7)
+        right_right.insert_right(8)
+        result = is_balanced(tree)
+        self.assertFalse(result)
+
+    def test_three_leaves_total(self):
+        tree = Test.BinaryTreeNode(1)
+        left = tree.insert_left(5)
+        right = tree.insert_right(9)
+        right.insert_left(8)
+        right.insert_right(5)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_both_subtrees_superbalanced(self):
+        tree = Test.BinaryTreeNode(1)
+        left = tree.insert_left(5)
+        right = tree.insert_right(9)
+        right_left = right.insert_left(8)
+        right.insert_right(5)
+        right_left.insert_left(7)
+        result = is_balanced(tree)
+        self.assertFalse(result)
+
+    def test_both_subtrees_superbalanced_two(self):
+        tree = Test.BinaryTreeNode(1)
+        left = tree.insert_left(2)
+        right = tree.insert_right(4)
+        left.insert_left(3)
+        left_right = left.insert_right(7)
+        left_right.insert_right(8)
+        right_right = right.insert_right(5)
+        right_right_right = right_right.insert_right(6)
+        right_right_right.insert_right(9)
+        result = is_balanced(tree)
+        self.assertFalse(result)
+
+    def test_only_one_node(self):
+        tree = Test.BinaryTreeNode(1)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_linked_list_tree(self):
+        tree = Test.BinaryTreeNode(1)
+        right = tree.insert_right(2)
+        right_right = right.insert_right(3)
+        right_right.insert_right(4)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/bit-manipulation.py b/users/wpcarro/scratch/data_structures_and_algorithms/bit-manipulation.py
new file mode 100644
index 0000000000..dc30bb5088
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/bit-manipulation.py
@@ -0,0 +1,32 @@
+def test(x, i):
+    return x & (1 << i) != 0
+
+
+def set(x, i):
+    return x | (1 << i)
+
+
+def clear(x, i):
+    return x & ~(1 << i)
+
+
+def toggle(x, i):
+    if test(x, i):
+        return clear(x, i)
+    else:
+        return set(x, i)
+
+
+def test_single(x):
+    if x == 0:
+        return False
+    else:
+        return x & (x - 1) == 0
+
+
+print(test(0b1010, 3))
+print('{0:b}'.format(set(0b1010, 1)))
+print('{0:b}'.format(clear(0b1010, 1)))
+print('{0:b}'.format(toggle(0b1010, 2)))
+print(test_single(0b1010))
+print(test_single(0b1000))
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/bracket-validator.py b/users/wpcarro/scratch/data_structures_and_algorithms/bracket-validator.py
new file mode 100644
index 0000000000..a50f8b074e
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/bracket-validator.py
@@ -0,0 +1,63 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+# is_valid :: String -> Boolean
+def is_valid(xs):
+    s = []
+    seeking = {
+        '}': '{',
+        ']': '[',
+        ')': '(',
+    }
+    openers = seeking.values()
+    closers = seeking.keys()
+    for c in xs:
+        if c in openers:
+            s.append(c)
+        elif c in closers:
+            if not s:
+                return False
+            elif s[-1] != seeking.get(c):
+                return False
+            else:
+                s.pop()
+    return len(s) == 0
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_valid_short_code(self):
+        result = is_valid('()')
+        self.assertTrue(result)
+
+    def test_valid_longer_code(self):
+        result = is_valid('([]{[]})[]{{}()}')
+        self.assertTrue(result)
+
+    def test_interleaved_openers_and_closers(self):
+        result = is_valid('([)]')
+        self.assertFalse(result)
+
+    def test_mismatched_opener_and_closer(self):
+        result = is_valid('([][]}')
+        self.assertFalse(result)
+
+    def test_missing_closer(self):
+        result = is_valid('[[]()')
+        self.assertFalse(result)
+
+    def test_extra_closer(self):
+        result = is_valid('[[]]())')
+        self.assertFalse(result)
+
+    def test_empty_string(self):
+        result = is_valid('')
+        self.assertTrue(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/bst-checker.py b/users/wpcarro/scratch/data_structures_and_algorithms/bst-checker.py
new file mode 100644
index 0000000000..689be97a85
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/bst-checker.py
@@ -0,0 +1,121 @@
+import unittest
+
+
+################################################################################
+# Implementation
+################################################################################
+# is_leaf :: Node(a) -> Boolean
+def is_leaf(node):
+    return not node.left and not node.right
+
+
+# is_binary_search_tree :: Node(Integer) -> Set(Int) -> Set(Int) -> Boolean
+def is_binary_search_tree_a(node, la=set(), ra=set()):
+    """My first solution for this problem."""
+    for x in la:
+        if not node.value < x:
+            return False
+    for x in ra:
+        if not node.value > x:
+            return False
+    if is_leaf(node):
+        return True
+    elif not node.left:
+        return is_binary_search_tree(
+            node.right,
+            la=la,
+            ra=ra ^ {node.value},
+        )
+    elif not node.right:
+        return is_binary_search_tree(node.left, la=la ^ {node.value}, ra=ra)
+    else:
+        return all([
+            is_binary_search_tree(node.left, la=la ^ {node.value}, ra=ra),
+            is_binary_search_tree(node.right, la=la, ra=ra ^ {node.value})
+        ])
+
+
+# is_binary_search_tree :: Node(Int) -> Maybe(Int) -> Maybe(Int) -> Boolean
+def is_binary_search_tree(node, lb=None, ub=None):
+    if lb:
+        if node.value < lb:
+            return False
+    if ub:
+        if node.value > ub:
+            return False
+    if is_leaf(node):
+        return True
+    elif not node.right:
+        return is_binary_search_tree(node.left, lb=lb, ub=node.value)
+    elif not node.left:
+        return is_binary_search_tree(node.right, lb=node.value, ub=ub)
+    else:
+        return is_binary_search_tree(
+            node.left, lb=lb, ub=node.value) and is_binary_search_tree(
+                node.right, lb=node.value, ub=ub)
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    class BinaryTreeNode(object):
+        def __init__(self, value):
+            self.value = value
+            self.left = None
+            self.right = None
+
+        def insert_left(self, value):
+            self.left = Test.BinaryTreeNode(value)
+            return self.left
+
+        def insert_right(self, value):
+            self.right = Test.BinaryTreeNode(value)
+            return self.right
+
+    def test_valid_full_tree(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(30)
+        right = tree.insert_right(70)
+        left.insert_left(10)
+        left.insert_right(40)
+        right.insert_left(60)
+        right.insert_right(80)
+        result = is_binary_search_tree(tree)
+        self.assertTrue(result)
+
+    def test_both_subtrees_valid(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(30)
+        right = tree.insert_right(80)
+        left.insert_left(20)
+        left.insert_right(60)
+        right.insert_left(70)
+        right.insert_right(90)
+        result = is_binary_search_tree(tree)
+        self.assertFalse(result)
+
+    def test_descending_linked_list(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(40)
+        left_left = left.insert_left(30)
+        left_left_left = left_left.insert_left(20)
+        left_left_left.insert_left(10)
+        result = is_binary_search_tree(tree)
+        self.assertTrue(result)
+
+    def test_out_of_order_linked_list(self):
+        tree = Test.BinaryTreeNode(50)
+        right = tree.insert_right(70)
+        right_right = right.insert_right(60)
+        right_right.insert_right(80)
+        result = is_binary_search_tree(tree)
+        self.assertFalse(result)
+
+    def test_one_node_tree(self):
+        tree = Test.BinaryTreeNode(50)
+        result = is_binary_search_tree(tree)
+        self.assertTrue(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/cafe-order-checker.py b/users/wpcarro/scratch/data_structures_and_algorithms/cafe-order-checker.py
new file mode 100644
index 0000000000..e34a2b136a
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/cafe-order-checker.py
@@ -0,0 +1,91 @@
+import unittest
+
+
+################################################################################
+# Implementation
+################################################################################
+def is_first_come_first_served(to, di, xs):
+    # All the guards, assertions we should need.
+    if to == di == xs == []:
+        return True
+    elif to == di == []:
+        return False
+    elif to == []:
+        return di == xs
+    elif to == []:
+        return di == xs
+    elif di == []:
+        return to == xs
+    elif xs == []:
+        return False
+    elif len(xs) != (len(to) + len(di)):
+        return False
+
+    fst, snd = to, di
+
+    if xs[0] == to[0]:
+        fst, snd = to, di
+    elif xs[0] == di[0]:
+        fst, snd = di, to
+    else:
+        return False
+
+    fst_done, snd_done = False, False
+    fi, si = 1, 0
+
+    for i in range(1, len(xs)):
+        # Short-circuit and avoid index-out-of-bounds without introducing overly
+        # defensive, sloppy code.
+        if fst_done:
+            return snd[si:] == xs[i:]
+        elif snd_done:
+            return fst[fi:] == xs[i:]
+
+        if fst[fi] == xs[i]:
+            fi += 1
+        elif snd[si] == xs[i]:
+            si += 1
+        else:
+            return False
+
+        fst_done, snd_done = fi == len(fst), si == len(snd)
+
+    return True
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_both_registers_have_same_number_of_orders(self):
+        result = is_first_come_first_served([1, 4, 5], [2, 3, 6],
+                                            [1, 2, 3, 4, 5, 6])
+        self.assertTrue(result)
+
+    def test_registers_have_different_lengths(self):
+        result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 2, 6, 3, 5])
+        self.assertFalse(result)
+
+    def test_one_register_is_empty(self):
+        result = is_first_come_first_served([], [2, 3, 6], [2, 3, 6])
+        self.assertTrue(result)
+
+    def test_served_orders_is_missing_orders(self):
+        result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 6, 3, 5])
+        self.assertFalse(result)
+
+    def test_served_orders_has_extra_orders(self):
+        result = is_first_come_first_served([1, 5], [2, 3, 6],
+                                            [1, 2, 3, 5, 6, 8])
+        self.assertFalse(result)
+
+    def test_one_register_has_extra_orders(self):
+        result = is_first_come_first_served([1, 9], [7, 8], [1, 7, 8])
+        self.assertFalse(result)
+
+    def test_one_register_has_unserved_orders(self):
+        result = is_first_come_first_served([55, 9], [7, 8], [1, 7, 8, 9])
+        self.assertFalse(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/cake-thief.py b/users/wpcarro/scratch/data_structures_and_algorithms/cake-thief.py
new file mode 100644
index 0000000000..9eddb34b2d
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/cake-thief.py
@@ -0,0 +1,71 @@
+import unittest
+from math import floor
+
+
+################################################################################
+# Solution
+################################################################################
+def max_duffel_bag_value(xs, cap):
+    ct = (cap + 1)
+    maxes = [0] * ct
+    for c in range(cap + 1):
+        for w, v in xs:
+            if w == 0 and v > 0:
+                return float('inf')
+            if w == c:
+                maxes[c:] = [max(maxes[c], v)] * (ct - c)
+            elif w < c:
+                d = c - w
+                maxes[c:] = [max(maxes[c], v + maxes[d])] * (ct - c)
+            else:
+                continue
+    return maxes[cap]
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_one_cake(self):
+        actual = max_duffel_bag_value([(2, 1)], 9)
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_two_cakes(self):
+        actual = max_duffel_bag_value([(4, 4), (5, 5)], 9)
+        expected = 9
+        self.assertEqual(actual, expected)
+
+    def test_only_take_less_valuable_cake(self):
+        actual = max_duffel_bag_value([(4, 4), (5, 5)], 12)
+        expected = 12
+        self.assertEqual(actual, expected)
+
+    def test_lots_of_cakes(self):
+        actual = max_duffel_bag_value([(2, 3), (3, 6), (5, 1), (6, 1), (7, 1),
+                                       (8, 1)], 7)
+        expected = 12
+        self.assertEqual(actual, expected)
+
+    def test_value_to_weight_ratio_is_not_optimal(self):
+        actual = max_duffel_bag_value([(51, 52), (50, 50)], 100)
+        expected = 100
+        self.assertEqual(actual, expected)
+
+    def test_zero_capacity(self):
+        actual = max_duffel_bag_value([(1, 2)], 0)
+        expected = 0
+        self.assertEqual(actual, expected)
+
+    def test_cake_with_zero_value_and_weight(self):
+        actual = max_duffel_bag_value([(0, 0), (2, 1)], 7)
+        expected = 3
+        self.assertEqual(actual, expected)
+
+    def test_cake_with_non_zero_value_and_zero_weight(self):
+        actual = max_duffel_bag_value([(0, 5)], 5)
+        expected = float('inf')
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/coins.py b/users/wpcarro/scratch/data_structures_and_algorithms/coins.py
new file mode 100644
index 0000000000..eb5754f982
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/coins.py
@@ -0,0 +1,57 @@
+import unittest
+from math import floor
+
+################################################################################
+# Solution
+################################################################################
+
+# change_possibilities :: Int -> [Int] -> Int
+def change_possibilities(n, xs):
+    combinations = [0] * (n + 1)
+    combinations[0] = 1
+
+    for x in xs:
+        for i in range(len(combinations)):
+            if i >= x:
+                combinations[i] += combinations[i - x]
+
+    return combinations[n]
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+
+    def test_sample_input(self):
+        actual = change_possibilities(4, (1, 2, 3))
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_one_way_to_make_zero_cents(self):
+        actual = change_possibilities(0, (1, 2))
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_no_ways_if_no_coins(self):
+        actual = change_possibilities(1, ())
+        expected = 0
+        self.assertEqual(actual, expected)
+
+    def test_big_coin_value(self):
+        actual = change_possibilities(5, (25, 50))
+        expected = 0
+        self.assertEqual(actual, expected)
+
+    def test_big_target_amount(self):
+        actual = change_possibilities(50, (5, 10))
+        expected = 6
+        self.assertEqual(actual, expected)
+
+    def test_change_for_one_dollar(self):
+        actual = change_possibilities(100, (1, 5, 10, 25, 50))
+        expected = 292
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/conways-game-of-life.py b/users/wpcarro/scratch/data_structures_and_algorithms/conways-game-of-life.py
new file mode 100644
index 0000000000..3836bcd0c6
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/conways-game-of-life.py
@@ -0,0 +1,78 @@
+from itertools import product
+from random import choice
+from time import sleep
+from os import system
+from math import floor
+from colorama import Back, Fore, Style
+
+################################################################################
+# Simulation of Conway's Game of Life. The goal here was to write this with a
+# small amount of code as a proof-of-concept that could be run in the terminal.
+#
+# If you'd like to tinker with the rules, see the conditionals defined in the
+# `advance/1` function. For other parameters, like the board size and refresh
+# rate, refer to the while-loop defined at the bottom of this file.
+################################################################################
+
+
+def init_board(n, init_alive_percentage):
+    """Initialize a board of size `n` by `n`. Supply a percentage,
+    `init_alive_percentage`, representing the number of cells in the board that
+    should be alive from the start."""
+    alive_count = floor(n * init_alive_percentage)
+    distribution = [True] * alive_count + [False] * (n - alive_count)
+    return [[choice(distribution) for _ in range(n)] for _ in range(n)]
+
+
+def neighbors(coord, board):
+    """Return the neighbors for a given `coord` on a `board`."""
+    n = len(board)
+    row, col = coord
+    return [
+        board[(row + row_d) % n][(col + col_d) % n]
+        for row_d, col_d in product([-1, 0, 1], [-1, 0, 1])
+        if (row_d, col_d) != (0, 0)
+    ]
+
+
+def advance(board):
+    """Advance the state of the `board` from T[n] to T[n+1]."""
+    n = len(board)
+    new_board = [[False for _ in range(n)] for _ in range(n)]
+    for row in range(n):
+        for col in range(n):
+            alive_count = len([x for x in neighbors((row, col), board) if x])
+            # Loneliness
+            if alive_count == 0:
+                new_board[row][col] = False
+            # Status Quo
+            elif alive_count == 1:
+                new_board[row][col] = board[row][col]
+            # Cooperation
+            elif alive_count == 2:
+                new_board[row][col] = True
+            # Resource starvation
+            elif alive_count >= 3:
+                new_board[row][col] = False
+    return new_board
+
+
+def print_board(board):
+    """Print the game `board` in a human-readable way."""
+    result = ''
+    for row in board:
+        for col in row:
+            if col:
+                result += Back.GREEN + '1 ' + Style.RESET_ALL
+            else:
+                result += Back.RED + '0 ' + Style.RESET_ALL
+        result += '\n'
+    print(result)
+
+
+board = init_board(100, 0.50)
+while True:
+    system('clear')
+    print_board(board)
+    sleep(0.15)
+    board = advance(board)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/delete-node.py b/users/wpcarro/scratch/data_structures_and_algorithms/delete-node.py
new file mode 100644
index 0000000000..7e431e2249
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/delete-node.py
@@ -0,0 +1,60 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+def delete_node(x):
+    if not x.next:
+        raise Exception('Cannot delete the last node in a linked list.')
+    else:
+        x.value = x.next.value
+        x.next = x.next.next
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    class LinkedListNode(object):
+        def __init__(self, value, next=None):
+            self.value = value
+            self.next = next
+
+        def get_values(self):
+            node = self
+            values = []
+            while node is not None:
+                values.append(node.value)
+                node = node.next
+            return values
+
+    def setUp(self):
+        self.fourth = Test.LinkedListNode(4)
+        self.third = Test.LinkedListNode(3, self.fourth)
+        self.second = Test.LinkedListNode(2, self.third)
+        self.first = Test.LinkedListNode(1, self.second)
+
+    def test_node_at_beginning(self):
+        delete_node(self.first)
+        actual = self.first.get_values()
+        expected = [2, 3, 4]
+        self.assertEqual(actual, expected)
+
+    def test_node_in_middle(self):
+        delete_node(self.second)
+        actual = self.first.get_values()
+        expected = [1, 3, 4]
+        self.assertEqual(actual, expected)
+
+    def test_node_at_end(self):
+        with self.assertRaises(Exception):
+            delete_node(self.fourth)
+
+    def test_one_node_in_list(self):
+        unique = Test.LinkedListNode(1)
+        with self.assertRaises(Exception):
+            delete_node(unique)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/dft.py b/users/wpcarro/scratch/data_structures_and_algorithms/dft.py
new file mode 100644
index 0000000000..127d48c186
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/dft.py
@@ -0,0 +1,65 @@
+from random import choice
+
+
+class Node(object):
+    def __init__(self, value=None, left=None, right=None):
+        self.value = value
+        self.left = left
+        self.right = left
+
+
+def p(node, indent=0):
+    print(indent * ' ' + '|-' + str(node.value))
+    if node.left is not None:
+        p(node.left, indent=indent + 2)
+    if node.right is not None:
+        p(node.right, indent=indent + 2)
+
+
+# read trees (i.e. traversing, parsing)
+# write trees (i.e. generating, printing)
+def random(d=0):
+    left = None
+    right = None
+
+    if choice([True, False]):
+        left = random(d + 1)
+
+    if choice([True, False]):
+        right = random(d + 1)
+
+    return Node(
+        value=d,
+        left=left,
+        right=right,
+    )
+
+
+################################################################################
+# DFTs can be:
+# - imperative (mutable)
+# - functional (immutable)
+# - iterative
+# - recursive
+################################################################################
+
+
+# Iterative
+def traverse(node, f):
+    stack = [(node, 0)]
+
+    while len(stack):
+        node, depth = stack.pop()
+        f(node, depth)
+        print(depth)
+
+        if node.left is not None:
+            stack.append((node.left, depth + 1))
+        if node.right is not None:
+            stack.append((node.right, depth + 1))
+
+
+print('----------------------------------------------------------------------')
+for _ in range(10):
+    traverse(random(), lambda _, d: print(d))
+print()
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/dijkstra-shortest-path.py b/users/wpcarro/scratch/data_structures_and_algorithms/dijkstra-shortest-path.py
new file mode 100644
index 0000000000..03907f6040
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/dijkstra-shortest-path.py
@@ -0,0 +1,48 @@
+from collections import deque
+from heapq import heappush, heappop
+from fixtures import weighted_graph
+
+
+def put(t, x, xs):
+    if t == 'stack':
+        return xs.append(x)
+    if t == 'queue':
+        return xs.append(x)
+    if t == 'priority':
+        return heappush(xs, x)
+
+
+def pop(t, xs):
+    if t == 'stack':
+        return xs.pop()
+    if t == 'queue':
+        return xs.popleft()
+    if t == 'priority':
+        return heappop(xs)
+
+
+# shortest_path :: Vertex -> Vertex -> Graph -> [Vertex]
+def shortest_path(a, b, g):
+    """Returns the shortest path from vertex a to vertex b in graph g."""
+    t = 'priority'
+    xs = []
+    seen = set()
+    # Map(Weight, [Vertex])
+    m = {}
+
+    put(t, (0, [a], a), xs)
+
+    while xs:
+        w0, path, v = pop(t, xs)
+
+        seen.add(v)
+        if v == b:
+            m[w0] = path
+        for w1, x in g.get(v):
+            if x not in seen:
+                put(t, (w0 + w1, path + [x], x), xs)
+
+    return m
+
+
+print(shortest_path('a', 'f', graph_a))
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/find-duplicate-optimize-for-space-beast.py b/users/wpcarro/scratch/data_structures_and_algorithms/find-duplicate-optimize-for-space-beast.py
new file mode 100644
index 0000000000..93fdd9eed2
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/find-duplicate-optimize-for-space-beast.py
@@ -0,0 +1,56 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+def find_duplicate(xs):
+    self_ref_count = 0
+    for i in range(len(xs)):
+        if xs[i] == i + 1:
+            self_ref_count += 1
+    hops = len(xs) - 1 - self_ref_count
+    current = xs[-1]
+    while hops > 0:
+        current = xs[current - 1]
+        hops -= 1
+    return current
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    # TODO: Debug why this fails.
+    def test_darren_from_interview_cake(self):
+        actual = find_duplicate([4, 1, 8, 3, 2, 7, 6, 5, 4])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_just_the_repeated_number(self):
+        actual = find_duplicate([1, 1])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_short_list(self):
+        actual = find_duplicate([1, 2, 3, 2])
+        expected = 2
+        self.assertEqual(actual, expected)
+
+    def test_last_cycle(self):
+        actual = find_duplicate([3, 4, 2, 3, 1, 5])
+        expected = 3
+        self.assertEqual(actual, expected)
+
+    def test_medium_list(self):
+        actual = find_duplicate([1, 2, 5, 5, 5, 5])
+        expected = 5
+        self.assertEqual(actual, expected)
+
+    def test_long_list(self):
+        actual = find_duplicate([4, 1, 4, 8, 3, 2, 7, 6, 5])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/find-duplicate-optimize-for-space.py b/users/wpcarro/scratch/data_structures_and_algorithms/find-duplicate-optimize-for-space.py
new file mode 100644
index 0000000000..e2739f0f60
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/find-duplicate-optimize-for-space.py
@@ -0,0 +1,61 @@
+from math import floor
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+def bounds(r):
+    ct = len(r)
+    if ct % 2 == 0:
+        h = int(ct / 2)
+        return ct, h
+    else:
+        h = floor(ct / 2)
+        return ct, h
+
+
+def find_repeat(xs):
+    ct, h = bounds(xs)
+    rl = range(1, h + 1)
+    rr = range(h + 1, ct)
+    while True:
+        nl = len([None for x in xs if x in rl])
+        nr = len([None for x in xs if x in rr])
+        branch = rl if nl > nr else rr
+        if len(branch) == 1:
+            return branch[0]
+        ct, h = bounds(branch)
+        rl = range(branch[0], branch[0])
+        rr = range(branch[0] + h, branch[-1] + 1)
+    raise Exception(
+        'We could not find any duplicates in xs. Perhaps xs did not adhere to the usage contract.'
+    )
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_just_the_repeated_number(self):
+        actual = find_repeat([1, 1])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_short_list(self):
+        actual = find_repeat([1, 2, 3, 2])
+        expected = 2
+        self.assertEqual(actual, expected)
+
+    def test_medium_list(self):
+        actual = find_repeat([1, 2, 5, 5, 5, 5])
+        expected = 5
+        self.assertEqual(actual, expected)
+
+    def test_long_list(self):
+        actual = find_repeat([4, 1, 4, 8, 3, 2, 7, 6, 5])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/find-rotation-point.py b/users/wpcarro/scratch/data_structures_and_algorithms/find-rotation-point.py
new file mode 100644
index 0000000000..2103a5b84f
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/find-rotation-point.py
@@ -0,0 +1,59 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+def find_rotation_point(xs):
+    """Usage of `visited` here is a hack, but works for the test cases
+    (gulp)."""
+    i = 0
+    j = round(len(xs) / 2)
+    result = None
+    visited = set()
+    while not result:
+        if i in visited:
+            i += 1
+        if j in visited:
+            j -= 1
+        visited.add(i)
+        visited.add(j)
+        if xs[j - 1] > xs[j]:
+            result = j
+        elif xs[i] < xs[j]:
+            i = j
+            j += round((len(xs) - j) / 2)
+        elif xs[i] >= xs[j]:
+            i = j
+            j -= round((j - i) / 2)
+    return result
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_small_list(self):
+        actual = find_rotation_point(['cape', 'cake'])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_medium_list(self):
+        actual = find_rotation_point(
+            ['grape', 'orange', 'plum', 'radish', 'apple'])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_large_list(self):
+        actual = find_rotation_point([
+            'ptolemaic', 'retrograde', 'supplant', 'undulate', 'xenoepist',
+            'asymptote', 'babka', 'banoffee', 'engender', 'karpatka',
+            'othellolagkage'
+        ])
+        expected = 5
+        self.assertEqual(actual, expected)
+
+    # Are we missing any edge cases?
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/find-unique-int-among-duplicates.py b/users/wpcarro/scratch/data_structures_and_algorithms/find-unique-int-among-duplicates.py
new file mode 100644
index 0000000000..dfa5de42cc
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/find-unique-int-among-duplicates.py
@@ -0,0 +1,45 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+def find_unique_delivery_id(xs):
+    a = 0
+    for x in xs:
+        a ^= x
+    return a
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_one_drone(self):
+        actual = find_unique_delivery_id([1])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_unique_id_comes_first(self):
+        actual = find_unique_delivery_id([1, 2, 2])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_unique_id_comes_last(self):
+        actual = find_unique_delivery_id([3, 3, 2, 2, 1])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_unique_id_in_middle(self):
+        actual = find_unique_delivery_id([3, 2, 1, 2, 3])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_many_drones(self):
+        actual = find_unique_delivery_id(
+            [2, 5, 4, 8, 6, 3, 1, 4, 2, 3, 6, 5, 1])
+        expected = 8
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/fixtures.py b/users/wpcarro/scratch/data_structures_and_algorithms/fixtures.py
new file mode 100644
index 0000000000..27689ca76d
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/fixtures.py
@@ -0,0 +1,110 @@
+# Using this module to store commonly used, but annoying to create, data
+# structures for my test inputs.
+#
+# Use like:
+# from fixtures import graph_a
+
+################################################################################
+# Constants
+################################################################################
+
+edge_list = [
+    ('a', 'b'),
+    ('a', 'c'),
+    ('a', 'e'),
+    ('b', 'c'),
+    ('b', 'd'),
+    ('c', 'e'),
+    ('d', 'f'),
+    ('e', 'd'),
+    ('e', 'f'),
+]
+
+unweighted_graph = {
+    'a': {'b', 'c', 'e'},
+    'b': {'c', 'd'},
+    'c': {'e'},
+    'd': {'f'},
+    'e': {'d', 'f'},
+    'f': set(),
+}
+
+adjacencies = {
+    'a': {
+        'a': False,
+        'b': False
+    },
+    'a': [],
+    'a': [],
+    'a': [],
+    'a': [],
+    'a': [],
+    'a': [],
+}
+
+weighted_graph = {
+    'a': {(4, 'b'), (2, 'c'), (4, 'e')},
+    'b': {(5, 'c'), (10, 'd')},
+    'c': {(3, 'e')},
+    'd': {(11, 'f')},
+    'e': {(4, 'd'), (5, 'f')},
+    'f': set(),
+}
+
+# This is `weighted_graph` with each of its weighted edges "expanded".
+expanded_weights_graph = {
+    'a': ['b-1', 'c-1', 'e-1'],
+    'b-1': ['b-2'],
+    'b-2': ['b-3'],
+    'b-3': ['b'],
+    'c-1': ['c'],
+    'e-1': ['e-2'],
+    'e-2': ['e-3'],
+    'e-3': ['e'],
+    # and so on...
+}
+
+unweighted_digraph = {
+    '5': {'2', '0'},
+    '4': {'0', '1'},
+    '3': {'1'},
+    '2': {'3'},
+    '1': set(),
+    '0': set(),
+}
+
+################################################################################
+# Functions
+################################################################################
+
+
+def vertices(xs):
+    result = set()
+    for a, b in xs:
+        result.add(a)
+        result.add(b)
+    return result
+
+
+def edges_to_neighbors(xs):
+    result = {v: set() for v in vertices(xs)}
+    for a, b in xs:
+        result[a].add(b)
+    return result
+
+
+def neighbors_to_edges(xs):
+    result = []
+    for k, ys in xs.items():
+        for y in ys:
+            result.append((k, y))
+    return result
+
+
+def edges_to_adjacencies(xs):
+    return xs
+
+
+# Skipping handling adjacencies because I cannot think of a reasonable use-case
+# for it when the vertex labels are items other than integers. I can think of
+# ways of handling this, but none excite me.
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/graph-coloring.py b/users/wpcarro/scratch/data_structures_and_algorithms/graph-coloring.py
new file mode 100644
index 0000000000..bc7f7ceea5
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/graph-coloring.py
@@ -0,0 +1,180 @@
+import unittest
+from collections import deque
+
+
+################################################################################
+# Solution
+################################################################################
+class GraphNode:
+    def __init__(self, label):
+        self.label = label
+        self.neighbors = set()
+        self.color = None
+
+
+# color_graph :: G(V, E) -> Set(Color) -> IO ()
+def color_graph(graph, colors):
+    q = deque()
+    seen = set()
+    q.append(graph[0])
+
+    while q:
+        node = q.popleft()
+
+        illegal = {n.color for n in node.neighbors}
+        for x in colors:
+            if x not in illegal:
+                node.color = x
+
+        seen.add(node)
+
+        for x in node.neighbors:
+            if x not in seen:
+                q.append(x)
+
+        # TODO: Is this the best way to traverse separate graphs?
+        for x in graph:
+            if x not in seen:
+                q.append(x)
+
+    return 0
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def setUp(self):
+        self.colors = frozenset([
+            'red',
+            'green',
+            'blue',
+            'orange',
+            'yellow',
+            'white',
+        ])
+
+    def assertGraphColoring(self, graph, colors):
+        self.assertGraphHasColors(graph, colors)
+        self.assertGraphColorLimit(graph)
+        for node in graph:
+            self.assertNodeUniqueColor(node)
+
+    def assertGraphHasColors(self, graph, colors):
+        for node in graph:
+            msg = 'Node %r color %r not in %r' % (node.label, node.color,
+                                                  colors)
+            self.assertIn(node.color, colors, msg=msg)
+
+    def assertGraphColorLimit(self, graph):
+        max_degree = 0
+        colors_found = set()
+        for node in graph:
+            degree = len(node.neighbors)
+            max_degree = max(degree, max_degree)
+            colors_found.add(node.color)
+        max_colors = max_degree + 1
+        used_colors = len(colors_found)
+        msg = 'Used %d colors and expected %d at most' % (used_colors,
+                                                          max_colors)
+        self.assertLessEqual(used_colors, max_colors, msg=msg)
+
+    def assertNodeUniqueColor(self, node):
+        for adjacent in node.neighbors:
+            msg = 'Adjacent nodes %r and %r have the same color %r' % (
+                node.label,
+                adjacent.label,
+                node.color,
+            )
+            self.assertNotEqual(node.color, adjacent.color, msg=msg)
+
+    def test_line_graph(self):
+        node_a = GraphNode('a')
+        node_b = GraphNode('b')
+        node_c = GraphNode('c')
+        node_d = GraphNode('d')
+
+        node_a.neighbors.add(node_b)
+        node_b.neighbors.add(node_a)
+        node_b.neighbors.add(node_c)
+        node_c.neighbors.add(node_b)
+        node_c.neighbors.add(node_d)
+        node_d.neighbors.add(node_c)
+
+        graph = [node_a, node_b, node_c, node_d]
+        tampered_colors = list(self.colors)
+        color_graph(graph, tampered_colors)
+        self.assertGraphColoring(graph, self.colors)
+
+    def test_separate_graph(self):
+        node_a = GraphNode('a')
+        node_b = GraphNode('b')
+        node_c = GraphNode('c')
+        node_d = GraphNode('d')
+
+        node_a.neighbors.add(node_b)
+        node_b.neighbors.add(node_a)
+        node_c.neighbors.add(node_d)
+        node_d.neighbors.add(node_c)
+
+        graph = [node_a, node_b, node_c, node_d]
+        tampered_colors = list(self.colors)
+        color_graph(graph, tampered_colors)
+        self.assertGraphColoring(graph, self.colors)
+
+    def test_triangle_graph(self):
+        node_a = GraphNode('a')
+        node_b = GraphNode('b')
+        node_c = GraphNode('c')
+
+        node_a.neighbors.add(node_b)
+        node_a.neighbors.add(node_c)
+        node_b.neighbors.add(node_a)
+        node_b.neighbors.add(node_c)
+        node_c.neighbors.add(node_a)
+        node_c.neighbors.add(node_b)
+
+        graph = [node_a, node_b, node_c]
+        tampered_colors = list(self.colors)
+        color_graph(graph, tampered_colors)
+        self.assertGraphColoring(graph, self.colors)
+
+    def test_envelope_graph(self):
+        node_a = GraphNode('a')
+        node_b = GraphNode('b')
+        node_c = GraphNode('c')
+        node_d = GraphNode('d')
+        node_e = GraphNode('e')
+
+        node_a.neighbors.add(node_b)
+        node_a.neighbors.add(node_c)
+        node_b.neighbors.add(node_a)
+        node_b.neighbors.add(node_c)
+        node_b.neighbors.add(node_d)
+        node_b.neighbors.add(node_e)
+        node_c.neighbors.add(node_a)
+        node_c.neighbors.add(node_b)
+        node_c.neighbors.add(node_d)
+        node_c.neighbors.add(node_e)
+        node_d.neighbors.add(node_b)
+        node_d.neighbors.add(node_c)
+        node_d.neighbors.add(node_e)
+        node_e.neighbors.add(node_b)
+        node_e.neighbors.add(node_c)
+        node_e.neighbors.add(node_d)
+
+        graph = [node_a, node_b, node_c, node_d, node_e]
+        tampered_colors = list(self.colors)
+        color_graph(graph, tampered_colors)
+        self.assertGraphColoring(graph, self.colors)
+
+    def test_loop_graph(self):
+        node_a = GraphNode('a')
+        node_a.neighbors.add(node_a)
+        graph = [node_a]
+        tampered_colors = list(self.colors)
+        with self.assertRaises(Exception):
+            color_graph(graph, tampered_colors)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/graph-to-graphviz.py b/users/wpcarro/scratch/data_structures_and_algorithms/graph-to-graphviz.py
new file mode 100644
index 0000000000..0e7e97a20c
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/graph-to-graphviz.py
@@ -0,0 +1,39 @@
+from graphviz import Digraph
+from collections import deque
+from fixtures import weighted_graph
+
+# There are three ways to model a graph:
+# 1. Edge list: [(Vertex, Vertex)]
+# 2. Neighbors table: Map(Vertex, [Vertex])
+# 3. Adjacency matrix: [[Boolean]]
+#
+# The following graph is a neighbors table.
+
+
+# to_graphviz :: Vertex -> Map(Vertex, [(Vertex, Weight)]) -> String
+def to_graphviz(start, g):
+    """Compiles the graph into GraphViz."""
+    d = Digraph()
+    q = deque()
+    seen = set()
+
+    q.append(start)
+
+    while q:
+        v = q.popleft()
+        if v in seen:
+            continue
+        d.node(v, label=v)
+
+        for w, x in g[v]:
+            d.edge(v, x, label=str(w))
+            q.append(x)
+        seen.add(v)
+
+    return d.source
+
+
+with open('/tmp/test.gv', 'w') as f:
+    src = to_graphviz('a', weighted_graph)
+    f.write(src)
+    print('/tmp/test.gv created!')
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/highest-product-of-3.py b/users/wpcarro/scratch/data_structures_and_algorithms/highest-product-of-3.py
new file mode 100644
index 0000000000..889663e058
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/highest-product-of-3.py
@@ -0,0 +1,89 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+# f :: [Int] -> Int
+def highest_product_of_3(xs):
+    """Here we're greedily storing:
+    - current max
+    - largest product of two
+    - largest positive number
+    - second largest positive number
+    - largest negative number
+    """
+    if len(xs) < 3:
+        raise Exception
+
+    cm = None
+    ld = xs[0] * xs[1]
+    l2 = min(xs[0], xs[1])
+    if xs[0] < 0 or xs[1] < 0:
+        ln = min(xs[0], xs[1])
+    else:
+        ln = 1
+    l = max(xs[0], xs[1])
+
+    for x in xs[2:]:
+        if not cm:
+            cm = max(x * ln * l, ld * x, x * l * l2)  # beware
+            ld = max(ld, x * ln, x * l)
+            ln = min(ln, x)
+            l = max(l, x)
+            if x < l:
+                l2 = max(l2, x)
+        else:
+            cm = max(cm, x * ln * l, x * ld, x * l * l2)
+            ld = max(ld, x * ln, x * l)
+            ln = min(ln, x)
+            l = max(l, x)
+            if x < l:
+                l2 = max(l2, x)
+
+    return cm
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_short_list(self):
+        actual = highest_product_of_3([1, 2, 3, 4])
+        expected = 24
+        self.assertEqual(actual, expected)
+
+    def test_longer_list(self):
+        actual = highest_product_of_3([6, 1, 3, 5, 7, 8, 2])
+        expected = 336
+        self.assertEqual(actual, expected)
+
+    def test_list_has_one_negative(self):
+        actual = highest_product_of_3([-5, 4, 8, 2, 3])
+        expected = 96
+        self.assertEqual(actual, expected)
+
+    def test_list_has_two_negatives(self):
+        actual = highest_product_of_3([-10, 1, 3, 2, -10])
+        expected = 300
+        self.assertEqual(actual, expected)
+
+    def test_list_is_all_negatives(self):
+        actual = highest_product_of_3([-5, -1, -3, -2])
+        expected = -6
+        self.assertEqual(actual, expected)
+
+    def test_error_with_empty_list(self):
+        with self.assertRaises(Exception):
+            highest_product_of_3([])
+
+    def test_error_with_one_number(self):
+        with self.assertRaises(Exception):
+            highest_product_of_3([1])
+
+    def test_error_with_two_numbers(self):
+        with self.assertRaises(Exception):
+            highest_product_of_3([1, 1])
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/inflight-entertainment.py b/users/wpcarro/scratch/data_structures_and_algorithms/inflight-entertainment.py
new file mode 100644
index 0000000000..6e17baef37
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/inflight-entertainment.py
@@ -0,0 +1,35 @@
+# possible :: Int -> [Int] -> Bool
+def possible(flight_duration, film_durations):
+    seeking = set()
+
+    for x in film_durations:
+        if x in seeking:
+            return True
+        else:
+            seeking.add(flight_duration - x)
+
+    return False
+
+
+should = [
+    (10, [1, 9, 8, 8, 8]),
+    (10, [1, 9]),
+    (10, [1, 9, 5, 5, 6]),
+    (1, [0.5, 0.5]),
+    (1, [0.5, 0.5]),
+]
+
+for a, b in should:
+    print("Testing: %s %s" % (a, b))
+    assert possible(a, b)
+
+shouldnt = [
+    (10, [1, 10, 1, 2, 1, 12]),
+    (1, [0.25, 0.25, 0.25, 0.25]),
+    (5, [1, 2, 2]),
+]
+for a, b in shouldnt:
+    print("Testing: %s %s" % (a, b))
+    assert not possible(a, b)
+
+print("Tests pass")
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/knapsack-0-1.py b/users/wpcarro/scratch/data_structures_and_algorithms/knapsack-0-1.py
new file mode 100644
index 0000000000..c72d19d4ed
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/knapsack-0-1.py
@@ -0,0 +1,38 @@
+import unittest
+from math import floor
+
+
+def knapify(xs, capacity=None):
+    assert capacity is not None
+    n = len(xs)
+    # For 0/1 Knapsack, we must use a table, since this will encode which values
+    # work for which items. This is cleaner than including a separate data
+    # structure to capture it.
+    maxes = [[0 for x in range(capacity + 1)] for x in range(n + 1)]
+
+    # Build table maxes[][] in bottom up manner
+    for row in range(n + 1):
+        for col in range(capacity + 1):
+            if row == 0 or col == 0:
+                maxes[row][col] = 0
+            elif xs[row - 1][0] <= col:
+                maxes[row][col] = max(
+                    xs[row - 1][1] + maxes[row - 1][col - xs[row - 1][0]],
+                    maxes[row - 1][col])
+            else:
+                maxes[row][col] = maxes[row - 1][col]
+
+    return maxes[-1][capacity]
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_one_cake(self):
+        actual = knapify([(3, 10), (2, 15), (7, 2), (12, 20)], capacity=12)
+        expected = None
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/kth-to-last.py b/users/wpcarro/scratch/data_structures_and_algorithms/kth-to-last.py
new file mode 100644
index 0000000000..8291e54533
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/kth-to-last.py
@@ -0,0 +1,82 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+def length(x):
+    if not x:
+        return 0
+    else:
+        count = 1
+        while x:
+            x = x.next
+            count += 1
+        return count
+
+
+def kth_to_last_node(k, x):
+    hops = length(x) - 1
+    dest = hops - k
+
+    if k == 0:
+        raise Exception("Our God doesn't support this kind of behavior.")
+
+    if dest < 0:
+        raise Exception('Value k to high for list.')
+
+    while dest > 0:
+        x = x.next
+        dest -= 1
+
+    return x
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    class LinkedListNode(object):
+        def __init__(self, value, next=None):
+            self.value = value
+            self.next = next
+
+        def get_values(self):
+            node = self
+            values = []
+            while node is not None:
+                values.append(node.value)
+                node = node.next
+            return values
+
+    def setUp(self):
+        self.fourth = Test.LinkedListNode(4)
+        self.third = Test.LinkedListNode(3, self.fourth)
+        self.second = Test.LinkedListNode(2, self.third)
+        self.first = Test.LinkedListNode(1, self.second)
+
+    def test_first_to_last_node(self):
+        actual = kth_to_last_node(1, self.first)
+        expected = self.fourth
+        self.assertEqual(actual, expected)
+
+    def test_second_to_last_node(self):
+        actual = kth_to_last_node(2, self.first)
+        expected = self.third
+        self.assertEqual(actual, expected)
+
+    def test_first_node(self):
+        actual = kth_to_last_node(4, self.first)
+        expected = self.first
+        self.assertEqual(actual, expected)
+
+    def test_k_greater_than_linked_list_length(self):
+        with self.assertRaises(Exception):
+            kth_to_last_node(5, self.first)
+
+    def test_k_is_zero(self):
+        with self.assertRaises(Exception):
+            kth_to_last_node(0, self.first)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/largest-stack.py b/users/wpcarro/scratch/data_structures_and_algorithms/largest-stack.py
new file mode 100644
index 0000000000..aab9671eb6
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/largest-stack.py
@@ -0,0 +1,107 @@
+import unittest
+
+
+class Stack(object):
+    def __init__(self):
+        """Initialize an empty stack"""
+        self.items = []
+
+    def push(self, item):
+        """Push a new item onto the stack"""
+        self.items.append(item)
+
+    def pop(self):
+        """Remove and return the last item"""
+        # If the stack is empty, return None
+        # (it would also be reasonable to throw an exception)
+        if not self.items:
+            return None
+
+        return self.items.pop()
+
+    def peek(self):
+        """Return the last item without removing it"""
+        if not self.items:
+            return None
+        return self.items[-1]
+
+
+class MaxStack(object):
+    # Implement the push, pop, and get_max methods
+    def __init__(self):
+        self.m = Stack()
+        self.stack = Stack()
+
+    def push(self, item):
+        if self.m.peek() is None:
+            self.m.push(item)
+        elif item >= self.m.peek():
+            self.m.push(item)
+        self.stack.push(item)
+
+    def pop(self):
+        x = self.stack.pop()
+        if x == self.m.peek():
+            self.m.pop()
+        return x
+
+    def get_max(self):
+        return self.m.peek()
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_stack_usage(self):
+        max_stack = MaxStack()
+
+        max_stack.push(5)
+
+        actual = max_stack.get_max()
+        expected = 5
+        self.assertEqual(actual, expected)
+
+        max_stack.push(4)
+        max_stack.push(7)
+        max_stack.push(7)
+        max_stack.push(8)
+
+        actual = max_stack.get_max()
+        expected = 8
+        self.assertEqual(actual, expected)
+
+        actual = max_stack.pop()
+        expected = 8
+        self.assertEqual(actual, expected)
+
+        actual = max_stack.get_max()
+        expected = 7
+        self.assertEqual(actual, expected)
+
+        actual = max_stack.pop()
+        expected = 7
+        self.assertEqual(actual, expected)
+
+        actual = max_stack.get_max()
+        expected = 7
+        self.assertEqual(actual, expected)
+
+        actual = max_stack.pop()
+        expected = 7
+        self.assertEqual(actual, expected)
+
+        actual = max_stack.get_max()
+        expected = 5
+        self.assertEqual(actual, expected)
+
+        actual = max_stack.pop()
+        expected = 4
+        self.assertEqual(actual, expected)
+
+        actual = max_stack.get_max()
+        expected = 5
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/linked-list-cycles.py b/users/wpcarro/scratch/data_structures_and_algorithms/linked-list-cycles.py
new file mode 100644
index 0000000000..75a4b99394
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/linked-list-cycles.py
@@ -0,0 +1,88 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+def contains_cycle(x):
+    if not x:
+        return False
+    elif not x.next:
+        return False
+
+    a = x
+    b = x.next
+
+    while b.next:
+        if a == b:
+            return True
+
+        a = a.next
+        b = b.next.next
+
+    return False
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    class LinkedListNode(object):
+        def __init__(self, value, next=None):
+            self.value = value
+            self.next = next
+
+    def test_linked_list_with_no_cycle(self):
+        fourth = Test.LinkedListNode(4)
+        third = Test.LinkedListNode(3, fourth)
+        second = Test.LinkedListNode(2, third)
+        first = Test.LinkedListNode(1, second)
+        result = contains_cycle(first)
+        self.assertFalse(result)
+
+    def test_cycle_loops_to_beginning(self):
+        fourth = Test.LinkedListNode(4)
+        third = Test.LinkedListNode(3, fourth)
+        second = Test.LinkedListNode(2, third)
+        first = Test.LinkedListNode(1, second)
+        fourth.next = first
+        result = contains_cycle(first)
+        self.assertTrue(result)
+
+    def test_cycle_loops_to_middle(self):
+        fifth = Test.LinkedListNode(5)
+        fourth = Test.LinkedListNode(4, fifth)
+        third = Test.LinkedListNode(3, fourth)
+        second = Test.LinkedListNode(2, third)
+        first = Test.LinkedListNode(1, second)
+        fifth.next = third
+        result = contains_cycle(first)
+        self.assertTrue(result)
+
+    def test_two_node_cycle_at_end(self):
+        fifth = Test.LinkedListNode(5)
+        fourth = Test.LinkedListNode(4, fifth)
+        third = Test.LinkedListNode(3, fourth)
+        second = Test.LinkedListNode(2, third)
+        first = Test.LinkedListNode(1, second)
+        fifth.next = fourth
+        result = contains_cycle(first)
+        self.assertTrue(result)
+
+    def test_empty_list(self):
+        result = contains_cycle(None)
+        self.assertFalse(result)
+
+    def test_one_element_linked_list_no_cycle(self):
+        first = Test.LinkedListNode(1)
+        result = contains_cycle(first)
+        self.assertFalse(result)
+
+    def test_one_element_linked_list_cycle(self):
+        first = Test.LinkedListNode(1)
+        first.next = first
+        result = contains_cycle(first)
+        self.assertTrue(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/memo.py b/users/wpcarro/scratch/data_structures_and_algorithms/memo.py
new file mode 100644
index 0000000000..44ea93e1bd
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/memo.py
@@ -0,0 +1,60 @@
+import time
+import random
+from heapq import heappush, heappop
+
+
+class Memo(object):
+    def __init__(self, size=1):
+        """
+        Create a key-value data-structure that will never exceed `size`
+        members. Memo evicts the least-recently-accessed elements from itself
+        before adding inserting new key-value pairs.
+        """
+        if size <= 0:
+            raise Exception("We do not support an empty memo")
+        self.xs = {}
+        self.heap = [(0, None)] * size
+
+    def contains(self, k):
+        """
+        Return true if key `k` exists in the Memo.
+        """
+        return k in self.xs
+
+    def get(self, k):
+        """
+        Return the memoized item at key `k`.
+        """
+        # "touch" the element in the heap
+        return self.xs[k]
+
+    def set(self, k, v):
+        """
+        Memoize value `v` at key `k`.
+        """
+        _, to_evict = heappop(self.heap)
+        if to_evict != None:
+            del self.xs[to_evict]
+        heappush(self.heap, (time.time(), k))
+        self.xs[k] = v
+
+
+memo = Memo(size=10)
+
+
+def f(x):
+    """
+    Compute some mysterious, expensive function.
+    """
+    if memo.contains(x):
+        print("Hit.\t\tf({})".format(x))
+        return memo.get(x)
+    else:
+        print("Computing...\tf({})".format(x))
+        time.sleep(0.25)
+        res = random.randint(0, 10)
+        memo.set(x, res)
+        return res
+
+
+[f(random.randint(0, 10)) for _ in range(10)]
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/merge-sort.py b/users/wpcarro/scratch/data_structures_and_algorithms/merge-sort.py
new file mode 100644
index 0000000000..6dbe0fa0f3
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/merge-sort.py
@@ -0,0 +1,28 @@
+
+
+
+# merge :: [a] -> [a] -> [a]
+# merge([], []): []
+# merge(xs, []): xs
+# merge([], ys): ys
+# merge(xs@[x|xs'], ys@[y|ys'])
+#   when y =< x: cons(y, merge(xs, ys'))
+#   when x < y:  cons(x, merge(xs', ys))
+def merge(xs, ys):
+    if xs == [] and ys == []:
+        return []
+    elif ys == []:
+        return xs
+    elif xs == []:
+        return ys
+    else:
+        x = xs[0]
+        y = ys[0]
+
+        if y <= x:
+            return [y] + merge(xs, ys[1:])
+        else:
+            return [x] + merge(xs[1:], ys)
+        
+print(merge([3, 4, 6, 10, 11, 15],
+            [1, 5, 8, 12, 14, 19]))
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/merging-ranges.py b/users/wpcarro/scratch/data_structures_and_algorithms/merging-ranges.py
new file mode 100644
index 0000000000..4e3604d5bc
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/merging-ranges.py
@@ -0,0 +1,94 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+# do_merge_ranges :: [(Int, Int)] -> [(Int, Int)] -> [(Int, Int)]
+def do_merge_ranges(prev, xs):
+    if len(xs) == 0:
+        return prev
+    elif len(xs) == 1:
+        return prev + xs
+    else:
+        a1, a2 = xs[0]
+        b1, b2 = xs[1]
+        rest = xs[2:]
+        if b1 <= a2:
+            return do_merge_ranges(prev, [(a1, max(a2, b2))] + rest)
+        else:
+            return do_merge_ranges(prev + [(a1, a2)], [(b1, b2)] + rest)
+
+
+# merge_ranges :: [(Int, Int)] -> [(Int, Int)]
+def merge_ranges(xs):
+    xs = xs[:]
+    xs.sort()
+    return do_merge_ranges([], xs)
+
+
+# merge_ranges_b :: [(Int, Int)] -> [(Int, Int)]
+def merge_ranges_b(xs):
+    fi = 0
+    ci = 1
+    result = []
+    xs = xs[:]
+    xs.sort()
+    while ci < len(xs):
+        while ci < len(xs) and xs[ci][0] <= xs[fi][1]:
+            xs[fi] = xs[fi][0], max(xs[ci][1], xs[fi][1])
+            ci += 1
+        result.append(xs[fi])
+        fi = ci
+        ci += 1
+    if fi < len(xs):
+        result.append(xs[fi])
+    return result
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_meetings_overlap(self):
+        actual = merge_ranges([(1, 3), (2, 4)])
+        expected = [(1, 4)]
+        self.assertEqual(actual, expected)
+
+    def test_meetings_touch(self):
+        actual = merge_ranges([(5, 6), (6, 8)])
+        expected = [(5, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_meeting_contains_other_meeting(self):
+        actual = merge_ranges([(1, 8), (2, 5)])
+        expected = [(1, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_meetings_stay_separate(self):
+        actual = merge_ranges([(1, 3), (4, 8)])
+        expected = [(1, 3), (4, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_multiple_merged_meetings(self):
+        actual = merge_ranges([(1, 4), (2, 5), (5, 8)])
+        expected = [(1, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_meetings_not_sorted(self):
+        actual = merge_ranges([(5, 8), (1, 4), (6, 8)])
+        expected = [(1, 4), (5, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_one_long_meeting_contains_smaller_meetings(self):
+        actual = merge_ranges([(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)])
+        expected = [(1, 12)]
+        self.assertEqual(actual, expected)
+
+    def test_sample_input(self):
+        actual = merge_ranges([(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)])
+        expected = [(0, 1), (3, 8), (9, 12)]
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/mesh-message.gv b/users/wpcarro/scratch/data_structures_and_algorithms/mesh-message.gv
new file mode 100644
index 0000000000..1e67c3954f
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/mesh-message.gv
@@ -0,0 +1,11 @@
+strict graph {
+    Min -- {William, Jayden, Omar}
+    William -- {Min, Noam}
+    Jayden -- {Min, Amelia, Ren, Noam}
+    Adam -- {Amelia, Miguel, Sofia, Lucas}
+    Ren -- {Jayden, Omar}
+    Amelia -- {Jayden, Adam, Miguel}
+    Miguel -- {Amelia, Adam, Liam, Nathan}
+    Noam -- {Nathan, Jayden, William}
+    Omar -- {Ren, Min, Scott}
+}
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/mesh-message.py b/users/wpcarro/scratch/data_structures_and_algorithms/mesh-message.py
new file mode 100644
index 0000000000..c9d7d9d741
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/mesh-message.py
@@ -0,0 +1,97 @@
+import unittest
+from collections import deque
+
+
+################################################################################
+# Solution
+################################################################################
+# get_path :: G(V, E) -> V -> V -> Maybe([V])
+def get_path(g, src, dst):
+    q = deque()
+    result = None
+    seen = set()
+    q.append(([], src))
+
+    if src not in g or dst not in g:
+        raise Exception
+
+    while q:
+        p, node = q.popleft()
+
+        seen.add(node)
+
+        if node == dst:
+            if not result:
+                result = p + [node]
+            elif len(p + [node]) < len(result):
+                result = p + [node]
+        else:
+            if node not in g:
+                raise Exception
+            for x in g.get(node):
+                if not x in seen:
+                    q.append((p + [node], x))
+
+    return result
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def setUp(self):
+        self.graph = {
+            'a': ['b', 'c', 'd'],
+            'b': ['a', 'd'],
+            'c': ['a', 'e'],
+            'd': ['a', 'b'],
+            'e': ['c'],
+            'f': ['g'],
+            'g': ['f'],
+        }
+
+    def test_two_hop_path_1(self):
+        actual = get_path(self.graph, 'a', 'e')
+        expected = ['a', 'c', 'e']
+        self.assertEqual(actual, expected)
+
+    def test_two_hop_path_2(self):
+        actual = get_path(self.graph, 'd', 'c')
+        expected = ['d', 'a', 'c']
+        self.assertEqual(actual, expected)
+
+    def test_one_hop_path_1(self):
+        actual = get_path(self.graph, 'a', 'c')
+        expected = ['a', 'c']
+        self.assertEqual(actual, expected)
+
+    def test_one_hop_path_2(self):
+        actual = get_path(self.graph, 'f', 'g')
+        expected = ['f', 'g']
+        self.assertEqual(actual, expected)
+
+    def test_one_hop_path_3(self):
+        actual = get_path(self.graph, 'g', 'f')
+        expected = ['g', 'f']
+        self.assertEqual(actual, expected)
+
+    def test_zero_hop_path(self):
+        actual = get_path(self.graph, 'a', 'a')
+        expected = ['a']
+        self.assertEqual(actual, expected)
+
+    def test_no_path(self):
+        actual = get_path(self.graph, 'a', 'f')
+        expected = None
+        self.assertEqual(actual, expected)
+
+    def test_start_node_not_present(self):
+        with self.assertRaises(Exception):
+            get_path(self.graph, 'h', 'a')
+
+    def test_end_node_not_present(self):
+        with self.assertRaises(Exception):
+            get_path(self.graph, 'a', 'h')
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/norman.py b/users/wpcarro/scratch/data_structures_and_algorithms/norman.py
new file mode 100644
index 0000000000..379ba92abb
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/norman.py
@@ -0,0 +1,78 @@
+
+
+
+# Write a function with the following type signature:L
+# equal? :: String -> String -> Bool
+#
+# Determine equality between two inputs with backspace characters encoded as
+# "<".
+
+################################################################################
+# Solution 1
+################################################################################
+
+# from collections import deque
+
+# def equal(a, b):
+#     sa = deque()
+#     sb = deque()
+
+#     for c in a:
+#         if c == '<':
+#             sa.pop()
+#         else:
+#             sa.append(c)
+
+#     for c in b:
+#         if c == '<':
+#             sb.pop()
+#         else:
+#             sb.append(c)
+
+#     return sa == sb
+
+################################################################################
+# Solution 2
+################################################################################
+
+def handle_dels(num_dels, i, xs):
+    if i < 0:
+        return -1
+
+    while xs[i] == '<':
+        num_dels += 1
+        i -= 1
+
+    while num_dels > 0 and xs[i] != '<':
+        num_dels -= 1
+        i -= 1
+
+    if xs[i] == '<':
+        return handle_dels(num_dels, i, xs)
+    else:
+        return i
+
+def update_index(i, xs):
+    # TODO: Indexing into non-available parts of a string.
+    if xs[i] != '<' and xs[i - 1] != '<':
+        return i - 1
+
+    elif xs[i - 1] == '<':
+        return handle_dels(0, i - 1, xs)
+
+def equal(a, b):
+    ia = len(a) - 1
+    ib = len(b) - 1
+
+    while ia >= 0 and ib >= 0:
+        if a[ia] != b[ib]:
+            return False
+        ia = update_index(ia, a)
+        ib = update_index(ib, b)
+
+    if ia != 0:
+        return update_index(ia, a) <= -1
+    if ib != 0:
+        return update_index(ib, b) <= -1
+
+    return True
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/nth-fibonacci.py b/users/wpcarro/scratch/data_structures_and_algorithms/nth-fibonacci.py
new file mode 100644
index 0000000000..cdb2846ea3
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/nth-fibonacci.py
@@ -0,0 +1,59 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+def fib(n):
+    """This should be accomplishable in O(1) space."""
+    if n in {0, 1}:
+        return n
+    a = 0  # i = 0
+    b = 1  # i = 1
+    for x in range(2, n + 1):
+        result = a + b
+        a = b
+        b = result
+    return result
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_zeroth_fibonacci(self):
+        actual = fib(0)
+        expected = 0
+        self.assertEqual(actual, expected)
+
+    def test_first_fibonacci(self):
+        actual = fib(1)
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_second_fibonacci(self):
+        actual = fib(2)
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_third_fibonacci(self):
+        actual = fib(3)
+        expected = 2
+        self.assertEqual(actual, expected)
+
+    def test_fifth_fibonacci(self):
+        actual = fib(5)
+        expected = 5
+        self.assertEqual(actual, expected)
+
+    def test_tenth_fibonacci(self):
+        actual = fib(10)
+        expected = 55
+        self.assertEqual(actual, expected)
+
+    def test_negative_fibonacci(self):
+        with self.assertRaises(Exception):
+            fib(-1)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/optimal-stopping.py b/users/wpcarro/scratch/data_structures_and_algorithms/optimal-stopping.py
new file mode 100644
index 0000000000..af13239941
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/optimal-stopping.py
@@ -0,0 +1,49 @@
+from random import choice
+from math import floor
+
+# Applying Chapter 1 from "Algorithms to Live By", which describes optimal
+# stopping problems. Technically this simulation is invalid because the
+# `candidates` function takes a lower bound and an upper bound, which allows us
+# to know the cardinal number of an individual candidates. The "look then leap"
+# algorithm is ideal for no-information games - i.e. games when upper and lower
+# bounds aren't known. The `look_then_leap/1` function is ignorant of this
+# information, so it behaves as if in a no-information game. Strangely enough,
+# this algorithm will pick the best candidate 37% of the time.
+#
+# Chapter 1 describes two algorithms:
+# 1. Look-then-leap: ordinal numbers - i.e. no-information games. Look-then-leap
+#    finds the best candidate 37% of the time.
+# 2. Threshold: cardinal numbers - i.e. where upper and lower bounds are
+#    known. The Threshold algorithm finds the best candidate ~55% of the time.
+#
+# All of this and more can be studied as "optimal stopping theory". This applies
+# to finding a spouse, parking a car, picking an apartment in a city, and more.
+
+
+# candidates :: Int -> Int -> Int -> [Int]
+def candidates(lb, ub, ct):
+    xs = list(range(lb, ub + 1))
+    return [choice(xs) for _ in range(ct)]
+
+
+# look_then_leap :: [Integer] -> Integer
+def look_then_leap(candidates):
+    best = candidates[0]
+    seen_ct = 1
+    ignore_ct = floor(len(candidates) * 0.37)
+    for x in candidates[1:]:
+        if ignore_ct > 0:
+            ignore_ct -= 1
+            best = max(best, x)
+        else:
+            if x > best:
+                print('Choosing the {} candidate.'.format(seen_ct))
+                return x
+        seen_ct += 1
+    print('You may have waited too long.')
+    return candidates[-1]
+
+
+candidates = candidates(1, 100, 100)
+print(candidates)
+print(look_then_leap(candidates))
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/perm-tree.py b/users/wpcarro/scratch/data_structures_and_algorithms/perm-tree.py
new file mode 100644
index 0000000000..0eb389c26b
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/perm-tree.py
@@ -0,0 +1,83 @@
+import unittest
+
+
+################################################################################
+# Answer
+################################################################################
+class Node(object):
+    def __init__(self, value, children=set()):
+        self.value = value
+        self.children = children
+
+
+# treeify :: Char -> Set(Char) -> Node(Char)
+def treeify(x, xs):
+    return Node(x, [treeify(c, xs - {c}) for c in xs])
+
+
+# dft :: Node(Char) -> [String]
+def dft(node):
+    result = []
+    s = []
+
+    s.append(('', node))
+
+    while s:
+        p, n = s.pop()
+        p += str(n.value)
+
+        if not n.children:
+            result.append(p)
+        else:
+            for c in n.children:
+                s.append((p, c))
+
+    return result
+
+
+# main :: String -> Set(String)
+def get_permutations(xs):
+    if xs == '':
+        return set([''])
+
+    ys = set(xs)
+    trees = []
+
+    for y in ys:
+        trees.append(treeify(y, ys - {y}))
+
+    result = set()
+
+    for t in trees:
+        for d in dft(t):
+            result.add(d)
+
+    return result
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_empty_string(self):
+        actual = get_permutations('')
+        expected = set([''])
+        self.assertEqual(actual, expected)
+
+    def test_one_character_string(self):
+        actual = get_permutations('a')
+        expected = set(['a'])
+        self.assertEqual(actual, expected)
+
+    def test_two_character_string(self):
+        actual = get_permutations('ab')
+        expected = set(['ab', 'ba'])
+        self.assertEqual(actual, expected)
+
+    def test_three_character_string(self):
+        actual = get_permutations('abc')
+        expected = set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba'])
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/permutation-palindrome.py b/users/wpcarro/scratch/data_structures_and_algorithms/permutation-palindrome.py
new file mode 100644
index 0000000000..0a2136a408
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/permutation-palindrome.py
@@ -0,0 +1,49 @@
+from collections import Counter
+import unittest
+
+
+################################################################################
+# Impl
+################################################################################
+# palindromifiable :: String -> Boolean
+def has_palindrome_permutation(x):
+    bag = Counter(x)
+    odd_entries_ct = 0
+
+    for _, y in bag.items():
+        if y % 2 != 0:
+            odd_entries_ct += 1
+
+    return odd_entries_ct in {0, 1}
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_permutation_with_odd_number_of_chars(self):
+        result = has_palindrome_permutation('aabcbcd')
+        self.assertTrue(result)
+
+    def test_permutation_with_even_number_of_chars(self):
+        result = has_palindrome_permutation('aabccbdd')
+        self.assertTrue(result)
+
+    def test_no_permutation_with_odd_number_of_chars(self):
+        result = has_palindrome_permutation('aabcd')
+        self.assertFalse(result)
+
+    def test_no_permutation_with_even_number_of_chars(self):
+        result = has_palindrome_permutation('aabbcd')
+        self.assertFalse(result)
+
+    def test_empty_string(self):
+        result = has_palindrome_permutation('')
+        self.assertTrue(result)
+
+    def test_one_character_string(self):
+        result = has_palindrome_permutation('a')
+        self.assertTrue(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/permutations.py b/users/wpcarro/scratch/data_structures_and_algorithms/permutations.py
new file mode 100644
index 0000000000..fc2c1ef7ee
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/permutations.py
@@ -0,0 +1,55 @@
+class Node(object):
+    # ctor :: a -> [a] -> Node(a)
+    def __init__(self, value, children=[]):
+        self.value = value
+        self.children = children
+
+
+# is_leaf :: Node(a) -> Boolean
+def is_leaf(node):
+    return len(node.children) == 0
+
+
+# enumerate :: Node(a) -> Set(List(a))
+def enumerate(node):
+    current = []
+    result = []
+    q = []
+
+    q.append(node)
+
+    while q:
+        x = q.pop()
+        print(x.value)
+
+        for c in x.children:
+            q.append(c)
+
+        current.append(x.value)
+        print(current)
+
+        if is_leaf(x):
+            result.append(current)
+            print("Reseting current")
+            current = []
+
+    return result
+
+
+node = Node('root', [
+    Node('a', [
+        Node('b', [Node('c')]),
+        Node('c', [Node('b')]),
+    ]),
+    Node('b', [
+        Node('a', [Node('c')]),
+        Node('c', [Node('a')]),
+    ]),
+    Node('c', [
+        Node('a', [Node('b')]),
+        Node('b', [Node('a')]),
+    ])
+])
+
+print('----------')
+print(enumerate(node))
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/plot.py b/users/wpcarro/scratch/data_structures_and_algorithms/plot.py
new file mode 100644
index 0000000000..5601891a0d
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/plot.py
@@ -0,0 +1,9 @@
+import numpy as np
+import matplotlib.pyplot as plt
+
+rng = np.random.RandomState(10)  # deterministic random data
+a = np.hstack((rng.normal(size=1000), rng.normal(loc=5, scale=2, size=1000)))
+_ = plt.hist(a, bins='auto')  # arguments are passed to np.histogram
+plt.title("Histogram with 'auto' bins")
+Text(0.5, 1.0, "Histogram with 'auto' bins")
+plt.show()
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/product-of-other-numbers.py b/users/wpcarro/scratch/data_structures_and_algorithms/product-of-other-numbers.py
new file mode 100644
index 0000000000..d05e82d42b
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/product-of-other-numbers.py
@@ -0,0 +1,68 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+# f :: [Int] -> [Int]
+def get_products_of_all_ints_except_at_index(xs):
+    if len(xs) in {0, 1}:
+        raise Exception
+
+    ct = len(xs)
+    lefts = [1] * ct
+    rights = [1] * ct
+    result = []
+
+    for i in range(1, ct):
+        lefts[i] = lefts[i - 1] * xs[i - 1]
+    for i in range(ct - 2, -1, -1):
+        rights[i] = rights[i + 1] * xs[i + 1]
+
+    return [lefts[i] * rights[i] for i in range(ct)]
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_small_list(self):
+        actual = get_products_of_all_ints_except_at_index([1, 2, 3])
+        expected = [6, 3, 2]
+        self.assertEqual(actual, expected)
+
+    def test_longer_list(self):
+        actual = get_products_of_all_ints_except_at_index([8, 2, 4, 3, 1, 5])
+        expected = [120, 480, 240, 320, 960, 192]
+        self.assertEqual(actual, expected)
+
+    def test_list_has_one_zero(self):
+        actual = get_products_of_all_ints_except_at_index([6, 2, 0, 3])
+        expected = [0, 0, 36, 0]
+        self.assertEqual(actual, expected)
+
+    def test_list_has_two_zeros(self):
+        actual = get_products_of_all_ints_except_at_index([4, 0, 9, 1, 0])
+        expected = [0, 0, 0, 0, 0]
+        self.assertEqual(actual, expected)
+
+    def test_one_negative_number(self):
+        actual = get_products_of_all_ints_except_at_index([-3, 8, 4])
+        expected = [32, -12, -24]
+        self.assertEqual(actual, expected)
+
+    def test_all_negative_numbers(self):
+        actual = get_products_of_all_ints_except_at_index([-7, -1, -4, -2])
+        expected = [-8, -56, -14, -28]
+        self.assertEqual(actual, expected)
+
+    def test_error_with_empty_list(self):
+        with self.assertRaises(Exception):
+            get_products_of_all_ints_except_at_index([])
+
+    def test_error_with_one_number(self):
+        with self.assertRaises(Exception):
+            get_products_of_all_ints_except_at_index([1])
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/queue-two-stacks.py b/users/wpcarro/scratch/data_structures_and_algorithms/queue-two-stacks.py
new file mode 100644
index 0000000000..63da08ebf7
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/queue-two-stacks.py
@@ -0,0 +1,66 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+class QueueTwoStacks(object):
+    def __init__(self):
+        self.a = []
+        self.b = []
+
+    def enqueue(self, x):
+        self.a.append(x)
+
+    def dequeue(self):
+        if self.b:
+            return self.b.pop()
+        else:
+            while self.a:
+                self.b.append(self.a.pop())
+            return self.dequeue()
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_basic_queue_operations(self):
+        queue = QueueTwoStacks()
+        queue.enqueue(1)
+        queue.enqueue(2)
+        queue.enqueue(3)
+        actual = queue.dequeue()
+        expected = 1
+        self.assertEqual(actual, expected)
+        actual = queue.dequeue()
+        expected = 2
+        self.assertEqual(actual, expected)
+        queue.enqueue(4)
+        actual = queue.dequeue()
+        expected = 3
+        self.assertEqual(actual, expected)
+        actual = queue.dequeue()
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_error_when_dequeue_from_new_queue(self):
+        queue = QueueTwoStacks()
+        with self.assertRaises(Exception):
+            queue.dequeue()
+
+    def test_error_when_dequeue_from_empty_queue(self):
+        queue = QueueTwoStacks()
+        queue.enqueue(1)
+        queue.enqueue(2)
+        actual = queue.dequeue()
+        expected = 1
+        self.assertEqual(actual, expected)
+        actual = queue.dequeue()
+        expected = 2
+        self.assertEqual(actual, expected)
+        with self.assertRaises(Exception):
+            queue.dequeue()
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/rectangular-love.py b/users/wpcarro/scratch/data_structures_and_algorithms/rectangular-love.py
new file mode 100644
index 0000000000..47c0f53979
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/rectangular-love.py
@@ -0,0 +1,246 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+# bottom :: Rectangle -> Int
+def bottom(x):
+    return x.get('bottom_y')
+
+
+# top :: Rectangle -> Int
+def top(x):
+    return bottom(x) + x.get('height')
+
+
+# left :: Rectangle -> Int
+def left(x):
+    return x.get('left_x')
+
+
+# right :: Rectangle -> Int
+def right(x):
+    return left(x) + x.get('width')
+
+
+# sort_highest :: Rectangle -> Rectangle -> (Rectangle, Rectangle)
+def sort_highest(x, y):
+    if top(x) >= top(y):
+        return x, y
+    else:
+        return y, x
+
+
+# sort_leftmost :: Rectangle -> Rectangle -> (Rectangle, Rectangle)
+def sort_leftmost(x, y):
+    if left(x) <= left(y):
+        return x, y
+    else:
+        return y, x
+
+
+# rectify :: Int -> Int -> Int -> Int -> Rectify
+def rectify(top=None, bottom=None, left=None, right=None):
+    assert top >= bottom
+    assert left <= right
+    return {
+        'left_x': left,
+        'bottom_y': bottom,
+        'width': right - left,
+        'height': top - bottom,
+    }
+
+
+# empty_rect :: Rectangle
+def empty_rect():
+    return {
+        'left_x': None,
+        'bottom_y': None,
+        'width': None,
+        'height': None,
+    }
+
+
+# find_rectangular_overlap :: Rectangle -> Rectangle -> Maybe(Rectangle)
+def find_rectangular_overlap(x, y):
+    ha, hb = sort_highest(x, y)
+    la, lb = sort_leftmost(x, y)
+
+    if bottom(hb) <= top(hb) <= bottom(ha) <= top(ha):
+        return empty_rect()
+
+    if left(la) <= right(la) <= left(lb) <= right(lb):
+        return empty_rect()
+
+    # We should have an intersection here.
+    verts = [bottom(ha), top(ha), bottom(hb), top(hb)]
+    verts.sort()
+    horzs = [left(la), right(la), left(lb), right(lb)]
+    horzs.sort()
+    return rectify(top=verts[2],
+                   bottom=verts[1],
+                   left=horzs[1],
+                   right=horzs[2])
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_overlap_along_both_axes(self):
+        rect1 = {
+            'left_x': 1,
+            'bottom_y': 1,
+            'width': 6,
+            'height': 3,
+        }
+        rect2 = {
+            'left_x': 5,
+            'bottom_y': 2,
+            'width': 3,
+            'height': 6,
+        }
+        expected = {
+            'left_x': 5,
+            'bottom_y': 2,
+            'width': 2,
+            'height': 2,
+        }
+        actual = find_rectangular_overlap(rect1, rect2)
+        self.assertEqual(actual, expected)
+
+    def test_one_rectangle_inside_another(self):
+        rect1 = {
+            'left_x': 1,
+            'bottom_y': 1,
+            'width': 6,
+            'height': 6,
+        }
+        rect2 = {
+            'left_x': 3,
+            'bottom_y': 3,
+            'width': 2,
+            'height': 2,
+        }
+        expected = {
+            'left_x': 3,
+            'bottom_y': 3,
+            'width': 2,
+            'height': 2,
+        }
+        actual = find_rectangular_overlap(rect1, rect2)
+        self.assertEqual(actual, expected)
+
+    def test_both_rectangles_the_same(self):
+        rect1 = {
+            'left_x': 2,
+            'bottom_y': 2,
+            'width': 4,
+            'height': 4,
+        }
+        rect2 = {
+            'left_x': 2,
+            'bottom_y': 2,
+            'width': 4,
+            'height': 4,
+        }
+        expected = {
+            'left_x': 2,
+            'bottom_y': 2,
+            'width': 4,
+            'height': 4,
+        }
+        actual = find_rectangular_overlap(rect1, rect2)
+        self.assertEqual(actual, expected)
+
+    def test_touch_on_horizontal_edge(self):
+        rect1 = {
+            'left_x': 1,
+            'bottom_y': 2,
+            'width': 3,
+            'height': 4,
+        }
+        rect2 = {
+            'left_x': 2,
+            'bottom_y': 6,
+            'width': 2,
+            'height': 2,
+        }
+        expected = {
+            'left_x': None,
+            'bottom_y': None,
+            'width': None,
+            'height': None,
+        }
+        actual = find_rectangular_overlap(rect1, rect2)
+        self.assertEqual(actual, expected)
+
+    def test_touch_on_vertical_edge(self):
+        rect1 = {
+            'left_x': 1,
+            'bottom_y': 2,
+            'width': 3,
+            'height': 4,
+        }
+        rect2 = {
+            'left_x': 4,
+            'bottom_y': 3,
+            'width': 2,
+            'height': 2,
+        }
+        expected = {
+            'left_x': None,
+            'bottom_y': None,
+            'width': None,
+            'height': None,
+        }
+        actual = find_rectangular_overlap(rect1, rect2)
+        self.assertEqual(actual, expected)
+
+    def test_touch_at_a_corner(self):
+        rect1 = {
+            'left_x': 1,
+            'bottom_y': 1,
+            'width': 2,
+            'height': 2,
+        }
+        rect2 = {
+            'left_x': 3,
+            'bottom_y': 3,
+            'width': 2,
+            'height': 2,
+        }
+        expected = {
+            'left_x': None,
+            'bottom_y': None,
+            'width': None,
+            'height': None,
+        }
+        actual = find_rectangular_overlap(rect1, rect2)
+        self.assertEqual(actual, expected)
+
+    def test_no_overlap(self):
+        rect1 = {
+            'left_x': 1,
+            'bottom_y': 1,
+            'width': 2,
+            'height': 2,
+        }
+        rect2 = {
+            'left_x': 4,
+            'bottom_y': 6,
+            'width': 3,
+            'height': 6,
+        }
+        expected = {
+            'left_x': None,
+            'bottom_y': None,
+            'width': None,
+            'height': None,
+        }
+        actual = find_rectangular_overlap(rect1, rect2)
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/recursive-string-permutations.py b/users/wpcarro/scratch/data_structures_and_algorithms/recursive-string-permutations.py
new file mode 100644
index 0000000000..70461ddf5d
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/recursive-string-permutations.py
@@ -0,0 +1,37 @@
+import unittest
+
+
+################################################################################
+# Implementation
+################################################################################
+# get_permutations :: String -> Set(String)
+def get_permutations(string):
+    pass
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_empty_string(self):
+        actual = get_permutations('')
+        expected = set([''])
+        self.assertEqual(actual, expected)
+
+    def test_one_character_string(self):
+        actual = get_permutations('a')
+        expected = set(['a'])
+        self.assertEqual(actual, expected)
+
+    def test_two_character_string(self):
+        actual = get_permutations('ab')
+        expected = set(['ab', 'ba'])
+        self.assertEqual(actual, expected)
+
+    def test_three_character_string(self):
+        actual = get_permutations('abc')
+        expected = set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba'])
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/reverse-linked-list.py b/users/wpcarro/scratch/data_structures_and_algorithms/reverse-linked-list.py
new file mode 100644
index 0000000000..b7396b20ce
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/reverse-linked-list.py
@@ -0,0 +1,79 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+# reverse :: List(a) -> List(a)
+def reverse(node):
+    curr = node
+    prev = None
+    while curr:
+        nxt = curr.next
+        curr.next = prev
+        prev = curr
+        curr = nxt
+    # Make sure to understand the spec! Debugging takes time. Rewriting takes
+    # time.
+    return prev
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    class LinkedListNode(object):
+        def __init__(self, value, next=None):
+            self.value = value
+            self.next = next
+
+        def get_values(self):
+            node = self
+            values = []
+            while node is not None:
+                values.append(node.value)
+                node = node.next
+            return values
+
+    def test_short_linked_list(self):
+        second = Test.LinkedListNode(2)
+        first = Test.LinkedListNode(1, second)
+
+        result = reverse(first)
+        self.assertIsNotNone(result)
+
+        actual = result.get_values()
+        expected = [2, 1]
+        self.assertEqual(actual, expected)
+
+    def test_long_linked_list(self):
+        sixth = Test.LinkedListNode(6)
+        fifth = Test.LinkedListNode(5, sixth)
+        fourth = Test.LinkedListNode(4, fifth)
+        third = Test.LinkedListNode(3, fourth)
+        second = Test.LinkedListNode(2, third)
+        first = Test.LinkedListNode(1, second)
+
+        result = reverse(first)
+        self.assertIsNotNone(result)
+
+        actual = result.get_values()
+        expected = [6, 5, 4, 3, 2, 1]
+        self.assertEqual(actual, expected)
+
+    def test_one_element_linked_list(self):
+        first = Test.LinkedListNode(1)
+
+        result = reverse(first)
+        self.assertIsNotNone(result)
+
+        actual = result.get_values()
+        expected = [1]
+        self.assertEqual(actual, expected)
+
+    def test_empty_linked_list(self):
+        result = reverse(None)
+        self.assertIsNone(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/reverse-words.py b/users/wpcarro/scratch/data_structures_and_algorithms/reverse-words.py
new file mode 100644
index 0000000000..5df12ebabd
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/reverse-words.py
@@ -0,0 +1,181 @@
+from collections import deque
+import unittest
+
+################################################################################
+# Solution
+################################################################################
+
+
+def rev(xs, i, j):
+    """Reverse xs in place from [i, j]"""
+    while i < j:
+        xs[i], xs[j] = xs[j], xs[i]
+        i += 1
+        j -= 1
+
+
+def rotate(xs, n, i=None, j=None):
+    """Mutably rotates list, xs, n times. Positive n values rotate right while
+    negative n values rotate left. Rotate within window [i, j]."""
+    i = i or 0
+    j = j or len(xs) - 1
+    ct = j - i
+
+    if n < 0:
+        n = abs(n)
+        p = i + n - 1
+        rev(xs, i, p)
+        rev(xs, p + 1, j)
+        rev(xs, i, j)
+    else:
+        p = j - (n - 1)
+        rev(xs, p, j)
+        rev(xs, i, p - 1)
+        rev(xs, i, j)
+    return xs
+
+
+def rev_words(xs, i, j):
+    if j + 1 == len(xs):
+        return 0
+
+    while j + 1 < len(xs):
+        while j + 1 < len(xs) and xs[j + 1] != ' ':
+            j += 1
+
+        rev(xs, i, j)
+        j += 2
+        i = j
+
+    return 0
+
+
+def reverse_words(xs):
+    # first reverse everything
+    rev(xs, 0, len(xs) - 1)
+    return rev_words(xs, 0, 0)
+
+
+def reverse_words_bak(xs, i=None, j=None):
+    i = i or 0
+    j = j or len(xs) - 1
+    w0, w1 = [], []
+
+    if i >= j:
+        return 0
+
+    pi = i
+    while pi < len(xs) and xs[pi] != ' ':
+        w0.append(xs[pi])
+        pi += 1
+
+    if pi == len(xs):
+        return 0
+
+    pj = j
+    while xs[pj] != ' ':
+        w1.append(xs[pj])
+        pj -= 1
+
+    d = len(w0) - len(w1)
+
+    rotate(xs, -1 * d, i, j)
+
+    for k in range(len(w1)):
+        xs[i + k] = w1[len(w1) - 1 - k]
+
+    for k in range(len(w0)):
+        xs[j - k] = w0[len(w0) - 1 - k]
+
+    while i != j and xs[i] != ' ' and xs[j] != ' ':
+        i += 1
+        j -= 1
+
+    if i == j:
+        return 0
+
+    elif xs[i] == ' ':
+        while j > 0 and xs[j] != ' ':
+            j -= 1
+        if j == 0:
+            return 0
+    elif xs[j] == ' ':
+        while i < len(xs) and xs[i] != ' ':
+            i += 1
+        if i == len(xs):
+            return 0
+    return reverse_words(xs, i + 1, j - 1)
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_rev(self):
+        xs = [1, 2, 3, 4, 5]
+        rev(xs, 0, len(xs) - 1)
+        self.assertEqual(xs, [5, 4, 3, 2, 1])
+
+    def test_rotate(self):
+        ys = [1, 2, 3, 4, 5]
+        xs = ys[:]
+        self.assertEqual(rotate(xs, 1, 1, 3), [1, 4, 2, 3, 5])
+        xs = ys[:]
+        self.assertEqual(rotate(xs, -1, 1, 3), [1, 3, 4, 2, 5])
+        xs = ys[:]
+        self.assertEqual(rotate(xs, 1), [5, 1, 2, 3, 4])
+        xs = ys[:]
+        self.assertEqual(rotate(xs, -1), [2, 3, 4, 5, 1])
+        xs = ys[:]
+        self.assertEqual(rotate(xs, -2), [3, 4, 5, 1, 2])
+        xs = ys[:]
+        self.assertEqual(rotate(xs, -5), [1, 2, 3, 4, 5])
+        xs = ys[:]
+        self.assertEqual(rotate(xs, 5), [1, 2, 3, 4, 5])
+        xs = ys[:]
+        self.assertEqual(rotate(xs, 3), [3, 4, 5, 1, 2])
+
+    def test_one_word(self):
+        message = list('vault')
+        reverse_words(message)
+        expected = list('vault')
+        self.assertEqual(message, expected)
+
+    def test_two_words(self):
+        message = list('thief cake')
+        reverse_words(message)
+        expected = list('cake thief')
+        self.assertEqual(message, expected)
+
+    def test_three_words(self):
+        message = list('one another get')
+        reverse_words(message)
+        expected = list('get another one')
+        self.assertEqual(message, expected)
+
+    def test_multiple_words_same_length(self):
+        message = list('rat the ate cat the')
+        reverse_words(message)
+        expected = list('the cat ate the rat')
+        self.assertEqual(message, expected)
+
+    def test_multiple_words_different_lengths(self):
+        message = list('at rat house')
+        reverse_words(message)
+        expected = list('house rat at')
+        self.assertEqual(message, expected)
+
+    def test_multiple_words_different_lengths(self):
+        message = list('yummy is cake bundt chocolate')
+        reverse_words(message)
+        expected = list('chocolate bundt cake is yummy')
+        self.assertEqual(message, expected)
+
+    def test_empty_string(self):
+        message = list('')
+        reverse_words(message)
+        expected = list('')
+        self.assertEqual(message, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/second-largest-item-bst.py b/users/wpcarro/scratch/data_structures_and_algorithms/second-largest-item-bst.py
new file mode 100644
index 0000000000..bc167d975a
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/second-largest-item-bst.py
@@ -0,0 +1,179 @@
+import unittest
+from collections import deque
+
+
+################################################################################
+# Implementation
+################################################################################
+def is_leaf(node):
+    return node.left is None and node.right is None
+
+
+def find_largest(node):
+    current = node
+    while current.right is not None:
+        current = current.right
+    return current.value
+
+
+def find_second_largest(node):
+    history = deque()
+    current = node
+
+    while current.right:
+        history.append(current)
+        current = current.right
+
+    if current.left:
+        return find_largest(current.left)
+    elif history:
+        return history.pop().value
+    else:
+        raise TypeError
+
+
+def find_second_largest_backup(node):
+    history = deque()
+    current = node
+
+    # traverse -> largest
+    while current.right:
+        history.append(current)
+        current = current.right
+
+    if current.left:
+        return find_largest(current.left)
+    elif history:
+        return history.pop().value
+    else:
+        raise ArgumentError
+
+
+# Write a iterative version to avoid consuming memory with the call stack.
+# Commenting out the recursive code for now.
+def find_second_largest_backup(node):
+    if node.left is None and node.right is None:
+        raise ArgumentError
+
+    elif node.right is None and is_leaf(node.left):
+        return node.left.value
+
+    # recursion
+    # elif node.right is None:
+    #     return find_largest(node.left)
+
+    # iterative version
+    elif node.right is None:
+        current = node.left
+        while current.right is not None:
+            current = current.right
+        return current.value
+
+    # recursion
+    # TODO: Remove recursion from here.
+    elif not is_leaf(node.right):
+        return find_second_largest(node.right)
+
+    # could do an else here, but let's be more assertive.
+    elif is_leaf(node.right):
+        return node.value
+
+    else:
+        raise ArgumentError
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    class BinaryTreeNode(object):
+        def __init__(self, value):
+            self.value = value
+            self.left = None
+            self.right = None
+
+        def insert_left(self, value):
+            self.left = Test.BinaryTreeNode(value)
+            return self.left
+
+        def insert_right(self, value):
+            self.right = Test.BinaryTreeNode(value)
+            return self.right
+
+    def test_full_tree(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(30)
+        right = tree.insert_right(70)
+        left.insert_left(10)
+        left.insert_right(40)
+        right.insert_left(60)
+        right.insert_right(80)
+        actual = find_second_largest(tree)
+        expected = 70
+        self.assertEqual(actual, expected)
+
+    def test_largest_has_a_left_child(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(30)
+        right = tree.insert_right(70)
+        left.insert_left(10)
+        left.insert_right(40)
+        right.insert_left(60)
+        actual = find_second_largest(tree)
+        expected = 60
+        self.assertEqual(actual, expected)
+
+    def test_largest_has_a_left_subtree(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(30)
+        right = tree.insert_right(70)
+        left.insert_left(10)
+        left.insert_right(40)
+        right_left = right.insert_left(60)
+        right_left_left = right_left.insert_left(55)
+        right_left.insert_right(65)
+        right_left_left.insert_right(58)
+        actual = find_second_largest(tree)
+        expected = 65
+        self.assertEqual(actual, expected)
+
+    def test_second_largest_is_root_node(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(30)
+        tree.insert_right(70)
+        left.insert_left(10)
+        left.insert_right(40)
+        actual = find_second_largest(tree)
+        expected = 50
+        self.assertEqual(actual, expected)
+
+    def test_descending_linked_list(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(40)
+        left_left = left.insert_left(30)
+        left_left_left = left_left.insert_left(20)
+        left_left_left.insert_left(10)
+        actual = find_second_largest(tree)
+        expected = 40
+        self.assertEqual(actual, expected)
+
+    def test_ascending_linked_list(self):
+        tree = Test.BinaryTreeNode(50)
+        right = tree.insert_right(60)
+        right_right = right.insert_right(70)
+        right_right.insert_right(80)
+        actual = find_second_largest(tree)
+        expected = 70
+        self.assertEqual(actual, expected)
+
+    def test_error_when_tree_has_one_node(self):
+        tree = Test.BinaryTreeNode(50)
+        with self.assertRaises(Exception):
+            find_second_largest(tree)
+
+    def test_error_when_tree_is_empty(self):
+        with self.assertRaises(Exception):
+            find_second_largest(None)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/shortest-path-inject-vertices.py b/users/wpcarro/scratch/data_structures_and_algorithms/shortest-path-inject-vertices.py
new file mode 100644
index 0000000000..e08ea66b8f
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/shortest-path-inject-vertices.py
@@ -0,0 +1,94 @@
+from heapq import heappush, heappop
+from collections import deque
+from fixtures import weighted_graph, expanded_weights_graph
+
+# UnweightedGraph(a) :: Map(a, Set(a))
+# WeightedGraph(a) :: Map(a, Set(a))
+
+
+# shortest_path_dijkstra :: Vertex -> Vertex -> WeightedGraph(Vertex)
+def shortest_path_dijkstra(a, b, g):
+    q = []
+    seen = set()
+
+    heappush(q, (0, a, [a]))
+
+    while q:
+        w0, v0, path = heappop(q)
+        if v0 in seen:
+            continue
+        elif v0 == b:
+            return w0, path
+        for w1, v1 in g.get(v0):
+            heappush(q, (w0 + w1, v1, path + [v1]))
+        seen.add(v0)
+    return 'weighted', 'pizza'
+
+
+# expand_edge :: Vertex -> (Weight, Vertex) -> Map(Vertex, [Vertex])
+def expand_edge(v0, wv):
+    w, v1 = wv
+    assert w > 1
+
+    result = {v0: ['{}-{}'.format(v1, 1)]}
+    for x in range(w - 2):
+        result['{}-{}'.format(v1, x + 1)] = ['{}-{}'.format(v1, x + 2)]
+    result['{}-{}'.format(v1, w - 1)] = [v1]
+
+    return result
+
+
+# expand_weights :: Vertex -> WeightedGraph(Vertex) -> UnweightedGraph(Vertex)
+def expand_weights(v, g):
+    result = {}
+    q = deque()
+    seen = set()
+
+    q.append(v)
+    while q:
+        v = d.popleft()
+        if v in seen:
+            continue
+        x = expand_edge(v, g.get)
+        for w, v1 in g.get(v):
+            if w > 1:
+                ws = expand_edge(v, (w, v1))
+                result = {**result, **ws}
+            q.append(v)
+        pass
+
+
+# shortest_path_inject :: Vertex -> Vertex -> WeightedGraph(Vertex)
+def shortest_path_inject(a, b, g):
+    q = deque()
+    seen = set()
+
+    q.append((a, [a]))
+
+    while q:
+        v0, path = q.popleft()
+        if v0 == 'dummy':
+            continue
+        elif v0 in seen:
+            continue
+        elif v0 == b:
+            return len(path), path
+        for _, v1 in g.get(v0):
+            q.append((v1, path + [v1]))
+        seen.add(v0)
+        continue
+
+    return None, None
+
+
+print(expand_edge('a', (4, 'b')))
+print(expand_edge('a', (5, 'e')))
+assert expand_weights('a', weighted_graph) == expanded_weights_graph
+# a = 'a'
+# b = 'd'
+# w, x = shortest_path_dijkstra(a, b, weighted_graph)
+# w1, x1 = shortest_path_inject(a, b, weighted_graph)
+# print("[dijkstra]  Shortest path from {} to {} is {} with weight {}".format(
+#     a, b, x, w))
+# print("[injection] Shortest path from {} to {} is {} with weight {}".format(
+#     a, b, x1, w1))
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/shuffle.py b/users/wpcarro/scratch/data_structures_and_algorithms/shuffle.py
new file mode 100644
index 0000000000..bdfbad2426
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/shuffle.py
@@ -0,0 +1,34 @@
+import random
+
+
+def get_random(floor, ceiling):
+    return random.randrange(floor, ceiling + 1)
+
+
+# shuffle_in_place :: [a] -> IO ()
+def shuffle_in_place(xs):
+    """Fisher-Yates algorithm. Notice that shuffling here is the same as
+    selecting a random permutation of the input set, `xs`."""
+    n = len(xs) - 1
+    for i in range(len(xs)):
+        r = get_random(i, n)
+        xs[i], xs[r] = xs[r], xs[i]
+    return xs
+
+
+# shuffle :: [a] -> [a]
+def shuffle_not_in_place(xs):
+    result = []
+
+    while xs:
+        i = get_random(0, len(xs) - 1)
+        x = xs.pop(i)
+        result.append(x)
+
+    return result
+
+
+xs = [x for x in range(9)]
+print(xs)
+# print(shuffle_not_in_place(xs))
+print(shuffle_in_place(xs[:]))
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/string-reverse.py b/users/wpcarro/scratch/data_structures_and_algorithms/string-reverse.py
new file mode 100644
index 0000000000..8b4cdac1c2
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/string-reverse.py
@@ -0,0 +1,22 @@
+
+# swap :: Int -> Int -> [Char] -> IO ()
+def swap(ia, iz, xs):
+    # handle swap when ia == iz
+    assert ia <= iz
+    xs[ia], xs[iz] = xs[iz], xs[ia]
+    
+
+# reverse :: [Char] -> IO ()
+def reverse(xs):
+    ia = 0
+    iz = len(xs) - 1
+
+    while ia <= iz:
+        swap(ia, iz, xs)
+        ia += 1
+        iz -= 1
+
+x = list("superduperpooper")
+reverse(x)
+print(x)
+print("Tests pass")
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/temperature-tracker.py b/users/wpcarro/scratch/data_structures_and_algorithms/temperature-tracker.py
new file mode 100644
index 0000000000..6b042182f0
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/temperature-tracker.py
@@ -0,0 +1,84 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+class TempTracker(object):
+    def __init__(self):
+        # min / max
+        self.min = None
+        self.max = None
+        # mean
+        self.sum = 0
+        self.num = 0
+        # mode
+        self.nums = [0] * 111
+        self.mode_num = 0
+        self.mode = None
+
+    def insert(self, x):
+        # min / max
+        if not self.min or x < self.min:
+            self.min = x
+        if not self.max or x > self.max:
+            self.max = x
+        # mean
+        self.sum += x
+        self.num += 1
+        # mode
+        self.nums[x] += 1
+        if self.nums[x] >= self.mode_num:
+            self.mode_num = self.nums[x]
+            self.mode = x
+
+    def get_max(self):
+        return self.max
+
+    def get_min(self):
+        return self.min
+
+    def get_mean(self):
+        return self.sum / self.num
+
+    def get_mode(self):
+        return self.mode
+
+
+# Tests
+
+
+class Test(unittest.TestCase):
+    def test_tracker_usage(self):
+        tracker = TempTracker()
+
+        tracker.insert(50)
+        msg = 'failed on first temp recorded'
+        self.assertEqual(tracker.get_max(), 50, msg='max ' + msg)
+        self.assertEqual(tracker.get_min(), 50, msg='min ' + msg)
+        self.assertEqual(tracker.get_mean(), 50.0, msg='mean ' + msg)
+        self.assertEqual(tracker.get_mode(), 50, msg='mode ' + msg)
+
+        tracker.insert(80)
+        msg = 'failed on higher temp recorded'
+        self.assertEqual(tracker.get_max(), 80, msg='max ' + msg)
+        self.assertEqual(tracker.get_min(), 50, msg='min ' + msg)
+        self.assertEqual(tracker.get_mean(), 65.0, msg='mean ' + msg)
+        self.assertIn(tracker.get_mode(), [50, 80], msg='mode ' + msg)
+
+        tracker.insert(80)
+        msg = 'failed on third temp recorded'
+        self.assertEqual(tracker.get_max(), 80, msg='max ' + msg)
+        self.assertEqual(tracker.get_min(), 50, msg='min ' + msg)
+        self.assertEqual(tracker.get_mean(), 70.0, msg='mean ' + msg)
+        self.assertEqual(tracker.get_mode(), 80, msg='mode ' + msg)
+
+        tracker.insert(30)
+        msg = 'failed on lower temp recorded'
+        self.assertEqual(tracker.get_max(), 80, msg='max ' + msg)
+        self.assertEqual(tracker.get_min(), 30, msg='min ' + msg)
+        self.assertEqual(tracker.get_mean(), 60.0, msg='mean ' + msg)
+        self.assertEqual(tracker.get_mode(), 80, msg='mode ' + msg)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/test.txt b/users/wpcarro/scratch/data_structures_and_algorithms/test.txt
new file mode 100644
index 0000000000..ce01362503
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/test.txt
@@ -0,0 +1 @@
+hello
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/top-scores.py b/users/wpcarro/scratch/data_structures_and_algorithms/top-scores.py
new file mode 100644
index 0000000000..8e7b073dd8
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/top-scores.py
@@ -0,0 +1,25 @@
+from collections import deque
+
+# list:
+# array:
+# vector:
+# bit-{array,vector}:
+
+
+def sort(xs, highest):
+    v = [0] * (highest + 1)
+    result = deque()
+
+    for x in xs:
+        v[x] += 1
+
+    for i, x in enumerate(v):
+        if x > 0:
+            result.appendleft(i)
+
+    return list(result)
+
+
+assert sort([37, 89, 41, 100, 65, 91, 53],
+            100) == [100, 91, 89, 65, 53, 41, 37]
+print("Tests pass!")
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/topo-sort.py b/users/wpcarro/scratch/data_structures_and_algorithms/topo-sort.py
new file mode 100644
index 0000000000..fe295b0279
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/topo-sort.py
@@ -0,0 +1,31 @@
+from fixtures import unweighted_digraph
+from collections import deque
+
+# vertices_no_in_edges :: UnweightedDigraph -> Set(Vertex)
+def vertices_no_in_edges(g):
+    """Return the vertices in graph `g` with no in-edges."""
+    result = set()
+    vertices = set(g.keys())
+    for neighbors in g.values():
+        result = result.union(neighbors)
+    return vertices ^ result
+
+# topo_sort :: UnweightedDigraph -> List(Vertex)
+def topo_sort(g):
+    q = deque()
+    seen = set()
+    result = []
+    for x in vertices_no_in_edges(g):
+        q.append(x)
+    while q:
+        vertex = q.popleft()
+        if vertex in seen:
+            continue
+        result.append(vertex)
+        neighbors = g.get(vertex)
+        for x in g.get(vertex):
+            q.append(x)
+        seen.add(vertex)
+    return result
+
+print(topo_sort(unweighted_digraph))
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/trickling-water.py b/users/wpcarro/scratch/data_structures_and_algorithms/trickling-water.py
new file mode 100644
index 0000000000..45621990ec
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/trickling-water.py
@@ -0,0 +1,38 @@
+class Node(object):
+    def __init__(self, value, children=[]):
+        self.value = value
+        self.children = children
+
+
+################################################################################
+# Solution
+################################################################################
+def trip_time(node):
+    s = []
+    result = 0
+    s.append((node.value, node))
+    while s:
+        p, node = s.pop()
+        if not node.children:
+            result = max(result, p)
+        for x in node.children:
+            s.append((p + x.value, x))
+    return result
+
+
+################################################################################
+# Tests
+################################################################################
+tree = Node(
+    0,
+    children=[
+        Node(5, children=[Node(6)]),
+        Node(2, children=[
+            Node(6),
+            Node(10),
+        ]),
+        Node(3, children=[Node(2, children=[Node(11)])]),
+    ])
+
+assert trip_time(tree) == 16
+print("Tests pass!")
diff --git a/users/wpcarro/scratch/data_structures_and_algorithms/which-appears-twice.py b/users/wpcarro/scratch/data_structures_and_algorithms/which-appears-twice.py
new file mode 100644
index 0000000000..e9a4f0eb24
--- /dev/null
+++ b/users/wpcarro/scratch/data_structures_and_algorithms/which-appears-twice.py
@@ -0,0 +1,33 @@
+import unittest
+
+
+################################################################################
+# Solution
+################################################################################
+# find_repeat :: [Int] -> Int
+def find_repeat(xs):
+    n = len(xs) - 1
+    return sum(xs) - ((n**2 + n) / 2)
+
+
+################################################################################
+# Tests
+################################################################################
+class Test(unittest.TestCase):
+    def test_short_list(self):
+        actual = find_repeat([1, 2, 1])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_medium_list(self):
+        actual = find_repeat([4, 1, 3, 4, 2])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_long_list(self):
+        actual = find_repeat([1, 5, 9, 7, 2, 6, 3, 8, 2, 4])
+        expected = 2
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/balanced-binary-tree.py b/users/wpcarro/scratch/deepmind/part_one/balanced-binary-tree.py
new file mode 100644
index 0000000000..7fc174a2a9
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/balanced-binary-tree.py
@@ -0,0 +1,123 @@
+import unittest
+from collections import deque
+
+
+def is_balanced(node):
+    q, seen, ds = deque(), set(), set()
+    q.append((0, node))
+    while q:
+        d, node = q.popleft()
+        l, r = node.left, node.right
+        seen.add(node)
+        if not l and not r:
+            if d not in ds and len(ds) == 2:
+                return False
+            else:
+                ds.add(d)
+        if l and l not in seen:
+            q.append((d + 1, l))
+        if r and r not in seen:
+            q.append((d + 1, r))
+    return max(ds) - min(ds) <= 1
+
+
+# Tests
+class Test(unittest.TestCase):
+    class BinaryTreeNode(object):
+        def __init__(self, value):
+            self.value = value
+            self.left = None
+            self.right = None
+
+        def insert_left(self, value):
+            self.left = Test.BinaryTreeNode(value)
+            return self.left
+
+        def insert_right(self, value):
+            self.right = Test.BinaryTreeNode(value)
+            return self.right
+
+    def test_full_tree(self):
+        tree = Test.BinaryTreeNode(5)
+        left = tree.insert_left(8)
+        right = tree.insert_right(6)
+        left.insert_left(1)
+        left.insert_right(2)
+        right.insert_left(3)
+        right.insert_right(4)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_both_leaves_at_the_same_depth(self):
+        tree = Test.BinaryTreeNode(3)
+        left = tree.insert_left(4)
+        right = tree.insert_right(2)
+        left.insert_left(1)
+        right.insert_right(9)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_leaf_heights_differ_by_one(self):
+        tree = Test.BinaryTreeNode(6)
+        left = tree.insert_left(1)
+        right = tree.insert_right(0)
+        right.insert_right(7)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_leaf_heights_differ_by_two(self):
+        tree = Test.BinaryTreeNode(6)
+        left = tree.insert_left(1)
+        right = tree.insert_right(0)
+        right_right = right.insert_right(7)
+        right_right.insert_right(8)
+        result = is_balanced(tree)
+        self.assertFalse(result)
+
+    def test_three_leaves_total(self):
+        tree = Test.BinaryTreeNode(1)
+        left = tree.insert_left(5)
+        right = tree.insert_right(9)
+        right.insert_left(8)
+        right.insert_right(5)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_both_subtrees_superbalanced(self):
+        tree = Test.BinaryTreeNode(1)
+        left = tree.insert_left(5)
+        right = tree.insert_right(9)
+        right_left = right.insert_left(8)
+        right.insert_right(5)
+        right_left.insert_left(7)
+        result = is_balanced(tree)
+        self.assertFalse(result)
+
+    def test_both_subtrees_superbalanced_two(self):
+        tree = Test.BinaryTreeNode(1)
+        left = tree.insert_left(2)
+        right = tree.insert_right(4)
+        left.insert_left(3)
+        left_right = left.insert_right(7)
+        left_right.insert_right(8)
+        right_right = right.insert_right(5)
+        right_right_right = right_right.insert_right(6)
+        right_right_right.insert_right(9)
+        result = is_balanced(tree)
+        self.assertFalse(result)
+
+    def test_only_one_node(self):
+        tree = Test.BinaryTreeNode(1)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_linked_list_tree(self):
+        tree = Test.BinaryTreeNode(1)
+        right = tree.insert_right(2)
+        right_right = right.insert_right(3)
+        right_right.insert_right(4)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/dijkstra.py b/users/wpcarro/scratch/deepmind/part_one/dijkstra.py
new file mode 100644
index 0000000000..6975dbe4d1
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/dijkstra.py
@@ -0,0 +1,26 @@
+# Doing a practice implementation of Dijkstra's algorithm: a priority-first
+# search.
+from heapq import heappush, heappop
+
+
+class Node(object):
+    def __init__(self, value, children):
+        self.value = value
+        self.children = children
+
+
+def shortest_path(a, b):
+    """Return the shortest path from `a` to `b`."""
+    q = []
+    seen = set()
+    heappush((a.value, a, [a]), q)
+
+    while q:
+        d, node, path = heappop(q)
+        if node == b:
+            return path
+        seen.add(node)
+        for child in node.children:
+            if child not in seen:
+                heappush((d + child.value, child, path + [child]), q)
+    raise Exception("Path between nodes A and B does not exist.")
diff --git a/users/wpcarro/scratch/deepmind/part_one/efficiency.org b/users/wpcarro/scratch/deepmind/part_one/efficiency.org
new file mode 100644
index 0000000000..89a45c52ad
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/efficiency.org
@@ -0,0 +1,6 @@
+* Sorting
+** Merge:	O(n*log(n))
+** Heap:	O(n*log(n))
+** Insertion:	O(n^2)
+** Quick:	O(n^2)
+** Bubble:	O(n^2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/find-rotation-point.py b/users/wpcarro/scratch/deepmind/part_one/find-rotation-point.py
new file mode 100644
index 0000000000..5c21d5167c
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/find-rotation-point.py
@@ -0,0 +1,55 @@
+import unittest
+from math import floor
+
+
+def midpoint(a, b):
+    return a + floor((b - a) / 2)
+
+
+def do_find_rotation_point(a, b, xs):
+    i = midpoint(a, b)
+    count = b - a + 1
+
+    if count == 2:
+        if xs[a] > xs[b]:
+            return b
+        else:
+            return -1
+
+    if i in {a, b}:
+        return i
+
+    if xs[a] < xs[i]:
+        return do_find_rotation_point(i, b, xs)
+    else:
+        return do_find_rotation_point(a, i, xs)
+
+
+def find_rotation_point(xs):
+    return do_find_rotation_point(0, len(xs) - 1, xs)
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_small_list(self):
+        actual = find_rotation_point(['cape', 'cake'])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_medium_list(self):
+        actual = find_rotation_point(
+            ['grape', 'orange', 'plum', 'radish', 'apple'])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_large_list(self):
+        actual = find_rotation_point([
+            'ptolemaic', 'retrograde', 'supplant', 'undulate', 'xenoepist',
+            'asymptote', 'babka', 'banoffee', 'engender', 'karpatka',
+            'othellolagkage'
+        ])
+        expected = 5
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/inflight-entertainment.py b/users/wpcarro/scratch/deepmind/part_one/inflight-entertainment.py
new file mode 100644
index 0000000000..2116b27b0b
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/inflight-entertainment.py
@@ -0,0 +1,51 @@
+import unittest
+
+
+def can_two_movies_fill_flight(xs, t):
+    seeking = set()
+    for x in xs:
+        if x in seeking:
+            return True
+        else:
+            seeking.add(t - x)
+    return False
+
+
+# Tests
+
+
+class Test(unittest.TestCase):
+    def test_short_flight(self):
+        result = can_two_movies_fill_flight([2, 4], 1)
+        self.assertFalse(result)
+
+    def test_long_flight(self):
+        result = can_two_movies_fill_flight([2, 4], 6)
+        self.assertTrue(result)
+
+    def test_one_movie_half_flight_length(self):
+        result = can_two_movies_fill_flight([3, 8], 6)
+        self.assertFalse(result)
+
+    def test_two_movies_half_flight_length(self):
+        result = can_two_movies_fill_flight([3, 8, 3], 6)
+        self.assertTrue(result)
+
+    def test_lots_of_possible_pairs(self):
+        result = can_two_movies_fill_flight([1, 2, 3, 4, 5, 6], 7)
+        self.assertTrue(result)
+
+    def test_not_using_first_movie(self):
+        result = can_two_movies_fill_flight([4, 3, 2], 5)
+        self.assertTrue(result)
+
+    def test_only_one_movie(self):
+        result = can_two_movies_fill_flight([6], 6)
+        self.assertFalse(result)
+
+    def test_no_movies(self):
+        result = can_two_movies_fill_flight([], 2)
+        self.assertFalse(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/kth-to-last.py b/users/wpcarro/scratch/deepmind/part_one/kth-to-last.py
new file mode 100644
index 0000000000..5335e419f7
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/kth-to-last.py
@@ -0,0 +1,64 @@
+import unittest
+
+
+def kth_to_last_node(k, x):
+    a, b = x, x
+
+    if k == 0:
+        raise Exception('Value of 0 for k is not supported')
+
+    for _ in range(k - 1):
+        if not a.next:
+            raise Exception('Value of {} for k is too large'.format(k))
+        a = a.next
+
+    while a.next:
+        a, b = a.next, b.next
+    return b
+
+
+class Test(unittest.TestCase):
+    class LinkedListNode(object):
+        def __init__(self, value, next=None):
+            self.value = value
+            self.next = next
+
+        def get_values(self):
+            node = self
+            values = []
+            while node is not None:
+                values.append(node.value)
+                node = node.next
+            return values
+
+    def setUp(self):
+        self.fourth = Test.LinkedListNode(4)
+        self.third = Test.LinkedListNode(3, self.fourth)
+        self.second = Test.LinkedListNode(2, self.third)
+        self.first = Test.LinkedListNode(1, self.second)
+
+    def test_first_to_last_node(self):
+        actual = kth_to_last_node(1, self.first)
+        expected = self.fourth
+        self.assertEqual(actual, expected)
+
+    def test_second_to_last_node(self):
+        actual = kth_to_last_node(2, self.first)
+        expected = self.third
+        self.assertEqual(actual, expected)
+
+    def test_first_node(self):
+        actual = kth_to_last_node(4, self.first)
+        expected = self.first
+        self.assertEqual(actual, expected)
+
+    def test_k_greater_than_linked_list_length(self):
+        with self.assertRaises(Exception):
+            kth_to_last_node(5, self.first)
+
+    def test_k_is_zero(self):
+        with self.assertRaises(Exception):
+            kth_to_last_node(0, self.first)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/merging-ranges.py b/users/wpcarro/scratch/deepmind/part_one/merging-ranges.py
new file mode 100644
index 0000000000..23b40793b8
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/merging-ranges.py
@@ -0,0 +1,59 @@
+import unittest
+
+
+def merge_ranges(xs):
+    xs.sort()
+    result = [xs[0]]
+    for curr in xs[1:]:
+        a, z = result[-1]
+        if z >= curr[0]:
+            result[-1] = (a, max(z, curr[1]))
+        else:
+            result.append(curr)
+    return result
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_meetings_overlap(self):
+        actual = merge_ranges([(1, 3), (2, 4)])
+        expected = [(1, 4)]
+        self.assertEqual(actual, expected)
+
+    def test_meetings_touch(self):
+        actual = merge_ranges([(5, 6), (6, 8)])
+        expected = [(5, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_meeting_contains_other_meeting(self):
+        actual = merge_ranges([(1, 8), (2, 5)])
+        expected = [(1, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_meetings_stay_separate(self):
+        actual = merge_ranges([(1, 3), (4, 8)])
+        expected = [(1, 3), (4, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_multiple_merged_meetings(self):
+        actual = merge_ranges([(1, 4), (2, 5), (5, 8)])
+        expected = [(1, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_meetings_not_sorted(self):
+        actual = merge_ranges([(5, 8), (1, 4), (6, 8)])
+        expected = [(1, 4), (5, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_one_long_meeting_contains_smaller_meetings(self):
+        actual = merge_ranges([(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)])
+        expected = [(1, 12)]
+        self.assertEqual(actual, expected)
+
+    def test_sample_input(self):
+        actual = merge_ranges([(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)])
+        expected = [(0, 1), (3, 8), (9, 12)]
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/recursive-string-permutations.py b/users/wpcarro/scratch/deepmind/part_one/recursive-string-permutations.py
new file mode 100644
index 0000000000..f50db28387
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/recursive-string-permutations.py
@@ -0,0 +1,56 @@
+import unittest
+from itertools import permutations
+
+
+class Node(object):
+    def __init__(self, x):
+        self.value = x
+        self.children = []
+
+
+def make_tree(c, xs):
+    root = Node(c)
+    for x in xs:
+        root.children.append(make_tree(x, xs - {x}))
+    return root
+
+
+def get_permutations(xs):
+    xs = set(xs)
+    root = make_tree("", xs)
+    q, perms = [], set()
+    q.append(("", root))
+    while q:
+        c, node = q.pop()
+        if not node.children:
+            perms.add(c)
+        else:
+            for child in node.children:
+                q.append((c + child.value, child))
+    return perms
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_empty_string(self):
+        actual = get_permutations('')
+        expected = set([''])
+        self.assertEqual(actual, expected)
+
+    def test_one_character_string(self):
+        actual = get_permutations('a')
+        expected = set(['a'])
+        self.assertEqual(actual, expected)
+
+    def test_two_character_string(self):
+        actual = get_permutations('ab')
+        expected = set(['ab', 'ba'])
+        self.assertEqual(actual, expected)
+
+    def test_three_character_string(self):
+        actual = get_permutations('abc')
+        expected = set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba'])
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/reverse-linked-list.py b/users/wpcarro/scratch/deepmind/part_one/reverse-linked-list.py
new file mode 100644
index 0000000000..82fac171d5
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/reverse-linked-list.py
@@ -0,0 +1,74 @@
+import unittest
+
+
+def reverse(node):
+    prev = None
+    next = None
+    curr = node
+
+    while curr:
+        next = curr.next
+        curr.next = prev
+        prev = curr
+        curr = next
+
+    return prev
+
+
+# Tests
+class Test(unittest.TestCase):
+    class LinkedListNode(object):
+        def __init__(self, value, next=None):
+            self.value = value
+            self.next = next
+
+        def get_values(self):
+            node = self
+            values = []
+            while node is not None:
+                values.append(node.value)
+                node = node.next
+            return values
+
+    def test_short_linked_list(self):
+        second = Test.LinkedListNode(2)
+        first = Test.LinkedListNode(1, second)
+
+        result = reverse(first)
+        self.assertIsNotNone(result)
+
+        actual = result.get_values()
+        expected = [2, 1]
+        self.assertEqual(actual, expected)
+
+    def test_long_linked_list(self):
+        sixth = Test.LinkedListNode(6)
+        fifth = Test.LinkedListNode(5, sixth)
+        fourth = Test.LinkedListNode(4, fifth)
+        third = Test.LinkedListNode(3, fourth)
+        second = Test.LinkedListNode(2, third)
+        first = Test.LinkedListNode(1, second)
+
+        result = reverse(first)
+        self.assertIsNotNone(result)
+
+        actual = result.get_values()
+        expected = [6, 5, 4, 3, 2, 1]
+        self.assertEqual(actual, expected)
+
+    def test_one_element_linked_list(self):
+        first = Test.LinkedListNode(1)
+
+        result = reverse(first)
+        self.assertIsNotNone(result)
+
+        actual = result.get_values()
+        expected = [1]
+        self.assertEqual(actual, expected)
+
+    def test_empty_linked_list(self):
+        result = reverse(None)
+        self.assertIsNone(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/stock-price.py b/users/wpcarro/scratch/deepmind/part_one/stock-price.py
new file mode 100644
index 0000000000..7055b66af1
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/stock-price.py
@@ -0,0 +1,51 @@
+def get_max_profit(xs):
+    best_profit = xs[1] - xs[0]
+    lowest_buy = xs[0]
+
+    for x in xs[1:]:
+        best_profit = max(best_profit, x - lowest_buy)
+        lowest_buy = min(lowest_buy, x)
+    return best_profit
+
+
+# Tests
+
+import unittest
+
+
+class Test(unittest.TestCase):
+    def test_price_goes_up_then_down(self):
+        actual = get_max_profit([1, 5, 3, 2])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_price_goes_down_then_up(self):
+        actual = get_max_profit([7, 2, 8, 9])
+        expected = 7
+        self.assertEqual(actual, expected)
+
+    def test_price_goes_up_all_day(self):
+        actual = get_max_profit([1, 6, 7, 9])
+        expected = 8
+        self.assertEqual(actual, expected)
+
+    def test_price_goes_down_all_day(self):
+        actual = get_max_profit([9, 7, 4, 1])
+        expected = -2
+        self.assertEqual(actual, expected)
+
+    def test_price_stays_the_same_all_day(self):
+        actual = get_max_profit([1, 1, 1, 1])
+        expected = 0
+        self.assertEqual(actual, expected)
+
+    def test_error_with_empty_prices(self):
+        with self.assertRaises(Exception):
+            get_max_profit([])
+
+    def test_error_with_one_price(self):
+        with self.assertRaises(Exception):
+            get_max_profit([1])
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_one/which-appears-twice.py b/users/wpcarro/scratch/deepmind/part_one/which-appears-twice.py
new file mode 100644
index 0000000000..c01379295d
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_one/which-appears-twice.py
@@ -0,0 +1,29 @@
+import unittest
+
+
+def find_repeat(xs):
+    n = max(xs)
+    expected_sum = (n + 1) * n / 2
+    actual_sum = sum(xs)
+    return actual_sum - expected_sum
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_short_list(self):
+        actual = find_repeat([1, 2, 1])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_medium_list(self):
+        actual = find_repeat([4, 1, 3, 4, 2])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_long_list(self):
+        actual = find_repeat([1, 5, 9, 7, 2, 6, 3, 8, 2, 4])
+        expected = 2
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/.envrc b/users/wpcarro/scratch/deepmind/part_two/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/scratch/deepmind/part_two/balanced-binary-tree.py b/users/wpcarro/scratch/deepmind/part_two/balanced-binary-tree.py
new file mode 100644
index 0000000000..03de0350d8
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/balanced-binary-tree.py
@@ -0,0 +1,126 @@
+import unittest
+from collections import deque
+
+
+# is_balanced :: Node(a) -> Bool
+def is_balanced(node):
+    q = deque()
+    q.append((0, node))
+    mn, mx = None, None
+
+    while q:
+        depth, node = q.popleft()
+        # Current node is a leaf node
+        if not node.left and not node.right:
+            mx = depth if mx is None else max(mx, depth)
+            mn = depth if mn is None else min(mn, depth)
+            if mx - mn > 1:
+                return False
+        if node.left:
+            q.append((depth + 1, node.left))
+        if node.right:
+            q.append((depth + 1, node.right))
+
+    return mx - mn <= 1
+
+
+# Tests
+class Test(unittest.TestCase):
+    class BinaryTreeNode(object):
+        def __init__(self, value):
+            self.value = value
+            self.left = None
+            self.right = None
+
+        def insert_left(self, value):
+            self.left = Test.BinaryTreeNode(value)
+            return self.left
+
+        def insert_right(self, value):
+            self.right = Test.BinaryTreeNode(value)
+            return self.right
+
+    def test_full_tree(self):
+        tree = Test.BinaryTreeNode(5)
+        left = tree.insert_left(8)
+        right = tree.insert_right(6)
+        left.insert_left(1)
+        left.insert_right(2)
+        right.insert_left(3)
+        right.insert_right(4)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_both_leaves_at_the_same_depth(self):
+        tree = Test.BinaryTreeNode(3)
+        left = tree.insert_left(4)
+        right = tree.insert_right(2)
+        left.insert_left(1)
+        right.insert_right(9)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_leaf_heights_differ_by_one(self):
+        tree = Test.BinaryTreeNode(6)
+        left = tree.insert_left(1)
+        right = tree.insert_right(0)
+        right.insert_right(7)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_leaf_heights_differ_by_two(self):
+        tree = Test.BinaryTreeNode(6)
+        left = tree.insert_left(1)
+        right = tree.insert_right(0)
+        right_right = right.insert_right(7)
+        right_right.insert_right(8)
+        result = is_balanced(tree)
+        self.assertFalse(result)
+
+    def test_three_leaves_total(self):
+        tree = Test.BinaryTreeNode(1)
+        left = tree.insert_left(5)
+        right = tree.insert_right(9)
+        right.insert_left(8)
+        right.insert_right(5)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_both_subtrees_superbalanced(self):
+        tree = Test.BinaryTreeNode(1)
+        left = tree.insert_left(5)
+        right = tree.insert_right(9)
+        right_left = right.insert_left(8)
+        right.insert_right(5)
+        right_left.insert_left(7)
+        result = is_balanced(tree)
+        self.assertFalse(result)
+
+    def test_both_subtrees_superbalanced_two(self):
+        tree = Test.BinaryTreeNode(1)
+        left = tree.insert_left(2)
+        right = tree.insert_right(4)
+        left.insert_left(3)
+        left_right = left.insert_right(7)
+        left_right.insert_right(8)
+        right_right = right.insert_right(5)
+        right_right_right = right_right.insert_right(6)
+        right_right_right.insert_right(9)
+        result = is_balanced(tree)
+        self.assertFalse(result)
+
+    def test_only_one_node(self):
+        tree = Test.BinaryTreeNode(1)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+    def test_linked_list_tree(self):
+        tree = Test.BinaryTreeNode(1)
+        right = tree.insert_right(2)
+        right_right = right.insert_right(3)
+        right_right.insert_right(4)
+        result = is_balanced(tree)
+        self.assertTrue(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/bst-checker.py b/users/wpcarro/scratch/deepmind/part_two/bst-checker.py
new file mode 100644
index 0000000000..fd0374a9ce
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/bst-checker.py
@@ -0,0 +1,110 @@
+import unittest
+from collections import deque
+
+
+# While this function solves the problem, it uses O(n) space since we're storing
+# all of the less-thans and greater-thans.
+def is_binary_search_tree_first_attempt(root):
+    q = deque()
+    q.append((set(), set(), root))
+
+    while q:
+        lts, gts, node = q.popleft()
+
+        if not all([node.value < lt for lt in lts]):
+            return False
+        if not all([node.value > gt for gt in gts]):
+            return False
+
+        if node.left:
+            q.append((lts | {node.value}, gts, node.left))
+        if node.right:
+            q.append((lts, gts | {node.value}, node.right))
+
+    return True
+
+
+# While I did not originally solve this problem this way, when I learned that I
+# could condense the space of my solution's runtime, I wrote this.
+def is_binary_search_tree(root):
+    q = deque()
+    q.append((None, None, root))
+
+    while q:
+        lt, gt, node = q.popleft()
+
+        if not lt is None and node.value >= lt:
+            return False
+        if not gt is None and node.value <= gt:
+            return False
+
+        if node.left:
+            q.append((node.value, gt, node.left))
+        if node.right:
+            q.append((lt, node.value, node.right))
+
+    return True
+
+
+# Tests
+class Test(unittest.TestCase):
+    class BinaryTreeNode(object):
+        def __init__(self, value):
+            self.value = value
+            self.left = None
+            self.right = None
+
+        def insert_left(self, value):
+            self.left = Test.BinaryTreeNode(value)
+            return self.left
+
+        def insert_right(self, value):
+            self.right = Test.BinaryTreeNode(value)
+            return self.right
+
+    def test_valid_full_tree(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(30)
+        right = tree.insert_right(70)
+        left.insert_left(10)
+        left.insert_right(40)
+        right.insert_left(60)
+        right.insert_right(80)
+        result = is_binary_search_tree(tree)
+        self.assertTrue(result)
+
+    def test_both_subtrees_valid(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(30)
+        right = tree.insert_right(80)
+        left.insert_left(20)
+        left.insert_right(60)
+        right.insert_left(70)
+        right.insert_right(90)
+        result = is_binary_search_tree(tree)
+        self.assertFalse(result)
+
+    def test_descending_linked_list(self):
+        tree = Test.BinaryTreeNode(50)
+        left = tree.insert_left(40)
+        left_left = left.insert_left(30)
+        left_left_left = left_left.insert_left(20)
+        left_left_left.insert_left(10)
+        result = is_binary_search_tree(tree)
+        self.assertTrue(result)
+
+    def test_out_of_order_linked_list(self):
+        tree = Test.BinaryTreeNode(50)
+        right = tree.insert_right(70)
+        right_right = right.insert_right(60)
+        right_right.insert_right(80)
+        result = is_binary_search_tree(tree)
+        self.assertFalse(result)
+
+    def test_one_node_tree(self):
+        tree = Test.BinaryTreeNode(50)
+        result = is_binary_search_tree(tree)
+        self.assertTrue(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/cafe-order-checker.py b/users/wpcarro/scratch/deepmind/part_two/cafe-order-checker.py
new file mode 100644
index 0000000000..0e31214b83
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/cafe-order-checker.py
@@ -0,0 +1,64 @@
+import unittest
+
+
+# Solution
+def is_first_come_first_served(xs, ys, zs):
+    i, j = 0, 0
+    for z in zs:
+        if i < len(xs) and z == xs[i]:
+            i += 1
+        elif j < len(ys) and z == ys[j]:
+            j += 1
+        else:
+            return False
+    return i == len(xs) and j == len(ys)
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_both_registers_have_same_number_of_orders(self):
+        result = is_first_come_first_served([1, 4, 5], [2, 3, 6],
+                                            [1, 2, 3, 4, 5, 6])
+        self.assertTrue(result)
+
+    def test_registers_have_different_lengths(self):
+        result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 2, 6, 3, 5])
+        self.assertFalse(result)
+
+    def test_one_register_is_empty(self):
+        result = is_first_come_first_served([], [2, 3, 6], [2, 3, 6])
+        self.assertTrue(result)
+
+    def test_served_orders_is_missing_orders(self):
+        result = is_first_come_first_served([1, 5], [2, 3, 6], [1, 6, 3, 5])
+        self.assertFalse(result)
+
+    def test_served_orders_has_extra_orders(self):
+        result = is_first_come_first_served([1, 5], [2, 3, 6],
+                                            [1, 2, 3, 5, 6, 8])
+        self.assertFalse(result)
+
+    def test_one_register_has_extra_orders(self):
+        result = is_first_come_first_served([1, 9], [7, 8], [1, 7, 8])
+        self.assertFalse(result)
+
+    def test_one_register_has_unserved_orders(self):
+        result = is_first_come_first_served([55, 9], [7, 8], [1, 7, 8, 9])
+        self.assertFalse(result)
+
+    # Bonus
+    def test_handles_repeats(self):
+        actual = is_first_come_first_served([1, 2, 1], [3, 4, 5, 5],
+                                            [3, 4, 1, 5, 5, 2, 1])
+        self.assertTrue(actual)
+
+    def test_kitchen_didnt_serve(self):
+        actual = is_first_come_first_served([1, 2], [3, 4], [1, 3, 4])
+        self.assertFalse(actual)
+
+    def test_customer_didnt_pay(self):
+        actual = is_first_come_first_served([2], [3, 4], [1, 3, 4])
+        self.assertFalse(actual)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/coin.ts b/users/wpcarro/scratch/deepmind/part_two/coin.ts
new file mode 100644
index 0000000000..8aa8de8bb8
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/coin.ts
@@ -0,0 +1,102 @@
+// The denomination of a coin.
+type Coin = number;
+
+// The amount of change remaining.
+type Amount = number;
+
+// Mapping of Coin -> Int
+type CoinBag = Map<Coin, number>;
+
+function createCoinBag(coins: Coin[]): CoinBag {
+  const result = new Map();
+
+  for (const coin of coins) {
+    result.set(coin, 0);
+  }
+
+  return result;
+}
+
+// This algorithm should work conceptual, but it does not actually
+// work. JavaScript uses reference equality when constructing a Set<Map<A,B>>,
+// so my result.size returns a higher number than I expect because it contains
+// many duplicate entries.
+//
+// Conceptually, I'm not sure this solution is optimal either -- even after I
+// can dedupe the entries in `result`.
+function changePossibilities(amt: Amount, coins: Coin[]): number {
+  if (amt === 0) {
+    return 1;
+  }
+  const result: Set<CoinBag> = new Set();
+
+  const q: [Coin, Amount, CoinBag][] = [];
+
+  for (const coin of coins) {
+    const bag = createCoinBag(coins);
+    bag.set(coin, 1);
+    q.push([coin, amt - coin, bag]);
+  }
+
+  while (q.length > 0) {
+    const [coin, amt, bag] = q.shift();
+
+    console.log([coin, amt, bag]);
+
+    if (amt === 0) {
+      result.add(bag);
+    } else if (amt < 0) {
+      continue;
+    } else {
+      for (const c of coins) {
+        const bagCopy = new Map(bag);
+        const value = bagCopy.get(c);
+        bagCopy.set(c, value + 1);
+        q.push([c, amt - c, bagCopy]);
+      }
+    }
+  }
+  console.log(result);
+  return result.size;
+}
+
+// Tests
+let desc = "sample input";
+let actual = changePossibilities(4, [1, 2, 3]);
+let expected = 4;
+assertEqual(actual, expected, desc);
+
+desc = "one way to make zero cents";
+actual = changePossibilities(0, [1, 2]);
+expected = 1;
+assertEqual(actual, expected, desc);
+
+desc = "no ways if no coins";
+actual = changePossibilities(1, []);
+expected = 0;
+assertEqual(actual, expected, desc);
+
+desc = "big coin value";
+actual = changePossibilities(5, [25, 50]);
+expected = 0;
+assertEqual(actual, expected, desc);
+
+desc = "big target amount";
+actual = changePossibilities(50, [5, 10]);
+expected = 6;
+assertEqual(actual, expected, desc);
+
+// I think InterviewCake designed this assertion to be computationally
+// expensive.
+desc = "change for one dollar";
+actual = changePossibilities(100, [1, 5, 10, 25, 50]);
+expected = 292;
+assertEqual(actual, expected, desc);
+
+function assertEqual(a, b, desc) {
+  if (a === b) {
+    console.log(`${desc} ... PASS`);
+  } else {
+    console.log(`${desc} ... FAIL: ${a} != ${b}`);
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/delete-node.py b/users/wpcarro/scratch/deepmind/part_two/delete-node.py
new file mode 100644
index 0000000000..4ed02ec308
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/delete-node.py
@@ -0,0 +1,57 @@
+import unittest
+
+
+def delete_node(node):
+    if node.next:
+        node.value = node.next.value
+        node.next = node.next.next
+    else:
+        raise Exception(
+            "We cannot delete the last node in a linked list using this function"
+        )
+
+
+# Tests
+class Test(unittest.TestCase):
+    class LinkedListNode(object):
+        def __init__(self, value, next=None):
+            self.value = value
+            self.next = next
+
+        def get_values(self):
+            node = self
+            values = []
+            while node is not None:
+                values.append(node.value)
+                node = node.next
+            return values
+
+    def setUp(self):
+        self.fourth = Test.LinkedListNode(4)
+        self.third = Test.LinkedListNode(3, self.fourth)
+        self.second = Test.LinkedListNode(2, self.third)
+        self.first = Test.LinkedListNode(1, self.second)
+
+    def test_node_at_beginning(self):
+        delete_node(self.first)
+        actual = self.first.get_values()
+        expected = [2, 3, 4]
+        self.assertEqual(actual, expected)
+
+    def test_node_in_middle(self):
+        delete_node(self.second)
+        actual = self.first.get_values()
+        expected = [1, 3, 4]
+        self.assertEqual(actual, expected)
+
+    def test_node_at_end(self):
+        with self.assertRaises(Exception):
+            delete_node(self.fourth)
+
+    def test_one_node_in_list(self):
+        unique = Test.LinkedListNode(1)
+        with self.assertRaises(Exception):
+            delete_node(unique)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/find-duplicate-optimize-for-space-beast-mode.py b/users/wpcarro/scratch/deepmind/part_two/find-duplicate-optimize-for-space-beast-mode.py
new file mode 100644
index 0000000000..c9edc32c88
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/find-duplicate-optimize-for-space-beast-mode.py
@@ -0,0 +1,114 @@
+import unittest
+
+
+################################################################################
+# InterviewCake's solution
+################################################################################
+def cycle_len(xs, i):
+    """
+    Returns the length of a cycle that contains no duplicate items.
+    """
+    result = 1
+    checkpt = i
+    current = xs[checkpt - 1]
+
+    while current != checkpt:
+        current = xs[current - 1]
+        result += 1
+
+    return result
+
+
+def theirs(xs):
+    """
+    This is InterviewCake's solution.
+    """
+    i = xs[-1]
+    for _ in range(len(xs) - 1):
+        i = xs[i - 1]
+
+    cycle_length = cycle_len(xs, i)
+
+    p0 = xs[-1]
+    p1 = xs[-1]
+    for _ in range(cycle_length):
+        p1 = xs[p1 - 1]
+
+    while p0 != p1:
+        p0 = xs[p0 - 1]
+        p1 = xs[p1 - 1]
+
+    print(p0, p1)
+
+    return p0
+
+
+################################################################################
+# My solution
+################################################################################
+def mine(xs):
+    """
+    This is the solution that I came up with, which differs from InterviewCake's
+    solution.
+    """
+    i = xs[-1]
+    offset = 1 if len(xs) % 2 == 0 else 2
+
+    for _ in range(len(xs) - offset):
+        i = xs[i - 1]
+
+    return i
+
+
+use_mine = True
+find_duplicate = mine if use_mine else theirs
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_just_the_repeated_number(self):
+        # len(xs) even
+        actual = find_duplicate([1, 1])
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_short_list(self):
+        # len(xs) even
+        actual = find_duplicate([1, 2, 3, 2])
+        expected = 2
+        self.assertEqual(actual, expected)
+
+    def test_medium_list(self):
+        # len(xs) even
+        actual = find_duplicate([1, 2, 5, 5, 5, 5])
+        expected = 5
+        self.assertEqual(actual, expected)
+
+    def test_long_list(self):
+        # len(xs) odd
+        actual = find_duplicate([4, 1, 4, 8, 3, 2, 7, 6, 5])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    ############################################################################
+    # Additional examples from InterviewCake.com
+    ############################################################################
+    def test_example_a(self):
+        # len(xs) even
+        actual = find_duplicate([3, 4, 2, 3, 1, 5])
+        expected = 3
+        self.assertTrue(actual, expected)
+
+    def test_example_b(self):
+        # len(xs) even
+        actual = find_duplicate([3, 1, 2, 2])
+        expected = 2
+        self.assertEqual(actual, expected)
+
+    def test_example_c(self):
+        # len(xs) odd BUT multiple duplicates
+        actual = find_duplicate([4, 3, 1, 1, 4])
+        self.assertTrue(actual in {1, 4})
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/find-duplicate-optimize-for-space.ts b/users/wpcarro/scratch/deepmind/part_two/find-duplicate-optimize-for-space.ts
new file mode 100644
index 0000000000..98f5bb144e
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/find-duplicate-optimize-for-space.ts
@@ -0,0 +1,70 @@
+function findRepeatBruteForce(xs: Array<number>): number {
+  // InterviewCake asks us to write a function that optimizes for space. Using
+  // brute force, we can write a function that returns an answer using constant
+  // (i.e. O(1)) space at the cost of a quadratic (i.e. O(n^2)) runtime.
+  //
+  // I did not think of this myself; InterviewCake's "Tell me more" hints
+  // did. Since I think this idea is clever, I wrote a solution from memory to
+  // help me internalize the solution.
+  for (let i = 0; i < xs.length; i += 1) {
+    let seeking = xs[i];
+    for (let j = i + 1; j < xs.length; j += 1) {
+      if (xs[j] === seeking) {
+        return seeking;
+      }
+    }
+  }
+}
+
+function findRepeatSort(xs: Array<number>): number {
+  // This version first sorts xs, which gives the function a time-complexity of
+  // O(n*log(n)), which is better than the quadratic complexity of the
+  // brute-force solution. The space requirement here is constant.
+  //
+  // Since we need to sort xs in-place to avoid paying a O(n) space cost for
+  // storing the newly sorted xs, we're mutating our input. InterviewCake
+  // advises us to not mutate our input.
+  xs.sort();
+  let i = 0;
+  let j = 1;
+  for (; j < xs.length; ) {
+    if (xs[i] === xs[j]) {
+      return xs[i];
+    }
+    i += 1;
+    j += 1;
+  }
+}
+
+function findRepeat(xs: Array<number>): number {
+  return 0;
+}
+
+// Tests
+let desc = "just the repeated number";
+let actual = findRepeat([1, 1]);
+let expected = 1;
+assertEqual(actual, expected, desc);
+
+desc = "short array";
+actual = findRepeat([1, 2, 3, 2]);
+expected = 2;
+assertEqual(actual, expected, desc);
+
+desc = "medium array";
+actual = findRepeat([1, 2, 5, 5, 5, 5]);
+expected = 5;
+assertEqual(actual, expected, desc);
+
+desc = "long array";
+actual = findRepeat([4, 1, 4, 8, 3, 2, 7, 6, 5]);
+expected = 4;
+assertEqual(actual, expected, desc);
+
+function assertEqual(a, b, desc) {
+  if (a === b) {
+    console.log(`${desc} ... PASS`);
+  } else {
+    console.log(`${desc} ... FAIL: ${a} != ${b}`);
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/find-rotation-point.ts b/users/wpcarro/scratch/deepmind/part_two/find-rotation-point.ts
new file mode 100644
index 0000000000..7bf1a48445
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/find-rotation-point.ts
@@ -0,0 +1,68 @@
+function findRotationPoint(xs: Array<string>): number {
+  // Find the rotation point in the vector.
+  let beg = 0;
+  let end = xs.length - 1;
+
+  while (beg != end) {
+    let mid = beg + Math.floor((end - beg) / 2);
+
+    if (beg === mid) {
+      return xs[beg] < xs[end] ? beg : end;
+    }
+
+    if (xs[end] <= xs[mid]) {
+      beg = mid;
+      end = end;
+    } else {
+      beg = beg;
+      end = mid;
+    }
+  }
+
+  return beg;
+}
+
+// Tests
+let desc;
+let actual;
+let expected;
+
+desc = "small array one";
+actual = findRotationPoint(["cape", "cake"]);
+expected = 1;
+assertEquals(actual, expected, desc);
+
+desc = "small array two";
+actual = findRotationPoint(["cake", "cape"]);
+expected = 0;
+assertEquals(actual, expected, desc);
+
+desc = "medium array";
+actual = findRotationPoint(["grape", "orange", "plum", "radish", "apple"]);
+expected = 4;
+assertEquals(actual, expected, desc);
+
+desc = "large array";
+actual = findRotationPoint([
+  "ptolemaic",
+  "retrograde",
+  "supplant",
+  "undulate",
+  "xenoepist",
+  "asymptote",
+  "babka",
+  "banoffee",
+  "engender",
+  "karpatka",
+  "othellolagkage"
+]);
+expected = 5;
+assertEquals(actual, expected, desc);
+
+function assertEquals(a, b, desc) {
+  if (a === b) {
+    console.log(`${desc} ... PASS`);
+  } else {
+    console.log(`${desc} ... FAIL: ${a} != ${b}`);
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/graph-coloring.ts b/users/wpcarro/scratch/deepmind/part_two/graph-coloring.ts
new file mode 100644
index 0000000000..a0b6d5dbae
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/graph-coloring.ts
@@ -0,0 +1,232 @@
+type Color = string;
+
+interface GraphNode {
+  label: string;
+  neighbors: Set<GraphNode>;
+  color: string;
+}
+
+class GraphNode {
+  constructor(label: string) {
+    this.label = label;
+    this.neighbors = new Set();
+    this.color = null;
+  }
+}
+
+interface Queue<A> {
+  xs: Array<A>;
+}
+
+class Queue<A> {
+  constructor() {
+    this.xs = [];
+  }
+  isEmpty(): boolean {
+    return this.xs.length === 0;
+  }
+  enqueue(x: A): void {
+    this.xs.push(x);
+  }
+  dequeue(): A {
+    return this.xs.shift();
+  }
+}
+
+type Graph = Array<GraphNode>;
+
+// Return a set of all of the colors from the neighbor nodes of `node`.
+function neighborColors(node: GraphNode): Set<Color> {
+  const result: Set<Color> = new Set();
+
+  for (const x of node.neighbors) {
+    if (typeof x.color === 'string') {
+      result.add(x.color);
+    }
+  }
+
+  return result;
+}
+
+// Returns the set difference between sets `xs`, and `ys`.
+function setDifference<A>(xs: Set<A>, ys: Set<A>): Set<A> {
+  const result: Set<A> = new Set();
+
+  for (const x of xs) {
+    if (!ys.has(x)) {
+      result.add(x);
+    }
+  }
+
+  return result;
+}
+
+// Returns an element from the set, `xs`.
+// Throwns an error if `xs` is an empty set.
+function choose<A>(xs: Set<A>): A {
+  if (xs.size === 0) {
+    throw new Error('Cannot choose an element from an empty set.');
+  } else {
+    return xs.values().next().value;
+  }
+}
+
+// Returns true if `node` is present in `node.neighbors`.
+function isCyclic(node: GraphNode): boolean {
+  for (const x of node.neighbors) {
+    if (x === node) {
+      return true;
+    }
+  }
+}
+
+function colorGraph(graph: Graph, colors: Array<Color>): void {
+  const allColors = new Set(colors);
+
+  for (const node of graph) {
+    if (isCyclic(node)) {
+      throw new Error('InterviewCake would like me to invalidate this');
+    }
+    if (typeof node.color !== 'string') {
+      node.color = choose(setDifference(allColors, neighborColors(node)));
+    }
+  }
+}
+
+
+// Tests
+const colors = ['red', 'green', 'blue', 'orange', 'yellow', 'white'];
+
+let graph = [];
+{
+  const nodeA = new GraphNode('A');
+  const nodeB = new GraphNode('B');
+  const nodeC = new GraphNode('C');
+  const nodeD = new GraphNode('D');
+  nodeA.neighbors.add(nodeB);
+  nodeB.neighbors.add(nodeA);
+  nodeB.neighbors.add(nodeC);
+  nodeC.neighbors.add(nodeB);
+  nodeC.neighbors.add(nodeD);
+  nodeD.neighbors.add(nodeC);
+  graph = [nodeA, nodeB, nodeC, nodeD];
+}
+colorGraph(graph, colors);
+assertEqual(validateGraphColoring(graph), true, 'line graph');
+
+{
+  const nodeA = new GraphNode('A');
+  const nodeB = new GraphNode('B');
+  const nodeC = new GraphNode('C');
+  const nodeD = new GraphNode('D');
+  nodeA.neighbors.add(nodeB);
+  nodeB.neighbors.add(nodeA);
+  nodeC.neighbors.add(nodeD);
+  nodeD.neighbors.add(nodeC);
+  graph = [nodeA, nodeB, nodeC, nodeD];
+}
+colorGraph(graph, colors);
+assertEqual(validateGraphColoring(graph), true, 'separate graph');
+
+{
+  const nodeA = new GraphNode('A');
+  const nodeB = new GraphNode('B');
+  const nodeC = new GraphNode('C');
+  nodeA.neighbors.add(nodeB);
+  nodeA.neighbors.add(nodeC);
+  nodeB.neighbors.add(nodeA);
+  nodeB.neighbors.add(nodeC);
+  nodeC.neighbors.add(nodeA);
+  nodeC.neighbors.add(nodeB);
+  graph = [nodeA, nodeB, nodeC];
+}
+colorGraph(graph, colors);
+assertEqual(validateGraphColoring(graph), true, 'triangle graph');
+
+{
+  const nodeA = new GraphNode('A');
+  const nodeB = new GraphNode('B');
+  const nodeC = new GraphNode('C');
+  const nodeD = new GraphNode('D');
+  const nodeE = new GraphNode('E');
+  nodeA.neighbors.add(nodeB);
+  nodeA.neighbors.add(nodeC);
+  nodeB.neighbors.add(nodeA);
+  nodeB.neighbors.add(nodeC);
+  nodeB.neighbors.add(nodeD);
+  nodeB.neighbors.add(nodeE);
+  nodeC.neighbors.add(nodeA);
+  nodeC.neighbors.add(nodeB);
+  nodeC.neighbors.add(nodeD);
+  nodeC.neighbors.add(nodeE);
+  nodeD.neighbors.add(nodeB);
+  nodeD.neighbors.add(nodeC);
+  nodeD.neighbors.add(nodeE);
+  nodeE.neighbors.add(nodeB);
+  nodeE.neighbors.add(nodeC);
+  nodeE.neighbors.add(nodeD);
+  graph = [nodeA, nodeB, nodeC, nodeD, nodeE];
+}
+colorGraph(graph, colors);
+assertEqual(validateGraphColoring(graph), true, 'envelope graph');
+
+{
+  const nodeA = new GraphNode('A');
+  nodeA.neighbors.add(nodeA);
+  graph = [nodeA];
+}
+assertThrows(() => {
+  colorGraph(graph, colors);
+}, 'loop graph');
+
+function validateGraphColoring(graph) {
+
+  const maxDegree = Math.max(...graph.map(node => node.neighbors.size));
+
+  const colorsUsed = new Set();
+
+  graph.forEach(node => {
+    colorsUsed.add(node.color);
+  });
+
+  if (colorsUsed.has(null)) {
+    return false;
+  }
+
+  if (colorsUsed.size > maxDegree + 1) {
+    return false;
+  }
+
+  let badEdges = 0;
+
+  graph.forEach(node => {
+    node.neighbors.forEach(neighbor => {
+      if (neighbor.color === node.color) {
+        badEdges += 1;
+      }
+    });
+  });
+
+  if (badEdges > 0) {
+    return false;
+  }
+
+  return true;
+}
+
+function assertEqual(a, b, desc) {
+  if (a === b) {
+    console.log(`${desc} ... PASS`);
+  } else {
+    console.log(`${desc} ... FAIL: ${a} != ${b}`);
+  }
+}
+
+function assertThrows(func, desc) {
+  try {
+    func();
+    console.log(`${desc} ... FAIL`);
+  } catch (e) {
+    console.log(`${desc} ... PASS`);
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/highest-product-of-3.py b/users/wpcarro/scratch/deepmind/part_two/highest-product-of-3.py
new file mode 100644
index 0000000000..8ebb5cf29a
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/highest-product-of-3.py
@@ -0,0 +1,81 @@
+import unittest
+import sys
+import trace
+
+
+def highest_product_of_3(xs):
+    if len(xs) < 3:
+        raise Exception("List needs to contain at least three elements.")
+    hp3 = xs[0] * xs[1] * xs[2]
+    hp2 = xs[0] * xs[1]
+    lp2 = xs[0] * xs[1]
+    hn = max(xs[0], xs[1])
+    ln = min(xs[0], xs[1])
+    for x in xs[2:]:
+        hp3 = max(hp3, hp2 * x, lp2 * x)
+        hp2 = max(hp2, hn * x, ln * x)
+        lp2 = min(lp2, hn * x, ln * x)
+        hn = max(hn, x)
+        ln = min(ln, x)
+    return hp3
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_short_list(self):
+        actual = highest_product_of_3([1, 2, 3, 4])
+        expected = 24
+        self.assertEqual(actual, expected)
+
+    def test_longer_list(self):
+        actual = highest_product_of_3([6, 1, 3, 5, 7, 8, 2])
+        expected = 336
+        self.assertEqual(actual, expected)
+
+    def test_list_has_one_negative(self):
+        actual = highest_product_of_3([-5, 4, 8, 2, 3])
+        expected = 96
+        self.assertEqual(actual, expected)
+
+    def test_list_has_two_negatives(self):
+        actual = highest_product_of_3([-10, 1, 3, 2, -10])
+        expected = 300
+        self.assertEqual(actual, expected)
+
+    def test_list_is_all_negatives(self):
+        actual = highest_product_of_3([-5, -1, -3, -2])
+        expected = -6
+        self.assertEqual(actual, expected)
+
+    def test_error_with_empty_list(self):
+        with self.assertRaises(Exception):
+            highest_product_of_3([])
+
+    def test_error_with_one_number(self):
+        with self.assertRaises(Exception):
+            highest_product_of_3([1])
+
+    def test_error_with_two_numbers(self):
+        with self.assertRaises(Exception):
+            highest_product_of_3([1, 1])
+
+    def test_custom(self):
+        actual = highest_product_of_3([9, 5, 2, 1, 7, 3])
+        expected = 9 * 7 * 5
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
+
+
+def main():
+    highest_product_of_3([-5, -1, -3, -2])
+
+
+tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix],
+                     trace=0,
+                     count=1)
+
+tracer.run('main()')
+r = tracer.results()
+r.write_results(show_missing=True, coverdir=".")
diff --git a/users/wpcarro/scratch/deepmind/part_two/inflight-entertainment.ts b/users/wpcarro/scratch/deepmind/part_two/inflight-entertainment.ts
new file mode 100644
index 0000000000..d6da1db3d3
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/inflight-entertainment.ts
@@ -0,0 +1,85 @@
+function canTwoMoviesFillFlightBonus(
+  xs: Array<number>,
+  duration: number
+): boolean {
+  // Returns true if two movies exist that can fill the flight duration +/- 20
+  // minutes.
+  const seeking = {};
+
+  for (let x of xs) {
+    for (let i = 0; i < 40; i += 1) {
+      if (seeking[x + i + 1]) {
+        return true;
+      }
+    }
+    for (let i = 1; i <= 20; i += 1) {
+      seeking[duration - x - i] = true;
+      seeking[duration - x + i] = true;
+    }
+  }
+
+  return false;
+}
+
+function canTwoMoviesFillFlight(xs: Array<number>, duration: number): boolean {
+  const seeking = {};
+
+  for (let x of xs) {
+    if (seeking[x]) {
+      return true;
+    } else {
+      seeking[duration - x] = true;
+    }
+  }
+
+  return false;
+}
+
+// Tests
+let desc = "short flight";
+let actual = canTwoMoviesFillFlight([2, 4], 1);
+let expected = false;
+assertEquals(actual, expected, desc);
+
+desc = "long flight";
+actual = canTwoMoviesFillFlight([2, 4], 6);
+expected = true;
+assertEquals(actual, expected, desc);
+
+desc = "one movie half flight length";
+actual = canTwoMoviesFillFlight([3, 8], 6);
+expected = false;
+assertEquals(actual, expected, desc);
+
+desc = "two movies half flight length";
+actual = canTwoMoviesFillFlight([3, 8, 3], 6);
+expected = true;
+assertEquals(actual, expected, desc);
+
+desc = "lots of possible pairs";
+actual = canTwoMoviesFillFlight([1, 2, 3, 4, 5, 6], 7);
+expected = true;
+assertEquals(actual, expected, desc);
+
+desc = "not using first movie";
+actual = canTwoMoviesFillFlight([4, 3, 2], 5);
+expected = true;
+assertEquals(actual, expected, desc);
+
+desc = "only one movie";
+actual = canTwoMoviesFillFlight([6], 6);
+expected = false;
+assertEquals(actual, expected, desc);
+
+desc = "no movies";
+actual = canTwoMoviesFillFlight([], 2);
+expected = false;
+assertEquals(actual, expected, desc);
+
+function assertEquals(a, b, desc) {
+  if (a === b) {
+    console.log(`${desc} ... PASS`);
+  } else {
+    console.log(`${desc} ... FAIL: ${a} != ${b}`);
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/merge-sorted-arrays.ts b/users/wpcarro/scratch/deepmind/part_two/merge-sorted-arrays.ts
new file mode 100644
index 0000000000..2d478e0e37
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/merge-sorted-arrays.ts
@@ -0,0 +1,63 @@
+function mergeArrays(xs: Array<number>, ys: Array<number>): Array<number> {
+  let i = 0;
+  let j = 0;
+  const result = [];
+
+  for (let q = 0; q < xs.length + ys.length; q += 1) {
+    if (i === xs.length) {
+      while (j < ys.length) {
+        result.push(ys[j]);
+        j += 1;
+      }
+    } else if (j === ys.length) {
+      while (i < xs.length) {
+        result.push(xs[i]);
+        i += 1;
+      }
+    } else if (xs[i] < ys[j]) {
+      result.push(xs[i]);
+      i += 1;
+    } else {
+      result.push(ys[j]);
+      j += 1;
+    }
+  }
+
+  return result;
+}
+
+// Tests
+let desc = "both arrays are empty";
+let actual = mergeArrays([], []);
+let expected = [];
+assertDeepEqual(actual, expected, desc);
+
+desc = "first array is empty";
+actual = mergeArrays([], [1, 2, 3]);
+expected = [1, 2, 3];
+assertDeepEqual(actual, expected, desc);
+
+desc = "second array is empty";
+actual = mergeArrays([5, 6, 7], []);
+expected = [5, 6, 7];
+assertDeepEqual(actual, expected, desc);
+
+desc = "both arrays have some numbers";
+actual = mergeArrays([2, 4, 6], [1, 3, 7]);
+expected = [1, 2, 3, 4, 6, 7];
+assertDeepEqual(actual, expected, desc);
+
+desc = "arrays are different lengths";
+actual = mergeArrays([2, 4, 6, 8], [1, 7]);
+expected = [1, 2, 4, 6, 7, 8];
+assertDeepEqual(actual, expected, desc);
+
+function assertDeepEqual(a: Array<number>, b: Array<number>, desc: string) {
+  const aStr = JSON.stringify(a);
+  const bStr = JSON.stringify(b);
+  if (aStr !== bStr) {
+    console.log(`${desc} ... FAIL: ${aStr} != ${bStr}`);
+  } else {
+    console.log(`${desc} ... PASS`);
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/merging-ranges.py b/users/wpcarro/scratch/deepmind/part_two/merging-ranges.py
new file mode 100644
index 0000000000..23d0813d15
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/merging-ranges.py
@@ -0,0 +1,115 @@
+import unittest
+import timeit
+
+
+# Solution that uses O(n) space to store the result.
+def not_in_place(xs):
+    xs.sort()
+    result = [xs[0]]
+    for ca, cb in xs[1:]:
+        pa, pb = result[-1]
+        if ca <= pb:
+            result[-1] = (pa, max(pb, cb))
+        else:
+            result.append((ca, cb))
+    return result
+
+
+# Solution that uses O(1) space to store the result.
+def in_place(xs):
+    xs.sort()
+    i = 0
+    j = i + 1
+    while j < len(xs):
+        pa, pb = xs[i]
+        ca, cb = xs[j]
+        if ca <= pb:
+            xs[i] = (pa, max(pb, cb))
+            del xs[j]
+        else:
+            i = j
+            j += 1
+    return xs
+
+
+def test_nip():
+    inputs = [
+        [(1, 3), (2, 4)],
+        [(5, 6), (6, 8)],
+        [(1, 8), (2, 5)],
+        [(1, 3), (4, 8)],
+        [(1, 4), (2, 5), (5, 8)],
+        [(5, 8), (1, 4), (6, 8)],
+        [(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)],
+        [(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)],
+    ]
+    for x in inputs:
+        not_in_place(x)
+
+
+def test_ip():
+    inputs = [
+        [(1, 3), (2, 4)],
+        [(5, 6), (6, 8)],
+        [(1, 8), (2, 5)],
+        [(1, 3), (4, 8)],
+        [(1, 4), (2, 5), (5, 8)],
+        [(5, 8), (1, 4), (6, 8)],
+        [(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)],
+        [(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)],
+    ]
+    for x in inputs:
+        in_place(x)
+
+
+merge_ranges = in_place
+
+setup = 'from __main__ import test_nip, test_ip'
+print(timeit.timeit('test_nip()', number=10000, setup=setup))
+print(timeit.timeit('test_ip()', number=10000, setup=setup))
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_meetings_overlap(self):
+        actual = merge_ranges([(1, 3), (2, 4)])
+        expected = [(1, 4)]
+        self.assertEqual(actual, expected)
+
+    def test_meetings_touch(self):
+        actual = merge_ranges([(5, 6), (6, 8)])
+        expected = [(5, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_meeting_contains_other_meeting(self):
+        actual = merge_ranges([(1, 8), (2, 5)])
+        expected = [(1, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_meetings_stay_separate(self):
+        actual = merge_ranges([(1, 3), (4, 8)])
+        expected = [(1, 3), (4, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_multiple_merged_meetings(self):
+        actual = merge_ranges([(1, 4), (2, 5), (5, 8)])
+        expected = [(1, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_meetings_not_sorted(self):
+        actual = merge_ranges([(5, 8), (1, 4), (6, 8)])
+        expected = [(1, 4), (5, 8)]
+        self.assertEqual(actual, expected)
+
+    def test_one_long_meeting_contains_smaller_meetings(self):
+        actual = merge_ranges([(1, 10), (2, 5), (6, 8), (9, 10), (10, 12)])
+        expected = [(1, 12)]
+        self.assertEqual(actual, expected)
+
+    def test_sample_input(self):
+        actual = merge_ranges([(0, 1), (3, 5), (4, 8), (10, 12), (9, 10)])
+        expected = [(0, 1), (3, 8), (9, 12)]
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/mesh-message.py b/users/wpcarro/scratch/deepmind/part_two/mesh-message.py
new file mode 100644
index 0000000000..a265296ab0
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/mesh-message.py
@@ -0,0 +1,183 @@
+import unittest
+from collections import deque
+from heapq import heappush, heappop
+
+
+################################################################################
+# InterviewCake.com
+################################################################################
+# construct_path :: Map String String -> String -> String -> [String]
+def construct_path(paths, beg, end):
+    """
+    Reconstruct the path from `beg` to `end`.
+    """
+    result = []
+    current = end
+
+    print(paths)
+    print(beg, end)
+    print('-----')
+    while current:
+        result.append(current)
+        current = paths[current]
+
+    result.reverse()
+    return result
+
+
+def get_path_ic(graph, beg, end):
+    """
+    InterviewCake uses a dictionary and back-tracking to store and reconstruct
+    the path instead of storing the path as state on each node.
+    This reduces the memory costs. See get_path_bft for an example of this less
+    optimal solution.
+    """
+    if beg not in graph:
+        raise Exception('Origin node absent from graph.')
+
+    if end not in graph:
+        raise Exception('Destination node absent from graph.')
+
+    q = deque()
+    q.append(beg)
+    paths = {beg: None}
+
+    while q:
+        node = q.popleft()
+
+        if node == end:
+            print(graph)
+            return construct_path(paths, beg, end)
+
+        for x in graph[node]:
+            if x not in paths:
+                paths[x] = node
+                q.append(x)
+
+    return None
+
+
+################################################################################
+# Per-node state
+################################################################################
+def get_path_bft(graph, beg, end):
+    """
+    Here we find the shortest path from `beg` to `end` in `graph` by doing a BFT
+    from beg to end and storing the path state alongside each node in the queue.
+    """
+    if beg not in graph:
+        raise Exception('Origin node absent from graph.')
+
+    if end not in graph:
+        raise Exception('Destination node absent from graph.')
+
+    q = deque()
+    seen = set()
+    q.append([beg])
+
+    while q:
+        path = q.popleft()
+        node = path[-1]
+        seen.add(node)
+
+        if node == end:
+            return path
+
+        for x in graph[node]:
+            if x not in seen:
+                q.append(path + [x])
+
+
+################################################################################
+# Dijkstra's Algorithm
+################################################################################
+def get_path(graph, beg, end):
+    """
+    Here we find the shortest path using Dijkstra's algorithm, which is my
+    favorite solution.
+    """
+    if beg not in graph:
+        raise Exception(
+            'The origin node, {}, is not present in the graph'.format(beg))
+
+    if end not in graph:
+        raise Exception(
+            'The origin node, {}, is not present in the graph'.format(end))
+
+    q = []
+    seen = set()
+    heappush(q, (1, [beg]))
+
+    while q:
+        weight, path = heappop(q)
+        node = path[-1]
+        seen.add(node)
+
+        if node == end:
+            return path
+
+        for x in graph[node]:
+            if x not in seen:
+                heappush(q, (weight + 1, path + [x]))
+
+    return None
+
+
+# Tests
+class Test(unittest.TestCase):
+    def setUp(self):
+        self.graph = {
+            'a': ['b', 'c', 'd'],
+            'b': ['a', 'd'],
+            'c': ['a', 'e'],
+            'd': ['b', 'a'],
+            'e': ['c'],
+            'f': ['g'],
+            'g': ['f'],
+        }
+
+    def test_two_hop_path_1(self):
+        actual = get_path(self.graph, 'a', 'e')
+        expected = ['a', 'c', 'e']
+        self.assertEqual(actual, expected)
+
+    def test_two_hop_path_2(self):
+        actual = get_path(self.graph, 'd', 'c')
+        expected = ['d', 'a', 'c']
+        self.assertEqual(actual, expected)
+
+    def test_one_hop_path_1(self):
+        actual = get_path(self.graph, 'a', 'c')
+        expected = ['a', 'c']
+        self.assertEqual(actual, expected)
+
+    def test_one_hop_path_2(self):
+        actual = get_path(self.graph, 'f', 'g')
+        expected = ['f', 'g']
+        self.assertEqual(actual, expected)
+
+    def test_one_hop_path_3(self):
+        actual = get_path(self.graph, 'g', 'f')
+        expected = ['g', 'f']
+        self.assertEqual(actual, expected)
+
+    def test_zero_hop_path(self):
+        actual = get_path(self.graph, 'a', 'a')
+        expected = ['a']
+        self.assertEqual(actual, expected)
+
+    def test_no_path(self):
+        actual = get_path(self.graph, 'a', 'f')
+        expected = None
+        self.assertEqual(actual, expected)
+
+    def test_start_node_not_present(self):
+        with self.assertRaises(Exception):
+            get_path(self.graph, 'h', 'a')
+
+    def test_end_node_not_present(self):
+        with self.assertRaises(Exception):
+            get_path(self.graph, 'a', 'h')
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/misc/matrix-traversals.py b/users/wpcarro/scratch/deepmind/part_two/misc/matrix-traversals.py
new file mode 100644
index 0000000000..52354f990e
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/misc/matrix-traversals.py
@@ -0,0 +1,104 @@
+# Herein I'm practicing two-dimensional matrix traversals in all directions of
+# which I can conceive:
+# 0. T -> B; L -> R
+# 1. T -> B; R -> L
+# 2. B -> T; L -> R
+# 3. B -> T; R -> L
+#
+# Commentary:
+# When I think of matrices, I'm reminded of cartesian planes. I think of the
+# cells as (X,Y) coordinates. This has been a pitfall for me because matrices
+# are usually encoded in the opposite way. That is, to access a cell at the
+# coordinates (X,Y) given a matrix M, you index M like this: M[Y][X]. To attempt
+# to avoid this confusion, instead of saying X and Y, I will prefer saying
+# "column" and "row".
+#
+# When traversing a matrix, you typically traverse vertically and then
+# horizontally; in other words, the rows come first followed by the columns. As
+# such, I'd like to refer to traversal orders as "top-to-bottom, left-to-right"
+# rather than "left-to-right, top-to-bottom".
+#
+# These practices are all in an attempt to rewire my thinking.
+
+# This is a list of matrices where the index of a matrix corresponds to the
+# order in which it should be traversed to produce the sequence:
+# [1,2,3,4,5,6,7,8,9].
+boards = [[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[3, 2, 1], [6, 5, 4], [9, 8, 7]],
+          [[7, 8, 9], [4, 5, 6], [1, 2, 3]], [[9, 8, 7], [6, 5, 4], [3, 2, 1]]]
+
+# T -> B; L -> R
+board = boards[0]
+result = []
+for row in board:
+    for col in row:
+        result.append(col)
+print(result)
+
+# T -> B; R -> L
+board = boards[1]
+result = []
+for row in board:
+    for col in reversed(row):
+        result.append(col)
+print(result)
+
+# B -> T; L -> R
+board = boards[2]
+result = []
+for row in reversed(board):
+    for col in row:
+        result.append(col)
+print(result)
+
+# B -> T; R -> L
+board = boards[3]
+result = []
+for row in reversed(board):
+    for col in reversed(row):
+        result.append(col)
+print(result)
+
+################################################################################
+# Neighbors
+################################################################################
+
+import random
+
+
+# Generate a matrix of size `rows` x `cols` where each cell contains an item
+# randomly selected from `xs`.
+def generate_board(rows, cols, xs):
+    result = []
+    for _ in range(rows):
+        row = []
+        for _ in range(cols):
+            row.append(random.choice(xs))
+        result.append(row)
+    return result
+
+
+# Print the `board` to the screen.
+def print_board(board):
+    print('\n'.join([' '.join(row) for row in board]))
+
+
+board = generate_board(4, 5, ['R', 'G', 'B'])
+print_board(board)
+
+
+# Return all of the cells horizontally and vertically accessible from a starting
+# cell at `row`, `col` in `board`.
+def neighbors(row, col, board):
+    result = {'top': [], 'bottom': [], 'left': [], 'right': []}
+    for i in range(row - 1, -1, -1):
+        result['top'].append(board[i][col])
+    for i in range(row + 1, len(board)):
+        result['bottom'].append(board[i][col])
+    for i in range(col - 1, -1, -1):
+        result['left'].append(board[row][i])
+    for i in range(col + 1, len(board[0])):
+        result['right'].append(board[row][i])
+    return result
+
+
+print(neighbors(1, 2, board))
diff --git a/users/wpcarro/scratch/deepmind/part_two/nth-fibonacci.py b/users/wpcarro/scratch/deepmind/part_two/nth-fibonacci.py
new file mode 100644
index 0000000000..14e176b62a
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/nth-fibonacci.py
@@ -0,0 +1,72 @@
+import unittest
+
+
+# Compute the fibonacci using a bottom-up algorithm.
+def fib(n):
+    if n < 0:
+        raise Error('Cannot call fibonacci with negative values')
+    cache = [0, 1]
+    for i in range(n):
+        cache[0], cache[1] = cache[1], cache[0] + cache[1]
+    return cache[0]
+
+
+# Compute the fibonacci using memoization.
+def fib_memoized(n):
+    cache = {
+        0: 0,
+        1: 1,
+    }
+
+    def do_fib(n):
+        if n < 0:
+            raise Error('The fib function does not support negative inputs')
+
+        if n in cache:
+            return cache[n]
+
+        cache[n - 1] = do_fib(n - 1)
+        cache[n - 2] = do_fib(n - 2)
+        return cache[n - 1] + cache[n - 2]
+
+    return do_fib(n)
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_zeroth_fibonacci(self):
+        actual = fib(0)
+        expected = 0
+        self.assertEqual(actual, expected)
+
+    def test_first_fibonacci(self):
+        actual = fib(1)
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_second_fibonacci(self):
+        actual = fib(2)
+        expected = 1
+        self.assertEqual(actual, expected)
+
+    def test_third_fibonacci(self):
+        actual = fib(3)
+        expected = 2
+        self.assertEqual(actual, expected)
+
+    def test_fifth_fibonacci(self):
+        actual = fib(5)
+        expected = 5
+        self.assertEqual(actual, expected)
+
+    def test_tenth_fibonacci(self):
+        actual = fib(10)
+        expected = 55
+        self.assertEqual(actual, expected)
+
+    def test_negative_fibonacci(self):
+        with self.assertRaises(Exception):
+            fib(-1)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/package-lock.json b/users/wpcarro/scratch/deepmind/part_two/package-lock.json
new file mode 100644
index 0000000000..340aad9f5c
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/package-lock.json
@@ -0,0 +1,79 @@
+{
+  "name": "deepmind-part-two",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true
+    },
+    "buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "dev": true
+    },
+    "diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true
+    },
+    "make-error": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
+      "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==",
+      "dev": true
+    },
+    "prettier": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.2.tgz",
+      "integrity": "sha512-5xJQIPT8BraI7ZnaDwSbu5zLrB6vvi8hVV58yHQ+QK64qrY40dULy0HSRlQ2/2IdzeBpjhDkqdcFBnFeDEMVdg==",
+      "dev": true
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true
+    },
+    "source-map-support": {
+      "version": "0.5.16",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
+      "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "ts-node": {
+      "version": "8.6.2",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz",
+      "integrity": "sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==",
+      "dev": true,
+      "requires": {
+        "arg": "^4.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "source-map-support": "^0.5.6",
+        "yn": "3.1.1"
+      }
+    },
+    "typescript": {
+      "version": "3.7.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
+      "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
+      "dev": true
+    },
+    "yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true
+    }
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/package.json b/users/wpcarro/scratch/deepmind/part_two/package.json
new file mode 100644
index 0000000000..1f10668ec8
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/package.json
@@ -0,0 +1,16 @@
+{
+  "name": "deepmind-part-two",
+  "version": "1.0.0",
+  "description": "Practicing coding interview questions",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "William Carroll",
+  "license": "MIT",
+  "devDependencies": {
+    "prettier": "^2.0.2",
+    "ts-node": "^8.6.2",
+    "typescript": "^3.7.5"
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/permutation-palindrome.py b/users/wpcarro/scratch/deepmind/part_two/permutation-palindrome.py
new file mode 100644
index 0000000000..730b4bfdc8
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/permutation-palindrome.py
@@ -0,0 +1,37 @@
+import unittest
+from collections import Counter
+
+
+def has_palindrome_permutation(xs):
+    vs = Counter(xs).values()
+    return len([v for v in vs if v % 2 == 1]) in {0, 1}
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_permutation_with_odd_number_of_chars(self):
+        result = has_palindrome_permutation('aabcbcd')
+        self.assertTrue(result)
+
+    def test_permutation_with_even_number_of_chars(self):
+        result = has_palindrome_permutation('aabccbdd')
+        self.assertTrue(result)
+
+    def test_no_permutation_with_odd_number_of_chars(self):
+        result = has_palindrome_permutation('aabcd')
+        self.assertFalse(result)
+
+    def test_no_permutation_with_even_number_of_chars(self):
+        result = has_palindrome_permutation('aabbcd')
+        self.assertFalse(result)
+
+    def test_empty_string(self):
+        result = has_palindrome_permutation('')
+        self.assertTrue(result)
+
+    def test_one_character_string(self):
+        result = has_palindrome_permutation('a')
+        self.assertTrue(result)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/product-of-other-numbers.py b/users/wpcarro/scratch/deepmind/part_two/product-of-other-numbers.py
new file mode 100644
index 0000000000..6f7858ff4e
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/product-of-other-numbers.py
@@ -0,0 +1,68 @@
+import unittest
+
+
+# get_products_of_all_ints_except_at_index :: [Int] -> [Int]
+def get_products_of_all_ints_except_at_index(xs):
+    n = len(xs)
+    if n < 2:
+        raise Exception("Cannot computer without 2 or elements")
+    # lhs
+    befores = [None] * n
+    befores[0] = 1
+    for i in range(1, n):
+        befores[i] = befores[i - 1] * xs[i - 1]
+
+    # rhs
+    afters = [None] * n
+    afters[-1] = 1
+    for i in range(n - 2, -1, -1):
+        afters[i] = afters[i + 1] * xs[i + 1]
+
+    result = [None] * n
+    for i in range(n):
+        result[i] = befores[i] * afters[i]
+    return result
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_small_list(self):
+        actual = get_products_of_all_ints_except_at_index([1, 2, 3])
+        expected = [6, 3, 2]
+        self.assertEqual(actual, expected)
+
+    def test_longer_list(self):
+        actual = get_products_of_all_ints_except_at_index([8, 2, 4, 3, 1, 5])
+        expected = [120, 480, 240, 320, 960, 192]
+        self.assertEqual(actual, expected)
+
+    def test_list_has_one_zero(self):
+        actual = get_products_of_all_ints_except_at_index([6, 2, 0, 3])
+        expected = [0, 0, 36, 0]
+        self.assertEqual(actual, expected)
+
+    def test_list_has_two_zeros(self):
+        actual = get_products_of_all_ints_except_at_index([4, 0, 9, 1, 0])
+        expected = [0, 0, 0, 0, 0]
+        self.assertEqual(actual, expected)
+
+    def test_one_negative_number(self):
+        actual = get_products_of_all_ints_except_at_index([-3, 8, 4])
+        expected = [32, -12, -24]
+        self.assertEqual(actual, expected)
+
+    def test_all_negative_numbers(self):
+        actual = get_products_of_all_ints_except_at_index([-7, -1, -4, -2])
+        expected = [-8, -56, -14, -28]
+        self.assertEqual(actual, expected)
+
+    def test_error_with_empty_list(self):
+        with self.assertRaises(Exception):
+            get_products_of_all_ints_except_at_index([])
+
+    def test_error_with_one_number(self):
+        with self.assertRaises(Exception):
+            get_products_of_all_ints_except_at_index([1])
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/recursive-string-permutations.ts b/users/wpcarro/scratch/deepmind/part_two/recursive-string-permutations.ts
new file mode 100644
index 0000000000..cb930d9ad6
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/recursive-string-permutations.ts
@@ -0,0 +1,85 @@
+// Returns a new string comprised of every characters in `xs` except for the
+// character at `i`.
+function everyOtherChar(xs: string, i: number): string[] {
+  const result = [];
+
+  for (let j = 0; j < xs.length; j += 1) {
+    if (i !== j) {
+      result.push(xs[j]);
+    }
+  }
+
+  return [xs[i], result.join('')];
+}
+
+function getPermutations(xs: string): Set<string> {
+  if (xs === '') {
+    return new Set(['']);
+  }
+
+  const result: Set<string> = new Set;
+
+  for (let i = 0; i < xs.length; i += 1) {
+    const [char, rest] = everyOtherChar(xs, i);
+    const perms = getPermutations(rest);
+
+    for (const perm of perms) {
+      result.add(char + perm);
+    }
+  }
+
+  return result;
+}
+
+// Tests
+let desc = 'empty string';
+let input = '';
+let actual = getPermutations(input);
+let expected = new Set(['']);
+assert(isSetsEqual(actual, expected), desc);
+
+desc = 'one character string';
+input = 'a';
+actual = getPermutations(input);
+expected = new Set(['a']);
+assert(isSetsEqual(actual, expected), desc);
+
+desc = 'two character string';
+input = 'ab';
+actual = getPermutations(input);
+expected = new Set(['ab', 'ba']);
+assert(isSetsEqual(actual, expected), desc);
+
+desc = 'three character string';
+input = 'abc';
+actual = getPermutations(input);
+expected = new Set(['abc', 'acb', 'bac', 'bca', 'cab', 'cba']);
+assert(isSetsEqual(actual, expected), desc);
+
+desc = 'four character string';
+input = 'abca';
+actual = getPermutations(input);
+expected = new Set([
+  'abca', 'abac', 'acba', 'acab', 'aabc', 'aacb', 'baca', 'baac', 'bcaa',
+  'bcaa', 'baac', 'baca', 'caba', 'caab', 'cbaa', 'cbaa', 'caab', 'caba',
+  'aabc', 'aacb', 'abac', 'abca', 'acab', 'acba'
+]);
+assert(isSetsEqual(actual, expected), desc);
+
+function isSetsEqual(as, bs) {
+  if (as.size !== bs.size) {
+    return false;
+  }
+  for (let a of as) {
+    if (!bs.has(a)) return false;
+  }
+  return true;
+}
+
+function assert(condition, desc) {
+  if (condition) {
+    console.log(`${desc} ... PASS`);
+  } else {
+    console.log(`${desc} ... FAIL`);
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/reverse-string-in-place.ts b/users/wpcarro/scratch/deepmind/part_two/reverse-string-in-place.ts
new file mode 100644
index 0000000000..d714dfef99
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/reverse-string-in-place.ts
@@ -0,0 +1,13 @@
+// Reverse array of characters, `xs`, mutatively.
+function reverse(xs: Array<string>) {
+  let i: number = 0;
+  let j: number = xs.length - 1;
+
+  while (i < j) {
+    let tmp = xs[i];
+    xs[i] = xs[j]
+    xs[j] = tmp
+    i += 1
+    j -= 1
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/reverse-words.py b/users/wpcarro/scratch/deepmind/part_two/reverse-words.py
new file mode 100644
index 0000000000..033d11244c
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/reverse-words.py
@@ -0,0 +1,74 @@
+import unittest
+
+
+def reverse(xs, i, j):
+    """Reverse array of characters, xs, in-place."""
+    while i < j:
+        xs[i], xs[j] = xs[j], xs[i]
+        i += 1
+        j -= 1
+
+
+def reverse_words(xs):
+    punctuation = None
+    if len(xs) > 0 and xs[-1] in ".?!":
+        punctuation = xs.pop()
+    reverse(xs, 0, len(xs) - 1)
+    i = 0
+    j = i
+    while j < len(xs):
+        while j < len(xs) and xs[j] != ' ':
+            j += 1
+        reverse(xs, i, j - 1)
+        j += 1
+        i = j
+    if punctuation:
+        xs.append(punctuation)
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_one_word(self):
+        message = list('vault')
+        reverse_words(message)
+        expected = list('vault')
+        self.assertEqual(message, expected)
+
+    def test_two_words(self):
+        message = list('thief cake')
+        reverse_words(message)
+        expected = list('cake thief')
+        self.assertEqual(message, expected)
+
+    def test_three_words(self):
+        message = list('one another get')
+        reverse_words(message)
+        expected = list('get another one')
+        self.assertEqual(message, expected)
+
+    def test_multiple_words_same_length(self):
+        message = list('rat the ate cat the')
+        reverse_words(message)
+        expected = list('the cat ate the rat')
+        self.assertEqual(message, expected)
+
+    def test_multiple_words_different_lengths(self):
+        message = list('yummy is cake bundt chocolate')
+        reverse_words(message)
+        expected = list('chocolate bundt cake is yummy')
+        self.assertEqual(message, expected)
+
+    def test_empty_string(self):
+        message = list('')
+        reverse_words(message)
+        expected = list('')
+        self.assertEqual(message, expected)
+
+    def test_bonus_support_punctuation(self):
+        message = list('yummy is cake bundt chocolate this!')
+        reverse_words(message)
+        expected = list('this chocolate bundt cake is yummy!')
+        self.assertEqual(message, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/second-largest-item-in-bst.ts b/users/wpcarro/scratch/deepmind/part_two/second-largest-item-in-bst.ts
new file mode 100644
index 0000000000..4c5e57607d
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/second-largest-item-in-bst.ts
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Setup
+ ******************************************************************************/
+
+interface BinaryTreeNode {
+  value: number;
+  left: BinaryTreeNode;
+  right: BinaryTreeNode;
+}
+
+class BinaryTreeNode {
+  constructor(value: number) {
+    this.value = value;
+    this.left  = null;
+    this.right = null;
+  }
+
+  insertLeft(value: number): BinaryTreeNode {
+    this.left = new BinaryTreeNode(value);
+    return this.left;
+  }
+
+  insertRight(value: number): BinaryTreeNode {
+    this.right = new BinaryTreeNode(value);
+    return this.right;
+  }
+}
+
+/*******************************************************************************
+ * First solution
+ ******************************************************************************/
+
+/**
+ * I first solved this problem using O(n) space and O(n*log(n))
+ * time. InterviewCake informs me that we can improve both the time and the
+ * space performance.
+ */
+function findSecondLargest_first(node: BinaryTreeNode): number {
+  const stack: Array<BinaryTreeNode> = [];
+  const xs: Array<number> = [];
+  stack.push(node);
+
+  while (stack.length > 0) {
+    const node = stack.pop()
+
+    xs.push(node.value);
+
+    if (node.left) {
+      stack.push(node.left);
+    }
+    if (node.right) {
+      stack.push(node.right);
+    }
+  }
+
+  xs.sort();
+
+  if (xs.length < 2) {
+    throw new Error('Cannot find the second largest element in a BST with fewer than two elements.');
+  } else {
+    return xs[xs.length - 2];
+  }
+}
+
+/*******************************************************************************
+ * Second solution
+ ******************************************************************************/
+
+/**
+ * My second solution accumulates a list of the values in the tree using an
+ * in-order traversal. This reduces the runtime costs from O(n*log(n)) from the
+ * previous solution to O(n). The memory cost is still O(n), which InterviewCake
+ * informs me can be reduced to O(1).
+ */
+function findSecondLargest_second(node: BinaryTreeNode): number {
+  const xs: Array<number> = accumulateInorder(node);
+
+  if (xs.length < 2) {
+    throw new Error('Cannot find the second largest element in a BST with fewer than two elements.');
+  } else {
+    return xs[xs.length - 2];
+  }
+}
+
+/**
+ * Returns an array containing the values of the tree, `node`, sorted in-order
+ * (i.e. from smallest-to-largest).
+ */
+function accumulateInorder(node: BinaryTreeNode): Array<number> {
+  let result = [];
+
+  if (node.left) {
+    result = result.concat(accumulateInorder(node.left));
+  }
+  result.push(node.value)
+  if (node.right) {
+    result = result.concat(accumulateInorder(node.right));
+  }
+
+  return result;
+}
+
+/*******************************************************************************
+ * Third solution
+ ******************************************************************************/
+
+/**
+ * Returns the largest number in a BST.
+ */
+function findLargest(node: BinaryTreeNode): number {
+  let curr: BinaryTreeNode = node;
+
+  while (curr.right) {
+    curr = curr.right;
+  }
+
+  return curr.value;
+}
+
+/**
+ * Returns the second largest number in a BST
+ */
+function findSecondLargest(node: BinaryTreeNode): number {
+  let curr = node;
+  let parent = null;
+
+  while (curr.right) {
+    parent = curr;
+    curr = curr.right
+  }
+
+  if (curr.left) {
+    return findLargest(curr.left);
+  }
+  else {
+    return parent.value;
+  }
+}
+
+
+// Tests
+let desc = 'full tree';
+let treeRoot = new BinaryTreeNode(50);
+let leftNode = treeRoot.insertLeft(30);
+leftNode.insertLeft(10);
+leftNode.insertRight(40);
+let rightNode = treeRoot.insertRight(70);
+rightNode.insertLeft(60);
+rightNode.insertRight(80);
+assertEquals(findSecondLargest(treeRoot), 70, desc);
+
+desc = 'largest has a left child';
+treeRoot = new BinaryTreeNode(50);
+leftNode = treeRoot.insertLeft(30);
+leftNode.insertLeft(10);
+leftNode.insertRight(40);
+rightNode = treeRoot.insertRight(70);
+rightNode.insertLeft(60);
+assertEquals(findSecondLargest(treeRoot), 60, desc);
+
+desc = 'largest has a left subtree';
+treeRoot = new BinaryTreeNode(50);
+leftNode = treeRoot.insertLeft(30);
+leftNode.insertLeft(10);
+leftNode.insertRight(40);
+rightNode = treeRoot.insertRight(70);
+leftNode = rightNode.insertLeft(60);
+leftNode.insertRight(65);
+leftNode = leftNode.insertLeft(55);
+leftNode.insertRight(58);
+assertEquals(findSecondLargest(treeRoot), 65, desc);
+
+desc = 'second largest is root node';
+treeRoot = new BinaryTreeNode(50);
+leftNode = treeRoot.insertLeft(30);
+leftNode.insertLeft(10);
+leftNode.insertRight(40);
+rightNode = treeRoot.insertRight(70);
+assertEquals(findSecondLargest(treeRoot), 50, desc);
+
+desc = 'descending linked list';
+treeRoot = new BinaryTreeNode(50);
+leftNode = treeRoot.insertLeft(40);
+leftNode = leftNode.insertLeft(30);
+leftNode = leftNode.insertLeft(20);
+leftNode = leftNode.insertLeft(10);
+assertEquals(findSecondLargest(treeRoot), 40, desc);
+
+desc = 'ascending linked list';
+treeRoot = new BinaryTreeNode(50);
+rightNode = treeRoot.insertRight(60);
+rightNode = rightNode.insertRight(70);
+rightNode = rightNode.insertRight(80);
+assertEquals(findSecondLargest(treeRoot), 70, desc);
+
+desc = 'one node tree';
+treeRoot = new BinaryTreeNode(50);
+assertThrowsError(() => findSecondLargest(treeRoot), desc);
+
+desc = 'when tree is empty';
+treeRoot = null;
+assertThrowsError(() => findSecondLargest(treeRoot), desc);
+
+function assertEquals(a, b, desc) {
+  if (a === b) {
+    console.log(`${desc} ... PASS`);
+  } else {
+    console.log(`${desc} ... FAIL: ${a} != ${b}`)
+  }
+}
+
+function assertThrowsError(func, desc) {
+  try {
+    func();
+    console.log(`${desc} ... FAIL`);
+  } catch (e) {
+    console.log(`${desc} ... PASS`);
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/shell.nix b/users/wpcarro/scratch/deepmind/part_two/shell.nix
new file mode 100644
index 0000000000..f1b02c4d2e
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/shell.nix
@@ -0,0 +1,10 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    nodejs
+    python3
+    go
+    goimports
+  ];
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/shuffle.py b/users/wpcarro/scratch/deepmind/part_two/shuffle.py
new file mode 100644
index 0000000000..fdc5a8bd80
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/shuffle.py
@@ -0,0 +1,20 @@
+import random
+
+
+def get_random(floor, ceiling):
+    return random.randrange(floor, ceiling + 1)
+
+
+def shuffle(xs):
+    n = len(xs)
+    for i in range(n - 1):
+        j = get_random(i + 1, n - 1)
+        xs[i], xs[j] = xs[j], xs[i]
+
+
+sample_list = [1, 2, 3, 4, 5]
+print('Sample list:', sample_list)
+
+print('Shuffling sample list...')
+shuffle(sample_list)
+print(sample_list)
diff --git a/users/wpcarro/scratch/deepmind/part_two/stock-price.py b/users/wpcarro/scratch/deepmind/part_two/stock-price.py
new file mode 100644
index 0000000000..56a3c20ea0
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/stock-price.py
@@ -0,0 +1,54 @@
+import unittest
+
+
+def get_max_profit(xs):
+    if len(xs) < 2:
+        raise Exception('Can only trade with two or more ticker values.')
+    lowest_buy = xs[0]
+    max_profit = None
+    for x in xs[1:]:
+        if not max_profit:
+            max_profit = x - lowest_buy
+        else:
+            max_profit = max(max_profit, x - lowest_buy)
+        lowest_buy = min(lowest_buy, x)
+    return max_profit
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_price_goes_up_then_down(self):
+        actual = get_max_profit([1, 5, 3, 2])
+        expected = 4
+        self.assertEqual(actual, expected)
+
+    def test_price_goes_down_then_up(self):
+        actual = get_max_profit([7, 2, 8, 9])
+        expected = 7
+        self.assertEqual(actual, expected)
+
+    def test_price_goes_up_all_day(self):
+        actual = get_max_profit([1, 6, 7, 9])
+        expected = 8
+        self.assertEqual(actual, expected)
+
+    def test_price_goes_down_all_day(self):
+        actual = get_max_profit([9, 7, 4, 1])
+        expected = -2
+        self.assertEqual(actual, expected)
+
+    def test_price_stays_the_same_all_day(self):
+        actual = get_max_profit([1, 1, 1, 1])
+        expected = 0
+        self.assertEqual(actual, expected)
+
+    def test_error_with_empty_prices(self):
+        with self.assertRaises(Exception):
+            get_max_profit([])
+
+    def test_error_with_one_price(self):
+        with self.assertRaises(Exception):
+            get_max_profit([1])
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/todo.org b/users/wpcarro/scratch/deepmind/part_two/todo.org
new file mode 100644
index 0000000000..9c76da7541
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/todo.org
@@ -0,0 +1,77 @@
+* Array and string manipulation
+** DONE Merging Meeting Times
+** DONE Reverse String in Place
+** DONE Reverse Words
+** DONE Merge Sorted Arrays
+** DONE Cafe Order Checker
+* Hashing and hash tables
+** DONE Inflight Entertainment
+** DONE Permutation Palindrome
+** DONE Word Cloud Data
+** DONE Top Scores
+* Greedy Algorithms
+** DONE Apple Stocks
+** DONE Highest Product of 3
+** DONE Product of All Other Numbers
+** DONE Cafe Order Checker
+** DONE In-Place Shuffle
+* Sorting, searching, and logarithms
+** DONE Find Rotation Point
+** TODO Find Repeat, Space Edition
+** DONE Top Scores
+** DONE Merging Meeting Times
+* Trees and graphs
+** DONE Balanced Binary Tree
+** DONE Binary Search Tree Checker
+** DONE 2nd Largest Item in a Binary Search Tree
+** DONE Graph Coloring
+** DONE MeshMessage
+** DONE Find Repeat, Space Edition BEAST MODE
+* Dynamic programming and recursion
+** DONE Recursive String Permutations
+** DONE Compute nth Fibonacci Number
+** TODO Making Change
+** TODO The Cake Thief
+** DONE Balanced Binary Tree
+** DONE Binary Search Tree Checker
+** DONE 2nd Largest Item in a Binary Search Tree
+* Queues and stacks
+** TODO Largest Stack
+** TODO Implement A Queue With Two Stacks
+** TODO Parenthesis Matching
+** TODO Bracket Validator
+* Linked lists
+** DONE Delete Node
+** TODO Does This Linked List Have A Cycle?
+** TODO Reverse A Linked List
+** TODO Kth to Last Node in a Singly-Linked List
+** DONE Find Repeat, Space Edition BEAST MODE
+* System design
+** TODO URL Shortener
+** TODO MillionGazillion
+** TODO Find Duplicate Files
+* General programming
+** TODO Rectangular Love
+** TODO Temperature Tracker
+* Bit manipulation
+** TODO Binary Numbers
+** TODO The Stolen Breakfast Drone
+* Combinatorics, probability, and other math
+** TODO Which Appears Twice
+** TODO Find in Ordered Set
+** DONE In-Place Shuffle
+** TODO Simulate 5-sided die
+** TODO Simulate 7-sided die
+** TODO Two Egg Problem
+* JavaScript
+** TODO JavaScript Scope
+** TODO What's Wrong with This JavaScript?
+* Coding interview tips
+** TODO How The Coding Interview Works
+** TODO General Coding Interview Advice
+** TODO Impostor Syndrome
+** TODO Why You Hit Dead Ends
+** TODO Tips for Getting Unstuck
+** TODO The 24 Hours Before Your Interview
+** TODO Beating Behavioral Questions
+** TODO Managing Your Interview Timeline
diff --git a/users/wpcarro/scratch/deepmind/part_two/top-scores.py b/users/wpcarro/scratch/deepmind/part_two/top-scores.py
new file mode 100644
index 0000000000..0ac349c1f8
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/top-scores.py
@@ -0,0 +1,47 @@
+import unittest
+
+
+def sort_scores(xs, highest_possible_score):
+    result = []
+    buckets = [0] * highest_possible_score
+
+    for x in xs:
+        buckets[x - 1] += 1
+
+    for i in range(highest_possible_score - 1, -1, -1):
+        if buckets[i] > 0:
+            for _ in range(buckets[i]):
+                result.append(i + 1)
+
+    return result
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_no_scores(self):
+        actual = sort_scores([], 100)
+        expected = []
+        self.assertEqual(actual, expected)
+
+    def test_one_score(self):
+        actual = sort_scores([55], 100)
+        expected = [55]
+        self.assertEqual(actual, expected)
+
+    def test_two_scores(self):
+        actual = sort_scores([30, 60], 100)
+        expected = [60, 30]
+        self.assertEqual(actual, expected)
+
+    def test_many_scores(self):
+        actual = sort_scores([37, 89, 41, 65, 91, 53], 100)
+        expected = [91, 89, 65, 53, 41, 37]
+        self.assertEqual(actual, expected)
+
+    def test_repeated_scores(self):
+        actual = sort_scores([20, 10, 30, 30, 10, 20], 100)
+        expected = [30, 30, 20, 20, 10, 10]
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/deepmind/part_two/top-scores.ts b/users/wpcarro/scratch/deepmind/part_two/top-scores.ts
new file mode 100644
index 0000000000..79c10c8832
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/top-scores.ts
@@ -0,0 +1,57 @@
+function sortScores(xs: Array<number>, highest: number): Array<number> {
+  const counts: Array<number> = [];
+  const result: Array<number> = [];
+
+  // Initialize counts
+  for (let i = 0; i <= highest; i += 1) {
+    counts.push(0);
+  }
+
+  for (let i = 0; i < xs.length; i += 1) {
+    counts[xs[i]] += 1;
+  }
+
+  for (let i = highest; i >= 0; i -= 1) {
+    let count: number = counts[i];
+
+    for (let j = 0; j < count; j += 1) {
+      result.push(i);
+    }
+  }
+
+  return result;
+}
+
+// Tests
+let desc = "no scores";
+let actual = sortScores([], 100);
+let expected = [];
+assertEqual(JSON.stringify(actual), JSON.stringify(expected), desc);
+
+desc = "one score";
+actual = sortScores([55], 100);
+expected = [55];
+assertEqual(JSON.stringify(actual), JSON.stringify(expected), desc);
+
+desc = "two scores";
+actual = sortScores([30, 60], 100);
+expected = [60, 30];
+assertEqual(JSON.stringify(actual), JSON.stringify(expected), desc);
+
+desc = "many scores";
+actual = sortScores([37, 89, 41, 65, 91, 53], 100);
+expected = [91, 89, 65, 53, 41, 37];
+assertEqual(JSON.stringify(actual), JSON.stringify(expected), desc);
+
+desc = "repeated scores";
+actual = sortScores([20, 10, 30, 30, 10, 20], 100);
+expected = [30, 30, 20, 20, 10, 10];
+assertEqual(JSON.stringify(actual), JSON.stringify(expected), desc);
+
+function assertEqual(a, b, desc) {
+  if (a === b) {
+    console.log(`${desc} ... PASS`);
+  } else {
+    console.log(`${desc} ... FAIL: ${a} != ${b}`);
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/tsconfig.json b/users/wpcarro/scratch/deepmind/part_two/tsconfig.json
new file mode 100644
index 0000000000..9b6918ca37
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/tsconfig.json
@@ -0,0 +1,7 @@
+{
+  "compilerOptions": {
+    "downlevelIteration": true,
+    "target": "es5",
+    "lib": ["es6", "dom"]
+  }
+}
diff --git a/users/wpcarro/scratch/deepmind/part_two/word-cloud.py b/users/wpcarro/scratch/deepmind/part_two/word-cloud.py
new file mode 100644
index 0000000000..36ace8405f
--- /dev/null
+++ b/users/wpcarro/scratch/deepmind/part_two/word-cloud.py
@@ -0,0 +1,79 @@
+import unittest
+import re
+from collections import Counter
+
+
+class WordCloudData(object):
+    def __init__(self, x):
+        x = x.replace('...', ' ').replace(' - ', ' ')
+        x = ''.join(c for c in x if c not in ',.!?;:')
+        self.words_to_counts = dict(
+            Counter(x.lower() for x in re.split(r'\s+', x)))
+
+
+# Tests
+class Test(unittest.TestCase):
+    def test_simple_sentence(self):
+        input = 'I like cake'
+
+        word_cloud = WordCloudData(input)
+        actual = word_cloud.words_to_counts
+
+        expected = {'i': 1, 'like': 1, 'cake': 1}
+        self.assertEqual(actual, expected)
+
+    def test_longer_sentence(self):
+        input = 'Chocolate cake for dinner and pound cake for dessert'
+
+        word_cloud = WordCloudData(input)
+        actual = word_cloud.words_to_counts
+
+        expected = {
+            'and': 1,
+            'pound': 1,
+            'for': 2,
+            'dessert': 1,
+            'chocolate': 1,
+            'dinner': 1,
+            'cake': 2,
+        }
+        self.assertEqual(actual, expected)
+
+    def test_punctuation(self):
+        input = 'Strawberry short cake? Yum!'
+
+        word_cloud = WordCloudData(input)
+        actual = word_cloud.words_to_counts
+
+        expected = {'cake': 1, 'strawberry': 1, 'short': 1, 'yum': 1}
+        self.assertEqual(actual, expected)
+
+    def test_hyphenated_words(self):
+        input = 'Dessert - mille-feuille cake'
+
+        word_cloud = WordCloudData(input)
+        actual = word_cloud.words_to_counts
+
+        expected = {'cake': 1, 'dessert': 1, 'mille-feuille': 1}
+        self.assertEqual(actual, expected)
+
+    def test_ellipses_between_words(self):
+        input = 'Mmm...mmm...decisions...decisions'
+
+        word_cloud = WordCloudData(input)
+        actual = word_cloud.words_to_counts
+
+        expected = {'mmm': 2, 'decisions': 2}
+        self.assertEqual(actual, expected)
+
+    def test_apostrophes(self):
+        input = "Allie's Bakery: Sasha's Cakes"
+
+        word_cloud = WordCloudData(input)
+        actual = word_cloud.words_to_counts
+
+        expected = {"bakery": 1, "cakes": 1, "allie's": 1, "sasha's": 1}
+        self.assertEqual(actual, expected)
+
+
+unittest.main(verbosity=2)
diff --git a/users/wpcarro/scratch/facebook/anglocize-int.py b/users/wpcarro/scratch/facebook/anglocize-int.py
new file mode 100644
index 0000000000..a828230d08
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/anglocize-int.py
@@ -0,0 +1,71 @@
+THOUSAND = int(1e3)
+MILLION = int(1e6)
+BILLION = int(1e9)
+TRILLION = int(1e12)
+
+facts = {
+    1: "One",
+    2: "Two",
+    3: "Three",
+    4: "Four",
+    5: "Five",
+    6: "Six",
+    7: "Seven",
+    8: "Eight",
+    9: "Nine",
+    10: "Ten",
+    11: "Eleven",
+    12: "Twelve",
+    13: "Thirteen",
+    14: "Fourteen",
+    15: "Fifteen",
+    16: "Sixteen",
+    17: "Seventeen",
+    18: "Eighteen",
+    19: "Nineteen",
+    20: "Twenty",
+    30: "Thirty",
+    40: "Forty",
+    50: "Fifty",
+    60: "Sixty",
+    70: "Seventy",
+    80: "Eighty",
+    90: "Ninety",
+    100: "Hundred",
+    THOUSAND: "Thousand",
+    MILLION: "Million",
+    BILLION: "Billion",
+    TRILLION: "Trillion",
+}
+
+def anglocize(x):
+    # ones
+    if x >= 0 and x < 10:
+        pass
+
+    # tens
+    elif x < 100:
+        pass
+
+    # hundreds
+    elif x < THOUSAND:
+        pass
+
+    # thousands
+    elif x < MILLION:
+        pass
+
+    # millions
+    elif x < BILLION:
+        pass
+
+    # billion
+    elif x < TRILLION:
+        pass
+
+    # trillion
+    else:
+        pass
+
+x = 1234
+assert anglocize(x) == "One Thousand, Two Hundred Thirty Four"
diff --git a/users/wpcarro/scratch/facebook/balanced-binary-tree.py b/users/wpcarro/scratch/facebook/balanced-binary-tree.py
new file mode 100644
index 0000000000..afa9706f97
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/balanced-binary-tree.py
@@ -0,0 +1,70 @@
+from collections import deque
+
+class Node(object):
+    # __init__ :: T(A)
+    def __init__(self, value=None, left=None, right=None):
+        self.value = value
+        self.left = left
+        self.right = right
+
+    # insert_left :: T(A) -> A -> T(A)
+    def insert_left(self, value):
+        self.left = Node(value)
+        return self.left
+
+    # insert_right :: T(A) -> A -> T(A)
+    def insert_right(self, value):
+        self.right = Node(value)
+        return self.right
+
+    # is_superbalanced :: T(A) -> Bool
+    def is_superbalanced(self):
+        xs = deque()
+        min_depth, max_depth = float('inf'), float('-inf')
+        xs.append((self, 0))
+        while xs:
+            x, d = xs.popleft()
+            # Only redefine the depths at leaf nodes
+            if not x.left and not x.right:
+                min_depth, max_depth = min(min_depth, d), max(max_depth, d)
+            if x.left:
+                xs.append((x.left, d + 1))
+            if x.right:
+                xs.append((x.right, d + 1))
+        return max_depth - min_depth <= 1
+
+    # __repr__ :: T(A) -> String
+    def __repr__(self):
+        result = ''
+        xs = deque()
+        xs.append((self, 0))
+        while xs:
+            node, indent = xs.popleft()
+            result += '{i}{x}\n'.format(i=' ' * indent, x=node.value)
+            if node.left:
+                xs.append((node.left, indent + 2))
+            if node.right:
+                xs.append((node.right, indent + 2))
+        return result
+
+# from_array :: List(A) -> T(A)
+def from_array(values):
+    xs = deque()
+    root = Node()
+    xs.append(root)
+    for value in values:
+        node = xs.popleft()
+        node.value = value
+        node.left = Node()
+        xs.append(node.left)
+        node.right = Node()
+        xs.append(node.right)
+    return root
+
+x = from_array([1, 1, 1, 1, 1, 1, 1])
+print(x)
+print(x.is_superbalanced())
+
+x = Node(1, Node(2), Node(3))
+print(x)
+print(x.is_superbalanced())
diff --git a/users/wpcarro/scratch/facebook/breakfast-generator.py b/users/wpcarro/scratch/facebook/breakfast-generator.py
new file mode 100644
index 0000000000..df9b5015ad
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/breakfast-generator.py
@@ -0,0 +1,112 @@
+# After being inspired by...
+# craftinginterpreters.com/representing-code.html
+# ...I'm implementing the breakfast generator that the author describes
+# therein.
+
+import random
+import string
+
+# Breakfast
+
+def breakfast():
+    fn = random.choice([
+        lambda: " ".join([protein(), "with", breakfast(), "on the side"]),
+        lambda: protein(),
+        lambda: bread(),
+    ])
+    return fn()
+
+def protein():
+    fn = random.choice([
+        lambda: " ".join([qualifier(), "crispy", "bacon"]),
+        lambda: "sausage",
+        lambda: " ".join([cooking_method(), "sausage"]),
+    ])
+    return fn()
+
+def qualifier():
+    fn = random.choice([
+        lambda: "really",
+        lambda: "super",
+        lambda: " ".join(["really", qualifier()]),
+    ])
+    return fn()
+
+def cooking_method():
+    return random.choice([
+        "scrambled",
+        "poached",
+        "fried",
+    ])
+
+def bread():
+    return random.choice([
+        "toast",
+        "biscuits",
+        "English muffin",
+    ])
+
+print(breakfast())
+
+# Expression Language
+
+# Because Python is a strictly evaluated language any functions that are
+# mutually recursive won't terminate and will overflow our stack. Therefore, any
+# non-terminals expressed in an alternative are wrapped in lambdas as thunks.
+
+def expression():
+    fn = random.choice([
+        lambda: literal(),
+        lambda: binary(),
+    ])
+    return fn()
+
+def literal():
+    return str(random.randint(0, 100))
+
+def binary():
+    return " ".join([expression(), operator(), expression()])
+
+def operator():
+    return random.choice(["+", "*"])
+
+print(expression())
+
+# Lox
+
+def lox_expression():
+    fn = random.choice([
+        lambda: lox_literal(),
+        lambda: lox_unary(),
+        lambda: lox_binary(),
+        lambda: lox_grouping(),
+    ])
+    return fn()
+
+def lox_literal():
+    fn = random.choice([
+        lambda: str(random.randint(0, 100)),
+        lambda: lox_string(),
+        lambda: random.choice(["true", "false"]),
+        lambda: "nil",
+    ])
+    return fn()
+
+def lox_string():
+    return "\"{}\"".format(
+        "".join(random.choice(string.ascii_lowercase)
+                for _ in range(random.randint(0, 25))))
+
+def lox_grouping():
+    return "(" + lox_expression() + ")"
+
+def lox_unary():
+    return random.choice(["-", "!"]) + lox_expression()
+
+def lox_binary():
+    return lox_expression() + lox_operator() + lox_expression()
+
+def lox_operator():
+    return random.choice(["==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/"])
+
+print(lox_expression())
diff --git a/users/wpcarro/scratch/facebook/bst-checker.py b/users/wpcarro/scratch/facebook/bst-checker.py
new file mode 100644
index 0000000000..7ef63a9531
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/bst-checker.py
@@ -0,0 +1,49 @@
+from collections import deque
+
+class Node(object):
+    def __init__(self, value, left=None, right=None):
+        self.value = value
+        self.left = left
+        self.right = right
+
+    def is_bst(self):
+        s = []
+        s.append((float('-inf'), self, float('inf')))
+        while s:
+            lo, node, hi = s.pop()
+            if lo <= node.value <= hi:
+                node.left and s.append((lo, node.left, node.value))
+                node.right and s.append((node.value, node.right, hi))
+            else:
+                return False
+        return True
+
+
+x = Node(
+    50,
+    Node(
+        17,
+        Node(
+            12,
+            Node(9),
+            Node(14),
+        ),
+        Node(
+            23,
+            Node(19),
+        ),
+    ),
+    Node(
+        72,
+        Node(
+            54,
+            None,
+            Node(67)
+        ),
+        Node(76),
+    ),
+)
+
+
+assert x.is_bst()
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/cafe-order-checker.py b/users/wpcarro/scratch/facebook/cafe-order-checker.py
new file mode 100644
index 0000000000..9d88a68069
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/cafe-order-checker.py
@@ -0,0 +1,19 @@
+def orders_are_sorted(take_out, dine_in, audit):
+    if len(take_out) + len(dine_in) != len(audit):
+        return False
+
+    i, j = 0, 0
+    for x in audit:
+        if i < len(take_out) and take_out[i] == x:
+            i += 1
+        elif j < len(dine_in) and dine_in[j] == x:
+            j += 1
+        else:
+            return False
+    return True
+
+
+assert orders_are_sorted([1,3,5], [2,4,6], [1,2,4,3,6,5])
+assert not orders_are_sorted([1,3,5], [2,4,6], [1,2,4,5,6,3])
+assert orders_are_sorted([], [2,4,6], [2,4,6])
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/cake_thief.py b/users/wpcarro/scratch/facebook/cake_thief.py
new file mode 100644
index 0000000000..90a2add066
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/cake_thief.py
@@ -0,0 +1,61 @@
+from math import floor
+
+def print_table(table):
+    print('\n-- TABLE --')
+    for row in range(len(table)):
+        x = ''
+        for col in range(len(table[row])):
+            x += ' ' + str(table[row][col])
+        print(x)
+
+def leftover(capacity, kg):
+    n = floor(capacity / kg)
+    return n, capacity - (n * kg)
+
+def init_table(num_rows, num_cols):
+    table = []
+    for _ in range(num_rows):
+        row = []
+        for _ in range(num_cols):
+            row.append(0)
+        table.append(row)
+    return table
+
+def get(table, row, col):
+    if row < 0 or col < 0:
+        return 0
+    return table[row][col]
+
+def max_haul(items, capacity):
+    table = init_table(len(items), capacity)
+
+    for row in range(len(table)):
+        for col in range(len(table[row])):
+            curr_capacity = col + 1
+            kg, val = items[row]
+            # A
+            a = get(table, row - 1, col)
+            # B
+            n, lo = leftover(curr_capacity, kg)
+            b = (val * n) + get(table, row - 1, lo - 1)
+            # commit
+            if kg > curr_capacity:
+                table[row][col] = a
+            else:
+                print(n, lo)
+                table[row][col] = max([a, b])
+            print_table(table)
+    return table[-1][-1]
+
+# There are multiple variants of this problem:
+#   1. We're allowed to take multiple of each item.
+#   2. We can only take one of each item.
+#   3. We can only take a fixed amount of each item.
+
+items = [(7,160), (3,90), (2,15)]
+capacity = 20
+result = max_haul(items, capacity)
+expected = None
+print("Result: {} == Expected: {}".format(result, expected))
+assert result == expected
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/camping-knapsack.py b/users/wpcarro/scratch/facebook/camping-knapsack.py
new file mode 100644
index 0000000000..add59ed409
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/camping-knapsack.py
@@ -0,0 +1,46 @@
+from utils import get, init_table, print_table
+
+def max_haul(capacity, items, names):
+    table = init_table(rows=len(items), cols=capacity, default=0)
+    items_table = init_table(rows=len(items), cols=capacity, default=[])
+    for row in range(len(table)):
+        for col in range(len(table[row])):
+            kg, value = items[row]
+            curr_capacity = col + 1
+
+            if kg > curr_capacity:
+                a = 0
+            else:
+                a = value + get(table, row - 1, curr_capacity - kg - 1)
+            b = get(table, row - 1, col)
+
+            if a > b:
+                rest = get(items_table, row - 1, curr_capacity - kg - 1)
+                knapsack = [names.get(items[row])]
+                if rest:
+                    knapsack += rest
+            else:
+                knapsack = get(items_table, row - 1, col)
+
+            table[row][col] = max([a, b])
+            items_table[row][col] = knapsack
+        print_table(table)
+    return items_table[-1][-1]
+
+water = (3, 10)
+book = (1, 3)
+food = (2, 9)
+jacket = (2, 5)
+camera = (1, 6)
+items = [water, book, food, jacket, camera]
+result = max_haul(6, items, {
+    water: 'water',
+    book: 'book',
+    food: 'food',
+    jacket: 'jacket',
+    camera: 'camera',
+})
+expected = ['camera', 'food', 'water']
+print(result, expected)
+assert result == expected
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/coin.py b/users/wpcarro/scratch/facebook/coin.py
new file mode 100644
index 0000000000..354e2dfb58
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/coin.py
@@ -0,0 +1,50 @@
+def init_table(rows=0, cols=0, default=None):
+    table = []
+    for _ in range(rows):
+        row = []
+        for _ in range(cols):
+            row.append(default)
+        table.append(row)
+    return table
+
+def print_table(table):
+    result = ''
+    for row in range(len(table)):
+        x = ''
+        for col in range(len(table[row])):
+            x += str(table[row][col]) + ' '
+        result += x + '\n'
+    print(result)
+
+def get(table, row, col):
+    if row < 0 or col < 0:
+        return 0
+    else:
+        return table[row][col]
+
+def make_change(coins, amt):
+    table = init_table(rows=len(coins), cols=amt, default=0)
+    for row in range(len(table)):
+        for col in range(len(table[row])):
+            coin = coins[row]
+            curr_amt = col + 1
+            pull_down = get(table, row - 1, col)
+
+            if curr_amt < coin:
+                table[row][col] = pull_down
+            elif curr_amt == coin:
+                table[row][col] = pull_down + 1
+            else:
+                leftover = get(table, row, curr_amt - coin - 1)
+                table[row][col] = pull_down + leftover
+
+    print_table(table)
+    return table[-1][-1]
+
+#   1 2 3 4
+# 1 1 1 1 1
+# 2 1 1 2 2
+# 3 1 1 3 4
+
+result = make_change([3,2,1], 4)
+print(result)
diff --git a/users/wpcarro/scratch/facebook/count-islands.py b/users/wpcarro/scratch/facebook/count-islands.py
new file mode 100644
index 0000000000..b876319b2f
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/count-islands.py
@@ -0,0 +1,53 @@
+from collections import deque
+
+def maybe_queue(row, col, game, q, seen):
+    """
+    Add coordinate, (`row`, `col`), to the queue, `q`, as long as it exists in
+    the map, `game`, and it is not already present in `seen`.
+    """
+    if row >= 0 and row < len(game) and col >= 0 and col < len(game[0]):
+        if game[row][col] == 'L' and (row, col) not in seen:
+            q.append((row, col))
+            seen.add((row, col))
+
+def visit_island(row, col, game, seen):
+    """
+    Starting at the coordinate, (`row`, `col`), in the map, `game`, visit all
+    surrounding tiles marked as land by adding them to the `seen` set.
+    """
+    q = deque()
+    q.append((row, col))
+    while q:
+        row, col = q.popleft()
+        maybe_queue(row - 1, col, game, q, seen) # UP
+        maybe_queue(row + 1, col, game, q, seen) # DOWN
+        maybe_queue(row, col - 1, game, q, seen) # LEFT
+        maybe_queue(row, col + 1, game, q, seen) # RIGHT
+
+def count_islands(game):
+    """
+    Return the number of contiguous land tiles in the map, `game`.
+    """
+    result = 0
+    seen = set()
+    for row in range(len(game)):
+        for col in range(len(game[row])):
+            if game[row][col] == 'L' and (row, col) not in seen:
+                visit_island(row, col, game, seen)
+                result += 1
+    return result
+
+################################################################################
+# Tests
+################################################################################
+
+game = [
+    "LWLWWW",
+    "LLLWWW",
+    "WWWLLW",
+]
+
+result = count_islands(game)
+print(result)
+assert result == 2
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/delete-node.py b/users/wpcarro/scratch/facebook/delete-node.py
new file mode 100644
index 0000000000..4034449ef0
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/delete-node.py
@@ -0,0 +1,19 @@
+from linked_list import Node, from_list
+
+def delete(node):
+    if not node.next:
+        node.value = None
+    else:
+        node.value = node.next.value
+        node.next = node.next.next
+
+one = Node(1)
+two = Node(2)
+three = Node(3)
+
+one.next = two
+two.next = three
+
+print(one)
+delete(two)
+print(one)
diff --git a/users/wpcarro/scratch/facebook/dijkstras.py b/users/wpcarro/scratch/facebook/dijkstras.py
new file mode 100644
index 0000000000..7031701994
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/dijkstras.py
@@ -0,0 +1,38 @@
+from heapq import heappush, heappop
+import random
+
+# Dijkstra's algorithm will traverse a directed graph with weighted edges. If
+# the edges aren't weighted, we can pretend that each edges weighs 1. The
+# algorithm will find the shortest path between points A and B.
+
+def dijkstra(a, b, graph):
+    h = []
+    seen = set()
+    heappush(h, (0, a, [a], []))
+    while h:
+        km, x, path, steps = heappop(h)
+
+        if x == b:
+            for a, b, d in steps:
+                print("{} -> {} => {}".format(a, b, d))
+            return path, km
+
+        seen.add(x)
+        for c, dist in graph[x]:
+            if c not in seen:
+                heappush(h, (km + dist, c, path + [c], steps + [(x, c, dist)]))
+    return [], float('inf')
+
+graph = {
+    1: [(3, 9), (2, 7), (6, 14)],
+    2: [(1, 7), (3, 10), (4, 15)],
+    3: [(1, 9), (6, 2), (4, 11), (2, 10)],
+    4: [(5, 6), (2, 15), (3, 11)],
+    5: [(4, 6), (6, 9)],
+    6: [(5, 9), (3, 2), (1, 14)],
+}
+
+beg = random.choice(list(graph.keys()))
+end = random.choice(list(graph.keys()))
+print("Searching for the shortest path from {} -> {}".format(beg, end))
+print(dijkstra(beg, end, graph))
diff --git a/users/wpcarro/scratch/facebook/edit-distance.py b/users/wpcarro/scratch/facebook/edit-distance.py
new file mode 100644
index 0000000000..a5b744f30f
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/edit-distance.py
@@ -0,0 +1,47 @@
+def print_grid(grid):
+    result = []
+    for row in grid:
+        result.append(" ".join(str(c) for c in row))
+    return print("\n".join(result))
+
+def edit_distance(a, b):
+    """
+    Compute the "edit distance" to transform string `a` into string `b`.
+    """
+    grid = []
+    for row in range(len(a) + 1):
+        r = []
+        for col in range(len(b) + 1):
+            r.append(0)
+        grid.append(r)
+
+    # left-to-right
+    # populate grid[0][i]
+    for col in range(len(grid[0])):
+        grid[0][col] = col
+
+    # top-to-bottom
+    # populate grid[i][0]
+    for row in range(len(grid)):
+        grid[row][0] = row
+
+    for row in range(1, len(grid)):
+        for col in range(1, len(grid[row])):
+            # last characters are the same
+            if a[0:row][-1] == b[0:col][-1]:
+                grid[row][col] = grid[row - 1][col - 1]
+            else:
+                # substitution
+                s = 1 + grid[row - 1][col - 1]
+                # deletion
+                d = 1 + grid[row - 1][col]
+                # insertion
+                i = 1 + grid[row][col - 1]
+                grid[row][col] = min(s, d, i)
+    print_grid(grid)
+    return grid[-1][-1]
+
+result = edit_distance("pizza", "pisa")
+print(result)
+assert result == 2
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/evaluator.hs b/users/wpcarro/scratch/facebook/evaluator.hs
new file mode 100644
index 0000000000..1ba46a7548
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/evaluator.hs
@@ -0,0 +1,39 @@
+module Evaluator where
+
+data Token
+  = TokenInt Integer
+  | TokenAdd
+  | TokenMultiply
+  deriving (Eq, Show)
+
+newtype AST = AST [Token]
+  deriving (Eq, Show)
+
+tokens :: [Token]
+tokens =
+  [ TokenInt 13
+  , TokenAdd
+  , TokenInt 2
+  , TokenMultiply
+  , TokenInt 4
+  , TokenAdd
+  , TokenInt 7
+  , TokenAdd
+  , TokenInt 3
+  , TokenMultiply
+  , TokenInt 8
+  ]
+
+-- expression     -> addition ;
+-- addition       -> multiplication ( "+" multiplication )* ;
+-- multiplication -> terminal ( "*" terminal )* ;
+-- terminal       -> NUMBER ;
+
+parseExpression :: [Token] -> ([Token], AST)
+parseExpression tokens = do
+  lhs, rest = parseMultiplication tokens
+
+parseMulitplication :: [Token] -> ([Token], AST)
+
+main :: IO ()
+main = print $ parse tokens
diff --git a/users/wpcarro/scratch/facebook/evaluator.py b/users/wpcarro/scratch/facebook/evaluator.py
new file mode 100644
index 0000000000..14deb66a8f
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/evaluator.py
@@ -0,0 +1,234 @@
+# After stumbling through my first technical screen, I'm going to drill
+# algorithms for implementing evaluators for a toy expression language:
+# e.g. 2 + 13 * 3 + 5 * 2
+#
+# As of now, I'm aware of a few algorithms for solving this:
+#   - DONE: Convert infix expression to Polish notation and evaluate the Polish
+#     notation.
+#   - DONE: Evaluate the tokens using two stacks and avoid converting it.
+#   - DONE: Create a tree of depth two to encode the operator precedence and
+#     evaluate that AST.
+#   - TODO: Convert the infix expression to a prefix expression
+#   - TODO: Write a recursive descent parser and evaluate the AST.
+
+operators = {
+    '*': 1,
+    '+': 0,
+}
+
+def tokenize(xs):
+    result = []
+    i = 0
+    while i < len(xs):
+        current = xs[i]
+        if current == ' ':
+            i += 1
+            continue
+        elif current in operators.keys():
+            result.append(current)
+            i += 1
+        else:
+            i += 1
+            while i < len(xs) and xs[i] in {str(n) for n in range(10)}:
+                current += xs[i]
+                i += 1
+            result.append(int(current))
+    return result
+
+# Convert infix to postfix; evaluate postfix
+# I believe this is known as the Shunting-Yards algorithm
+def postfix(tokens):
+    result = []
+    s = []
+    for token in tokens:
+        if type(token) == int:
+            result.append(token)
+        else:
+            while s and operators[token] < operators[s[-1]]:
+                result.append(s.pop())
+            s.append(token)
+    while s:
+        result.append(s.pop())
+    return result
+
+def do_evaluate_with_polish_notation(tokens):
+    s = []
+    for token in tokens:
+        if token == '*':
+            s.append(s.pop() * s.pop())
+        elif token == '+':
+            s.append(s.pop() + s.pop())
+        else:
+            s.append(token)
+    return s[-1]
+
+def evaluate_with_polish_notation(expr):
+    tokens = tokenize(expr)
+    print("Tokens:  {}".format(tokens))
+    pn = postfix(tokens)
+    print("Postfix: {}".format(pn))
+    result = do_evaluate_with_polish_notation(pn)
+    print("Result:  {}".format(result))
+    return result
+
+# Evaluate Tokens
+
+def apply_operator(op, a, b):
+    if op == '*':
+        return a * b
+    elif op == '+':
+        return a + b
+
+def do_evaluate_tokens(tokens):
+    vals = []
+    ops = []
+    for token in tokens:
+        if type(token) == int:
+            vals.append(token)
+        elif token == '*':
+            ops.append(token)
+        elif token == '+':
+            while ops and operators[token] < operators[ops[-1]]:
+                vals.append(apply_operator(ops.pop(), vals.pop(), vals.pop()))
+            ops.append(token)
+        else:
+            raise Exception("Unexpected token: {}".format(token))
+    while ops:
+        vals.append(apply_operator(ops.pop(), vals.pop(), vals.pop()))
+    return vals[-1]
+
+def evaluate_tokens(expr):
+    tokens = tokenize(expr)
+    print("Tokens:  {}".format(tokens))
+    result = do_evaluate_tokens(tokens)
+    print("Result:  {}".format(result))
+    return result
+
+# Ad Hoc Tree
+
+def parse(tokens):
+    result = []
+    series = []
+    for token in tokens:
+        if type(token) == int:
+            series.append(token)
+        elif token == '*':
+            continue
+        elif token == '+':
+            result.append(series)
+            series = []
+        else:
+            raise Exception("Unexpected token: {}".format(token))
+    result.append(series)
+    return result
+
+def product(xs):
+    result = 1
+    for x in xs:
+        result *= x
+    return result
+
+def do_evaluate_ad_hoc_tree(ast):
+    return sum([product(xs) for xs in ast])
+
+def evaluate_ad_hoc_tree(expr):
+    tokens = tokenize(expr)
+    print("Tokens:  {}".format(tokens))
+    ast = parse(tokens)
+    print("AST:     {}".format(ast))
+    result = do_evaluate_ad_hoc_tree(ast)
+    print("Result:  {}".format(result))
+    return result
+
+# Recursive Descent Parser
+
+# expression     -> addition ;
+# addition       -> multiplication ( "+" multiplication )* ;
+# multiplication -> terminal ( "*" terminal )* ;
+# terminal       -> NUMBER ;
+
+class Parser(object):
+    def __init__(self, tokens):
+        self.tokens = tokens
+        self.i = 0
+
+    # mutations
+    def advance(self):
+        self.i += 1
+
+    def consume(self):
+        result = self.curr()
+        self.advance()
+        return result
+
+    # predicates
+    def match(self, x):
+        if self.curr() == x:
+            self.advance()
+            return True
+        return False
+
+    def tokens_available(self):
+        return self.i < len(self.tokens)
+
+    # getters
+    def prev(self):
+        return self.tokens[self.i - 1]
+
+    def curr(self):
+        return self.tokens[self.i] if self.tokens_available() else None
+
+    def next(self):
+        return self.tokens[self.i + 1]
+
+def parse_expression(tokens):
+    parser = Parser(tokens)
+    return parse_addition(parser)
+
+def parse_addition(parser):
+    result = parse_multiplication(parser)
+    while parser.match("+"):
+        op = parser.prev()
+        rhs = parse_multiplication(parser)
+        result = ["+", result, rhs]
+    return result
+
+def parse_multiplication(parser):
+    result = parse_terminal(parser)
+    while parser.match("*"):
+        op = parser.prev()
+        rhs = parse_terminal(parser)
+        result = ["*", result, rhs]
+    return result
+
+def parse_terminal(parser):
+    # If we reach here, the current token *must* be a number.
+    return parser.consume()
+
+def evaluate_ast(ast):
+    if type(ast) == int:
+        return ast
+    else:
+        op, lhs, rhs = ast[0], ast[1], ast[2]
+        return apply_operator(op, evaluate_ast(lhs), evaluate_ast(rhs))
+
+def evaluate_recursive_descent(expr):
+    tokens = tokenize(expr)
+    print("Tokens:  {}".format(tokens))
+    ast = parse_expression(tokens)
+    print("AST:     {}".format(ast))
+    result = evaluate_ast(ast)
+    return result
+
+methods = {
+    'Polish Notation': evaluate_with_polish_notation,
+    'Evaluate Tokens': evaluate_tokens,
+    'Ad Hoc Tree': evaluate_ad_hoc_tree,
+    'Recursive Descent': evaluate_recursive_descent,
+}
+
+for name, fn in methods.items():
+    expr = "13 + 2 * 4 + 7 + 3 * 8"
+    print("Evaluating \"{}\" using the \"{}\" method...".format(expr, name))
+    assert fn(expr) == eval(expr)
+    print("Success!")
diff --git a/users/wpcarro/scratch/facebook/find-duplicate-beast-mode.py b/users/wpcarro/scratch/facebook/find-duplicate-beast-mode.py
new file mode 100644
index 0000000000..e246415efd
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/find-duplicate-beast-mode.py
@@ -0,0 +1,57 @@
+def advance(position, xs):
+    """
+    Return the next element in `xs` pointed to by the current `position`.
+    """
+    return xs[position - 1]
+
+def find_duplicate(xs):
+    """
+    Find the duplicate integer in the list, `xs`.
+    """
+    beg = xs[-1]
+    a = beg
+    b = advance(a, xs)
+    # Find the first element of the cycle
+    cycle_beg = None
+    while a != b:
+        cycle_beg = a
+        a = advance(a, xs)
+        b = advance(b, xs)
+        b = advance(b, xs)
+    # The duplicate element is the element before the `cycle_beg`
+    a = beg
+    result = None
+    while a != cycle_beg:
+        result = a
+        a = advance(a, xs)
+    return result
+
+def find_duplicate(xs):
+    """
+    This is the solution that InterviewCake.com suggests.
+    """
+    # find length of the cycle
+    beg = xs[-1]
+    a = beg
+    for _ in range(len(xs)):
+        a = advance(a, xs)
+    element = a
+    a = advance(a, xs)
+    n = 1
+    while a != element:
+        a = advance(a, xs)
+        n += 1
+    # find the first element in the cycle
+    a, b = beg, beg
+    for _ in range(n):
+        b = advance(b, xs)
+    while a != b:
+        a = advance(a, xs)
+        b = advance(b, xs)
+    return a
+
+xs = [2, 3, 1, 3]
+result = find_duplicate(xs)
+print(result)
+assert result == 3
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/find-duplicate-optimize-for-space.py b/users/wpcarro/scratch/facebook/find-duplicate-optimize-for-space.py
new file mode 100644
index 0000000000..7c491aef60
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/find-duplicate-optimize-for-space.py
@@ -0,0 +1,22 @@
+import random
+
+def find_duplicate(xs):
+    print(xs)
+    # entry point in our cycle is the duplicate
+    i = xs[0]
+    j = xs[xs[0]]
+    while i != j:
+        print(i, xs[i], j, xs[j])
+        i = xs[i]
+        j = xs[xs[j]]
+    # detect cycle
+    j = 0
+    while i != j:
+        i = xs[i]
+        j = xs[j]
+    return xs[i]
+
+n = random.randint(5, 10)
+xs = [random.randint(0, n - 1) for _ in range(n)]
+result = find_duplicate(xs)
+print(xs, result)
diff --git a/users/wpcarro/scratch/facebook/find-rotation-point.py b/users/wpcarro/scratch/facebook/find-rotation-point.py
new file mode 100644
index 0000000000..3636be4d93
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/find-rotation-point.py
@@ -0,0 +1,47 @@
+from math import floor
+
+def find_rotation(xs):
+    if xs[0] < xs[-1]:
+        return xs[0]
+    beg, end = 0, len(xs) - 1
+    found = False
+    count = 10
+    while not found and count >= 0:
+        i = beg + floor((end - beg) / 2)
+        if xs[beg] < xs[i]:
+            beg = i
+            i = beg + floor((end - beg) / 2)
+        elif xs[beg] > xs[i]:
+            end = i
+        found = xs[i - 1] > xs[i]
+        count -= 1
+    return xs[i]
+
+
+xs = [(['ptolemaic',
+        'retrograde',
+        'supplant',
+        'undulate',
+        'xenoepist',
+        'zebra',
+        'asymptote',
+        'babka',
+        'banoffee',
+        'engender',
+        'karpatka',
+        'othellolagkage',
+        ], "asymptote"),
+      (['asymptote',
+        'babka',
+        'banoffee',
+        'engender',
+        'karpatka',
+        'othellolagkage',
+        ], "asymptote"),
+      ]
+
+for x, expected in xs:
+    result = find_rotation(x)
+    print(x, result)
+    assert result == expected
+    print("Success!")
diff --git a/users/wpcarro/scratch/facebook/find-unique-int-among-duplicates.py b/users/wpcarro/scratch/facebook/find-unique-int-among-duplicates.py
new file mode 100644
index 0000000000..56032aa05c
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/find-unique-int-among-duplicates.py
@@ -0,0 +1,17 @@
+import random
+
+def find_duplicate(xs):
+    mini, maxi, acc = xs[0], xs[0], xs[0]
+    for i in range(1, len(xs)):
+        mini = min(mini, xs[i])
+        maxi = max(maxi, xs[i])
+        acc = acc ^ xs[i]
+    mask = mini
+    for i in range(mini + 1, maxi + 1):
+        mask = mask ^ i
+    return mask ^ acc
+
+xs = [5, 3, 4, 1, 5, 2]
+print(xs)
+result = find_duplicate(xs)
+print(result)
diff --git a/users/wpcarro/scratch/facebook/graph-coloring.py b/users/wpcarro/scratch/facebook/graph-coloring.py
new file mode 100644
index 0000000000..e5b6d9c893
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/graph-coloring.py
@@ -0,0 +1,60 @@
+from collections import deque
+
+class Palette(object):
+    def __init__(self, n):
+        self.i = 0
+        self.colors = list(range(n))
+
+    def get(self):
+        return self.colors[self.i]
+
+    def advance(self):
+        self.i += 1 % len(self.colors)
+
+class GraphNode(object):
+    def __init__(self, label):
+        self.label = label
+        self.neighbors = set()
+        self.color = None
+
+    def __repr__(self):
+        result = []
+        xs = deque()
+        xs.append(self)
+        seen = set()
+        while xs:
+            node = xs.popleft()
+            result.append('{} ({})'.format(node.label, str(node.color)))
+            for c in node.neighbors:
+                if c.label not in seen:
+                    xs.append(c)
+                    seen.add(node.label)
+        return ', '.join(result)
+
+def color_graph(graph, d):
+    seen = set()
+    start = graph
+    xs = deque()
+    palette = Palette(d + 1)
+    xs.append((start, palette.get()))
+    while xs:
+        x, color = xs.popleft()
+        x.color = color
+        for c in x.neighbors:
+            if c.label not in seen:
+                palette.advance()
+                xs.append((c, palette.get()))
+                seen.add(x.label)
+
+a = GraphNode('a')
+b = GraphNode('b')
+c = GraphNode('c')
+
+a.neighbors.add(b)
+b.neighbors.add(a)
+b.neighbors.add(c)
+c.neighbors.add(b)
+
+print(a)
+color_graph(a, 3)
+print(a)
diff --git a/users/wpcarro/scratch/facebook/hard/binary-adder.py b/users/wpcarro/scratch/facebook/hard/binary-adder.py
new file mode 100644
index 0000000000..f79a9f22b3
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/hard/binary-adder.py
@@ -0,0 +1,22 @@
+import random
+
+def add(a, b):
+    """
+    Return the sum of `a` and `b`.
+    """
+    if b == 0:
+        return a
+    sum = a ^ b
+    carry = (a & b) << 1
+    return add(sum, carry)
+
+################################################################################
+# Tests
+################################################################################
+
+for _ in range(10):
+    x, y = random.randint(0, 100), random.randint(0, 100)
+    print("{} + {} = {} == {}".format(x, y, x + y, add(x, y)))
+    assert add(x, y) == x + y
+    print("Pass!")
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/hard/fisher-yates.py b/users/wpcarro/scratch/facebook/hard/fisher-yates.py
new file mode 100644
index 0000000000..200d1613dd
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/hard/fisher-yates.py
@@ -0,0 +1,7 @@
+import random
+
+def shuffle(xs):
+    n = len(xs)
+    for i in range(n):
+        j = random.randint(i, n - 1)
+        xs[i], xs[j] = xs[j], xs[i]
diff --git a/users/wpcarro/scratch/facebook/hard/random-choice.py b/users/wpcarro/scratch/facebook/hard/random-choice.py
new file mode 100644
index 0000000000..a5c6e4e6ee
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/hard/random-choice.py
@@ -0,0 +1,50 @@
+import random
+
+# This class of problems is known as "resevoir sampling".
+def choose_a(m, xs):
+    """
+    Randomly choose `m` elements from `xs`.
+    This algorithm runs in linear time with respect to the size of `xs`.
+    """
+    result = [None] * m
+    for i in range(len(xs)):
+        j = random.randint(0, i)
+        if j < m:
+            result[j] = xs[i]
+    return result
+
+def choose_b(m, xs):
+    """
+    This algorithm, which copies `xs`, which runs in linear time, and then
+    shuffles the copies, which also runs in linear time, achieves the same
+    result as `choose_a` and both run in linear time.
+
+    `choose_a` is still preferable since it has a coefficient of one, while this
+    version has a coefficient of two because it copies + shuffles.
+    """
+    ys = xs[:]
+    random.shuffle(ys)
+    return ys[:m]
+
+def choose_c(m, xs):
+    """
+    This is one, possibly inefficient, way to randomly sample `m` elements from
+    `xs`.
+    """
+    choices = set()
+    while len(choices) < m:
+        choices.add(random.randint(0, len(xs) - 1))
+    return [xs[i] for i in choices]
+
+# ROYGBIV
+xs = [
+    'red',
+    'orange',
+    'yellow',
+    'green',
+    'blue',
+    'indigo',
+    'violet',
+]
+print(choose_b(3, xs))
+print(choose_c(3, xs))
diff --git a/users/wpcarro/scratch/facebook/hard/suffix-tree.py b/users/wpcarro/scratch/facebook/hard/suffix-tree.py
new file mode 100644
index 0000000000..782678fb82
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/hard/suffix-tree.py
@@ -0,0 +1,93 @@
+import random
+from collections import deque
+
+def exists(pattern, tree):
+    """
+    Return true if `pattern` exists in `tree`.
+    """
+    if len(pattern) == 0:
+        return True
+    if len(pattern) == 1:
+        for branch in tree:
+            if branch[0] == pattern[0]:
+                return True
+        return False
+    for branch in tree:
+        if branch[0] == pattern[0]:
+            return exists(pattern[1:], branch[1])
+    return False
+
+# Branch :: (Char, [Branch])
+# SuffixTree :: [Branch]
+
+def suffix_tree(xs):
+    """
+    Create a suffix tree from the input string, `xs`.
+    """
+    root = []
+    for i in range(len(xs)):
+        curr = xs[i:]
+        parent = root
+        for c1 in curr:
+            grafted = False
+            for c2, children in parent:
+                if c1 == c2:
+                    grafted = True
+                    parent = children
+            if grafted:
+                continue
+            else:
+                children = []
+                child = (c1, children)
+                parent.append(child)
+                parent = children
+    return root
+
+def suffix_tree(x):
+    """
+    Creates a suffix from the input string, `x`. This implementation uses a
+    stack.
+    """
+    result = [None, []]
+    q = deque()
+    for i in range(len(x)):
+        q.append((result, x[i:]))
+    while q:
+        parent, x = q.popleft()
+        s = []
+        s.append((parent, x))
+        while s:
+            parent, x = s.pop()
+            if not x:
+                continue
+            c, rest = x[0], x[1:]
+            grafted = False
+            for child in parent[1]:
+                if c == child[0]:
+                    s.append((child, rest))
+                    grafted = True
+            if not grafted:
+                child = [c, []]
+                parent[1].append(child)
+                s.append((child, rest))
+    return result[1]
+
+################################################################################
+# Tests
+################################################################################
+
+x = random.choice(["burrito", "pizza", "guacamole"])
+tree = suffix_tree(x)
+for branch in tree:
+    print(branch)
+
+for _ in range(3):
+    n = len(x)
+    i, j = random.randint(0, n), random.randint(0, n)
+    pattern = x[min(i, j):max(i, j)]
+    print("Checking \"{}\" for \"{}\" ...".format(x, pattern))
+    print("Result: {}".format(exists(pattern, tree)))
+    pattern = random.choice(["foo", "bar", "baz"])
+    print("Checking \"{}\" for \"{}\" ...".format(x, pattern))
+    print("Result: {}".format(exists(pattern, tree)))
+    print()
diff --git a/users/wpcarro/scratch/facebook/heap.py b/users/wpcarro/scratch/facebook/heap.py
new file mode 100644
index 0000000000..0c0dce91b4
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/heap.py
@@ -0,0 +1,30 @@
+from math import floor
+
+class Heap(object):
+    def __init__(self):
+        self.xs = [None]
+        self.i = 1
+
+    def __repr__(self):
+        return "[{}]".format(", ".join(str(x) for x in self.xs[1:]))
+
+    def insert(self, x):
+        if len(self.xs) == 1:
+            self.xs.append(x)
+            self.i += 1
+            return
+        self.xs.append(x)
+        i = self.i
+        while i != 1 and self.xs[floor(i / 2)] > self.xs[i]:
+            self.xs[floor(i / 2)], self.xs[i] = self.xs[i], self.xs[floor(i / 2)]
+            i = floor(i / 2)
+        self.i += 1
+
+    def root(self):
+        return self.xs[1]
+
+xs = Heap()
+print(xs)
+for x in [12, 15, 14, 21, 1, 10]:
+    xs.insert(x)
+    print(xs)
diff --git a/users/wpcarro/scratch/facebook/highest-product-of-3.py b/users/wpcarro/scratch/facebook/highest-product-of-3.py
new file mode 100644
index 0000000000..c237b8e52e
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/highest-product-of-3.py
@@ -0,0 +1,20 @@
+def hi_product(xs):
+    lowest_one, highest_one = min(xs[0], xs[1]), max(xs[0], xs[1])
+    lowest_two, highest_two = xs[0] * xs[1], xs[0] * xs[1]
+    highest = float('-inf')
+    for x in xs[2:]:
+        highest = max(highest, highest_two * x, lowest_two * x)
+        lowest_one = min(lowest_one, x)
+        highest_one = max(highest_one, x)
+        lowest_two = min(lowest_two, highest_one * x, lowest_one * x)
+        highest_two = max(highest_two, highest_one * x, lowest_one * x)
+    return highest
+
+xs = [([-10,-10,1,3,2], 300),
+      ([1,10,-5,1,-100], 5000)]
+
+for x, expected in xs:
+    result = hi_product(x)
+    print(x, result)
+    assert result == expected
+    print("Success!")
diff --git a/users/wpcarro/scratch/facebook/infix-to-postfix.py b/users/wpcarro/scratch/facebook/infix-to-postfix.py
new file mode 100644
index 0000000000..4c6d64494d
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/infix-to-postfix.py
@@ -0,0 +1,51 @@
+operators = {
+    '*': 1,
+    '+': 0,
+}
+
+def tokenize(xs):
+    result = []
+    i = 0
+    while i < len(xs):
+        current = xs[i]
+        if current in operators.keys():
+            result.append(current)
+            i += 1
+            continue
+        else:
+            i += 1
+            while i < len(xs) and xs[i] in {str(n) for n in range(10)}:
+                current += xs[i]
+                i += 1
+            result.append(int(current))
+    return result
+
+def postfix(xs):
+    result = []
+    s = []
+    for x in xs:
+        if x in operators.keys():
+            while s and operators[s[-1]] >= operators[x]:
+                result.append(s.pop())
+            s.append(x)
+        else:
+            result.append(x)
+    while s:
+        result.append(s.pop())
+    return result
+
+def evaluate(xs):
+    s = []
+    for x in xs:
+        print(s, x)
+        if x == '*':
+            s.append(s.pop() * s.pop())
+        elif x == '+':
+            s.append(s.pop() + s.pop())
+        else:
+            s.append(x)
+        print(s)
+    return s[-1]
+
+
+print(evaluate(postfix(tokenize("12+3*10"))))
diff --git a/users/wpcarro/scratch/facebook/inflight-entertainment.py b/users/wpcarro/scratch/facebook/inflight-entertainment.py
new file mode 100644
index 0000000000..7ddea5350a
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/inflight-entertainment.py
@@ -0,0 +1,29 @@
+from random import choice
+from utils import init_table
+
+def get(movie, seeking):
+    return any([movie in xs for xs in seeking.values()])
+
+def set_complement(movie, seeking):
+    for duration, xs in seeking.items():
+        seeking[duration].add(duration - movie)
+
+def choose_movies(tolerance, duration, movies):
+    seeking = {duration + i: set() for i in range(-1 * tolerance, tolerance + 1)}
+    for movie in movies:
+        if get(movie, seeking):
+            return movie, duration - movie
+        else:
+            set_complement(movie, seeking)
+    return None
+
+tolerance = 20
+duration = choice([1, 2, 3]) * choice([1, 2]) * choice([15, 30, 45])
+movies = [choice([1, 2, 3]) * choice([15, 30, 45]) for _ in range(10)]
+print("Seeking two movies for a duration of [{}, {}] minutes".format(duration - tolerance, duration + tolerance))
+print(movies)
+result = choose_movies(tolerance, duration, movies)
+if result:
+    print("{} + {} = {}".format(result[0], result[1], duration))
+else:
+    print(":( We're sad because we couldn't find two movies for a {} minute flight".format(duration))
diff --git a/users/wpcarro/scratch/facebook/intersecting-linked-lists.py b/users/wpcarro/scratch/facebook/intersecting-linked-lists.py
new file mode 100644
index 0000000000..80ac01dafd
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/intersecting-linked-lists.py
@@ -0,0 +1,34 @@
+class LinkedList(object):
+    def __init__(self, x):
+        self.val = x
+        self.next = None
+
+    def __repr__(self):
+        if self.next:
+            return "{} -> {}".format(self.val, self.next)
+        return "{}".format(self.val)
+
+def find_intersection(a, b):
+    init_a, init_b = a, b
+
+    while a != b:
+        a = a.next if a.next else init_b
+        b = b.next if b.next else init_a
+
+    return a
+
+# make A...
+e1 = LinkedList(5)
+d1 = LinkedList(2); d1.next = e1
+c1 = LinkedList(3); c1.next = d1 # shared
+b1 = LinkedList(1); b1.next = c1 # shared
+a1 = LinkedList(4); a1.next = b1 # shared
+
+# make B...
+c2 = LinkedList(1); c2.next = c1
+b2 = LinkedList(5); b2.next = c2
+a2 = LinkedList(6); a2.next = b2
+
+print(a1)
+print(a2)
+print(find_intersection(a1, a2).val)
diff --git a/users/wpcarro/scratch/facebook/interview-cake/bst-checker.py b/users/wpcarro/scratch/facebook/interview-cake/bst-checker.py
new file mode 100644
index 0000000000..bbd52fa9c6
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/interview-cake/bst-checker.py
@@ -0,0 +1,14 @@
+def is_valid(node):
+    """
+    Return True if `node` is a valid binary search tree.
+    """
+    s = []
+    s.append((float('-inf'), node, float('inf')))
+    while s:
+        lo, node, hi = s.pop()
+        if lo <= node.value <= hi:
+            node.lhs and s.append((lo, node.lhs, node.value))
+            node.rhs and s.append((node.value, node.rhs, hi))
+        else:
+            return False
+    return True
diff --git a/users/wpcarro/scratch/facebook/interview-cake/cafe-order-checker.py b/users/wpcarro/scratch/facebook/interview-cake/cafe-order-checker.py
new file mode 100644
index 0000000000..688c340b98
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/interview-cake/cafe-order-checker.py
@@ -0,0 +1,34 @@
+def valid(take_out, dine_in, served):
+    # edge case
+    if len(take_out) + len(dine_in) != len(served):
+        return False
+    i = 0
+    j = 0
+    k = 0
+    while i < len(take_out) and j < len(dine_in):
+        if take_out[i] == served[k]:
+            i += 1
+        elif dine_in[j] == served[k]:
+            j += 1
+        else:
+            return False
+        k += 1
+    # take out
+    while i < len(take_out):
+        if take_out[i] != served[k]:
+            return False
+        i += 1
+    # dine in
+    while j < len(dine_in):
+        if dine_in[j] != served[k]:
+            return False
+        j += 1
+    return True
+
+take_out = [17, 8, 24]
+dine_in  = [12, 19, 2]
+served   = [17, 8, 12, 19, 24, 2]
+result = valid(take_out, dine_in, served)
+print(result)
+assert result
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/interview-cake/linked-list-cycles.py b/users/wpcarro/scratch/facebook/interview-cake/linked-list-cycles.py
new file mode 100644
index 0000000000..523ecd959d
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/interview-cake/linked-list-cycles.py
@@ -0,0 +1,70 @@
+def contains_cycle(node):
+    """
+    Return True if the linked-list, `node`, contains a cycle.
+    """
+    if not node:
+        return False
+    a = node
+    b = node.next
+    while a != b:
+        a = a.next
+        if b and b.next and b.next.next:
+            b = b.next.next
+        else:
+            return False
+    return True
+
+################################################################################
+# Bonus
+################################################################################
+
+def first_node_in_cycle(node):
+    """
+    Given that the linked-list, `node`, contains a cycle, return the first
+    element of that cycle.
+    """
+    # enter the cycle
+    a = node
+    b = node.next
+    while a != b:
+        a = a.next
+        b = b.next.next
+
+    # get the length of the cycle
+    beg = a
+    a = a.next
+    n = 1
+    while a != beg:
+        a = a.next
+        n += 1
+
+    # run b n-steps ahead of a
+    a = node
+    b = node
+    for _ in range(n):
+        b = b.next
+
+    # where they intersect is the answer
+    while a != b:
+        a = a.next
+        b = b.next
+    return a
+
+################################################################################
+# Tests
+################################################################################
+
+class Node(object):
+    def __init__(self, value, next=None):
+        self.value = value
+        self.next = next
+    def __repr__(self):
+        return "Node({}) -> ...".format(self.value)
+
+d = Node('d')
+c = Node('c', d)
+b = Node('b', c)
+a = Node('a', b)
+d.next = b
+
+print(first_node_in_cycle(a))
diff --git a/users/wpcarro/scratch/facebook/interview-cake/merge-sorted-arrays.py b/users/wpcarro/scratch/facebook/interview-cake/merge-sorted-arrays.py
new file mode 100644
index 0000000000..877bb218fd
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/interview-cake/merge-sorted-arrays.py
@@ -0,0 +1,30 @@
+def merge_sorted(xs, ys):
+    result = []
+    i = 0
+    j = 0
+    while i < len(xs) and j < len(ys):
+        if xs[i] <= ys[j]:
+            result.append(xs[i])
+            i += 1
+        else:
+            result.append(ys[j])
+            j += 1
+    while i < len(xs):
+        result.append(xs[i])
+        i += 1
+    while j < len(xs):
+        result.append(ys[j])
+        j += 1
+    return result
+
+################################################################################
+# Tests
+################################################################################
+
+xs = [3, 4, 6, 10, 11, 15]
+ys = [1, 5, 8, 12, 14, 19]
+result = merge_sorted(xs, ys)
+print(result)
+assert len(result) == len(xs) + len(ys)
+assert result == [1, 3, 4, 5, 6, 8, 10, 11, 12, 14, 15, 19]
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/interview-cake/nth-fibonacci.py b/users/wpcarro/scratch/facebook/interview-cake/nth-fibonacci.py
new file mode 100644
index 0000000000..4629798cf7
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/interview-cake/nth-fibonacci.py
@@ -0,0 +1,6 @@
+def fib(n):
+    cache = (0, 1)
+    for _ in range(n):
+        a, b = cache
+        cache = (b, a + b)
+    return cache[0]
diff --git a/users/wpcarro/scratch/facebook/interview-cake/permutation-palindrome.py b/users/wpcarro/scratch/facebook/interview-cake/permutation-palindrome.py
new file mode 100644
index 0000000000..ced3b336e0
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/interview-cake/permutation-palindrome.py
@@ -0,0 +1,8 @@
+from collections import Counter
+
+def permutation_can_be_palindrome(x):
+    odd = 0
+    for _, n in Counter(x):
+        if n % 0 != 0:
+            odd += 1
+    return odd <= 1
diff --git a/users/wpcarro/scratch/facebook/interview-cake/queue-two-stacks.py b/users/wpcarro/scratch/facebook/interview-cake/queue-two-stacks.py
new file mode 100644
index 0000000000..bfa465f98d
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/interview-cake/queue-two-stacks.py
@@ -0,0 +1,17 @@
+class Queue(object):
+    def __init__(self):
+        self.lhs = []
+        self.rhs = []
+
+    def enqueue(self, x):
+        self.lhs.append(x)
+
+    def dequeue(self):
+        if self.rhs:
+            return self.rhs.pop()
+        while self.lhs:
+            self.rhs.append(self.lhs.pop())
+        if self.rhs:
+            return self.rhs.pop()
+        else:
+            raise Exception("Attempting to remove an item from an empty queue")
diff --git a/users/wpcarro/scratch/facebook/knapsack-faq.py b/users/wpcarro/scratch/facebook/knapsack-faq.py
new file mode 100644
index 0000000000..ae04f5eb96
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/knapsack-faq.py
@@ -0,0 +1,42 @@
+from utils import get, init_table, print_table
+
+# This problem has a few variants:
+#   - limited supply of each item
+#   - unlimited supply of each item
+#   - fractional amounts of each item (e.g. rice)
+
+def max_haul(capacity, items):
+    min_kg = min([kg for _, kg in items])
+    max_kg = max([kg for _, kg in items])
+
+    cols = int(max_kg / min_kg)
+    fr_col_index = lambda index: min_kg * index + min_kg
+    to_col_index = lambda capacity: int((capacity - min_kg) * cols / max_kg)
+
+    table = init_table(rows=len(items), cols=cols, default=0)
+    for row in range(len(table)):
+        for col in range(len(table[row])):
+            curr_capacity = fr_col_index(col)
+            value, kg = items[row]
+
+            if kg > curr_capacity:
+                a = 0
+            else:
+                a = value + get(table, row - 1, to_col_index(curr_capacity - kg))
+
+            b = get(table, row - 1, col)
+            table[row][col] = max([a, b])
+        print_table(table)
+    return table[-1][-1]
+
+guitar = (1500, 1)
+stereo = (3000, 4)
+laptop = (2000, 3)
+necklace = (2000, 0.5)
+items = [necklace, guitar, stereo, laptop]
+capacity = 4
+result = max_haul(capacity, items)
+expected = 4000
+print(result, expected)
+assert result == expected
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/kth-to-last-node-in-singly-linked-list.py b/users/wpcarro/scratch/facebook/kth-to-last-node-in-singly-linked-list.py
new file mode 100644
index 0000000000..dd258d924d
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/kth-to-last-node-in-singly-linked-list.py
@@ -0,0 +1,26 @@
+from linked_list import Node, from_list
+
+def kth_to_last_node(k, node):
+    one = node
+    two = node
+    for _ in range(k - 1):
+        if not one:
+            return None
+        one = one.next
+    while one.next:
+        one = one.next
+        two = two.next
+    return two.value
+
+
+xs = from_list(["Angel Food", "Bundt", "Cheese", "Devil's Food", "Eccles"])
+result = kth_to_last_node(2, xs)
+print(result)
+assert result == "Devil's Food"
+print("Success!")
+
+xs = from_list(["Angel Food", "Bundt"])
+result = kth_to_last_node(30, xs)
+print(result)
+assert result is None
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/language.py b/users/wpcarro/scratch/facebook/language.py
new file mode 100644
index 0000000000..b57f469b49
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/language.py
@@ -0,0 +1,70 @@
+import random
+
+# Write an evaluator for a small language:
+#   - operators: '+', '*'
+#   - operands:  Integers
+#
+# E.g. evaluate("2+14*90+5*16")
+
+def tokenize(xs):
+    result = []
+    i = 0
+    while i < len(xs):
+        current = xs[i]
+        if current in {'*', '+'}:
+            result.append(current)
+            i += 1
+            continue
+        elif current == ' ':
+            i += 1
+            continue
+        else:
+            i += 1
+            while i < len(xs) and xs[i] in {str(x) for x in range(10)}:
+                current += xs[i]
+                i += 1
+            result.append(int(current))
+    return result
+
+def ast(tokens):
+    result = []
+    series = []
+    for token in tokens:
+        if token == '+':
+            result.append(series)
+            series = []
+        elif token == '*':
+            continue
+        else:
+            series.append(token)
+    if series:
+        result.append(series)
+    return result
+
+def product(xs):
+    result = 1
+    for x in xs:
+        result *= x
+    return result
+
+def evaluate(x):
+    tokens = tokenize(x)
+    tree = ast(tokens)
+    return sum([product(xs) for xs in tree])
+
+n = 7
+operands = [random.randint(0, 100) for _ in range(n)]
+operators = [random.choice(['+','*']) for _ in range(n - 1)]
+expr = []
+for i in range(n - 1):
+    expr.append(operands[i])
+    expr.append(operators[i])
+expr.append(operands[-1])
+
+expr = ' '.join([str(x) for x in expr])
+print("Expression: {}".format(expr))
+print("Tokens: {}".format(tokenize(expr)))
+print("AST: {}".format(ast(tokenize(expr))))
+print("Answer: {}".format(evaluate(expr)))
+assert evaluate(expr) == eval(expr)
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/language2.py b/users/wpcarro/scratch/facebook/language2.py
new file mode 100644
index 0000000000..3aebd45483
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/language2.py
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+def tokenize(xs):
+    result = []
+    i = 0
+    while i < len(xs):
+        curr = xs[i]
+        if curr in {'*','+'}:
+            result.append(curr)
+            i += 1
+            continue
+        i += 1
+        while i < len(xs) and xs[i] in {str(x) for x in range(10)}:
+            curr += xs[i]
+            i += 1
+        result.append(int(curr))
+    return result
+
+def parse(tokens):
+    result = []
+    series = []
+    for token in tokens:
+        if token == '*':
+            continue
+        elif token == '+':
+            result.append(series)
+            series = []
+        else:
+            series.append(token)
+    if series:
+        result.append(series)
+    return result
+
+def product(xs):
+    result = 1
+    for x in xs:
+        result *= x
+    return result
+
+def evaluate(tree):
+    return sum([product(xs) for xs in tree])
+
+print(evaluate(parse(tokenize("2+30*8*9+10"))))
diff --git a/users/wpcarro/scratch/facebook/largest-contiguous-sum.py b/users/wpcarro/scratch/facebook/largest-contiguous-sum.py
new file mode 100644
index 0000000000..7761bf1c61
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/largest-contiguous-sum.py
@@ -0,0 +1,15 @@
+def find_sum(xs):
+    result = float('-inf')
+    streak = 0
+    for x in xs:
+        result = max(result, streak, x)
+        if streak + x <= 0:
+            streak = x
+        else:
+            streak += x
+    return result
+
+
+x = [2,-8,3,-2,4,-10]
+assert find_sum(x) == 5
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/largest-stack.py b/users/wpcarro/scratch/facebook/largest-stack.py
new file mode 100644
index 0000000000..052db44153
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/largest-stack.py
@@ -0,0 +1,49 @@
+from stack import Stack, from_list
+from heapq import heapify, heappush, heappop
+from random import shuffle
+
+class MaxStack(Stack):
+    def __init__(self):
+        self.max = Stack()
+        super().__init__()
+
+    def __repr__(self):
+        return super().__repr__()
+
+    def push(self, x):
+        super().push(x)
+        max = self.get_max()
+        if not max:
+            self.max.push(x)
+        else:
+            self.max.push(max if x < max else x)
+
+    def pop(self):
+        self.max.pop()
+        return super().pop()
+
+    def get_max(self):
+        return self.max.peek()
+
+xs = list(range(1, 11))
+shuffle(xs)
+stack = MaxStack()
+for x in xs:
+    stack.push(x)
+
+print(stack)
+result = stack.get_max()
+print(result)
+assert result == 10
+
+popped = stack.pop()
+print("Popped: {}".format(popped))
+print(stack)
+while popped != 10:
+    assert stack.get_max() == 10
+    popped = stack.pop()
+    print("Popped: {}".format(popped))
+    print(stack)
+
+assert stack.get_max() != 10
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/leetcode.org b/users/wpcarro/scratch/facebook/leetcode.org
new file mode 100644
index 0000000000..6e915faf29
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/leetcode.org
@@ -0,0 +1,163 @@
+# This list is from:
+# https://www.teamblind.com/post/New-Year-Gift---Curated-List-of-Top-100-LeetCode-Questions-to-Save-Your-Time-OaM1orEU
+* Array
+** DONE Two Sum
+   https://leetcode.com/problems/two-sum/
+** DONE Best Time to Buy and Sell Stock
+   https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
+** DONE Contains Duplicate
+   https://leetcode.com/problems/contains-duplicate/
+** DONE Product of Array Except Self
+   https://leetcode.com/problems/product-of-array-except-self/
+** DONE Maximum Subarray
+   https://leetcode.com/problems/maximum-subarray/
+** DONE Maximum Product Subarray
+   https://leetcode.com/problems/maximum-product-subarray/
+** DONE Find Minimum in Rotated Sorted Array
+   https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/
+** DONE Search in Rotated Sorted Array
+   https://leetcode.com/problems/search-in-rotated-sorted-array/
+** DONE 3Sum
+   https://leetcode.com/problems/3sum/
+** DONE Container With Most Water
+   https://leetcode.com/problems/container-with-most-water/
+* Binary
+** DONE Sum of Two Integers
+   https://leetcode.com/problems/sum-of-two-integers/
+** DONE Number of 1 Bits
+   https://leetcode.com/problems/number-of-1-bits/
+** TODO Counting Bits
+   https://leetcode.com/problems/counting-bits/
+** DONE Missing Number
+   https://leetcode.com/problems/missing-number/
+** TODO Reverse Bits
+   https://leetcode.com/problems/reverse-bits/
+* Dynamic Programming
+** DONE Climbing Stairs
+   https://leetcode.com/problems/climbing-stairs/
+** TODO Coin Change
+   https://leetcode.com/problems/coin-change/
+** TODO Longest Increasing Subsequence
+   https://leetcode.com/problems/longest-increasing-subsequence/
+** TODO Longest Common Subsequence
+** DONE Word Break Problem
+   https://leetcode.com/problems/word-break/
+** TODO Combination Sum
+   https://leetcode.com/problems/combination-sum-iv/
+** TODO House Robber
+   https://leetcode.com/problems/house-robber/
+** TODO House Robber II
+   https://leetcode.com/problems/house-robber-ii/
+** TODO Decode Ways
+   https://leetcode.com/problems/decode-ways/
+** TODO Unique Paths
+   https://leetcode.com/problems/unique-paths/
+** TODO Jump Game
+   https://leetcode.com/problems/jump-game/
+* Graph
+** DONE Clone Graph
+   https://leetcode.com/problems/clone-graph/
+** DONE Course Schedule
+   https://leetcode.com/problems/course-schedule/
+** TODO Pacific Atlantic Water Flow
+   https://leetcode.com/problems/pacific-atlantic-water-flow/
+** DONE Number of Islands
+   https://leetcode.com/problems/number-of-islands/
+** TODO Longest Consecutive Sequence
+   https://leetcode.com/problems/longest-consecutive-sequence/
+** TODO Alien Dictionary (Leetcode Premium)
+   https://leetcode.com/problems/alien-dictionary/
+** DONE Graph Valid Tree (Leetcode Premium)
+   https://leetcode.com/problems/graph-valid-tree/
+** DONE Number of Connected Components in an Undirected Graph (Leetcode Premium)
+   https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/
+* Interval
+** TODO Insert Interval
+   https://leetcode.com/problems/insert-interval/
+** DONE Merge Intervals
+   https://leetcode.com/problems/merge-intervals/
+** TODO No Overlapping Intervals
+   https://leetcode.com/problems/non-overlapping-intervals/
+** DONE Meeting Rooms (Leetcode Premium)
+   https://leetcode.com/problems/meeting-rooms/
+** TODO Meeting Rooms II (Leetcode Premium)
+   https://leetcode.com/problems/meeting-rooms-ii/
+* Linked List
+** DONE Reverse a Linked List
+   https://leetcode.com/problems/reverse-linked-list/
+** DONE Detect Cycle in a Linked List
+   https://leetcode.com/problems/linked-list-cycle/
+** DONE Merge Two Sorted Lists
+   https://leetcode.com/problems/merge-two-sorted-lists/
+** DONE Merge K Sorted Lists
+   https://leetcode.com/problems/merge-k-sorted-lists/
+** DONE Remove Nth Node From End Of List
+   https://leetcode.com/problems/remove-nth-node-from-end-of-list/
+** DONE Reorder List
+   https://leetcode.com/problems/reorder-list/
+* Matrix
+** DONE Set Matrix Zeroes
+   https://leetcode.com/problems/set-matrix-zeroes/
+** DONE Spiral Matrix
+   https://leetcode.com/problems/spiral-matrix/
+** TODO Rotate Image
+   https://leetcode.com/problems/rotate-image/
+** DONE Word Search
+   https://leetcode.com/problems/word-search/
+* String
+** TODO Longest Substring Without Repeating Characters
+   https://leetcode.com/problems/longest-substring-without-repeating-characters/
+** TODO Longest Repeating Character Replacement
+   https://leetcode.com/problems/longest-repeating-character-replacement/
+** TODO Minimum Window Substring
+   https://leetcode.com/problems/minimum-window-substring/
+** DONE Valid Anagram
+   https://leetcode.com/problems/valid-anagram/
+** DONE Group Anagrams
+   https://leetcode.com/problems/group-anagrams/
+** DONE Valid Parentheses
+   https://leetcode.com/problems/valid-parentheses/
+** DONE Valid Palindrome
+   https://leetcode.com/problems/valid-palindrome/
+** TODO Longest Palindromic Substring
+   https://leetcode.com/problems/longest-palindromic-substring/
+** TODO Palindromic Substrings
+   https://leetcode.com/problems/palindromic-substrings/
+** DONE Encode and Decode Strings (Leetcode Premium)
+   https://leetcode.com/problems/encode-and-decode-strings/
+* Tree
+** DONE Maximum Depth of Binary Tree
+   https://leetcode.com/problems/maximum-depth-of-binary-tree/
+** DONE Same Tree
+   https://leetcode.com/problems/same-tree/
+** DONE Invert/Flip Binary Tree
+   https://leetcode.com/problems/invert-binary-tree/
+** DONE Binary Tree Maximum Path Sum
+   https://leetcode.com/problems/binary-tree-maximum-path-sum/
+** DONE Binary Tree Level Order Traversal
+   https://leetcode.com/problems/binary-tree-level-order-traversal/
+** DONE Serialize and Deserialize Binary Tree
+   https://leetcode.com/problems/serialize-and-deserialize-binary-tree/
+** DONE Subtree of Another Tree
+   https://leetcode.com/problems/subtree-of-another-tree/
+** DONE Construct Binary Tree from Preorder and Inorder Traversal
+   https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
+** DONE Validate Binary Search Tree
+   https://leetcode.com/problems/validate-binary-search-tree/
+** DONE Kth Smallest Element in a BST
+   https://leetcode.com/problems/kth-smallest-element-in-a-bst/
+** DONE Lowest Common Ancestor of BST
+   https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
+** DONE Implement Trie (Prefix Tree)
+   https://leetcode.com/problems/implement-trie-prefix-tree/
+** DONE Add and Search Word
+   https://leetcode.com/problems/add-and-search-word-data-structure-design/
+** DONE Word Search II
+   https://leetcode.com/problems/word-search-ii/
+* Heap
+** DONE Merge K Sorted Lists
+   https://leetcode.com/problems/merge-k-sorted-lists/
+** DONE Top K Frequent Elements
+   https://leetcode.com/problems/top-k-frequent-elements/
+** DONE Find Median from Data Stream
+   https://leetcode.com/problems/find-median-from-data-stream/
diff --git a/users/wpcarro/scratch/facebook/linked-list-cycles.py b/users/wpcarro/scratch/facebook/linked-list-cycles.py
new file mode 100644
index 0000000000..56f54d4978
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/linked-list-cycles.py
@@ -0,0 +1,26 @@
+import random
+
+from linked_list import Node
+
+def contains_cycle(node):
+    one = node
+    two = node
+    while two.next and two.next.next:
+        one = one.next
+        two = two.next.next
+        if one == two:
+            return True
+    return False
+
+xs = Node(1, Node(2, Node(3)))
+assert not contains_cycle(xs)
+print("Success!")
+
+a = Node(1)
+b = Node(2)
+c = Node(3)
+a.next = b
+b.next = c
+c.next = random.choice([a, b, c])
+assert contains_cycle(a)
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/linked_list.py b/users/wpcarro/scratch/facebook/linked_list.py
new file mode 100644
index 0000000000..1ae7061e83
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/linked_list.py
@@ -0,0 +1,22 @@
+class Node(object):
+    def __init__(self, value=None, next=None):
+        self.value = value
+        self.next = next
+
+    def __repr__(self):
+        result = []
+        node = self
+        while node:
+            result.append(str(node.value))
+            node = node.next
+        return 'LinkedList({xs})'.format(xs=', '.join(result))
+
+def from_list(xs):
+    head = Node(xs[0])
+    node = head
+    for x in xs[1:]:
+        node.next = Node(x)
+        node = node.next
+    return head
+
+list = from_list(['A', 'B', 'C'])
diff --git a/users/wpcarro/scratch/facebook/london-knapsack.py b/users/wpcarro/scratch/facebook/london-knapsack.py
new file mode 100644
index 0000000000..a034fb4961
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/london-knapsack.py
@@ -0,0 +1,42 @@
+from utils import get, init_table, print_table
+
+def optimal_itinerary(duration, items):
+    min_duration = min([duration for duration, _ in items])
+    max_duration = max([duration for duration, _ in items])
+    table = init_table(rows=len(items), cols=int(max_duration / min_duration), default=0)
+    to_index = lambda duration: int(duration / min_duration) - 1
+    to_duration = lambda i: i * min_duration + min_duration
+
+    for row in range(len(table)):
+        for col in range(len(table[row])):
+            curr_duration = to_duration(col)
+            duration, value = items[row]
+            if duration > curr_duration:
+                a = 0
+            else:
+                a = value + get(table, row - 1, to_index(curr_duration - duration))
+            b = get(table, row - 1, col)
+            table[row][col] = max([a, b])
+
+        print_table(table)
+    return table[-1][-1]
+
+# You're in London for two days, and you'd like to see the following
+# attractions. How can you maximize your time spent in London?
+westminster = (0.5, 7)
+globe_theater = (0.5, 6)
+national_gallery = (1, 9)
+british_museum = (2, 9)
+st_pauls_cathedral = (0.5, 8)
+items = [
+    westminster,
+    globe_theater,
+    national_gallery,
+    british_museum,
+    st_pauls_cathedral,
+]
+result = optimal_itinerary(2, items)
+expected = 24
+print(result, expected)
+assert result == expected
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/longest-common-substring.py b/users/wpcarro/scratch/facebook/longest-common-substring.py
new file mode 100644
index 0000000000..8a838db45d
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/longest-common-substring.py
@@ -0,0 +1,20 @@
+from utils import get, init_table, print_table
+
+def longest_common_substring(a, b):
+    """
+    Computes the length of the longest string that's present in both `a` and
+    `b`.
+    """
+    table = init_table(rows=len(b), cols=len(a), default=0)
+    for row in range(len(table)):
+        for col in range(len(table[row])):
+            if b[row] == a[col]:
+                table[row][col] = 1 + get(table, row - 1, col - 1)
+    return max([max(row) for row in table])
+
+dictionary = ["fish", "vista"]
+result = [longest_common_substring("hish", x) for x in dictionary]
+expected = [3, 2]
+print(result, expected)
+assert result == expected
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/merge-sorted-arrays.py b/users/wpcarro/scratch/facebook/merge-sorted-arrays.py
new file mode 100644
index 0000000000..ae9377ad11
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/merge-sorted-arrays.py
@@ -0,0 +1,44 @@
+def merge_sorted(xs, ys):
+    result = []
+    i, j = 0, 0
+
+    while i < len(xs) and j < len(ys):
+        if xs[i] <= ys[j]:
+            result.append(xs[i])
+            i += 1
+        else:
+            result.append(ys[j])
+            j += 1
+
+    while i < len(xs):
+        result.append(xs[i])
+        i += 1
+
+    while j < len(ys):
+        result.append(ys[j])
+        j += 1
+
+    return result
+
+# A
+result = merge_sorted([3, 4, 6, 10, 11, 15], [1, 5, 8, 12, 14, 19])
+print(result)
+assert result == [1, 3, 4, 5, 6, 8, 10, 11, 12, 14, 15, 19]
+
+# B
+result = merge_sorted([], [1,2,3])
+print(result)
+assert result == [1,2,3]
+
+# C
+result = merge_sorted([1,2,3], [])
+print(result)
+assert result == [1,2,3]
+
+# D
+result = merge_sorted([], [])
+print(result)
+assert result == []
+
+# Wahoo!
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/merging-ranges.py b/users/wpcarro/scratch/facebook/merging-ranges.py
new file mode 100644
index 0000000000..6da44572ee
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/merging-ranges.py
@@ -0,0 +1,23 @@
+
+def merge(xs):
+    xs.sort()
+    result = xs[0:1]
+    for a, b in xs[1:]:
+        y, z = result[-1]
+        if a <= z:
+            result[-1] = (y, max(b, z))
+        else:
+            result.append((a, b))
+    return result
+
+inputs = [([(0,1),(3,5),(4,8),(10,12),(9,10)], [(0,1),(3,8),(9,12)]),
+          ([(1,2),(2,3)], [(1,3)]),
+          ([(1,5),(2,3)], [(1,5)]),
+          ([(1,10),(2,6),(3,5),(7,9)], [(1,10)]),
+          ]
+for x, expected in inputs:
+    result = merge(x)
+    print(x)
+    print(result)
+    assert result == expected
+    print("Success!")
diff --git a/users/wpcarro/scratch/facebook/mesh-message.py b/users/wpcarro/scratch/facebook/mesh-message.py
new file mode 100644
index 0000000000..8438b059d8
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/mesh-message.py
@@ -0,0 +1,40 @@
+from heapq import heappush, heappop
+import random
+
+def shortest_path(a, b, graph):
+    seen = set()
+    h = []
+    heappush(h, (0, a, [a]))
+    while h:
+        km, x, path = heappop(h)
+        if x == b:
+            return path
+        for c in graph[x]:
+            if c not in seen:
+                heappush(h, (km + 1, c, path + [c]))
+    raise Exception("We were unable to find a path from {} to {}".format(a, b))
+
+graph = {
+    'Min'     : ['William', 'Jayden', 'Omar'],
+    'William' : ['Min', 'Noam'],
+    'Jayden'  : ['Min', 'Amelia', 'Ren', 'Noam'],
+    'Ren'     : ['Jayden', 'Omar'],
+    'Amelia'  : ['Jayden', 'Adam', 'Miguel'],
+    'Adam'    : ['Amelia', 'Miguel', 'Sofia', 'Lucas'],
+    'Miguel'  : ['Amelia', 'Adam', 'Liam', 'Nathan'],
+    'Noam'    : ['Nathan', 'Jayden', 'William'],
+    'Omar'    : ['Ren', 'Min', 'Scott'],
+    'Liam'    : ['Ren'],
+    'Nathan'  : ['Noam'],
+    'Scott'   : [],
+}
+
+result = shortest_path('Jayden', 'Adam', graph)
+print(result)
+assert result == ['Jayden', 'Amelia', 'Adam']
+print('Success!')
+
+beg = random.choice(list(graph.keys()))
+end = random.choice(list(graph.keys()))
+print("Attempting to find the shortest path between {} and {}".format(beg, end))
+print(shortest_path(beg, end, graph))
diff --git a/users/wpcarro/scratch/facebook/moderate/decompress-xml.py b/users/wpcarro/scratch/facebook/moderate/decompress-xml.py
new file mode 100644
index 0000000000..b22983ed7a
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/moderate/decompress-xml.py
@@ -0,0 +1,98 @@
+import string
+from parser import Parser
+
+mapping = {
+    1: "family",
+    2: "person",
+    3: "firstName",
+    4: "lastName",
+    5: "state",
+}
+
+def parse_int(i, xs):
+    result = ""
+    while i < len(xs) and xs[i] in string.digits:
+        result += xs[i]
+        i += 1
+    return i, int(result)
+
+def parse_string(i, xs):
+    result = ""
+    while xs[i+1] not in string.digits:
+        result += xs[i]
+        i += 1
+    return i, result
+
+def tokenize(xs):
+    result = []
+    i = 0
+    while i < len(xs):
+        if xs[i] in string.digits:
+            i, n = parse_int(i, xs)
+            result.append(n)
+        elif xs[i] in string.ascii_letters:
+            i, x = parse_string(i, xs)
+            result.append(x)
+        elif xs[i] == " ":
+            i += 1
+            continue
+    return result
+
+def parse(xs):
+    parser = Parser(tokenize(xs))
+    return parse_element(parser)
+
+# Element   -> Tag Attribute* End Element* End ;
+# Tag       -> INTEGER ;
+# Value     -> STRING End ;
+# Attribute -> Tag Value ;
+# End       -> 0 ;
+
+def parse_element(parser):
+    if type(parser.curr()) == str:
+        return parser.consume()
+    tag_id = parser.expect_predicate(lambda x: type(x) == int)
+    tag = mapping[tag_id]
+    attrs = parse_attrs(parser)
+    parser.expect([0])
+    children = []
+    while not parser.exhausted() and parser.curr() != 0:
+        children.append(parse_element(parser))
+    parser.expect([0])
+    return [tag, attrs, children]
+
+def parse_attrs(parser):
+    result = []
+    while parser.curr() != 0:
+        tag_id = parser.expect_predicate(lambda x: type(x) == int)
+        tag = mapping[tag_id]
+        value = parser.consume()
+        result.append((tag, value))
+    return result
+
+def stringify_xml(tree, indent=0):
+    if type(tree) == str:
+        return tree
+    result = ""
+    tag, attrs, children = tree
+
+    str_attrs = []
+    for k, v in attrs:
+        str_attrs.append("{}=\"{}\"".format(k, v))
+    str_attrs = (" " if str_attrs else "") + " ".join(str_attrs)
+
+    str_children = []
+    for child in children:
+        str_children.append(" " * 2 * indent + stringify_xml(child, indent + 1))
+    str_children = "\n".join(str_children)
+
+    result += "{}<{}{}>\n{}{}\n{}</{}>".format(
+        " " * 2 * indent, tag, str_attrs, " " * 2 * indent, str_children,
+        " " * 2 * indent, tag)
+    return result
+
+x = "1 4 McDowell 5 CA 0 2 3 Gayle 0 Some Message 0 0"
+print("Input:   {}".format(x))
+print("Tokens:  {}".format(tokenize(x)))
+print("Parsed:  {}".format(parse(x)))
+print("{}".format(stringify_xml(parse(x))))
diff --git a/users/wpcarro/scratch/facebook/moderate/find-pairs-for-sum.py b/users/wpcarro/scratch/facebook/moderate/find-pairs-for-sum.py
new file mode 100644
index 0000000000..69c2fc4312
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/moderate/find-pairs-for-sum.py
@@ -0,0 +1,19 @@
+import random
+
+def find_pairs(xs, n):
+    """
+    Return all pairs of integers in `xs` that sum to `n`.
+    """
+    seeking = set()
+    result = set()
+    for x in xs:
+        if x in seeking:
+            result.add((n - x, x))
+        else:
+            seeking.add(n - x)
+    return result
+
+xs = [random.randint(1, 10) for _ in range(10)]
+n = random.randint(1, 10) + random.randint(1, 10)
+print("Seeking all pairs in {} for {}...".format(xs, n))
+print(find_pairs(xs, n))
diff --git a/users/wpcarro/scratch/facebook/moderate/parser.py b/users/wpcarro/scratch/facebook/moderate/parser.py
new file mode 100644
index 0000000000..57dfb058c0
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/moderate/parser.py
@@ -0,0 +1,37 @@
+class Parser(object):
+    def __init__(self, tokens):
+        self.tokens = tokens
+        self.i = 0
+
+    def prev(self):
+        return self.tokens[self.i - 1]
+
+    def curr(self):
+        return self.tokens[self.i]
+
+    def next(self):
+        return self.tokens[self.i + 1]
+
+    def consume(self):
+        if not self.exhausted():
+            self.i += 1
+            return self.prev()
+
+    def match(self, xs):
+        if not self.exhausted() and self.curr() in xs:
+            self.consume()
+            return True
+        return False
+
+    def expect(self, xs):
+        if not self.match(xs):
+            raise Exception("Expected token \"{}\" but received \"{}\"".format(xs, self.curr()))
+        return self.prev()
+
+    def expect_predicate(self, predicate):
+        if predicate(self.curr()):
+            return self.consume()
+        raise Exception("Expected token \"{}\" to pass predicate, but it did not".format(self.curr()))
+
+    def exhausted(self):
+        return self.i >= len(self.tokens)
diff --git a/users/wpcarro/scratch/facebook/moderate/rand7.py b/users/wpcarro/scratch/facebook/moderate/rand7.py
new file mode 100644
index 0000000000..ed3a7cea80
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/moderate/rand7.py
@@ -0,0 +1,25 @@
+# Define a function, rand7, that generates a random number [0,7), using only
+# rand5, which generates a random number [0,5).
+
+import random
+from collections import Counter
+
+# Returns [0,4]
+def rand5():
+    return random.randint(0,4)
+
+# Return [0,6]
+def rand7_a():
+    return sum(rand5() for _ in range(7)) % 7
+
+# Return [0,6]
+def rand7_b():
+    x = 5 * rand5() + rand5()
+    if x < 21:
+        return x % 7
+    return rand7_b()
+
+c = Counter([rand7_a() for _ in range(100000)])
+print(c)
+c = Counter([rand7_b() for _ in range(100000)])
+print(c)
diff --git a/users/wpcarro/scratch/facebook/moderate/tic-tac-toe-checker.py b/users/wpcarro/scratch/facebook/moderate/tic-tac-toe-checker.py
new file mode 100644
index 0000000000..342c29be6b
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/moderate/tic-tac-toe-checker.py
@@ -0,0 +1,99 @@
+import random
+
+def print_board(board):
+    result = []
+    for row in range(len(board)):
+        r = []
+        for col in range(len(board[row])):
+            cell = board[row][col]
+            if not cell:
+                r.append("-")
+            else:
+                r.append(cell)
+        result.append(" | ".join(r))
+    print("\n---------\n".join(result))
+
+def init_board():
+    result = []
+    for row in range(3):
+        r = []
+        for col in range(3):
+            r.append(None)
+        result.append(r)
+    return result
+
+def check(board, player):
+    print_board(board)
+    print()
+    if player not in "XO":
+        raise Exception("Only checking the board for Xs or Os. You supplied {}".format(player))
+    dn, ax, ddg, udg = "DOWN", "ACROSS", "DOWN_DIAGONAL", "UP_DIAGONAL"
+    ways = [
+        [[dn, ax, ddg], [dn], [dn, udg]],
+        [[ax], [], []],
+        [[ax], [], []],
+    ]
+    for row in range(len(board)):
+        for col in range(len(board[row])):
+            if board[row][col] == player:
+                xs = ways[row][col]
+                for x in xs:
+                    if x == dn:
+                        if {player} == {board[row+1][col], board[row+2][col]}:
+                            return True
+                    if x == ax:
+                        if {player} == {board[row][col+1], board[row][col+2]}:
+                            return True
+                    if x == ddg:
+                        if {player} == {board[row+1][col+1], board[row+2][col+2]}:
+                            return True
+                    if x == udg:
+                        if {player} == {board[row+1][col-1], board[row+2][col-2]}:
+                            return True
+    return False
+
+def op(player):
+    return "X" if player == "O" else "O"
+
+dn_win = lambda p: [
+    [op(p), p, None],
+    [op(p), p, None],
+    [None,  p, None],
+]
+
+ax_win = lambda p: [
+    [p, p, p],
+    [op(p), op(p), None],
+    [None, None, None],
+]
+
+ddg_win = lambda p: [
+    [p, None, None],
+    [op(p), p, None],
+    [op(p), None, p],
+]
+
+udg_win = lambda p: [
+    [op(p), None, p],
+    [op(p), p, None],
+    [p, None, None],
+]
+
+# Down
+p = random.choice(["X", "O"])
+assert check(dn_win(p), p) == True
+assert check(dn_win(p), op(p)) == False
+# Across
+p = random.choice(["X", "O"])
+assert check(ax_win(p), p) == True
+assert check(ax_win(p), op(p)) == False
+# Down Diagonally
+p = random.choice(["X", "O"])
+assert check(ddg_win(p), p) == True
+assert check(ddg_win(p), op(p)) == False
+# Down Diagonally
+p = random.choice(["X", "O"])
+assert check(udg_win(p), p) == True
+assert check(udg_win(p), op(p)) == False
+# Success
+print("Tests pass!")
diff --git a/users/wpcarro/scratch/facebook/moderate/unsorted-substring.py b/users/wpcarro/scratch/facebook/moderate/unsorted-substring.py
new file mode 100644
index 0000000000..de7326b058
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/moderate/unsorted-substring.py
@@ -0,0 +1,67 @@
+# Write a function that accepts an array of integers and returns the indices for
+# the starting and ending integers that, if their elements were sorted, the
+# entire array would be sorted.
+
+################################################################################
+# First Attempt
+################################################################################
+
+def unsorted_substring(xs):
+    ys = xs[:]; ys.sort()
+    m = 0
+    while xs[m] == ys[m]:
+        m += 1
+        if m >= len(xs):
+            return -1, -1
+    n = len(xs) - 1
+    while xs[n] == ys[n]:
+        n -= 1
+    return m, n
+
+################################################################################
+# Second Attempt
+################################################################################
+
+def unsorted_substring_2(xs):
+    beg = 1
+    while xs[beg - 1] <= xs[beg]:
+        beg += 1
+        if beg >= len(xs):
+            return -1, -1
+    end = len(xs) - 2
+    while xs[end + 1] >= xs[end]:
+        end -= 1
+
+    min_mid = xs[beg]
+    max_mid = xs[beg]
+    i = beg + 1
+    while i <= end:
+        min_mid = min(min_mid, xs[i])
+        max_mid = max(max_mid, xs[i])
+        i += 1
+
+    # beg -= 1 until max(lhs) <= min(mid)
+    while beg - 1 >= 0 and xs[beg - 1] >= min_mid:
+        beg -= 1
+
+    # end += 1 while max(mid) <= min(rhs)
+    while end + 1 < len(xs) and max_mid >= xs[end + 1]:
+        end += 1
+    return beg, end
+
+################################################################################
+# Tests
+################################################################################
+
+xs = [
+    [1,2,4,7,10,11,7,12,6,7,16,18,19],
+    [1,2,3,4],
+    [4,3,2,1],
+    [1,3,2,4],
+    [2,1,3,4],
+]
+
+for x in xs:
+    print("Testing: {}".format(x))
+    print("1) {}".format(unsorted_substring(x)))
+    print("2) {}".format(unsorted_substring_2(x)))
diff --git a/users/wpcarro/scratch/facebook/move-zeroes-to-end.py b/users/wpcarro/scratch/facebook/move-zeroes-to-end.py
new file mode 100644
index 0000000000..1535b5a9fa
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/move-zeroes-to-end.py
@@ -0,0 +1,62 @@
+from collections import deque
+
+def move_zeroes_to_end_quadratic(xs):
+    """
+    This solution is suboptimal. It runs in quadratic time, and it uses constant
+    space.
+    """
+    i = 0
+    while i < len(xs) - 1:
+        if xs[i] == 0:
+            j = i + 1
+            while j < len(xs) and xs[j] == 0:
+                j += 1
+            if j >= len(xs):
+                break
+            xs[i], xs[j] = xs[j], xs[i]
+        i += 1
+
+def move_zeroes_to_end_linear(xs):
+    """
+    This solution is clever. It runs in linear time proportionate to the number
+    of elements in `xs`, and has linear space proportionate to the number of
+    consecutive zeroes in `xs`.
+    """
+    q = deque()
+    for i in range(len(xs)):
+        if xs[i] == 0:
+            q.append(i)
+        else:
+            if q:
+                j = q.popleft()
+                xs[i], xs[j] = xs[j], xs[i]
+                q.append(i)
+
+def move_zeroes_to_end_linear_constant_space(xs):
+    """
+    This is the optimal solution. It runs in linear time and uses constant
+    space.
+    """
+    i = 0
+    for j in range(len(xs)):
+        if xs[j] != 0:
+            xs[i], xs[j] = xs[j], xs[i]
+            i += 1
+
+
+################################################################################
+# Tests
+################################################################################
+
+xss = [
+    [1, 2, 0, 3, 4, 0, 0, 5, 0],
+    [0, 1, 2, 0, 3, 4],
+    [0, 0],
+]
+
+f = move_zeroes_to_end_linear_constant_space
+
+for xs in xss:
+    print(xs)
+    f(xs)
+    print(xs)
diff --git a/users/wpcarro/scratch/facebook/mst.py b/users/wpcarro/scratch/facebook/mst.py
new file mode 100644
index 0000000000..81aa5cd487
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/mst.py
@@ -0,0 +1,71 @@
+from heapq import heappush, heappop
+import random
+
+def to_vertex_list(graph):
+    result = {}
+    for a, b, kg in graph:
+        if a in result:
+            result[a].append((b, kg))
+        else:
+            result[a] = [(b, kg)]
+        if b in result:
+            result[b].append((a, kg))
+        else:
+            result[b] = [(a, kg)]
+    return result
+
+def mst(graph):
+    graph = to_vertex_list(graph)
+    beg = random.choice(list(graph.keys()))
+    h = []
+    result = []
+    seen = set()
+    for c, kg in graph[beg]:
+        heappush(h, (kg, beg, c))
+    while h:
+        kg, beg, end = heappop(h)
+        # detect cycles
+        if end in seen:
+            continue
+        # use the edge
+        seen.add(beg)
+        seen.add(end)
+        result.append((beg, end))
+        for c, kg in graph[end]:
+            heappush(h, (kg, end, c))
+    return result
+
+graphs = [
+    [
+        ('A', 'B', 7),
+        ('A', 'D', 5),
+        ('B', 'D', 9),
+        ('E', 'D', 15),
+        ('F', 'D', 6),
+        ('F', 'G', 11),
+        ('F', 'E', 8),
+        ('G', 'E', 9),
+        ('C', 'E', 5),
+        ('B', 'E', 7),
+        ('B', 'C', 8),
+    ],
+    [
+        ('A', 'B', 4),
+        ('A', 'C', 8),
+        ('B', 'C', 11),
+        ('B', 'E', 8),
+        ('C', 'D', 7),
+        ('C', 'F', 1),
+        ('D', 'E', 2),
+        ('D', 'F', 6),
+        ('E', 'G', 7),
+        ('E', 'H', 4),
+        ('F', 'H', 2),
+        ('G', 'H', 14),
+        ('G', 'I', 9),
+        ('H', 'I', 10),
+    ],
+]
+
+for graph in graphs:
+    print(mst(graph))
diff --git a/users/wpcarro/scratch/facebook/n-queens.py b/users/wpcarro/scratch/facebook/n-queens.py
new file mode 100644
index 0000000000..fc9326886c
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/n-queens.py
@@ -0,0 +1,46 @@
+def print_board(board):
+    result = []
+    for row in range(8):
+        r = []
+        for col in range(8):
+            r.append("X" if col == board[row] else "-")
+        result.append(" ".join(r))
+    print("\n".join(result))
+    print()
+
+def can_place(board, row, col):
+    column_occupied = not any([board[i] == col for i in range(row)])
+
+    diagonals_clear = True
+    for r in range(row):
+        w = abs(col - board[r])
+        h = abs(r - row)
+        if w == h:
+            diagonals_clear = False
+            break
+
+    return all([column_occupied, diagonals_clear])
+
+def init_board():
+    board = []
+    for row in range(8):
+        board.append(None)
+    return board
+
+def copy_board(board):
+    return board[:]
+
+def n_queens():
+    do_n_queens(init_board(), 0, 0)
+
+def do_n_queens(board, row, col):
+    if row == 8:
+        print_board(board)
+        return
+    for i in range(col, 8):
+        if can_place(board, row, i):
+            copy = copy_board(board)
+            copy[row] = i
+            do_n_queens(copy, row + 1, 0)
+
+n_queens()
diff --git a/users/wpcarro/scratch/facebook/nearby-words.py b/users/wpcarro/scratch/facebook/nearby-words.py
new file mode 100644
index 0000000000..d2fc3cf5cf
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/nearby-words.py
@@ -0,0 +1,33 @@
+def nearby_chars(c):
+    keyboard = [
+        "qwertyuiop",
+        "asdfghjkl",
+        "zxcvbnm",
+    ]
+
+    for row in keyboard:
+        for i in range(len(row)):
+            if row[i] == c:
+                result = set()
+                if i + 1 < len(row):
+                    result.add(row[i + 1])
+                if i - 1 >= 0:
+                    result.add(row[i - 1])
+                return result
+
+def is_word(word):
+    words = {
+        "hello",
+    }
+    return word in words
+
+def nearby_words(x):
+    result = set()
+    for i in range(len(x)):
+        for c in nearby_chars(x[i]):
+            candidate = x[0:i] + c + x[i+1:]
+            if is_word(candidate):
+                result.add(candidate)
+    return result
+
+print(nearby_words('gello'))
diff --git a/users/wpcarro/scratch/facebook/node.py b/users/wpcarro/scratch/facebook/node.py
new file mode 100644
index 0000000000..4e24983af7
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/node.py
@@ -0,0 +1,38 @@
+class Node(object):
+    def __init__(self, value, left=None, right=None):
+        self.value = value
+        self.left = left
+        self.right = right
+
+    def insert_left(self, value):
+        self.left = Node(value)
+        return self.left
+
+    def insert_right(self, value):
+        self.right = Node(value)
+        return self.right
+
+tree = Node(
+    50,
+    Node(
+        17,
+        Node(
+            12,
+            Node(9),
+            Node(14),
+        ),
+        Node(
+            23,
+            Node(19),
+        ),
+    ),
+    Node(
+        72,
+        Node(
+            54,
+            None,
+            Node(67)
+        ),
+        Node(76),
+    ),
+)
diff --git a/users/wpcarro/scratch/facebook/nth-fibonacci.py b/users/wpcarro/scratch/facebook/nth-fibonacci.py
new file mode 100644
index 0000000000..f524067b3b
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/nth-fibonacci.py
@@ -0,0 +1,13 @@
+# 0, 1, 1, 2, 3, 5
+def fib(n):
+    if n < 0:
+        raise Exception("Need to supply an index that's >= 0. Not: {}".format(n))
+    elif n in {0, 1}:
+        return n
+    state = [0, 1]
+    for i in range(1, n):
+        state[0], state[1] = state[1], state[0] + state[1]
+    return state[-1]
+
+for i in range(10):
+    print("fib({}) => {}".format(i, fib(i)))
diff --git a/users/wpcarro/scratch/facebook/onsite.txt b/users/wpcarro/scratch/facebook/onsite.txt
new file mode 100644
index 0000000000..b5242c4bd3
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/onsite.txt
@@ -0,0 +1,22 @@
+** Behavior Interview **
+- Can I work in an unstructured environment?
+- Do I have a growth mindset?
+- How do I handle conflict?
+- Am I empathic?
+- Am I a self-starter?
+- What is my communication style?
+- Do I persevere?
+- <forgot to write this one down>
+
+** Design Interview **
+- requirement gathering, problem exploring
+- component analysis
+- quantitative analysis
+- trade-offs
+- bottlenecks, weaknesses
+- securing data (e.g. PII)
+
+Consider:
+- pagination
+- push/pull requests
+- API design
diff --git a/users/wpcarro/scratch/facebook/parsing/json.py b/users/wpcarro/scratch/facebook/parsing/json.py
new file mode 100644
index 0000000000..3975e973fe
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/parsing/json.py
@@ -0,0 +1,121 @@
+from parser import Parser
+
+# As an exercise to stress-test my understanding of recursive descent parsers,
+# I'm attempting to write a JSON parser without referencing any existing BNF
+# descriptions of JSON or existing JSON parser implementations.
+#
+# I'm only parsing a subset of JSON: enough to parse `sample`. Here is the BNF
+# that I wrote to describe my expected input:
+#
+# expression -> object
+# object     -> '{' ( STRING ':' expression ) ( ',' STRING ':' expression )* '}'
+#            |  array
+# array      -> '[' expression ( ',' expression )* ']'
+#            |  literal
+# literal    -> STRING | INT
+
+def tokenize(xs):
+    """
+    Return a list of tokens from the string input, `xs`.
+    """
+    result = []
+    i = 0
+    while i < len(xs):
+        # single characters
+        if xs[i] in ",{}:[]":
+            result.append(xs[i])
+            i += 1
+        # strings
+        elif xs[i] == "\"":
+            curr = xs[i]
+            i += 1
+            while xs[i] != "\"":
+                curr += xs[i]
+                i += 1
+            curr += xs[i]
+            result.append(curr)
+            i += 1
+        # integers
+        elif xs[i] in "0123456789":
+            curr = xs[i]
+            i += 1
+            while xs[i] in "0123456789":
+                curr += xs[i]
+                i += 1
+            result.append(int(curr))
+        # whitespace
+        elif xs[i] in {" ", "\n"}:
+            i += 1
+    return result
+
+def parse_json(x):
+    """
+    Attempt to parse the string, `x`, into JSON.
+    """
+    tokens = tokenize(x)
+    return parse_object(Parser(tokens))
+
+def parse_object(parser):
+    if parser.match(['{']):
+        key = parse_string(parser)
+        parser.expect([':'])
+        value = parse_object(parser)
+        result = [(key, value)]
+        while parser.match([',']):
+            key = parse_string(parser)
+            parser.match([':'])
+            value = parse_object(parser)
+            result.append((key, value))
+        return result
+    return parse_array(parser)
+
+def parse_array(parser):
+    if parser.match(['[']):
+        if parser.match([']']):
+            return []
+        result = [parse_object(parser)]
+        while parser.match([',']):
+            result.append(parse_object(parser))
+        parser.expect([']'])
+        return result
+    else:
+        return parse_literal(parser)
+
+def parse_string(parser):
+    if parser.curr().startswith("\""):
+        return parser.consume()
+    else:
+        raise Exception("Unexpected token: {}".format(parser.curr()))
+
+def parse_literal(parser):
+    return parser.consume()
+
+sample = """
+{
+  "glossary": {
+    "title": "example glossary",
+    "GlossDiv": {
+      "title": "S",
+      "GlossList": {
+        "GlossEntry": {
+          "ID": "SGML",
+          "SortAs": "SGML",
+          "GlossTerm": "Standard Generalized Markup Language",
+          "Acronym": "SGML",
+          "Abbrev": "ISO 8879:1986",
+          "GlossDef": {
+            "para": "A meta-markup language, used to create markup languages such as DocBook.",
+            "GlossSeeAlso": [
+              "GML",
+              "XML"
+            ]
+          },
+          "GlossSee": "markup"
+        }
+      }
+    }
+  }
+}
+"""
+
+print(parse_json(sample))
diff --git a/users/wpcarro/scratch/facebook/parsing/parser.py b/users/wpcarro/scratch/facebook/parsing/parser.py
new file mode 100644
index 0000000000..407bff61c9
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/parsing/parser.py
@@ -0,0 +1,28 @@
+class Parser(object):
+    def __init__(self, tokens):
+        self.tokens = tokens
+        self.i = 0
+
+    def prev(self):
+        return self.tokens[self.i - 1]
+
+    def curr(self):
+        return self.tokens[self.i]
+
+    def consume(self):
+        if not self.exhausted():
+            self.i += 1
+            return self.prev()
+
+    def match(self, xs):
+        if not self.exhausted() and self.curr() in xs:
+            self.consume()
+            return True
+        return False
+
+    def expect(self, xs):
+        if not self.match(xs):
+            raise Exception("Expected token \"{}\" but received \"{}\"".format(xs, self.curr()))
+
+    def exhausted(self):
+        return self.i >= len(self.tokens)
diff --git a/users/wpcarro/scratch/facebook/parsing/regex.py b/users/wpcarro/scratch/facebook/parsing/regex.py
new file mode 100644
index 0000000000..7fc2ef34e2
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/parsing/regex.py
@@ -0,0 +1,184 @@
+# Writing a small proof-of-concept...
+#   - lexer
+#   - parser
+#   - compiler
+# ...for regex.
+#
+# BNF
+# expression -> ( char_class | CHAR ) quantifier? ( "|" expression )*
+# char_class -> "[" CHAR+ "]"
+# quantifier -> "?" | "*" | "+" | "{" INT? "," INT? "}"
+#
+# Of the numerous things I do not support, here are a few items of which I'm
+# aware:
+#   - alternatives:   (a|b)
+#   - capture groups: (ab)cd
+
+from parser import Parser
+import string
+
+################################################################################
+# Top-Level API
+################################################################################
+
+def tokenize(xs):
+    """
+    Transform `xs` into a list of tokens.
+
+    Also: expand shorthand symbols using the following table:
+      - ? -> {0,1}
+      - * -> {0,}
+      - + -> {1,}
+    """
+    result = []
+    i = 0
+    shorthand = {
+        "?": ["{", 0, ",", 1, "}"],
+        "*": ["{", 0, ",", "}"],
+        "+": ["{", 1, ",", "}"],
+    }
+    while i < len(xs):
+        if xs[i] in shorthand:
+            for c in shorthand[xs[i]]:
+                result.append(c)
+            i += 1
+        elif xs[i] == "{":
+            result.append(xs[i])
+            i += 1
+            curr = ""
+            while xs[i] in string.digits:
+                curr += xs[i]
+                i += 1
+            result.append(int(curr))
+            assert xs[i] == ","
+            result.append(",")
+            i += 1
+            curr = ""
+            while xs[i] in string.digits:
+                curr += xs[i]
+                i += 1
+            result.append(int(curr))
+        else:
+            result.append(xs[i])
+            i += 1
+    return result
+
+def parse(expr):
+    """
+    Tokenize `expr` and convert it into a parse-tree.
+    """
+    tokens = tokenize(expr)
+    return parse_tokens(tokens)
+
+def compile(xs):
+    """
+    Transform `xs`, a parse-tree representing a regex, into a function that
+    accepts a string, and returns the substring that the regex matches.
+    """
+    def fn(input):
+        match = ""
+        i = 0
+        for x in xs:
+            matches, q = x[1], x[2]
+            lo, hi = q[1], q[2]
+            for j in range(lo):
+                if i < len(input) and input[i] in matches:
+                    match += input[i]
+                    i += 1
+                else:
+                    print("Failed to match {} with {}".format(input[i], matches))
+                    return None
+            if hi == float('inf'):
+                while i < len(input) and input[i] in matches:
+                    match += input[i]
+                    i += 1
+            else:
+                for j in range(hi - lo):
+                    if i < len(input) and input[i] in matches:
+                        match += input[i]
+                        i += 1
+        return match
+    return fn
+
+################################################################################
+# Helper Functions
+################################################################################
+
+def parse_tokens(tokens):
+    result = []
+    parser = Parser(tokens)
+    while not parser.exhausted():
+        result.append(parse_expression(parser))
+    return result
+
+def parse_expression(parser):
+    if parser.curr() == "[":
+        return parse_character_class(parser)
+    else:
+        return parse_character(parser)
+
+def parse_character_class(parser):
+    parser.expect("[")
+    beg = parser.consume()
+    parser.expect("-")
+    end = parser.consume()
+    parser.expect("]")
+    if parser.curr() == "{":
+        q = parse_quantifier(parser)
+    return char_class(xs=expand_range(beg, end), q=q)
+
+def parse_quantifier(parser):
+    parser.expect("{")
+    if parser.match([","]):
+        end = parser.consume()
+        parser.expect("}")
+        return quantifier(beg=0, end=end)
+    else:
+        beg = parser.consume()
+        parser.expect(",")
+        if parser.match(["}"]):
+            return quantifier(beg=beg)
+        else:
+            end = parser.consume()
+            parser.expect("}")
+            return quantifier(beg=beg, end=end)
+
+def parse_character(parser):
+    c = parser.consume()
+    q = None
+    if parser.curr() == "{":
+        q = parse_quantifier(parser)
+    return char_class(xs={c}, q=q)
+
+def char_class(xs=set(), q=None):
+    if not q:
+        q = quantifier(beg=1, end=1)
+    return ["CHARACTER_CLASS", xs, q]
+
+def expand_range(beg, end):
+    # TODO: Implement this
+    return {string.printable[i]
+            for i in range(string.printable.index(beg),
+                           string.printable.index(end) + 1)}
+
+def quantifier(beg=0, end=float('inf')):
+    return ['QUANTIFIER', beg, end]
+
+################################################################################
+# Tests
+################################################################################
+
+xs = [
+    ("[a-c]*[0-9]{2,3}", ["dog"]),
+    ("ca+t?", ["cat", "caaaat", "ca", "dog"]),
+]
+
+for re, inputs in xs:
+    print("Regex:  {}".format(re))
+    print("Tokens: {}".format(tokenize(re)))
+    print("Parsed: {}".format(parse(re)))
+    print("\nTESTS")
+    for input in inputs:
+        print("Attempting to match \"{}\"...".format(input))
+        parser = compile(parse(re))
+        print("Result: \"{}\"\n".format(parser(input)))
diff --git a/users/wpcarro/scratch/facebook/permutation-palindrome.py b/users/wpcarro/scratch/facebook/permutation-palindrome.py
new file mode 100644
index 0000000000..30603578ff
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/permutation-palindrome.py
@@ -0,0 +1,17 @@
+from collections import Counter
+
+def is_palindrome(x):
+    return len([count for _, count in Counter(x).items() if count % 2 == 1]) <= 1
+
+
+xs = [("civic", True),
+      ("ivicc", True),
+      ("civil", False),
+      ("livci", False)]
+
+for x, expected in xs:
+    result = is_palindrome(x)
+    print(x)
+    print(result)
+    assert result == expected
+    print("Success!")
diff --git a/users/wpcarro/scratch/facebook/polynomial-rolling-hash.py b/users/wpcarro/scratch/facebook/polynomial-rolling-hash.py
new file mode 100644
index 0000000000..0c7b7cb5a0
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/polynomial-rolling-hash.py
@@ -0,0 +1,72 @@
+def compute_hash(x):
+    """
+    Compute a unique fingerprint for the string input, `x`, as an integer using
+    the following equation:
+
+    x[0] * P^0 + x[1] * P^1 + ... x[n-1] * P^(n-1) % M
+
+    P and M are constants where P represents the next available prime number
+    that's GTE the number of unique characters you'll be hashing. In the case of
+    all lowercase characters, of which there are 26, the next available prime
+    number is 31.
+    """
+    p = 31
+    m = int(10e9) + 9 # large prime number
+    power = 0
+    result = 0
+    for c in x:
+        result += ord(c) * p**power
+        power += 1
+    return result % m
+
+class HashTable(object):
+    def __init__(self, size):
+        """
+        Create a hash table with `size` buckets.
+        """
+        buckets = []
+        for _ in range(size):
+            buckets.append([])
+        self.xs = buckets
+        self.compute_hash = lambda k: compute_hash(k) % size
+
+    def __repr__(self):
+        result = []
+        for bucket in self.xs:
+            for entry in bucket:
+                result.append(entry)
+        return "HashTable({})".format(",".join(str(x) for x in result))
+
+    def get(self, key):
+        """
+        Attempt to retrieve value stored under `key`.
+        """
+        h = self.compute_hash(key)
+        for k, v in self.xs[h]:
+            if k == key:
+                return v
+        return None
+
+    def put(self, key, val):
+        """
+        Set `key` to `val`; update value at `key` if it already exists.
+        """
+        h = self.compute_hash(key)
+        for i in range(len(self.xs[h])):
+            # Update entry if the key exists...
+            if self.xs[h][i][0] == key:
+                self.xs[h][i] = (key, val)
+                return None
+        # ...create a new entry otherwise
+        self.xs[h].append((key, val))
+
+    def delete(self, key):
+        """
+        Remove entry `key` from the hash table.
+        """
+        h = self.compute_hash(key)
+        for i in range(len(self.xs[h])):
+            k, v = self.xs[h][i]
+            if k == key:
+                self.xs[h].remove((k, v))
+                return
diff --git a/users/wpcarro/scratch/facebook/product-of-all-other-numbers.py b/users/wpcarro/scratch/facebook/product-of-all-other-numbers.py
new file mode 100644
index 0000000000..d381386b62
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/product-of-all-other-numbers.py
@@ -0,0 +1,33 @@
+from random import randint
+from math import floor
+
+# loop {forwards, backwards, up, down}
+# through a table of values, [[a]].
+
+def product(xs):
+    n = len(xs)
+    lhs = [1] * (n + 1)
+    for i in range(1, n):
+        lhs[i] = lhs[i - 1] * xs[i - 1]
+    rhs = [1] * (n + 1)
+    for i in range(n - 1, 0, -1):
+        rhs[i] = rhs[i + 1] * xs[i]
+    result = []
+    for i in range(n):
+        result.append(lhs[i] * rhs[i + 1])
+    return result
+
+def computed_expected(xs):
+    product = 1
+    for x in xs:
+        product *= x
+    return [floor(product / x) for x in xs]
+
+xs = [randint(1, 10) for _ in range(5)]
+expected = computed_expected(xs)
+result = product(xs)
+print(xs, result, expected)
+assert result == expected
+print("Success!")
+
+print(product([2, 4, 3, 10, 5]))
diff --git a/users/wpcarro/scratch/facebook/queue-two-stacks.py b/users/wpcarro/scratch/facebook/queue-two-stacks.py
new file mode 100644
index 0000000000..a71abeb005
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/queue-two-stacks.py
@@ -0,0 +1,20 @@
+from stack import Stack
+
+class Queue(object):
+    def __init__(self):
+        self.lhs = Stack()
+        self.rhs = Stack()
+
+    def enqueue(self, x):
+        self.rhs.push(x)
+
+    def dequeue(self, x):
+        y = self.rhs.pop()
+        while y:
+            self.lhs.push(y)
+            y = self.rhs.pop()
+        result = self.lhs.pop()
+        y = self.lhs.pop()
+        while y:
+            self.rhs.push(y)
+        return result
diff --git a/users/wpcarro/scratch/facebook/rabin-karp.py b/users/wpcarro/scratch/facebook/rabin-karp.py
new file mode 100644
index 0000000000..53a47b2783
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/rabin-karp.py
@@ -0,0 +1,27 @@
+def substring_exists(corpus, pattern):
+    """
+    Return True if `pattern` appears in `corpus`.
+
+    This function runs in O(m) time where n is equal to the length of
+    `corpus`. To improve the efficiency of this algorithm, use a hashing
+    function the reduces the number of collisions, which will consequently
+    reduce the number of string-to-string, linear comparisons.
+    """
+    m, n = len(corpus), len(pattern)
+    a = sum(ord(c) for c in corpus[0:n])
+    b = sum(ord(c) for c in pattern)
+
+    # (clumsily) prevent an off-by-one error...
+    if a == b and corpus[0:n] == pattern:
+        return True
+
+    for i in range(1, m - n):
+        # Update the hash of corpus by subtracting the hash of the character
+        # that is sliding out of view and adding the hash of the character that
+        # is sliding into view.
+        a = a - ord(corpus[i - 1]) + ord(corpus[i + n - 1])
+        # Integer comparison in O(0) time followed by string comparison in O(m)
+        # time.
+        if a == b and corpus[i:i + n] == pattern:
+            return True
+    return False
diff --git a/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/magic-index.py b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/magic-index.py
new file mode 100644
index 0000000000..03b2de015d
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/magic-index.py
@@ -0,0 +1,33 @@
+from math import floor
+
+def find_magic_index_brute(xs):
+    for i in range(len(xs)):
+        if xs[i] == i:
+            return i
+    return -1
+
+def mid(lo, hi):
+    return lo + floor((hi - lo) / 2)
+
+def find_magic_index(xs):
+    lo, hi = 0, len(xs) - 1
+    return do_find_magic_index(xs, 0, len(xs) - 1)
+
+def do_find_magic_index(xs, lo, hi):
+    pass
+
+xss = [
+    [],
+    [-1,0,2,4,5,6],
+    [1,1,1,1,1,5],
+    [-2,-2,-2,-2,4],
+    [1,2,3,4,5],
+]
+
+for xs in xss:
+    print(xs)
+    a = find_magic_index_brute(xs)
+    b = find_magic_index(xs)
+    print(a, b)
+    assert a == b
+    print("Success!")
diff --git a/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/making-change.py b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/making-change.py
new file mode 100644
index 0000000000..30c95a66c3
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/making-change.py
@@ -0,0 +1,56 @@
+# Given an infinite supply of:
+#   - quarters
+#   - dimes
+#   - nickels
+#   - pennies
+# Write a function to count the number of ways to make change of n.
+
+def get(table, row, col):
+    """
+    Defensively get cell `row`, `col` from `table`.
+    """
+    if row < 0 or row >= len(table):
+        return 0
+    if col < 0 or col >= len(table[0]):
+        return 0
+    return table[row][col]
+
+def print_table(table):
+    print('\n'.join([
+        ','.join([str(col) for col in table[row]])
+        for row in range(len(table))]))
+
+def init_table(rows=0, cols=0, default=0):
+    result = []
+    for row in range(rows):
+        r = []
+        for col in range(cols):
+            r.append(default)
+        result.append(r)
+    return result
+
+def make_change(n):
+    coins = [1, 5, 10, 25]
+    table = init_table(rows=len(coins), cols=n)
+
+    for row in range(len(table)):
+        for col in range(len(table[row])):
+            curr_coin = coins[row]
+            curr_n = col + 1
+            # a
+            a = get(table, row - 1, col)
+            # b
+            b = get(table, row, curr_n - curr_coin - 1)
+            # c
+            c = 1 if curr_coin <= curr_n else 0
+            # commit
+            if curr_coin == curr_n:
+                table[row][col] = a + c
+            else:
+                table[row][col] = a + b * c
+            # debug
+            print_table(table)
+            print()
+    return table[-1][-1]
+
+print(make_change(7))
diff --git a/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/paint-fill.py b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/paint-fill.py
new file mode 100644
index 0000000000..e9e7f6a9c1
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/paint-fill.py
@@ -0,0 +1,36 @@
+from collection import deque
+
+def fill(point, canvas, color):
+    if x not in canvas:
+        return
+    elif y not in canvas[x]:
+        return
+
+    x, y = point
+    if canvas[y][x] == color:
+        return
+    canvas[y][x] = color
+    fill((x + 1, y), canvas, color)
+    fill((x - 1, y), canvas, color)
+    fill((x, y + 1), canvas, color)
+    fill((x, y - 1), canvas, color)
+
+def fill_bfs(point, canvas, color):
+    x, y = point
+    if x not in canvas:
+        return None
+    if y not in canvas[x]:
+        return None
+    xs = deque()
+    xs.append((x, y))
+    while xs:
+        x, y = xs.popleft()
+        canvas[y][x] = color
+        for x2, y2 in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]:
+            if x2 not in canvas:
+                continue
+            elif y2 not in canvas[x2]:
+                continue
+            if canvas[y2][x2] != color:
+                xs.append((x2, y2))
+    return None
diff --git a/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/parenthesize-bools.py b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/parenthesize-bools.py
new file mode 100644
index 0000000000..f406d64e65
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/parenthesize-bools.py
@@ -0,0 +1,114 @@
+# BNF
+# expression -> bool ( ( '|' | '&' | '^' ) bool )*
+# bool       -> '0' | '1'
+
+def tokenize(xs):
+    result = []
+    for c in xs:
+        if c == '0':
+            result.append(0)
+        elif c == '1':
+            result.append(1)
+        elif c in "&|^":
+            result.append(c)
+        else:
+            raise Exception("Unexpected token, \"{}\"".format(c))
+    return result
+
+class Parser(object):
+    def __init__(self, tokens):
+        self.tokens = tokens
+        self.i = 0
+
+    def prev(self):
+        return self.tokens[self.i - 1]
+
+    def curr(self):
+        return self.tokens[self.i]
+
+    def match(self, xs):
+        if self.exhausted():
+            return False
+        if (self.curr() in xs):
+            self.consume()
+            return True
+        return False
+
+    def consume(self):
+        result = self.curr()
+        self.i += 1
+        return result
+
+    def exhausted(self):
+        return self.i >= len(self.tokens)
+
+def recursive_descent(tokens):
+    parser = Parser(tokens)
+    return parse_expression(parser)
+
+def parse_expression(parser):
+    lhs = parse_bool(parser)
+    while parser.match(['|', '&', '^']):
+        op = parser.prev()
+        rhs = parse_expression(parser)
+        lhs = [op, lhs, rhs]
+    return lhs
+
+def parse_bool(parser):
+    if parser.curr() == 0:
+        parser.consume()
+        return False
+    elif parser.curr() == 1:
+        parser.consume()
+        return True
+    else:
+        raise Exception("Unexpected token: {}".format(parser.curr()))
+
+def f(expr, result):
+    tokens = tokenize(expr)
+    tree = recursive_descent(tokens)
+    return do_f(tree, result)
+
+def do_f(tree, result):
+    if type(tree) == bool:
+        if tree == result:
+            return 1
+        else:
+            return 0
+
+    op, lhs, rhs = tree[0], tree[1], tree[2]
+    truth_tables = {
+        True: {
+            '|': [
+                (True, True),
+                (True, False),
+                (False, True),
+            ],
+            '&': [
+                (True, True),
+            ],
+            '^': [
+                (True, False),
+                (False, True),
+            ],
+        },
+        False: {
+            '|': [
+                (False, False),
+            ],
+            '&': [
+                (False, False),
+                (True, False),
+                (False, True),
+            ],
+            '^': [
+                (True, True),
+                (False, False),
+            ],
+        }
+    }
+
+    return sum([do_f(lhs, x) * do_f(rhs, y) for x, y in truth_tables[result][op]])
+
+print(f("1^0|0|1", False))
+print(f("1|0|1|1", False))
diff --git a/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/permutations.py b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/permutations.py
new file mode 100644
index 0000000000..e23972d418
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/permutations.py
@@ -0,0 +1,13 @@
+def char_and_rest(i, xs):
+    return xs[i], xs[:i] + xs[i+1:]
+
+# perms :: String -> [String]
+def perms(xs):
+    if len(xs) == 1:
+        return [xs]
+    result = []
+    for c, rest in [char_and_rest(i, xs) for i in range(len(xs))]:
+        result += [c + perm for perm in perms(rest)]
+    return result
+
+print(perms("cat"))
diff --git a/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/robot-grid-traversal.py b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/robot-grid-traversal.py
new file mode 100644
index 0000000000..9ccc08526a
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/robot-grid-traversal.py
@@ -0,0 +1,28 @@
+import random
+
+def factorial(n):
+    result = 1
+    for i in range(1, n + 1):
+        result *= i
+    return result
+
+def travel(a, b):
+    if a == b:
+        return 1
+
+    ax, ay = a
+    bx, by = b
+    if ax > bx or ay > by:
+        return 0
+
+    return sum([travel((ax + 1, ay), b), travel((ax, ay + 1), b)])
+
+def travel_compute(a, b):
+    bx, by = b
+    return int(factorial(bx + by) / (factorial(bx) * factorial(by)))
+
+a = (0, 0)
+b = (random.randint(1, 10), random.randint(1, 10))
+print("Travelling to {}, {}".format(b[0], b[1]))
+print(travel(a, b))
+print(travel_compute(a, b))
diff --git a/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/staircase.py b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/staircase.py
new file mode 100644
index 0000000000..5eb4a85606
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/staircase.py
@@ -0,0 +1 @@
+# accidentally deleted my solution... TBI (again)
diff --git a/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/subsets.py b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/subsets.py
new file mode 100644
index 0000000000..a6d26aa850
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/subsets.py
@@ -0,0 +1,41 @@
+# take-aways:
+#   - Use integers as lists of boolean values
+#   - Use 1 << n to compute 2^n where n = len(xs)
+
+def set_from_int(xs, n):
+    result = []
+    for i in range(len(xs)):
+        if n & (1 << i) != 0:
+            result.append(xs[i])
+    return result
+
+# subsets :: Set a -> List (Set a)
+def subsets(xs):
+    n = len(xs)
+    return [set_from_int(xs, i) for i in range(1 << n)]
+
+#   0 1 2
+# 0 N Y Y
+# 1 _ N Y
+# 2 _ _ N
+
+# For my interview, be able to compute *permutations* and *combinations*
+
+# This differs from permutations because this is about finding combinations...
+#
+# bottom-up
+# 0 =>        { }
+# 1 =>  {3}   {4}   {3}
+# 2 => {5,4} {5,3} {4,3}
+
+xs = [
+    ([], [[]]),
+    ([5], [[], [5]]),
+    ([5,4], [[],[5],[4],[5,4]]),
+]
+
+for x, expected in xs:
+    result = subsets(x)
+    print("subsets({}) => {} == {}".format(x, result, expected))
+    assert result == expected
+    print("Success!")
diff --git a/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/valid-parens.py b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/valid-parens.py
new file mode 100644
index 0000000000..56f2c0b274
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursion-and-dynamic-programming/valid-parens.py
@@ -0,0 +1,50 @@
+def valid_parens(n):
+    if n == 0:
+        return []
+    if n == 1:
+        return ["()"]
+
+    result = set()
+    for x in valid_parens(n - 1):
+        result.add("({})".format(x))
+        result.add("(){}".format(x))
+        result.add("{}()".format(x))
+    return result
+
+def valid_parens_efficient(n):
+    result = []
+    curr = [''] * n**2
+    do_valid_parens_efficient(result, curr, 0, n, n)
+    return result
+
+def do_valid_parens_efficient(result, curr, i, lhs, rhs):
+    if lhs == 0 and rhs == 0:
+        result.append(''.join(curr))
+    else:
+        if lhs > 0:
+            curr[i] = '('
+            do_valid_parens_efficient(result, curr, i + 1, lhs - 1, rhs)
+        if rhs > lhs:
+            curr[i] = ')'
+            do_valid_parens_efficient(result, curr, i + 1, lhs, rhs - 1)
+
+# Avoids recursion by using either a stack or a queue. I think this version is
+# easier to understand.
+def valid_parens_efficient_2(n):
+    result = []
+    xs = []
+    xs.append(('', n, n))
+    while xs:
+        curr, lhs, rhs = xs.pop()
+        print(curr)
+        if lhs == 0 and rhs == 0:
+            result.append(''.join(curr))
+        if lhs > 0:
+            xs.append((curr + '(', lhs - 1, rhs))
+        if rhs > lhs:
+            xs.append((curr + ')', lhs, rhs - 1))
+    return result
+
+# print(valid_parens(4))
+print(valid_parens_efficient(3))
+print(valid_parens_efficient_2(3))
diff --git a/users/wpcarro/scratch/facebook/recursive-string-permutations.py b/users/wpcarro/scratch/facebook/recursive-string-permutations.py
new file mode 100644
index 0000000000..e4c61eff9f
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/recursive-string-permutations.py
@@ -0,0 +1,19 @@
+# permutations: no repeat characters
+
+def char_and_rest(i, xs):
+    return xs[i], xs[0:i] + xs[i + 1:]
+
+def permutations(xs):
+    if len(xs) == 1:
+        return [xs]
+    result = []
+    for c, rest in [char_and_rest(i, xs) for i in range(len(xs))]:
+        result += [c + perm for perm in permutations(rest)]
+    return result
+
+expected = ["cat", "cta", "act", "atc", "tca", "tac"]
+result = permutations("cat")
+print(result, expected)
+assert len(result) == len(expected)
+assert result == expected
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/reverse-linked-list.py b/users/wpcarro/scratch/facebook/reverse-linked-list.py
new file mode 100644
index 0000000000..820726733f
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/reverse-linked-list.py
@@ -0,0 +1,25 @@
+from linked_list import Node
+
+def reverse(node):
+    prev, curr, next = None, node, node.next
+
+    while curr:
+        curr.next = prev
+        prev = curr
+        curr = next
+        next = curr.next if curr else None
+    return prev
+
+one = Node(1)
+two = Node(2)
+three = Node(3)
+one.next = two
+two.next = three
+
+print(one)
+result = reverse(one)
+print(result)
+assert all([result == three,
+            three.next == two,
+            two.next == one])
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/reverse-string-in-place.py b/users/wpcarro/scratch/facebook/reverse-string-in-place.py
new file mode 100644
index 0000000000..72cd6c27a3
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/reverse-string-in-place.py
@@ -0,0 +1,14 @@
+# reverse :: [Char] -> ()
+def reverse(xs):
+    i = 0
+    j = len(xs) - 1
+    while i < j:
+        xs[i], xs[j] = xs[j], xs[i]
+        i += 1
+        j -= 1
+
+xs = [list("testing"), list("a"), list("to")]
+for x in xs:
+    print(x)
+    reverse(x)
+    print(x)
diff --git a/users/wpcarro/scratch/facebook/reverse-words.py b/users/wpcarro/scratch/facebook/reverse-words.py
new file mode 100644
index 0000000000..5a38b828a3
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/reverse-words.py
@@ -0,0 +1,8 @@
+# reverse_word :: [Char] -> ()
+def reverse_words(x):
+    pass
+
+x = list("This is a test")
+print(''.join(x))
+reverse_words(x)
+print(''.join(result))
diff --git a/users/wpcarro/scratch/facebook/scratch.py b/users/wpcarro/scratch/facebook/scratch.py
new file mode 100644
index 0000000000..e772d75847
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/scratch.py
@@ -0,0 +1,94 @@
+# This is a scratch pad for randomly selected questions
+
+# def char_and_rest(i, xs):
+#     return xs[i], xs[:i] + xs[i+1:]
+
+# def perms(xs):
+#     if len(xs) == 1:
+#         return [xs]
+#     result = []
+#     for i in range(len(xs)):
+#         c, rest = char_and_rest(i, xs)
+#         for perm in perms(rest):
+#             result.append(c + ''.join(perm))
+#     return result
+
+# print(perms(list("woah")))
+
+# def f(take_out, dine_in, served):
+#     j, k = 0, 0
+#     for i in range(len(served)):
+#         if j < len(take_out) and served[i] == take_out[j]:
+#             j += 1
+#         elif k < len(dine_in) and served[i] == dine_in[k]:
+#             k += 1
+#         else:
+#             return False
+#     if j < len(take_out) or k < len(dine_in):
+#         return False
+#     return True
+
+# take_out = [17, 8, 24]
+# dine_in = [12, 19, 2]
+# served = [17, 8, 12, 19, 24, 2]
+# print(f(take_out, dine_in, served))
+
+# def match(a, b):
+#     if a == '{':
+#         return b == '}'
+#     if a == '[':
+#         return b == ']'
+#     if a == '(':
+#         return b == ')'
+#     return False
+
+# def f(xs):
+#     s = []
+#     for c in xs:
+#         if c in {'{', '[', '('}:
+#             s.append(c)
+#         elif c in {'}', ']', ')'}:
+#             opener = s.pop()
+#             if not match(opener, c):
+#                 return False
+#     return len(s) == 0
+
+# assert f("{[]()}")
+# assert f("{[(])}") == False
+# assert f("{[}") == False
+# print("Success!")
+
+# def valid_bst(node):
+#     lhs = max_bst_value(node.left) if node.left else float('-inf')
+#     rhs = min_bst_value(node.right) if node.right else float('inf')
+
+#     return and([
+#         lhs <= node.value,
+#         rhs > node.value,
+#         valid_bst(node.left),
+#         valid_bst(node.right),
+#     ])
+
+import random
+import math
+
+def shuffle(xs):
+    n = len(xs)
+    for i in range(n - 1):
+        j = random.randint(i + 1, n - 1)
+        xs[i], xs[j] = xs[j], xs[i]
+    return xs
+
+def as_card(i):
+    if i not in range(1, 53):
+        raise Exception("Not a card")
+    # 1
+    suit = ['Hearts', 'Clubs', 'Diamonds', 'Spades'][math.floor((i - 1) / 13)]
+    n = ['Ace',2,3,4,5,6,7,8,9,10,'Jack','Queen','King'][(i - 1) % 13]
+    return '{} of {}'.format(n, suit)
+
+xs = list(range(1, 53))
+print(xs)
+shuffle(xs)
+for x in xs:
+    print(as_card(x))
diff --git a/users/wpcarro/scratch/facebook/second-largest-item-in-bst.py b/users/wpcarro/scratch/facebook/second-largest-item-in-bst.py
new file mode 100644
index 0000000000..2815dec9ee
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/second-largest-item-in-bst.py
@@ -0,0 +1,22 @@
+from collections import deque
+from node import Node, tree
+
+def find_largest(node):
+    while node.right:
+        node = node.right
+    return node.value
+
+def find_second_largest(node):
+    # parent of the rightmost, when rightmost is leaf
+    # max(rightmost.left)
+    prev = None
+    while node.right:
+        prev = node
+        node = node.right
+    if node.left:
+        return find_largest(node.left)
+    else:
+        return prev.value
+
+assert find_second_largest(tree) == 72
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/shuffle.py b/users/wpcarro/scratch/facebook/shuffle.py
new file mode 100644
index 0000000000..21a6a96c60
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/shuffle.py
@@ -0,0 +1,17 @@
+from random import randint
+
+def get_random(i, j):
+    return randint(i, j)
+
+def shuffle(xs):
+    for i in range(len(xs)):
+        j = get_random(i, len(xs) - 1)
+        xs[i], xs[j] = xs[j], xs[i]
+
+xs = list(range(1, 53))
+print(xs)
+assert len(set(xs)) == 52
+shuffle(xs)
+assert len(set(xs)) == 52
+print(xs)
+print("Success!")
diff --git a/users/wpcarro/scratch/facebook/stack.py b/users/wpcarro/scratch/facebook/stack.py
new file mode 100644
index 0000000000..2a843e2216
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/stack.py
@@ -0,0 +1,25 @@
+class Stack(object):
+    def __init__(self):
+        self.items = []
+
+    def __repr__(self):
+        return self.items.__repr__()
+
+    def push(self, x):
+        self.items.append(x)
+
+    def pop(self):
+        if not self.items:
+            return None
+        return self.items.pop()
+
+    def peek(self):
+        if not self.items:
+            return None
+        return self.items[-1]
+
+def from_list(xs):
+    result = Stack()
+    for x in xs:
+        result.push(x)
+    return result
diff --git a/users/wpcarro/scratch/facebook/stacking-boxes.py b/users/wpcarro/scratch/facebook/stacking-boxes.py
new file mode 100644
index 0000000000..7a3304bc51
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/stacking-boxes.py
@@ -0,0 +1,50 @@
+from random import randint
+
+class Box(object):
+    def __init__(self, w, h, d):
+        self.width  = w
+        self.depth  = d
+        self.height = h
+
+    def __repr__(self):
+        return "{}x{}x{}".format(self.width, self.depth, self.height)
+
+    def lt(self, b):
+        return all([
+            self.width  < b.width,
+            self.height < b.height,
+            self.depth  < b.depth,
+        ])
+
+    def gt(self, b):
+        return all([
+            self.width  > b.width,
+            self.height > b.height,
+            self.depth  > b.depth,
+        ])
+
+def random_box():
+    return Box(
+        randint(1, 10),
+        randint(1, 10),
+        randint(1, 10),
+    )
+
+xs = [random_box() for _ in range(5)]
+
+def highest_stack(xs, cache={}):
+    if not xs:
+        return 0
+    heights = []
+    for i in range(len(xs)):
+        x, rest = xs[i], xs[0:i] + xs[i+1:]
+        if cache and x in cache:
+            height = cache[x]
+        else:
+            height = x.height + highest_stack([b for b in rest if x.gt(b)], cache)
+            cache[x] = height
+        heights += [height]
+    return max(heights)
+
+print(xs)
+print(highest_stack(xs))
diff --git a/users/wpcarro/scratch/facebook/stock-price.py b/users/wpcarro/scratch/facebook/stock-price.py
new file mode 100644
index 0000000000..8e42f81523
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/stock-price.py
@@ -0,0 +1,16 @@
+def max_profit(xs):
+    buy = xs[0]
+    profit = xs[1] - xs[0]
+    for price in xs[1:]:
+        profit = max(profit, price - buy)
+        buy = min(buy, price)
+    return profit
+
+xs = [([10,7,5,8,11,9], 6),
+      ([10,8,7,6,5], -1)]
+
+for x, expected in xs:
+    result = max_profit(x)
+    print(x, result)
+    assert result == expected
+    print("Success!")
diff --git a/users/wpcarro/scratch/facebook/todo.org b/users/wpcarro/scratch/facebook/todo.org
new file mode 100644
index 0000000000..6ac99267db
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/todo.org
@@ -0,0 +1,60 @@
+* Array and string manipulation
+** DONE Merging Meeting Times
+** DONE Reverse String in Place
+** TODO Reverse Words
+** DONE Merge Sorted Arrays
+** DONE Cafe Order Checker
+* Hashing and hash tables
+** DONE Inflight Entertainment
+** DONE Permutation Palindrome
+** DONE Word Cloud Data
+** DONE Top Scores
+* Greedy Algorithms
+** DONE Apple Stocks
+** DONE Highest Product of 3
+** DONE Product of All Other Numbers
+** DONE Cafe Order Checker
+** DONE In-Place Shuffle
+* Sorting, searching, and logarithms
+** DONE Find Rotation Point
+** TODO Find Repeat, Space Edition
+** DONE Top Scores
+** DONE Merging Meeting Times
+* Trees and graphs
+** DONE Balanced Binary Tree
+** DONE Binary Search Tree Checker
+** DONE 2nd Largest Item in a Binary Search Tree
+** DONE Graph Coloring
+** DONE MeshMessage
+** DONE Find Repeat, Space Edition BEAST MODE
+* Dynamic programming and recursion
+** DONE Recursive String Permutations
+** DONE Compute nth Fibonacci Number
+** DONE Making Change
+** DONE The Cake Thief
+** DONE Balanced Binary Tree
+** DONE Binary Search Tree Checker
+** DONE 2nd Largest Item in a Binary Search Tree
+* Queues and stacks
+** DONE Largest Stack
+** DONE Implement A Queue With Two Stacks
+** DONE Parenthesis Matching
+** DONE Bracket Validator
+* Linked lists
+** DONE Delete Node
+** DONE Does This Linked List Have A Cycle?
+** DONE Reverse A Linked List
+** DONE Kth to Last Node in a Singly-Linked List
+** DONE Find Repeat, Space Edition BEAST MODE
+* General programming
+** TODO Rectangular Love
+** TODO Temperature Tracker
+* Bit manipulation
+** DONE The Stolen Breakfast Drone
+* Combinatorics, probability, and other math
+** TODO Which Appears Twice
+** TODO Find in Ordered Set
+** TODO In-Place Shuffle
+** TODO Simulate 5-sided die
+** TODO Simulate 7-sided die
+** TODO Two Egg Problem
diff --git a/users/wpcarro/scratch/facebook/top-scores.py b/users/wpcarro/scratch/facebook/top-scores.py
new file mode 100644
index 0000000000..c8a10ae5f1
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/top-scores.py
@@ -0,0 +1,20 @@
+import random
+from collections import deque
+
+def sorted(xs):
+    result = [0] * 100
+    for x in xs:
+        result[x - 1] += 1
+
+    answer = deque()
+    for i in range(len(result)):
+        x = result[i]
+        for _ in range(x):
+            answer.appendleft(i + 1)
+
+    return list(answer)
+
+scores = [random.choice(range(70, 100)) for _ in range(20)]
+print(scores)
+result = sorted(scores)
+print(result)
diff --git a/users/wpcarro/scratch/facebook/topo-sort.py b/users/wpcarro/scratch/facebook/topo-sort.py
new file mode 100644
index 0000000000..874005a019
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/topo-sort.py
@@ -0,0 +1,61 @@
+import random
+from heapq import heappush, heappop
+from collections import deque
+
+# A topological sort returns the vertices of a graph sorted in an ascending
+# order by the number of incoming edges each vertex has.
+#
+# A few algorithms for solving this exist, and at the time of this writing, I
+# know none. I'm going to focus on two:
+#   1. Kahn's
+#   2. DFS (TODO)
+
+def count_in_edges(graph):
+    result = {k: 0 for k in graph.keys()}
+    for xs in graph.values():
+        for x in xs:
+            result[x] += 1
+    return result
+
+# Kahn's algorithm for returning a topological sorting of the vertices in
+# `graph`.
+def kahns_sort(graph):
+    result = []
+    q = deque()
+    in_edges = count_in_edges(graph)
+    for x in [k for k, v in in_edges.items() if v == 0]:
+        q.append(x)
+    while q:
+        x = q.popleft()
+        result.append(x)
+        for c in graph[x]:
+            in_edges[c] -= 1
+            if in_edges[c] == 0:
+                q.append(c)
+    return result
+
+graphs = [
+    {
+        0: [],
+        1: [],
+        2: [3],
+        3: [1],
+        4: [0, 1],
+        5: [0, 2],
+    },
+    {
+        'A': ['C', 'D'],
+        'B': ['D', 'E'],
+        'C': [],
+        'D': ['F', 'G'],
+        'E': [],
+        'F': [],
+        'G': ['I'],
+        'H': ['I'],
+        'I': [],
+    }
+]
+
+print("--- Kahn's --- ")
+for graph in graphs:
+    print(kahns_sort(graph))
diff --git a/users/wpcarro/scratch/facebook/traversals.py b/users/wpcarro/scratch/facebook/traversals.py
new file mode 100644
index 0000000000..e2565a3231
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/traversals.py
@@ -0,0 +1,100 @@
+from math import floor
+
+# Lists
+def cycle_backwards(times, xs):
+    n = len(xs)
+    for i in range(n * times):
+        print(xs[n - 1 - i % n])
+
+def cycle_forwards(times, xs):
+    n = len(xs)
+    for i in range(n * times):
+        print(xs[i % n])
+
+def backwards(xs):
+    n = len(xs)
+    for i in range(n):
+        print(xs[n - 1 - i])
+
+def forwards(xs):
+    for i in range(len(xs)):
+        print(xs[i])
+
+xs = [2, 5, 6, 9, 12]
+
+print("Forwards")
+forwards(xs)
+print("Backwards")
+backwards(xs)
+print("Cycle forwards")
+cycle_forwards(2, xs)
+print("Cycle backwards")
+cycle_backwards(2, xs)
+
+# Tables
+def tblr(table):
+    for row in range(len(table)):
+        for col in range(len(table[row])):
+            print(table[row][col])
+
+def tbrl(table):
+    for row in range(len(table)):
+        n = len(table[row])
+        for col in range(n):
+            print(table[row][n - 1 - col])
+
+def btlr(table):
+    n = len(table)
+    for row in range(n):
+        for col in range(len(table[row])):
+            print(table[n - 1 - row][col])
+
+def btrl(table):
+    rows = len(table)
+    for row in range(rows):
+        cols = len(table[row])
+        for col in range(cols):
+            print(table[rows - 1 - row][cols - 1 - col])
+
+def special(table):
+    rows = len(table)
+    cols = len(table[0])
+    for col in range(cols):
+        for row in range(rows):
+            print(table[row][col])
+
+def double_bonus(table):
+    rows = len(table)
+    cols = len(table[0])
+    for i in range(rows):
+        row = i
+        for col in range(cols):
+            print(table[row][col % cols])
+            row = (row + 1) % rows
+
+def free(table):
+    rows = len(table)
+    cols = len(table[0])
+    d = rows * cols
+    for i in range(d):
+        row = floor((i % d) / cols)
+        col = i % cols
+        print(table[row][col])
+
+table = [[1,2,3,4],
+         [5,6,7,8]]
+
+print("Top->Bottom, Left->Right")
+tblr(table)
+print("Top->Bottom, Right->Left")
+tbrl(table)
+print("Bottom->Top, Left->Right")
+btlr(table)
+print("Bottom->Top, Right->Left")
+btrl(table)
+print("Special")
+special(table)
+print("2x Bonus")
+double_bonus(table)
+print("Free")
+free(table)
diff --git a/users/wpcarro/scratch/facebook/utils.py b/users/wpcarro/scratch/facebook/utils.py
new file mode 100644
index 0000000000..9a3e8a045e
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/utils.py
@@ -0,0 +1,19 @@
+def init_table(rows=0, cols=0, default=None):
+    table = []
+    for row in range(rows):
+        x = []
+        for col in range(cols):
+            x.append(default)
+        table.append(x)
+    return table
+
+def get(table, row, col, default=0):
+    if row < 0 or col < 0:
+        return default
+    return table[row][col]
+
+def print_table(table):
+    result = []
+    for row in range(len(table)):
+        result.append(' '.join([str(cell) for cell in table[row]]))
+    print('\n'.join(result))
diff --git a/users/wpcarro/scratch/facebook/word-cloud.py b/users/wpcarro/scratch/facebook/word-cloud.py
new file mode 100644
index 0000000000..88422e3631
--- /dev/null
+++ b/users/wpcarro/scratch/facebook/word-cloud.py
@@ -0,0 +1,32 @@
+def normalize(x):
+    noise = ".,;-"
+    for y in noise:
+        if x.endswith(y):
+            return normalize(x[0:-1])
+        if x.startswith(y):
+            return normalize(x[1:])
+    return x.lower()
+
+def word_cloud(xs):
+    result = dict()
+
+    for x in xs.split(' '):
+        k = normalize(x)
+        if k in result:
+            result[k] += 1
+        else:
+            result[k] = 1
+
+    return result
+
+result = word_cloud("This is just the beginning. The UK will lockdown again.")
+assert result.get('this') == 1
+assert result.get('is') == 1
+assert result.get('just') == 1
+assert result.get('the') == 2
+assert result.get('beginning') == 1
+assert result.get('uk') == 1
+assert result.get('will') == 1
+assert result.get('lockdown') == 1
+assert result.get('again') == 1
+print("Success!")
diff --git a/users/wpcarro/scratch/groceries/.envrc b/users/wpcarro/scratch/groceries/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/scratch/groceries/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/scratch/groceries/export.hs b/users/wpcarro/scratch/groceries/export.hs
new file mode 100644
index 0000000000..ed43c9a3e8
--- /dev/null
+++ b/users/wpcarro/scratch/groceries/export.hs
@@ -0,0 +1,22 @@
+module Main where
+
+import qualified Data.List as L
+
+(|>) :: a -> (a -> b) -> b
+x |> f = f x
+
+-- | Ignore items with zero quantity (i.e. "0x") and comments (i.e. "#")
+isUndesirableOutput :: String -> Bool
+isUndesirableOutput x =
+  (L.isPrefixOf "- 0x" x) || (L.isPrefixOf "#" x)
+
+-- | Run this to export the grocery list.
+main :: IO ()
+main = do
+  content <- readFile "./list.org"
+  content
+    |> lines
+    |> filter (not . isUndesirableOutput)
+    |> unlines
+    |> putStrLn
+  pure ()
diff --git a/users/wpcarro/scratch/groceries/list.org b/users/wpcarro/scratch/groceries/list.org
new file mode 100644
index 0000000000..a823b2a8eb
--- /dev/null
+++ b/users/wpcarro/scratch/groceries/list.org
@@ -0,0 +1,112 @@
+# The sections are sorted such that the first section is likely the first area
+# in the grocery store you'll encounter.
+#
+# This version is written for Tesco Metro in London Bridge.
+* Beer
+- 0x beer (6x)
+* Bread
+- 0x GF bread
+- 0x flour
+- 0x GF flour
+* Produce
+- 0x brocoli
+- 0x green beans
+- 0x green asparagus
+- 2x spinach greens
+- 0x romaine lettuce head
+- 0x tomatoes
+- 0x zucchini
+- 0x lemons
+- 1x limes
+- 0x large carrot
+- 2x garlic
+- 1x green onions
+- 0x onions
+- 0x avocado
+- 0x basil plant
+- 0x jalapeno
+- 0x red pepper
+- 0x green pepper
+- 0x cherry tomatoes
+- 0x potato
+- 0x bag dry black beans
+- 1x Scotch Bonnet pepper
+* Spices
+- 0x onion powder
+- 0x garlic powder
+- 0x chicken bouillon
+- 0x oregano
+- 0x red pepper flakes
+- 0x basil plant
+- 0x cilantro plant
+* Meat
+- 0x sausages
+- 0x steak
+- 0x chicken breasts
+- 0x chicken legs
+- 0x lamb
+- 0x ground beef
+* Frozen
+- 0x Salmon
+- 0x white fish
+- 0x shrimp
+- 0x bag green beans
+- 1x bag peas
+- 0x bag corn
+* Dairy
+- 1x unsalted butter
+- 0x coconut milk
+- 2x egg cartons (12x each)
+- 2x sour cream
+- 0x cheddar cheese
+- 0x parmesan
+- 0x gouda
+- 0x random cheese
+* Pasta
+- 0x box of quinoa
+- 0x box of rice
+- 1x GF pasta
+- 0x tortellini / ravioli
+- 0x tomato sauce
+- 0x tomato paste
+- 0x can diced tomatoes
+- 0x pesto
+* Oil
+- 0x olive oil
+- 0x sesame oil
+- 0x avocado oil
+- 0x coconut oil
+- 0x white wine vinegar
+* Condiments
+- 0x red Tabasco
+- 0x green Tabasco
+- 0x habanero Tabasco
+- 0x BBQ sauce
+- 0x french mustard
+- 0x ketchup
+- 0x oyster sauce
+- 0x soy sauce
+- 0x Srirachi sauce
+* Nuts
+- 0x almonds
+- 0x walnuts
+- 0x peanuts
+- 0x cashews
+- 0x Brazil nuts
+- 0x mixed nuts
+- 0x peanuts
+- 2x peanut butter
+* Sugar
+- 0x Lindt chocolate
+* Asian
+- 0x red curry
+- 0x green curry
+- 0x coconut cream
+* Wine
+- 0x red wine
+- 0x white wine
+* Miscellaneous
+- 0x coffee beans
+- 0x tea
+- 0x AA batteries
+- 0x rubbing alcohol
diff --git a/users/wpcarro/scratch/groceries/shell.nix b/users/wpcarro/scratch/groceries/shell.nix
new file mode 100644
index 0000000000..0c6a298bf2
--- /dev/null
+++ b/users/wpcarro/scratch/groceries/shell.nix
@@ -0,0 +1,5 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.shell {
+  deps = hpkgs: [ ];
+}
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/.envrc b/users/wpcarro/scratch/haskell-programming-from-first-principles/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/.ghci b/users/wpcarro/scratch/haskell-programming-from-first-principles/.ghci
new file mode 100644
index 0000000000..12aab7f08e
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/.ghci
@@ -0,0 +1 @@
+:set prompt "> "
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/applicative.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/applicative.hs
new file mode 100644
index 0000000000..8259606da3
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/applicative.hs
@@ -0,0 +1,213 @@
+module ApplicativeScratch where
+
+import Data.Function ((&))
+
+import Control.Applicative (liftA3)
+import qualified Data.List as List
+import qualified GHC.Base as Base
+
+--------------------------------------------------------------------------------
+
+-- xs :: [(Integer, Integer)]
+-- xs = zip [1..3] [4..6]
+
+-- added :: Maybe Integer
+-- added =
+--   (+3) <$> (lookup 3 xs)
+
+--------------------------------------------------------------------------------
+
+-- y :: Maybe Integer
+-- y = lookup 3 xs
+
+-- z :: Maybe Integer
+-- z = lookup 2 xs
+
+-- tupled :: Maybe (Integer, Integer)
+-- tupled = Base.liftA2 (,) y z
+
+--------------------------------------------------------------------------------
+
+-- x :: Maybe Int
+-- x = List.elemIndex 3 [1..5]
+
+-- y :: Maybe Int
+-- y = List.elemIndex 4 [1..5]
+
+-- maxed :: Maybe Int
+-- maxed = Base.liftA2 max x y
+
+--------------------------------------------------------------------------------
+
+xs = [1..3]
+ys = [4..6]
+
+x :: Maybe Integer
+x = lookup 3 $ zip xs ys
+
+y :: Maybe Integer
+y = lookup 2 $ zip xs ys
+
+summed :: Maybe Integer
+summed = sum <$> Base.liftA2 (,) x y
+
+--------------------------------------------------------------------------------
+
+newtype Identity a = Identity a deriving (Eq, Show)
+
+instance Functor Identity where
+  fmap f (Identity x) = Identity (f x)
+
+instance Applicative Identity where
+  pure = Identity
+  (Identity f) <*> (Identity x) = Identity (f x)
+
+--------------------------------------------------------------------------------
+
+newtype Constant a b =
+  Constant { getConstant :: a }
+  deriving (Eq, Ord, Show)
+
+instance Functor (Constant a) where
+  fmap _ (Constant x) = Constant x
+
+instance Monoid a => Applicative (Constant a) where
+  pure _ = Constant mempty
+  (Constant x) <*> (Constant y) = Constant (x <> y)
+
+--------------------------------------------------------------------------------
+
+one = const <$> Just "Hello" <*> Just "World"
+
+two :: Maybe (Integer, Integer, String, [Integer])
+two = (,,,) <$> (Just 90)
+            <*> (Just 10)
+            <*> (Just "Tierness")
+            <*> (Just [1..3])
+
+--------------------------------------------------------------------------------
+
+data List a = Nil | Cons a (List a) deriving (Eq, Show)
+
+instance Semigroup (List a) where
+  Nil <> xs = xs
+  xs <> Nil = xs
+  (Cons x xs) <> ys = Cons x (xs <> ys)
+
+instance Functor List where
+  fmap f Nil = Nil
+  fmap f (Cons x xs) = Cons (f x) (fmap f xs)
+
+instance Applicative List where
+  pure x = Cons x Nil
+  Nil <*> _ = Nil
+  _ <*> Nil = Nil
+  (Cons f fs) <*> xs =
+    (f <$> xs) <> (fs <*> xs)
+
+toList :: List a -> [a]
+toList Nil = []
+toList (Cons x xs) = x : toList xs
+
+fromList :: [a] -> List a
+fromList [] = Nil
+fromList (x:xs) = Cons x (fromList xs)
+
+--------------------------------------------------------------------------------
+
+newtype ZipList' a =
+  ZipList' [a]
+  deriving (Eq, Show)
+
+-- instance Eq a => EqProp (ZipList' a) where
+--   (ZipList' lhs) =-= (ZipList' rhs) =
+--     (take 1000 lhs) `eq` (take 1000 rhs)
+
+instance Functor ZipList' where
+  fmap f (ZipList' xs) = ZipList' $ fmap f xs
+
+instance Applicative ZipList' where
+  pure x = ZipList' (repeat x)
+  (ZipList' fs) <*> (ZipList' xs) =
+    ZipList' $ zipWith ($) fs xs
+
+--------------------------------------------------------------------------------
+
+data Validation e a
+  = Failure e
+  | Success a
+  deriving (Eq, Show)
+
+instance Functor (Validation e) where
+  fmap f (Failure x) = Failure x
+  fmap f (Success x) = Success (f x)
+
+instance Monoid e => Applicative (Validation e) where
+  pure = undefined
+  (Success f) <*> (Success x) = Success (f x)
+  _ <*> (Failure x) = Failure x
+  (Failure x) <*> _ = Failure x
+
+data Error
+  = DivideByZero
+  | StackOverflow
+  deriving (Eq, Show)
+
+--------------------------------------------------------------------------------
+
+stops :: String
+stops = "pbtdkg"
+
+vowels :: String
+vowels = "aeiou"
+
+combos :: [a] -> [b] -> [c] -> [(a, b, c)]
+combos xs ys zs =
+  liftA3 (,,) xs ys zs
+
+--------------------------------------------------------------------------------
+
+data Pair a = Pair a a deriving Show
+
+instance Functor Pair where
+  fmap f (Pair x y) = Pair (f x) (f y)
+
+instance Applicative Pair where
+  pure x = Pair x x
+  (Pair f g) <*> (Pair x y) = Pair (f x) (g x)
+
+p :: Pair Integer
+p = Pair 1 2
+
+--------------------------------------------------------------------------------
+
+data Two a b = Two a b
+
+instance Functor (Two a) where
+  fmap f (Two x y) = Two x (f y)
+
+instance Monoid a => Applicative (Two a) where
+  pure x = Two mempty x
+  _ <*> _ = undefined
+
+--------------------------------------------------------------------------------
+
+data Three a b c = Three a b c
+
+instance Functor (Three a b) where
+  fmap f (Three x y z) = Three x y (f z)
+
+instance (Monoid a, Monoid b) => Applicative (Three a b) where
+  pure x = Three mempty mempty x
+  (Three a b f) <*> (Three x y z) = Three (a <> x) (b <> y) (f z)
+
+--------------------------------------------------------------------------------
+
+data Three' a b = Three' a b b
+
+instance Functor (Three' a) where
+  fmap f (Three' x y z) = Three' x (f y) (f z)
+
+instance Monoid a => Applicative (Three' a) where
+  pure x = Three' mempty x x
+  (Three' a f g) <*> (Three' x y z) = Three' (a <> x) (f y) (g z)
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/basic-libraries.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/basic-libraries.hs
new file mode 100644
index 0000000000..bb1f89987e
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/basic-libraries.hs
@@ -0,0 +1,60 @@
+module BasicLibrariesScratch where
+
+import Data.Function ((&))
+
+--------------------------------------------------------------------------------
+newtype DList a = DL { unDL :: [a] -> [a] }
+
+instance (Show a) => Show (DList a) where
+  show (DL x) = "DL " ++ show (x [])
+
+-- | Create an empty difference list.
+emptyDList :: DList a
+emptyDList = DL $ \xs -> xs
+{-# INLINE emptyDList #-}
+
+-- | Create a difference list with `x` as the only member.
+singleton :: a -> DList a
+singleton x =  DL $ \xs -> x : xs
+{-# INLINE singleton #-}
+
+-- | Convert the DList into a list.
+toList :: DList a -> [a]
+toList (DL unDL) = unDL mempty
+{-# INLINE toList #-}
+
+-- | Add an element to the end of a DList.
+infixr `snoc`
+snoc :: a -> DList a -> DList a
+snoc x (DL xs) = DL $ \ys -> xs (x : ys)
+{-# INLINE snoc #-}
+
+-- | Add an element to the beginning of a DList.
+infixr `cons`
+cons :: a -> DList a -> DList a
+cons x (DL xs) = DL $ \ys -> x : xs ys
+{-# INLINE cons #-}
+
+-- | Combine two DLists together.
+append :: DList a -> DList a -> DList a
+append (DL xs) (DL ys) = DL $ \zs -> zs & ys & xs
+{-# INLINE append #-}
+
+--------------------------------------------------------------------------------
+data Queue a =
+  Queue { one :: [a]
+        , two :: [a]
+        } deriving (Show, Eq)
+
+emptyQueue :: Queue a
+emptyQueue = Queue mempty mempty
+
+enqueue :: a -> Queue a -> Queue a
+enqueue x (Queue en de) = Queue (x:en) de
+
+dequeue :: Queue a -> Maybe (a, Queue a)
+dequeue (Queue [] []) = Nothing
+dequeue (Queue en []) =
+  let (d:de) = reverse en
+  in Just (d, Queue de [])
+dequeue (Queue en (d:de)) = Just (d, Queue en de)
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/composing-types.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/composing-types.hs
new file mode 100644
index 0000000000..378cfb7cea
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/composing-types.hs
@@ -0,0 +1,75 @@
+module ComposingTypesScratch where
+
+import Data.Function ((&))
+import Data.Bifunctor
+
+import qualified Data.Foldable as F
+
+--------------------------------------------------------------------------------
+
+newtype Identity a =
+  Identity { getIdentity :: a }
+  deriving (Eq, Show)
+
+newtype Compose f g a =
+  Compose { getCompose :: f (g a) }
+  deriving (Eq, Show)
+
+--------------------------------------------------------------------------------
+
+instance (Functor f, Functor g) => Functor (Compose f g) where
+  fmap f (Compose getCompose) = Compose $ (fmap . fmap) f getCompose
+
+instance (Applicative f, Applicative g) => Applicative (Compose f g) where
+  pure x = x & pure & pure & Compose
+  fgf <*> fga = undefined
+
+--------------------------------------------------------------------------------
+
+instance (Foldable f, Foldable g) => Foldable (Compose f g) where
+  foldMap toMonoid x = undefined
+
+instance (Traversable f, Traversable g) => Traversable (Compose f g) where
+  traverse = undefined
+
+--------------------------------------------------------------------------------
+
+data Deux a b = Deux a b deriving (Show, Eq)
+
+instance Bifunctor Deux where
+  bimap f g (Deux x y) = Deux (f x) (g y)
+
+data Const a b = Const a deriving (Show, Eq)
+
+instance Bifunctor Const where
+  bimap f _ (Const x) = Const (f x)
+
+data Drei a b c = Drei a b c deriving (Show, Eq)
+
+instance Bifunctor (Drei a) where
+  bimap f g (Drei x y z) = Drei x (f y) (g z)
+
+data SuperDrei a b c = SuperDrei a b deriving (Show, Eq)
+
+instance Bifunctor (SuperDrei a) where
+  bimap f g (SuperDrei x y) = SuperDrei x (f y)
+
+data SemiDrei a b c = SemiDrei a deriving (Show, Eq)
+
+instance Bifunctor (SemiDrei a) where
+  bimap _ _ (SemiDrei x) = SemiDrei x
+
+data Quadriceps a b c d = Quadzzz a b c d
+
+instance Bifunctor (Quadriceps a b) where
+  bimap f g (Quadzzz w x y z) = Quadzzz w x (f y) (g z)
+
+-- | Analogue for Either
+data LeftRight a b
+  = Failure a
+  | Success b
+  deriving (Show, Eq)
+
+instance Bifunctor LeftRight where
+  bimap f _ (Failure x) = Failure (f x)
+  bimap _ g (Success y) = Success (g y)
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/foldable.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/foldable.hs
new file mode 100644
index 0000000000..5b59d9e9ba
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/foldable.hs
@@ -0,0 +1,107 @@
+module FoldableScratch where
+
+import Data.Function ((&))
+
+--------------------------------------------------------------------------------
+
+sum :: (Foldable t, Num a) => t a -> a
+sum xs =
+  foldr (+) 0 xs
+
+product :: (Foldable t, Num a) => t a -> a
+product xs =
+  foldr (*) 1 xs
+
+elem :: (Foldable t, Eq a) => a -> t a -> Bool
+elem y xs =
+  foldr (\x acc -> if acc then acc else y == x) False xs
+
+minimum :: (Foldable t, Ord a) => t a -> Maybe a
+minimum xs =
+  foldr (\x acc ->
+           case acc of
+             Nothing   -> Just x
+             Just curr -> Just (min curr x)) Nothing xs
+
+maximum :: (Foldable t, Ord a) => t a -> Maybe a
+maximum xs =
+  foldr (\x acc ->
+           case acc of
+             Nothing   -> Nothing
+             Just curr -> Just (max curr x)) Nothing xs
+
+-- TODO: How could I use QuickCheck to see if Prelude.null and this null return
+-- the same results for the same inputs?
+null :: (Foldable t) => t a -> Bool
+null xs =
+  foldr (\_ _ -> False) True xs
+
+length :: (Foldable t) => t a -> Int
+length xs =
+  foldr (\_ acc -> acc + 1) 0 xs
+
+toList :: (Foldable t) => t a -> [a]
+toList xs =
+  reverse $ foldr (\x acc -> x : acc) [] xs
+
+fold :: (Foldable t, Monoid m) => t m -> m
+fold xs =
+  foldr mappend mempty xs
+
+foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
+foldMap f xs =
+  foldr (\x acc -> mappend (f x) acc) mempty xs
+
+--------------------------------------------------------------------------------
+
+data List a = Nil | Cons a (List a) deriving (Eq, Show)
+
+instance Foldable List where
+  foldr f acc (Cons x rest) = foldr f (f x acc) rest
+  foldr f acc Nil = acc
+
+fromList :: [a] -> List a
+fromList [] = Nil
+fromList (x:rest) = Cons x (fromList rest)
+
+--------------------------------------------------------------------------------
+
+data Constant a b = Constant b deriving (Eq, Show)
+
+-- TODO: Is this correct?
+instance Foldable (Constant a) where
+  foldr f acc (Constant x) = f x acc
+
+--------------------------------------------------------------------------------
+
+data Two a b = Two a b deriving (Eq, Show)
+
+instance Foldable (Two a) where
+  foldr f acc (Two x y) = f y acc
+
+--------------------------------------------------------------------------------
+
+data Three a b c = Three a b c deriving (Eq, Show)
+
+instance Foldable (Three a b) where
+  foldr f acc (Three x y z) = f z acc
+
+--------------------------------------------------------------------------------
+
+data Three' a b = Three' a b b deriving (Eq, Show)
+
+instance Foldable (Three' a) where
+  foldr f acc (Three' x y z) = acc & f z & f y
+
+--------------------------------------------------------------------------------
+
+data Four' a b = Four' a b b b deriving (Eq, Show)
+
+instance Foldable (Four' a) where
+  foldr f acc (Four' w x y z) = acc & f z & f y & f x
+
+--------------------------------------------------------------------------------
+
+filterF :: (Applicative f, Foldable t, Monoid (f a)) => (a -> Bool) -> t a -> f a
+filterF pred xs =
+  foldr (\x acc -> if pred x then pure x `mappend` acc else acc) mempty xs
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/io.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/io.hs
new file mode 100644
index 0000000000..1de8937fce
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/io.hs
@@ -0,0 +1,35 @@
+module IOScratch where
+
+import qualified System.Environment as SE
+import qualified System.IO as SIO
+--------------------------------------------------------------------------------
+
+docs :: String
+docs = "Pass -e to encrypt and -d to decrypt."
+
+encryptStdin :: IO ()
+encryptStdin = do
+  char <- SIO.hGetChar SIO.stdin
+  -- encrypt char
+  SIO.hPutStr SIO.stdout [char]
+
+decryptStdin :: IO ()
+decryptStdin = do
+  char <- SIO.hGetChar SIO.stdin
+  -- decrypt char
+  SIO.hPutStr SIO.stdout [char]
+
+main :: IO ()
+main = do
+  args <- SE.getArgs
+  case args of
+    [] ->
+      putStrLn $ "You did not pass enough arguments. " ++ docs
+    ["-e"] ->
+      encryptStdin
+    ["-d"] ->
+      decryptStdin
+    [x] ->
+      putStrLn $ "You passed an unsupported option: " ++ x ++ ". " ++ docs
+    _ ->
+      putStrLn $ "You passed too many arguments. " ++ docs
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/monad-transformers.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/monad-transformers.hs
new file mode 100644
index 0000000000..3a780fc16c
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/monad-transformers.hs
@@ -0,0 +1,183 @@
+module MonadTransformersScratch where
+
+import Control.Monad
+import qualified Control.Monad.Trans.Maybe as M
+import qualified Control.Monad.Trans.Reader as R
+import qualified Control.Monad.Trans.State as S
+import Data.Function ((&))
+--------------------------------------------------------------------------------
+
+newtype MaybeT m a =
+  MaybeT { runMaybeT :: m (Maybe a) }
+
+instance (Functor f) => Functor (MaybeT f) where
+  fmap f (MaybeT run) =
+    MaybeT $ (fmap . fmap) f run
+
+instance (Applicative m) => Applicative (MaybeT m) where
+  pure x = x & pure & pure & MaybeT
+  _ <*> _ = undefined
+
+instance (Monad m) => Monad (MaybeT m) where
+  return = pure
+  (MaybeT ma) >>= f = MaybeT $ do
+    maybeX <- ma
+    case maybeX of
+      Nothing -> pure Nothing
+      Just x -> x & f & runMaybeT
+
+--------------------------------------------------------------------------------
+
+newtype EitherT e m a =
+  EitherT { runEitherT :: m (Either e a) }
+
+instance (Functor m) => Functor (EitherT e m) where
+  fmap f (EitherT mEither) =
+    EitherT $ (fmap . fmap) f mEither
+
+instance (Applicative m) => Applicative (EitherT e m) where
+  pure x = EitherT $ (pure . pure) x
+  EitherT mEitherF <*> EitherT mEitherX =
+    EitherT $ (fmap (<*>) mEitherF) <*> mEitherX
+
+instance (Monad m) => Monad (EitherT e m) where
+  return = pure
+  EitherT mEitherX >>= f = EitherT $ do
+    eitherX <- mEitherX
+    case eitherX of
+      Left x -> pure $ Left x
+      Right x -> runEitherT $ f x
+
+swapEither :: Either l r -> Either r l
+swapEither (Left x) = Right x
+swapEither (Right x) = Left x
+
+swapEitherT :: (Functor m) => EitherT e m a -> EitherT a m e
+swapEitherT (EitherT mEitherX) =
+  EitherT $ fmap swapEither mEitherX
+
+eitherT :: Monad m => (a -> m c) -> (b -> m c) -> EitherT a m b -> m c
+eitherT aToMC bToMC (EitherT mEitherX) = do
+  eitherX <- mEitherX
+  case eitherX of
+    Left x -> aToMC x
+    Right x -> bToMC x
+
+--------------------------------------------------------------------------------
+
+newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
+
+instance (Functor m) => Functor (ReaderT r m) where
+  fmap f (ReaderT rma) =
+    ReaderT $ (fmap . fmap) f rma
+
+instance (Applicative m) => Applicative (ReaderT r m) where
+  pure x = x & pure & pure & ReaderT
+  ReaderT f <*> ReaderT x = ReaderT $ fmap (<*>) f <*> x
+
+-- instance (Monad m) => Monad (ReaderT r m) where
+--   return = pure
+--   ReaderT rma >>= f =
+--     ReaderT $ \r -> do
+--       a <- rma r
+--       runReaderT (f a) r
+-- --------------------------------------------------------------------------------
+
+rDec :: Num a => R.Reader a a
+rDec = R.ReaderT $ \x -> pure $ x + 1
+
+rShow :: Show a => R.Reader a String
+rShow = R.ReaderT $ \x -> pure $ show x
+
+rPrintAndInc :: (Num a, Show a) => R.ReaderT a IO a
+rPrintAndInc = R.ReaderT $ \x ->
+  putStrLn ("Hi: " ++ show x) >> pure (x + 1)
+
+sPrintIncAccum :: (Num a, Show a) => S.StateT a IO String
+sPrintIncAccum = S.StateT $ \x -> do
+  putStrLn ("Hi: " ++ show x)
+  pure (show x, x + 1)
+
+--------------------------------------------------------------------------------
+
+isValid :: String -> Bool
+isValid v = '!' `elem` v
+
+maybeExcite :: M.MaybeT IO String
+maybeExcite = M.MaybeT $ do
+  x <- getLine
+  putStrLn ""
+  case isValid x of
+    False -> pure Nothing
+    True -> pure $ Just x
+
+doExcite :: IO ()
+doExcite = do
+  putStr "Say something *exciting*: "
+  excite <- M.runMaybeT maybeExcite
+  case excite of
+    Nothing -> putStrLn "Gonna need some more excitement..."
+    Just x  -> putStrLn "Now THAT'S exciting...nice!"
+
+--------------------------------------------------------------------------------
+
+data Participant
+  = Man
+  | Machine
+  deriving (Show, Eq)
+
+newtype Hand = Hand (Integer, Integer) deriving (Show, Eq)
+
+newtype Score = Score (Integer, Integer) deriving (Show, Eq)
+
+getLineLn :: String -> IO String
+getLineLn prompt = do
+  putStr prompt
+  x <- getLine
+  putStrLn ""
+  pure x
+
+promptGuess :: IO Hand
+promptGuess = do
+  fingers <- getLineLn "How many fingers (0-5): "
+  guess <- getLineLn "Guess: "
+  pure $ Hand (read guess, read fingers)
+
+aiGuess :: IO Hand
+aiGuess = pure $ Hand (2, 3)
+
+whoWon :: Hand -> Hand -> Maybe Participant
+whoWon (Hand (guessA, fingersA)) (Hand (guessB, fingersB))
+  | guessA == guessB && guessA == (fingersA + fingersB) = Nothing
+  | guessA == (fingersA + fingersB) = Just Man
+  | guessB == (fingersA + fingersB) = Just Machine
+  | otherwise = Nothing
+
+initScore :: Score
+initScore = Score (0, 0)
+
+printScore :: Score -> IO ()
+printScore (Score (man, machine)) =
+  putStrLn $ "Man: " ++ show man ++ " Machine: " ++ show machine
+
+startMorra :: S.StateT Score IO ()
+startMorra = S.StateT $ \(Score (man, machine)) -> do
+  Hand (guessA, fingersA) <- promptGuess
+  Hand (guessB, fingersB) <- aiGuess
+  putStrLn $ "P: " ++ show fingersA ++ "," ++ show guessA
+  putStrLn $ "C: " ++ show fingersB ++ "," ++ show guessB
+  case whoWon (Hand (guessA, fingersA)) (Hand (guessB, fingersB)) of
+    Nothing -> do
+      putStrLn "Nobody won..."
+      printScore (Score (man, machine))
+      pure ((), Score (man, machine))
+    Just Man -> do
+      putStrLn "Man won!"
+      printScore (Score (man + 1, machine))
+      pure ((), Score (man + 1, machine))
+    Just Machine -> do
+      putStrLn "Oh no... Machine won..."
+      printScore (Score (man, machine + 1))
+      pure ((), Score (man, machine + 1))
+
+playMorra = S.runStateT (forever startMorra) initScore
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/monad.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/monad.hs
new file mode 100644
index 0000000000..2f80b457b1
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/monad.hs
@@ -0,0 +1,178 @@
+module MonadScratch where
+
+import Data.Function ((&))
+import Test.QuickCheck
+import Test.QuickCheck.Checkers
+import Control.Applicative (liftA2)
+import qualified Control.Monad as Monad
+
+--------------------------------------------------------------------------------
+
+bind :: Monad m => (a -> m b) -> m a -> m b
+bind f x = Monad.join $ fmap f x
+
+--------------------------------------------------------------------------------
+
+fTrigger :: Functor f => f (Int, String, [Int])
+fTrigger = undefined
+
+aTrigger :: Applicative a => a (Int, String, [Int])
+aTrigger = undefined
+
+mTrigger :: Monad m => m (Int, String, [Int])
+mTrigger = undefined
+
+--------------------------------------------------------------------------------
+
+data Sum a b
+  = Fst a
+  | Snd b
+  deriving (Eq, Show)
+
+instance (Eq a, Eq b) => EqProp (Sum a b) where
+  (=-=) = eq
+
+instance (Arbitrary a, Arbitrary b) => Arbitrary (Sum a b) where
+  arbitrary = frequency [ (1, Fst <$> arbitrary)
+                        , (1, Snd <$> arbitrary)
+                        ]
+
+instance Functor (Sum a) where
+  fmap f (Fst x) = Fst x
+  fmap f (Snd x) = Snd (f x)
+
+instance Applicative (Sum a) where
+  pure x = Snd x
+  (Snd f) <*> (Snd x) = Snd (f x)
+  (Snd f) <*> (Fst x) = Fst x
+  (Fst x) <*> _ = Fst x
+
+instance Monad (Sum a) where
+  (Fst x) >>= _ = Fst x
+  (Snd x) >>= f = f x
+
+--------------------------------------------------------------------------------
+
+data Nope a = NopeDotJpg deriving (Eq, Show)
+
+instance Arbitrary (Nope a) where
+  arbitrary = pure NopeDotJpg
+
+instance EqProp (Nope a) where
+  (=-=) = eq
+
+instance Functor Nope where
+  fmap f _ = NopeDotJpg
+
+instance Applicative Nope where
+  pure _ = NopeDotJpg
+  _ <*> _ = NopeDotJpg
+
+instance Monad Nope where
+  NopeDotJpg >>= f = NopeDotJpg
+
+--------------------------------------------------------------------------------
+
+data BahEither b a
+  = PLeft a
+  | PRight b
+  deriving (Eq, Show)
+
+instance (Arbitrary b, Arbitrary a) => Arbitrary (BahEither b a) where
+  arbitrary = frequency [ (1, PLeft <$> arbitrary)
+                        , (1, PRight <$> arbitrary)
+                        ]
+
+instance (Eq a, Eq b) => EqProp (BahEither a b) where
+  (=-=) = eq
+
+instance Functor (BahEither b) where
+  fmap f (PLeft x) = PLeft (f x)
+  fmap _ (PRight x) = PRight x
+
+instance Applicative (BahEither b) where
+  pure = PLeft
+  (PRight x) <*> _ = PRight x
+  (PLeft f) <*> (PLeft x) = PLeft (f x)
+  _ <*> (PRight x) = PRight x
+
+instance Monad (BahEither b) where
+  (PRight x) >>= _ = PRight x
+  (PLeft x) >>= f = f x
+
+--------------------------------------------------------------------------------
+
+newtype Identity a = Identity a
+  deriving (Eq, Ord, Show)
+
+instance Functor Identity where
+  fmap f (Identity x) = Identity (f x)
+
+instance Applicative Identity where
+  pure = Identity
+  (Identity f) <*> (Identity x) = Identity (f x)
+
+instance Monad Identity where
+  (Identity x) >>= f = f x
+
+--------------------------------------------------------------------------------
+
+data List a
+  = Nil
+  | Cons a (List a)
+  deriving (Eq, Show)
+
+instance Arbitrary a => Arbitrary (List a) where
+  arbitrary = frequency [ (1, pure Nil)
+                        , (1, Cons <$> arbitrary <*> arbitrary)
+                        ]
+
+instance Eq a => EqProp (List a) where
+  (=-=) = eq
+
+fromList :: [a] -> List a
+fromList [] = Nil
+fromList (x:xs) = Cons x (fromList xs)
+
+instance Semigroup (List a) where
+  Nil <> xs = xs
+  xs <> Nil = xs
+  (Cons x xs) <> ys =
+    Cons x (xs <> ys)
+
+instance Functor List where
+  fmap f Nil = Nil
+  fmap f (Cons x xs) = Cons (f x) (fmap f xs)
+
+instance Applicative List where
+  pure x = Cons x Nil
+  Nil <*> _ = Nil
+  _ <*> Nil = Nil
+  (Cons f fs) <*> xs =
+    (f <$> xs) <> (fs <*> xs)
+
+instance Monad List where
+  Nil >>= _ = Nil
+  (Cons x xs) >>= f = (f x) <> (xs >>= f)
+
+--------------------------------------------------------------------------------
+
+j :: Monad m => m (m a) -> m a
+j = Monad.join
+
+l1 :: Monad m => (a -> b) -> m a -> m b
+l1 = Monad.liftM
+
+l2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
+l2 = Monad.liftM2
+
+a :: Monad m => m a -> m (a -> b) -> m b
+a = flip (<*>)
+
+meh :: Monad m => [a] -> (a -> m b) -> m [b]
+meh xs f = flipType $ f <$> xs
+
+flipType :: Monad m => [m a] -> m [a]
+flipType [] = pure mempty
+flipType (m:ms) =
+  m >>= (\x -> (x:) <$> flipType ms)
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/non-strictness.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/non-strictness.hs
new file mode 100644
index 0000000000..42608fb0c9
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/non-strictness.hs
@@ -0,0 +1,6 @@
+module NonStrictnessScratch where
+
+x = undefined
+y = "blah"
+main = do
+  print $ snd (x, x `seq` y)
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/reader.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/reader.hs
new file mode 100644
index 0000000000..7cb7b4a1bb
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/reader.hs
@@ -0,0 +1,149 @@
+module Reader where
+
+import Data.Char
+import Data.Function ((&))
+import Data.Functor ((<&>))
+import qualified Control.Applicative as A
+import qualified Data.Maybe as MB
+
+cap :: String -> String
+cap xs = xs <&> toUpper
+
+rev :: String -> String
+rev = reverse
+
+compose :: String -> String
+compose xs = xs & rev . cap
+
+fmapped :: String -> String
+fmapped xs = xs & rev <$> cap
+
+tupled :: String -> (String, String)
+tupled xs = A.liftA2 (,) cap rev $ xs
+
+tupled' :: String -> (String, String)
+tupled' = do
+  capResult <- cap
+  revResult <- rev
+  pure (revResult, capResult)
+
+--------------------------------------------------------------------------------
+
+newtype Reader r a = Reader { runReader :: r -> a }
+
+ask :: Reader a a
+ask = Reader id
+
+--------------------------------------------------------------------------------
+
+newtype HumanName = HumanName String
+  deriving (Eq, Show)
+
+newtype DogName = DogName String
+  deriving (Eq, Show)
+
+newtype Address = Address String
+  deriving (Eq, Show)
+
+data Person
+  = Person
+  { humanName :: HumanName
+  , dogName :: DogName
+  , address :: Address
+  } deriving (Eq, Show)
+
+data Dog
+  = Dog
+  { dogsName :: DogName
+  , dogsAddress :: Address
+  } deriving (Eq, Show)
+
+pers :: Person
+pers =
+  Person (HumanName "Big Bird")
+         (DogName "Barkley")
+         (Address "Sesame Street")
+
+chris :: Person
+chris =
+  Person (HumanName "Chris Allen")
+         (DogName "Papu")
+         (Address "Austin")
+
+getDog :: Person -> Dog
+getDog p =
+  Dog (dogName p) (address p)
+
+getDogR :: Person -> Dog
+getDogR =
+  A.liftA2 Dog dogName address
+
+--------------------------------------------------------------------------------
+
+myLiftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
+myLiftA2 f x y =
+  f <$> x <*> y
+
+asks :: (r -> a) -> Reader r a
+asks f = Reader f
+
+--------------------------------------------------------------------------------
+
+instance Functor (Reader a) where
+  fmap f (Reader ab) = Reader $ f . ab
+
+instance Applicative (Reader a) where
+  pure x = Reader $ \_ -> x
+  (Reader rab) <*> (Reader ra) = Reader $ do
+    ab <- rab
+    fmap ab ra
+
+--------------------------------------------------------------------------------
+
+instance Monad (Reader r) where
+  return = pure
+  -- (>>=) :: Reader r a -> (a -> Reader r b) -> Reader r b
+  (Reader x) >>= f = undefined
+
+--------------------------------------------------------------------------------
+
+x = [1..3]
+y = [4..6]
+z = [7..9]
+
+xs :: Maybe Integer
+xs = zip x y & lookup 3
+
+ys :: Maybe Integer
+ys = zip y z & lookup 6
+
+zs :: Maybe Integer
+zs = zip x y & lookup 4
+
+z' :: Integer -> Maybe Integer
+z' n = zip x y & lookup n
+
+x1 :: Maybe (Integer, Integer)
+x1 = A.liftA2 (,) xs ys
+
+x2 :: Maybe (Integer, Integer)
+x2 = A.liftA2 (,) ys zs
+
+x3 :: Integer -> (Maybe Integer, Maybe Integer)
+x3 n = (z' n, z' n)
+
+summed :: Num a => (a, a) -> a
+summed (x, y) = x + y
+
+bolt :: Integer -> Bool
+bolt x = x > 3 && x < 8
+
+main :: IO ()
+main = do
+  print $ sequenceA [Just 3, Just 2, Just 1]
+  print $ sequenceA [x, y]
+  print $ sequenceA [xs, ys]
+  print $ summed <$> ((,) <$> xs <*> ys)
+  print $ bolt 7
+  print $ bolt <$> z
+  print $ sequenceA [(>3), (<8) ,even] 7
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/shell.nix b/users/wpcarro/scratch/haskell-programming-from-first-principles/shell.nix
new file mode 100644
index 0000000000..49dbe746d3
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/shell.nix
@@ -0,0 +1,8 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.shell {
+  deps = hpkgs: with hpkgs; [
+    quickcheck-simple
+    checkers
+  ];
+}
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/state.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/state.hs
new file mode 100644
index 0000000000..f63e0ecdf1
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/state.hs
@@ -0,0 +1,93 @@
+module StateScratch where
+
+--------------------------------------------------------------------------------
+import System.Random
+-- import Control.Monad.Trans.State
+import Data.Function ((&))
+
+import qualified Control.Applicative as Ap
+import qualified Control.Monad as M
+--------------------------------------------------------------------------------
+
+data Die
+  = DieOne
+  | DieTwo
+  | DieThree
+  | DieFour
+  | DieFive
+  | DieSix
+  deriving (Eq, Show)
+
+intToDie :: Integer -> Maybe Die
+intToDie 1 = Just DieOne
+intToDie 2 = Just DieTwo
+intToDie 3 = Just DieThree
+intToDie 4 = Just DieFour
+intToDie 5 = Just DieFive
+intToDie 6 = Just DieSix
+intToDie _ = Nothing
+
+rollDie :: Moi StdGen Die
+rollDie = do
+  (n, s) <- randomR (1, 6)
+  case intToDie n of
+    Just d  -> pure (d, s)
+    Nothing -> pure (DieOne, s)
+
+rollsToGetN :: Integer -> StdGen -> [Die]
+rollsToGetN n g = go 0 [] g
+  where
+    go sum result gen
+      | sum >= n = result
+      | otherwise =
+        let (dice, nextGen) = randomR (1, 6) gen
+        in case intToDie dice of
+          Nothing -> go (sum + dice) result nextGen
+          Just d  -> go (sum + dice) (d : result) nextGen
+
+--------------------------------------------------------------------------------
+
+newtype Moi s a = Moi { runMoi :: s -> (a, s) }
+
+instance Functor (Moi s) where
+  fmap f (Moi run) =
+    Moi $ \s -> let (x, t) = run s
+                in (f x, t)
+
+instance Applicative (Moi s) where
+  pure x = Moi $ \s -> (x, s)
+  (Moi f) <*> (Moi run) =
+    Moi $ \s -> let (g, t) = f s
+                    (x, u) = run t
+                in (g x, u)
+
+instance Monad (Moi s) where
+  (Moi run1) >>= f =
+    Moi $ \s -> let (x, t) = run1 s
+                    (Moi run2) = f x
+                in run2 t
+
+--------------------------------------------------------------------------------
+
+fizzBuzz :: Integer -> String
+fizzBuzz n | n `mod` 15 == 0 = "FizzBuzz"
+           | n `mod`  5 == 0 = "Buzz"
+           | n `mod`  3 == 0 = "Fizz"
+           | otherwise       = show n
+
+--------------------------------------------------------------------------------
+
+get :: Moi s s
+get = Moi $ \s -> (s, s)
+
+put :: s -> Moi s ()
+put x = Moi $ \s -> ((), x)
+
+exec :: Moi s a -> s -> s
+exec (Moi run) x = x & run & snd
+
+eval :: Moi s a -> s -> a
+eval (Moi run) x = x & run & fst
+
+modify :: (s -> s) -> Moi s ()
+modify f = Moi $ \s -> ((), f s)
diff --git a/users/wpcarro/scratch/haskell-programming-from-first-principles/traversable.hs b/users/wpcarro/scratch/haskell-programming-from-first-principles/traversable.hs
new file mode 100644
index 0000000000..5dc4ea411b
--- /dev/null
+++ b/users/wpcarro/scratch/haskell-programming-from-first-principles/traversable.hs
@@ -0,0 +1,131 @@
+module TraversableScratch where
+
+import qualified Data.Foldable as F
+
+import Test.QuickCheck
+
+newtype Identity a = Identity a
+  deriving (Eq, Ord, Show)
+
+instance Functor Identity where
+  fmap f (Identity x) = Identity (f x)
+
+instance Foldable Identity where
+  foldMap f (Identity x) = f x
+
+instance Traversable Identity where
+  traverse f (Identity x) = Identity <$> f x
+
+--------------------------------------------------------------------------------
+
+data Optional a
+  = Nada
+  | Some a
+  deriving (Eq, Show)
+
+instance Functor Optional where
+  fmap f Nada = Nada
+  fmap f (Some x) = Some (f x)
+
+instance Foldable Optional where
+  foldMap f Nada = mempty
+  foldMap f (Some x) = f x
+
+instance Traversable Optional where
+  traverse f Nada = pure Nada
+  traverse f (Some x) = Some <$> f x
+
+--------------------------------------------------------------------------------
+
+data List a = Nil | Cons a (List a) deriving (Eq, Show)
+
+instance Functor List where
+  fmap _ Nil = Nil
+  fmap f (Cons x xs) = Cons (f x) (fmap f xs)
+
+instance Foldable List where
+  foldMap f Nil = mempty
+  foldMap f (Cons x xs) = mappend (f x) (foldMap f xs)
+
+instance Traversable List where
+  sequenceA Nil = pure Nil
+  sequenceA (Cons x xs) = Cons <$> x <*> sequenceA xs
+
+--------------------------------------------------------------------------------
+
+data Three a b c = Three a b c
+  deriving (Eq, Show)
+
+instance Functor (Three a b) where
+  fmap f (Three x y z) = Three x y (f z)
+
+instance Foldable (Three a b) where
+  foldMap f (Three _ _ z) = f z
+
+instance Traversable (Three a b) where
+  sequenceA (Three x y z) = (\z' -> Three x y z') <$> z
+
+--------------------------------------------------------------------------------
+
+data Pair a b = Pair a b
+  deriving (Eq, Show)
+
+instance Functor (Pair a) where
+  fmap f (Pair x y) = Pair x (f y)
+
+instance Foldable (Pair a) where
+  foldMap f (Pair x y) = f y
+
+instance Traversable (Pair a) where
+  sequenceA (Pair x y) = (\y' -> Pair x y') <$> y
+
+--------------------------------------------------------------------------------
+
+data Big a b = Big a b b
+  deriving (Eq, Show)
+
+instance Functor (Big a) where
+  fmap f (Big x y z) = Big x (f y) (f z)
+
+instance Foldable (Big a) where
+  foldMap f (Big x y z) = f y <> f z
+
+instance Traversable (Big a) where
+  sequenceA (Big x y z) = (\y' z' -> Big x y' z') <$> y <*> z
+
+--------------------------------------------------------------------------------
+
+data Bigger a b = Bigger a b b b
+  deriving (Eq, Show)
+
+instance Functor (Bigger a) where
+  fmap f (Bigger w x y z) = Bigger w (f x) (f y) (f z)
+
+instance Foldable (Bigger a) where
+  foldMap f (Bigger w x y z) = f x <> f y <> f z
+
+instance Traversable (Bigger a) where
+  sequenceA (Bigger w x y z) = (\x' y' z' -> Bigger w x' y' z') <$> x <*> y <*> z
+
+--------------------------------------------------------------------------------
+
+data Tree a
+  = Empty
+  | Leaf a
+  | Node (Tree a) a (Tree a)
+  deriving (Eq, Show)
+
+instance Functor Tree where
+  fmap f Empty = Empty
+  fmap f (Leaf x) = Leaf (f x)
+  fmap f (Node lhs x rhs) = Node (fmap f lhs) (f x) (fmap f rhs)
+
+instance Foldable Tree where
+  foldMap f Empty = mempty
+  foldMap f (Leaf x) = f x
+  foldMap f (Node lhs x rhs) = (foldMap f lhs) <> (f x) <> (foldMap f rhs)
+
+instance Traversable Tree where
+  sequenceA Empty = pure Empty
+  sequenceA (Leaf x) = Leaf <$> x
+  sequenceA (Node lhs x rhs) = Node <$> sequenceA lhs <*> x <*> sequenceA rhs
diff --git a/users/wpcarro/scratch/picoctf/.skip-subtree b/users/wpcarro/scratch/picoctf/.skip-subtree
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/users/wpcarro/scratch/picoctf/.skip-subtree
diff --git a/users/wpcarro/scratch/picoctf/README.md b/users/wpcarro/scratch/picoctf/README.md
new file mode 100644
index 0000000000..03a49817f7
--- /dev/null
+++ b/users/wpcarro/scratch/picoctf/README.md
@@ -0,0 +1,3 @@
+# picoCTF
+
+My solutions for some of the questsions at https://play.picoctf.org/practice.
diff --git a/users/wpcarro/scratch/picoctf/challenge_144.py b/users/wpcarro/scratch/picoctf/challenge_144.py
new file mode 100644
index 0000000000..570a7fd5a7
--- /dev/null
+++ b/users/wpcarro/scratch/picoctf/challenge_144.py
@@ -0,0 +1,11 @@
+def rotate_alpha(x, n):
+    def rotate_char(c, n):
+        offset = 'A' if c.isupper() else 'a'
+        return chr((ord(c) - ord(offset) + n) % 26 + ord(offset))
+    return "".join([rotate_char(c, n) if c.isalpha() else c for c in x])
+
+xs = [
+    "cvpbPGS{arkg_gvzr_V'yy_gel_2_ebhaqf_bs_ebg13_Ncualgvd}",
+]
+for x in xs:
+    print(rotate_alpha(x, 13))
diff --git a/users/wpcarro/scratch/picoctf/challenge_156.py b/users/wpcarro/scratch/picoctf/challenge_156.py
new file mode 100644
index 0000000000..8c87a1ce76
--- /dev/null
+++ b/users/wpcarro/scratch/picoctf/challenge_156.py
@@ -0,0 +1,13 @@
+bytestring = [
+    112, 105, 99, 111, 67, 84, 70, 123, 103, 48, 48, 100, 95, 107, 49, 116,
+    116, 121, 33, 95, 110, 49, 99, 51, 95, 107, 49, 116, 116, 121, 33, 95, 57,
+    98, 51, 98, 55, 51, 57, 50, 125, 10,
+]
+
+def decode(xs):
+    result = []
+    for x in xs:
+        result.append(chr(x))
+    return "".join(result)
+
+print(decode(bytestring))
diff --git a/users/wpcarro/scratch/picoctf/challenge_166/ende.py b/users/wpcarro/scratch/picoctf/challenge_166/ende.py
new file mode 100644
index 0000000000..08395f9209
--- /dev/null
+++ b/users/wpcarro/scratch/picoctf/challenge_166/ende.py
@@ -0,0 +1,60 @@
+
+import sys
+import base64
+from cryptography.fernet import Fernet
+
+
+
+usage_msg = "Usage: "+ sys.argv[0] +" (-e/-d) [file]"
+help_msg = usage_msg + "\n" +\
+        "Examples:\n" +\
+        "  To decrypt a file named 'pole.txt', do: " +\
+        "'$ python "+ sys.argv[0] +" -d pole.txt'\n"
+
+
+
+if len(sys.argv) < 2 or len(sys.argv) > 4:
+    print(usage_msg)
+    sys.exit(1)
+
+
+
+if sys.argv[1] == "-e":
+    if len(sys.argv) < 4:
+        sim_sala_bim = input("Please enter the password:")
+    else:
+        sim_sala_bim = sys.argv[3]
+
+    ssb_b64 = base64.b64encode(sim_sala_bim.encode())
+    c = Fernet(ssb_b64)
+
+    with open(sys.argv[2], "rb") as f:
+        data = f.read()
+        data_c = c.encrypt(data)
+        sys.stdout.write(data_c.decode())
+
+
+elif sys.argv[1] == "-d":
+    if len(sys.argv) < 4:
+        sim_sala_bim = input("Please enter the password:")
+    else:
+        sim_sala_bim = sys.argv[3]
+
+    ssb_b64 = base64.b64encode(sim_sala_bim.encode())
+    c = Fernet(ssb_b64)
+
+    with open(sys.argv[2], "r") as f:
+        data = f.read()
+        data_c = c.decrypt(data.encode())
+        sys.stdout.buffer.write(data_c)
+
+
+elif sys.argv[1] == "-h" or sys.argv[1] == "--help":
+    print(help_msg)
+    sys.exit(1)
+
+
+else:
+    print("Unrecognized first argument: "+ sys.argv[1])
+    print("Please use '-e', '-d', or '-h'.")
+
diff --git a/users/wpcarro/scratch/picoctf/challenge_166/flag.txt.en b/users/wpcarro/scratch/picoctf/challenge_166/flag.txt.en
new file mode 100644
index 0000000000..1c4d245811
--- /dev/null
+++ b/users/wpcarro/scratch/picoctf/challenge_166/flag.txt.en
@@ -0,0 +1 @@
+gAAAAABgUAIWsYfVayn4m1dKle5X91HrZW_MIRAW4ILPgf4gD6jalLF4PysYB5_YTpDwclcQPqw_0xTxanpJ_Urx5Vi6mTeBA_rWPA_WQLvVXXHp1mG3EpOgY8Na1_NIAfc9LceH_L2o
\ No newline at end of file
diff --git a/users/wpcarro/scratch/picoctf/challenge_166/pw.txt b/users/wpcarro/scratch/picoctf/challenge_166/pw.txt
new file mode 100644
index 0000000000..a4c1c7ae66
--- /dev/null
+++ b/users/wpcarro/scratch/picoctf/challenge_166/pw.txt
@@ -0,0 +1 @@
+67c6cc9667c6cc9667c6cc9667c6cc96
diff --git a/users/wpcarro/scratch/picoctf/challenge_166/shell.nix b/users/wpcarro/scratch/picoctf/challenge_166/shell.nix
new file mode 100644
index 0000000000..85d3865a51
--- /dev/null
+++ b/users/wpcarro/scratch/picoctf/challenge_166/shell.nix
@@ -0,0 +1,8 @@
+{ pkgs, ... }:
+
+let
+  python = pkgs.python3.withPackages (pypkgs: with pypkgs; [
+    cryptography
+  ]);
+in
+python.env
diff --git a/users/wpcarro/scratch/picoctf/challenge_170/README.md b/users/wpcarro/scratch/picoctf/challenge_170/README.md
new file mode 100644
index 0000000000..2507208f5c
--- /dev/null
+++ b/users/wpcarro/scratch/picoctf/challenge_170/README.md
@@ -0,0 +1,11 @@
+# challenge 170
+
+The following should work on most Linux distros, but it didn't for me on NixOS:
+
+```shell
+chmod u+x ./warm
+./warm -h
+```
+
+So instead, just call `strings` on the exectuable to search for the help text,
+which contains the flag.
diff --git a/users/wpcarro/scratch/rust/.gitignore b/users/wpcarro/scratch/rust/.gitignore
new file mode 100644
index 0000000000..9f970225ad
--- /dev/null
+++ b/users/wpcarro/scratch/rust/.gitignore
@@ -0,0 +1 @@
+target/
\ No newline at end of file
diff --git a/users/wpcarro/scratch/rust/Cargo.lock b/users/wpcarro/scratch/rust/Cargo.lock
new file mode 100644
index 0000000000..28aa1250ce
--- /dev/null
+++ b/users/wpcarro/scratch/rust/Cargo.lock
@@ -0,0 +1,89 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "itoa"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rust"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
+
+[[package]]
+name = "serde"
+version = "1.0.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.99"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
diff --git a/users/wpcarro/scratch/rust/Cargo.toml b/users/wpcarro/scratch/rust/Cargo.toml
new file mode 100644
index 0000000000..76235d11d3
--- /dev/null
+++ b/users/wpcarro/scratch/rust/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "rust"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+serde_json = "1.0.81"
+serde = { version = "1.0.137", features = ["derive"] }
diff --git a/users/wpcarro/scratch/rust/README.md b/users/wpcarro/scratch/rust/README.md
new file mode 100644
index 0000000000..9ff7dd97ea
--- /dev/null
+++ b/users/wpcarro/scratch/rust/README.md
@@ -0,0 +1,11 @@
+# Rust
+
+Watch me fumble around as I learn Rust.
+
+## Usage
+
+```shell
+$ nix-shell /depot -A users.wpcarro.scratch.rust
+$ cargo new json && cd ./json
+$ cargo run json
+```
diff --git a/users/wpcarro/scratch/rust/shell.nix b/users/wpcarro/scratch/rust/shell.nix
new file mode 100644
index 0000000000..98e2dbf4b2
--- /dev/null
+++ b/users/wpcarro/scratch/rust/shell.nix
@@ -0,0 +1,7 @@
+{ pkgs ? import <nixpkgs> { }, ... }:
+
+pkgs.mkShell {
+  buildInputs = [
+    pkgs.cargo
+  ];
+}
diff --git a/users/wpcarro/scratch/rust/src/display/mod.rs b/users/wpcarro/scratch/rust/src/display/mod.rs
new file mode 100644
index 0000000000..8384631091
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/display/mod.rs
@@ -0,0 +1,13 @@
+use std::fmt;
+
+pub struct Person {
+    pub fname: String,
+    pub lname: String,
+    pub age: i8,
+}
+
+impl fmt::Display for Person {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}, {} ({} years old)", self.lname, self.fname, self.age)
+    }
+}
diff --git a/users/wpcarro/scratch/rust/src/json/mod.rs b/users/wpcarro/scratch/rust/src/json/mod.rs
new file mode 100644
index 0000000000..d3307b394e
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/json/mod.rs
@@ -0,0 +1,81 @@
+use serde::{Deserialize, Serialize};
+use serde_json::{json, Value};
+
+// From the serde_json docs:
+//
+// > There are three common ways that you might find yourself needing to work
+// > with JSON data in Rust.
+// >
+// > 1. As text data. An unprocessed string of JSON data that you receive on an
+// >    HTTP endpoint, read from a file, or prepare to send to a remote server.
+// > 2. As an untyped or loosely typed representation. Maybe you want to check
+// >    that some JSON data is valid before passing it on, but without knowing
+// >    the structure of what it contains. Or you want to do very basic
+// >    manipulations like insert a key in a particular spot.
+// > 3. As a strongly typed Rust data structure. When you expect all or most of
+// >    your data to conform to a particular structure and want to get real work
+// >    done without JSONโ€™s loosey-goosey nature tripping you up.
+//
+// So let's take a look at all three...
+
+////////////////////////////////////////////////////////////////////////////////
+// Types
+////////////////////////////////////////////////////////////////////////////////
+
+#[derive(Serialize, Deserialize, Debug)]
+struct Person {
+    fname: String,
+    lname: String,
+    age: u8,
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Functions
+////////////////////////////////////////////////////////////////////////////////
+
+// 1) Reading/writing from/to plain text.
+//    TL;DR:
+//    - read:  serde_json::from_str(data)
+//    - write: x.to_string()
+pub fn one() {
+    let data = json!({
+        "fname": "William",
+        "lname": "Carroll",
+        "age": 30,
+    })
+    .to_string();
+
+    println!("result: {:?}", data);
+}
+
+// 2) Parse into a loosely typed representation; mutate it; serialize it back.
+//    TL;DR:
+//    - read:  serde_json::from_str(data)
+//    - write: x.to_string()
+pub fn two() {
+    let data = r#"{"fname":"William","lname":"Carroll","age":30}"#;
+
+    let mut parsed: Value = serde_json::from_str(data).unwrap();
+    parsed["fname"] = json!("Norm");
+    parsed["lname"] = json!("Macdonald");
+    parsed["age"] = json!(61);
+
+    let result = parsed.to_string();
+    println!("result: {:?}", result);
+}
+
+// 3) Parse into a strongly typed structure.
+//    TL;DR:
+//    - read:  serde_json::from_str(data)
+//    - write: serde_json::to_string(x).unwrap()
+pub fn three() {
+    let data = r#"{"fname":"William","lname":"Carroll","age":30}"#;
+
+    let mut read: Person = serde_json::from_str(data).unwrap();
+    read.fname = "Norm".to_string();
+    read.lname = "Macdonald".to_string();
+    read.age = 61;
+
+    let write = serde_json::to_string(&read).unwrap();
+    println!("result: {:?}", write);
+}
diff --git a/users/wpcarro/scratch/rust/src/main.rs b/users/wpcarro/scratch/rust/src/main.rs
new file mode 100644
index 0000000000..671b330930
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/main.rs
@@ -0,0 +1,15 @@
+use serde::{Deserialize, Serialize};
+use serde_json::{json, Value};
+
+mod display;
+mod json;
+mod rc;
+mod stdin;
+
+////////////////////////////////////////////////////////////////////////////////
+// Main
+////////////////////////////////////////////////////////////////////////////////
+
+fn main() {
+    rc::example();
+}
diff --git a/users/wpcarro/scratch/rust/src/rc/mod.rs b/users/wpcarro/scratch/rust/src/rc/mod.rs
new file mode 100644
index 0000000000..67251ca6aa
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/rc/mod.rs
@@ -0,0 +1,12 @@
+// Playing around with Rust's "smart pointers". Starting off with a wrapper type
+// that allows multiple readers (owners?) of some data.
+
+use std::rc::Rc;
+
+pub fn example() {
+    let five = Rc::new(5);
+    let x = Rc::clone(&five);
+    let y = Rc::clone(&five);
+    let z = Rc::clone(&five);
+    println!("result: {}", *x + *y + *z)
+}
diff --git a/users/wpcarro/scratch/rust/src/stdin/mod.rs b/users/wpcarro/scratch/rust/src/stdin/mod.rs
new file mode 100644
index 0000000000..4be95afa45
--- /dev/null
+++ b/users/wpcarro/scratch/rust/src/stdin/mod.rs
@@ -0,0 +1,22 @@
+use std::io::Write;
+use std::process::{Command, Stdio};
+
+// Example of piping-in a string defined in Rust to a shell command.
+pub fn example() {
+    let input = "Hello, world!";
+
+    let mut cat = Command::new("cat")
+        .stdin(Stdio::piped())
+        .spawn()
+        .ok()
+        .unwrap();
+
+    cat.stdin
+        .take()
+        .unwrap()
+        .write_all(&input.as_bytes())
+        .unwrap();
+
+    let output = cat.wait_with_output().unwrap();
+    println!("{}", String::from_utf8_lossy(&output.stdout));
+}
diff --git a/users/wpcarro/scratch/simple-select/README.md b/users/wpcarro/scratch/simple-select/README.md
new file mode 100644
index 0000000000..69e5707302
--- /dev/null
+++ b/users/wpcarro/scratch/simple-select/README.md
@@ -0,0 +1,71 @@
+# Simple Select
+
+- Simple Select is a less expressive but more ergonomic query language for
+  tabular data than SQL.
+- `slx` is a command-line tool for querying CSVs using the Simple Select query
+  language.
+
+Simple Select queries look like this: `director:"Tarantino" OR director:"Scorsese"`.
+
+## Example
+
+Say we have the following data in a CSV:
+
+```csv
+title,year,rating,director
+"Spirited Away",2001,8.5,"Hayao Miyazaki"
+Andhadhun,2018,8.1,"Sriram Raghavan"
+Dangal,2016,8.3,"Sriram Raghavan"
+"Avengers: Infinity War",2019,8.4,"Anthony Russo"
+Alien,1979,8.4,"Ridley Scott"
+...
+```
+
+We can invoke `slx` like so...
+
+```
+$ slx -f /tmp/movies.csv
+```
+
+...and then query using the REPL:
+
+```
+> director:/S.*m/ OR director:"Hayao"
+Andhadhun       2018    8.1     1       Sriram Raghavan 0       1
+Dangal  2016    8.3     1       Sriram Raghavan 0       1
+Howls Moving Castle     2004    8.2     0       Hayao Miyazaki  1       1
+Judgment at Nuremberg   1961    8.1     0       Stanley Kramer  0       0
+Laputa: Castle in the Sky       1986    8.0     0       Hayao Miyazaki  1       1
+Nausicaa of the Valley of the Wind      1984    8.0     0       Hayao Miyazaki  1       1
+Network 1976    8.1     0       Sidney Lumet    0       0
+```
+
+## Warning
+
+Simple Select is **not intended for production use**. I wrote this as a toy
+project for my own consumption. There are quite a few bugs of which I'm aware
+and quite a few other features that I'd like to support but haven't had time to
+support just yet.
+
+Why publish it then? Maybe this project will inspire drive-by contributions or
+other, better-implemented spin-offs.
+
+## Wish List
+
+Speaking of drive-by contributions, here are some things that I'd like to
+support:
+
+- Implicit `AND` conjunctions (`director:/Tarantino/ year:"2000"` instead of
+  `director:/Tarantino/ AND year:"2000"`)
+- Support for types like numbers, dates (`year:2000` instead of `year:"2000"`)
+- `slx` should support CSV *and* (at the very least) sqlite3 file formats (open
+  to other formats as well)
+- Regexes should be the default query primitive (`director:Tarantino` instead of
+  `director:/Tarantino/`)
+- Improve parsing errors (including surfacing errors to the user)
+- Support for reading from `STDIN` and issuing queries from the command-line
+- Unit-testing
+- Configurable delimiters for output data (right now it's just `\t`)
+- (Maybe) rewrite in a faster, more-type-safe languages (e.g. Rust)
+
+I'm likely missing other FRs, bugs, so please file issues!
diff --git a/users/wpcarro/scratch/simple-select/main.py b/users/wpcarro/scratch/simple-select/main.py
new file mode 100644
index 0000000000..3ae6c5d60e
--- /dev/null
+++ b/users/wpcarro/scratch/simple-select/main.py
@@ -0,0 +1,262 @@
+from argparse import ArgumentParser
+
+import csv
+from parser import Parser
+import sqlite3
+import string
+from scanner import Scanner
+import re
+import readline
+
+################################################################################
+# Predicates
+################################################################################
+
+def is_alpha(c):
+  return c in string.ascii_letters
+
+def is_digit(c):
+  return c in "0123456789"
+
+def is_alphanumeric(c):
+  return is_alpha(c) or is_digit(c)
+
+def is_whitespace(c):
+  return c in " \r\t\n"
+
+################################################################################
+# Tokenizer
+################################################################################
+
+AND    = ("CONJUNCTION", "AND")
+OR     = ("CONJUNCTION", "OR")
+NOT    = ("PUNCTUATION", "NOT")
+COLON  = ("PUNCTUATION", "COLON")
+LPAREN = ("PUNCTUATION", "LPAREN")
+RPAREN = ("PUNCTUATION", "RPAREN")
+
+def tokenize(x):
+  s = Scanner(x)
+  tokens = scan_tokens(s)
+  return tokens
+
+def scan_tokens(s):
+  result = []
+  while not s.exhausted():
+    if is_whitespace(s.peek()):
+      s.advance()
+    else:
+      result.append(scan_token(s))
+  return result
+
+def scan_token(s):
+  punctuation = {
+      "-": NOT,
+      ":": COLON,
+      "(": LPAREN,
+      ")": RPAREN,
+  }
+  c = s.peek()
+  if c in punctuation:
+    s.advance()
+    return punctuation[c]
+  if c == "\"":
+    return tokenize_string(s)
+  if c == "/":
+    return tokenize_regex(s)
+  if is_alpha(c):
+    return tokenize_identifier(s)
+
+def tokenize_string(s):
+  s.advance() # ignore opening 2x-quote
+  current = ""
+  while s.peek() != "\"" and not s.exhausted():
+    current += s.advance()
+  if s.exhausted():
+    raise Exception("Unterminated string")
+  s.advance() # ignore closing 2x-quote
+  return ("STRING", current)
+
+def tokenize_regex(s):
+  s.advance() # ignore opening forward-slash
+  current = ""
+  while s.peek() != "/" and not s.exhausted():
+    current += s.advance()
+  if s.exhausted():
+    raise Exception("Unterminated regex")
+  s.advance() # ignore closing forward-slash
+  return ("REGEX", current)
+
+def tokenize_identifier(s):
+  conjunctions = {
+      "AND",
+      "OR",
+  }
+  current = s.advance()
+  while is_alphanumeric(s.peek()):
+    current += s.advance()
+  if current.upper() in conjunctions:
+    return ("CONJUNCTION", current.upper())
+  else:
+    return ("IDENTIFIER", current)
+
+################################################################################
+# Parser
+################################################################################
+
+# EBNF
+# Note: we order expression types by ascending levels of precedence.
+#
+# expression  -> conjunction ;
+# conjunction -> selection ( ( "AND" | "OR" )? selection )* ;
+# selection   -> "-"? IDENTIFIER ":" ( REGEX | STRING ) | grouping ;
+# grouping    -> REGEX | STRING | "(" expression ")" ;
+
+def parse(x):
+  tokens = tokenize(x)
+  p = Parser(tokens)
+  return expression(p)
+
+def expression(p):
+  return conjunction(p)
+
+def conjunction(p):
+  lhs = selection(p)
+
+  # TODO(wpcarro): Support default AND conjuctions when they're undefined.
+  while not p.exhausted() and p.match({AND, OR}):
+    conj = p.peek(n=-1)
+    rhs = selection(p)
+    lhs = ("CONJUNCTION", conj[1], lhs, rhs)
+
+  return lhs
+
+def selection(p):
+  negate = False
+  if p.peek() == NOT:
+    negate = True
+    p.advance()
+
+  if p.peek()[0] != "IDENTIFIER":
+    return grouping(p)
+
+  ident = p.expect(lambda x: x[0] == "IDENTIFIER")
+  colon = p.expect(lambda x: x[1] == "COLON")
+  value = p.expect(lambda x: x[0] in {"REGEX", "STRING"})
+  return ("SELECTION", negate, ident[1], value)
+
+def grouping(p):
+  if p.peek()[0] == "REGEX":
+    return p.advance()
+
+  if p.peek()[0] == "STRING":
+    return p.advance()
+
+  if p.peek() == LPAREN:
+    p.advance()
+    expr = expression(p)
+    p.expect(lambda x: x == RPAREN)
+    return ("GROUPING", expr)
+
+################################################################################
+# Compiler
+################################################################################
+
+def compile(source, table, columns):
+  ast = parse(source)
+  return "SELECT * FROM {} WHERE {};".format(table, do_compile(ast, columns))
+
+def do_compile(ast, columns):
+  if ast[0] == "REGEX":
+    cols = "({})".format(" || ".join(columns))
+    return "{} REGEXP '.*{}.*'".format(cols, ast[1])
+
+  if ast[0] == "STRING":
+    cols = "({})".format(" || ".join(columns))
+    return "{} LIKE '%{}%'".format(cols, ast[1])
+
+  if ast[0] == "SELECTION":
+    return compile_selection(ast)
+
+  if ast[0] == "CONJUNCTION":
+    _, conj, lhs, rhs = ast
+    lhs = do_compile(lhs, columns)
+    rhs = do_compile(rhs, columns)
+    return "{} {} {}".format(lhs, conj, rhs)
+
+  if ast[0] == "GROUPING":
+    return "({})".format(do_compile(ast[1], columns))
+
+  raise Exception("Unexpected AST: \"{}\"".format(ast))
+
+def compile_selection(ast):
+  _, negate, column, query = ast
+  match = compile_query(negate, query)
+  return "{} {}".format(column, match)
+
+def compile_query(negate, query):
+  query_type, query_string = query
+  if query_type == "REGEX":
+    if negate:
+      return "NOT REGEXP '.*{}.*'".format(query_string)
+    return "REGEXP '.*{}.*'".format(query_string)
+
+  if query_type == "STRING":
+    if negate:
+      return "NOT LIKE '%{}%'".format(query_string)
+    return "LIKE '%{}%'".format(query_string)
+
+################################################################################
+# Helper Functions
+################################################################################
+
+def regexp(expr, x):
+  reg = re.compile(expr)
+  return reg.search(x) is not None
+
+################################################################################
+# Main
+################################################################################
+
+def main(csv_path=None, debug=False):
+  # Import CSV to SQLite
+  table = "main"
+  con = sqlite3.connect(":memory:")
+
+  con.create_function("REGEXP", 2, regexp)
+
+  cur = con.cursor()
+  with open(csv_path, "r") as f:
+    r = csv.DictReader(f)
+    columns = next(r).keys()
+
+    # TODO(wpcarro): Use safer interpolation variant of "?" here and throughout.
+    cur.execute("CREATE TABLE {} ({});".format(table, ",".join(columns)))
+    rows = [tuple(row[col] for col in columns) for row in r]
+    cur.executemany("INSERT INTO {} ({}) VALUES ({});".format(table, ",".join(columns), ",".join("?" for _ in columns)), rows)
+    con.commit()
+
+  while True:
+    x = input("> ")
+
+    if debug:
+      print("tokens:\t{}".format(tokenize(x)))
+      print("AST:\t{}".format(parse(x)))
+      print("query:\t\"{}\"".format(compile(x, table, columns)))
+
+    try:
+      compile(x, table, columns)
+      for row in cur.execute(compile(x, table, columns)):
+        print("\t".join(str(cell) for cell in row))
+    except:
+      print("Compilation error.")
+
+  # TODO(wpcarro): Trap exits and ensure cleanup always runs.
+  con.close()
+
+if __name__ == "__main__":
+  parser = ArgumentParser()
+  parser.add_argument("-f", "--file", dest="file", help="Path to the CSV from which to read", metavar="PATH")
+  parser.add_argument("-d", "--debug", dest="debug", default=False, action="store_true", help="Enable debugging")
+  args = parser.parse_args()
+  main(csv_path=args.file, debug=args.debug)
diff --git a/users/wpcarro/scratch/simple-select/parser.py b/users/wpcarro/scratch/simple-select/parser.py
new file mode 100644
index 0000000000..d26f970e57
--- /dev/null
+++ b/users/wpcarro/scratch/simple-select/parser.py
@@ -0,0 +1,31 @@
+class Parser(object):
+    def __init__(self, tokens):
+        self.tokens = tokens
+        self.i = 0
+
+    def exhausted(self):
+        return self.i >= len(self.tokens)
+
+    def peek(self, n=0):
+        return self.tokens[self.i + n]
+
+    def advance(self):
+        if not self.exhausted():
+            self.i += 1
+        return self.peek(n=-1)
+
+    def match(self, xs):
+        if self.peek() in xs:
+            self.advance()
+            return True
+        return False
+
+    def test(self, predicate):
+        return predicate(self.tokens, self.i)
+
+    def expect(self, predicate):
+        if self.exhausted():
+            raise Exception("Unexpected EOL")
+        if predicate(self.peek()):
+            return self.advance()
+        raise Exception("Unexpected token: \"{}\"".format(self.peek()))
diff --git a/users/wpcarro/scratch/simple-select/scanner.py b/users/wpcarro/scratch/simple-select/scanner.py
new file mode 100644
index 0000000000..5dae68aee5
--- /dev/null
+++ b/users/wpcarro/scratch/simple-select/scanner.py
@@ -0,0 +1,27 @@
+# According to Crafting Interpreters, the only two primitives that a
+# scanner/lexer needs are peek and advance; other functions (e.g. match) are
+# nice-to-haves.
+class Scanner(object):
+  def __init__(self, chars):
+    self.i = 0
+    self.chars = chars
+
+  def exhausted(self):
+    return self.i >= len(self.chars)
+
+  def peek(self, n=0):
+    return self.chars[self.i + n] if self.i in range(0, len(self.chars)) else '\0'
+
+  def advance(self):
+    result = self.peek()
+    self.i += 1
+    return result
+
+  def match(self, x):
+    if self.exhausted():
+      return False
+    if self.peek() == x:
+      self.advance()
+      return True
+    else:
+      return False
diff --git a/users/wpcarro/secrets.json.secret b/users/wpcarro/secrets.json.secret
new file mode 100644
index 0000000000..d4c02bf693
--- /dev/null
+++ b/users/wpcarro/secrets.json.secret
Binary files differdiff --git a/users/wpcarro/slx.js/.gitignore b/users/wpcarro/slx.js/.gitignore
new file mode 100644
index 0000000000..d60e5798c1
--- /dev/null
+++ b/users/wpcarro/slx.js/.gitignore
@@ -0,0 +1,3 @@
+/.parcel-cache
+/dist
+/node_modules
\ No newline at end of file
diff --git a/users/wpcarro/slx.js/README.md b/users/wpcarro/slx.js/README.md
new file mode 100644
index 0000000000..3fbebc4706
--- /dev/null
+++ b/users/wpcarro/slx.js/README.md
@@ -0,0 +1,55 @@
+# slx.js
+
+Filter tabular data in the browser using an ergonomic query language.
+
+## Status
+
+This project is usable today (I use it in my projects), but it's currently alpha
+status. See the wish list for remaining features.
+
+## Installation
+
+`slx.js` is available via CDN:
+
+```shell
+<script src="https://cdn.jsdelivr.net/gh/wpcarro/slx.js/index.js" async></script>
+```
+
+## Usage
+
+`slx.js` hasn't been properly benchmarked, but in my personal projects, it works
+fine with `O(1,000)s` of records.
+
+```javascript
+const cast = [
+  { first: "Graham", last: "Chapman" },
+  { first: "John", last: "Cleese" },
+  { first: "Terry", last: "Gilliam" },
+  { first: "Eric", last: "Idle" },
+  { first: "Terry", last: "Jones" },
+  { first: "Michael", last: "Palin" },
+];
+
+const config = {
+    // Match values case sensitively when filtering.
+    caseSensitive: false,
+    // Coerce values into regular expressions (instead of strings) when they're defined as atoms.
+    preferRegex: true,
+    // The key in the JS object that hosts the Date type against which we filter.
+    dateKey: 'Date',
+};
+
+console.log(select('last:^C.+$', cast, config));
+// [{ first: "Graham", last: "Chapman" }, { first: "John", last: "Cleese" }]
+```
+
+## Wish List
+
+- Support explicit grouping with parentheses (e.g. `title:once (director:Tarantino OR director:Coen)`).
+- Proper benchmarking (see "Usage" section).
+- Something something documentation.
+- Something something testing.
+
+## See also:
+
+- [`slx`](https://github.com/wpcarro/slx)
diff --git a/users/wpcarro/slx.js/default.nix b/users/wpcarro/slx.js/default.nix
new file mode 100644
index 0000000000..bf903e77aa
--- /dev/null
+++ b/users/wpcarro/slx.js/default.nix
@@ -0,0 +1,11 @@
+{ pkgs, depot, ... }:
+
+(pkgs.writeText "source.txt" ''
+  ${depot.third_party.gitignoreSource ./.}
+'').overrideAttrs (_: {
+  meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush {
+    filter = ":/users/wpcarro/slx.js";
+    remote = "git@github.com:wpcarro/slx.js.git";
+    ref = "refs/heads/canon";
+  };
+})
diff --git a/users/wpcarro/slx.js/index.html b/users/wpcarro/slx.js/index.html
new file mode 100644
index 0000000000..966705a642
--- /dev/null
+++ b/users/wpcarro/slx.js/index.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>Tests</title>
+    <link rel="stylesheet" href="https://unpkg.com/terminal.css@0.7.2/dist/terminal.min.css" />
+  </head>
+  <body>
+    <div id="mount"></div>
+    <script src="./index.js"></script>
+    <script src="./tests.js" type="module"></script>
+  </body>
+</html>
diff --git a/users/wpcarro/slx.js/index.js b/users/wpcarro/slx.js/index.js
new file mode 100644
index 0000000000..3729978c75
--- /dev/null
+++ b/users/wpcarro/slx.js/index.js
@@ -0,0 +1,455 @@
+function select(query, xs, config) {
+    // naive optimizations
+    if (query === '' || xs === []) {
+        return xs;
+    }
+
+    const predicate = compile(parse(query, config), config);
+    return xs.filter(predicate);
+}
+
+function compile(ast, config) {
+    if (ast.type === 'CONJUNCTION') {
+        const lhs = compile(ast.lhs, compile);
+        const rhs = compile(ast.rhs, compile);
+
+        if (ast.joint === 'AND') {
+            return function(x) {
+                return lhs(x) && rhs(x);
+            };
+        }
+        if (ast.joint === 'OR') {
+            return function(x) {
+                return lhs(x) || rhs(x);
+            };
+        }
+    }
+    if (ast.type === 'DATE_SELECTION') {
+        if (ast.key === 'before') {
+            return function(row) {
+                let t = new Date();
+                if (ast.val === 'yesterday') {
+                    t.setDate(t.getDate() - 1);
+                    console.log(t);
+                }
+                // MM/DD/YYYY
+                else {
+                    t = new Date(ast.val);
+                }
+                return row[config.dateKey] < t;
+            };
+        }
+        if (ast.key === 'after') {
+            return function(row) {
+                let t = new Date();
+                if (ast.val === 'yesterday') {
+                    t.setDate(t.getDate() - 1);
+                    console.log(t);
+                }
+                // MM/DD/YYYY
+                else {
+                    t = new Date(ast.val);
+                }
+                return row[config.dateKey] > t;
+            };
+        }
+    }
+    if (ast.type === 'COMPARE_SELECTION') {
+        const f = compile(ast.val, config);
+
+        let compare = null;
+        if (ast.operator === 'EQ') { compare = (x, y) => x === y; }
+        if (ast.operator === 'LT') { compare = (x, y) => x < y; }
+        if (ast.operator === 'GT') { compare = (x, y) => x > y; }
+        if (ast.operator === 'LTE') { compare = (x, y) => x <= y; }
+        if (ast.operator === 'GTE') { compare = (x, y) => x >= y; }
+
+        return function(row) {
+            return ast.negate ? !compare(row[ast.key], ast.val) : compare(row[ast.key], ast.val);
+        };
+    }
+    if (ast.type === 'SELECTION') {
+        const f = compile(ast.val, config);
+        return function(row) {
+            return ast.negate ? !f(row[ast.key]) : f(row[ast.key]);
+        };
+    }
+    if (ast.type === 'MATCH_ALL') {
+        if (ast.matchType === 'STRING') {
+            return function(row) {
+                return Object.values(row).some(x => {
+                    if (config.caseSensitive) {
+                        return x === ast.val;
+                    } else {
+                        return x.toLowerCase() === ast.val.toLowerCase();
+                    }
+                })
+            };
+        }
+        if (ast.matchType === 'REGEX') {
+            return function(row) {
+                return Object.values(row).some(x => ast.val.test(x));
+            };
+        }
+    }
+    if (ast.type === 'GROUPING') {
+        return compile(ast.content);
+    }
+    if (ast.type === 'STRING') {
+        return function(x) {
+            if (config.caseSensitive) {
+                return x === ast.val;
+            } else {
+                return x.toLowerCase() === ast.val.toLowerCase();
+            }
+        };
+    }
+    if (ast.type === 'REGEX') {
+        return function(x) {
+            return ast.val.test(x);
+        };
+    }
+}
+
+// A "selection" without a "$column:" prefix should fuzzy-search all columns.
+//
+// conjunction -> selection ( ( "AND" | "OR" )? selection )* ;
+// selection   -> "-"? COLUMN ":" ( regex | string ) | regex ;
+// regex       -> [_-a-zA-Z0-9] | "/" [ _-a-zA-Z0-9] "/" | string ;
+// string      -> "\"" [ _-a-zA-Z0-9] "\"" ;
+
+// Whatever characters are valid for a JS regex.
+const ATOM_REGEX = /[-_.\[\]a-zA-Z0-9*+^$]/;
+
+function tokenize(x) {
+    const result = [];
+    let i = 0;
+    while (i < x.length) {
+        if (x[i] === ' ') {
+            i += 1;
+            while (i < x.length && x[i] === ' ') {
+                i += 1;
+            }
+            result.push(['WHITESPACE', null]);
+            continue;
+        }
+        if (x[i] === '-') {
+            result.push(['NEGATE', null]);
+            i += 1;
+            continue;
+        }
+        // Tokenize numbers (i.e. integers, floats).
+        if (/[0-9]/.test(x[i])) {
+            let curr = x[i];
+            i += 1;
+            while (i < x.length && /[0-9]/.test(x[i])) {
+                curr += x[i];
+                i += 1;
+            }
+            result.push(['NUMBER', parseFloat(curr)]);
+            continue;
+        }
+        if (ATOM_REGEX.test(x[i])) {
+            let curr = x[i];
+            i += 1;
+            while (i < x.length && ATOM_REGEX.test(x[i])) {
+                curr += x[i];
+                i += 1;
+            }
+            result.push(['ATOM', curr]);
+            continue;
+        }
+        if (x[i] === '=') {
+            result.push(['COMPARE', 'EQ']);
+            i += 1;
+            continue;
+        }
+        if (x[i] === '<' && i + 1 < x.length && x[i + 1] === '=') {
+            result.push(['COMPARE', 'LTE']);
+            i += 2;
+            continue;
+        }
+        if (x[i] === '<') {
+            result.push(['COMPARE', 'LT']);
+            i += 1;
+            continue;
+        }
+        if (x[i] === '>' && i + i < x.length && x[i + 1] === '=') {
+            result.push(['COMPARE', 'GTE']);
+            i += 2;
+            continue;
+        }
+        if (x[i] === '>') {
+            result.push(['COMPARE', 'GT']);
+            i += 1;
+            continue;
+        }
+        if (x[i] === ':') {
+            result.push(['COLON', null]);
+            i += 1;
+            continue;
+        }
+        if (x[i] === '(') {
+            result.push(['LPAREN', null]);
+            i += 1;
+            continue;
+        }
+        if (x[i] === ')') {
+            result.push(['RPAREN', null]);
+            i += 1;
+            continue;
+        }
+        if (x[i] === '/') {
+            let start = i;
+            let curr = '';
+            i += 1;
+            while (i < x.length && x[i] !== '/') {
+                curr += x[i];
+                i += 1;
+            }
+            // error
+            if (i >= x.length) {
+                throw `Tokenize Error: EOL while attempting to tokenize the regex beginning at column: ${start}`;
+            }
+            if (x[i] === '/') {
+                result.push(['REGEX', curr]);
+                i += 1;
+            }
+            continue;
+        }
+        if (x[i] === '"') {
+            let start = i;
+            let curr = '';
+            i += 1;
+            while (i < x.length && x[i] !== '"') {
+                // continue on \"
+                if (x[i] === '\\' && x[i + 1] === '"') {
+                    curr += '\"';
+                    i += 2;
+                } else {
+                    curr += x[i];
+                    i += 1;
+                }
+            }
+            if (i >= x.length) {
+                throw `Tokenize Error: EOL while attempting to tokenize the string starting at column: ${start}`;
+            }
+            if (x[i] === '"') {
+                result.push(['STRING', curr]);
+                i += 1;
+            }
+            continue;
+        }
+        else {
+            i += 1;
+        }
+    }
+    return result;
+}
+
+function expect(f, expectation, p) {
+    const [type, val] = p.tokens[p.i];
+    if (f(type, val)) {
+        p.i += 1;
+    } else {
+        throw `Parse Error: expected ${expectation}, but got ${p.tokens[p.i]}; ${JSON.stringify(p)}`
+    }
+}
+
+function matches(f, p) {
+    const [type, val] = p.tokens[p.i];
+    if (f(type, val)) {
+        return true;
+    }
+    return false;
+}
+
+function match(f, expectation, p) {
+    const [type, val] = p.tokens[p.i];
+    if (f(type, val)) {
+        p.i += 1;
+        return val;
+    }
+    throw `Parse Error: expected ${expectation}, but got: ${p.tokens[p.i]}; ${JSON.stringify(p)}`;
+}
+
+function skipWhitespace(p) {
+    while (p.i < p.tokens.length && matches((type, _) => type === 'WHITESPACE', p)) {
+        p.i += 1;
+    }
+}
+
+function peekType(n, p) {
+    if (p.i + n < p.tokens.length) {
+        return p.tokens[p.i + n][0];
+    }
+    return null;
+}
+
+function parser(tokens) {
+    return { i: 0, tokens };
+}
+
+function parse(x, config) {
+    const tokens = tokenize(x);
+    const p = parser(tokens);
+    return conjunction(p, config);
+}
+
+function conjunction(p, config) {
+    skipWhitespace(p);
+
+    const lhs = selection(p, config);
+    skipWhitespace(p);
+
+    // TODO(wpcarro): Consider re-architecting the parser to avoid smells like
+    // this.
+    if (peekType(0, p) === 'RPAREN') {
+        return lhs;
+    }
+
+    if (p.i >= p.tokens.length) {
+        return lhs;
+    }
+
+    let joint = 'AND';
+    if (matches((type, val) => type === 'ATOM' && val === 'AND', p)) {
+        joint = 'AND';
+        p.i += 1;
+    } else if (matches((type, val) => type === 'ATOM' && val === 'OR', p)) {
+        joint = 'OR';
+        p.i += 1;
+    }
+    skipWhitespace(p);
+    const rhs = conjunction(p, config);
+
+    return {
+        type: 'CONJUNCTION',
+        joint,
+        lhs,
+        rhs,
+    };
+}
+
+function selection(p, config) {
+    // column:value OR -column:value
+    if ((peekType(0, p) === 'ATOM' && peekType(1, p) === 'COLON') ||
+        (peekType(0, p) === 'NEGATE' && peekType(1, p) === 'ATOM' && peekType(2, p) === 'COLON')) {
+
+        let negate = false;
+        if (p.tokens[p.i][0] === 'NEGATE') {
+            negate = true;
+            p.i += 1;
+        }
+
+        const key = match((type, _) => type === 'ATOM', 'a column label', p);
+        expect((type, val) => type === 'COLON', 'a colon', p);
+
+        if (key === 'before' || key === 'after') {
+            const val = date(p);
+            return {
+                type: 'DATE_SELECTION',
+                key,
+                val,
+            };
+        } else {
+            const val = value(p, config);
+            return {
+                type: 'SELECTION',
+                negate,
+                key,
+                val,
+            };
+        }
+    }
+    // column<value OR -column<value
+    else if ((peekType(0, p) === 'ATOM' && peekType(1, p) === 'COMPARE') ||
+             (peekType(0, p) === 'NEGATE' && peekType(1, p) === 'ATOM' && peekType(2, p) === 'COMPARE')) {
+        let negate = false;
+        if (p.tokens[p.i][0] === 'NEGATE') {
+            negate = true;
+            p.i += 1;
+        }
+
+        const key = match((type, _) => type === 'ATOM', 'a column label', p);
+        const operator = match((type, _) => type === 'COMPARE', 'a comparison operator (i.e. "<", ">", "<=", ">=")', p);
+        const val = match((type, _) => type === 'NUMBER', 'a number', p);
+
+        return {
+            type: 'COMPARE_SELECTION',
+            operator,
+            negate,
+            key,
+            val,
+        };
+    }
+    else {
+        return matchAll(p, config);
+    }
+}
+
+function matchAll(p, config) {
+    const [type, val] = p.tokens[p.i];
+
+    // Cast atoms into strings or regexes depending on the current config.
+    if (type === 'ATOM') {
+        p.i += 1;
+        if (config.preferRegex) {
+            const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i");
+            return { type: 'MATCH_ALL', matchType: 'REGEX', val: regex };
+        } else {
+            return { type: 'MATCH_ALL', matchType: 'STRING', val }
+        }
+    }
+    if (type === 'STRING') {
+        p.i += 1;
+        return { type: 'MATCH_ALL', matchType: 'STRING', val };
+    }
+    if (type === 'REGEX') {
+        p.i += 1;
+        const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i");
+        return { type: 'MATCH_ALL', matchType: 'REGEX', val: regex };
+    }
+    if (type === 'LPAREN') {
+        p.i += 1;
+        const content = conjunction(p, config);
+        expect((type, _) => type === 'RPAREN', 'a closing parenthesis', p);
+        return {
+            type: 'GROUPING',
+            content,
+        };
+    }
+    throw `Parse Error: Expected a regular expression or a string, but got: ${p.tokens[p.i]}; ${JSON.stringify(p)}`;
+}
+
+function value(p, config) {
+    const [type, val] = p.tokens[p.i];
+
+    // Cast atoms into strings or regexes depending on the current config.
+    if (type === 'ATOM') {
+        p.i += 1;
+        if (config.preferRegex) {
+            const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i");
+            return { type: 'REGEX', val: regex };
+        } else {
+            return { type: 'STRING', val }
+        }
+    }
+    if (type === 'STRING') {
+        p.i += 1;
+        return { type, val };
+    }
+    if (type === 'REGEX') {
+        p.i += 1;
+        const regex = config.caseSensitive ? new RegExp(val) : new RegExp(val, "i");
+        return { type, val: regex };
+    }
+    throw `Parse Error: Expected a regular expression or a string, but got: ${p.tokens[p.i]}; ${JSON.stringify(p)}`;
+}
+
+function date(p) {
+    const [type, val] = p.tokens[p.i];
+    p.i += 1;
+
+    return val;
+}
diff --git a/users/wpcarro/slx.js/package.json b/users/wpcarro/slx.js/package.json
new file mode 100644
index 0000000000..d8f2e678fa
--- /dev/null
+++ b/users/wpcarro/slx.js/package.json
@@ -0,0 +1,14 @@
+{
+  "name": "slx.js",
+  "version": "1.0.0",
+  "main": "index.js",
+  "license": "MIT",
+  "dependencies": {
+    "parcel": "^2.8.3",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"
+  },
+  "devDependencies": {
+    "process": "^0.11.10"
+  }
+}
diff --git a/users/wpcarro/slx.js/tests.js b/users/wpcarro/slx.js/tests.js
new file mode 100644
index 0000000000..9ed68a588c
--- /dev/null
+++ b/users/wpcarro/slx.js/tests.js
@@ -0,0 +1,68 @@
+import { createRoot } from "react-dom/client";
+import React from "react";
+
+
+const john = { first: 'John', last: 'Cleese', age: 83, birthday: new Date("10/27/1939") };
+const graham = { first: 'Graham', last: 'Chapman', age: 48, birthday: new Date("01/08/1941") };
+
+const xs = [
+    john,
+    graham,
+];
+const cfg = {
+    caseSensitive: false,
+    preferRegex: true,
+    dateKey: 'birthday',
+};
+const tests = [
+    ['support EQ', 'age=83', xs, cfg, [john]],
+    ['supports LT', 'age<83', xs, cfg, [graham]],
+    ['supports LTE', 'age<=83', xs, cfg, [john, graham]],
+    ['supports GT', 'age>48', xs, cfg, [john]],
+    ['supports GTE', 'age>=48', xs, cfg, [john, graham]],
+    ['supports grouping (1)', 'last:/^C/ (age=83 OR age=48)', xs, cfg, [john, graham]],
+    ['supports grouping (2)', '(age=83)', xs, cfg, [john]],
+    ['supports grouping (3)', '(age=83 OR age=48)', xs, cfg, [john, graham]],
+];
+
+function equal(xs, ys) {
+    return xs.length === ys.length && xs.every((x, i) => x === ys[i]);
+}
+
+class App extends React.Component {
+    constructor(props) {
+        super(props);
+    }
+    render() {
+        return (
+            <table>
+              <thead>
+                <th>pass/fail</th>
+                <th>Label</th>
+                <th>code</th>
+                <th>actual</th>
+                <th>expected</th>
+              </thead>
+              <tbody>
+                {this.props.tests.map(test => {
+                    const [label, query, xs, cfg, expected] = test;
+                    const actual = select(query, xs, cfg);
+                    return (
+                        <tr style={{backgroundColor: equal(actual, expected) ? null : 'red'}}>
+                          <td>{equal(actual, expected) ? "pass" : "fail"}</td>
+                          <td>{label}</td>
+                          <td>select("{query}", {JSON.stringify(xs)}, {JSON.stringify(cfg)})</td>
+                          <td>{JSON.stringify(actual)}</td>
+                          <td>{JSON.stringify(expected)}</td>
+                        </tr>
+                    );
+                })}
+              </tbody>
+            </table>
+        );
+    }
+}
+
+const container = document.getElementById("mount");
+const root = createRoot(container);
+root.render(<App tests={tests} />);
diff --git a/users/wpcarro/slx.js/yarn.lock b/users/wpcarro/slx.js/yarn.lock
new file mode 100644
index 0000000000..4d0ec7633f
--- /dev/null
+++ b/users/wpcarro/slx.js/yarn.lock
@@ -0,0 +1,1495 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
+  integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
+  dependencies:
+    "@babel/highlight" "^7.18.6"
+
+"@babel/helper-validator-identifier@^7.18.6":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
+  integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
+
+"@babel/highlight@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+  integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.18.6"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
+"@jridgewell/gen-mapping@^0.3.0":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
+  integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/resolve-uri@3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+  integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/set-array@^1.0.1":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+  integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
+"@jridgewell/source-map@^0.3.2":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+  integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
+  version "1.4.14"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+  integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@^0.3.9":
+  version "0.3.17"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
+  integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
+  dependencies:
+    "@jridgewell/resolve-uri" "3.1.0"
+    "@jridgewell/sourcemap-codec" "1.4.14"
+
+"@lezer/common@^0.15.0", "@lezer/common@^0.15.7":
+  version "0.15.12"
+  resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.12.tgz#2f21aec551dd5fd7d24eb069f90f54d5bc6ee5e9"
+  integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==
+
+"@lezer/lr@^0.15.4":
+  version "0.15.8"
+  resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.8.tgz#1564a911e62b0a0f75ca63794a6aa8c5dc63db21"
+  integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==
+  dependencies:
+    "@lezer/common" "^0.15.0"
+
+"@lmdb/lmdb-darwin-arm64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz#bc66fa43286b5c082e8fee0eacc17995806b6fbe"
+  integrity sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A==
+
+"@lmdb/lmdb-darwin-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz#89d8390041bce6bab24a82a20392be22faf54ffc"
+  integrity sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA==
+
+"@lmdb/lmdb-linux-arm64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz#14fe4c96c2bb1285f93797f45915fa35ee047268"
+  integrity sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ==
+
+"@lmdb/lmdb-linux-arm@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz#05bde4573ab10cf21827339fe687148f2590cfa1"
+  integrity sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw==
+
+"@lmdb/lmdb-linux-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz#d2f85afd857d2c33d2caa5b057944574edafcfee"
+  integrity sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q==
+
+"@lmdb/lmdb-win32-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz#28f643fbc0bec30b07fbe95b137879b6b4d1c9c5"
+  integrity sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA==
+
+"@mischnic/json-sourcemap@^0.1.0":
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz#38af657be4108140a548638267d02a2ea3336507"
+  integrity sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==
+  dependencies:
+    "@lezer/common" "^0.15.7"
+    "@lezer/lr" "^0.15.4"
+    json5 "^2.2.1"
+
+"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.2.0.tgz#901c5937e1441572ea23e631fe6deca68482fe76"
+  integrity sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==
+
+"@msgpackr-extract/msgpackr-extract-darwin-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.2.0.tgz#fb877fe6bae3c4d3cea29786737840e2ae689066"
+  integrity sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.2.0.tgz#986179c38b10ac41fbdaf7d036c825cbc72855d9"
+  integrity sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.2.0.tgz#15f2c6fe9e0adc06c21af7e95f484ff4880d79ce"
+  integrity sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg==
+
+"@msgpackr-extract/msgpackr-extract-linux-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.2.0.tgz#30cae5c9a202f3e1fa1deb3191b18ffcb2f239a2"
+  integrity sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw==
+
+"@msgpackr-extract/msgpackr-extract-win32-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.2.0.tgz#016d855b6bc459fd908095811f6826e45dd4ba64"
+  integrity sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA==
+
+"@parcel/bundler-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/bundler-default/-/bundler-default-2.8.3.tgz#d64739dbc2dbd59d6629861bf77a8083aced5229"
+  integrity sha512-yJvRsNWWu5fVydsWk3O2L4yIy3UZiKWO2cPDukGOIWMgp/Vbpp+2Ct5IygVRtE22bnseW/E/oe0PV3d2IkEJGg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/graph" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/cache@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/cache/-/cache-2.8.3.tgz#169e130cf59913c0ed9fadce1a450e68f710e16f"
+  integrity sha512-k7xv5vSQrJLdXuglo+Hv3yF4BCSs1tQ/8Vbd6CHTkOhf7LcGg6CPtLw053R/KdMpd/4GPn0QrAsOLdATm1ELtQ==
+  dependencies:
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    lmdb "2.5.2"
+
+"@parcel/codeframe@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.8.3.tgz#84fb529ef70def7f5bc64f6c59b18d24826f5fcc"
+  integrity sha512-FE7sY53D6n/+2Pgg6M9iuEC6F5fvmyBkRE4d9VdnOoxhTXtkEqpqYgX7RJ12FAQwNlxKq4suBJQMgQHMF2Kjeg==
+  dependencies:
+    chalk "^4.1.0"
+
+"@parcel/compressor-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/compressor-raw/-/compressor-raw-2.8.3.tgz#301753df8c6de967553149639e8a4179b88f0c95"
+  integrity sha512-bVDsqleBUxRdKMakWSlWC9ZjOcqDKE60BE+Gh3JSN6WJrycJ02P5wxjTVF4CStNP/G7X17U+nkENxSlMG77ySg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/config-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/config-default/-/config-default-2.8.3.tgz#9a43486e7c702e96c68052c37b79098d7240e35b"
+  integrity sha512-o/A/mbrO6X/BfGS65Sib8d6SSG45NYrNooNBkH/o7zbOBSRQxwyTlysleK1/3Wa35YpvFyLOwgfakqCtbGy4fw==
+  dependencies:
+    "@parcel/bundler-default" "2.8.3"
+    "@parcel/compressor-raw" "2.8.3"
+    "@parcel/namer-default" "2.8.3"
+    "@parcel/optimizer-css" "2.8.3"
+    "@parcel/optimizer-htmlnano" "2.8.3"
+    "@parcel/optimizer-image" "2.8.3"
+    "@parcel/optimizer-svgo" "2.8.3"
+    "@parcel/optimizer-terser" "2.8.3"
+    "@parcel/packager-css" "2.8.3"
+    "@parcel/packager-html" "2.8.3"
+    "@parcel/packager-js" "2.8.3"
+    "@parcel/packager-raw" "2.8.3"
+    "@parcel/packager-svg" "2.8.3"
+    "@parcel/reporter-dev-server" "2.8.3"
+    "@parcel/resolver-default" "2.8.3"
+    "@parcel/runtime-browser-hmr" "2.8.3"
+    "@parcel/runtime-js" "2.8.3"
+    "@parcel/runtime-react-refresh" "2.8.3"
+    "@parcel/runtime-service-worker" "2.8.3"
+    "@parcel/transformer-babel" "2.8.3"
+    "@parcel/transformer-css" "2.8.3"
+    "@parcel/transformer-html" "2.8.3"
+    "@parcel/transformer-image" "2.8.3"
+    "@parcel/transformer-js" "2.8.3"
+    "@parcel/transformer-json" "2.8.3"
+    "@parcel/transformer-postcss" "2.8.3"
+    "@parcel/transformer-posthtml" "2.8.3"
+    "@parcel/transformer-raw" "2.8.3"
+    "@parcel/transformer-react-refresh-wrap" "2.8.3"
+    "@parcel/transformer-svg" "2.8.3"
+
+"@parcel/core@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/core/-/core-2.8.3.tgz#22a69f36095d53736ab10bf42697d9aa5f4e382b"
+  integrity sha512-Euf/un4ZAiClnlUXqPB9phQlKbveU+2CotZv7m7i+qkgvFn5nAGnrV4h1OzQU42j9dpgOxWi7AttUDMrvkbhCQ==
+  dependencies:
+    "@mischnic/json-sourcemap" "^0.1.0"
+    "@parcel/cache" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/graph" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    abortcontroller-polyfill "^1.1.9"
+    base-x "^3.0.8"
+    browserslist "^4.6.6"
+    clone "^2.1.1"
+    dotenv "^7.0.0"
+    dotenv-expand "^5.1.0"
+    json5 "^2.2.0"
+    msgpackr "^1.5.4"
+    nullthrows "^1.1.1"
+    semver "^5.7.1"
+
+"@parcel/diagnostic@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/diagnostic/-/diagnostic-2.8.3.tgz#d560276d5d2804b48beafa1feaf3fc6b2ac5e39d"
+  integrity sha512-u7wSzuMhLGWZjVNYJZq/SOViS3uFG0xwIcqXw12w54Uozd6BH8JlhVtVyAsq9kqnn7YFkw6pXHqAo5Tzh4FqsQ==
+  dependencies:
+    "@mischnic/json-sourcemap" "^0.1.0"
+    nullthrows "^1.1.1"
+
+"@parcel/events@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.8.3.tgz#205f8d874e6ecc2cbdb941bf8d54bae669e571af"
+  integrity sha512-hoIS4tAxWp8FJk3628bsgKxEvR7bq2scCVYHSqZ4fTi/s0+VymEATrRCUqf+12e5H47uw1/ZjoqrGtBI02pz4w==
+
+"@parcel/fs-search@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.8.3.tgz#1c7d812c110b808758f44c56e61dfffdb09e9451"
+  integrity sha512-DJBT2N8knfN7Na6PP2mett3spQLTqxFrvl0gv+TJRp61T8Ljc4VuUTb0hqBj+belaASIp3Q+e8+SgaFQu7wLiQ==
+  dependencies:
+    detect-libc "^1.0.3"
+
+"@parcel/fs@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.8.3.tgz#80536afe877fc8a2bd26be5576b9ba27bb4c5754"
+  integrity sha512-y+i+oXbT7lP0e0pJZi/YSm1vg0LDsbycFuHZIL80pNwdEppUAtibfJZCp606B7HOjMAlNZOBo48e3hPG3d8jgQ==
+  dependencies:
+    "@parcel/fs-search" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/watcher" "^2.0.7"
+    "@parcel/workers" "2.8.3"
+
+"@parcel/graph@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/graph/-/graph-2.8.3.tgz#00ffe8ec032e74fee57199e54529f1da7322571d"
+  integrity sha512-26GL8fYZPdsRhSXCZ0ZWliloK6DHlMJPWh6Z+3VVZ5mnDSbYg/rRKWmrkhnr99ZWmL9rJsv4G74ZwvDEXTMPBg==
+  dependencies:
+    nullthrows "^1.1.1"
+
+"@parcel/hash@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/hash/-/hash-2.8.3.tgz#bc2499a27395169616cad2a99e19e69b9098f6e9"
+  integrity sha512-FVItqzjWmnyP4ZsVgX+G00+6U2IzOvqDtdwQIWisCcVoXJFCqZJDy6oa2qDDFz96xCCCynjRjPdQx2jYBCpfYw==
+  dependencies:
+    detect-libc "^1.0.3"
+    xxhash-wasm "^0.4.2"
+
+"@parcel/logger@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.8.3.tgz#e14e4debafb3ca9e87c07c06780f9afc38b2712c"
+  integrity sha512-Kpxd3O/Vs7nYJIzkdmB6Bvp3l/85ydIxaZaPfGSGTYOfaffSOTkhcW9l6WemsxUrlts4za6CaEWcc4DOvaMOPA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+
+"@parcel/markdown-ansi@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.8.3.tgz#1337d421bb1133ad178f386a8e1b746631bba4a1"
+  integrity sha512-4v+pjyoh9f5zuU/gJlNvNFGEAb6J90sOBwpKJYJhdWXLZMNFCVzSigxrYO+vCsi8G4rl6/B2c0LcwIMjGPHmFQ==
+  dependencies:
+    chalk "^4.1.0"
+
+"@parcel/namer-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/namer-default/-/namer-default-2.8.3.tgz#5304bee74beb4b9c1880781bdbe35be0656372f4"
+  integrity sha512-tJ7JehZviS5QwnxbARd8Uh63rkikZdZs1QOyivUhEvhN+DddSAVEdQLHGPzkl3YRk0tjFhbqo+Jci7TpezuAMw==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/node-resolver-core@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/node-resolver-core/-/node-resolver-core-2.8.3.tgz#581df074a27646400b3fed9da95297b616a7db8f"
+  integrity sha512-12YryWcA5Iw2WNoEVr/t2HDjYR1iEzbjEcxfh1vaVDdZ020PiGw67g5hyIE/tsnG7SRJ0xdRx1fQ2hDgED+0Ww==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    semver "^5.7.1"
+
+"@parcel/optimizer-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-css/-/optimizer-css-2.8.3.tgz#420a333f4b78f7ff15e69217dfed34421b1143ee"
+  integrity sha512-JotGAWo8JhuXsQDK0UkzeQB0UR5hDAKvAviXrjqB4KM9wZNLhLleeEAW4Hk8R9smCeQFP6Xg/N/NkLDpqMwT3g==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    lightningcss "^1.16.1"
+    nullthrows "^1.1.1"
+
+"@parcel/optimizer-htmlnano@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.8.3.tgz#a71ab6f0f24160ef9f573266064438eff65e96d0"
+  integrity sha512-L8/fHbEy8Id2a2E0fwR5eKGlv9VYDjrH9PwdJE9Za9v1O/vEsfl/0T/79/x129l5O0yB6EFQkFa20MiK3b+vOg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    htmlnano "^2.0.0"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    svgo "^2.4.0"
+
+"@parcel/optimizer-image@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-image/-/optimizer-image-2.8.3.tgz#ea49b4245b4f7d60b38c7585c6311fb21d341baa"
+  integrity sha512-SD71sSH27SkCDNUNx9A3jizqB/WIJr3dsfp+JZGZC42tpD/Siim6Rqy9M4To/BpMMQIIiEXa5ofwS+DgTEiEHQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    detect-libc "^1.0.3"
+
+"@parcel/optimizer-svgo@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-svgo/-/optimizer-svgo-2.8.3.tgz#04da4efec6b623679539a84961bff6998034ba8a"
+  integrity sha512-9KQed99NZnQw3/W4qBYVQ7212rzA9EqrQG019TIWJzkA9tjGBMIm2c/nXpK1tc3hQ3e7KkXkFCQ3C+ibVUnHNA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    svgo "^2.4.0"
+
+"@parcel/optimizer-terser@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-terser/-/optimizer-terser-2.8.3.tgz#3a06d98d09386a1a0ae1be85376a8739bfba9618"
+  integrity sha512-9EeQlN6zIeUWwzrzu6Q2pQSaYsYGah8MtiQ/hog9KEPlYTP60hBv/+utDyYEHSQhL7y5ym08tPX5GzBvwAD/dA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    terser "^5.2.0"
+
+"@parcel/package-manager@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/package-manager/-/package-manager-2.8.3.tgz#ddd0d62feae3cf0fb6cc0537791b3a16296ad458"
+  integrity sha512-tIpY5pD2lH53p9hpi++GsODy6V3khSTX4pLEGuMpeSYbHthnOViobqIlFLsjni+QA1pfc8NNNIQwSNdGjYflVA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    semver "^5.7.1"
+
+"@parcel/packager-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-css/-/packager-css-2.8.3.tgz#0eff34268cb4f5dfb53c1bbca85f5567aeb1835a"
+  integrity sha512-WyvkMmsurlHG8d8oUVm7S+D+cC/T3qGeqogb7sTI52gB6uiywU7lRCizLNqGFyFGIxcVTVHWnSHqItBcLN76lA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/packager-html@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-html/-/packager-html-2.8.3.tgz#f9263b891aa4dd46c6e2fa2b07025a482132fff1"
+  integrity sha512-OhPu1Hx1RRKJodpiu86ZqL8el2Aa4uhBHF6RAL1Pcrh2EhRRlPf70Sk0tC22zUpYL7es+iNKZ/n0Rl+OWSHWEw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+
+"@parcel/packager-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-js/-/packager-js-2.8.3.tgz#3ed11565915d73d12192b6901c75a6b820e4a83a"
+  integrity sha512-0pGKC3Ax5vFuxuZCRB+nBucRfFRz4ioie19BbDxYnvBxrd4M3FIu45njf6zbBYsI9eXqaDnL1b3DcZJfYqtIzw==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    globals "^13.2.0"
+    nullthrows "^1.1.1"
+
+"@parcel/packager-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-raw/-/packager-raw-2.8.3.tgz#bdec826df991e186cb58691cc45d12ad5c06676e"
+  integrity sha512-BA6enNQo1RCnco9MhkxGrjOk59O71IZ9DPKu3lCtqqYEVd823tXff2clDKHK25i6cChmeHu6oB1Rb73hlPqhUA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/packager-svg@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-svg/-/packager-svg-2.8.3.tgz#7233315296001c531cb55ca96b5f2ef672343630"
+  integrity sha512-mvIoHpmv5yzl36OjrklTDFShLUfPFTwrmp1eIwiszGdEBuQaX7JVI3Oo2jbVQgcN4W7J6SENzGQ3Q5hPTW3pMw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    posthtml "^0.16.4"
+
+"@parcel/plugin@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/plugin/-/plugin-2.8.3.tgz#7bb30a5775eaa6473c27f002a0a3ee7308d6d669"
+  integrity sha512-jZ6mnsS4D9X9GaNnvrixDQwlUQJCohDX2hGyM0U0bY2NWU8Km97SjtoCpWjq+XBCx/gpC4g58+fk9VQeZq2vlw==
+  dependencies:
+    "@parcel/types" "2.8.3"
+
+"@parcel/reporter-cli@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/reporter-cli/-/reporter-cli-2.8.3.tgz#12a4743b51b8fe6837f53c20e01bbf1f7336e8e4"
+  integrity sha512-3sJkS6tFFzgIOz3u3IpD/RsmRxvOKKiQHOTkiiqRt1l44mMDGKS7zANRnJYsQzdCsgwc9SOP30XFgJwtoVlMbw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chalk "^4.1.0"
+    term-size "^2.2.1"
+
+"@parcel/reporter-dev-server@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/reporter-dev-server/-/reporter-dev-server-2.8.3.tgz#a0daa5cc015642684cea561f4e0e7116bbffdc1c"
+  integrity sha512-Y8C8hzgzTd13IoWTj+COYXEyCkXfmVJs3//GDBsH22pbtSFMuzAZd+8J9qsCo0EWpiDow7V9f1LischvEh3FbQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/resolver-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/resolver-default/-/resolver-default-2.8.3.tgz#5ae41e537ae4a793c1abb47f094482b9e2ac3535"
+  integrity sha512-k0B5M/PJ+3rFbNj4xZSBr6d6HVIe6DH/P3dClLcgBYSXAvElNDfXgtIimbjCyItFkW9/BfcgOVKEEIZOeySH/A==
+  dependencies:
+    "@parcel/node-resolver-core" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/runtime-browser-hmr@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.8.3.tgz#1fa74e1fbd1030b0a920c58afa3a9eb7dc4bcd1e"
+  integrity sha512-2O1PYi2j/Q0lTyGNV3JdBYwg4rKo6TEVFlYGdd5wCYU9ZIN9RRuoCnWWH2qCPj3pjIVtBeppYxzfVjPEHINWVg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/runtime-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-js/-/runtime-js-2.8.3.tgz#0baa4c8fbf77eabce05d01ccc186614968ffc0cd"
+  integrity sha512-IRja0vNKwvMtPgIqkBQh0QtRn0XcxNC8HU1jrgWGRckzu10qJWO+5ULgtOeR4pv9krffmMPqywGXw6l/gvJKYQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/runtime-react-refresh@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.8.3.tgz#381a942fb81e8f5ac6c7e0ee1b91dbf34763c3f8"
+  integrity sha512-2v/qFKp00MfG0234OdOgQNAo6TLENpFYZMbVbAsPMY9ITiqG73MrEsrGXVoGbYiGTMB/Toer/lSWlJxtacOCuA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    react-error-overlay "6.0.9"
+    react-refresh "^0.9.0"
+
+"@parcel/runtime-service-worker@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-service-worker/-/runtime-service-worker-2.8.3.tgz#54d92da9ff1dfbd27db0e84164a22fa59e99b348"
+  integrity sha512-/Skkw+EeRiwzOJso5fQtK8c9b452uWLNhQH1ISTodbmlcyB4YalAiSsyHCtMYD0c3/t5Sx4ZS7vxBAtQd0RvOw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/source-map@^2.1.1":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@parcel/source-map/-/source-map-2.1.1.tgz#fb193b82dba6dd62cc7a76b326f57bb35000a782"
+  integrity sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==
+  dependencies:
+    detect-libc "^1.0.3"
+
+"@parcel/transformer-babel@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-babel/-/transformer-babel-2.8.3.tgz#286bc6cb9afe4c0259f0b28e0f2f47322a24b130"
+  integrity sha512-L6lExfpvvC7T/g3pxf3CIJRouQl+sgrSzuWQ0fD4PemUDHvHchSP4SNUVnd6gOytF3Y1KpnEZIunQGi5xVqQCQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    json5 "^2.2.0"
+    nullthrows "^1.1.1"
+    semver "^5.7.0"
+
+"@parcel/transformer-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-css/-/transformer-css-2.8.3.tgz#d6c44100204e73841ad8e0f90472172ea8b9120c"
+  integrity sha512-xTqFwlSXtnaYen9ivAgz+xPW7yRl/u4QxtnDyDpz5dr8gSeOpQYRcjkd4RsYzKsWzZcGtB5EofEk8ayUbWKEUg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    lightningcss "^1.16.1"
+    nullthrows "^1.1.1"
+
+"@parcel/transformer-html@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-html/-/transformer-html-2.8.3.tgz#5c68b28ee6b8c7a13b8aee87f7957ad3227bd83f"
+  integrity sha512-kIZO3qsMYTbSnSpl9cnZog+SwL517ffWH54JeB410OSAYF1ouf4n5v9qBnALZbuCCmPwJRGs4jUtE452hxwN4g==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+    srcset "4"
+
+"@parcel/transformer-image@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-image/-/transformer-image-2.8.3.tgz#73805b2bfc3c8919d7737544e5f8be39e3f303fe"
+  integrity sha512-cO4uptcCGTi5H6bvTrAWEFUsTNhA4kCo8BSvRSCHA2sf/4C5tGQPHt3JhdO0GQLPwZRCh/R41EkJs5HZ8A8DAg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/transformer-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-js/-/transformer-js-2.8.3.tgz#fe400df428394d1e7fe5afb6dea5c7c858e44f03"
+  integrity sha512-9Qd6bib+sWRcpovvzvxwy/PdFrLUXGfmSW9XcVVG8pvgXsZPFaNjnNT8stzGQj1pQiougCoxMY4aTM5p1lGHEQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    "@swc/helpers" "^0.4.12"
+    browserslist "^4.6.6"
+    detect-libc "^1.0.3"
+    nullthrows "^1.1.1"
+    regenerator-runtime "^0.13.7"
+    semver "^5.7.1"
+
+"@parcel/transformer-json@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-json/-/transformer-json-2.8.3.tgz#25deb3a5138cc70a83269fc5d39d564609354d36"
+  integrity sha512-B7LmVq5Q7bZO4ERb6NHtRuUKWGysEeaj9H4zelnyBv+wLgpo4f5FCxSE1/rTNmP9u1qHvQ3scGdK6EdSSokGPg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    json5 "^2.2.0"
+
+"@parcel/transformer-postcss@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-postcss/-/transformer-postcss-2.8.3.tgz#df4fdc1c90893823445f2a8eb8e2bdd0349ccc58"
+  integrity sha512-e8luB/poIlz6jBsD1Izms+6ElbyzuoFVa4lFVLZnTAChI3UxPdt9p/uTsIO46HyBps/Bk8ocvt3J4YF84jzmvg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    clone "^2.1.1"
+    nullthrows "^1.1.1"
+    postcss-value-parser "^4.2.0"
+    semver "^5.7.1"
+
+"@parcel/transformer-posthtml@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-posthtml/-/transformer-posthtml-2.8.3.tgz#7c3912a5a631cb26485f6464e0d6eeabb6f1e718"
+  integrity sha512-pkzf9Smyeaw4uaRLsT41RGrPLT5Aip8ZPcntawAfIo+KivBQUV0erY1IvHYjyfFzq1ld/Fo2Ith9He6mxpPifA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+
+"@parcel/transformer-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-raw/-/transformer-raw-2.8.3.tgz#3a22213fe18a5f83fd78889cb49f06e059cfead7"
+  integrity sha512-G+5cXnd2/1O3nV/pgRxVKZY/HcGSseuhAe71gQdSQftb8uJEURyUHoQ9Eh0JUD3MgWh9V+nIKoyFEZdf9T0sUQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/transformer-react-refresh-wrap@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.8.3.tgz#8b0392638405dd470a886002229f7889d5464822"
+  integrity sha512-q8AAoEvBnCf/nPvgOwFwKZfEl/thwq7c2duxXkhl+tTLDRN2vGmyz4355IxCkavSX+pLWSQ5MexklSEeMkgthg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    react-refresh "^0.9.0"
+
+"@parcel/transformer-svg@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-svg/-/transformer-svg-2.8.3.tgz#4df959cba4ebf45d7aaddd540f752e6e84df38b2"
+  integrity sha512-3Zr/gBzxi1ZH1fftH/+KsZU7w5GqkmxlB0ZM8ovS5E/Pl1lq1t0xvGJue9m2VuQqP8Mxfpl5qLFmsKlhaZdMIQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+
+"@parcel/types@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/types/-/types-2.8.3.tgz#3306bc5391b6913bd619914894b8cd84a24b30fa"
+  integrity sha512-FECA1FB7+0UpITKU0D6TgGBpGxYpVSMNEENZbSJxFSajNy3wrko+zwBKQmFOLOiPcEtnGikxNs+jkFWbPlUAtw==
+  dependencies:
+    "@parcel/cache" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/workers" "2.8.3"
+    utility-types "^3.10.0"
+
+"@parcel/utils@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.8.3.tgz#0d56c9e8e22c119590a5e044a0e01031965da40e"
+  integrity sha512-IhVrmNiJ+LOKHcCivG5dnuLGjhPYxQ/IzbnF2DKNQXWBTsYlHkJZpmz7THoeLtLliGmSOZ3ZCsbR8/tJJKmxjA==
+  dependencies:
+    "@parcel/codeframe" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/markdown-ansi" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    chalk "^4.1.0"
+
+"@parcel/watcher@^2.0.7":
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545"
+  integrity sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==
+  dependencies:
+    is-glob "^4.0.3"
+    micromatch "^4.0.5"
+    node-addon-api "^3.2.1"
+    node-gyp-build "^4.3.0"
+
+"@parcel/workers@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-2.8.3.tgz#255450ccf4db234082407e4ddda5fd575f08c235"
+  integrity sha512-+AxBnKgjqVpUHBcHLWIHcjYgKIvHIpZjN33mG5LG9XXvrZiqdWvouEzqEXlVLq5VzzVbKIQQcmsvRy138YErkg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chrome-trace-event "^1.0.2"
+    nullthrows "^1.1.1"
+
+"@swc/helpers@^0.4.12":
+  version "0.4.14"
+  resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
+  integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==
+  dependencies:
+    tslib "^2.4.0"
+
+"@trysound/sax@0.2.0":
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
+  integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
+
+"@types/parse-json@^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
+  integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+
+abortcontroller-polyfill@^1.1.9:
+  version "1.7.5"
+  resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed"
+  integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==
+
+acorn@^8.5.0:
+  version "8.8.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
+  integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
+
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+base-x@^3.0.8:
+  version "3.0.9"
+  resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320"
+  integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==
+  dependencies:
+    safe-buffer "^5.0.1"
+
+boolbase@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
+braces@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+  dependencies:
+    fill-range "^7.0.1"
+
+browserslist@^4.6.6:
+  version "4.21.4"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
+  integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
+  dependencies:
+    caniuse-lite "^1.0.30001400"
+    electron-to-chromium "^1.4.251"
+    node-releases "^2.0.6"
+    update-browserslist-db "^1.0.9"
+
+buffer-from@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+  integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+caniuse-lite@^1.0.30001400:
+  version "1.0.30001446"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz#6d4ba828ab19f49f9bcd14a8430d30feebf1e0c5"
+  integrity sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==
+
+chalk@^2.0.0:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^4.1.0:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chrome-trace-event@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
+  integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
+
+clone@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
+  integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
+
+color-convert@^1.9.0:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@^7.0.0, commander@^7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+  integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
+cosmiconfig@^7.0.1:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
+  integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
+  dependencies:
+    "@types/parse-json" "^4.0.0"
+    import-fresh "^3.2.1"
+    parse-json "^5.0.0"
+    path-type "^4.0.0"
+    yaml "^1.10.0"
+
+css-select@^4.1.3:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
+  integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^6.0.1"
+    domhandler "^4.3.1"
+    domutils "^2.8.0"
+    nth-check "^2.0.1"
+
+css-tree@^1.1.2, css-tree@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
+  integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
+  dependencies:
+    mdn-data "2.0.14"
+    source-map "^0.6.1"
+
+css-what@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+  integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
+csso@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
+  integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
+  dependencies:
+    css-tree "^1.1.2"
+
+detect-libc@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+  integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
+
+dom-serializer@^1.0.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
+  integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.2.0"
+    entities "^2.0.0"
+
+domelementtype@^2.0.1, domelementtype@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+  integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^4.2.0, domhandler@^4.2.2, domhandler@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
+  integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
+  dependencies:
+    domelementtype "^2.2.0"
+
+domutils@^2.8.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
+  integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
+  dependencies:
+    dom-serializer "^1.0.1"
+    domelementtype "^2.2.0"
+    domhandler "^4.2.0"
+
+dotenv-expand@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
+  integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
+
+dotenv@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c"
+  integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==
+
+electron-to-chromium@^1.4.251:
+  version "1.4.284"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592"
+  integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==
+
+entities@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
+  integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+
+entities@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
+  integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
+
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+escalade@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+get-port@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119"
+  integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==
+
+globals@^13.2.0:
+  version "13.19.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8"
+  integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==
+  dependencies:
+    type-fest "^0.20.2"
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+htmlnano@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-2.0.3.tgz#50ee639ed63357d4a6c01309f52a35892e4edc2e"
+  integrity sha512-S4PGGj9RbdgW8LhbILNK7W9JhmYP8zmDY7KDV/8eCiJBQJlbmltp5I0gv8c5ntLljfdxxfmJ+UJVSqyH4mb41A==
+  dependencies:
+    cosmiconfig "^7.0.1"
+    posthtml "^0.16.5"
+    timsort "^0.3.0"
+
+htmlparser2@^7.1.1:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5"
+  integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.2.2"
+    domutils "^2.8.0"
+    entities "^3.0.1"
+
+import-fresh@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-glob@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-json@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-json/-/is-json-2.0.1.tgz#6be166d144828a131d686891b983df62c39491ff"
+  integrity sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+json-parse-even-better-errors@^2.3.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+  integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json5@^2.2.0, json5@^2.2.1:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+lightningcss-darwin-arm64@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.18.0.tgz#bcd7d494d99c69947abd71136a42e80dfa80c682"
+  integrity sha512-OqjydwtiNPgdH1ByIjA1YzqvDG/OMR6L3LPN6wRl1729LB0y4Mik7L06kmZaTb+pvUHr+NmDd2KCwnlrQ4zO3w==
+
+lightningcss-darwin-x64@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.18.0.tgz#952abea2405fe2bb8dd0bb57a9d5590f8d1d6414"
+  integrity sha512-mNiuPHj89/JHZmJMp+5H8EZSt6EL5DZRWJ31O6k3DrLLnRIQjXuXdDdN8kP7LoIkeWI5xvyD60CsReJm+YWYAw==
+
+lightningcss-linux-arm-gnueabihf@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.18.0.tgz#23ca85e05dc4def9b4975aef307554ef292b56cd"
+  integrity sha512-S+25JjI6601HiAVoTDXW6SqH+E94a+FHA7WQqseyNHunOgVWKcAkNEc2LJvVxgwTq6z41sDIb9/M3Z9wa9lk4A==
+
+lightningcss-linux-arm64-gnu@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.18.0.tgz#6c8e0a6e2c8b44cf180f3a0f0740402e8f656155"
+  integrity sha512-JSqh4+21dCgBecIQUet35dtE4PhhSEMyqe3y0ZNQrAJQ5kyUPSQHiw81WXnPJcOSTTpG0TyMLiC8K//+BsFGQA==
+
+lightningcss-linux-arm64-musl@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.18.0.tgz#88393c101cf236ea0cdc97fddd66b82db964d835"
+  integrity sha512-2FWHa8iUhShnZnqhn2wfIcK5adJat9hAAaX7etNsoXJymlliDIOFuBQEsba2KBAZSM4QqfQtvRdR7m8i0I7ybQ==
+
+lightningcss-linux-x64-gnu@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.18.0.tgz#ad068d24836568337bfe545650565e13f813c8ee"
+  integrity sha512-plCPGQJtDZHcLVKVRLnQVF2XRsIC32WvuJhQ7fJ7F6BV98b/VZX0OlX05qUaOESD9dCDHjYSfxsgcvOKgCWh7A==
+
+lightningcss-linux-x64-musl@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.18.0.tgz#4d84de26b8185aa42450e0f4c83bbfb5a36ae750"
+  integrity sha512-na+BGtVU6fpZvOHKhnlA0XHeibkT3/46nj6vLluG3kzdJYoBKU6dIl7DSOk++8jv4ybZyFJ0aOFMMSc8g2h58A==
+
+lightningcss-win32-x64-msvc@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.18.0.tgz#f83952d16b83dfce65f4615f87c867769220d117"
+  integrity sha512-5qeAH4RMNy2yMNEl7e5TI6upt/7xD2ZpHWH4RkT8iJ7/6POS5mjHbXWUO9Q1hhDhqkdzGa76uAdMzEouIeCyNw==
+
+lightningcss@^1.16.1:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.18.0.tgz#ca3327a1a7571a83bbb9733ed4e4cded775bdadf"
+  integrity sha512-uk10tNxi5fhZqU93vtYiQgx/8a9f0Kvtj5AXIm+VlOXY+t/DWDmCZWJEkZJmmALgvbS6aAW8or+Kq85eJ6TDTw==
+  dependencies:
+    detect-libc "^1.0.3"
+  optionalDependencies:
+    lightningcss-darwin-arm64 "1.18.0"
+    lightningcss-darwin-x64 "1.18.0"
+    lightningcss-linux-arm-gnueabihf "1.18.0"
+    lightningcss-linux-arm64-gnu "1.18.0"
+    lightningcss-linux-arm64-musl "1.18.0"
+    lightningcss-linux-x64-gnu "1.18.0"
+    lightningcss-linux-x64-musl "1.18.0"
+    lightningcss-win32-x64-msvc "1.18.0"
+
+lines-and-columns@^1.1.6:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+  integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+lmdb@2.5.2:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.5.2.tgz#37e28a9fb43405f4dc48c44cec0e13a14c4a6ff1"
+  integrity sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA==
+  dependencies:
+    msgpackr "^1.5.4"
+    node-addon-api "^4.3.0"
+    node-gyp-build-optional-packages "5.0.3"
+    ordered-binary "^1.2.4"
+    weak-lru-cache "^1.2.2"
+  optionalDependencies:
+    "@lmdb/lmdb-darwin-arm64" "2.5.2"
+    "@lmdb/lmdb-darwin-x64" "2.5.2"
+    "@lmdb/lmdb-linux-arm" "2.5.2"
+    "@lmdb/lmdb-linux-arm64" "2.5.2"
+    "@lmdb/lmdb-linux-x64" "2.5.2"
+    "@lmdb/lmdb-win32-x64" "2.5.2"
+
+loose-envify@^1.1.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+mdn-data@2.0.14:
+  version "2.0.14"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
+  integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
+
+micromatch@^4.0.5:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+  dependencies:
+    braces "^3.0.2"
+    picomatch "^2.3.1"
+
+msgpackr-extract@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.2.0.tgz#4bb749b58d9764cfdc0d91c7977a007b08e8f262"
+  integrity sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==
+  dependencies:
+    node-gyp-build-optional-packages "5.0.3"
+  optionalDependencies:
+    "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-arm" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-x64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-win32-x64" "2.2.0"
+
+msgpackr@^1.5.4:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.1.tgz#2298aed8a14f83e99df77d344cbda3e436f29b5b"
+  integrity sha512-05fT4J8ZqjYlR4QcRDIhLCYKUOHXk7C/xa62GzMKj74l3up9k2QZ3LgFc6qWdsPHl91QA2WLWqWc8b8t7GLNNw==
+  optionalDependencies:
+    msgpackr-extract "^2.2.0"
+
+node-addon-api@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
+  integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
+
+node-addon-api@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
+  integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
+
+node-gyp-build-optional-packages@5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17"
+  integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==
+
+node-gyp-build@^4.3.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055"
+  integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==
+
+node-releases@^2.0.6:
+  version "2.0.8"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae"
+  integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==
+
+nth-check@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+  integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+  dependencies:
+    boolbase "^1.0.0"
+
+nullthrows@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
+  integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==
+
+ordered-binary@^1.2.4:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.0.tgz#6bb53d44925f3b8afc33d1eed0fa15693b211389"
+  integrity sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ==
+
+parcel@^2.8.3:
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.8.3.tgz#1ff71d7317274fd367379bc7310a52c6b75d30c2"
+  integrity sha512-5rMBpbNE72g6jZvkdR5gS2nyhwIXaJy8i65osOqs/+5b7zgf3eMKgjSsDrv6bhz3gzifsba6MBJiZdBckl+vnA==
+  dependencies:
+    "@parcel/config-default" "2.8.3"
+    "@parcel/core" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/reporter-cli" "2.8.3"
+    "@parcel/reporter-dev-server" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chalk "^4.1.0"
+    commander "^7.0.0"
+    get-port "^4.2.0"
+    v8-compile-cache "^2.0.0"
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+parse-json@^5.0.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+  integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    error-ex "^1.3.1"
+    json-parse-even-better-errors "^2.3.0"
+    lines-and-columns "^1.1.6"
+
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picocolors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+postcss-value-parser@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+  integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+posthtml-parser@^0.10.1:
+  version "0.10.2"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.10.2.tgz#df364d7b179f2a6bf0466b56be7b98fd4e97c573"
+  integrity sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==
+  dependencies:
+    htmlparser2 "^7.1.1"
+
+posthtml-parser@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.11.0.tgz#25d1c7bf811ea83559bc4c21c189a29747a24b7a"
+  integrity sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==
+  dependencies:
+    htmlparser2 "^7.1.1"
+
+posthtml-render@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-3.0.0.tgz#97be44931496f495b4f07b99e903cc70ad6a3205"
+  integrity sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==
+  dependencies:
+    is-json "^2.0.1"
+
+posthtml@^0.16.4, posthtml@^0.16.5:
+  version "0.16.6"
+  resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.16.6.tgz#e2fc407f67a64d2fa3567afe770409ffdadafe59"
+  integrity sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==
+  dependencies:
+    posthtml-parser "^0.11.0"
+    posthtml-render "^3.0.0"
+
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
+
+react-dom@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+  integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
+  dependencies:
+    loose-envify "^1.1.0"
+    scheduler "^0.23.0"
+
+react-error-overlay@6.0.9:
+  version "6.0.9"
+  resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
+  integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
+
+react-refresh@^0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf"
+  integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==
+
+react@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+  integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
+  dependencies:
+    loose-envify "^1.1.0"
+
+regenerator-runtime@^0.13.7:
+  version "0.13.11"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
+  integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+safe-buffer@^5.0.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+scheduler@^0.23.0:
+  version "0.23.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+  integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
+  dependencies:
+    loose-envify "^1.1.0"
+
+semver@^5.7.0, semver@^5.7.1:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+source-map-support@~0.5.20:
+  version "0.5.21"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+  integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map@^0.6.0, source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+srcset@4:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4"
+  integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==
+
+stable@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+svgo@^2.4.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
+  integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
+  dependencies:
+    "@trysound/sax" "0.2.0"
+    commander "^7.2.0"
+    css-select "^4.1.3"
+    css-tree "^1.1.3"
+    csso "^4.2.0"
+    picocolors "^1.0.0"
+    stable "^0.1.8"
+
+term-size@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
+  integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
+
+terser@^5.2.0:
+  version "5.16.1"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.1.tgz#5af3bc3d0f24241c7fb2024199d5c461a1075880"
+  integrity sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==
+  dependencies:
+    "@jridgewell/source-map" "^0.3.2"
+    acorn "^8.5.0"
+    commander "^2.20.0"
+    source-map-support "~0.5.20"
+
+timsort@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
+  integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+tslib@^2.4.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
+  integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
+
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+update-browserslist-db@^1.0.9:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
+  integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==
+  dependencies:
+    escalade "^3.1.1"
+    picocolors "^1.0.0"
+
+utility-types@^3.10.0:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b"
+  integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==
+
+v8-compile-cache@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+  integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
+weak-lru-cache@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19"
+  integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==
+
+xxhash-wasm@^0.4.2:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz#752398c131a4dd407b5132ba62ad372029be6f79"
+  integrity sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==
+
+yaml@^1.10.0:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
+  integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
diff --git a/users/wpcarro/terraform/.gitignore b/users/wpcarro/terraform/.gitignore
new file mode 100644
index 0000000000..f437e99d80
--- /dev/null
+++ b/users/wpcarro/terraform/.gitignore
@@ -0,0 +1,4 @@
+*.tfstate
+*.tfstate.backup
+.terraform.lock.hcl
+.terraform/**/*
\ No newline at end of file
diff --git a/users/wpcarro/terraform/default.nix b/users/wpcarro/terraform/default.nix
new file mode 100644
index 0000000000..b8625c3b8d
--- /dev/null
+++ b/users/wpcarro/terraform/default.nix
@@ -0,0 +1,192 @@
+{ depot, pkgs, lib, ... }:
+
+let
+  inherit (builtins) concatLists concatStringsSep toJSON unsafeDiscardStringContext;
+  inherit (depot.users) wpcarro;
+  inherit (pkgs) writeText;
+
+  images = import (pkgs.path + "/nixos/modules/virtualisation/gce-images.nix");
+  nixosImage = images."20.09";
+in
+{
+  googleCloudVM =
+    { project
+    , name
+    , region
+    , zone
+    , configuration
+    , extraConfig ? { }
+    ,
+    }:
+    let
+      inherit (configuration.users.users) root;
+      inherit (configuration.networking) firewall;
+
+      # Convert NixOS-style port numbers to Terraform-style.
+      asStrings = xs: map toString xs;
+      asRanges = xs: map (x: "${toString x.from}-${toString x.to}") xs;
+
+      sshKeys = concatStringsSep "\n"
+        (map (key: "root:${key}") root.openssh.authorizedKeys.keys);
+
+      os = depot.ops.nixos.nixosFor (_: {
+        imports = [
+          (pkgs.path + "/nixos/modules/virtualisation/google-compute-image.nix")
+          configuration
+        ];
+
+        networking.hostName = name;
+
+        fileSystems."/nix" = {
+          device = "/dev/disk/by-label/google-${name}-disk";
+          fsType = "ext4";
+        };
+      });
+
+      osRoot = os.config.system.build.toplevel;
+      osPath = unsafeDiscardStringContext (toString osRoot.outPath);
+      drvPath = unsafeDiscardStringContext (toString osRoot.drvPath);
+    in
+    {
+      inherit drvPath osPath;
+      json = writeText "terraform.tf.json" (toJSON (lib.recursiveUpdate extraConfig {
+        provider.google = {
+          inherit project region zone;
+        };
+
+        resource.google_compute_instance."${name}" = {
+          inherit name zone;
+          machine_type = "e2-standard-2";
+
+          tags = [
+            "http-server"
+            "https-server"
+            "${name}-firewall"
+          ];
+
+          boot_disk = {
+            device_name = "boot";
+            initialize_params = {
+              size = 10;
+              image = "projects/nixos-cloud/global/images/${nixosImage.name}";
+            };
+          };
+
+          attached_disk = {
+            source = "\${google_compute_disk.${name}.id}";
+            device_name = "${name}-disk";
+          };
+
+          network_interface = {
+            network = "default";
+            subnetwork = "default";
+            access_config = { };
+          };
+
+          # Copy root's SSH keys from the NixOS configuration and expose them to the
+          # metadata server.
+          metadata = {
+            inherit sshKeys;
+            ssh-keys = sshKeys;
+
+            # NixOS's fetch-instance-ssh-keys.bash relies on these fields being
+            # available on the metadata server.
+            ssh_host_ed25519_key = "\${tls_private_key.${name}.private_key_pem}";
+            ssh_host_ed25519_key_pub = "\${tls_private_key.${name}.public_key_pem}";
+
+            # Even though we have SSH access, having oslogin can still be useful for
+            # troubleshooting in the browser if for some reason SSH isn't working as
+            # expected.
+            enable-oslogin = "TRUE";
+          };
+
+          service_account.scopes = [ "cloud-platform" ];
+        };
+
+        resource.tls_private_key."${name}" = {
+          algorithm = "ECDSA";
+          ecdsa_curve = "P384";
+        };
+
+        resource.google_compute_firewall."${name}" = {
+          name = "${name}-firewall";
+          network = "default";
+
+          # Read the firewall configuration from the NixOS configuration.
+          allow = [
+            {
+              protocol = "tcp";
+              ports = concatLists [
+                (asStrings (firewall.allowedTCPPorts or [ ]))
+                (asRanges (firewall.allowedTCPPortRanges or [ ]))
+              ];
+            }
+            {
+              protocol = "udp";
+              ports = concatLists [
+                (asStrings (firewall.allowedUDPPorts or [ ]))
+                (asRanges (firewall.allowedUDPPortRanges or [ ]))
+              ];
+            }
+          ];
+          source_ranges = [ "0.0.0.0/0" ];
+        };
+
+        resource.google_compute_disk."${name}" = {
+          inherit zone;
+          name = "${name}-disk";
+          size = 100;
+        };
+
+        resource.null_resource.deploy_nixos = {
+          triggers = {
+            # Redeploy when the NixOS configuration changes.
+            os = "${osPath}";
+            # Redeploy when a new machine is provisioned.
+            machine_id = "\${google_compute_instance.${name}.id}";
+          };
+
+          connection = {
+            host = "\${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip}";
+          };
+
+          provisioner = [
+            { remote-exec.inline = [ "true" ]; }
+            {
+              local-exec.command = ''
+                export PATH="${pkgs.openssh}/bin:$PATH"
+
+                scratch="$(mktemp -d)"
+                function cleanup() {
+                  rm -rf $scratch
+                }
+                trap cleanup EXIT
+
+                # write out ssh key
+                echo -n "''${tls_private_key.${name}.private_key_pem}" > $scratch/id_rsa.pem
+                chmod 0600 $scratch/id_rsa.pem
+
+                export NIX_SSHOPTS="\
+                  -o StrictHostKeyChecking=no\
+                  -o UserKnownHostsFile=/dev/null\
+                  -o GlobalKnownHostsFile=/dev/null\
+                  -o IdentityFile=$scratch/id_rsa.pem
+                "
+
+                nix-build ${drvPath}
+                nix-copy-closure --to \
+                  root@''${google_compute_instance.${name}.network_interface[0].access_config[0].nat_ip} \
+                  ${osPath} --gzip --use-substitutes
+              '';
+            }
+            {
+              remote-exec.inline = [
+                "nix-env --profile /nix/var/nix/profiles/system --set ${osPath}"
+                "${osPath}/bin/switch-to-configuration switch"
+              ];
+            }
+          ];
+        };
+      }));
+    };
+}
diff --git a/users/wpcarro/todo-lists/cta-curriculum.csv b/users/wpcarro/todo-lists/cta-curriculum.csv
new file mode 100644
index 0000000000..7ebc82d645
--- /dev/null
+++ b/users/wpcarro/todo-lists/cta-curriculum.csv
@@ -0,0 +1,108 @@
+name,position,goal

+collar choke,back,submission

+bow & arrow choke,back,submission

+rear naked choke,back,submission

+armlock,back,submission

+collar choke escape,back,escape

+framing defense,back,defense

+pulling the arm to the other side (I),back,defense

+pulling the arm to the other side (II),back,defense

+back escape to 1x-leg,back,escape

+changing sides,back,defense

+opening closed guard (sleeve),closed guard,escape

+opening closed guard (hips),closed guard,escape

+opening closed guard (hips -> sleeve),closed guard,escape

+catucada (I),closed guard,sweep

+catucada (II),closed guard,sweep

+sit-up sweep,closed guard,sweep

+scissor sweep,closed guard,sweep

+2x ankle sweep,closed guard,sweep

+sit-up sweep -> kimura (I),closed guard,submission

+sit-up sweep -> kimura (variations),closed guard,submission

+omoplata,closed guard,submission

+omoplata escape -> side control,closed guard,escape

+omoplata escape -> standing,closed guard,escape

+overhook triangle (I),closed guard,submission

+overhook triangle (II),closed guard,submission

+armlock,closed guard,submission

+flower sweep,closed guard,sweep

+kimura,closed guard,submission

+triangle defense,closed guard,defense

+triangle escape,closed guard,escape

+armlock escape,closed guard,escape

+half guard -> closed guard (I),half guard,transition

+half guard -> closed guard (II),half guard,transition

+upa,half guard,sweep

+half guard -> back,half guard,transition

+underhook sweep,half guard,sweep

+knee slide pass (backstep),half guard,pass

+knee slide pass (hip-switch, knee-cut),half guard,pass

+knee slide pass (push the knee),half guard,pass

+knee slide pass (2x-hook magic),half guard,pass

+tripod pass (backstep),half guard,pass

+tripod pass (hip-switch, knee-cut),half guard,pass

+tripod pass (push the knee),half guard,pass

+tripod pass (2x-hook magic),half guard,pass

+keylock,mount,submission

+keylock -> armlock (I),mount,submission

+keylock -> armlock (II),mount,submission

+upa,mount,sweep

+cross-choke defense,mount,defense

+keylock escape,mount,escape

+hip press escape (straight back),mount,escape

+hip press escape (sideways),mount,escape

+elbow escape,mount,escape

+ezekiel choke,mount,submission

+retaining low mount,mount,retention

+retaining high mount,mount,retention

+armlock,mount,submission

+armlock escape,mount,escape

+armlock (breaking the grips),mount,submission

+cross-choke (I),mount,submission

+cross-choke (II),mount,submission

+bull pass,open guard,pass

+2x-under,open guard,pass

+1x-under,open guard,pass

+1x-under -> half guard,open guard,pass

+straight ankle lock,open guard,submission

+straight ankle lock defense,open guard,defense

+straight ankle lock defense -> mount,open guard,escape

+side control -> mount,side control,transition

+armlock (same side),side control,submission

+armlock escape (hitchhiker),side control,escape

+kimura,side control,submission

+kimura -> armlock,side control,submission

+kimura (breaking the grips),side control,submission

+escape (doorstop),side control,escape

+modern hip escape,side control,escape

+escape,side control,escape

+kesagatame escape,side control,escape

+kesagatame escape (from punches),side control,escape

+retention,knee on belly,retention

+armlock,knee on belly,submission

+knee on belly escape,knee on belly,escape

+knee on belly -> mount,knee on belly,transition

+pulling closed guard,standing,transition

+pulling to armbar,standing,submission

+pendulum sweep,standing,sweep

+2x-ankle sweep,standing,sweep

+collar drag to 1x-leg,standing,sweep

+collar drag sweep,standing,sweep

+collar drag (seated),standing,transition

+hip throw from neck control,standing,escape

+hip throw to armbar,standing,submission

+osoto gari from neck control,standing,escape

+osoto gari to armbar,standing,submission

+basic osoto gari,standing,takedown

+1x-leg,standing,takedown

+guillotine (arm out),standing,submission

+guillotine (arm out) escape,standing,escape

+headlock escape,standing,escape

+headlock escape (from punches),standing,escape

+guillotine (arm in),standing,submission

+guillotine (arm in) escape,standing,escape

+outside trip -> 2x-leg,standing,takedown

+bear hug escape,standing,escape

+body lock escape,standing,escape

+2x-leg,standing,takedown

+2x-leg sprawl defense to back,standing,defense
\ No newline at end of file
diff --git a/users/wpcarro/todo-lists/imdb/db.sqlite3 b/users/wpcarro/todo-lists/imdb/db.sqlite3
new file mode 100644
index 0000000000..bb893387ec
--- /dev/null
+++ b/users/wpcarro/todo-lists/imdb/db.sqlite3
Binary files differdiff --git a/users/wpcarro/todo-lists/imdb/imdb-top-250.org b/users/wpcarro/todo-lists/imdb/imdb-top-250.org
new file mode 100644
index 0000000000..58a52392ca
--- /dev/null
+++ b/users/wpcarro/todo-lists/imdb/imdb-top-250.org
@@ -0,0 +1,256 @@
+# A few years ago, I set a goal to watch every movie on IMDb.com's "Top 250"
+# movies list. The list changes frequently, so I took a snapshot of it so that
+# I wouldn't be trying to hit a moving target.
+#
+# Here is my progress thus far:
+* IMDB Top 250
+** DONE The Shawshank Redemption
+** DONE The Godfather
+** DONE The Dark Knight
+** DONE The Godfather: Part II
+** DONE The Lord of the Rings: The Return of the King
+** DONE Pulp Fiction
+** DONE Schindler's List
+** DONE The Good, the Bad and the Ugly
+** DONE 12 Angry Men
+** DONE Inception
+** DONE Fight Club
+** DONE The Lord of the Rings: The Fellowship of the Ring
+** DONE Forrest Gump
+** DONE The Lord of the Rings: The Two Towers
+** DONE The Matrix
+** DONE Goodfellas
+** TODO Star Wars: Episode V - The Empire Strikes Back
+** DONE One Flew Over the Cuckoo's Nest
+** DONE Seven Samurai
+** DONE Interstellar
+** DONE City of God
+** TODO Spirited Away
+** DONE Saving Private Ryan
+** DONE The Green Mile
+** DONE Life Is Beautiful
+** DONE The Usual Suspects
+** DONE Se7en
+** DONE Leon
+** DONE The Silence of the Lambs
+** TODO Star Wars: Episode IV - A New Hope
+** DONE It's a Wonderful Life
+** DONE Andhadhun
+** DONE Dangal
+** DONE Spider-Man: Into the Spider-Verse
+** TODO Avengers: Infinity War
+** DONE Whiplash
+** DONE Untouchable
+** DONE The Prestige
+** DONE The Departed
+** DONE The Pianist
+** DONE Memento
+** DONE Gladiator
+** DONE American History X
+** DONE The Lion King
+** DONE Terminator 2: Judgment Day
+** DONE Cinema Paradiso
+** DONE Grave of the Fireflies
+** DONE Back to the Future
+** DONE Indiana Jones and the Raiders of the Lost Ark
+** DONE Apocalypse Now
+** TODO Alien
+** DONE Once Upon a Time in the West
+** DONE Psycho
+** DONE Rear Window
+** DONE Casablanca
+** TODO The Great Dictator
+** TODO Modern Times
+** TODO City Lights
+** TODO Kimi no na wa.
+** DONE Coco
+** DONE Django Unchained
+** DONE The Dark Knight Rises
+** DONE 3 Idiots
+** TODO Taare Zameen Par
+** DONE WALLยทE
+** TODO Babam ve Oglum
+** DONE The Lives of Others
+** DONE Old boy
+** DONE American Beauty
+** DONE Princess Mononoke
+** DONE Braveheart
+** TODO Aliens
+** DONE Once Upon a Time in America
+** TODO Das Boot
+** DONE The Shining
+** DONE Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb
+** TODO Witness for the Prosecution
+** DONE Paths of Glory
+** TODO Sunset Blvd.
+** DONE Green Book
+** DONE The Hunt
+** DONE Jodaeiye Nader az Simin
+** DONE Incendies
+** DONE Toy Story 3
+** DONE Inglourious Basterds
+** DONE Eternal Sunshine of the Spotless Mind
+** DONE Amelie
+** DONE Snatch
+** DONE Requiem for a Dream
+** TODO Neon Genesis Evangelion: The End of Evangelion
+** DONE L.A. Confidential
+** DONE Good Will Hunting
+** TODO Bacheha-Ye aseman
+** TODO Eskiya
+** DONE Toy Story
+** DONE Reservoir Dogs
+** DONE Full Metal Jacket
+** DONE Amadeus
+** DONE Scarface
+** TODO Star Wars: Episode VI - Return of the Jedi
+** DONE Taxi Driver
+** DONE Monty Python and the Holy Grail
+** DONE The Sting
+** DONE A Clockwork Orange
+** DONE 2001: A Space Odyssey
+** TODO For a Few Dollars More
+** TODO To Kill a Mockingbird
+** TODO Lawrence of Arabia
+** TODO Yojimbo
+** DONE The Apartment
+** TODO North by Northwest
+** DONE Vertigo
+** TODO Singin' in the Rain
+** TODO Ikiru
+** TODO Rashomon
+** TODO All About Eve
+** TODO Bicycle Thieves
+** TODO Double Indemnity
+** TODO Citizen Kane
+** TODO M
+** TODO Metropolis
+** TODO The Kid
+** DONE Three Billboards Outside Ebbing, Missouri
+** DONE Room
+** TODO PK
+** DONE Inside Out
+** DONE El secreto de sus ojos
+** DONE Warrior
+** DONE Up
+** DONE The Wolf of Wall Street
+** DONE There Will Be Blood
+** DONE Pan's Labyrinth
+** DONE V for Vendetta
+** TODO Rang De Basanti
+** DONE Batman Begins
+** DONE Downfall
+** TODO Howl's Moving Castle
+** DONE A Beautiful Mind
+** DONE Lock, Stock and Two Smoking Barrels
+** DONE Trainspotting
+** DONE Heat
+** DONE Casino
+** DONE Unforgiven
+** TODO Indiana Jones and the Last Crusade
+** DONE My Neighbour Totoro
+** DONE Die Hard
+** TODO Come and See
+** TODO Ran
+** DONE Blade Runner
+** DONE Raging Bull
+** TODO The Elephant Man
+** DONE Chinatown
+** TODO Andrei Rublev
+** DONE The Great Escape
+** TODO Judgment at Nuremberg
+** TODO Some Like It Hot
+** TODO Wild Strawberries
+** TODO The Seventh Seal
+** TODO The Bridge on the River Kwai
+** TODO On the Waterfront
+** TODO Dial M for Murder
+** TODO Tokyo Story
+** TODO The Third Man
+** TODO The Treasure of the Sierra Madre
+** TODO Mr. Smith Goes to Washington
+** TODO Gone with the Wind
+** TODO Sunrise: A Song of Two Humans
+** TODO The General
+** TODO The Gold Rush
+** TODO Sherlock Jr.
+** DONE The Handmaiden
+** DONE Logan
+** TODO Relatos salvajes
+** DONE The Grand Budapest Hotel
+** DONE Gone Girl
+** DONE Hacksaw Ridge
+** TODO 12 Years a Slave
+** DONE Guardians of the Galaxy
+** DONE Rush
+** DONE Spotlight
+** TODO Song of the Sea
+** TODO The Help
+** DONE Prisoners
+** DONE Mad Max: Fury Road
+** DONE Gran Torino
+** TODO Harry Potter and the Deathly Hallows: Part 2
+** DONE Shutter Island
+** DONE Hachi: A Dog's Tale
+** DONE Mary and Max
+** DONE How to Train Your Dragon
+** DONE Into the Wild
+** DONE No Country for Old Men
+** DONE Million Dollar Baby
+** DONE Hotel Rwanda
+** TODO Before Sunset
+** TODO Memories of Murder
+** DONE Kill Bill: Vol. 1
+** DONE Finding Nemo
+** DONE Catch Me If You Can
+** TODO Donnie Darko
+** DONE Amores Perros
+** DONE Monsters, Inc.
+** DONE The Sixth Sense
+** DONE The Truman Show
+** DONE The Big Lebowski
+** TODO In the Mood for Love
+** DONE Fargo
+** TODO La Haine
+** TODO Before Sunrise
+** TODO Three Colours: Red
+** DONE Jurassic Park
+** DONE In the Name of the Father
+** DONE Dead Poets Society
+** TODO Akira
+** DONE The Princess Bride
+** TODO Laputa: Castle in the Sky
+** DONE Stand by Me
+** DONE Platoon
+** TODO Paris, Texas
+** TODO Nausicaa of the Valley of the Wind
+** DONE The Thing
+** TODO Gandhi
+** TODO Fanny and Alexander
+** TODO Stalker
+** DONE Life of Brian
+** DONE The Deer Hunter
+** TODO Rocky
+** TODO Network
+** TODO Barry Lyndon
+** TODO Butch Cassidy and the Sundance Kid
+** DONE Cool Hand Luke
+** TODO Persona
+** TODO The 400 Blows
+** TODO Ben-Hur
+** TODO The Nights of Cabiria
+** TODO Les Diaboliques
+** TODO The Wages of Fear
+** TODO The Best Years of Our Lives
+** TODO The Maltese Falcon
+** TODO Rebecca
+** TODO The Grapes of Wrath
+** TODO It Happened One Night
+** TODO La passion de Jeanne d'Arc
+** DONE Pirates of the Caribbean: The Curse of the Black Pearl
+** DONE Groundhog Day
+** DONE Beauty and the Beast
+** DONE The Terminator
+** DONE Jaws
+** DONE The Exorcist
+** DONE The Wizard of Oz
diff --git a/users/wpcarro/todo-lists/imdb/scratch.sql b/users/wpcarro/todo-lists/imdb/scratch.sql
new file mode 100644
index 0000000000..6835c73bd8
--- /dev/null
+++ b/users/wpcarro/todo-lists/imdb/scratch.sql
@@ -0,0 +1,65 @@
+-- which directors appear most often
+SELECT director, COUNT(*)
+FROM Movies
+GROUP BY director
+ORDER BY COUNT(*) DESC
+LIMIT 10;
+
+-- top-rated, most recent movies
+SELECT *
+FROM (
+  SELECT *
+  FROM Movies
+  ORDER BY rating DESC
+  LIMIT 20
+)
+ORDER BY YEAR DESC;
+
+-- top-rated, most recent movies (ignore foreign)
+SELECT *
+FROM (
+  SELECT *
+  FROM Movies
+  WHERE requiresSubtitles = 0
+  ORDER BY rating DESC
+  LIMIT 20
+)
+ORDER BY YEAR DESC;
+
+-- most recent movies
+SELECT *
+FROM Movies
+ORDER BY YEAR DESC
+LIMIT 15;
+
+-- most recent movies (ignore foreign)
+SELECT *
+FROM Movies
+WHERE requiresSubtitles = 0
+ORDER BY YEAR DESC
+LIMIT 10;
+
+-- only cartoons
+SELECT *
+FROM Movies
+WHERE isCartoon = true;
+
+-- only cartoons (ignore foreign)
+SELECT *
+FROM Movies
+WHERE isCartoon = true AND requiresSubtitles = false;
+
+-- show the movies from the directors that show up on the list more than once.
+SELECT *
+FROM Movies
+WHERE director in (
+  SELECT director
+  FROM (
+    SELECT director, COUNT(*) as num
+    FROM Movies
+    GROUP BY director
+    HAVING num > 1
+    ORDER BY num DESC
+  )
+)
+ORDER BY director, rating DESC, year DESC;
diff --git a/users/wpcarro/todo-lists/paul-graham-essays.org b/users/wpcarro/todo-lists/paul-graham-essays.org
new file mode 100644
index 0000000000..7cddcef478
--- /dev/null
+++ b/users/wpcarro/todo-lists/paul-graham-essays.org
@@ -0,0 +1,190 @@
+# I'd like to read all of Paul Graham's essays. I cannot rely on my web browser
+# to tell me which I've already read, so I'm resorting to an org file.
+* TODO How to Write Usefully
+* DONE Being a Noob
+* TODO Haters
+* TODO The Two Kinds of Moderate
+* TODO Fashionable Problems
+* TODO Having Kids
+* DONE The Lesson to Unlearn
+* TODO Novelty and Heresy
+* TODO The Bus Ticket Theory of Genius
+* TODO General and Surprising
+* DONE Charisma / Power
+* TODO The Risk of Discovery
+* TODO How to Make Pittsburgh a Startup Hub
+* TODO Life is Short
+* TODO Economic Inequality
+* TODO The Refragmentation
+* TODO Jessica Livingston
+* TODO A Way to Detect Bias
+* TODO Write Like You Talk
+* TODO Default Alive or Default Dead?
+* TODO Why It's Safe for Founders to Be Nice
+* TODO Change Your Name
+* TODO What Microsoft Is this the Altair Basic of?
+* TODO The Ronco Principle
+* TODO What Doesn't Seem Like Work?
+* TODO Don't Talk to Corp Dev
+* TODO Let the Other 95% of Great Programmers In
+* TODO How to Be an Expert in a Changing World
+* TODO How You Know
+* TODO The Fatal Pinch
+* DONE Mean People Fail
+* TODO Before the Startup
+* TODO How to Raise Money
+* TODO Investor Herd Dynamics
+* TODO How to Convince Investors
+* TODO Do Things that Don't Scale
+* TODO Startup Investing Trends
+* TODO How to Get Startup Ideas
+* TODO The Hardware Renaissance
+* TODO Startup = Growth
+* TODO Black Swan Farming
+* TODO The Top of My Todo List
+* TODO Writing and Speaking
+* TODO How Y Combinator Started
+* TODO Defining Property
+* TODO Frighteningly Ambitious Startup Ideas
+* TODO A Word to the Resourceful
+* TODO Schlep Blindness
+* TODO Snapshot: Viaweb, June 1998
+* TODO Why Startup Hubs Work
+* TODO The Patent Pledge
+* TODO Subject: Airbnb
+* TODO Founder Control
+* TODO Tablets
+* TODO What We Look for in Founders
+* TODO The New Funding Landscape
+* TODO Where to See Silicon Valley
+* TODO High Resolution Fundraising
+* TODO What Happened to Yahoo
+* TODO The Future of Startup Funding
+* TODO The Acceleration of Addictiveness
+* TODO The Top Idea in Your Mind
+* TODO How to Lose Time and Money
+* TODO Organic Startup Ideas
+* TODO Apple's Mistake
+* TODO What Startups Are Really Like
+* TODO Persuade xor Discover
+* TODO Post-Medium Publishing
+* TODO The List of N Things
+* TODO The Anatomy of Determination
+* TODO What Kate Saw in Silicon Valley
+* TODO The Trouble with the Segway
+* TODO Ramen Profitable
+* DONE Maker's Schedule, Manager's Schedule
+* TODO A Local Revolution?
+* TODO Why Twitter is a Big Deal
+* TODO The Founder Visa
+* TODO Five Founders
+* TODO Relentlessly Resourceful
+* TODO How to Be an Angel Investor
+* TODO Why TV Lost
+* TODO Can You Buy a Silicon Valley?  Maybe.
+* TODO What I've Learned from Hacker News
+* TODO Startups in 13 Sentences
+* TODO Keep Your Identity Small
+* TODO After Credentials
+* TODO Could VC be a Casualty of the Recession?
+* TODO The High-Res Society
+* TODO The Other Half of "Artists Ship"
+* TODO Why to Start a Startup in a Bad Economy
+* TODO A Fundraising Survival Guide
+* TODO The Pooled-Risk Company Management Company
+* TODO Cities and Ambition
+* TODO Disconnecting Distraction
+* TODO Lies We Tell Kids
+* TODO Be Good
+* TODO Why There Aren't More Googles
+* TODO Some Heroes
+* TODO How to Disagree
+* TODO You Weren't Meant to Have a Boss
+* TODO A New Venture Animal
+* TODO Trolls
+* TODO Six Principles for Making New Things
+* TODO Why to Move to a Startup Hub
+* TODO The Future of Web Startups
+* TODO How to Do Philosophy
+* TODO News from the Front
+* TODO How Not to Die
+* TODO Holding a Program in One's Head
+* TODO Stuff
+* TODO The Equity Equation
+* TODO An Alternative Theory of Unions
+* TODO The Hacker's Guide to Investors
+* TODO Two Kinds of Judgement
+* TODO Microsoft is Dead
+* TODO Why to Not Not Start a Startup
+* TODO Is It Worth Being Wise?
+* TODO Learning from Founders
+* TODO How Art Can Be Good
+* TODO The 18 Mistakes That Kill Startups
+* TODO A Student's Guide to Startups
+* TODO How to Present to Investors
+* TODO Copy What You Like
+* TODO The Island Test
+* TODO The Power of the Marginal
+* TODO Why Startups Condense in America
+* TODO How to Be Silicon Valley
+* TODO The Hardest Lessons for Startups to Learn
+* TODO See Randomness
+* TODO Are Software Patents Evil?
+* TODO 6,631,372
+* TODO Why YC
+* TODO How to Do What You Love
+* TODO Good and Bad Procrastination
+* TODO Web 2.0
+* TODO How to Fund a Startup
+* TODO The Venture Capital Squeeze
+* TODO Ideas for Startups
+* TODO What I Did this Summer
+* TODO Inequality and Risk
+* TODO After the Ladder
+* TODO What Business Can Learn from Open Source
+* TODO Hiring is Obsolete
+* TODO The Submarine
+* TODO Why Smart People Have Bad Ideas
+* TODO Return of the Mac
+* DONE Writing,  Briefly
+* TODO Undergraduation
+* TODO A Unified Theory of VC Suckage
+* TODO How to Start a Startup
+* TODO What You'll Wish You'd Known
+* TODO Made in USA
+* TODO It's Charisma, Stupid
+* TODO Bradley's Ghost
+* TODO A Version 1.0
+* TODO What the Bubble Got Right
+* TODO The Age of the Essay
+* TODO The Python Paradox
+* TODO Great Hackers
+* TODO Mind the Gap
+* TODO How to Make Wealth
+* TODO The Word "Hacker"
+* TODO What You Can't Say
+* TODO Filters that Fight Back
+* TODO Hackers and Painters
+* TODO If Lisp is So Great
+* TODO The Hundred-Year Language
+* TODO Why Nerds are Unpopular
+* TODO Better Bayesian Filtering
+* TODO Design and Research
+* TODO A Plan for Spam
+* TODO Revenge of the Nerds
+* TODO Succinctness is Power
+* TODO What Languages Fix
+* DONE Taste for Makers
+* TODO Why Arc Isn't Especially Object-Oriented
+* TODO What Made Lisp Different
+* TODO The Other Road Ahead
+* TODO The Roots of Lisp
+* DONE Five Questions about Language Design
+* DONE Being Popular
+* DONE Java's Cover
+* DONE Beating the Averages
+* DONE Lisp for Web-Based Applications
+* TODO Chapter 1 of Ansi Common Lisp
+* TODO Chapter 2 of Ansi Common Lisp
+* DONE Programming Bottom-Up
+* DONE This Year We Can End the Death Penalty in California
diff --git a/users/wpcarro/todo-lists/travel-hitlist.md b/users/wpcarro/todo-lists/travel-hitlist.md
new file mode 100644
index 0000000000..058ff6b274
--- /dev/null
+++ b/users/wpcarro/todo-lists/travel-hitlist.md
@@ -0,0 +1,83 @@
+# Hit List
+
+A crude journal of cities I have visited and cities I would like to visit.
+
+# Europe
+* ~~Berlin, Germany~~
+* ~~Hamburg, Germany~~
+* Munich, Germany
+* Heidelberg, Germany
+* ~~Geneva, Switzerland~~
+* Bern, Switzerland
+* Zurich, Switzerland
+* Lausanne, Switzerland
+* ~~Grenoble, France~~
+* ~~Lyons, France~~
+* ~~Paris, France~~
+* ~~Aix-en-Provence, France~~
+* ~~Bordeaux, France~~
+* Monaco, France
+* ~~Ibiza, Spain~~
+* ~~Formentera, Spain~~
+* Barcelona, Spain
+* ~~Lisbon, Portugal~~
+* ~~Lagos, Portugal~~
+* ~~Rome, Italy~~
+* ~~Venice, Italy~~
+* Cinque Terre, Italy
+* Milan, Italy
+* Florence, Italy
+* Oslo, Norway
+* Bergen, Norway
+* Copenhagen, Denmark
+* Reykjavik, Iceland
+* Stockholm, Sweden
+* Gothenburg, Sweden
+* ~~Amsterdam, Netherlands~~
+* Dubrovnik, Croatia
+* Split, Croatia
+* Lake Bled, Slovenia
+* Santorini, Greece
+* Vienna, Austria
+* Salzburg, Austria
+* Hallstatt, Austria
+* St. Petersburg, Russia
+* ~~London, England~~
+* Cambridge, England
+* Chester, England
+* Edinburgh, Scotland
+* ~~Dublin, Ireland~~
+* Galway, Ireland
+* Luxembourg, Luxembourg
+* Cappadocia, Turkey
+* Istanbul, Turkey
+* Ankara, Turkey
+
+# North and South America
+* Montreal, Canada
+* Quebec City, Canada
+* Vancouver, Canada
+* Oahu Hawaii, USA
+* Chicago, USA
+* New Orleans, USA
+* Mexico City, Mexico
+* Cabo San Lucas, Mexico
+* Rio de Janerio, Brazil
+* Cartegena, Colombia
+
+# Asia / Pacific
+* Gold Coast, Australia
+* Sydney, Australia
+* Auckland, New Zealand
+* Kohphiphi Islands, Thailand
+* Hong Kong, China
+* Shanghai, China
+* Xitang, China
+* Tokyo, Japan
+* Kyoto, Japan
+* Seoul, South Korea
+
+# Middle East
+* Jaffa, Israel
+* Tel Aviv, Israel
+* Beirut, Lebanon
diff --git a/users/wpcarro/tools/monzo_ynab/.envrc b/users/wpcarro/tools/monzo_ynab/.envrc
new file mode 100644
index 0000000000..6560926eae
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/.envrc
@@ -0,0 +1,9 @@
+source_up
+
+# TODO(wpcarro): Prefer age-nix solution if possible.
+export monzo_client_id="$(jq -j '.monzo | .clientId' < $WPCARRO/secrets.json)"
+export monzo_client_secret="$(jq -j '.monzo | .clientSecret' < $WPCARRO/secrets.json)"
+export ynab_personal_access_token="$(jq -j '.ynab | .personalAccessToken' < $WPCARRO/secrets.json)"
+export ynab_account_id="$(jq -j '.ynab | .accountId' < $WPCARRO/secrets.json)"
+export ynab_budget_id="$(jq -j '.ynab | .budgetId' < $WPCARRO/secrets.json)"
+export store_path="$(pwd)"
diff --git a/users/wpcarro/tools/monzo_ynab/.gitignore b/users/wpcarro/tools/monzo_ynab/.gitignore
new file mode 100644
index 0000000000..e92078303b
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/.gitignore
@@ -0,0 +1,3 @@
+/ynab/fixture.json
+/monzo/fixture.json
+/kv.json
diff --git a/users/wpcarro/tools/monzo_ynab/README.md b/users/wpcarro/tools/monzo_ynab/README.md
new file mode 100644
index 0000000000..c0c0c772f6
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/README.md
@@ -0,0 +1,41 @@
+# monzo_ynab
+
+Exporting Monzo transactions to my YouNeedABudget.com (i.e. YNAB) account. YNAB
+unfortunately doesn't currently offer an Monzo integration. As a workaround and
+a practical excuse to learn Go, I decided to write one myself.
+
+This job is going to run N times per 24 hours. Monzo offers webhooks for
+reacting to certain types of events. I don't expect I'll need realtime data for
+my YNAB integration. That may change, however, so it's worth noting.
+
+## Installation
+
+Like many other packages in this repository, `monzo_ynab` is packaged using
+Nix. To install and use, you have two options:
+
+You can install using `nix-build` and then run the resulting
+`./result/bin/monzo_ynab`.
+
+```shell
+> nix-build . && ./result/bin/monzo_ynab
+```
+
+Or you can install using `nix-env` if you'd like to create the `monzo_ynab`
+symlink.
+
+```shell
+> nix-env -iA users.wpcarro.monzo_ynab
+```
+
+## Deployment
+
+While this project is currently not deployed, my plan is to host it on Google
+Cloud and run it as a Cloud Run application. What I don't yet know is whether or
+not this is feasible or a good idea. One complication that I foresee is that the
+OAuth 2.0 login flow requires a web browser until the access token and refresh
+tokens are acquired. I'm unsure how to workaround this at the moment.
+
+For more information about the general packaging and deployment strategies I'm
+currently using, refer to the [deployments][deploy] writeup.
+
+[deploy]: ../deploy/README.md
diff --git a/users/wpcarro/tools/monzo_ynab/auth.go b/users/wpcarro/tools/monzo_ynab/auth.go
new file mode 100644
index 0000000000..b66bacb106
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/auth.go
@@ -0,0 +1,101 @@
+package auth
+
+////////////////////////////////////////////////////////////////////////////////
+// Dependencies
+////////////////////////////////////////////////////////////////////////////////
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+	"net/http"
+	"net/url"
+	"os"
+	"os/exec"
+	"utils"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Constants
+////////////////////////////////////////////////////////////////////////////////
+
+var (
+	BROWSER      = os.Getenv("BROWSER")
+	REDIRECT_URI = "http://localhost:8080/authorization-code"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Types
+////////////////////////////////////////////////////////////////////////////////
+
+// This is the response returned from Monzo when we exchange our authorization
+// code for an access token. While Monzo returns additional fields, I'm only
+// interested in AccessToken and RefreshToken.
+type accessTokenResponse struct {
+	AccessToken  string `json:"access_token"`
+	RefreshToken string `json:"refresh_token"`
+	ExpiresIn    int    `json:"expires_in"`
+}
+
+type Tokens struct {
+	AccessToken  string
+	RefreshToken string
+	ExpiresIn    int
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Functions
+////////////////////////////////////////////////////////////////////////////////
+
+// Returns the access token and refresh tokens for the Monzo API.
+func GetTokensFromAuthCode(authCode string, clientID string, clientSecret string) *Tokens {
+	res, err := http.PostForm("https://api.monzo.com/oauth2/token", url.Values{
+		"grant_type":    {"authorization_code"},
+		"client_id":     {clientID},
+		"client_secret": {clientSecret},
+		"redirect_uri":  {REDIRECT_URI},
+		"code":          {authCode},
+	})
+	utils.FailOn(err)
+	defer res.Body.Close()
+	payload := &accessTokenResponse{}
+	json.NewDecoder(res.Body).Decode(payload)
+
+	return &Tokens{payload.AccessToken, payload.RefreshToken, payload.ExpiresIn}
+}
+
+// Open a web browser to allow the user to authorize this application. Return
+// the authorization code sent from Monzo.
+func GetAuthCode(clientID string) string {
+	// TODO(wpcarro): Consider generating a random string for the state when the
+	// application starts instead of hardcoding it here.
+	state := "xyz123"
+	url := fmt.Sprintf(
+		"https://auth.monzo.com/?client_id=%s&redirect_uri=%s&response_type=code&state=%s",
+		clientID, REDIRECT_URI, state)
+	exec.Command(BROWSER, url).Start()
+
+	authCode := make(chan string)
+	go func() {
+		log.Fatal(http.ListenAndServe(":8080",
+			http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+				// 1. Get authorization code from Monzo.
+				if req.URL.Path == "/authorization-code" {
+					params := req.URL.Query()
+					reqState := params["state"][0]
+					code := params["code"][0]
+
+					if reqState != state {
+						log.Fatalf("Value for state returned by Monzo does not equal our state. %s != %s", reqState, state)
+					}
+					authCode <- code
+
+					fmt.Fprintf(w, "Authorized!")
+				} else {
+					log.Printf("Unhandled request: %v\n", *req)
+				}
+			})))
+	}()
+	result := <-authCode
+	return result
+}
diff --git a/users/wpcarro/tools/monzo_ynab/main.go b/users/wpcarro/tools/monzo_ynab/main.go
new file mode 100644
index 0000000000..900deac0cb
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/main.go
@@ -0,0 +1,48 @@
+// Exporting Monzo transactions to my YouNeedABudget.com (i.e. YNAB)
+// account. YNAB unfortunately doesn't currently offer an Monzo integration. As
+// a workaround and a practical excuse to learn Go, I decided to write one
+// myself.
+//
+// This job is going to run N times per 24 hours. Monzo offers webhooks for
+// reacting to certain types of events. I don't expect I'll need realtime data
+// for my YNAB integration. That may change, however, so it's worth noting.
+
+package main
+
+import (
+	"monzoClient"
+	"monzoSerde"
+	"os"
+	"ynabClient"
+	"ynabSerde"
+)
+
+var (
+	ynabAccountID = os.Getenv("ynab_account_id")
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Business Logic
+////////////////////////////////////////////////////////////////////////////////
+
+// Convert a Monzo transaction struct, `tx`, into a YNAB transaction struct.
+func toYnab(tx monzoSerde.Transaction) ynabSerde.Transaction {
+	return ynabSerde.Transaction{
+		Id:        tx.Id,
+		Date:      tx.Created,
+		Amount:    tx.Amount,
+		Memo:      tx.Notes,
+		AccountId: ynabAccountID,
+	}
+}
+
+func main() {
+	monzo := monzoClient.Create()
+	txs := monzo.TransactionsLast24Hours()
+	var ynabTxs []ynabSerde.Transaction
+	for _, tx := range txs {
+		ynabTxs = append(ynabTxs, toYnab(tx))
+	}
+	ynabClient.PostTransactions(ynabTxs)
+	os.Exit(0)
+}
diff --git a/users/wpcarro/tools/monzo_ynab/monzo/client.go b/users/wpcarro/tools/monzo_ynab/monzo/client.go
new file mode 100644
index 0000000000..9621ffc5ad
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/monzo/client.go
@@ -0,0 +1,52 @@
+package monzoClient
+
+import (
+	"fmt"
+	"log"
+	"monzoSerde"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+	"tokens"
+	"utils"
+)
+
+const (
+	accountID = "pizza"
+)
+
+type Client struct{}
+
+// Ensure that the token server is running and return a new instance of a Client
+// struct.
+func Create() *Client {
+	tokens.StartServer()
+	time.Sleep(time.Second * 1)
+	return &Client{}
+}
+
+// Returns a slice of transactions from the last 24 hours.
+func (c *Client) TransactionsLast24Hours() []monzoSerde.Transaction {
+	token := tokens.GetState().AccessToken
+	form := url.Values{"account_id": {accountID}}
+	client := http.Client{}
+	req, _ := http.NewRequest("POST", "https://api.monzo.com/transactions",
+		strings.NewReader(form.Encode()))
+	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
+	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+	req.Header.Add("User-Agent", "monzo-ynab")
+	res, err := client.Do(req)
+
+	utils.DebugRequest(req)
+	utils.DebugResponse(res)
+
+	if err != nil {
+		utils.DebugRequest(req)
+		utils.DebugResponse(res)
+		log.Fatal(err)
+	}
+	defer res.Body.Close()
+
+	return []monzoSerde.Transaction{}
+}
diff --git a/users/wpcarro/tools/monzo_ynab/monzo/serde.go b/users/wpcarro/tools/monzo_ynab/monzo/serde.go
new file mode 100644
index 0000000000..e2f55dad45
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/monzo/serde.go
@@ -0,0 +1,72 @@
+// This package hosts the serialization and deserialization logic for all of the
+// data types with which our application interacts from the Monzo API.
+package monzoSerde
+
+import (
+	"encoding/json"
+	"time"
+)
+
+type TxMetadata struct {
+	FasterPayment string `json:"faster_payment"`
+	FpsPaymentId  string `json:"fps_payment_id"`
+	Insertion     string `json:"insertion"`
+	Notes         string `json:"notes"`
+	Trn           string `json:"trn"`
+}
+
+type TxCounterparty struct {
+	AccountNumber string `json:"account_number"`
+	Name          string `json:"name"`
+	SortCode      string `json:"sort_code"`
+	UserId        string `json:"user_id"`
+}
+
+type Transaction struct {
+	Id                         string    `json:"id"`
+	Created                    time.Time `json:"created"`
+	Description                string    `json:"description"`
+	Amount                     int       `json:"amount"`
+	Currency                   string    `json:"currency"`
+	Notes                      string    `json:"notes"`
+	Metadata                   TxMetadata
+	AccountBalance             int            `json:"account_balance"`
+	International              interface{}    `json:"international"`
+	Category                   string         `json:"category"`
+	IsLoad                     bool           `json:"is_load"`
+	Settled                    time.Time      `json:"settled"`
+	LocalAmount                int            `json:"local_amount"`
+	LocalCurrency              string         `json:"local_currency"`
+	Updated                    time.Time      `json:"updated"`
+	AccountId                  string         `json:"account_id"`
+	UserId                     string         `json:"user_id"`
+	Counterparty               TxCounterparty `json:"counterparty"`
+	Scheme                     string         `json:"scheme"`
+	DedupeId                   string         `json:"dedupe_id"`
+	Originator                 bool           `json:"originator"`
+	IncludeInSpending          bool           `json:"include_in_spending"`
+	CanBeExcludedFromBreakdown bool           `json:"can_be_excluded_from_breakdown"`
+	CanBeMadeSubscription      bool           `json:"can_be_made_subscription"`
+	CanSplitTheBill            bool           `json:"can_split_the_bill"`
+	CanAddToTab                bool           `json:"can_add_to_tab"`
+	AmountIsPending            bool           `json:"amount_is_pending"`
+	// Fees interface{} `json:"fees"`
+	// Merchant interface `json:"merchant"`
+	// Labels interface{} `json:"labels"`
+	// Attachments interface{} `json:"attachments"`
+	// Categories interface{} `json:"categories"`
+}
+
+// Attempts to encode a Monzo transaction struct into a string.
+func serializeTx(tx *Transaction) (string, error) {
+	x, err := json.Marshal(tx)
+	return string(x), err
+}
+
+// Attempts to parse a string encoding a transaction presumably sent from a
+// Monzo server.
+func deserializeTx(x string) (*Transaction, error) {
+	target := &Transaction{}
+	err := json.Unmarshal([]byte(x), target)
+	return target, err
+}
diff --git a/users/wpcarro/tools/monzo_ynab/requests.txt b/users/wpcarro/tools/monzo_ynab/requests.txt
new file mode 100644
index 0000000000..2da17c0b32
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/requests.txt
@@ -0,0 +1,80 @@
+################################################################################
+# YNAB
+################################################################################
+:ynab = https://api.youneedabudget.com/v1
+:ynab-access-token := (getenv "ynab_personal_access_token")
+:ynab-budget-id := (getenv "ynab_budget_id")
+:ynab-account-id := (getenv "ynab_account_id")
+
+# Test
+GET :ynab/budgets
+Authorization: Bearer :ynab-access-token
+
+# List transactions
+GET :ynab/budgets/:ynab-budget-id/transactions
+Authorization: Bearer :ynab-access-token
+
+# Post transactions
+POST :ynab/budgets/:ynab-budget-id/transactions
+Authorization: Bearer :ynab-access-token
+Content-Type: application/json
+{
+  "transactions": [
+    {
+      "account_id": ":ynab-account-id",
+      "date": "2019-12-30",
+      "amount": 10000,
+      "payee_name": "Richard Stallman",
+      "memo": "Not so free software after all...",
+      "cleared": "cleared",
+      "approved": true,
+      "flag_color": "red",
+      "import_id": "xyz-123"
+    }
+  ]
+}
+
+################################################################################
+# Monzo
+################################################################################
+:monzo = https://api.monzo.com
+:monzo-access-token := (getenv "monzo_cached_access_token")
+:monzo-refresh-token := (getenv "monzo_cached_refresh_token")
+:monzo-client-id := (getenv "monzo_client_id")
+:monzo-client-secret := (getenv "monzo_client_secret")
+:monzo-account-id := (getenv "monzo_account_id")
+
+# List transactions
+GET :monzo/transactions
+Authorization: Bearer :monzo-access-token
+account_id==:monzo-account-id
+
+# Refresh access token
+# According from the docs, the access token expires in 6 hours.
+POST :monzo/oauth2/token
+Content-Type: application/x-www-form-urlencoded
+Authorization: Bearer :monzo-access-token
+grant_type=refresh_token&client_id=:monzo-client-id&client_secret=:monzo-client-secret&refresh_token=:monzo-refresh-token
+
+################################################################################
+# Tokens server
+################################################################################
+:tokens = http://localhost:4242
+
+# Get tokens
+GET :tokens/tokens
+
+# Get application state for debugging purposes
+GET :tokens/state
+
+# Force refresh tokens
+POST :tokens/refresh-tokens
+
+# Set tokens
+POST :tokens/set-tokens
+Content-Type: application/json
+{
+  "access_token": "access-token",
+  "refresh_token": "refresh-token",
+  "expires_in": 120
+}
diff --git a/users/wpcarro/tools/monzo_ynab/tokens.go b/users/wpcarro/tools/monzo_ynab/tokens.go
new file mode 100644
index 0000000000..01b57d3daa
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/tokens.go
@@ -0,0 +1,279 @@
+// Creating a Tokens server to manage my access and refresh tokens. Keeping this
+// as a separate server allows me to develop and use the access tokens without
+// going through client authorization.
+package tokens
+
+////////////////////////////////////////////////////////////////////////////////
+// Dependencies
+////////////////////////////////////////////////////////////////////////////////
+
+import (
+	"auth"
+	"encoding/json"
+	"fmt"
+	"io"
+	"kv"
+	"log"
+	"net/http"
+	"net/url"
+	"os"
+	"os/signal"
+	"syscall"
+	"time"
+	"utils"
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Types
+////////////////////////////////////////////////////////////////////////////////
+
+// This is the response from Monzo's API after we request an access token
+// refresh.
+type refreshTokenResponse struct {
+	AccessToken  string `json:"access_token"`
+	RefreshToken string `json:"refresh_token"`
+	ClientId     string `json:"client_id"`
+	ExpiresIn    int    `json:"expires_in"`
+}
+
+// This is the shape of the request from clients wishing to set state of the
+// server.
+type setTokensRequest struct {
+	AccessToken  string `json:"access_token"`
+	RefreshToken string `json:"refresh_token"`
+	ExpiresIn    int    `json:"expires_in"`
+}
+
+// This is our application state.
+type state struct {
+	AccessToken  string `json:"access_token"`
+	refreshToken string `json:"refresh_token"`
+}
+
+type readMsg struct {
+	sender chan state
+}
+
+type writeMsg struct {
+	state  state
+	sender chan bool
+}
+
+type channels struct {
+	reads  chan readMsg
+	writes chan writeMsg
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Top-level Definitions
+////////////////////////////////////////////////////////////////////////////////
+
+var chans = &channels{
+	reads:  make(chan readMsg),
+	writes: make(chan writeMsg),
+}
+
+var (
+	monzoClientId     = os.Getenv("monzo_client_id")
+	monzoClientSecret = os.Getenv("monzo_client_secret")
+	storePath         = os.Getenv("store_path")
+)
+
+////////////////////////////////////////////////////////////////////////////////
+// Utils
+////////////////////////////////////////////////////////////////////////////////
+
+// Print the access and refresh tokens for debugging.
+func logTokens(access string, refresh string) {
+	log.Printf("Access: %s\n", access)
+	log.Printf("Refresh: %s\n", refresh)
+}
+
+func (state *state) String() string {
+	return fmt.Sprintf("state{\n\tAccessToken: \"%s\",\n\trefreshToken: \"%s\"\n}\n", state.AccessToken, state.refreshToken)
+}
+
+// Schedule a token refresh for `expiresIn` seconds using the provided
+// `refreshToken`. This will update the application state with the access token
+// and schedule an additional token refresh for the newly acquired tokens.
+func scheduleTokenRefresh(expiresIn int, refreshToken string) {
+	duration := time.Second * time.Duration(expiresIn)
+	timestamp := time.Now().Local().Add(duration)
+	// TODO(wpcarro): Consider adding a more human readable version that will
+	// log the number of hours, minutes, etc. until the next refresh.
+	log.Printf("Scheduling token refresh for %v\n", timestamp)
+	time.Sleep(duration)
+	log.Println("Refreshing tokens now...")
+	AccessToken, refreshToken := refreshTokens(refreshToken)
+	log.Println("Successfully refreshed tokens.")
+	logTokens(AccessToken, refreshToken)
+	setState(AccessToken, refreshToken)
+}
+
+// Exchange existing credentials for a new access token and `refreshToken`. Also
+// schedule the next refresh. This function returns the newly acquired access
+// token and refresh token.
+func refreshTokens(refreshToken string) (string, string) {
+	// TODO(wpcarro): Support retries with exponential backoff.
+	res, err := http.PostForm("https://api.monzo.com/oauth2/token", url.Values{
+		"grant_type":    {"refresh_token"},
+		"client_id":     {monzoClientId},
+		"client_secret": {monzoClientSecret},
+		"refresh_token": {refreshToken},
+	})
+	if res.StatusCode != http.StatusOK {
+		// TODO(wpcarro): Considering panicking here.
+		utils.DebugResponse(res)
+	}
+	if err != nil {
+		utils.DebugResponse(res)
+		log.Fatal("The request to Monzo to refresh our access token failed.", err)
+	}
+	defer res.Body.Close()
+	payload := &refreshTokenResponse{}
+	err = json.NewDecoder(res.Body).Decode(payload)
+	if err != nil {
+		log.Fatal("Could not decode the JSON response from Monzo.", err)
+	}
+
+	go scheduleTokenRefresh(payload.ExpiresIn, payload.RefreshToken)
+
+	// Interestingly, JSON decoding into the refreshTokenResponse can success
+	// even if the decoder doesn't populate any of the fields in the
+	// refreshTokenResponse struct. From what I read, it isn't possible to make
+	// these fields as required using an annotation, so this guard must suffice
+	// for now.
+	if payload.AccessToken == "" || payload.RefreshToken == "" {
+		log.Fatal("JSON parsed correctly but failed to populate token fields.")
+	}
+
+	return payload.AccessToken, payload.RefreshToken
+}
+
+func persistTokens(access string, refresh string) {
+	log.Println("Persisting tokens...")
+	kv.Set(storePath, "monzoAccessToken", access)
+	kv.Set(storePath, "monzoRefreshToken", refresh)
+	log.Println("Successfully persisted tokens.")
+}
+
+// Listen for SIGINT and SIGTERM signals. When received, persist the access and
+// refresh tokens and shutdown the server.
+func handleInterrupts() {
+	// Gracefully handle interruptions.
+	sigs := make(chan os.Signal, 1)
+	done := make(chan bool)
+
+	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
+
+	go func() {
+		sig := <-sigs
+		log.Printf("Received signal to shutdown. %v\n", sig)
+		state := GetState()
+		persistTokens(state.AccessToken, state.refreshToken)
+		done <- true
+	}()
+
+	<-done
+	log.Println("Exiting...")
+	os.Exit(0)
+}
+
+// Set `AccessToken` and `refreshToken` on application state.
+func setState(AccessToken string, refreshToken string) {
+	msg := writeMsg{state{AccessToken, refreshToken}, make(chan bool)}
+	chans.writes <- msg
+	<-msg.sender
+}
+
+// Return our application state.
+func GetState() state {
+	msg := readMsg{make(chan state)}
+	chans.reads <- msg
+	return <-msg.sender
+}
+
+func StartServer() {
+	// Manage application state.
+	go func() {
+		state := &state{}
+		for {
+			select {
+			case msg := <-chans.reads:
+				log.Println("Reading from state...")
+				log.Println(state)
+				msg.sender <- *state
+			case msg := <-chans.writes:
+				log.Println("Writing to state.")
+				log.Printf("Old: %s\n", state)
+				*state = msg.state
+				log.Printf("New: %s\n", state)
+				// As an attempt to maintain consistency between application
+				// state and persisted state, everytime we write to the
+				// application state, we will write to the store.
+				persistTokens(state.AccessToken, state.refreshToken)
+				msg.sender <- true
+			}
+		}
+	}()
+
+	// Retrieve cached tokens from store.
+	accessToken := fmt.Sprintf("%v", kv.Get(storePath, "monzoAccessToken"))
+	refreshToken := fmt.Sprintf("%v", kv.Get(storePath, "monzoRefreshToken"))
+
+	log.Println("Attempting to retrieve cached credentials...")
+	logTokens(accessToken, refreshToken)
+
+	if accessToken == "" || refreshToken == "" {
+		log.Println("Cached credentials are absent. Authorizing client...")
+		authCode := auth.GetAuthCode(monzoClientId)
+		tokens := auth.GetTokensFromAuthCode(authCode, monzoClientId, monzoClientSecret)
+		setState(tokens.AccessToken, tokens.RefreshToken)
+		go scheduleTokenRefresh(tokens.ExpiresIn, tokens.RefreshToken)
+	} else {
+		setState(accessToken, refreshToken)
+		// If we have tokens, they may be expiring soon. We don't know because
+		// we aren't storing the expiration timestamp in the state or in the
+		// store. Until we have that information, and to be safe, let's refresh
+		// the tokens.
+		go scheduleTokenRefresh(0, refreshToken)
+	}
+
+	// Gracefully handle shutdowns.
+	go handleInterrupts()
+
+	// Listen to inbound requests.
+	fmt.Println("Listening on http://localhost:4242 ...")
+	log.Fatal(http.ListenAndServe(":4242",
+		http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+			if req.URL.Path == "/refresh-tokens" && req.Method == "POST" {
+				state := GetState()
+				go scheduleTokenRefresh(0, state.refreshToken)
+				fmt.Fprintf(w, "Done.")
+			} else if req.URL.Path == "/set-tokens" && req.Method == "POST" {
+				// Parse
+				payload := &setTokensRequest{}
+				err := json.NewDecoder(req.Body).Decode(payload)
+				if err != nil {
+					log.Fatal("Could not decode the user's JSON request.", err)
+				}
+
+				// Update application state
+				setState(payload.AccessToken, payload.RefreshToken)
+
+				// Refresh tokens
+				go scheduleTokenRefresh(payload.ExpiresIn, payload.RefreshToken)
+
+				// Ack
+				fmt.Fprintf(w, "Done.")
+			} else if req.URL.Path == "/state" && req.Method == "GET" {
+				// TODO(wpcarro): Ensure that this returns serialized state.
+				w.Header().Set("Content-type", "application/json")
+				state := GetState()
+				payload, _ := json.Marshal(state)
+				io.WriteString(w, string(payload))
+			} else {
+				log.Printf("Unhandled request: %v\n", *req)
+			}
+		})))
+}
diff --git a/users/wpcarro/tools/monzo_ynab/ynab/client.go b/users/wpcarro/tools/monzo_ynab/ynab/client.go
new file mode 100644
index 0000000000..e63010b281
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/ynab/client.go
@@ -0,0 +1,9 @@
+package ynabClient
+
+import (
+	"ynabSerde"
+)
+
+// See requests.txt for more details.
+func PostTransactions(txs []ynabSerde.Transaction) {
+}
diff --git a/users/wpcarro/tools/monzo_ynab/ynab/serde.go b/users/wpcarro/tools/monzo_ynab/ynab/serde.go
new file mode 100644
index 0000000000..45dd921b24
--- /dev/null
+++ b/users/wpcarro/tools/monzo_ynab/ynab/serde.go
@@ -0,0 +1,44 @@
+// This package hosts the serialization and deserialization logic for all of the
+// data types with which our application interacts from the YNAB API.
+package ynabSerde
+
+import (
+	"encoding/json"
+	"time"
+)
+
+type Transaction struct {
+	Id           string    `json:"id"`
+	Date         time.Time `json:"date"`
+	Amount       int       `json:"amount"`
+	Memo         string    `json:"memo"`
+	Cleared      string    `json:"cleared"`
+	Approved     bool      `json:"approved"`
+	FlagColor    string    `json:"flag_color"`
+	AccountId    string    `json:"account_id"`
+	AccountName  string    `json:"account_name"`
+	PayeeId      string    `json:"payeed_id"`
+	PayeeName    string    `json:"payee_name"`
+	CategoryId   string    `json:"category_id"`
+	CategoryName string    `json:"category_name"`
+	Deleted      bool      `json:"deleted"`
+	// TransferAccountId interface{} `json:"transfer_account_id"`
+	// TransferTransactionId interface{} `json:"transfer_transaction_id"`
+	// MatchedTransactionId interface{} `json:"matched_transaction_id"`
+	// ImportId interface{} `json:"import_id"`
+	// Subtransactions interface{} `json:"subtransactions"`
+}
+
+// Attempts to encode a YNAB transaction into a string.
+func serializeTx(tx *Transaction) (string, error) {
+	x, err := json.Marshal(tx)
+	return string(x), err
+}
+
+// Attempts to parse a string encoding a transaction presumably sent from a
+// YNAB server.
+func deserializeTx(x string) (*Transaction, error) {
+	target := &Transaction{}
+	err := json.Unmarshal([]byte(x), target)
+	return target, err
+}
diff --git a/users/wpcarro/tools/rfcToKindle/LICENSE b/users/wpcarro/tools/rfcToKindle/LICENSE
new file mode 100644
index 0000000000..7a4a3ea242
--- /dev/null
+++ b/users/wpcarro/tools/rfcToKindle/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/users/wpcarro/tools/rfcToKindle/README.md b/users/wpcarro/tools/rfcToKindle/README.md
new file mode 100644
index 0000000000..e7b4fa841e
--- /dev/null
+++ b/users/wpcarro/tools/rfcToKindle/README.md
@@ -0,0 +1,30 @@
+# rfcToKindle
+
+Wirelessly transfer RFC documents to your Kindle to device for an alternative
+medium for reading.
+
+## Installation
+
+`rfcToKindle` makes use of [`buildGo.nix`][2] to package itself.  If you're
+using [Nix][1], you can install `rfcToKindle` using `nix-env`:
+
+```shell
+> nix-env -f https://github.com/wpcarro/rfcToKindle -i
+```
+
+## Usage
+
+```shell
+> rfcToKindle -document rfc6479 -recipient username@kindle.com
+```
+
+## Dependencies
+
+This uses `sendgmr` to send the file to the Kindle. Make sure:
+1. That `sendgmr` is installed and available on $PATH.
+2. That it is configured to work with your preferred email address.
+3. That the email address `sendgmr` is configured to use is whitelisted in
+   your Kindle "Personal Document Settings".
+
+[1]: https://nixos.org/nix/
+[2]: https://git.tazj.in/tree/nix/buildGo
diff --git a/users/wpcarro/tools/rfcToKindle/default.nix b/users/wpcarro/tools/rfcToKindle/default.nix
new file mode 100644
index 0000000000..ca87abdee0
--- /dev/null
+++ b/users/wpcarro/tools/rfcToKindle/default.nix
@@ -0,0 +1,11 @@
+{ depot, ... }:
+
+# TODO: This doesn't depend on `sendgmr` at the moment, but it should. As such,
+# it's an imcomplete packaging.
+depot.nix.buildGo.program {
+  name = "rfcToKindle";
+  srcs = [
+    ./main.go
+  ];
+  deps = [ ];
+}
diff --git a/users/wpcarro/tools/rfcToKindle/main.go b/users/wpcarro/tools/rfcToKindle/main.go
new file mode 100644
index 0000000000..0f4f2dd9ec
--- /dev/null
+++ b/users/wpcarro/tools/rfcToKindle/main.go
@@ -0,0 +1,89 @@
+// Author: wpcarro@gmail.com
+//
+// Wirelessly transfer RFC documents to your Kindle to device for an alternative
+// medium for reading.
+//
+// Usage:
+// ```shell
+// > go run rfcToKindle.go -document rfc6479 -recipient username@kindle.com
+// ```
+//
+// This uses `sendgmr` to send the file to the Kindle. Make sure:
+// 1. That `sendgmr` is installed and available on $PATH.
+// 2. That it is configured to work with your preferred email address.
+// 3. That the email address `sendgmr` is configured to use is whitelisted in
+//    your Kindle "Personal Document Settings".
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"os"
+	"os/exec"
+	"strings"
+)
+
+func main() {
+	document := flag.String("document", "", "(Required) The name of the document to fetch. For example \"RFC6479\".")
+	recipient := flag.String("recipient", "", "(Required) The email address of the Kindle device.")
+	subject := flag.String("subject", "", "(Optional) The email address of the Kindle device.")
+	flag.Parse()
+
+	if *document == "" {
+		// TODO: Is log.Fatal the best function to use here?
+		log.Fatal("-document cannot be empty. See -help for more information.")
+	}
+
+	if *recipient == "" {
+		log.Fatal("-recipient cannot be empty. See -help for more information.")
+	}
+
+	*document = strings.ToLower(*document)
+
+	url := fmt.Sprintf("https://www.ietf.org/rfc/%s.txt", *document)
+	resp, err := http.Get(url)
+	fmt.Printf("Downloading %s ... ", url)
+
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer resp.Body.Close()
+
+	f, err := ioutil.TempFile("", fmt.Sprintf("%s-*.txt", *document))
+	if err != nil {
+		log.Fatal(err)
+	}
+	// TODO: Verify if this is cleaning up or not.
+	defer os.Remove(f.Name())
+
+	_, err = io.Copy(f, resp.Body)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("done.")
+
+	if *subject == "" {
+		*subject = fmt.Sprintf("%s - Sent from rfcToKindle.go", *document)
+	}
+
+	// Although I couldn't find it documented anywhere, the email sent to the
+	// Kindle must have a body, even if the body isn't used for anything.
+	fmt.Printf("Emailing %s to %s ... ", f.Name(), *recipient)
+	cmd := exec.Command("sendgmr",
+		fmt.Sprintf("--to=%s", *recipient),
+		fmt.Sprintf("--body_file=%s", f.Name()),
+		fmt.Sprintf("--subject=%s", *subject),
+		fmt.Sprintf("--attachment_files=%s", f.Name()))
+	err = cmd.Run()
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println("done.")
+
+	os.Exit(0)
+}
diff --git a/users/wpcarro/tools/run/.envrc b/users/wpcarro/tools/run/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/tools/run/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/tools/run/README.md b/users/wpcarro/tools/run/README.md
new file mode 100644
index 0000000000..fd24495035
--- /dev/null
+++ b/users/wpcarro/tools/run/README.md
@@ -0,0 +1,30 @@
+# run
+
+Simplify the commands you call to run scripts on the command line.
+
+```shell
+> run path/to/file.py
+> run path/to/file.ts
+```
+
+## How?
+
+Define a run.json configuration mapping commands to filename extensions like
+so:
+```json
+{
+  ".ts": "npx ts-node $file",
+  ".py": "python3 $file"
+}
+```
+
+Then call `run path/to/some/file.ts` on the command line, and `npx ts-node
+file.ts` will run.
+
+## Installation
+
+Install `run` using Nix.
+
+```shell
+> nix-env -iA users.wpcarro.run
+```
diff --git a/users/wpcarro/tools/run/default.nix b/users/wpcarro/tools/run/default.nix
new file mode 100644
index 0000000000..807a75c284
--- /dev/null
+++ b/users/wpcarro/tools/run/default.nix
@@ -0,0 +1,11 @@
+{ pkgs, depot, ... }:
+
+depot.nix.buildGo.program {
+  name = "run";
+  srcs = [
+    ./main.go
+  ];
+  deps = with depot.users.wpcarro.gopkgs; [
+    utils
+  ];
+}
diff --git a/users/wpcarro/tools/run/main.go b/users/wpcarro/tools/run/main.go
new file mode 100644
index 0000000000..04906ece91
--- /dev/null
+++ b/users/wpcarro/tools/run/main.go
@@ -0,0 +1,49 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"utils"
+)
+
+func main() {
+	if len(os.Args) != 2 {
+		log.Fatal("You can only call run with a single file at a time.")
+	}
+
+	rulesPath := utils.Resolve("run.json", []string{"/home/wpcarro/.config/run/run.json"})
+	b, err := ioutil.ReadFile(rulesPath)
+	if err != nil {
+		log.Fatal("Could not locate a run.json file: ", err)
+	}
+	rules := map[string]string{}
+	err = json.Unmarshal(b, &rules)
+	if err != nil {
+		log.Fatal("Could not decode run.json as JSON: ", err)
+	}
+
+	fileName := os.Args[1]
+	ext := filepath.Ext(fileName)
+	cmd, ok := rules[ext]
+
+	if !ok {
+		log.Fatalf("No rules for extension, %s, have been defined.", ext)
+	}
+
+	// TODO(wpcarro): Support more sophisticated parsing than just string
+	// splitting. To handle 'cases like this'.
+	tokens := strings.Split(strings.Replace(cmd, "$file", fileName, 1), " ")
+	c := exec.Command(tokens[0], tokens[1:]...)
+	err = c.Start()
+	// TODO(wpcarro): Forward STDERR and STDOUT.
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Println(c.Wait())
+}
diff --git a/users/wpcarro/tools/run/shell.nix b/users/wpcarro/tools/run/shell.nix
new file mode 100644
index 0000000000..f777c13fef
--- /dev/null
+++ b/users/wpcarro/tools/run/shell.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    go
+    goimports
+    godef
+  ];
+}
diff --git a/users/wpcarro/tools/simple_vim/config.vim b/users/wpcarro/tools/simple_vim/config.vim
new file mode 100644
index 0000000000..bbdcdc5f1a
--- /dev/null
+++ b/users/wpcarro/tools/simple_vim/config.vim
@@ -0,0 +1,101 @@
+" My barebones vimrc without any Vundle dependencies.
+"
+" I'm attempting to optimize the following:
+" - Minimize dependencies
+" - Maximize ergonomics
+" - Maximize Tmux compatibility
+" - Minimize shadowing of existing Vim KBDs
+"
+" Warning: This is currently unstable as it is a work-in-progress.
+"
+" Author: William Carroll <wpcarro@gmail.com>
+
+" Use <Space> as the leader key.
+let mapleader = " "
+nnoremap <leader>ev :tabnew<CR>:edit ~/.vimrc<CR>
+nnoremap <leader>sv :source ~/.vimrc<CR>
+nnoremap <leader>w  :w<CR>
+nnoremap <leader>h  :help 
+
+" increment,decrement numbers
+nnoremap + <C-a>
+" TODO: Restore with better KBD
+" nnoremap - <C-x>
+
+" Visit the CWD
+nnoremap - :e .<CR>
+
+" Turn line numbers on.
+set number
+
+" Prevent lines from wrapping
+set nowrap
+
+" Easily create vertical, horizontal window splits.
+nnoremap sh :vsplit<CR>
+nnoremap sj :split<CR>:wincmd j<CR>
+nnoremap sk :split<CR>
+nnoremap sl :vsplit<CR>:wincmd l<CR>
+
+" Move across window splits.
+" TODO: Change to <M-{h,j,k,l}>.
+nnoremap <C-h> :wincmd h<CR>
+nnoremap <C-j> :wincmd j<CR>
+nnoremap <C-k> :wincmd k<CR>
+nnoremap <C-l> :wincmd l<CR>
+
+" TODO: Support these.
+" nnoremap <M-q> :q<CR>
+" nnoremap <M-h> :wincmd h<CR>
+" nnoremap <M-j> :wincmd j<CR>
+" nnoremap <M-k> :wincmd k<CR>
+" nnoremap <M-l> :wincmd l<CR>
+
+" Use <CR> instead of G to support:
+"        20<CR> - to jump to line 20
+"       d20<CR> - to delete from the current line until line 20
+"   <C-v>20<CR> - to select from the current line until line 20
+nnoremap <CR> G
+onoremap <CR> G
+vnoremap <CR> G
+
+" Easily change modes on keyboards that don't have CapsLock mapped to <Esc>
+inoremap jk      <ESC>
+
+" CRUD tabs.
+nnoremap <TAB>   :tabnext<CR>
+nnoremap <S-TAB> :tabprevious<CR>
+nnoremap <C-t>   :tabnew<CR>:edit .<CR>
+nnoremap <C-w>   :tabclose<CR>
+" TODO: Re-enable these once <M-{h,j,k,l}> are supported.
+" nnoremap <C-l> :+tabmove<CR>
+" nnoremap <C-h> :-tabmove<CR>
+
+" Use H,L to goto beggining,end of a line.
+" Swaps the keys to ensure original functionality of H,L are preserved.
+nnoremap H ^
+nnoremap L $
+nnoremap ^ H
+nnoremap $ L
+
+" Use H,L in visual mode too
+vnoremap H ^
+vnoremap L $
+vnoremap ^ H
+vnoremap $ L
+
+" Emacs hybrid mode
+" TODO: model this after tpope's rsi.vim (Readline-style insertion)
+cnoremap <C-g> <C-c>
+cnoremap <C-a> <C-b>
+inoremap <C-a> <C-o>^
+inoremap <C-e> <C-o>$
+inoremap <C-b> <C-o>h
+inoremap <C-f> <C-o>l
+
+" Indenting
+" The following three settings are based on option 2 of `:help tabstop`
+set tabstop=4
+set shiftwidth=4
+set expandtab
+set autoindent
diff --git a/users/wpcarro/tools/simple_vim/default.nix b/users/wpcarro/tools/simple_vim/default.nix
new file mode 100644
index 0000000000..ae3fd76a46
--- /dev/null
+++ b/users/wpcarro/tools/simple_vim/default.nix
@@ -0,0 +1,5 @@
+{ pkgs, ... }:
+
+pkgs.writeShellScriptBin "simple_vim" ''
+  ${pkgs.neovim}/bin/nvim -u ${./config.vim} "$@"
+''
diff --git a/users/wpcarro/tools/symlinkManager/README.md b/users/wpcarro/tools/symlinkManager/README.md
new file mode 100644
index 0000000000..e8298ea654
--- /dev/null
+++ b/users/wpcarro/tools/symlinkManager/README.md
@@ -0,0 +1,12 @@
+# Dotfile Symlink Manager
+
+Find and delete all symlinks to my dotfiles.
+
+Oftentimes I corrupt the state of my configuration files. The intention with
+this script is to help me clean things up when this happens. An example workflow
+might look like:
+
+```shell
+> symlink-mgr --audit
+> symlink-mgr --seriously
+```
diff --git a/users/wpcarro/tools/symlinkManager/default.nix b/users/wpcarro/tools/symlinkManager/default.nix
new file mode 100644
index 0000000000..7d022828ee
--- /dev/null
+++ b/users/wpcarro/tools/symlinkManager/default.nix
@@ -0,0 +1,14 @@
+{ depot, ... }:
+
+let
+  inherit (depot.users.wpcarro) gopkgs;
+in
+depot.nix.buildGo.program {
+  name = "symlink-mgr";
+  srcs = [
+    ./main.go
+  ];
+  deps = with gopkgs; [
+    utils
+  ];
+}
diff --git a/users/wpcarro/tools/symlinkManager/main.go b/users/wpcarro/tools/symlinkManager/main.go
new file mode 100644
index 0000000000..d99c7cb863
--- /dev/null
+++ b/users/wpcarro/tools/symlinkManager/main.go
@@ -0,0 +1,61 @@
+package main
+
+import (
+	"errors"
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"utils"
+)
+
+func main() {
+	audit := flag.Bool("audit", false, "Output all symlinks that would be deleted. This is the default behavior. This option is mutually exclusive with the --seriously option.")
+	seriously := flag.Bool("seriously", false, "Actually delete the symlinks. This option is mutually exclusive with the --audit option.")
+	repoName := flag.String("repo-name", "briefcase", "The name of the repository.")
+	flag.Parse()
+
+	if !*audit && !*seriously {
+		log.Fatal(errors.New("Either -audit or -seriously needs to be set."))
+	}
+	if *audit == *seriously {
+		log.Fatal(errors.New("Arguments -audit and -seriously are mutually exclusive"))
+	}
+
+	home, err := os.UserHomeDir()
+	utils.FailOn(err)
+	count := 0
+
+	err = filepath.Walk(home, func(path string, info os.FileInfo, err error) error {
+		if utils.IsSymlink(info.Mode()) {
+			dest, err := os.Readlink(path)
+			utils.FailOn(err)
+
+			predicate := func(dest string) bool {
+				return strings.Contains(dest, *repoName)
+			}
+
+			if predicate(dest) {
+				if *audit {
+					fmt.Printf("%s -> %s\n", path, dest)
+				} else if *seriously {
+					fmt.Printf("rm %s\n", path)
+					err = os.Remove(path)
+					utils.FailOn(err)
+				}
+				count += 1
+			}
+		}
+		return nil
+	})
+	utils.FailOn(err)
+	if *audit {
+		fmt.Printf("Would have deleted %d symlinks.\n", count)
+	} else if *seriously {
+		fmt.Printf("Successfully deleted %d symlinks.\n", count)
+	}
+
+	os.Exit(0)
+}
diff --git a/users/wpcarro/tools/systemd-shell/default.nix b/users/wpcarro/tools/systemd-shell/default.nix
new file mode 100644
index 0000000000..eace76b708
--- /dev/null
+++ b/users/wpcarro/tools/systemd-shell/default.nix
@@ -0,0 +1,8 @@
+{ pkgs, ... }:
+
+pkgs.python310Packages.buildPythonApplication {
+  pname = "systemd-shell";
+  version = "0.0.1";
+  src = ./.;
+  doCheck = false;
+}
diff --git a/users/wpcarro/tools/systemd-shell/setup.py b/users/wpcarro/tools/systemd-shell/setup.py
new file mode 100644
index 0000000000..f45e058e67
--- /dev/null
+++ b/users/wpcarro/tools/systemd-shell/setup.py
@@ -0,0 +1,9 @@
+from setuptools import setup
+
+setup(
+    name="systemd-shell",
+    version="0.0.1",
+    author="William Carroll",
+    author_email="wpcarro@gmail.com",
+    scripts=["systemd-shell"],
+)
diff --git a/users/wpcarro/tools/systemd-shell/systemd-shell b/users/wpcarro/tools/systemd-shell/systemd-shell
new file mode 100644
index 0000000000..646d59143a
--- /dev/null
+++ b/users/wpcarro/tools/systemd-shell/systemd-shell
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+
+# Drop into a new shell environment with the same variables defined in a systemd
+# unit file (for debugging purposes).
+#
+# USAGE:
+#   $ systemd-shell -u buildkite-agent-foundation-1.service
+
+import argparse
+import os
+
+def parse_env(entry):
+    x = entry[12:].split("=", 1)
+    return x[0].removeprefix("\"").removesuffix("\""), x[1].removeprefix("\"").removesuffix("\"")
+
+def run(unit):
+  envfile = []
+  print("--- Environment ---")
+  for line in open(f"/etc/systemd/system/{unit}").readlines():
+      if line.startswith("Environment="):
+          key, val = parse_env(line[:-1])
+          print(f"export {key}={val}")
+          envfile.append(f"{key}={val}")
+      else:
+          continue
+  print()
+
+  env = " ".join(envfile)
+  print("--- Command ---")
+  os.system(f"/usr/bin/env {env} /bin/sh")
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument("-u", "--unit", type=str, required=True)
+    args = parser.parse_args()
+    run(args.unit)
diff --git a/users/wpcarro/tools/url-blocker/.envrc b/users/wpcarro/tools/url-blocker/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/tools/url-blocker/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/tools/url-blocker/Main.hs b/users/wpcarro/tools/url-blocker/Main.hs
new file mode 100644
index 0000000000..926412ce91
--- /dev/null
+++ b/users/wpcarro/tools/url-blocker/Main.hs
@@ -0,0 +1,205 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE DeriveGeneric #-}
+module Main ( main ) where
+
+--------------------------------------------------------------------------------
+-- Dependencies
+--------------------------------------------------------------------------------
+
+import qualified Data.Maybe as Maybe
+import qualified Data.Time.Clock as Clock
+import qualified Data.Time.Calendar as Calendar
+import qualified Data.Time.LocalTime as LocalTime
+import qualified Data.ByteString.Lazy as LazyByteString
+import qualified Data.Aeson as Aeson
+import qualified Data.Either.Combinators as Either
+import qualified Data.HashMap.Strict as HashMap
+import qualified Data.Text as Text
+import qualified Data.Text.IO as TextIO
+import qualified Data.Text.Read as TextRead
+import qualified Data.List as List
+
+import GHC.Generics
+import Data.Aeson ((.:))
+import Data.Text (Text)
+
+--------------------------------------------------------------------------------
+-- Types
+--------------------------------------------------------------------------------
+
+newtype URL = URL { getURL :: Text } deriving (Show, Eq, Generic)
+
+newtype IPAddress = IPAddress { getIPAddress :: Text } deriving (Show)
+
+newtype Domain = Domain { getDomain :: Text } deriving (Show)
+
+newtype Hour = Hour { getHour :: Int } deriving (Show, Eq, Generic)
+
+newtype Minute = Minute { getMinute :: Int } deriving (Show, Eq, Generic)
+
+data EtcHostsEntry = EtcHostsEntry { ip :: IPAddress
+                                   , domains :: [Domain]
+                                   } deriving (Show)
+
+-- | Write these in terms of your system's local time (i.e. `date`).
+data TimeSlot = TimeSlot { beg :: (Hour, Minute)
+                         , end :: (Hour, Minute)
+                         } deriving (Show, Eq, Generic)
+
+data Allowance = Allowance { day :: Calendar.DayOfWeek
+                           , timeslots :: [TimeSlot]
+                           } deriving (Show, Eq, Generic)
+
+data Rule = Rule { urls :: [URL]
+                 , allowed :: [Allowance]
+                 } deriving (Show, Eq, Generic)
+
+--------------------------------------------------------------------------------
+-- Instances
+--------------------------------------------------------------------------------
+
+instance Aeson.FromJSON TimeSlot where
+  parseJSON = Aeson.withText "timeslot" $ \x -> do
+    let [a, b] = Text.splitOn "-" x
+        [ah, am] = Text.splitOn ":" a
+        [bh, bm] = Text.splitOn ":" b
+    case extractTimeSlot ah am bh bm of
+      Left s  -> fail s
+      Right x -> pure x
+    where
+      extractTimeSlot :: Text -> Text -> Text -> Text -> Either String TimeSlot
+      extractTimeSlot ah am bh bm = do
+        (begh, _) <- TextRead.decimal ah
+        (begm, _) <- TextRead.decimal am
+        (endh, _) <- TextRead.decimal bh
+        (endm, _) <- TextRead.decimal bm
+        pure $ TimeSlot{ beg = (Hour begh, Minute begm)
+                       , end = (Hour endh, Minute endm)
+                       }
+
+instance Aeson.FromJSON Allowance where
+  parseJSON = Aeson.withObject "allowance" $ \x -> do
+    day <- x .: "day"
+    timeslots <- x .: "timeslots"
+    pure $ Allowance{day, timeslots}
+
+instance Aeson.FromJSON URL where
+  parseJSON = Aeson.withText "URL" $ \x -> do
+    pure $ URL { getURL = x }
+
+instance Aeson.FromJSON Rule where
+  parseJSON = Aeson.withObject "rule" $ \x -> do
+    urls <- x .: "urls"
+    allowed <- x .: "allowed"
+    pure Rule{urls, allowed}
+
+--------------------------------------------------------------------------------
+-- Functions
+--------------------------------------------------------------------------------
+
+-- | Pipe operator
+(|>) :: a -> (a -> b) -> b
+(|>) a f = f a
+infixl 1 |>
+
+-- | Returns True if the current time falls within any of the `timeslots`.
+isWithinTimeSlot :: LocalTime.LocalTime -> [TimeSlot] -> Bool
+isWithinTimeSlot date timeslots =
+  List.any withinTimeSlot timeslots
+  where
+    withinTimeSlot :: TimeSlot -> Bool
+    withinTimeSlot TimeSlot{ beg = (Hour ah, Minute am)
+                           , end = (Hour bh, Minute bm)
+                           } =
+      let LocalTime.TimeOfDay{LocalTime.todHour, LocalTime.todMin} =
+            LocalTime.localTimeOfDay date
+      in (todHour > ah) && (todMin > am) && (todHour < bh) && (todMin < bm)
+
+-- | Returns True if `day` is the same day as today.
+isToday :: LocalTime.LocalTime -> Calendar.DayOfWeek -> Bool
+isToday date day = today == day
+  where
+    today = Calendar.dayOfWeek (LocalTime.localDay date)
+
+-- | Returns True if a list of none of the `allowances` are valid.
+shouldBeBlocked :: LocalTime.LocalTime -> [Allowance] -> Bool
+shouldBeBlocked _ [] = True
+shouldBeBlocked date allowances = do
+  case filter (isToday date . day) allowances of
+    [Allowance{timeslots}] -> not $ isWithinTimeSlot date timeslots
+    [] -> True
+    -- Error when more than one rule per day
+    _  -> True
+
+-- | Maps an EtcHostsEntry to the line of text url-blocker will append to /etc/hosts.
+serializeEtcHostEntry :: EtcHostsEntry -> Text
+serializeEtcHostEntry EtcHostsEntry{ip, domains} =
+  (getIPAddress ip) <> "\t" <> (Text.unwords $ fmap getDomain domains)
+
+-- | Create an EtcHostsEntry mapping the URLs in `rule` to 127.0.0.1 if the
+-- URLs should be blocked.
+maybeBlockURL :: LocalTime.LocalTime -> Rule -> Maybe EtcHostsEntry
+maybeBlockURL date Rule{urls, allowed} =
+  if shouldBeBlocked date allowed then
+    Just $ EtcHostsEntry { ip = IPAddress "127.0.0.1"
+                        , domains = fmap (Domain . getURL) urls
+                        }
+  else
+    Nothing
+
+-- | Read and parse the rules.json file.
+-- TODO(wpcarro): Properly handle errors for file not found.
+-- TODO(wpcarro): Properly handle errors for parse failures.
+-- TODO(wpcarro): How can we resolve the $HOME directory when this is run as
+-- root?
+getRules :: IO [Rule]
+getRules = do
+  contents <- LazyByteString.readFile "/home/wpcarro/.config/url-blocker/rules.json"
+  let payload = Aeson.eitherDecode contents
+  pure $ Either.fromRight [] payload
+
+-- | Informational header added to /etc/hosts before the entries that
+-- url-blocker adds.
+urlBlockerHeader :: Text
+urlBlockerHeader =
+  Text.unlines [ "################################################################################"
+               , "# Added by url-blocker."
+               , "#"
+               , "# Warning: url-blocker will remove anything that you add beneath this header."
+               , "################################################################################"
+               ]
+
+-- | Removes all entries that url-blocker may have added to /etc/hosts.
+removeURLBlockerEntries :: Text -> Text
+removeURLBlockerEntries etcHosts =
+  case Text.breakOn urlBlockerHeader etcHosts of
+    (etcHosts', _) -> etcHosts'
+
+-- | Appends the newly created `entries` to `etcHosts`.
+addURLBlockerEntries :: Text -> Text -> Text
+addURLBlockerEntries entries etcHosts =
+  Text.unlines [ etcHosts
+               , urlBlockerHeader
+               , entries
+               ]
+
+-- | This script reads the current /etc/hosts, removes any entries that
+-- url-blocker may have added in a previous run, and adds new entries to block
+-- URLs according to the rules.json file.
+main :: IO ()
+main = do
+  rules <- getRules
+  tz <- LocalTime.getCurrentTimeZone
+  ct <- Clock.getCurrentTime
+  let date = LocalTime.utcToLocalTime tz ct
+      entries = rules
+                |> fmap (maybeBlockURL date)
+                |> Maybe.catMaybes
+                |> fmap serializeEtcHostEntry
+                |> Text.unlines
+  existingEtcHosts <- TextIO.readFile "/etc/hosts"
+  existingEtcHosts
+    |> removeURLBlockerEntries
+    |> addURLBlockerEntries entries
+    |> \x -> writeFile "/etc/hosts" (Text.unpack x)
diff --git a/users/wpcarro/tools/url-blocker/README.md b/users/wpcarro/tools/url-blocker/README.md
new file mode 100644
index 0000000000..a6063f3256
--- /dev/null
+++ b/users/wpcarro/tools/url-blocker/README.md
@@ -0,0 +1,47 @@
+# url-blocker
+
+`url-blocker` blocks the URLs that you want to block when you want it to block
+them.
+
+Let's say that you don't want to visit Twitter during the work week. Create the
+file `~/.config/url-blocker/rules.json` with the following contents and
+`url-blocker` will take care of the rest.
+
+```json
+# ~/.config/url-blocker/rules.json
+[
+  {
+    "urls": [
+      "twitter.com",
+      "www.twitter.com",
+    ],
+    "allowed": [
+      {
+        "day": "Saturday",
+        "timeslots": [
+          "00:00-11:59"
+        ]
+      },
+      {
+        "day": "Sunday",
+        "timeslots": [
+          "00:00-11:59"
+        ]
+      }
+    ]
+  }
+]
+```
+
+## Installation
+
+```shell
+$ nix-env -iA users.wpcarro.tools.url-blocker
+```
+
+## How does it work?
+
+`systemd` is intended to run `url-blocker` once every minute. `url-blocker` will
+read `/etc/hosts` and map the URLs defined in `rules.json` to `127.0.0.1` when
+you want them blocked. Because `systemd` run once every minute, `/etc/hosts`
+should be current to the minute as well.
diff --git a/users/wpcarro/tools/url-blocker/default.nix b/users/wpcarro/tools/url-blocker/default.nix
new file mode 100644
index 0000000000..ae24aa41b7
--- /dev/null
+++ b/users/wpcarro/tools/url-blocker/default.nix
@@ -0,0 +1,34 @@
+{ pkgs, ... }:
+
+let
+  ghc = pkgs.haskellPackages.ghcWithPackages (hpkgs: [
+    hpkgs.time
+    hpkgs.aeson
+    hpkgs.either
+  ]);
+
+  # This is the systemd service unit
+  service = pkgs.stdenv.mkDerivation {
+    name = "url-blocker";
+    src = builtins.path { path = ./.; name = "url-blocker"; };
+    buildPhase = ''
+      ${ghc}/bin/ghc Main.hs
+    '';
+    installPhase = ''
+      mv ./Main $out
+    '';
+  };
+
+  # This is the systemd timer unit.
+  # Run once every minute.
+  # Give root privilege.
+  systemdUnit = {
+    systemd = {
+      timers.simple-timer = {
+        wantedBy = [ "timers.target" ];
+        partOf = [ ];
+      };
+    };
+  };
+in
+null
diff --git a/users/wpcarro/tools/url-blocker/rules.json b/users/wpcarro/tools/url-blocker/rules.json
new file mode 100644
index 0000000000..95e4dc9a90
--- /dev/null
+++ b/users/wpcarro/tools/url-blocker/rules.json
@@ -0,0 +1,28 @@
+[
+  {
+    "urls": [
+      "facebook.com",
+      "www.facebook.com",
+      "twitter.com",
+      "www.twitter.com",
+      "youtube.com",
+      "www.youtube.com",
+      "instagram.com",
+      "www.instagram.com"
+    ],
+    "allowed": []
+  },
+  {
+    "urls": [
+      "chat.googleplex.com"
+    ],
+    "allowed": [
+      {
+        "day": "Sunday",
+        "timeslots": [
+          "18:35-18:39"
+        ]
+      }
+    ]
+  }
+]
diff --git a/users/wpcarro/tools/url-blocker/shell.nix b/users/wpcarro/tools/url-blocker/shell.nix
new file mode 100644
index 0000000000..aa5c906edc
--- /dev/null
+++ b/users/wpcarro/tools/url-blocker/shell.nix
@@ -0,0 +1,10 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.shell {
+  deps = hpkgs: with hpkgs; [
+    time
+    aeson
+    either
+    hspec
+  ];
+}
diff --git a/users/wpcarro/tools/wpcarro-deps.nix b/users/wpcarro/tools/wpcarro-deps.nix
new file mode 100644
index 0000000000..5eafd0bfda
--- /dev/null
+++ b/users/wpcarro/tools/wpcarro-deps.nix
@@ -0,0 +1,8 @@
+# Shell derivation to invoke //nix/lazy-deps with the dependencies that should
+# be lazily made available in wpcarro's users dir in depot.
+{ pkgs, depot, ... }:
+
+depot.nix.lazy-deps {
+  import-gpg.attr = "users.wpcarro.configs.import-gpg";
+  export-gpg.attr = "users.wpcarro.configs.export-gpg";
+}
diff --git a/users/wpcarro/utils/README.md b/users/wpcarro/utils/README.md
new file mode 100644
index 0000000000..0724dff2a9
--- /dev/null
+++ b/users/wpcarro/utils/README.md
@@ -0,0 +1,8 @@
+# Nix Utils
+
+Nix is useful and well-designed in many cases. Nix's standard library of is not
+a representative example of the value of Nix. I'm hoping to replace some of the
+standard library functions with versions that I find more intuitive and
+ergonomic to consume.
+
+This directcory is where I will host that work.
diff --git a/users/wpcarro/utils/builder.nix b/users/wpcarro/utils/builder.nix
new file mode 100644
index 0000000000..2bc061d366
--- /dev/null
+++ b/users/wpcarro/utils/builder.nix
@@ -0,0 +1,12 @@
+{ pkgs, ... }:
+
+let
+  inherit (pkgs) writeShellScriptBin;
+in
+{
+  # Create a derivation that creates an executable shell script named `as` that
+  # calls the program located at `path`, forwarding all of the arguments.
+  wrapNonNixProgram = { path, as }: writeShellScriptBin as ''
+    exec ${path} "$@"
+  '';
+}
diff --git a/users/wpcarro/utils/default.nix b/users/wpcarro/utils/default.nix
new file mode 100644
index 0000000000..46d30acfa2
--- /dev/null
+++ b/users/wpcarro/utils/default.nix
@@ -0,0 +1,15 @@
+args@{ pkgs, ... }:
+
+# This top-level module exposes all of my utility functions for Nix. It should
+# be used like:
+# ```nix
+# inherit (depot.users.wpcarro.utils) fs;
+# ```
+
+let
+  builder = import ./builder.nix args;
+  fs = import ./fs.nix args;
+in
+{
+  inherit builder fs;
+}
diff --git a/users/wpcarro/utils/fs.nix b/users/wpcarro/utils/fs.nix
new file mode 100644
index 0000000000..d7d5e34e99
--- /dev/null
+++ b/users/wpcarro/utils/fs.nix
@@ -0,0 +1,42 @@
+{ pkgs, ... }:
+
+# `fs` contains utility functions for working with the filesystem.
+
+let
+  inherit (builtins) attrNames hasAttr map readDir;
+  inherit (pkgs.lib) filterAttrs;
+in
+{
+  # Returns a list of all of the regular files in `dir`.
+  files = dir:
+    map (name: dir + "/${name}")
+      (attrNames
+        (filterAttrs (_: type: type == "regular") (readDir dir)));
+
+  # Returns a list of all of the directories in `dir`.
+  dirs = dir:
+    map (name: dir + "/${name}")
+      (attrNames
+        (filterAttrs (_: type: type == "directory") (readDir dir)));
+
+  # Returns a list of paths to all of the `name` files starting at `dir`.
+  find = name: dir:
+    if hasAttr name (readDir dir) then
+      [ (dir + name) ] ++ concatMap findAllDefaultNix (dirs dir)
+    else
+      concatMap findAllDefaultNix (dirs dir);
+
+  # Looks for `name` in `dir`; if it cannot find it, it checks the parent
+  # directory.
+  resolve = name: dir:
+    if hasAttr name (readDir dir) then
+      dir + "/${name}"
+    else
+    # This prevents the function from infinitely recursing and eventually
+    # stack overflowing.
+      if (dirOf dir) == dir then
+        null
+      else
+        resolve name (dirOf dir);
+};
+}
diff --git a/users/wpcarro/website/README.md b/users/wpcarro/website/README.md
new file mode 100644
index 0000000000..30420571c0
--- /dev/null
+++ b/users/wpcarro/website/README.md
@@ -0,0 +1,3 @@
+# wpcarro.dev
+
+https://wpcarro.dev is my personal website. Check it out!
diff --git a/users/wpcarro/website/blog/.skip-subtree b/users/wpcarro/website/blog/.skip-subtree
new file mode 100644
index 0000000000..3a9dbd4d2b
--- /dev/null
+++ b/users/wpcarro/website/blog/.skip-subtree
@@ -0,0 +1 @@
+Subdirectories contain blog posts and static assets only
\ No newline at end of file
diff --git a/users/wpcarro/website/blog/default.nix b/users/wpcarro/website/blog/default.nix
new file mode 100644
index 0000000000..27541b0f39
--- /dev/null
+++ b/users/wpcarro/website/blog/default.nix
@@ -0,0 +1,47 @@
+{ depot, lib, pkgs, ... }:
+
+with depot.nix.yants;
+
+let
+  inherit (builtins) hasAttr filter readFile;
+  inherit (depot.web.blog) post includePost renderPost;
+  inherit (depot.users.wpcarro.website) domain renderTemplate withBrand;
+  inherit (lib.lists) sort;
+
+  config = {
+    name = "bill and his blog";
+    baseUrl = "https://${domain}/blog";
+    staticUrl = "https://static.tvl.fyi/latest";
+    footer = "";
+  };
+
+  posts = sort (x: y: x.date > y.date)
+    (filter includePost (list post (import ./posts.nix)));
+
+  rendered = pkgs.runCommand "blog-posts" { } ''
+    mkdir -p $out
+
+    ${lib.concatStringsSep "\n" (map (post:
+      "cp ${renderPost config post} $out/${post.key}.html"
+    ) posts)}
+  '';
+
+  formatDate = date: readFile (pkgs.runCommand "date" { } ''
+    date --date='@${toString date}' '+%B %e, %Y' > $out
+  '');
+
+  postsHtml = renderTemplate ./fragments/posts.html {
+    postsHtml = lib.concatStringsSep "\n" (map toPostHtml posts);
+  };
+
+  toPostHtml = post: readFile (renderTemplate ./fragments/post.html {
+    postUrl = "${config.baseUrl}/posts/${post.key}.html";
+    postTitle = post.title;
+    postDate = formatDate post.date;
+  });
+in
+pkgs.runCommand "blog" { } ''
+  mkdir -p $out
+  cp ${withBrand (readFile postsHtml)} $out/index.html
+  cp -r ${rendered} $out/posts
+''
diff --git a/users/wpcarro/website/blog/fragments/.skip-subtree b/users/wpcarro/website/blog/fragments/.skip-subtree
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/users/wpcarro/website/blog/fragments/.skip-subtree
diff --git a/users/wpcarro/website/blog/fragments/post.html b/users/wpcarro/website/blog/fragments/post.html
new file mode 100644
index 0000000000..2741292aa9
--- /dev/null
+++ b/users/wpcarro/website/blog/fragments/post.html
@@ -0,0 +1,8 @@
+<li class="pb-6 md:pb-10 text-md md:text-xl">
+  <h2 class="text-bold">
+    <a class="font-bold text-blue-600 hover:underline" href="@postUrl@">
+      @postTitle@
+    </a>
+  </h2>
+  <p class="text-gray-500">@postDate@</p>
+</li>
diff --git a/users/wpcarro/website/blog/fragments/posts.html b/users/wpcarro/website/blog/fragments/posts.html
new file mode 100644
index 0000000000..a85a4b7110
--- /dev/null
+++ b/users/wpcarro/website/blog/fragments/posts.html
@@ -0,0 +1,10 @@
+<div class="max-w-sm md:max-w-prose mx-auto">
+  <section class="pt-8 pb-14">
+    <p class="font-bold pb-3 text-xl">
+      Personal blog by <a class="font-bold text-blue-600 hover:underline" href="@homepage@">Bill</a>.
+    </p>
+    <p class="text-gray-500">&gt; Half-baked musings lossily encoded.</p>
+    <p class="text-gray-500">&gt; - misc reviewer</p>
+  </section>
+  <ul>@postsHtml@</ul>
+</div>
diff --git a/users/wpcarro/website/blog/posts.nix b/users/wpcarro/website/blog/posts.nix
new file mode 100644
index 0000000000..31fb0c83d8
--- /dev/null
+++ b/users/wpcarro/website/blog/posts.nix
@@ -0,0 +1,116 @@
+# To format the date, run:
+#   $ date -d "today" +%s
+[
+  {
+    key = "cell-phone-experiment";
+    title = "Cell Phone Experiment";
+    date = 1585800000;
+    content = ./posts/cell-phone-experiment.md;
+    draft = false;
+  }
+  {
+    key = "quassel-google-vm";
+    title = "IRC, GCP, and NixOS";
+    date = 1640404800;
+    content = ./posts/quassel-google-vm.md;
+    draft = true;
+  }
+  {
+    key = "send-mail-as-2fa";
+    title = "2FA and Gmail's \"Send mail as\"";
+    date = 1641497483;
+    content = ./posts/send-mail-as-2fa.md;
+    draft = false;
+  }
+  {
+    key = "auto-reboot-nixos";
+    title = "Automatically Reboot NixOS";
+    date = 1643666914;
+    content = ./posts/auto-reboot-nixos.md;
+    draft = false;
+  }
+  {
+    key = "csharp-unused-variables";
+    title = "Unused Variables Broke Prod";
+    date = 1655840877;
+    content = ./posts/csharp-unused-variables.md;
+    draft = false;
+  }
+  {
+    key = "restic-field-guide";
+    title = "Beginner's Field Guide to restic";
+    date = 1656645093;
+    content = ./posts/restic.md;
+    draft = false;
+  }
+  {
+    key = "tee-time";
+    title = "tee time";
+    date = 1657597870;
+    content = ./posts/tee-time.md;
+    draft = false;
+  }
+  {
+    key = "ssh-oddities";
+    title = "SSH Oddities";
+    date = 1657647994;
+    content = ./posts/ssh-oddities.md;
+    draft = false;
+  }
+  {
+    key = "nix-shell";
+    title = "nix-shell (note to self)";
+    date = 1664902186;
+    content = ./posts/nix-shell-note.md;
+    draft = false;
+  }
+  {
+    key = "git-filter-repo-note";
+    title = "git-filter-repo (note to self)";
+    date = 1665163559;
+    content = ./posts/git-filter-repo-note.md;
+    draft = false;
+  }
+  {
+    key = "nixos-disk-full-note";
+    title = "disk full (note to self)";
+    date = 1666801882;
+    content = ./posts/nixos-disk-full-note.md;
+    draft = false;
+  }
+  {
+    key = "git-rev-refs";
+    title = "git revision numbers as refs (note to self)";
+    date = 1666823030;
+    content = ./posts/git-rev-refs.md;
+    draft = false;
+  }
+  {
+    key = "import-subtree-checklist";
+    title = "Checklist for Importing Subtrees";
+    date = 1666903846;
+    content = ./posts/importing-subtrees.md;
+    draft = false;
+  }
+  {
+    key = "nix-env-note";
+    title = "nix-env (note to self)";
+    date = 1667343279;
+    content = ./posts/nix-env-note.md;
+    draft = false;
+  }
+  {
+    key = "nginx-virtual-host-note";
+    title = "Nginx Virtual Host (note to self)";
+    date = 1668448541;
+    content = ./posts/nginx-curl-note.md;
+    draft = false;
+  }
+  {
+    key = "tcp-tunneling-note";
+    title = "TCP Tunneling (note to self)";
+    date = 1668709613;
+    content = ./posts/tcp-tunneling-note.md;
+    draft = false;
+  }
+]
diff --git a/users/wpcarro/website/blog/posts/auto-reboot-nixos.md b/users/wpcarro/website/blog/posts/auto-reboot-nixos.md
new file mode 100644
index 0000000000..24474e6dfe
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/auto-reboot-nixos.md
@@ -0,0 +1,40 @@
+## Show me the codes
+
+Regularly rebooting machines can be a useful, hygienic practice, but quite
+frankly I cannot be relied on to remember to regularly reboot my machine.
+
+Let's free-up some wetware-RAM by automating this with Nix. The following
+addition to your `configuration.nix` will schedule daily reboots at `03:00`:
+
+```nix
+systemd.timers.auto-reboot = {
+  wantedBy = [ "timers.target" ];
+  timerConfig = {
+    OnCalendar = "*-*-* 03:00:00";
+    Unit = "reboot.target";
+  };
+};
+```
+
+If you want to fiddle with the date format, `systemd-analyze` is your friend:
+
+```shell
+ฮป systemd-analyze calendar '*-*-* 03:00:00'
+Normalized form: *-*-* 03:00:00
+    Next elapse: Tue 2022-02-01 03:00:00 PST
+       (in UTC): Tue 2022-02-01 11:00:00 UTC
+       From now: 12h left
+```
+
+After calling `nixos-rebuild switch`, you can verify that `systemd` started the
+timer with:
+
+```shell
+ฮป systemctl list-timers auto-reboot
+#  output omitted because I'm writing this from a different machine
+```
+
+## That's all, folks!
+
+I wanted to keep this post short-and-sweet, to build the habit of posting more
+regularly. Hopefully someone out there found this useful.
diff --git a/users/wpcarro/website/blog/posts/cell-phone-experiment.md b/users/wpcarro/website/blog/posts/cell-phone-experiment.md
new file mode 100644
index 0000000000..f781a60873
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/cell-phone-experiment.md
@@ -0,0 +1,274 @@
+### TL;DR
+
+I will not use my cell phone during March to learn more about how much I depend
+on it.
+
+### Explore/Exploit
+
+Ever since I read Charles Duhigg's book, [The Power of Habit][poh], I try to
+habituate as many aspects of my life as I can.
+
+Making my bed every morning is an example of a habit -- so too is flossing at
+night before bed.
+
+The *exploit* axis of the [explore/exploit tradeoff][exp-exp] endows habits with
+their power. Brian Christian and Tom Griffiths explain this concept more clearly
+than I can in Chapter 2 of their exceptional book, [Algorithms to Live
+By][algos].
+
+Habits are powerful, but if I overly exploit an activity, I may settle on a
+local optimum in lieu of settling on a global optimum; these are the opportunity
+costs of exploiting (i.e. habits) versus exploring (i.e. spontaneity).
+
+But what if it was possible to habituate exploration?
+
+### Monthly challenges
+
+Every month since October 2018, I challenge myself to try something new. In the
+past, monthly challenges have been things like:
+- sign up and take Brazilian Jiu Jitsu classes
+- buy a guitar and learn [Freight Train](https://www.youtube.com/watch?v=IUK8emiWabU)
+- study Italian
+- learn a handstand
+
+Typically for an activity to qualify as a challenge, I must spend *at least
+fifteen minutes* working on it *at least five days* each week.
+
+This month (i.e. March) I'm challenging myself to avoid using my cell phone.
+
+My parents gave me a cell phone when when I was a freshman in High School; I was
+14 years old. I am now 28, so I have been using a cell phone semi-daily for over
+a decade.
+
+While I enjoy the convenience that my cell phone provides me, I am curious to
+suspend my usage to more clearly understand how much I depend on it...
+
+### April
+
+Now it is early April, and I completed March's challenge. So how was it?
+
+Below I outline the parts of using a cell phone that I missed and the parts that
+I surprisingly did not miss. I will also mention the two times that I used my
+cell phone and why.
+
+The first three things that I missed all relate to time.
+
+#### Timekeeping
+
+On the first day I realized that unless I was near a computer, I did not know
+what time it was.
+
+I exclusively use my cell phone as my watch; I do not wear a watch. To adapt, I
+started looking for clocks around my office and while I was taking public
+transportation. Thankfully London posts the current time on the digital train
+schedules. This oriented me while I was traveling, which was also when I needed
+to know the time the most.
+
+Most of the month, however, I never precisely knew what time it was.
+
+#### Alarm clocks
+
+While I anticipated living without an alarm clock prior to the experiment, I
+decided against buying a substitute. Prior to this month, I theorized that
+morning alarms probably disrupt the quality of my sleep. If I'm tired, shouldn't
+I keep sleeping?
+
+As the month progressed and my 24 hour day morphed into a 25 hour day, I learned
+that I would prefer waking up at a set time every day and synchronize my
+schedule with the rest of my timezone.
+
+I am still unsure if alarm clocks are helpful in the long-term. I would have
+slept with the curtains drawn to allow the morning sun to wake me
+up. Unfortunately, I live on the ground floor nearby a brightly shining street
+lamp that spills into my bedroom.
+
+If I lived somewhere more remote (perhaps even a suburb would do) I would like
+to repeat an experiment where I live for a month without an alarm clock.
+
+For now, I must return to the Temple of Chronology and supplicate until Father
+Time restores my sanity.
+
+#### Timers
+
+Using timers motivates me to do a bunch of short tasks like cleaning my flat for
+fifteen minutes, stretching, or reading before bed. Thankfully, I already owned
+a physical timer that I keep in my kitchen. This replaced the timer on my phone
+without disrupting my routine.
+
+#### Maps
+
+Speaking of being disoriented, what about living without maps software?  On the
+few occasions where I traveled somewhere that was unfamiliar to me, I had to
+memorize the directions from my computer before I departed.
+
+At least I didn't need to visit gas stations or museums to buy trifold tourist
+maps...
+
+I once left my office mistakenly assuming that I would download the directions
+to my destination while commuting. As I awaited the office elevator, I realized
+that I had no clue where I was heading.
+
+Thankfully I wasn't far from the safety, comfort, and familiarity of my desktop
+computer -- with its fatty WiFi connection. In no time I was studying Google
+Maps in my web browser and memorizing the directions.
+
+Overall this was hardly an inconvenience, and I think I even enjoyed
+stress-testing my memory: a job that I so often outsource to hardware.
+
+#### Rendezvouses
+
+A couple of times I met friends in various parts of the city. Organizing these
+particular rendezvouses was a novel (read: anachronistic) experience. For all
+you young whippersnappers reading, take out your stone tablets and chisels. I'm
+going to explain how this works:
+
+First I would tell my friends where and when to meet me. I emphasized that I
+would be quite helpless to any changes they might make to the plans once I began
+commuting, which made the commitments unusually more binding.
+
+On one occasion my friend -- who is characteristically prompt, and even chides
+me for when I'm late -- was twenty minutes late for our engagement. My friend is
+German, so I figured I should do my civic duty of alerting the German embassy
+that my friend had broken German code, is obscenely late, and should therefore
+hand-in his passport and renounce his citizenship. After awhile my conscience
+advised me to reconsider.
+
+It was fortunate for both of us that I did not fully understand how late he was.
+Remember: I didn't know what time it was.
+
+I decided this would be a useful opportunity to test my patience, so I loitered
+for twenty minutes outside of our meeting point. He couldn't text me to tell me
+that he was late. I couldn't listen to music, call family or friends, or partake
+in any of the other rituals that modern-day loiterers observe to pass the
+time. In the end he showed up, and it was scarcely a big deal.
+
+This experience made me wonder what the policy for abandoning plans is when
+someone is running late. Before smart phones, how long did people wait? Maybe
+the proper etiquette is to wait long enough for you to absolve yourself of the
+guilt of flaking in the unlikely event that your friend arrives shortly after
+you leave.
+
+So... thirty minutes? I'll call my grandma tomorrow and ask her.
+
+#### Boredom
+
+My phone couldn't entertain me while I queued at the grocery store. Same too
+when I commuted.
+
+I also found myself listening to less music than I usually do. I decided to read
+to occupy the void when I could; this helped me progress towards completing this
+year's [GoodReads challenge][gr-annual].
+
+### Cheating
+
+I used my phone twice during March.
+
+1. Once to use my bank's mobile app to internationally transfer money from my
+   U.K. account to my U.S. account. I could have used [TransferWise's][tw]
+   website, but I didn't.
+2. Another time I used my phone to take pictures of an item that I wanted to
+   sell on [CraigsList][cl]. I could have and perhaps should have used my laptop's
+   webcam, but at the time, I didn't want to. I am accustomed to using my phone
+   to take pictures, and I wanted to sell something.
+
+In both of these cases, prior habits eroded my resolve to stay the course. These
+are useful reminders that habits don't distinguish between helpful and hurtful;
+they just exist.
+
+In total I would estimate that I spent somewhere around fifteen minutes using
+my phone in March. While not perfect:
+
+> Better a diamond with a flaw than a pebble without (Confucius)
+
+### Substitution = Dilution
+
+While the explicit goal of this challenge was to avoid using my cell phone for a
+month, the implicit goal was to disengage from many of the
+[nonessential][essentialism] activities that compete for my attention.
+
+There were some activities that I didn't miss while living without a cell
+phone. This wasn't because I don't value these activities, but rather because I
+can adequately replace them with alternatives.
+
+For texting and making phone calls, I used [Telegram][wtf-telegram]. Telegram
+helped me sustain a healthy relationship with my girlfriend while still honoring
+the constraints of the challenge.
+
+While I appreciated the convenience Telegram provided, I felt that I remained
+about as [available][wtf-availability] during March as I was in February. If I
+ever experiment with drastically reducing my availability, I will be more
+explicit about my objectives.
+
+### Distraction displacement (whack-a-mole)
+
+Because cell phones and other electronics have conditioned my behavior, I
+habitually avoid boredom and seek entertainment. On its face this may not sound
+like a harmful practice. My generation drills the aphorism "you only live once",
+suggesting that we may want to embrace a Hedonistic lifestyle.
+
+Hedonism may or may not be a wise way to play the game of Life. All I know is
+that living a life in which I am often stimulated but proportionately distracted
+appeals increasingly less to me as time progresses.
+
+During March I noticed that once I freed my attention from sending/receiving
+texts, my brain quickly reassigned my attention to maintaining a vigil over the
+other social media outposts that I maintain.
+
+I should also admit that I habitually checked Telegram now that it served as my
+new cell phone. Didn't see that coming...
+
+In another case, once I discovered that I could use Instagram in a web browser
+instead of on my phone, I filled my newfound time and attention on
+[Instagram.com][ig] (don't click!): displacing the time that I spent on an app
+on my phone to time that I spent on a website in a web browser.
+
+Holy whack-a-mole!
+
+Halfway through the month, I wrote a [program to block websites][url-blocker] on
+my computer. Surprisingly this worked and forced me to more deliberately fill
+this hard-fought, foreign time with other activities.
+
+### Easy come, easy go?
+
+As the saying for making friends goes, "easy come, easy go", implying that
+friendships that you easily form can just as easily be destroyed.
+
+Habits invert this creation/destruction relationship. In my experience "easy
+come" implies "difficult to go".
+
+For example, I could easily form the habit of eating chocolate around 15:00 at
+work; curbing this habit would require more effort. When I compare this to the
+difficulty I experienced habituating a meditation practice, and how easily I
+can dislodge my meditation practice, it seems to me that the laws of habits
+dictate "easy come, difficult go; difficult come, easy go".
+
+I suspect that while my cravings for using a cell phone have temporarily ceased,
+they will return shortly after I start using my cell phone. And as if nothing
+happened, I return to where I was at the end of February just before I decided
+to curb my cell phone usage.
+
+Because of this, I'm planning on keeping my cell phone in my closet where I
+stored it during the month of March. As noted, enough substitutes exist for me
+to live a mostly normal life: one where I am not unnecessarily straining the
+relationships of my friends and my family. After all these are the people who
+matter most to me and those who drive me to explore new ways to improve.
+
+I recognize that the "self" in self-experimentation is a misnomer. Can you truly
+conduct an [N of 1 trial][nof1]? My decisions impact the people in my life, and
+I want to thank everyone who tolerates my eccentric and oftentimes annoying
+experimentation.
+
+Thank you for reading.
+
+[pod]: https://www.goodreads.com/book/show/12609433-the-power-of-habit
+[exp-exp]: https://en.wikipedia.org/wiki/Multi-armed_bandit
+[algos]: https://www.goodreads.com/book/show/25666050-algorithms-to-live-by
+[gr-annual]: https://www.goodreads.com/user_challenges/19737920
+[cl]: http://craigslist.com
+[tw]: https://transferwise.com
+[url-blocker]: https://github.com/wpcarro/url-blocker
+[wtf-telegram]: https://telegram.org
+[wtf-availability]: https://landing.google.com/sre/sre-book/chapters/availability-table
+[essentialism]: https://www.goodreads.com/book/show/18077875-essentialism
+[ig]: https://instagram.com
+[nof1]: https://en.wikipedia.org/wiki/N_of_1_trial
diff --git a/users/wpcarro/website/blog/posts/csharp-unused-variables.md b/users/wpcarro/website/blog/posts/csharp-unused-variables.md
new file mode 100644
index 0000000000..a5b62647bc
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/csharp-unused-variables.md
@@ -0,0 +1,38 @@
+**Problem**: This morning we broke production because (believe it or not) an
+unused variable went undetected.
+
+**Solution**: Consume the variable in the relevant codepath.
+
+**Further AI**: Treat unused variables as errors (which will block CI).
+
+## Warning/Disclaimer
+
+I am not a C# programmer. I know close to nothing about C#. But at `$WORK`, one
+of our codebases is written in C#, so occasionally I interface with it.
+
+## Treating Unused Variables as Errors
+
+C# uses `.csproj` files to configure projects. The following changes to our
+`.csproj` file WAI'd:
+
+```diff
++    <!-- IDE0059: Remove unnecessary value assignment -->
++    <WarningsAsErrors>IDE0059</WarningsAsErrors>
++    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
+```
+
+However, supporting this turned out to be a ~1h adventure... Why was this
+unexpectedly difficult? As it turns out, there are the 3x promising compiler
+warnings that I had to discover/try:
+
+- `CS0219`: doesn't WAI (see "Note" here: https://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0219)
+- `CA1804`: silently unsupported (replaced by `IDE0059`)
+- `IDE0059`: WAIs
+
+Legend:
+- `CS`: stands for C#
+- `CA`: stands for Code Analysis (I *think* a Visual Studio concept)
+- `IDE`: stands for IDE (I think *also* a Visual Studio concept)
+
+For `CA` and `IDE` prefixed warnings, `EnforceCodeStyleInBuild` must also be
+enabled.
diff --git a/users/wpcarro/website/blog/posts/git-filter-repo-note.md b/users/wpcarro/website/blog/posts/git-filter-repo-note.md
new file mode 100644
index 0000000000..e5fbb05f5c
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/git-filter-repo-note.md
@@ -0,0 +1,59 @@
+## Background
+
+- I recently used `git-filter-repo` to scrub cleartext secrets from a
+  repository.
+- We pin some services' deployments to commit SHAs.
+- These commit SHAs are no longer reachable from `origin/main`.
+
+## Problem
+
+If `git` garbage-collects any of the commits to which services are pinned, and
+that service attempts to redeploy, the deployment will fail.
+
+`git for-each-ref --contains $SHA` will report all of the refs that can reach
+some commit, `$SHA`. This may report things like:
+- `refs/replace` (i.e. `git-filter-repo` artifacts)
+- `refs/stash`
+- some local branches
+- some remote branches
+
+One solution might involve creating references to avoid garbage-collection. But
+if any of our pinned commits contains sensitive cleartext we *want* to ensure
+that `git` purges these.
+
+Instead let's find the SHAs of the new, rewritten commits and replace the pinned
+versions with those.
+
+## Solution
+
+Essentially we want to find a commit with the same *tree* state as the currently
+pinned commit. Here are two ways to get that info...
+
+This way is indirect, but provides more context about the change:
+
+```shell
+ฮป git cat-file -p $SHA
+tree d011a1dd4a3c5c4c6455ab3592fa2bf71d551d22 # <-- copy this tree info
+parent ba88bbf8de61be932184631244d2ec0ec8205cb8
+author William Carroll <wpcarro@gmail.com> 1664993052 -0700
+committer William Carroll <wpcarro@gmail.com> 1665116042 -0700
+
+feat(florp): Florp can now flarp
+
+You're welcome :)
+```
+
+This way is more direct (read: code-golf-friendly):
+
+```shell
+ฮป git log -1 --format=%T $SHA
+```
+
+Now that we have the SHA of the desired tree state, let's use it to query `git`
+for commits with the same tree SHA.
+
+```shell
+ฮป git log --format='%H %T' | grep $(git log --format=%T -1 $SHA) | awk '{ print $1 }'
+```
+
+Hopefully this helps!
diff --git a/users/wpcarro/website/blog/posts/git-rev-refs.md b/users/wpcarro/website/blog/posts/git-rev-refs.md
new file mode 100644
index 0000000000..fdc0aaf5cc
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/git-rev-refs.md
@@ -0,0 +1,85 @@
+## Credit
+
+Credit goes to `tazjin@` for this idea :)
+
+## Background
+
+Using `git` revisions to pin versions is nice, but git SHAs aren't very
+human-friendly:
+
+- They're difficult to type.
+- They're difficult to say in conversation.
+- They're difficult to compare. e.g. Which is newer? `2911fcd` or `db6ac90`?
+
+## Solution
+
+Let's assign monotonically increasing natural numbers to each of
+our repo's mainline commits and create `git` refs so we can use references like
+`r/123` instead of `2911fcd`.
+
+- They're easy to type: `r/123`
+- They're easy to say in conversion: "Check-out rev one-twenty-three."
+- They're easy to compare: `r/123` is an earlier version than `r/147`.
+
+## Backfilling
+
+Let's start-off by assigning "revision numbers" as refs for each of the mainline
+commits:
+
+```shell
+for commit in $(git rev-list --first-parent HEAD); do
+  git update-ref "refs/r/$(git rev-list --count --first-parent $commit)" $commit
+done
+```
+
+We can verify with:
+
+```shell
+ฮป git log --first-parent --oneline
+```
+
+If everything looks good, we can publish the refs to the remote:
+
+```shell
+ฮป git push origin 'refs/r/*:refs/r/*'
+```
+
+## Staying Current
+
+In order to make sure that any newly merged commits have an associated revision
+number as a ref, add something like the following to your CI system to run on
+the builds of your repo's mainline branch:
+
+```shell
+ฮป git push origin "HEAD:refs/r/$(git rev-list --count --first-parent HEAD)"
+```
+
+## Summary
+
+To verify that the remote now has the expected refs, we can use:
+
+```shell
+ฮป git ls-remote origin | less # grep for refs/r
+```
+
+If that looks good, you should now be able to *manually* fetch the refs with:
+
+```shell
+ฮป git fetch origin 'refs/r/*:refs/r/*'
+```
+
+Or you can use `git config` to automate this:
+
+```shell
+ฮป git config --add remote.origin.fetch '+refs/r/*:refs/r/*'
+ฮป git fetch origin
+```
+
+Now you can run fun commands like:
+
+```shell
+ฮป git show r/1234
+ฮป git diff r/123{4,8} # see changes from 1234 -> 1238
+```
+
+Thanks for reading!
diff --git a/users/wpcarro/website/blog/posts/importing-subtrees.md b/users/wpcarro/website/blog/posts/importing-subtrees.md
new file mode 100644
index 0000000000..e1070fc3b9
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/importing-subtrees.md
@@ -0,0 +1,147 @@
+## Background
+
+Sometimes you need to merge one Git repo into another. This is a common task
+when administrating a monorepo.
+
+Here's a checklist that I follow:
+
+1. Detect leaked secrets.
+1. Rotate leaked secrets.
+1. Purge leaked secrets from repo history.
+1. Create mainline references to branches (for deployments).
+1. Subtree-merge into the target repo.
+1. Format the code.
+1. Celebrate!
+
+## Secrets
+
+**Note:** If you notice any leaked secrets, first and foremost rotate them
+before moving on...
+
+`gitleaks` supports `gitleaks protect`, but that doesn't seem to work for `WRN`
+level leaks, which in my experience often contain sensitive cleartext. We can
+use `git-filter-repo` to purge the cleartext from our repo history.
+
+Let's make a `secrets.txt` file that we can feed `git-filter-repo`:
+
+```shell
+ฮป gitleaks detect -r /tmp/secrets.json
+ฮป jq -r 'map_values(.Secret) | .[]' /tmp/secrets.txt
+```
+
+Now for the redacting...
+
+```shell
+ฮป git-filter-repo --force --replace-text /tmp/secrets.txt
+```
+
+Verify that the secrets were removed.
+
+```shell
+ฮป rg --hidden '\*\*\*REMOVED\*\*\*'
+ฮป gitleaks detect -v
+```
+
+Looks good! Let's move on to support the adopted repo's deploy strategy.
+
+## Supporting Deploys
+
+While deploying services when someone pushes to a given branch is a common
+deployment strategy, branch-based deployment don't make a whole lot of sense in
+a monorepo.
+
+When adopting another repo, you'll typically encounter a Github Action
+configuration that contains a section like this:
+
+```yaml
+on:
+  push:
+    - staging
+    - production
+```
+
+In our monorepo, `staging` and `production` don't exist. And I don't think we
+want to support them either. `staging` and `production` are ambiguous in a
+monorepo that hosts multiple services each of which likely having its own notion
+of `staging` and `production`.
+
+Doing "pinned releases" where a service is deployed from a `git` revision from
+the mainline branch works well in these scenarios. In order to support this we
+need to make sure the adopted repo has references to
+
+`git subtree add` asks us to define which branch it should use when grafting the
+repository onto our monorepo. We'll use `main` (or whatever the mainline branch
+is).
+
+In order to support the *current* deployments while migrating to a pinned
+release strategy, we have to ensure that `main` has a commit containing the same
+tree state as `staging` *and* another commit containing the same tree state as
+`production`. Let's do that!
+
+```shell
+ฮป git checkout main # ensure you're on the main branch
+ฮป git diff main staging >/tmp/main-to-staging.patch
+ฮป git diff main production >/tmp/main-to-production.patch
+```
+
+### staging
+
+```shell
+ฮป git apply /tmp/main-to-staging.patch
+ฮป git add . && git commit # chore: main -> staging
+ฮป git revert HEAD
+ฮป git commit --amend # revert: staging -> main
+```
+
+### production
+
+```shell
+ฮป git apply /tmp/main-to-production.patch
+ฮป git add . && git commit # chore: main -> production
+ฮป git revert HEAD
+ฮป git commit --amend # revert: production -> main
+```
+
+Now let's check our work:
+
+```shell
+ฮป git log --oneline
+38f4422 revert: production -> main
+f071a9f chore: main -> production
+02ea731 revert: staging -> main
+308ed90 chore: main -> staging
+```
+
+When we go to support pinned releases we can do something like so:
+
+```json
+{
+  "staging": "308ed90",
+  "production": "f071a9f"
+}
+```
+
+## Subtree Merge
+
+Now the repo is ready to be merged.
+
+```shell
+ฮป git subtree add --prefix=foo/bar/baz path/to/baz main
+ฮป git commit --amend # subtree: Dock baz into monorepo!
+```
+
+## Formatting
+
+Some CI enforces code formatting standards, so you may need to run that:
+
+```shell
+ฮป repofmt
+ฮป git add . && git commit # chore(fmt): Format the codes
+```
+
+Lastly, if you need the latest monorepo code from `origin/main` before opening a
+pull request, the following should work:
+
+```shell
+ฮป git fetch origin main && git rebase origin/main --rebase-merges --strategy=subtree
+```
diff --git a/users/wpcarro/website/blog/posts/nginx-curl-note.md b/users/wpcarro/website/blog/posts/nginx-curl-note.md
new file mode 100644
index 0000000000..e2f4341f54
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/nginx-curl-note.md
@@ -0,0 +1,5 @@
+Use the following to make requests to Nginx virtual hosts from the host itself:
+
+```shell
+$ curl -H 'Host: trace.website.internal' localhost:8000
+```
diff --git a/users/wpcarro/website/blog/posts/nix-env-note.md b/users/wpcarro/website/blog/posts/nix-env-note.md
new file mode 100644
index 0000000000..8683c52e8f
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/nix-env-note.md
@@ -0,0 +1,33 @@
+## Background
+
+Much in the same vain as my [nix-shell (note to self)][nix-shell-note], I'm
+going to leave a note to my future self on how to install packages using
+`nix-env`, which is something I do once in a blue moon.
+
+## Solution
+
+```shell
+ฮป nix-env -iA tvix.eval -f /depot
+```
+
+Looks like I was forgetting the `-f /depot` option all this time:
+
+> --file / -f path
+>     Specifies the Nix expression (designated below as the active Nix
+>     expression) used by the --install, --upgrade, and --query --available
+>     operations to obtain derivations. The default is ~/.nix-defexpr.
+> - `man nix-env`
+
+## Failed Attempts (don't try these at home)
+
+This section is brought to you by my shell's `Ctrl-r`!
+
+```shell
+ฮป nix-env -I depot=/depot -iA depot.tvix.eval
+ฮป NIX_PATH=depot=/depot nix-env -iA depot.tvix.eval
+ฮป nix-env -iE '(import /depot {}).tvix.eval'
+```
+
+Thanks for reading!
+
+[nix-shell-note]: https://billandhiscomputer.com/blog/posts/nix-shell.html
diff --git a/users/wpcarro/website/blog/posts/nix-shell-note.md b/users/wpcarro/website/blog/posts/nix-shell-note.md
new file mode 100644
index 0000000000..da33c846ce
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/nix-shell-note.md
@@ -0,0 +1,50 @@
+## Background
+
+I rarely use `nix-shell` for its originally intended purpose of "reproducing the
+environment of a derivation for development". Instead, I often use it to put
+some executable on my `PATH` for some ad hoc task.
+
+What's `nix-shell`'s "intended purpose"? Let's ask The Man (`man nix-shell`):
+
+> The command nix-shell will build the dependencies of the specified derivation,
+> but not the derivation itself. It will then start an interactive shell in
+> which all environment variables defined by the derivation path have been set
+> to their corresponding values, and the script $stdenv/setup has been
+> sourced. This is useful for reproducing the environment of a derivation for
+> development.
+
+Because I'm abusing `nix-shell` in this way, I'm liable to forget that
+`nix-shell` puts `buildInputs` on `PATH` and *not* the derivation itself. But I
+often only want the derivation!
+
+## Solution
+
+Pass the Nix expression to `nix-shell -p`:
+
+```shell
+ฮป nix-shell -p '(import /depot {}).tvix.eval'
+```
+
+## Explanation
+
+This works because Nix forwards the arguments passed to `-p` (i.e. `--packages`)
+and interpolates them into this expression here: [source][nix-src]
+
+```nix
+{ ... }@args:
+
+with import <nixpkgs> args;
+
+(pkgs.runCommandCC or pkgs.runCommand) "shell" {
+  buildInputs = [
+    # --packages go here
+  ];
+}
+```
+
+So really you can pass-in *any* valid Nix expression that produces a derivation
+and `nix-shell` will put its outputs on your `PATH`.
+
+Enjoy!
+
+[nix-src]: https://sourcegraph.com/github.com/NixOS/nix@3ae9467d57188f9db41f85b0e5c41c0c9d141955/-/blob/src/nix-build/nix-build.cc?L266
diff --git a/users/wpcarro/website/blog/posts/nixos-disk-full-note.md b/users/wpcarro/website/blog/posts/nixos-disk-full-note.md
new file mode 100644
index 0000000000..4bbd3f58e2
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/nixos-disk-full-note.md
@@ -0,0 +1,113 @@
+## Background
+
+Every now and then NixOS hosts runs out of disk space. This happened to my IRC
+server recently...
+
+> No problem. Let's free-up some space with Nix's garbage-collection:
+> - me
+
+```shell
+ฮป nix-collect-garbage -d # failed due lack of disk space
+```
+
+Ironically Nix needs to do an SQLite transaction before deleting stuff and
+SQLite can't do that if there's no space. This is especially funny because the
+SQLite is probably a `DELETE`.
+
+## Solution
+
+First let's verify that our disk is indeed at capacity:
+
+```shell
+ฮป df -h
+Filesystem                Size  Used Avail Use% Mounted on
+devtmpfs                  399M     0  399M   0% /dev
+tmpfs                     3.9G     0  3.9G   0% /dev/shm
+tmpfs                     2.0G  3.7M  2.0G   1% /run
+tmpfs                     3.9G  408K  3.9G   1% /run/wrappers
+/dev/disk/by-label/nixos  9.9G  9.9G    0G 100% /
+tmpfs                     4.0M     0  4.0M   0% /sys/fs/cgroup
+tmpfs                     797M     0  797M   0% /run/user/0
+```
+
+Looks like `/dev/disk/by-label/nixos` is at `100%`. Now let's find some easy
+targets to free-up space so that we can run `nix-collect-garbage -d`...
+
+```shell
+ฮป du -hs /* 2>/dev/null
+8.0K    /bin
+12M     /boot
+0       /dev
+200K    /etc
+68K     /home
+16K     /lost+found
+9.0G    /nix
+0       /proc
+1.2M    /root
+2.9M    /run
+4.0K    /srv
+0       /sys
+44K     /tmp
+12K     /usr
+1.2G    /var
+```
+
+Okay: `/var` looks like an easy candidate. Let's recurse into that directory:
+
+```shell
+ฮป du -hs /var/*
+40K     /var/cache
+12K     /var/db
+4.0K    /var/empty
+4.0K    /var/google-users.d
+211M    /var/lib
+0       /var/lock
+918M    /var/log
+0       /var/run
+4.0K    /var/spool
+44K     /var/tmp
+ฮป du -hs /var/log/* # /var/log looks promising
+60M     /var/log/btmp
+82M     /var/log/btmp.1
+776M    /var/log/journal # ah-ha! journald. Let's clean-up some logs
+8.0K    /var/log/lastlog
+1.1M    /var/log/nginx
+4.0K    /var/log/private
+12K     /var/log/wtmp
+```
+
+To retain at most 1w's worth of logs:
+
+```shell
+ฮป journalctl --vacuum-time=1w
+```
+
+...or if you'd prefer to retain only 100M's worth of logs:
+
+```shell
+ฮป journalctl --vacuum-size=100M
+```
+
+Now Nix should be able to garbage-collect!
+
+```shell
+ฮป nix-collect-garbage -d
+```
+
+And lastly verify that it WAI'd:
+
+```
+ฮป df -h
+Filesystem                Size  Used Avail Use% Mounted on
+devtmpfs                  399M     0  399M   0% /dev
+tmpfs                     3.9G     0  3.9G   0% /dev/shm
+tmpfs                     2.0G  3.7M  2.0G   1% /run
+tmpfs                     3.9G  408K  3.9G   1% /run/wrappers
+/dev/disk/by-label/nixos  9.9G  5.1G  4.3G  55% /
+tmpfs                     4.0M     0  4.0M   0% /sys/fs/cgroup
+tmpfs                     797M     0  797M   0% /run/user/0
+```
+
+## Closing Thoughts
+
+Why doesn't Nix just reserve enough space to be able to GC itself? Not sure...
diff --git a/users/wpcarro/website/blog/posts/quassel-google-vm.md b/users/wpcarro/website/blog/posts/quassel-google-vm.md
new file mode 100644
index 0000000000..dd74387f8b
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/quassel-google-vm.md
@@ -0,0 +1,34 @@
+# IRC, GCP, and NixOS
+
+- "cannot read /var/lib/acme/wpcarro.dev/full.pem"
+- `sudo stat /var/lib/acme/wpcarro.dev/full.pem` exists
+- `sudo -i`
+- `su quassel` # denied
+- `sudo --user=quassel stat /var/lib/acme/wpcarro.dev/full.pem` exists
+- `groups quassel` quassel
+- `usermod -a -G nginx quassel` exists
+- `groups quassel` quassel, nginx
+- `sudo --user=quassel cat /var/lib/acme/wpcarro.dev/full.pem` exists
+
+# Firewall
+
+- `nmap localhost`
+- `nmap wpcarro.dev`
+- Update `configuration.nix` firewall
+- `nmap localhost`
+- `nmap wpcarro.dev`
+- Edit cloud.google.com Configuration (VPC > Firewall > 6697)
+
+# Quassel
+
+- Test connecting, disconnecting, persisted logs?
+- Change `~quassel@253.253.209.35.bc.googleusercontent.com` -> `~quassel@wpcarro.dev`
+  - cloaking?
+  - rDNS?
+    - `dig wpcarro.dev`       -> `35.209.253.253`
+    - `dig -x 35.209.253.253` -> `253.253.209.35.bc.googleusercontent.com`
+    - From within GCP https://stackoverflow.com/a/47060002 (create the PTR record)
+- `/msg hostserv take hackint/user/$account` add cloaking
+- disconnect/connect from hackint for changes to take affect
+- `/msg hostserv drop` remove cloaking
+- Test can I log-in from another machine?
diff --git a/users/wpcarro/website/blog/posts/restic.md b/users/wpcarro/website/blog/posts/restic.md
new file mode 100644
index 0000000000..4af1fab368
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/restic.md
@@ -0,0 +1,91 @@
+Continuing along the trend that [Profpatsch][2] recently inspired in me: writing
+short notes to myself instead of fully fledged blog posts aimed at some
+unknowable audience. Today we're looking at how I burned myself by only
+*partially* RTFD.
+
+## Background
+
+I recently started using [restic][4] and NixOS thanks to the help of [TVL's
+`restic.nix` module][1]. I setup `1x/h` backups to [MinIO][3] (S3-compatible
+storage) for just a handful of `/var/lib` directories (`~9GiB` total), but after
+a few days MinIO reported that my bucket size was `O(100GiB)`!
+
+> What's going on?
+> -- me
+
+```shell
+$ restic stats
+repository 763bfe37 opened successfully, password is correct
+scanning...
+Stats in restore-size mode:
+Snapshots processed:   175
+   Total File Count:   31369384
+         Total Size:   21.027 GiB
+```
+
+> Wait: 20GiB... wat?
+> -- me (moments later)
+
+Maybe we're snapshotting our MinIO buckets, and that's contributing to our
+bucket size. Checking the logs proved that `restic` was backing-up `1.5GiB/h`,
+which supported MinIO's reports.
+
+> Ah maybe `restic stats` isn't reporting what I *think* it's reporting...
+> -- me (again)
+
+Let's consult Le Docs:
+
+```shell
+$ restic stats -h
+
+The "stats" command walks one or multiple snapshots in a repository
+and accumulates statistics about the data stored therein. It reports
+on the number of unique files and their sizes, according to one of
+the counting modes as given by the --mode flag.
+
+It operates on all snapshots matching the selection criteria or all
+snapshots if nothing is specified. The special snapshot ID "latest"
+is also supported. Some modes make more sense over
+just a single snapshot, while others are useful across all snapshots,
+depending on what you are trying to calculate.
+
+[to be continued]
+```
+
+This is where I stopped reading (the first time). But then I returned a second
+time as I was running low on theories...
+
+```shell
+[continued]
+
+The modes are:
+
+* restore-size: (default) Counts the size of the restored files.
+* files-by-contents: Counts total size of files, where a file is
+   considered unique if it has unique contents.
+* raw-data: Counts the size of blobs in the repository, regardless of
+  how many files reference them.
+* blobs-per-file: A combination of files-by-contents and raw-data.
+```
+
+Bingo: `--mode=raw-data` **not** `--mode=restore-size`.
+
+## Solution
+
+```shell
+$ restic stats --mode=raw-data
+repository 763bfe37 opened successfully, password is correct
+scanning...
+Stats in raw-data mode:
+Snapshots processed:   175
+   Total Blob Count:   710988
+         Total Size:   303.216 GiB
+```
+
+> Ah... the world agrees again.
+> -- me
+
+[1]: https://cs.tvl.fyi/depot@2ec0d3611960b163a7052e8554ba065f3c89a8cc/-/blob/ops/modules/restic.nix?L9
+[2]: https://github.com/profpatsch
+[3]: https://min.io/
+[4]: https://restic.net/
diff --git a/users/wpcarro/website/blog/posts/send-mail-as-2fa.md b/users/wpcarro/website/blog/posts/send-mail-as-2fa.md
new file mode 100644
index 0000000000..5d18935c7a
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/send-mail-as-2fa.md
@@ -0,0 +1,43 @@
+## Prelude
+
+This is a short story about how I configured myself out of my own email. Posting
+this as an exercise in humility, a tutorial for my future self in case of
+amnesia, and penance for my sins.
+
+## Background
+
+-   I have 2x Gmail accounts: **work** and **personal**.
+-   I configure **work** to send emails as **personal**.
+-   I configure **personal** to forward incoming emails to **work**.
+
+This allows me to use **work** and manage both of my inboxes as one. I recently
+added two-factor authentication (2FA) to **personal**, forgot about it, and
+spent a few days unable to send **personal** emails from any **work** device.
+
+## Symptoms
+
+Whenever I tried to send emails on behalf of **personal**, I'd receive the
+following error message as a reply:
+
+> You're sending this from a different address using the 'Send mail as' feature.
+> The settings for your 'Send mail as' account are misconfigured or out of date.
+> Check those settings and try resending.
+
+Useful error message if you ask me (especially in retrospect), but because I had
+*forgotten* that I setup 2FA for **personal**, I naively assumed this issue
+might magically disappear given enough time... kind of how restarting your
+device resets the state and causes the symptoms of a certain class of bugs to
+disappear.
+
+After a few days of mounting frustration, I decided to take a closer look...
+
+## Solution
+
+-   Create an "App Password" for **personal**:
+    [instructions](https://support.google.com/accounts/answer/185833?hl=en).
+-   Login to **work** and delete **personal** from `Settings > Accounts > Send
+    mail as`.
+-   `Add another email address` for **personal** using the "App Password" you
+    just created.
+
+And now I'm back in business!
diff --git a/users/wpcarro/website/blog/posts/ssh-oddities.md b/users/wpcarro/website/blog/posts/ssh-oddities.md
new file mode 100644
index 0000000000..ae0bd5c9f5
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/ssh-oddities.md
@@ -0,0 +1,59 @@
+## Background
+
+I was trying to debug a service over `ssh` that offered password-only
+authentication, but I couldn't seem to get the `ssh` client to prompt me for the
+password.
+
+It looked something like this (skip ahead to the conclusion if you're pressed
+for time):
+
+## Troubleshooting
+
+```shell
+ฮป ssh root@[redacted]
+Unable to negotiate with [redacted] port 22: no matching host key type found. Their offer: ssh-rsa
+```
+
+But the same command was working just fine for my coworker.
+
+I took a closer look with `ssh -v root@[redacted]`, but nothing jumped-out at
+me. Maybe it's something with *my* `ssh` configuration; let's remove that
+variable:
+
+```shell
+ฮป ssh -F /dev/null root@[redacted]
+Unable to negotiate with [redacted] port 22: no matching host key type found. Their offer: ssh-rsa
+```
+
+> Ah it looks like there's a way to set my preferred authentication method...
+> -- me
+
+```shell
+ฮป ssh -F /dev/null -o PreferredAuthentications=password root@[redacted]
+Unable to negotiate with [redacted] port 22: no matching host key type found. Their offer: ssh-rsa
+```
+
+## Conclusion
+
+Well it turns-out that newer SSH clients disable the `ssh-rsa` public key
+signature algorithm because it depends on SHA-1, which is considered insecure.
+
+```shell
+ฮป ssh -V
+OpenSSH_9.0p1, OpenSSL 1.1.1p  21 Jun 2022
+```
+
+...and according to the `ssh -v` output, the server is running a pre-COVID(!!!)
+version of `ssh`:
+
+```
+debug1: Remote protocol version 2.0, remote software version dropbear_2018.76
+```
+
+So if you don't have time to upgrade the SSH server, and you just want to
+connect, the following should work because we're *opting-into* the less secure
+option:
+
+```shell
+ฮป ssh -o HostKeyAlgorithms=+ssh-rsa root@[redacted]
+```
diff --git a/users/wpcarro/website/blog/posts/tcp-tunneling-note.md b/users/wpcarro/website/blog/posts/tcp-tunneling-note.md
new file mode 100644
index 0000000000..06f6469aff
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/tcp-tunneling-note.md
@@ -0,0 +1,63 @@
+## Background
+
+Let's say we'd like to debug a remote machine but use some of the debugging
+tools we have on our local machine like wireshark.
+
+You *can* run `tcpdump` on the remote and then `scp` the file to your local
+machine to analyze the traffic, but after doing that a few times you may want a
+workflow with a tighter feedback loop. For this we'll forward traffic from a
+remote machine to our local machine.
+
+**Note:** There's also `termshark`, which is a `wireshark` TUI that you can run
+on the remote. It's quite cool!
+
+## Local
+
+Run the following on your local machine to forward your remote's traffic:
+
+```shell
+ฮป ssh -R 4317:127.0.0.1:4317 -N -f user@remote
+```
+
+Here is an abridged explanation of the flags we're passing from `man ssh`:
+
+```
+-N     Do  not  execute  a remote command.  This is useful for just forwarding ports.
+-f     Requests ssh to go to background just before command execution.
+```
+
+**Note:** I couldn't find a good explanation for the `-R` option, so I tried
+removing it and re-running the command, but that results in a resolution error:
+
+```
+ssh: Could not resolve hostname 4317:127.0.0.1:4317: Name or service not known
+```
+
+The remote should now be forwarding traffic from port `4317` to our
+machine.
+
+## Testing
+
+Let's generate some traffic on the remote:
+
+```shell
+ฮป telnet localhost 4317
+Trying ::1...
+Connected to localhost.
+Escape character is '^]'.
+hello
+world
+```
+
+Locally you should see:
+
+```shell
+ฮป nc -l 4317 -k # run this *before* running the above command
+hello
+world
+```
+
+You should now be able to `tcpdump -i lo port 4317` or just use `wireshark`
+locally.
+
+Happy debugging!
diff --git a/users/wpcarro/website/blog/posts/tee-time.md b/users/wpcarro/website/blog/posts/tee-time.md
new file mode 100644
index 0000000000..c8107fcded
--- /dev/null
+++ b/users/wpcarro/website/blog/posts/tee-time.md
@@ -0,0 +1,16 @@
+I encountered this fun TIL while troubleshooting Linux write permissions
+issues...
+
+## TL;DR
+
+Don't do this (unless you want misleading test results):
+
+```shell
+ฮป sudo -u node-exporter echo 'Hello, world' >/var/lib/textfile-exporter/test.prom
+```
+
+Do this:
+
+```shell
+ฮป echo 'Hello, world' | sudo -u node-exporter tee /var/lib/textfile-exporter/test.prom
+```
diff --git a/users/wpcarro/website/default.nix b/users/wpcarro/website/default.nix
new file mode 100644
index 0000000000..56f5b02cc8
--- /dev/null
+++ b/users/wpcarro/website/default.nix
@@ -0,0 +1,77 @@
+{ pkgs, depot, ... }:
+
+let
+  inherit (builtins) readFile;
+  inherit (depot.users) wpcarro;
+
+  domain = "billandhiscomputer.com";
+
+  globalVars = {
+    inherit domain;
+    homepage = "https://${domain}/";
+    blog = "https://${domain}/blog";
+    habits = "https://${domain}/habits";
+    github = "https://github.com/wpcarro";
+    linkedin = "https://linkedin.com/in/williampatrickcarroll";
+    depotWork = "https://cs.tvl.fyi/depot/-/blob/users/wpcarro";
+  };
+
+  renderTemplate = src: vars: pkgs.substituteAll (globalVars // vars // {
+    inherit src;
+  });
+
+  withBrand = contentHtml: renderTemplate ./fragments/template.html {
+    inherit contentHtml;
+  };
+
+  # Create a simple static file server using nginx to serve `content`.
+  nginxCfgFor = content: pkgs.writeText "nginx.conf" ''
+    user nobody nobody;
+    daemon off;
+    error_log /dev/stdout info;
+    pid /dev/null;
+    events {}
+    http {
+      server {
+        listen 8080;
+        location / {
+          root ${content};
+        }
+      }
+    }
+  '';
+in
+rec {
+  inherit domain renderTemplate withBrand;
+
+  content = pkgs.runCommand "wpcarro.dev" { } ''
+    mkdir -p $out
+
+    # /
+    cp ${withBrand (readFile (renderTemplate ./fragments/homepage.html {}))} $out/index.html
+
+    # /habits
+    mkdir -p $out/habits
+    cp -r ${wpcarro.website.habit-screens} $out/habits/index.html
+
+    # /blog
+    cp -r ${wpcarro.website.blog} $out/blog
+  '';
+
+  # Create a docker image suitable for Google Cloud Run (to save costs).
+  image = pkgs.dockerTools.buildLayeredImage {
+    name = "website";
+    tag = "latest";
+    contents = [ pkgs.fakeNss ];
+    extraCommands = ''
+      mkdir -p tmp/nginx_client_body
+      mkdir -p var/log/nginx
+    '';
+    config = {
+      Cmd = [ "${pkgs.nginx}/bin/nginx" "-c" (nginxCfgFor content) ];
+      ExposedPorts = { "8080/tcp" = { }; };
+    };
+  };
+
+  meta.ci.targets = [ "root" "image" ];
+}
diff --git a/users/wpcarro/website/fragments/.skip-subtree b/users/wpcarro/website/fragments/.skip-subtree
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/users/wpcarro/website/fragments/.skip-subtree
diff --git a/users/wpcarro/website/fragments/homepage.html b/users/wpcarro/website/fragments/homepage.html
new file mode 100644
index 0000000000..79ccd0ded4
--- /dev/null
+++ b/users/wpcarro/website/fragments/homepage.html
@@ -0,0 +1,20 @@
+<section class="leading-6 md:leading-7 text-xs md:text-base">
+  <p class="relative text-xl md:text-3xl text-center font-bold pt-6 md:pt-14 pb-10">
+    Hey! I'm Bill.<span class="pl-10 relative"><span class="block absolute right-0 top-0 transition-transform hover:rotate-90">๐Ÿ‘‹</span></span>
+  </p>
+  <p class="pb-4">
+    I write software. Currently I work as a <b>Site Reliability Engineer</b> for
+    <a class="text-blue-600 font-bold hover:underline" href="https://drive.google.com">Google Drive</a>.
+  </p>
+  <p class="pb-4">
+    I'm <b>wpcarro</b> on
+    <a class="font-bold text-blue-600 hover:underline" href="@github@">GitHub</a>
+    (and elsewhere), but if you're looking for code samples, the majority of
+    my open-source work resides in a magical place called the
+    <a class="font-bold text-blue-600 hover:underline" href="@depotWork@">depot</a>.
+  </p>
+  <p class="pb-4">
+    If I'm not coding, I'm likely meditating, training Jiu Jitsu, or
+    fumbling around on the piano or drums.
+  </p>
+</section>
diff --git a/users/wpcarro/website/fragments/template.html b/users/wpcarro/website/fragments/template.html
new file mode 100644
index 0000000000..0da4e46d65
--- /dev/null
+++ b/users/wpcarro/website/fragments/template.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <title>@domain@</title>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=JetBrains+Mono">
+    <script src="https://cdn.tailwindcss.com"></script>
+    <script>
+      tailwind.config = {
+        theme: {
+          extend: {
+            fontFamily: {
+                mono: ["JetBrains Mono", "normal"]
+            },
+          },
+        },
+      };
+    </script>
+  </head>
+  <body class="font-mono bg-gray-100">
+    <header class="sticky z-10 transition duration-300 bg-gray-100 top-0 py-3 md:py-6">
+      <div class="flex max-w-sm md:max-w-3xl mx-auto">
+        <div class="flex-1 text-center md:text-left text-sm md:text-base">
+          <a href="@depotWork@/website">
+            <h1 class="font-bold">
+              <span class="text-black">(</span><a class="text-purple-600 hover:underline" href="@depotWork@/website">def</a>&nbsp;<a class="text-green-600 hover:underline text-bold" href="@homepage@">"@domain@"</a><span class="text-black">)</span>
+            </h1>
+          </a>
+        </div>
+        <nav class="flex-1 hidden md:block">
+          <ul class="list-reset flex justify-end space-x-8">
+            <li>
+              <a class="hover:underline" href="@habits@">
+                Habits
+              </a>
+            </li>
+            <li>
+              <a class="hover:underline" href="@blog@">
+                Blog
+              </a>
+            </li>
+            <li>
+              <a class="hover:underline" href="@github@">
+                GitHub
+              </a>
+            </li>
+            <li>
+              <a class="hover:underline" href="@linkedin@">
+                LinkedIn
+              </a>
+            </li>
+          </ul>
+        </nav>
+      </div>
+    </header>
+    <div class="max-w-sm px-2 md:px-0 md:max-w-prose mx-auto">
+      @contentHtml@
+      <footer class="md:hidden pb-6">
+        <h2 class="text-xl font-bold py-4">More Bill?</h2>
+        <ul>
+          <li class="pb-6">
+            <a class="text-blue-600 font-bold" href="@homepage@">
+              Home <span class="text-blue-300">-></span>
+            </a>
+          </li>
+          <li class="pb-6">
+            <a class="text-blue-600 font-bold" href="@blog@">
+              Blog <span class="text-blue-300">-></span>
+            </a>
+          </li>
+          <li class="pb-6">
+            <a class="text-blue-600 font-bold" href="@github@">
+              GitHub <span class="text-blue-300">-></span>
+            </a>
+          </li>
+          <li class="pb-6">
+            <a class="text-blue-600 font-bold" href="@linkedin@">
+              LinkedIn <span class="text-blue-300">-></span>
+            </a>
+          </li>
+        </ul>
+      </footer>
+    </div>
+    <script>
+      const $header = document.querySelector("header");
+      const dropShadow = "drop-shadow-md";
+      const update = () => window.scrollY !== 0 ?
+        $header.classList.add(dropShadow) :
+        $header.classList.remove(dropShadow);
+
+      update();
+      document.addEventListener("scroll", update);
+    </script>
+  </body>
+</html>
diff --git a/users/wpcarro/website/habit-screens/.envrc b/users/wpcarro/website/habit-screens/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/website/habit-screens/.gitignore b/users/wpcarro/website/habit-screens/.gitignore
new file mode 100644
index 0000000000..0c1c258f65
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/.gitignore
@@ -0,0 +1,2 @@
+/elm-stuff
+/Main.min.js
diff --git a/users/wpcarro/website/habit-screens/README.md b/users/wpcarro/website/habit-screens/README.md
new file mode 100644
index 0000000000..506cdf9c4a
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/README.md
@@ -0,0 +1,31 @@
+# Habit Screens
+
+Problem: I would like to increase the rate at which I complete my daily, weekly,
+monthly, yearly habits.
+
+Solution: Habit Screens are mounted in strategic locations throughout my
+apartment. Each Habit Screen displays the habits that I should complete that
+day, and I can tap each item to mark it as complete. I will encounter the Habit
+Screens in my bedroom, kitchen, and bathroom, so I will have adequate "cues" to
+focus my attention. By marking each item as complete and tracking the results
+over time, I will have more incentive to maintain my consistency
+(i.e. "reward").
+
+## Elm
+
+Elm has one of the best developer experiences that I'm aware of. The error
+messages are helpful and the entire experience is optimized to improve the ease
+of writing web applications.
+
+### Developing
+
+If you're interested in contributing, the following will create an environment
+in which you can develop:
+
+```shell
+$ nix-shell
+$ npx tailwindcss build index.css -o output.css
+$ elm-live -- src/Main.elm --output=Main.min.js
+```
+
+You can now view your web client at `http://localhost:8000`!
diff --git a/users/wpcarro/website/habit-screens/default.nix b/users/wpcarro/website/habit-screens/default.nix
new file mode 100644
index 0000000000..3036ba1821
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/default.nix
@@ -0,0 +1,63 @@
+{ pkgs, ... }:
+
+with pkgs;
+
+let
+  mkDerivation =
+    { srcs ? ./elm-srcs.nix
+    , src
+    , name
+    , srcdir ? "./src"
+    , targets ? [ ]
+    , registryDat ? ./registry.dat
+    , outputJavaScript ? false
+    }:
+    stdenv.mkDerivation {
+      inherit name src;
+
+      buildInputs = [ elmPackages.elm ]
+        ++ lib.optional outputJavaScript nodePackages.uglify-js;
+
+      buildPhase = elmPackages.fetchElmDeps {
+        elmPackages = import srcs;
+        elmVersion = "0.19.1";
+        inherit registryDat;
+      };
+
+      installPhase =
+        let
+          elmfile = module: "${srcdir}/${builtins.replaceStrings ["."] ["/"] module}.elm";
+          extension = if outputJavaScript then "js" else "html";
+        in
+        ''
+          mkdir -p $out/share/doc
+          ${lib.concatStrings (map (module: ''
+            echo "compiling ${elmfile module}"
+            elm make ${elmfile module} --output $out/${module}.${extension} --docs $out/share/doc/${module}.json
+            ${lib.optionalString outputJavaScript ''
+              echo "minifying ${elmfile module}"
+              uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \
+                  | uglifyjs --mangle --output $out/${module}.min.${extension}
+            ''}
+          '') targets)}
+        '';
+    };
+  mainDotElm = mkDerivation {
+    name = "elm-app-0.1.0";
+    srcs = ./elm-srcs.nix;
+    src = ./.;
+    targets = [ "Main" ];
+    srcdir = "./src";
+    outputJavaScript = true;
+  };
+in
+stdenv.mkDerivation {
+  name = "habit-screens";
+  buildInputs = [ ];
+  src = builtins.path { path = ./.; name = "habit-screens"; };
+  buildPhase = ''
+    mkdir -p $out
+    cp index.html output.css ${mainDotElm}/Main.min.js $out
+  '';
+  dontInstall = true;
+}
diff --git a/users/wpcarro/website/habit-screens/design.md b/users/wpcarro/website/habit-screens/design.md
new file mode 100644
index 0000000000..f16361ac43
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/design.md
@@ -0,0 +1,43 @@
+# Habit Screens
+
+## MVP
+
+One Android tablet mounted on my bedroom wall displaying habits for that day. I
+can toggle the done/todo states on each item by tapping it. There is no
+server. All of the habits are defined in the client-side codebase. The
+application is available online at wpcarro.dev.
+
+## Ideal
+
+Three Android tablets: one mounted in my bedroom, another in my bathroom, and a
+third in my kitchen. Each tablet has a view of the current state of the
+application and updates in soft real-time.
+
+I track the rates at which I complete each habit and compile all of the metrics
+into a dashboard. When I move a habit from Saturday to Sunday or from Wednesday
+to Monday, it doesn't break the tracking.
+
+When I complete a habit, it quickly renders some consistency information like
+"completing rate since Monday" and "length of current streak".
+
+I don't consider this application that sensitive, but for security purposes I
+would like this application to be accessible within a private network. This is
+something I don't know too much about setting up, but I don't want anyone to be
+able to visit www.BillAndHisHabits.com and change the states of my habits and
+affect the tracking data. Nor do I want anyone to be able to make HTTP requests
+to my server to alter the state of the application without my permission.
+
+## Client
+
+Language: Elm
+
+### Updates across devices
+
+Instead of setting up sockets on my server and subscribing to them from the
+client, I think each device should poll the server once every second (or fewer)
+to maintain UI consistency.
+
+## Server
+
+Language: Haskell
+Database: SQLite
diff --git a/users/wpcarro/website/habit-screens/elm-srcs.nix b/users/wpcarro/website/habit-screens/elm-srcs.nix
new file mode 100644
index 0000000000..7f6f77741a
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/elm-srcs.nix
@@ -0,0 +1,77 @@
+{
+
+  "elm-community/maybe-extra" = {
+    sha256 = "0qslmgswa625d218djd3p62pnqcrz38f5p558mbjl6kc1ss0kzv3";
+    version = "5.2.0";
+  };
+
+  "elm/html" = {
+    sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
+    version = "1.0.0";
+  };
+
+  "elm-community/random-extra" = {
+    sha256 = "1dg2nz77w2cvp16xazbdsxkkw0xc9ycqpkd032faqdyky6gmz9g6";
+    version = "3.1.0";
+  };
+
+  "elm/svg" = {
+    sha256 = "1cwcj73p61q45wqwgqvrvz3aypjyy3fw732xyxdyj6s256hwkn0k";
+    version = "1.0.1";
+  };
+
+  "justinmimbs/date" = {
+    sha256 = "1f0wcl8yhlvp3x4rj53rdy4r4ga7lkl6n8fdfh6b96scz2rnxmd4";
+    version = "3.2.1";
+  };
+
+  "elm/browser" = {
+    sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
+    version = "1.0.2";
+  };
+
+  "elm/core" = {
+    sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
+    version = "1.0.5";
+  };
+
+  "elm-community/list-extra" = {
+    sha256 = "1ayv3148drynqnxdfwpjxal8vwzgsjqanjg7yxp6lhdcbkxgd3vd";
+    version = "8.2.3";
+  };
+
+  "elm/random" = {
+    sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
+    version = "1.0.0";
+  };
+
+  "elm/time" = {
+    sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
+    version = "1.0.0";
+  };
+
+  "elm/json" = {
+    sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
+    version = "1.1.3";
+  };
+
+  "elm/parser" = {
+    sha256 = "0a3cxrvbm7mwg9ykynhp7vjid58zsw03r63qxipxp3z09qks7512";
+    version = "1.1.0";
+  };
+
+  "owanturist/elm-union-find" = {
+    sha256 = "13gm7msnp0gr1lqia5m7m4lhy3m6kvjg37d304whb3psn88wqhj5";
+    version = "1.0.0";
+  };
+
+  "elm/url" = {
+    sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
+    version = "1.0.0";
+  };
+
+  "elm/virtual-dom" = {
+    sha256 = "0q1v5gi4g336bzz1lgwpn5b1639lrn63d8y6k6pimcyismp2i1yg";
+    version = "1.0.2";
+  };
+}
diff --git a/users/wpcarro/website/habit-screens/elm.json b/users/wpcarro/website/habit-screens/elm.json
new file mode 100644
index 0000000000..6839ac4fab
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/elm.json
@@ -0,0 +1,32 @@
+{
+    "type": "application",
+    "source-directories": [
+        "src"
+    ],
+    "elm-version": "0.19.1",
+    "dependencies": {
+        "direct": {
+            "elm/browser": "1.0.2",
+            "elm/core": "1.0.5",
+            "elm/html": "1.0.0",
+            "elm/random": "1.0.0",
+            "elm/svg": "1.0.1",
+            "elm/time": "1.0.0",
+            "elm-community/list-extra": "8.2.3",
+            "elm-community/maybe-extra": "5.2.0",
+            "elm-community/random-extra": "3.1.0",
+            "justinmimbs/date": "3.2.1"
+        },
+        "indirect": {
+            "elm/json": "1.1.3",
+            "elm/parser": "1.1.0",
+            "elm/url": "1.0.0",
+            "elm/virtual-dom": "1.0.2",
+            "owanturist/elm-union-find": "1.0.0"
+        }
+    },
+    "test-dependencies": {
+        "direct": {},
+        "indirect": {}
+    }
+}
diff --git a/users/wpcarro/website/habit-screens/index.css b/users/wpcarro/website/habit-screens/index.css
new file mode 100644
index 0000000000..b5c61c9567
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/index.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/users/wpcarro/website/habit-screens/index.html b/users/wpcarro/website/habit-screens/index.html
new file mode 100644
index 0000000000..b587e09012
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/index.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>Elm SPA</title>
+    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Chilanka">
+    <link rel="stylesheet" href="./output.css">
+    <style>
+      body {
+          font-family: 'Chilanka';
+      }
+    </style>
+    <script src="./Main.min.js"></script>
+  </head>
+  <body>
+    <div id="mount"></div>
+    <script>
+     Elm.Main.init({node: document.getElementById("mount")});
+    </script>
+  </body>
+</html>
diff --git a/users/wpcarro/website/habit-screens/output.css b/users/wpcarro/website/habit-screens/output.css
new file mode 100644
index 0000000000..b522419aa3
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/output.css
@@ -0,0 +1,103571 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+   ========================================================================== */
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+
+html {
+  line-height: 1.15; /* 1 */
+  -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/* Sections
+   ========================================================================== */
+
+/**
+ * Remove the margin in all browsers.
+ */
+
+body {
+  margin: 0;
+}
+
+/**
+ * Render the `main` element consistently in IE.
+ */
+
+main {
+  display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+/* Grouping content
+   ========================================================================== */
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+  box-sizing: content-box; /* 1 */
+  height: 0; /* 1 */
+  overflow: visible; /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+   ========================================================================== */
+
+/**
+ * Remove the gray background on active links in IE 10.
+ */
+
+a {
+  background-color: transparent;
+}
+
+/**
+ * 1. Remove the bottom border in Chrome 57-
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+  border-bottom: none; /* 1 */
+  text-decoration: underline; /* 2 */
+  -webkit-text-decoration: underline dotted;
+          text-decoration: underline dotted; /* 2 */
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+  font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+  font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+sup {
+  top: -0.5em;
+}
+
+/* Embedded content
+   ========================================================================== */
+
+/**
+ * Remove the border on images inside links in IE 10.
+ */
+
+img {
+  border-style: none;
+}
+
+/* Forms
+   ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers.
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+  font-family: inherit; /* 1 */
+  font-size: 100%; /* 1 */
+  line-height: 1.15; /* 1 */
+  margin: 0; /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input { /* 1 */
+  overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select { /* 1 */
+  text-transform: none;
+}
+
+/**
+ * Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+  -webkit-appearance: button;
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+  border-style: none;
+  padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+  outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+
+fieldset {
+  padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ *    `fieldset` elements in all browsers.
+ */
+
+legend {
+  box-sizing: border-box; /* 1 */
+  color: inherit; /* 2 */
+  display: table; /* 1 */
+  max-width: 100%; /* 1 */
+  padding: 0; /* 3 */
+  white-space: normal; /* 1 */
+}
+
+/**
+ * Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+  vertical-align: baseline;
+}
+
+/**
+ * Remove the default vertical scrollbar in IE 10+.
+ */
+
+textarea {
+  overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10.
+ * 2. Remove the padding in IE 10.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+  box-sizing: border-box; /* 1 */
+  padding: 0; /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+  -webkit-appearance: textfield; /* 1 */
+  outline-offset: -2px; /* 2 */
+}
+
+/**
+ * Remove the inner padding in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+  -webkit-appearance: button; /* 1 */
+  font: inherit; /* 2 */
+}
+
+/* Interactive
+   ========================================================================== */
+
+/*
+ * Add the correct display in Edge, IE 10+, and Firefox.
+ */
+
+details {
+  display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+  display: list-item;
+}
+
+/* Misc
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 10+.
+ */
+
+template {
+  display: none;
+}
+
+/**
+ * Add the correct display in IE 10.
+ */
+
+[hidden] {
+  display: none;
+}
+
+/**
+ * Manually forked from SUIT CSS Base: https://github.com/suitcss/base
+ * A thin layer on top of normalize.css that provides a starting point more
+ * suitable for web applications.
+ */
+
+/**
+ * Removes the default spacing and border for appropriate elements.
+ */
+
+blockquote,
+dl,
+dd,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+hr,
+figure,
+p,
+pre {
+  margin: 0;
+}
+
+button {
+  background-color: transparent;
+  background-image: none;
+}
+
+/**
+ * Work around a Firefox/IE bug where the transparent `button` background
+ * results in a loss of the default `button` focus styles.
+ */
+
+button:focus {
+  outline: 1px dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+}
+
+fieldset {
+  margin: 0;
+  padding: 0;
+}
+
+ol,
+ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+
+/**
+ * Tailwind custom reset styles
+ */
+
+/**
+ * 1. Use the user's configured `sans` font-family (with Tailwind's default
+ *    sans-serif font stack as a fallback) as a sane default.
+ * 2. Use Tailwind's default "normal" line-height so the user isn't forced
+ *    to override it to ensure consistency even when using the default theme.
+ */
+
+html {
+  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 1 */
+  line-height: 1.5; /* 2 */
+}
+
+/**
+ * 1. Prevent padding and border from affecting element width.
+ *
+ *    We used to set this in the html element and inherit from
+ *    the parent element for everything else. This caused issues
+ *    in shadow-dom-enhanced elements like <details> where the content
+ *    is wrapped by a div with box-sizing set to `content-box`.
+ *
+ *    https://github.com/mozdevs/cssremedy/issues/4
+ *
+ *
+ * 2. Allow adding a border to an element by just adding a border-width.
+ *
+ *    By default, the way the browser specifies that an element should have no
+ *    border is by setting it's border-style to `none` in the user-agent
+ *    stylesheet.
+ *
+ *    In order to easily add borders to elements by just setting the `border-width`
+ *    property, we change the default border-style for all elements to `solid`, and
+ *    use border-width to hide them instead. This way our `border` utilities only
+ *    need to set the `border-width` property instead of the entire `border`
+ *    shorthand, making our border utilities much more straightforward to compose.
+ *
+ *    https://github.com/tailwindcss/tailwindcss/pull/116
+ */
+
+*,
+::before,
+::after {
+  box-sizing: border-box; /* 1 */
+  border-width: 0; /* 2 */
+  border-style: solid; /* 2 */
+  border-color: #e2e8f0; /* 2 */
+}
+
+/*
+ * Ensure horizontal rules are visible by default
+ */
+
+hr {
+  border-top-width: 1px;
+}
+
+/**
+ * Undo the `border-style: none` reset that Normalize applies to images so that
+ * our `border-{width}` utilities have the expected effect.
+ *
+ * The Normalize reset is unnecessary for us since we default the border-width
+ * to 0 on all elements.
+ *
+ * https://github.com/tailwindcss/tailwindcss/issues/362
+ */
+
+img {
+  border-style: solid;
+}
+
+textarea {
+  resize: vertical;
+}
+
+input::-moz-placeholder, textarea::-moz-placeholder {
+  color: #a0aec0;
+}
+
+input:-ms-input-placeholder, textarea:-ms-input-placeholder {
+  color: #a0aec0;
+}
+
+input::placeholder,
+textarea::placeholder {
+  color: #a0aec0;
+}
+
+button,
+[role="button"] {
+  cursor: pointer;
+}
+
+table {
+  border-collapse: collapse;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  font-size: inherit;
+  font-weight: inherit;
+}
+
+/**
+ * Reset links to optimize for opt-in styling instead of
+ * opt-out.
+ */
+
+a {
+  color: inherit;
+  text-decoration: inherit;
+}
+
+/**
+ * Reset form element properties that are easy to forget to
+ * style explicitly so you don't inadvertently introduce
+ * styles that deviate from your design system. These styles
+ * supplement a partial reset that is already applied by
+ * normalize.css.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+  padding: 0;
+  line-height: inherit;
+  color: inherit;
+}
+
+/**
+ * Use the configured 'mono' font family for elements that
+ * are expected to be rendered with a monospace font, falling
+ * back to the system monospace stack if there is no configured
+ * 'mono' font family.
+ */
+
+pre,
+code,
+kbd,
+samp {
+  font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+/**
+ * Make replaced elements `display: block` by default as that's
+ * the behavior you want almost all of the time. Inspired by
+ * CSS Remedy, with `svg` added as well.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/14
+ */
+
+img,
+svg,
+video,
+canvas,
+audio,
+iframe,
+embed,
+object {
+  display: block;
+  vertical-align: middle;
+}
+
+/**
+ * Constrain images and videos to the parent width and preserve
+ * their instrinsic aspect ratio.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/14
+ */
+
+img,
+video {
+  max-width: 100%;
+  height: auto;
+}
+
+.container {
+  width: 100%;
+}
+
+@media (min-width: 640px) {
+  .container {
+    max-width: 640px;
+  }
+}
+
+@media (min-width: 768px) {
+  .container {
+    max-width: 768px;
+  }
+}
+
+@media (min-width: 1024px) {
+  .container {
+    max-width: 1024px;
+  }
+}
+
+@media (min-width: 1280px) {
+  .container {
+    max-width: 1280px;
+  }
+}
+
+.space-y-0 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(0px * var(--space-y-reverse));
+}
+
+.space-x-0 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(0px * var(--space-x-reverse));
+  margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-1 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(0.25rem * var(--space-y-reverse));
+}
+
+.space-x-1 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(0.25rem * var(--space-x-reverse));
+  margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-2 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(0.5rem * var(--space-y-reverse));
+}
+
+.space-x-2 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(0.5rem * var(--space-x-reverse));
+  margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-3 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(0.75rem * var(--space-y-reverse));
+}
+
+.space-x-3 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(0.75rem * var(--space-x-reverse));
+  margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-4 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(1rem * var(--space-y-reverse));
+}
+
+.space-x-4 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(1rem * var(--space-x-reverse));
+  margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-5 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(1.25rem * var(--space-y-reverse));
+}
+
+.space-x-5 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(1.25rem * var(--space-x-reverse));
+  margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-6 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(1.5rem * var(--space-y-reverse));
+}
+
+.space-x-6 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(1.5rem * var(--space-x-reverse));
+  margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-8 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(2rem * var(--space-y-reverse));
+}
+
+.space-x-8 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(2rem * var(--space-x-reverse));
+  margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-10 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(2.5rem * var(--space-y-reverse));
+}
+
+.space-x-10 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(2.5rem * var(--space-x-reverse));
+  margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-12 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(3rem * var(--space-y-reverse));
+}
+
+.space-x-12 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(3rem * var(--space-x-reverse));
+  margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-16 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(4rem * var(--space-y-reverse));
+}
+
+.space-x-16 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(4rem * var(--space-x-reverse));
+  margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-20 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(5rem * var(--space-y-reverse));
+}
+
+.space-x-20 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(5rem * var(--space-x-reverse));
+  margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-24 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(6rem * var(--space-y-reverse));
+}
+
+.space-x-24 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(6rem * var(--space-x-reverse));
+  margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-32 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(8rem * var(--space-y-reverse));
+}
+
+.space-x-32 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(8rem * var(--space-x-reverse));
+  margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-40 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(10rem * var(--space-y-reverse));
+}
+
+.space-x-40 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(10rem * var(--space-x-reverse));
+  margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-48 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(12rem * var(--space-y-reverse));
+}
+
+.space-x-48 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(12rem * var(--space-x-reverse));
+  margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-56 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(14rem * var(--space-y-reverse));
+}
+
+.space-x-56 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(14rem * var(--space-x-reverse));
+  margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-64 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(16rem * var(--space-y-reverse));
+}
+
+.space-x-64 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(16rem * var(--space-x-reverse));
+  margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-px > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(1px * var(--space-y-reverse));
+}
+
+.space-x-px > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(1px * var(--space-x-reverse));
+  margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-1 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+}
+
+.-space-x-1 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-0.25rem * var(--space-x-reverse));
+  margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-2 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+}
+
+.-space-x-2 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-0.5rem * var(--space-x-reverse));
+  margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-3 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+}
+
+.-space-x-3 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-0.75rem * var(--space-x-reverse));
+  margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-4 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-1rem * var(--space-y-reverse));
+}
+
+.-space-x-4 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-1rem * var(--space-x-reverse));
+  margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-5 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+}
+
+.-space-x-5 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-1.25rem * var(--space-x-reverse));
+  margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-6 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+}
+
+.-space-x-6 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-1.5rem * var(--space-x-reverse));
+  margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-8 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-2rem * var(--space-y-reverse));
+}
+
+.-space-x-8 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-2rem * var(--space-x-reverse));
+  margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-10 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+}
+
+.-space-x-10 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-2.5rem * var(--space-x-reverse));
+  margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-12 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-3rem * var(--space-y-reverse));
+}
+
+.-space-x-12 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-3rem * var(--space-x-reverse));
+  margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-16 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-4rem * var(--space-y-reverse));
+}
+
+.-space-x-16 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-4rem * var(--space-x-reverse));
+  margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-20 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-5rem * var(--space-y-reverse));
+}
+
+.-space-x-20 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-5rem * var(--space-x-reverse));
+  margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-24 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-6rem * var(--space-y-reverse));
+}
+
+.-space-x-24 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-6rem * var(--space-x-reverse));
+  margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-32 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-8rem * var(--space-y-reverse));
+}
+
+.-space-x-32 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-8rem * var(--space-x-reverse));
+  margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-40 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-10rem * var(--space-y-reverse));
+}
+
+.-space-x-40 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-10rem * var(--space-x-reverse));
+  margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-48 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-12rem * var(--space-y-reverse));
+}
+
+.-space-x-48 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-12rem * var(--space-x-reverse));
+  margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-56 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-14rem * var(--space-y-reverse));
+}
+
+.-space-x-56 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-14rem * var(--space-x-reverse));
+  margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-64 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-16rem * var(--space-y-reverse));
+}
+
+.-space-x-64 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-16rem * var(--space-x-reverse));
+  margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-px > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-1px * var(--space-y-reverse));
+}
+
+.-space-x-px > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-1px * var(--space-x-reverse));
+  margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-reverse > :not(template) ~ :not(template) {
+  --space-y-reverse: 1;
+}
+
+.space-x-reverse > :not(template) ~ :not(template) {
+  --space-x-reverse: 1;
+}
+
+.divide-y-0 > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(0px * var(--divide-y-reverse));
+}
+
+.divide-x-0 > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(0px * var(--divide-x-reverse));
+  border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y-2 > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(2px * var(--divide-y-reverse));
+}
+
+.divide-x-2 > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(2px * var(--divide-x-reverse));
+  border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y-4 > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(4px * var(--divide-y-reverse));
+}
+
+.divide-x-4 > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(4px * var(--divide-x-reverse));
+  border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y-8 > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(8px * var(--divide-y-reverse));
+}
+
+.divide-x-8 > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(8px * var(--divide-x-reverse));
+  border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(1px * var(--divide-y-reverse));
+}
+
+.divide-x > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(1px * var(--divide-x-reverse));
+  border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y-reverse > :not(template) ~ :not(template) {
+  --divide-y-reverse: 1;
+}
+
+.divide-x-reverse > :not(template) ~ :not(template) {
+  --divide-x-reverse: 1;
+}
+
+.divide-transparent > :not(template) ~ :not(template) {
+  border-color: transparent;
+}
+
+.divide-current > :not(template) ~ :not(template) {
+  border-color: currentColor;
+}
+
+.divide-black > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #000;
+  border-color: rgba(0, 0, 0, var(--divide-opacity));
+}
+
+.divide-white > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fff;
+  border-color: rgba(255, 255, 255, var(--divide-opacity));
+}
+
+.divide-gray-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f7fafc;
+  border-color: rgba(247, 250, 252, var(--divide-opacity));
+}
+
+.divide-gray-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #edf2f7;
+  border-color: rgba(237, 242, 247, var(--divide-opacity));
+}
+
+.divide-gray-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #e2e8f0;
+  border-color: rgba(226, 232, 240, var(--divide-opacity));
+}
+
+.divide-gray-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #cbd5e0;
+  border-color: rgba(203, 213, 224, var(--divide-opacity));
+}
+
+.divide-gray-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #a0aec0;
+  border-color: rgba(160, 174, 192, var(--divide-opacity));
+}
+
+.divide-gray-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #718096;
+  border-color: rgba(113, 128, 150, var(--divide-opacity));
+}
+
+.divide-gray-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #4a5568;
+  border-color: rgba(74, 85, 104, var(--divide-opacity));
+}
+
+.divide-gray-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2d3748;
+  border-color: rgba(45, 55, 72, var(--divide-opacity));
+}
+
+.divide-gray-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #1a202c;
+  border-color: rgba(26, 32, 44, var(--divide-opacity));
+}
+
+.divide-red-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fff5f5;
+  border-color: rgba(255, 245, 245, var(--divide-opacity));
+}
+
+.divide-red-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fed7d7;
+  border-color: rgba(254, 215, 215, var(--divide-opacity));
+}
+
+.divide-red-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #feb2b2;
+  border-color: rgba(254, 178, 178, var(--divide-opacity));
+}
+
+.divide-red-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fc8181;
+  border-color: rgba(252, 129, 129, var(--divide-opacity));
+}
+
+.divide-red-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f56565;
+  border-color: rgba(245, 101, 101, var(--divide-opacity));
+}
+
+.divide-red-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #e53e3e;
+  border-color: rgba(229, 62, 62, var(--divide-opacity));
+}
+
+.divide-red-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #c53030;
+  border-color: rgba(197, 48, 48, var(--divide-opacity));
+}
+
+.divide-red-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #9b2c2c;
+  border-color: rgba(155, 44, 44, var(--divide-opacity));
+}
+
+.divide-red-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #742a2a;
+  border-color: rgba(116, 42, 42, var(--divide-opacity));
+}
+
+.divide-orange-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fffaf0;
+  border-color: rgba(255, 250, 240, var(--divide-opacity));
+}
+
+.divide-orange-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #feebc8;
+  border-color: rgba(254, 235, 200, var(--divide-opacity));
+}
+
+.divide-orange-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fbd38d;
+  border-color: rgba(251, 211, 141, var(--divide-opacity));
+}
+
+.divide-orange-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f6ad55;
+  border-color: rgba(246, 173, 85, var(--divide-opacity));
+}
+
+.divide-orange-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ed8936;
+  border-color: rgba(237, 137, 54, var(--divide-opacity));
+}
+
+.divide-orange-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #dd6b20;
+  border-color: rgba(221, 107, 32, var(--divide-opacity));
+}
+
+.divide-orange-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #c05621;
+  border-color: rgba(192, 86, 33, var(--divide-opacity));
+}
+
+.divide-orange-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #9c4221;
+  border-color: rgba(156, 66, 33, var(--divide-opacity));
+}
+
+.divide-orange-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #7b341e;
+  border-color: rgba(123, 52, 30, var(--divide-opacity));
+}
+
+.divide-yellow-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fffff0;
+  border-color: rgba(255, 255, 240, var(--divide-opacity));
+}
+
+.divide-yellow-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fefcbf;
+  border-color: rgba(254, 252, 191, var(--divide-opacity));
+}
+
+.divide-yellow-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #faf089;
+  border-color: rgba(250, 240, 137, var(--divide-opacity));
+}
+
+.divide-yellow-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f6e05e;
+  border-color: rgba(246, 224, 94, var(--divide-opacity));
+}
+
+.divide-yellow-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ecc94b;
+  border-color: rgba(236, 201, 75, var(--divide-opacity));
+}
+
+.divide-yellow-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #d69e2e;
+  border-color: rgba(214, 158, 46, var(--divide-opacity));
+}
+
+.divide-yellow-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #b7791f;
+  border-color: rgba(183, 121, 31, var(--divide-opacity));
+}
+
+.divide-yellow-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #975a16;
+  border-color: rgba(151, 90, 22, var(--divide-opacity));
+}
+
+.divide-yellow-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #744210;
+  border-color: rgba(116, 66, 16, var(--divide-opacity));
+}
+
+.divide-green-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f0fff4;
+  border-color: rgba(240, 255, 244, var(--divide-opacity));
+}
+
+.divide-green-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #c6f6d5;
+  border-color: rgba(198, 246, 213, var(--divide-opacity));
+}
+
+.divide-green-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #9ae6b4;
+  border-color: rgba(154, 230, 180, var(--divide-opacity));
+}
+
+.divide-green-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #68d391;
+  border-color: rgba(104, 211, 145, var(--divide-opacity));
+}
+
+.divide-green-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #48bb78;
+  border-color: rgba(72, 187, 120, var(--divide-opacity));
+}
+
+.divide-green-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #38a169;
+  border-color: rgba(56, 161, 105, var(--divide-opacity));
+}
+
+.divide-green-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2f855a;
+  border-color: rgba(47, 133, 90, var(--divide-opacity));
+}
+
+.divide-green-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #276749;
+  border-color: rgba(39, 103, 73, var(--divide-opacity));
+}
+
+.divide-green-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #22543d;
+  border-color: rgba(34, 84, 61, var(--divide-opacity));
+}
+
+.divide-teal-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #e6fffa;
+  border-color: rgba(230, 255, 250, var(--divide-opacity));
+}
+
+.divide-teal-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #b2f5ea;
+  border-color: rgba(178, 245, 234, var(--divide-opacity));
+}
+
+.divide-teal-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #81e6d9;
+  border-color: rgba(129, 230, 217, var(--divide-opacity));
+}
+
+.divide-teal-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #4fd1c5;
+  border-color: rgba(79, 209, 197, var(--divide-opacity));
+}
+
+.divide-teal-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #38b2ac;
+  border-color: rgba(56, 178, 172, var(--divide-opacity));
+}
+
+.divide-teal-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #319795;
+  border-color: rgba(49, 151, 149, var(--divide-opacity));
+}
+
+.divide-teal-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2c7a7b;
+  border-color: rgba(44, 122, 123, var(--divide-opacity));
+}
+
+.divide-teal-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #285e61;
+  border-color: rgba(40, 94, 97, var(--divide-opacity));
+}
+
+.divide-teal-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #234e52;
+  border-color: rgba(35, 78, 82, var(--divide-opacity));
+}
+
+.divide-blue-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ebf8ff;
+  border-color: rgba(235, 248, 255, var(--divide-opacity));
+}
+
+.divide-blue-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #bee3f8;
+  border-color: rgba(190, 227, 248, var(--divide-opacity));
+}
+
+.divide-blue-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #90cdf4;
+  border-color: rgba(144, 205, 244, var(--divide-opacity));
+}
+
+.divide-blue-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #63b3ed;
+  border-color: rgba(99, 179, 237, var(--divide-opacity));
+}
+
+.divide-blue-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #4299e1;
+  border-color: rgba(66, 153, 225, var(--divide-opacity));
+}
+
+.divide-blue-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #3182ce;
+  border-color: rgba(49, 130, 206, var(--divide-opacity));
+}
+
+.divide-blue-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2b6cb0;
+  border-color: rgba(43, 108, 176, var(--divide-opacity));
+}
+
+.divide-blue-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2c5282;
+  border-color: rgba(44, 82, 130, var(--divide-opacity));
+}
+
+.divide-blue-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2a4365;
+  border-color: rgba(42, 67, 101, var(--divide-opacity));
+}
+
+.divide-indigo-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ebf4ff;
+  border-color: rgba(235, 244, 255, var(--divide-opacity));
+}
+
+.divide-indigo-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #c3dafe;
+  border-color: rgba(195, 218, 254, var(--divide-opacity));
+}
+
+.divide-indigo-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #a3bffa;
+  border-color: rgba(163, 191, 250, var(--divide-opacity));
+}
+
+.divide-indigo-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #7f9cf5;
+  border-color: rgba(127, 156, 245, var(--divide-opacity));
+}
+
+.divide-indigo-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #667eea;
+  border-color: rgba(102, 126, 234, var(--divide-opacity));
+}
+
+.divide-indigo-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #5a67d8;
+  border-color: rgba(90, 103, 216, var(--divide-opacity));
+}
+
+.divide-indigo-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #4c51bf;
+  border-color: rgba(76, 81, 191, var(--divide-opacity));
+}
+
+.divide-indigo-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #434190;
+  border-color: rgba(67, 65, 144, var(--divide-opacity));
+}
+
+.divide-indigo-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #3c366b;
+  border-color: rgba(60, 54, 107, var(--divide-opacity));
+}
+
+.divide-purple-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #faf5ff;
+  border-color: rgba(250, 245, 255, var(--divide-opacity));
+}
+
+.divide-purple-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #e9d8fd;
+  border-color: rgba(233, 216, 253, var(--divide-opacity));
+}
+
+.divide-purple-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #d6bcfa;
+  border-color: rgba(214, 188, 250, var(--divide-opacity));
+}
+
+.divide-purple-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #b794f4;
+  border-color: rgba(183, 148, 244, var(--divide-opacity));
+}
+
+.divide-purple-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #9f7aea;
+  border-color: rgba(159, 122, 234, var(--divide-opacity));
+}
+
+.divide-purple-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #805ad5;
+  border-color: rgba(128, 90, 213, var(--divide-opacity));
+}
+
+.divide-purple-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #6b46c1;
+  border-color: rgba(107, 70, 193, var(--divide-opacity));
+}
+
+.divide-purple-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #553c9a;
+  border-color: rgba(85, 60, 154, var(--divide-opacity));
+}
+
+.divide-purple-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #44337a;
+  border-color: rgba(68, 51, 122, var(--divide-opacity));
+}
+
+.divide-pink-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fff5f7;
+  border-color: rgba(255, 245, 247, var(--divide-opacity));
+}
+
+.divide-pink-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fed7e2;
+  border-color: rgba(254, 215, 226, var(--divide-opacity));
+}
+
+.divide-pink-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fbb6ce;
+  border-color: rgba(251, 182, 206, var(--divide-opacity));
+}
+
+.divide-pink-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f687b3;
+  border-color: rgba(246, 135, 179, var(--divide-opacity));
+}
+
+.divide-pink-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ed64a6;
+  border-color: rgba(237, 100, 166, var(--divide-opacity));
+}
+
+.divide-pink-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #d53f8c;
+  border-color: rgba(213, 63, 140, var(--divide-opacity));
+}
+
+.divide-pink-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #b83280;
+  border-color: rgba(184, 50, 128, var(--divide-opacity));
+}
+
+.divide-pink-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #97266d;
+  border-color: rgba(151, 38, 109, var(--divide-opacity));
+}
+
+.divide-pink-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #702459;
+  border-color: rgba(112, 36, 89, var(--divide-opacity));
+}
+
+.divide-solid > :not(template) ~ :not(template) {
+  border-style: solid;
+}
+
+.divide-dashed > :not(template) ~ :not(template) {
+  border-style: dashed;
+}
+
+.divide-dotted > :not(template) ~ :not(template) {
+  border-style: dotted;
+}
+
+.divide-double > :not(template) ~ :not(template) {
+  border-style: double;
+}
+
+.divide-none > :not(template) ~ :not(template) {
+  border-style: none;
+}
+
+.divide-opacity-0 > :not(template) ~ :not(template) {
+  --divide-opacity: 0;
+}
+
+.divide-opacity-25 > :not(template) ~ :not(template) {
+  --divide-opacity: 0.25;
+}
+
+.divide-opacity-50 > :not(template) ~ :not(template) {
+  --divide-opacity: 0.5;
+}
+
+.divide-opacity-75 > :not(template) ~ :not(template) {
+  --divide-opacity: 0.75;
+}
+
+.divide-opacity-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+}
+
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border-width: 0;
+}
+
+.not-sr-only {
+  position: static;
+  width: auto;
+  height: auto;
+  padding: 0;
+  margin: 0;
+  overflow: visible;
+  clip: auto;
+  white-space: normal;
+}
+
+.focus\:sr-only:focus {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border-width: 0;
+}
+
+.focus\:not-sr-only:focus {
+  position: static;
+  width: auto;
+  height: auto;
+  padding: 0;
+  margin: 0;
+  overflow: visible;
+  clip: auto;
+  white-space: normal;
+}
+
+.appearance-none {
+  -webkit-appearance: none;
+     -moz-appearance: none;
+          appearance: none;
+}
+
+.bg-fixed {
+  background-attachment: fixed;
+}
+
+.bg-local {
+  background-attachment: local;
+}
+
+.bg-scroll {
+  background-attachment: scroll;
+}
+
+.bg-clip-border {
+  background-clip: border-box;
+}
+
+.bg-clip-padding {
+  background-clip: padding-box;
+}
+
+.bg-clip-content {
+  background-clip: content-box;
+}
+
+.bg-clip-text {
+  -webkit-background-clip: text;
+          background-clip: text;
+}
+
+.bg-transparent {
+  background-color: transparent;
+}
+
+.bg-current {
+  background-color: currentColor;
+}
+
+.bg-black {
+  --bg-opacity: 1;
+  background-color: #000;
+  background-color: rgba(0, 0, 0, var(--bg-opacity));
+}
+
+.bg-white {
+  --bg-opacity: 1;
+  background-color: #fff;
+  background-color: rgba(255, 255, 255, var(--bg-opacity));
+}
+
+.bg-gray-100 {
+  --bg-opacity: 1;
+  background-color: #f7fafc;
+  background-color: rgba(247, 250, 252, var(--bg-opacity));
+}
+
+.bg-gray-200 {
+  --bg-opacity: 1;
+  background-color: #edf2f7;
+  background-color: rgba(237, 242, 247, var(--bg-opacity));
+}
+
+.bg-gray-300 {
+  --bg-opacity: 1;
+  background-color: #e2e8f0;
+  background-color: rgba(226, 232, 240, var(--bg-opacity));
+}
+
+.bg-gray-400 {
+  --bg-opacity: 1;
+  background-color: #cbd5e0;
+  background-color: rgba(203, 213, 224, var(--bg-opacity));
+}
+
+.bg-gray-500 {
+  --bg-opacity: 1;
+  background-color: #a0aec0;
+  background-color: rgba(160, 174, 192, var(--bg-opacity));
+}
+
+.bg-gray-600 {
+  --bg-opacity: 1;
+  background-color: #718096;
+  background-color: rgba(113, 128, 150, var(--bg-opacity));
+}
+
+.bg-gray-700 {
+  --bg-opacity: 1;
+  background-color: #4a5568;
+  background-color: rgba(74, 85, 104, var(--bg-opacity));
+}
+
+.bg-gray-800 {
+  --bg-opacity: 1;
+  background-color: #2d3748;
+  background-color: rgba(45, 55, 72, var(--bg-opacity));
+}
+
+.bg-gray-900 {
+  --bg-opacity: 1;
+  background-color: #1a202c;
+  background-color: rgba(26, 32, 44, var(--bg-opacity));
+}
+
+.bg-red-100 {
+  --bg-opacity: 1;
+  background-color: #fff5f5;
+  background-color: rgba(255, 245, 245, var(--bg-opacity));
+}
+
+.bg-red-200 {
+  --bg-opacity: 1;
+  background-color: #fed7d7;
+  background-color: rgba(254, 215, 215, var(--bg-opacity));
+}
+
+.bg-red-300 {
+  --bg-opacity: 1;
+  background-color: #feb2b2;
+  background-color: rgba(254, 178, 178, var(--bg-opacity));
+}
+
+.bg-red-400 {
+  --bg-opacity: 1;
+  background-color: #fc8181;
+  background-color: rgba(252, 129, 129, var(--bg-opacity));
+}
+
+.bg-red-500 {
+  --bg-opacity: 1;
+  background-color: #f56565;
+  background-color: rgba(245, 101, 101, var(--bg-opacity));
+}
+
+.bg-red-600 {
+  --bg-opacity: 1;
+  background-color: #e53e3e;
+  background-color: rgba(229, 62, 62, var(--bg-opacity));
+}
+
+.bg-red-700 {
+  --bg-opacity: 1;
+  background-color: #c53030;
+  background-color: rgba(197, 48, 48, var(--bg-opacity));
+}
+
+.bg-red-800 {
+  --bg-opacity: 1;
+  background-color: #9b2c2c;
+  background-color: rgba(155, 44, 44, var(--bg-opacity));
+}
+
+.bg-red-900 {
+  --bg-opacity: 1;
+  background-color: #742a2a;
+  background-color: rgba(116, 42, 42, var(--bg-opacity));
+}
+
+.bg-orange-100 {
+  --bg-opacity: 1;
+  background-color: #fffaf0;
+  background-color: rgba(255, 250, 240, var(--bg-opacity));
+}
+
+.bg-orange-200 {
+  --bg-opacity: 1;
+  background-color: #feebc8;
+  background-color: rgba(254, 235, 200, var(--bg-opacity));
+}
+
+.bg-orange-300 {
+  --bg-opacity: 1;
+  background-color: #fbd38d;
+  background-color: rgba(251, 211, 141, var(--bg-opacity));
+}
+
+.bg-orange-400 {
+  --bg-opacity: 1;
+  background-color: #f6ad55;
+  background-color: rgba(246, 173, 85, var(--bg-opacity));
+}
+
+.bg-orange-500 {
+  --bg-opacity: 1;
+  background-color: #ed8936;
+  background-color: rgba(237, 137, 54, var(--bg-opacity));
+}
+
+.bg-orange-600 {
+  --bg-opacity: 1;
+  background-color: #dd6b20;
+  background-color: rgba(221, 107, 32, var(--bg-opacity));
+}
+
+.bg-orange-700 {
+  --bg-opacity: 1;
+  background-color: #c05621;
+  background-color: rgba(192, 86, 33, var(--bg-opacity));
+}
+
+.bg-orange-800 {
+  --bg-opacity: 1;
+  background-color: #9c4221;
+  background-color: rgba(156, 66, 33, var(--bg-opacity));
+}
+
+.bg-orange-900 {
+  --bg-opacity: 1;
+  background-color: #7b341e;
+  background-color: rgba(123, 52, 30, var(--bg-opacity));
+}
+
+.bg-yellow-100 {
+  --bg-opacity: 1;
+  background-color: #fffff0;
+  background-color: rgba(255, 255, 240, var(--bg-opacity));
+}
+
+.bg-yellow-200 {
+  --bg-opacity: 1;
+  background-color: #fefcbf;
+  background-color: rgba(254, 252, 191, var(--bg-opacity));
+}
+
+.bg-yellow-300 {
+  --bg-opacity: 1;
+  background-color: #faf089;
+  background-color: rgba(250, 240, 137, var(--bg-opacity));
+}
+
+.bg-yellow-400 {
+  --bg-opacity: 1;
+  background-color: #f6e05e;
+  background-color: rgba(246, 224, 94, var(--bg-opacity));
+}
+
+.bg-yellow-500 {
+  --bg-opacity: 1;
+  background-color: #ecc94b;
+  background-color: rgba(236, 201, 75, var(--bg-opacity));
+}
+
+.bg-yellow-600 {
+  --bg-opacity: 1;
+  background-color: #d69e2e;
+  background-color: rgba(214, 158, 46, var(--bg-opacity));
+}
+
+.bg-yellow-700 {
+  --bg-opacity: 1;
+  background-color: #b7791f;
+  background-color: rgba(183, 121, 31, var(--bg-opacity));
+}
+
+.bg-yellow-800 {
+  --bg-opacity: 1;
+  background-color: #975a16;
+  background-color: rgba(151, 90, 22, var(--bg-opacity));
+}
+
+.bg-yellow-900 {
+  --bg-opacity: 1;
+  background-color: #744210;
+  background-color: rgba(116, 66, 16, var(--bg-opacity));
+}
+
+.bg-green-100 {
+  --bg-opacity: 1;
+  background-color: #f0fff4;
+  background-color: rgba(240, 255, 244, var(--bg-opacity));
+}
+
+.bg-green-200 {
+  --bg-opacity: 1;
+  background-color: #c6f6d5;
+  background-color: rgba(198, 246, 213, var(--bg-opacity));
+}
+
+.bg-green-300 {
+  --bg-opacity: 1;
+  background-color: #9ae6b4;
+  background-color: rgba(154, 230, 180, var(--bg-opacity));
+}
+
+.bg-green-400 {
+  --bg-opacity: 1;
+  background-color: #68d391;
+  background-color: rgba(104, 211, 145, var(--bg-opacity));
+}
+
+.bg-green-500 {
+  --bg-opacity: 1;
+  background-color: #48bb78;
+  background-color: rgba(72, 187, 120, var(--bg-opacity));
+}
+
+.bg-green-600 {
+  --bg-opacity: 1;
+  background-color: #38a169;
+  background-color: rgba(56, 161, 105, var(--bg-opacity));
+}
+
+.bg-green-700 {
+  --bg-opacity: 1;
+  background-color: #2f855a;
+  background-color: rgba(47, 133, 90, var(--bg-opacity));
+}
+
+.bg-green-800 {
+  --bg-opacity: 1;
+  background-color: #276749;
+  background-color: rgba(39, 103, 73, var(--bg-opacity));
+}
+
+.bg-green-900 {
+  --bg-opacity: 1;
+  background-color: #22543d;
+  background-color: rgba(34, 84, 61, var(--bg-opacity));
+}
+
+.bg-teal-100 {
+  --bg-opacity: 1;
+  background-color: #e6fffa;
+  background-color: rgba(230, 255, 250, var(--bg-opacity));
+}
+
+.bg-teal-200 {
+  --bg-opacity: 1;
+  background-color: #b2f5ea;
+  background-color: rgba(178, 245, 234, var(--bg-opacity));
+}
+
+.bg-teal-300 {
+  --bg-opacity: 1;
+  background-color: #81e6d9;
+  background-color: rgba(129, 230, 217, var(--bg-opacity));
+}
+
+.bg-teal-400 {
+  --bg-opacity: 1;
+  background-color: #4fd1c5;
+  background-color: rgba(79, 209, 197, var(--bg-opacity));
+}
+
+.bg-teal-500 {
+  --bg-opacity: 1;
+  background-color: #38b2ac;
+  background-color: rgba(56, 178, 172, var(--bg-opacity));
+}
+
+.bg-teal-600 {
+  --bg-opacity: 1;
+  background-color: #319795;
+  background-color: rgba(49, 151, 149, var(--bg-opacity));
+}
+
+.bg-teal-700 {
+  --bg-opacity: 1;
+  background-color: #2c7a7b;
+  background-color: rgba(44, 122, 123, var(--bg-opacity));
+}
+
+.bg-teal-800 {
+  --bg-opacity: 1;
+  background-color: #285e61;
+  background-color: rgba(40, 94, 97, var(--bg-opacity));
+}
+
+.bg-teal-900 {
+  --bg-opacity: 1;
+  background-color: #234e52;
+  background-color: rgba(35, 78, 82, var(--bg-opacity));
+}
+
+.bg-blue-100 {
+  --bg-opacity: 1;
+  background-color: #ebf8ff;
+  background-color: rgba(235, 248, 255, var(--bg-opacity));
+}
+
+.bg-blue-200 {
+  --bg-opacity: 1;
+  background-color: #bee3f8;
+  background-color: rgba(190, 227, 248, var(--bg-opacity));
+}
+
+.bg-blue-300 {
+  --bg-opacity: 1;
+  background-color: #90cdf4;
+  background-color: rgba(144, 205, 244, var(--bg-opacity));
+}
+
+.bg-blue-400 {
+  --bg-opacity: 1;
+  background-color: #63b3ed;
+  background-color: rgba(99, 179, 237, var(--bg-opacity));
+}
+
+.bg-blue-500 {
+  --bg-opacity: 1;
+  background-color: #4299e1;
+  background-color: rgba(66, 153, 225, var(--bg-opacity));
+}
+
+.bg-blue-600 {
+  --bg-opacity: 1;
+  background-color: #3182ce;
+  background-color: rgba(49, 130, 206, var(--bg-opacity));
+}
+
+.bg-blue-700 {
+  --bg-opacity: 1;
+  background-color: #2b6cb0;
+  background-color: rgba(43, 108, 176, var(--bg-opacity));
+}
+
+.bg-blue-800 {
+  --bg-opacity: 1;
+  background-color: #2c5282;
+  background-color: rgba(44, 82, 130, var(--bg-opacity));
+}
+
+.bg-blue-900 {
+  --bg-opacity: 1;
+  background-color: #2a4365;
+  background-color: rgba(42, 67, 101, var(--bg-opacity));
+}
+
+.bg-indigo-100 {
+  --bg-opacity: 1;
+  background-color: #ebf4ff;
+  background-color: rgba(235, 244, 255, var(--bg-opacity));
+}
+
+.bg-indigo-200 {
+  --bg-opacity: 1;
+  background-color: #c3dafe;
+  background-color: rgba(195, 218, 254, var(--bg-opacity));
+}
+
+.bg-indigo-300 {
+  --bg-opacity: 1;
+  background-color: #a3bffa;
+  background-color: rgba(163, 191, 250, var(--bg-opacity));
+}
+
+.bg-indigo-400 {
+  --bg-opacity: 1;
+  background-color: #7f9cf5;
+  background-color: rgba(127, 156, 245, var(--bg-opacity));
+}
+
+.bg-indigo-500 {
+  --bg-opacity: 1;
+  background-color: #667eea;
+  background-color: rgba(102, 126, 234, var(--bg-opacity));
+}
+
+.bg-indigo-600 {
+  --bg-opacity: 1;
+  background-color: #5a67d8;
+  background-color: rgba(90, 103, 216, var(--bg-opacity));
+}
+
+.bg-indigo-700 {
+  --bg-opacity: 1;
+  background-color: #4c51bf;
+  background-color: rgba(76, 81, 191, var(--bg-opacity));
+}
+
+.bg-indigo-800 {
+  --bg-opacity: 1;
+  background-color: #434190;
+  background-color: rgba(67, 65, 144, var(--bg-opacity));
+}
+
+.bg-indigo-900 {
+  --bg-opacity: 1;
+  background-color: #3c366b;
+  background-color: rgba(60, 54, 107, var(--bg-opacity));
+}
+
+.bg-purple-100 {
+  --bg-opacity: 1;
+  background-color: #faf5ff;
+  background-color: rgba(250, 245, 255, var(--bg-opacity));
+}
+
+.bg-purple-200 {
+  --bg-opacity: 1;
+  background-color: #e9d8fd;
+  background-color: rgba(233, 216, 253, var(--bg-opacity));
+}
+
+.bg-purple-300 {
+  --bg-opacity: 1;
+  background-color: #d6bcfa;
+  background-color: rgba(214, 188, 250, var(--bg-opacity));
+}
+
+.bg-purple-400 {
+  --bg-opacity: 1;
+  background-color: #b794f4;
+  background-color: rgba(183, 148, 244, var(--bg-opacity));
+}
+
+.bg-purple-500 {
+  --bg-opacity: 1;
+  background-color: #9f7aea;
+  background-color: rgba(159, 122, 234, var(--bg-opacity));
+}
+
+.bg-purple-600 {
+  --bg-opacity: 1;
+  background-color: #805ad5;
+  background-color: rgba(128, 90, 213, var(--bg-opacity));
+}
+
+.bg-purple-700 {
+  --bg-opacity: 1;
+  background-color: #6b46c1;
+  background-color: rgba(107, 70, 193, var(--bg-opacity));
+}
+
+.bg-purple-800 {
+  --bg-opacity: 1;
+  background-color: #553c9a;
+  background-color: rgba(85, 60, 154, var(--bg-opacity));
+}
+
+.bg-purple-900 {
+  --bg-opacity: 1;
+  background-color: #44337a;
+  background-color: rgba(68, 51, 122, var(--bg-opacity));
+}
+
+.bg-pink-100 {
+  --bg-opacity: 1;
+  background-color: #fff5f7;
+  background-color: rgba(255, 245, 247, var(--bg-opacity));
+}
+
+.bg-pink-200 {
+  --bg-opacity: 1;
+  background-color: #fed7e2;
+  background-color: rgba(254, 215, 226, var(--bg-opacity));
+}
+
+.bg-pink-300 {
+  --bg-opacity: 1;
+  background-color: #fbb6ce;
+  background-color: rgba(251, 182, 206, var(--bg-opacity));
+}
+
+.bg-pink-400 {
+  --bg-opacity: 1;
+  background-color: #f687b3;
+  background-color: rgba(246, 135, 179, var(--bg-opacity));
+}
+
+.bg-pink-500 {
+  --bg-opacity: 1;
+  background-color: #ed64a6;
+  background-color: rgba(237, 100, 166, var(--bg-opacity));
+}
+
+.bg-pink-600 {
+  --bg-opacity: 1;
+  background-color: #d53f8c;
+  background-color: rgba(213, 63, 140, var(--bg-opacity));
+}
+
+.bg-pink-700 {
+  --bg-opacity: 1;
+  background-color: #b83280;
+  background-color: rgba(184, 50, 128, var(--bg-opacity));
+}
+
+.bg-pink-800 {
+  --bg-opacity: 1;
+  background-color: #97266d;
+  background-color: rgba(151, 38, 109, var(--bg-opacity));
+}
+
+.bg-pink-900 {
+  --bg-opacity: 1;
+  background-color: #702459;
+  background-color: rgba(112, 36, 89, var(--bg-opacity));
+}
+
+.hover\:bg-transparent:hover {
+  background-color: transparent;
+}
+
+.hover\:bg-current:hover {
+  background-color: currentColor;
+}
+
+.hover\:bg-black:hover {
+  --bg-opacity: 1;
+  background-color: #000;
+  background-color: rgba(0, 0, 0, var(--bg-opacity));
+}
+
+.hover\:bg-white:hover {
+  --bg-opacity: 1;
+  background-color: #fff;
+  background-color: rgba(255, 255, 255, var(--bg-opacity));
+}
+
+.hover\:bg-gray-100:hover {
+  --bg-opacity: 1;
+  background-color: #f7fafc;
+  background-color: rgba(247, 250, 252, var(--bg-opacity));
+}
+
+.hover\:bg-gray-200:hover {
+  --bg-opacity: 1;
+  background-color: #edf2f7;
+  background-color: rgba(237, 242, 247, var(--bg-opacity));
+}
+
+.hover\:bg-gray-300:hover {
+  --bg-opacity: 1;
+  background-color: #e2e8f0;
+  background-color: rgba(226, 232, 240, var(--bg-opacity));
+}
+
+.hover\:bg-gray-400:hover {
+  --bg-opacity: 1;
+  background-color: #cbd5e0;
+  background-color: rgba(203, 213, 224, var(--bg-opacity));
+}
+
+.hover\:bg-gray-500:hover {
+  --bg-opacity: 1;
+  background-color: #a0aec0;
+  background-color: rgba(160, 174, 192, var(--bg-opacity));
+}
+
+.hover\:bg-gray-600:hover {
+  --bg-opacity: 1;
+  background-color: #718096;
+  background-color: rgba(113, 128, 150, var(--bg-opacity));
+}
+
+.hover\:bg-gray-700:hover {
+  --bg-opacity: 1;
+  background-color: #4a5568;
+  background-color: rgba(74, 85, 104, var(--bg-opacity));
+}
+
+.hover\:bg-gray-800:hover {
+  --bg-opacity: 1;
+  background-color: #2d3748;
+  background-color: rgba(45, 55, 72, var(--bg-opacity));
+}
+
+.hover\:bg-gray-900:hover {
+  --bg-opacity: 1;
+  background-color: #1a202c;
+  background-color: rgba(26, 32, 44, var(--bg-opacity));
+}
+
+.hover\:bg-red-100:hover {
+  --bg-opacity: 1;
+  background-color: #fff5f5;
+  background-color: rgba(255, 245, 245, var(--bg-opacity));
+}
+
+.hover\:bg-red-200:hover {
+  --bg-opacity: 1;
+  background-color: #fed7d7;
+  background-color: rgba(254, 215, 215, var(--bg-opacity));
+}
+
+.hover\:bg-red-300:hover {
+  --bg-opacity: 1;
+  background-color: #feb2b2;
+  background-color: rgba(254, 178, 178, var(--bg-opacity));
+}
+
+.hover\:bg-red-400:hover {
+  --bg-opacity: 1;
+  background-color: #fc8181;
+  background-color: rgba(252, 129, 129, var(--bg-opacity));
+}
+
+.hover\:bg-red-500:hover {
+  --bg-opacity: 1;
+  background-color: #f56565;
+  background-color: rgba(245, 101, 101, var(--bg-opacity));
+}
+
+.hover\:bg-red-600:hover {
+  --bg-opacity: 1;
+  background-color: #e53e3e;
+  background-color: rgba(229, 62, 62, var(--bg-opacity));
+}
+
+.hover\:bg-red-700:hover {
+  --bg-opacity: 1;
+  background-color: #c53030;
+  background-color: rgba(197, 48, 48, var(--bg-opacity));
+}
+
+.hover\:bg-red-800:hover {
+  --bg-opacity: 1;
+  background-color: #9b2c2c;
+  background-color: rgba(155, 44, 44, var(--bg-opacity));
+}
+
+.hover\:bg-red-900:hover {
+  --bg-opacity: 1;
+  background-color: #742a2a;
+  background-color: rgba(116, 42, 42, var(--bg-opacity));
+}
+
+.hover\:bg-orange-100:hover {
+  --bg-opacity: 1;
+  background-color: #fffaf0;
+  background-color: rgba(255, 250, 240, var(--bg-opacity));
+}
+
+.hover\:bg-orange-200:hover {
+  --bg-opacity: 1;
+  background-color: #feebc8;
+  background-color: rgba(254, 235, 200, var(--bg-opacity));
+}
+
+.hover\:bg-orange-300:hover {
+  --bg-opacity: 1;
+  background-color: #fbd38d;
+  background-color: rgba(251, 211, 141, var(--bg-opacity));
+}
+
+.hover\:bg-orange-400:hover {
+  --bg-opacity: 1;
+  background-color: #f6ad55;
+  background-color: rgba(246, 173, 85, var(--bg-opacity));
+}
+
+.hover\:bg-orange-500:hover {
+  --bg-opacity: 1;
+  background-color: #ed8936;
+  background-color: rgba(237, 137, 54, var(--bg-opacity));
+}
+
+.hover\:bg-orange-600:hover {
+  --bg-opacity: 1;
+  background-color: #dd6b20;
+  background-color: rgba(221, 107, 32, var(--bg-opacity));
+}
+
+.hover\:bg-orange-700:hover {
+  --bg-opacity: 1;
+  background-color: #c05621;
+  background-color: rgba(192, 86, 33, var(--bg-opacity));
+}
+
+.hover\:bg-orange-800:hover {
+  --bg-opacity: 1;
+  background-color: #9c4221;
+  background-color: rgba(156, 66, 33, var(--bg-opacity));
+}
+
+.hover\:bg-orange-900:hover {
+  --bg-opacity: 1;
+  background-color: #7b341e;
+  background-color: rgba(123, 52, 30, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-100:hover {
+  --bg-opacity: 1;
+  background-color: #fffff0;
+  background-color: rgba(255, 255, 240, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-200:hover {
+  --bg-opacity: 1;
+  background-color: #fefcbf;
+  background-color: rgba(254, 252, 191, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-300:hover {
+  --bg-opacity: 1;
+  background-color: #faf089;
+  background-color: rgba(250, 240, 137, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-400:hover {
+  --bg-opacity: 1;
+  background-color: #f6e05e;
+  background-color: rgba(246, 224, 94, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-500:hover {
+  --bg-opacity: 1;
+  background-color: #ecc94b;
+  background-color: rgba(236, 201, 75, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-600:hover {
+  --bg-opacity: 1;
+  background-color: #d69e2e;
+  background-color: rgba(214, 158, 46, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-700:hover {
+  --bg-opacity: 1;
+  background-color: #b7791f;
+  background-color: rgba(183, 121, 31, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-800:hover {
+  --bg-opacity: 1;
+  background-color: #975a16;
+  background-color: rgba(151, 90, 22, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-900:hover {
+  --bg-opacity: 1;
+  background-color: #744210;
+  background-color: rgba(116, 66, 16, var(--bg-opacity));
+}
+
+.hover\:bg-green-100:hover {
+  --bg-opacity: 1;
+  background-color: #f0fff4;
+  background-color: rgba(240, 255, 244, var(--bg-opacity));
+}
+
+.hover\:bg-green-200:hover {
+  --bg-opacity: 1;
+  background-color: #c6f6d5;
+  background-color: rgba(198, 246, 213, var(--bg-opacity));
+}
+
+.hover\:bg-green-300:hover {
+  --bg-opacity: 1;
+  background-color: #9ae6b4;
+  background-color: rgba(154, 230, 180, var(--bg-opacity));
+}
+
+.hover\:bg-green-400:hover {
+  --bg-opacity: 1;
+  background-color: #68d391;
+  background-color: rgba(104, 211, 145, var(--bg-opacity));
+}
+
+.hover\:bg-green-500:hover {
+  --bg-opacity: 1;
+  background-color: #48bb78;
+  background-color: rgba(72, 187, 120, var(--bg-opacity));
+}
+
+.hover\:bg-green-600:hover {
+  --bg-opacity: 1;
+  background-color: #38a169;
+  background-color: rgba(56, 161, 105, var(--bg-opacity));
+}
+
+.hover\:bg-green-700:hover {
+  --bg-opacity: 1;
+  background-color: #2f855a;
+  background-color: rgba(47, 133, 90, var(--bg-opacity));
+}
+
+.hover\:bg-green-800:hover {
+  --bg-opacity: 1;
+  background-color: #276749;
+  background-color: rgba(39, 103, 73, var(--bg-opacity));
+}
+
+.hover\:bg-green-900:hover {
+  --bg-opacity: 1;
+  background-color: #22543d;
+  background-color: rgba(34, 84, 61, var(--bg-opacity));
+}
+
+.hover\:bg-teal-100:hover {
+  --bg-opacity: 1;
+  background-color: #e6fffa;
+  background-color: rgba(230, 255, 250, var(--bg-opacity));
+}
+
+.hover\:bg-teal-200:hover {
+  --bg-opacity: 1;
+  background-color: #b2f5ea;
+  background-color: rgba(178, 245, 234, var(--bg-opacity));
+}
+
+.hover\:bg-teal-300:hover {
+  --bg-opacity: 1;
+  background-color: #81e6d9;
+  background-color: rgba(129, 230, 217, var(--bg-opacity));
+}
+
+.hover\:bg-teal-400:hover {
+  --bg-opacity: 1;
+  background-color: #4fd1c5;
+  background-color: rgba(79, 209, 197, var(--bg-opacity));
+}
+
+.hover\:bg-teal-500:hover {
+  --bg-opacity: 1;
+  background-color: #38b2ac;
+  background-color: rgba(56, 178, 172, var(--bg-opacity));
+}
+
+.hover\:bg-teal-600:hover {
+  --bg-opacity: 1;
+  background-color: #319795;
+  background-color: rgba(49, 151, 149, var(--bg-opacity));
+}
+
+.hover\:bg-teal-700:hover {
+  --bg-opacity: 1;
+  background-color: #2c7a7b;
+  background-color: rgba(44, 122, 123, var(--bg-opacity));
+}
+
+.hover\:bg-teal-800:hover {
+  --bg-opacity: 1;
+  background-color: #285e61;
+  background-color: rgba(40, 94, 97, var(--bg-opacity));
+}
+
+.hover\:bg-teal-900:hover {
+  --bg-opacity: 1;
+  background-color: #234e52;
+  background-color: rgba(35, 78, 82, var(--bg-opacity));
+}
+
+.hover\:bg-blue-100:hover {
+  --bg-opacity: 1;
+  background-color: #ebf8ff;
+  background-color: rgba(235, 248, 255, var(--bg-opacity));
+}
+
+.hover\:bg-blue-200:hover {
+  --bg-opacity: 1;
+  background-color: #bee3f8;
+  background-color: rgba(190, 227, 248, var(--bg-opacity));
+}
+
+.hover\:bg-blue-300:hover {
+  --bg-opacity: 1;
+  background-color: #90cdf4;
+  background-color: rgba(144, 205, 244, var(--bg-opacity));
+}
+
+.hover\:bg-blue-400:hover {
+  --bg-opacity: 1;
+  background-color: #63b3ed;
+  background-color: rgba(99, 179, 237, var(--bg-opacity));
+}
+
+.hover\:bg-blue-500:hover {
+  --bg-opacity: 1;
+  background-color: #4299e1;
+  background-color: rgba(66, 153, 225, var(--bg-opacity));
+}
+
+.hover\:bg-blue-600:hover {
+  --bg-opacity: 1;
+  background-color: #3182ce;
+  background-color: rgba(49, 130, 206, var(--bg-opacity));
+}
+
+.hover\:bg-blue-700:hover {
+  --bg-opacity: 1;
+  background-color: #2b6cb0;
+  background-color: rgba(43, 108, 176, var(--bg-opacity));
+}
+
+.hover\:bg-blue-800:hover {
+  --bg-opacity: 1;
+  background-color: #2c5282;
+  background-color: rgba(44, 82, 130, var(--bg-opacity));
+}
+
+.hover\:bg-blue-900:hover {
+  --bg-opacity: 1;
+  background-color: #2a4365;
+  background-color: rgba(42, 67, 101, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-100:hover {
+  --bg-opacity: 1;
+  background-color: #ebf4ff;
+  background-color: rgba(235, 244, 255, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-200:hover {
+  --bg-opacity: 1;
+  background-color: #c3dafe;
+  background-color: rgba(195, 218, 254, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-300:hover {
+  --bg-opacity: 1;
+  background-color: #a3bffa;
+  background-color: rgba(163, 191, 250, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-400:hover {
+  --bg-opacity: 1;
+  background-color: #7f9cf5;
+  background-color: rgba(127, 156, 245, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-500:hover {
+  --bg-opacity: 1;
+  background-color: #667eea;
+  background-color: rgba(102, 126, 234, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-600:hover {
+  --bg-opacity: 1;
+  background-color: #5a67d8;
+  background-color: rgba(90, 103, 216, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-700:hover {
+  --bg-opacity: 1;
+  background-color: #4c51bf;
+  background-color: rgba(76, 81, 191, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-800:hover {
+  --bg-opacity: 1;
+  background-color: #434190;
+  background-color: rgba(67, 65, 144, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-900:hover {
+  --bg-opacity: 1;
+  background-color: #3c366b;
+  background-color: rgba(60, 54, 107, var(--bg-opacity));
+}
+
+.hover\:bg-purple-100:hover {
+  --bg-opacity: 1;
+  background-color: #faf5ff;
+  background-color: rgba(250, 245, 255, var(--bg-opacity));
+}
+
+.hover\:bg-purple-200:hover {
+  --bg-opacity: 1;
+  background-color: #e9d8fd;
+  background-color: rgba(233, 216, 253, var(--bg-opacity));
+}
+
+.hover\:bg-purple-300:hover {
+  --bg-opacity: 1;
+  background-color: #d6bcfa;
+  background-color: rgba(214, 188, 250, var(--bg-opacity));
+}
+
+.hover\:bg-purple-400:hover {
+  --bg-opacity: 1;
+  background-color: #b794f4;
+  background-color: rgba(183, 148, 244, var(--bg-opacity));
+}
+
+.hover\:bg-purple-500:hover {
+  --bg-opacity: 1;
+  background-color: #9f7aea;
+  background-color: rgba(159, 122, 234, var(--bg-opacity));
+}
+
+.hover\:bg-purple-600:hover {
+  --bg-opacity: 1;
+  background-color: #805ad5;
+  background-color: rgba(128, 90, 213, var(--bg-opacity));
+}
+
+.hover\:bg-purple-700:hover {
+  --bg-opacity: 1;
+  background-color: #6b46c1;
+  background-color: rgba(107, 70, 193, var(--bg-opacity));
+}
+
+.hover\:bg-purple-800:hover {
+  --bg-opacity: 1;
+  background-color: #553c9a;
+  background-color: rgba(85, 60, 154, var(--bg-opacity));
+}
+
+.hover\:bg-purple-900:hover {
+  --bg-opacity: 1;
+  background-color: #44337a;
+  background-color: rgba(68, 51, 122, var(--bg-opacity));
+}
+
+.hover\:bg-pink-100:hover {
+  --bg-opacity: 1;
+  background-color: #fff5f7;
+  background-color: rgba(255, 245, 247, var(--bg-opacity));
+}
+
+.hover\:bg-pink-200:hover {
+  --bg-opacity: 1;
+  background-color: #fed7e2;
+  background-color: rgba(254, 215, 226, var(--bg-opacity));
+}
+
+.hover\:bg-pink-300:hover {
+  --bg-opacity: 1;
+  background-color: #fbb6ce;
+  background-color: rgba(251, 182, 206, var(--bg-opacity));
+}
+
+.hover\:bg-pink-400:hover {
+  --bg-opacity: 1;
+  background-color: #f687b3;
+  background-color: rgba(246, 135, 179, var(--bg-opacity));
+}
+
+.hover\:bg-pink-500:hover {
+  --bg-opacity: 1;
+  background-color: #ed64a6;
+  background-color: rgba(237, 100, 166, var(--bg-opacity));
+}
+
+.hover\:bg-pink-600:hover {
+  --bg-opacity: 1;
+  background-color: #d53f8c;
+  background-color: rgba(213, 63, 140, var(--bg-opacity));
+}
+
+.hover\:bg-pink-700:hover {
+  --bg-opacity: 1;
+  background-color: #b83280;
+  background-color: rgba(184, 50, 128, var(--bg-opacity));
+}
+
+.hover\:bg-pink-800:hover {
+  --bg-opacity: 1;
+  background-color: #97266d;
+  background-color: rgba(151, 38, 109, var(--bg-opacity));
+}
+
+.hover\:bg-pink-900:hover {
+  --bg-opacity: 1;
+  background-color: #702459;
+  background-color: rgba(112, 36, 89, var(--bg-opacity));
+}
+
+.focus\:bg-transparent:focus {
+  background-color: transparent;
+}
+
+.focus\:bg-current:focus {
+  background-color: currentColor;
+}
+
+.focus\:bg-black:focus {
+  --bg-opacity: 1;
+  background-color: #000;
+  background-color: rgba(0, 0, 0, var(--bg-opacity));
+}
+
+.focus\:bg-white:focus {
+  --bg-opacity: 1;
+  background-color: #fff;
+  background-color: rgba(255, 255, 255, var(--bg-opacity));
+}
+
+.focus\:bg-gray-100:focus {
+  --bg-opacity: 1;
+  background-color: #f7fafc;
+  background-color: rgba(247, 250, 252, var(--bg-opacity));
+}
+
+.focus\:bg-gray-200:focus {
+  --bg-opacity: 1;
+  background-color: #edf2f7;
+  background-color: rgba(237, 242, 247, var(--bg-opacity));
+}
+
+.focus\:bg-gray-300:focus {
+  --bg-opacity: 1;
+  background-color: #e2e8f0;
+  background-color: rgba(226, 232, 240, var(--bg-opacity));
+}
+
+.focus\:bg-gray-400:focus {
+  --bg-opacity: 1;
+  background-color: #cbd5e0;
+  background-color: rgba(203, 213, 224, var(--bg-opacity));
+}
+
+.focus\:bg-gray-500:focus {
+  --bg-opacity: 1;
+  background-color: #a0aec0;
+  background-color: rgba(160, 174, 192, var(--bg-opacity));
+}
+
+.focus\:bg-gray-600:focus {
+  --bg-opacity: 1;
+  background-color: #718096;
+  background-color: rgba(113, 128, 150, var(--bg-opacity));
+}
+
+.focus\:bg-gray-700:focus {
+  --bg-opacity: 1;
+  background-color: #4a5568;
+  background-color: rgba(74, 85, 104, var(--bg-opacity));
+}
+
+.focus\:bg-gray-800:focus {
+  --bg-opacity: 1;
+  background-color: #2d3748;
+  background-color: rgba(45, 55, 72, var(--bg-opacity));
+}
+
+.focus\:bg-gray-900:focus {
+  --bg-opacity: 1;
+  background-color: #1a202c;
+  background-color: rgba(26, 32, 44, var(--bg-opacity));
+}
+
+.focus\:bg-red-100:focus {
+  --bg-opacity: 1;
+  background-color: #fff5f5;
+  background-color: rgba(255, 245, 245, var(--bg-opacity));
+}
+
+.focus\:bg-red-200:focus {
+  --bg-opacity: 1;
+  background-color: #fed7d7;
+  background-color: rgba(254, 215, 215, var(--bg-opacity));
+}
+
+.focus\:bg-red-300:focus {
+  --bg-opacity: 1;
+  background-color: #feb2b2;
+  background-color: rgba(254, 178, 178, var(--bg-opacity));
+}
+
+.focus\:bg-red-400:focus {
+  --bg-opacity: 1;
+  background-color: #fc8181;
+  background-color: rgba(252, 129, 129, var(--bg-opacity));
+}
+
+.focus\:bg-red-500:focus {
+  --bg-opacity: 1;
+  background-color: #f56565;
+  background-color: rgba(245, 101, 101, var(--bg-opacity));
+}
+
+.focus\:bg-red-600:focus {
+  --bg-opacity: 1;
+  background-color: #e53e3e;
+  background-color: rgba(229, 62, 62, var(--bg-opacity));
+}
+
+.focus\:bg-red-700:focus {
+  --bg-opacity: 1;
+  background-color: #c53030;
+  background-color: rgba(197, 48, 48, var(--bg-opacity));
+}
+
+.focus\:bg-red-800:focus {
+  --bg-opacity: 1;
+  background-color: #9b2c2c;
+  background-color: rgba(155, 44, 44, var(--bg-opacity));
+}
+
+.focus\:bg-red-900:focus {
+  --bg-opacity: 1;
+  background-color: #742a2a;
+  background-color: rgba(116, 42, 42, var(--bg-opacity));
+}
+
+.focus\:bg-orange-100:focus {
+  --bg-opacity: 1;
+  background-color: #fffaf0;
+  background-color: rgba(255, 250, 240, var(--bg-opacity));
+}
+
+.focus\:bg-orange-200:focus {
+  --bg-opacity: 1;
+  background-color: #feebc8;
+  background-color: rgba(254, 235, 200, var(--bg-opacity));
+}
+
+.focus\:bg-orange-300:focus {
+  --bg-opacity: 1;
+  background-color: #fbd38d;
+  background-color: rgba(251, 211, 141, var(--bg-opacity));
+}
+
+.focus\:bg-orange-400:focus {
+  --bg-opacity: 1;
+  background-color: #f6ad55;
+  background-color: rgba(246, 173, 85, var(--bg-opacity));
+}
+
+.focus\:bg-orange-500:focus {
+  --bg-opacity: 1;
+  background-color: #ed8936;
+  background-color: rgba(237, 137, 54, var(--bg-opacity));
+}
+
+.focus\:bg-orange-600:focus {
+  --bg-opacity: 1;
+  background-color: #dd6b20;
+  background-color: rgba(221, 107, 32, var(--bg-opacity));
+}
+
+.focus\:bg-orange-700:focus {
+  --bg-opacity: 1;
+  background-color: #c05621;
+  background-color: rgba(192, 86, 33, var(--bg-opacity));
+}
+
+.focus\:bg-orange-800:focus {
+  --bg-opacity: 1;
+  background-color: #9c4221;
+  background-color: rgba(156, 66, 33, var(--bg-opacity));
+}
+
+.focus\:bg-orange-900:focus {
+  --bg-opacity: 1;
+  background-color: #7b341e;
+  background-color: rgba(123, 52, 30, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-100:focus {
+  --bg-opacity: 1;
+  background-color: #fffff0;
+  background-color: rgba(255, 255, 240, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-200:focus {
+  --bg-opacity: 1;
+  background-color: #fefcbf;
+  background-color: rgba(254, 252, 191, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-300:focus {
+  --bg-opacity: 1;
+  background-color: #faf089;
+  background-color: rgba(250, 240, 137, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-400:focus {
+  --bg-opacity: 1;
+  background-color: #f6e05e;
+  background-color: rgba(246, 224, 94, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-500:focus {
+  --bg-opacity: 1;
+  background-color: #ecc94b;
+  background-color: rgba(236, 201, 75, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-600:focus {
+  --bg-opacity: 1;
+  background-color: #d69e2e;
+  background-color: rgba(214, 158, 46, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-700:focus {
+  --bg-opacity: 1;
+  background-color: #b7791f;
+  background-color: rgba(183, 121, 31, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-800:focus {
+  --bg-opacity: 1;
+  background-color: #975a16;
+  background-color: rgba(151, 90, 22, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-900:focus {
+  --bg-opacity: 1;
+  background-color: #744210;
+  background-color: rgba(116, 66, 16, var(--bg-opacity));
+}
+
+.focus\:bg-green-100:focus {
+  --bg-opacity: 1;
+  background-color: #f0fff4;
+  background-color: rgba(240, 255, 244, var(--bg-opacity));
+}
+
+.focus\:bg-green-200:focus {
+  --bg-opacity: 1;
+  background-color: #c6f6d5;
+  background-color: rgba(198, 246, 213, var(--bg-opacity));
+}
+
+.focus\:bg-green-300:focus {
+  --bg-opacity: 1;
+  background-color: #9ae6b4;
+  background-color: rgba(154, 230, 180, var(--bg-opacity));
+}
+
+.focus\:bg-green-400:focus {
+  --bg-opacity: 1;
+  background-color: #68d391;
+  background-color: rgba(104, 211, 145, var(--bg-opacity));
+}
+
+.focus\:bg-green-500:focus {
+  --bg-opacity: 1;
+  background-color: #48bb78;
+  background-color: rgba(72, 187, 120, var(--bg-opacity));
+}
+
+.focus\:bg-green-600:focus {
+  --bg-opacity: 1;
+  background-color: #38a169;
+  background-color: rgba(56, 161, 105, var(--bg-opacity));
+}
+
+.focus\:bg-green-700:focus {
+  --bg-opacity: 1;
+  background-color: #2f855a;
+  background-color: rgba(47, 133, 90, var(--bg-opacity));
+}
+
+.focus\:bg-green-800:focus {
+  --bg-opacity: 1;
+  background-color: #276749;
+  background-color: rgba(39, 103, 73, var(--bg-opacity));
+}
+
+.focus\:bg-green-900:focus {
+  --bg-opacity: 1;
+  background-color: #22543d;
+  background-color: rgba(34, 84, 61, var(--bg-opacity));
+}
+
+.focus\:bg-teal-100:focus {
+  --bg-opacity: 1;
+  background-color: #e6fffa;
+  background-color: rgba(230, 255, 250, var(--bg-opacity));
+}
+
+.focus\:bg-teal-200:focus {
+  --bg-opacity: 1;
+  background-color: #b2f5ea;
+  background-color: rgba(178, 245, 234, var(--bg-opacity));
+}
+
+.focus\:bg-teal-300:focus {
+  --bg-opacity: 1;
+  background-color: #81e6d9;
+  background-color: rgba(129, 230, 217, var(--bg-opacity));
+}
+
+.focus\:bg-teal-400:focus {
+  --bg-opacity: 1;
+  background-color: #4fd1c5;
+  background-color: rgba(79, 209, 197, var(--bg-opacity));
+}
+
+.focus\:bg-teal-500:focus {
+  --bg-opacity: 1;
+  background-color: #38b2ac;
+  background-color: rgba(56, 178, 172, var(--bg-opacity));
+}
+
+.focus\:bg-teal-600:focus {
+  --bg-opacity: 1;
+  background-color: #319795;
+  background-color: rgba(49, 151, 149, var(--bg-opacity));
+}
+
+.focus\:bg-teal-700:focus {
+  --bg-opacity: 1;
+  background-color: #2c7a7b;
+  background-color: rgba(44, 122, 123, var(--bg-opacity));
+}
+
+.focus\:bg-teal-800:focus {
+  --bg-opacity: 1;
+  background-color: #285e61;
+  background-color: rgba(40, 94, 97, var(--bg-opacity));
+}
+
+.focus\:bg-teal-900:focus {
+  --bg-opacity: 1;
+  background-color: #234e52;
+  background-color: rgba(35, 78, 82, var(--bg-opacity));
+}
+
+.focus\:bg-blue-100:focus {
+  --bg-opacity: 1;
+  background-color: #ebf8ff;
+  background-color: rgba(235, 248, 255, var(--bg-opacity));
+}
+
+.focus\:bg-blue-200:focus {
+  --bg-opacity: 1;
+  background-color: #bee3f8;
+  background-color: rgba(190, 227, 248, var(--bg-opacity));
+}
+
+.focus\:bg-blue-300:focus {
+  --bg-opacity: 1;
+  background-color: #90cdf4;
+  background-color: rgba(144, 205, 244, var(--bg-opacity));
+}
+
+.focus\:bg-blue-400:focus {
+  --bg-opacity: 1;
+  background-color: #63b3ed;
+  background-color: rgba(99, 179, 237, var(--bg-opacity));
+}
+
+.focus\:bg-blue-500:focus {
+  --bg-opacity: 1;
+  background-color: #4299e1;
+  background-color: rgba(66, 153, 225, var(--bg-opacity));
+}
+
+.focus\:bg-blue-600:focus {
+  --bg-opacity: 1;
+  background-color: #3182ce;
+  background-color: rgba(49, 130, 206, var(--bg-opacity));
+}
+
+.focus\:bg-blue-700:focus {
+  --bg-opacity: 1;
+  background-color: #2b6cb0;
+  background-color: rgba(43, 108, 176, var(--bg-opacity));
+}
+
+.focus\:bg-blue-800:focus {
+  --bg-opacity: 1;
+  background-color: #2c5282;
+  background-color: rgba(44, 82, 130, var(--bg-opacity));
+}
+
+.focus\:bg-blue-900:focus {
+  --bg-opacity: 1;
+  background-color: #2a4365;
+  background-color: rgba(42, 67, 101, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-100:focus {
+  --bg-opacity: 1;
+  background-color: #ebf4ff;
+  background-color: rgba(235, 244, 255, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-200:focus {
+  --bg-opacity: 1;
+  background-color: #c3dafe;
+  background-color: rgba(195, 218, 254, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-300:focus {
+  --bg-opacity: 1;
+  background-color: #a3bffa;
+  background-color: rgba(163, 191, 250, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-400:focus {
+  --bg-opacity: 1;
+  background-color: #7f9cf5;
+  background-color: rgba(127, 156, 245, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-500:focus {
+  --bg-opacity: 1;
+  background-color: #667eea;
+  background-color: rgba(102, 126, 234, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-600:focus {
+  --bg-opacity: 1;
+  background-color: #5a67d8;
+  background-color: rgba(90, 103, 216, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-700:focus {
+  --bg-opacity: 1;
+  background-color: #4c51bf;
+  background-color: rgba(76, 81, 191, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-800:focus {
+  --bg-opacity: 1;
+  background-color: #434190;
+  background-color: rgba(67, 65, 144, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-900:focus {
+  --bg-opacity: 1;
+  background-color: #3c366b;
+  background-color: rgba(60, 54, 107, var(--bg-opacity));
+}
+
+.focus\:bg-purple-100:focus {
+  --bg-opacity: 1;
+  background-color: #faf5ff;
+  background-color: rgba(250, 245, 255, var(--bg-opacity));
+}
+
+.focus\:bg-purple-200:focus {
+  --bg-opacity: 1;
+  background-color: #e9d8fd;
+  background-color: rgba(233, 216, 253, var(--bg-opacity));
+}
+
+.focus\:bg-purple-300:focus {
+  --bg-opacity: 1;
+  background-color: #d6bcfa;
+  background-color: rgba(214, 188, 250, var(--bg-opacity));
+}
+
+.focus\:bg-purple-400:focus {
+  --bg-opacity: 1;
+  background-color: #b794f4;
+  background-color: rgba(183, 148, 244, var(--bg-opacity));
+}
+
+.focus\:bg-purple-500:focus {
+  --bg-opacity: 1;
+  background-color: #9f7aea;
+  background-color: rgba(159, 122, 234, var(--bg-opacity));
+}
+
+.focus\:bg-purple-600:focus {
+  --bg-opacity: 1;
+  background-color: #805ad5;
+  background-color: rgba(128, 90, 213, var(--bg-opacity));
+}
+
+.focus\:bg-purple-700:focus {
+  --bg-opacity: 1;
+  background-color: #6b46c1;
+  background-color: rgba(107, 70, 193, var(--bg-opacity));
+}
+
+.focus\:bg-purple-800:focus {
+  --bg-opacity: 1;
+  background-color: #553c9a;
+  background-color: rgba(85, 60, 154, var(--bg-opacity));
+}
+
+.focus\:bg-purple-900:focus {
+  --bg-opacity: 1;
+  background-color: #44337a;
+  background-color: rgba(68, 51, 122, var(--bg-opacity));
+}
+
+.focus\:bg-pink-100:focus {
+  --bg-opacity: 1;
+  background-color: #fff5f7;
+  background-color: rgba(255, 245, 247, var(--bg-opacity));
+}
+
+.focus\:bg-pink-200:focus {
+  --bg-opacity: 1;
+  background-color: #fed7e2;
+  background-color: rgba(254, 215, 226, var(--bg-opacity));
+}
+
+.focus\:bg-pink-300:focus {
+  --bg-opacity: 1;
+  background-color: #fbb6ce;
+  background-color: rgba(251, 182, 206, var(--bg-opacity));
+}
+
+.focus\:bg-pink-400:focus {
+  --bg-opacity: 1;
+  background-color: #f687b3;
+  background-color: rgba(246, 135, 179, var(--bg-opacity));
+}
+
+.focus\:bg-pink-500:focus {
+  --bg-opacity: 1;
+  background-color: #ed64a6;
+  background-color: rgba(237, 100, 166, var(--bg-opacity));
+}
+
+.focus\:bg-pink-600:focus {
+  --bg-opacity: 1;
+  background-color: #d53f8c;
+  background-color: rgba(213, 63, 140, var(--bg-opacity));
+}
+
+.focus\:bg-pink-700:focus {
+  --bg-opacity: 1;
+  background-color: #b83280;
+  background-color: rgba(184, 50, 128, var(--bg-opacity));
+}
+
+.focus\:bg-pink-800:focus {
+  --bg-opacity: 1;
+  background-color: #97266d;
+  background-color: rgba(151, 38, 109, var(--bg-opacity));
+}
+
+.focus\:bg-pink-900:focus {
+  --bg-opacity: 1;
+  background-color: #702459;
+  background-color: rgba(112, 36, 89, var(--bg-opacity));
+}
+
+.bg-none {
+  background-image: none;
+}
+
+.bg-gradient-to-t {
+  background-image: linear-gradient(to top, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-tr {
+  background-image: linear-gradient(to top right, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-r {
+  background-image: linear-gradient(to right, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-br {
+  background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-b {
+  background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-bl {
+  background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-l {
+  background-image: linear-gradient(to left, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-tl {
+  background-image: linear-gradient(to top left, var(--gradient-color-stops));
+}
+
+.from-transparent {
+  --gradient-from-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.from-current {
+  --gradient-from-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.from-black {
+  --gradient-from-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.from-white {
+  --gradient-from-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.from-gray-100 {
+  --gradient-from-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.from-gray-200 {
+  --gradient-from-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.from-gray-300 {
+  --gradient-from-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.from-gray-400 {
+  --gradient-from-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.from-gray-500 {
+  --gradient-from-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.from-gray-600 {
+  --gradient-from-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.from-gray-700 {
+  --gradient-from-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.from-gray-800 {
+  --gradient-from-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.from-gray-900 {
+  --gradient-from-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.from-red-100 {
+  --gradient-from-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.from-red-200 {
+  --gradient-from-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.from-red-300 {
+  --gradient-from-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.from-red-400 {
+  --gradient-from-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.from-red-500 {
+  --gradient-from-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.from-red-600 {
+  --gradient-from-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.from-red-700 {
+  --gradient-from-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.from-red-800 {
+  --gradient-from-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.from-red-900 {
+  --gradient-from-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.from-orange-100 {
+  --gradient-from-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.from-orange-200 {
+  --gradient-from-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.from-orange-300 {
+  --gradient-from-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.from-orange-400 {
+  --gradient-from-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.from-orange-500 {
+  --gradient-from-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.from-orange-600 {
+  --gradient-from-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.from-orange-700 {
+  --gradient-from-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.from-orange-800 {
+  --gradient-from-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.from-orange-900 {
+  --gradient-from-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.from-yellow-100 {
+  --gradient-from-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.from-yellow-200 {
+  --gradient-from-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.from-yellow-300 {
+  --gradient-from-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.from-yellow-400 {
+  --gradient-from-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.from-yellow-500 {
+  --gradient-from-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.from-yellow-600 {
+  --gradient-from-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.from-yellow-700 {
+  --gradient-from-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.from-yellow-800 {
+  --gradient-from-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.from-yellow-900 {
+  --gradient-from-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.from-green-100 {
+  --gradient-from-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.from-green-200 {
+  --gradient-from-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.from-green-300 {
+  --gradient-from-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.from-green-400 {
+  --gradient-from-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.from-green-500 {
+  --gradient-from-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.from-green-600 {
+  --gradient-from-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.from-green-700 {
+  --gradient-from-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.from-green-800 {
+  --gradient-from-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.from-green-900 {
+  --gradient-from-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.from-teal-100 {
+  --gradient-from-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.from-teal-200 {
+  --gradient-from-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.from-teal-300 {
+  --gradient-from-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.from-teal-400 {
+  --gradient-from-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.from-teal-500 {
+  --gradient-from-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.from-teal-600 {
+  --gradient-from-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.from-teal-700 {
+  --gradient-from-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.from-teal-800 {
+  --gradient-from-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.from-teal-900 {
+  --gradient-from-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.from-blue-100 {
+  --gradient-from-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.from-blue-200 {
+  --gradient-from-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.from-blue-300 {
+  --gradient-from-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.from-blue-400 {
+  --gradient-from-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.from-blue-500 {
+  --gradient-from-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.from-blue-600 {
+  --gradient-from-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.from-blue-700 {
+  --gradient-from-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.from-blue-800 {
+  --gradient-from-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.from-blue-900 {
+  --gradient-from-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.from-indigo-100 {
+  --gradient-from-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.from-indigo-200 {
+  --gradient-from-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.from-indigo-300 {
+  --gradient-from-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.from-indigo-400 {
+  --gradient-from-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.from-indigo-500 {
+  --gradient-from-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.from-indigo-600 {
+  --gradient-from-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.from-indigo-700 {
+  --gradient-from-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.from-indigo-800 {
+  --gradient-from-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.from-indigo-900 {
+  --gradient-from-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.from-purple-100 {
+  --gradient-from-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.from-purple-200 {
+  --gradient-from-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.from-purple-300 {
+  --gradient-from-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.from-purple-400 {
+  --gradient-from-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.from-purple-500 {
+  --gradient-from-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.from-purple-600 {
+  --gradient-from-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.from-purple-700 {
+  --gradient-from-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.from-purple-800 {
+  --gradient-from-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.from-purple-900 {
+  --gradient-from-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.from-pink-100 {
+  --gradient-from-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.from-pink-200 {
+  --gradient-from-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.from-pink-300 {
+  --gradient-from-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.from-pink-400 {
+  --gradient-from-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.from-pink-500 {
+  --gradient-from-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.from-pink-600 {
+  --gradient-from-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.from-pink-700 {
+  --gradient-from-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.from-pink-800 {
+  --gradient-from-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.from-pink-900 {
+  --gradient-from-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.via-transparent {
+  --gradient-via-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.via-current {
+  --gradient-via-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.via-black {
+  --gradient-via-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.via-white {
+  --gradient-via-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.via-gray-100 {
+  --gradient-via-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.via-gray-200 {
+  --gradient-via-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.via-gray-300 {
+  --gradient-via-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.via-gray-400 {
+  --gradient-via-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.via-gray-500 {
+  --gradient-via-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.via-gray-600 {
+  --gradient-via-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.via-gray-700 {
+  --gradient-via-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.via-gray-800 {
+  --gradient-via-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.via-gray-900 {
+  --gradient-via-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.via-red-100 {
+  --gradient-via-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.via-red-200 {
+  --gradient-via-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.via-red-300 {
+  --gradient-via-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.via-red-400 {
+  --gradient-via-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.via-red-500 {
+  --gradient-via-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.via-red-600 {
+  --gradient-via-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.via-red-700 {
+  --gradient-via-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.via-red-800 {
+  --gradient-via-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.via-red-900 {
+  --gradient-via-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.via-orange-100 {
+  --gradient-via-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.via-orange-200 {
+  --gradient-via-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.via-orange-300 {
+  --gradient-via-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.via-orange-400 {
+  --gradient-via-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.via-orange-500 {
+  --gradient-via-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.via-orange-600 {
+  --gradient-via-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.via-orange-700 {
+  --gradient-via-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.via-orange-800 {
+  --gradient-via-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.via-orange-900 {
+  --gradient-via-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.via-yellow-100 {
+  --gradient-via-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.via-yellow-200 {
+  --gradient-via-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.via-yellow-300 {
+  --gradient-via-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.via-yellow-400 {
+  --gradient-via-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.via-yellow-500 {
+  --gradient-via-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.via-yellow-600 {
+  --gradient-via-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.via-yellow-700 {
+  --gradient-via-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.via-yellow-800 {
+  --gradient-via-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.via-yellow-900 {
+  --gradient-via-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.via-green-100 {
+  --gradient-via-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.via-green-200 {
+  --gradient-via-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.via-green-300 {
+  --gradient-via-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.via-green-400 {
+  --gradient-via-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.via-green-500 {
+  --gradient-via-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.via-green-600 {
+  --gradient-via-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.via-green-700 {
+  --gradient-via-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.via-green-800 {
+  --gradient-via-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.via-green-900 {
+  --gradient-via-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.via-teal-100 {
+  --gradient-via-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.via-teal-200 {
+  --gradient-via-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.via-teal-300 {
+  --gradient-via-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.via-teal-400 {
+  --gradient-via-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.via-teal-500 {
+  --gradient-via-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.via-teal-600 {
+  --gradient-via-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.via-teal-700 {
+  --gradient-via-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.via-teal-800 {
+  --gradient-via-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.via-teal-900 {
+  --gradient-via-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.via-blue-100 {
+  --gradient-via-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.via-blue-200 {
+  --gradient-via-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.via-blue-300 {
+  --gradient-via-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.via-blue-400 {
+  --gradient-via-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.via-blue-500 {
+  --gradient-via-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.via-blue-600 {
+  --gradient-via-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.via-blue-700 {
+  --gradient-via-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.via-blue-800 {
+  --gradient-via-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.via-blue-900 {
+  --gradient-via-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.via-indigo-100 {
+  --gradient-via-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.via-indigo-200 {
+  --gradient-via-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.via-indigo-300 {
+  --gradient-via-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.via-indigo-400 {
+  --gradient-via-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.via-indigo-500 {
+  --gradient-via-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.via-indigo-600 {
+  --gradient-via-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.via-indigo-700 {
+  --gradient-via-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.via-indigo-800 {
+  --gradient-via-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.via-indigo-900 {
+  --gradient-via-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.via-purple-100 {
+  --gradient-via-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.via-purple-200 {
+  --gradient-via-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.via-purple-300 {
+  --gradient-via-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.via-purple-400 {
+  --gradient-via-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.via-purple-500 {
+  --gradient-via-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.via-purple-600 {
+  --gradient-via-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.via-purple-700 {
+  --gradient-via-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.via-purple-800 {
+  --gradient-via-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.via-purple-900 {
+  --gradient-via-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.via-pink-100 {
+  --gradient-via-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.via-pink-200 {
+  --gradient-via-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.via-pink-300 {
+  --gradient-via-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.via-pink-400 {
+  --gradient-via-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.via-pink-500 {
+  --gradient-via-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.via-pink-600 {
+  --gradient-via-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.via-pink-700 {
+  --gradient-via-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.via-pink-800 {
+  --gradient-via-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.via-pink-900 {
+  --gradient-via-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.to-transparent {
+  --gradient-to-color: transparent;
+}
+
+.to-current {
+  --gradient-to-color: currentColor;
+}
+
+.to-black {
+  --gradient-to-color: #000;
+}
+
+.to-white {
+  --gradient-to-color: #fff;
+}
+
+.to-gray-100 {
+  --gradient-to-color: #f7fafc;
+}
+
+.to-gray-200 {
+  --gradient-to-color: #edf2f7;
+}
+
+.to-gray-300 {
+  --gradient-to-color: #e2e8f0;
+}
+
+.to-gray-400 {
+  --gradient-to-color: #cbd5e0;
+}
+
+.to-gray-500 {
+  --gradient-to-color: #a0aec0;
+}
+
+.to-gray-600 {
+  --gradient-to-color: #718096;
+}
+
+.to-gray-700 {
+  --gradient-to-color: #4a5568;
+}
+
+.to-gray-800 {
+  --gradient-to-color: #2d3748;
+}
+
+.to-gray-900 {
+  --gradient-to-color: #1a202c;
+}
+
+.to-red-100 {
+  --gradient-to-color: #fff5f5;
+}
+
+.to-red-200 {
+  --gradient-to-color: #fed7d7;
+}
+
+.to-red-300 {
+  --gradient-to-color: #feb2b2;
+}
+
+.to-red-400 {
+  --gradient-to-color: #fc8181;
+}
+
+.to-red-500 {
+  --gradient-to-color: #f56565;
+}
+
+.to-red-600 {
+  --gradient-to-color: #e53e3e;
+}
+
+.to-red-700 {
+  --gradient-to-color: #c53030;
+}
+
+.to-red-800 {
+  --gradient-to-color: #9b2c2c;
+}
+
+.to-red-900 {
+  --gradient-to-color: #742a2a;
+}
+
+.to-orange-100 {
+  --gradient-to-color: #fffaf0;
+}
+
+.to-orange-200 {
+  --gradient-to-color: #feebc8;
+}
+
+.to-orange-300 {
+  --gradient-to-color: #fbd38d;
+}
+
+.to-orange-400 {
+  --gradient-to-color: #f6ad55;
+}
+
+.to-orange-500 {
+  --gradient-to-color: #ed8936;
+}
+
+.to-orange-600 {
+  --gradient-to-color: #dd6b20;
+}
+
+.to-orange-700 {
+  --gradient-to-color: #c05621;
+}
+
+.to-orange-800 {
+  --gradient-to-color: #9c4221;
+}
+
+.to-orange-900 {
+  --gradient-to-color: #7b341e;
+}
+
+.to-yellow-100 {
+  --gradient-to-color: #fffff0;
+}
+
+.to-yellow-200 {
+  --gradient-to-color: #fefcbf;
+}
+
+.to-yellow-300 {
+  --gradient-to-color: #faf089;
+}
+
+.to-yellow-400 {
+  --gradient-to-color: #f6e05e;
+}
+
+.to-yellow-500 {
+  --gradient-to-color: #ecc94b;
+}
+
+.to-yellow-600 {
+  --gradient-to-color: #d69e2e;
+}
+
+.to-yellow-700 {
+  --gradient-to-color: #b7791f;
+}
+
+.to-yellow-800 {
+  --gradient-to-color: #975a16;
+}
+
+.to-yellow-900 {
+  --gradient-to-color: #744210;
+}
+
+.to-green-100 {
+  --gradient-to-color: #f0fff4;
+}
+
+.to-green-200 {
+  --gradient-to-color: #c6f6d5;
+}
+
+.to-green-300 {
+  --gradient-to-color: #9ae6b4;
+}
+
+.to-green-400 {
+  --gradient-to-color: #68d391;
+}
+
+.to-green-500 {
+  --gradient-to-color: #48bb78;
+}
+
+.to-green-600 {
+  --gradient-to-color: #38a169;
+}
+
+.to-green-700 {
+  --gradient-to-color: #2f855a;
+}
+
+.to-green-800 {
+  --gradient-to-color: #276749;
+}
+
+.to-green-900 {
+  --gradient-to-color: #22543d;
+}
+
+.to-teal-100 {
+  --gradient-to-color: #e6fffa;
+}
+
+.to-teal-200 {
+  --gradient-to-color: #b2f5ea;
+}
+
+.to-teal-300 {
+  --gradient-to-color: #81e6d9;
+}
+
+.to-teal-400 {
+  --gradient-to-color: #4fd1c5;
+}
+
+.to-teal-500 {
+  --gradient-to-color: #38b2ac;
+}
+
+.to-teal-600 {
+  --gradient-to-color: #319795;
+}
+
+.to-teal-700 {
+  --gradient-to-color: #2c7a7b;
+}
+
+.to-teal-800 {
+  --gradient-to-color: #285e61;
+}
+
+.to-teal-900 {
+  --gradient-to-color: #234e52;
+}
+
+.to-blue-100 {
+  --gradient-to-color: #ebf8ff;
+}
+
+.to-blue-200 {
+  --gradient-to-color: #bee3f8;
+}
+
+.to-blue-300 {
+  --gradient-to-color: #90cdf4;
+}
+
+.to-blue-400 {
+  --gradient-to-color: #63b3ed;
+}
+
+.to-blue-500 {
+  --gradient-to-color: #4299e1;
+}
+
+.to-blue-600 {
+  --gradient-to-color: #3182ce;
+}
+
+.to-blue-700 {
+  --gradient-to-color: #2b6cb0;
+}
+
+.to-blue-800 {
+  --gradient-to-color: #2c5282;
+}
+
+.to-blue-900 {
+  --gradient-to-color: #2a4365;
+}
+
+.to-indigo-100 {
+  --gradient-to-color: #ebf4ff;
+}
+
+.to-indigo-200 {
+  --gradient-to-color: #c3dafe;
+}
+
+.to-indigo-300 {
+  --gradient-to-color: #a3bffa;
+}
+
+.to-indigo-400 {
+  --gradient-to-color: #7f9cf5;
+}
+
+.to-indigo-500 {
+  --gradient-to-color: #667eea;
+}
+
+.to-indigo-600 {
+  --gradient-to-color: #5a67d8;
+}
+
+.to-indigo-700 {
+  --gradient-to-color: #4c51bf;
+}
+
+.to-indigo-800 {
+  --gradient-to-color: #434190;
+}
+
+.to-indigo-900 {
+  --gradient-to-color: #3c366b;
+}
+
+.to-purple-100 {
+  --gradient-to-color: #faf5ff;
+}
+
+.to-purple-200 {
+  --gradient-to-color: #e9d8fd;
+}
+
+.to-purple-300 {
+  --gradient-to-color: #d6bcfa;
+}
+
+.to-purple-400 {
+  --gradient-to-color: #b794f4;
+}
+
+.to-purple-500 {
+  --gradient-to-color: #9f7aea;
+}
+
+.to-purple-600 {
+  --gradient-to-color: #805ad5;
+}
+
+.to-purple-700 {
+  --gradient-to-color: #6b46c1;
+}
+
+.to-purple-800 {
+  --gradient-to-color: #553c9a;
+}
+
+.to-purple-900 {
+  --gradient-to-color: #44337a;
+}
+
+.to-pink-100 {
+  --gradient-to-color: #fff5f7;
+}
+
+.to-pink-200 {
+  --gradient-to-color: #fed7e2;
+}
+
+.to-pink-300 {
+  --gradient-to-color: #fbb6ce;
+}
+
+.to-pink-400 {
+  --gradient-to-color: #f687b3;
+}
+
+.to-pink-500 {
+  --gradient-to-color: #ed64a6;
+}
+
+.to-pink-600 {
+  --gradient-to-color: #d53f8c;
+}
+
+.to-pink-700 {
+  --gradient-to-color: #b83280;
+}
+
+.to-pink-800 {
+  --gradient-to-color: #97266d;
+}
+
+.to-pink-900 {
+  --gradient-to-color: #702459;
+}
+
+.hover\:from-transparent:hover {
+  --gradient-from-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.hover\:from-current:hover {
+  --gradient-from-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.hover\:from-black:hover {
+  --gradient-from-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.hover\:from-white:hover {
+  --gradient-from-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.hover\:from-gray-100:hover {
+  --gradient-from-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.hover\:from-gray-200:hover {
+  --gradient-from-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.hover\:from-gray-300:hover {
+  --gradient-from-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.hover\:from-gray-400:hover {
+  --gradient-from-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.hover\:from-gray-500:hover {
+  --gradient-from-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.hover\:from-gray-600:hover {
+  --gradient-from-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.hover\:from-gray-700:hover {
+  --gradient-from-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.hover\:from-gray-800:hover {
+  --gradient-from-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.hover\:from-gray-900:hover {
+  --gradient-from-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.hover\:from-red-100:hover {
+  --gradient-from-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.hover\:from-red-200:hover {
+  --gradient-from-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.hover\:from-red-300:hover {
+  --gradient-from-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.hover\:from-red-400:hover {
+  --gradient-from-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.hover\:from-red-500:hover {
+  --gradient-from-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.hover\:from-red-600:hover {
+  --gradient-from-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.hover\:from-red-700:hover {
+  --gradient-from-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.hover\:from-red-800:hover {
+  --gradient-from-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.hover\:from-red-900:hover {
+  --gradient-from-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.hover\:from-orange-100:hover {
+  --gradient-from-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.hover\:from-orange-200:hover {
+  --gradient-from-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.hover\:from-orange-300:hover {
+  --gradient-from-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.hover\:from-orange-400:hover {
+  --gradient-from-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.hover\:from-orange-500:hover {
+  --gradient-from-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.hover\:from-orange-600:hover {
+  --gradient-from-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.hover\:from-orange-700:hover {
+  --gradient-from-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.hover\:from-orange-800:hover {
+  --gradient-from-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.hover\:from-orange-900:hover {
+  --gradient-from-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.hover\:from-yellow-100:hover {
+  --gradient-from-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.hover\:from-yellow-200:hover {
+  --gradient-from-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.hover\:from-yellow-300:hover {
+  --gradient-from-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.hover\:from-yellow-400:hover {
+  --gradient-from-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.hover\:from-yellow-500:hover {
+  --gradient-from-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.hover\:from-yellow-600:hover {
+  --gradient-from-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.hover\:from-yellow-700:hover {
+  --gradient-from-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.hover\:from-yellow-800:hover {
+  --gradient-from-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.hover\:from-yellow-900:hover {
+  --gradient-from-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.hover\:from-green-100:hover {
+  --gradient-from-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.hover\:from-green-200:hover {
+  --gradient-from-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.hover\:from-green-300:hover {
+  --gradient-from-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.hover\:from-green-400:hover {
+  --gradient-from-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.hover\:from-green-500:hover {
+  --gradient-from-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.hover\:from-green-600:hover {
+  --gradient-from-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.hover\:from-green-700:hover {
+  --gradient-from-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.hover\:from-green-800:hover {
+  --gradient-from-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.hover\:from-green-900:hover {
+  --gradient-from-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.hover\:from-teal-100:hover {
+  --gradient-from-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.hover\:from-teal-200:hover {
+  --gradient-from-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.hover\:from-teal-300:hover {
+  --gradient-from-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.hover\:from-teal-400:hover {
+  --gradient-from-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.hover\:from-teal-500:hover {
+  --gradient-from-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.hover\:from-teal-600:hover {
+  --gradient-from-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.hover\:from-teal-700:hover {
+  --gradient-from-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.hover\:from-teal-800:hover {
+  --gradient-from-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.hover\:from-teal-900:hover {
+  --gradient-from-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.hover\:from-blue-100:hover {
+  --gradient-from-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.hover\:from-blue-200:hover {
+  --gradient-from-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.hover\:from-blue-300:hover {
+  --gradient-from-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.hover\:from-blue-400:hover {
+  --gradient-from-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.hover\:from-blue-500:hover {
+  --gradient-from-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.hover\:from-blue-600:hover {
+  --gradient-from-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.hover\:from-blue-700:hover {
+  --gradient-from-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.hover\:from-blue-800:hover {
+  --gradient-from-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.hover\:from-blue-900:hover {
+  --gradient-from-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.hover\:from-indigo-100:hover {
+  --gradient-from-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.hover\:from-indigo-200:hover {
+  --gradient-from-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.hover\:from-indigo-300:hover {
+  --gradient-from-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.hover\:from-indigo-400:hover {
+  --gradient-from-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.hover\:from-indigo-500:hover {
+  --gradient-from-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.hover\:from-indigo-600:hover {
+  --gradient-from-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.hover\:from-indigo-700:hover {
+  --gradient-from-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.hover\:from-indigo-800:hover {
+  --gradient-from-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.hover\:from-indigo-900:hover {
+  --gradient-from-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.hover\:from-purple-100:hover {
+  --gradient-from-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.hover\:from-purple-200:hover {
+  --gradient-from-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.hover\:from-purple-300:hover {
+  --gradient-from-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.hover\:from-purple-400:hover {
+  --gradient-from-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.hover\:from-purple-500:hover {
+  --gradient-from-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.hover\:from-purple-600:hover {
+  --gradient-from-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.hover\:from-purple-700:hover {
+  --gradient-from-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.hover\:from-purple-800:hover {
+  --gradient-from-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.hover\:from-purple-900:hover {
+  --gradient-from-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.hover\:from-pink-100:hover {
+  --gradient-from-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.hover\:from-pink-200:hover {
+  --gradient-from-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.hover\:from-pink-300:hover {
+  --gradient-from-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.hover\:from-pink-400:hover {
+  --gradient-from-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.hover\:from-pink-500:hover {
+  --gradient-from-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.hover\:from-pink-600:hover {
+  --gradient-from-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.hover\:from-pink-700:hover {
+  --gradient-from-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.hover\:from-pink-800:hover {
+  --gradient-from-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.hover\:from-pink-900:hover {
+  --gradient-from-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.hover\:via-transparent:hover {
+  --gradient-via-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.hover\:via-current:hover {
+  --gradient-via-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.hover\:via-black:hover {
+  --gradient-via-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.hover\:via-white:hover {
+  --gradient-via-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.hover\:via-gray-100:hover {
+  --gradient-via-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.hover\:via-gray-200:hover {
+  --gradient-via-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.hover\:via-gray-300:hover {
+  --gradient-via-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.hover\:via-gray-400:hover {
+  --gradient-via-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.hover\:via-gray-500:hover {
+  --gradient-via-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.hover\:via-gray-600:hover {
+  --gradient-via-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.hover\:via-gray-700:hover {
+  --gradient-via-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.hover\:via-gray-800:hover {
+  --gradient-via-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.hover\:via-gray-900:hover {
+  --gradient-via-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.hover\:via-red-100:hover {
+  --gradient-via-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.hover\:via-red-200:hover {
+  --gradient-via-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.hover\:via-red-300:hover {
+  --gradient-via-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.hover\:via-red-400:hover {
+  --gradient-via-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.hover\:via-red-500:hover {
+  --gradient-via-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.hover\:via-red-600:hover {
+  --gradient-via-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.hover\:via-red-700:hover {
+  --gradient-via-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.hover\:via-red-800:hover {
+  --gradient-via-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.hover\:via-red-900:hover {
+  --gradient-via-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.hover\:via-orange-100:hover {
+  --gradient-via-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.hover\:via-orange-200:hover {
+  --gradient-via-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.hover\:via-orange-300:hover {
+  --gradient-via-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.hover\:via-orange-400:hover {
+  --gradient-via-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.hover\:via-orange-500:hover {
+  --gradient-via-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.hover\:via-orange-600:hover {
+  --gradient-via-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.hover\:via-orange-700:hover {
+  --gradient-via-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.hover\:via-orange-800:hover {
+  --gradient-via-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.hover\:via-orange-900:hover {
+  --gradient-via-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.hover\:via-yellow-100:hover {
+  --gradient-via-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.hover\:via-yellow-200:hover {
+  --gradient-via-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.hover\:via-yellow-300:hover {
+  --gradient-via-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.hover\:via-yellow-400:hover {
+  --gradient-via-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.hover\:via-yellow-500:hover {
+  --gradient-via-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.hover\:via-yellow-600:hover {
+  --gradient-via-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.hover\:via-yellow-700:hover {
+  --gradient-via-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.hover\:via-yellow-800:hover {
+  --gradient-via-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.hover\:via-yellow-900:hover {
+  --gradient-via-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.hover\:via-green-100:hover {
+  --gradient-via-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.hover\:via-green-200:hover {
+  --gradient-via-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.hover\:via-green-300:hover {
+  --gradient-via-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.hover\:via-green-400:hover {
+  --gradient-via-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.hover\:via-green-500:hover {
+  --gradient-via-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.hover\:via-green-600:hover {
+  --gradient-via-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.hover\:via-green-700:hover {
+  --gradient-via-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.hover\:via-green-800:hover {
+  --gradient-via-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.hover\:via-green-900:hover {
+  --gradient-via-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.hover\:via-teal-100:hover {
+  --gradient-via-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.hover\:via-teal-200:hover {
+  --gradient-via-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.hover\:via-teal-300:hover {
+  --gradient-via-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.hover\:via-teal-400:hover {
+  --gradient-via-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.hover\:via-teal-500:hover {
+  --gradient-via-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.hover\:via-teal-600:hover {
+  --gradient-via-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.hover\:via-teal-700:hover {
+  --gradient-via-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.hover\:via-teal-800:hover {
+  --gradient-via-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.hover\:via-teal-900:hover {
+  --gradient-via-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.hover\:via-blue-100:hover {
+  --gradient-via-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.hover\:via-blue-200:hover {
+  --gradient-via-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.hover\:via-blue-300:hover {
+  --gradient-via-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.hover\:via-blue-400:hover {
+  --gradient-via-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.hover\:via-blue-500:hover {
+  --gradient-via-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.hover\:via-blue-600:hover {
+  --gradient-via-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.hover\:via-blue-700:hover {
+  --gradient-via-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.hover\:via-blue-800:hover {
+  --gradient-via-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.hover\:via-blue-900:hover {
+  --gradient-via-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.hover\:via-indigo-100:hover {
+  --gradient-via-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.hover\:via-indigo-200:hover {
+  --gradient-via-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.hover\:via-indigo-300:hover {
+  --gradient-via-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.hover\:via-indigo-400:hover {
+  --gradient-via-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.hover\:via-indigo-500:hover {
+  --gradient-via-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.hover\:via-indigo-600:hover {
+  --gradient-via-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.hover\:via-indigo-700:hover {
+  --gradient-via-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.hover\:via-indigo-800:hover {
+  --gradient-via-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.hover\:via-indigo-900:hover {
+  --gradient-via-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.hover\:via-purple-100:hover {
+  --gradient-via-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.hover\:via-purple-200:hover {
+  --gradient-via-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.hover\:via-purple-300:hover {
+  --gradient-via-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.hover\:via-purple-400:hover {
+  --gradient-via-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.hover\:via-purple-500:hover {
+  --gradient-via-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.hover\:via-purple-600:hover {
+  --gradient-via-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.hover\:via-purple-700:hover {
+  --gradient-via-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.hover\:via-purple-800:hover {
+  --gradient-via-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.hover\:via-purple-900:hover {
+  --gradient-via-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.hover\:via-pink-100:hover {
+  --gradient-via-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.hover\:via-pink-200:hover {
+  --gradient-via-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.hover\:via-pink-300:hover {
+  --gradient-via-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.hover\:via-pink-400:hover {
+  --gradient-via-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.hover\:via-pink-500:hover {
+  --gradient-via-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.hover\:via-pink-600:hover {
+  --gradient-via-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.hover\:via-pink-700:hover {
+  --gradient-via-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.hover\:via-pink-800:hover {
+  --gradient-via-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.hover\:via-pink-900:hover {
+  --gradient-via-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.hover\:to-transparent:hover {
+  --gradient-to-color: transparent;
+}
+
+.hover\:to-current:hover {
+  --gradient-to-color: currentColor;
+}
+
+.hover\:to-black:hover {
+  --gradient-to-color: #000;
+}
+
+.hover\:to-white:hover {
+  --gradient-to-color: #fff;
+}
+
+.hover\:to-gray-100:hover {
+  --gradient-to-color: #f7fafc;
+}
+
+.hover\:to-gray-200:hover {
+  --gradient-to-color: #edf2f7;
+}
+
+.hover\:to-gray-300:hover {
+  --gradient-to-color: #e2e8f0;
+}
+
+.hover\:to-gray-400:hover {
+  --gradient-to-color: #cbd5e0;
+}
+
+.hover\:to-gray-500:hover {
+  --gradient-to-color: #a0aec0;
+}
+
+.hover\:to-gray-600:hover {
+  --gradient-to-color: #718096;
+}
+
+.hover\:to-gray-700:hover {
+  --gradient-to-color: #4a5568;
+}
+
+.hover\:to-gray-800:hover {
+  --gradient-to-color: #2d3748;
+}
+
+.hover\:to-gray-900:hover {
+  --gradient-to-color: #1a202c;
+}
+
+.hover\:to-red-100:hover {
+  --gradient-to-color: #fff5f5;
+}
+
+.hover\:to-red-200:hover {
+  --gradient-to-color: #fed7d7;
+}
+
+.hover\:to-red-300:hover {
+  --gradient-to-color: #feb2b2;
+}
+
+.hover\:to-red-400:hover {
+  --gradient-to-color: #fc8181;
+}
+
+.hover\:to-red-500:hover {
+  --gradient-to-color: #f56565;
+}
+
+.hover\:to-red-600:hover {
+  --gradient-to-color: #e53e3e;
+}
+
+.hover\:to-red-700:hover {
+  --gradient-to-color: #c53030;
+}
+
+.hover\:to-red-800:hover {
+  --gradient-to-color: #9b2c2c;
+}
+
+.hover\:to-red-900:hover {
+  --gradient-to-color: #742a2a;
+}
+
+.hover\:to-orange-100:hover {
+  --gradient-to-color: #fffaf0;
+}
+
+.hover\:to-orange-200:hover {
+  --gradient-to-color: #feebc8;
+}
+
+.hover\:to-orange-300:hover {
+  --gradient-to-color: #fbd38d;
+}
+
+.hover\:to-orange-400:hover {
+  --gradient-to-color: #f6ad55;
+}
+
+.hover\:to-orange-500:hover {
+  --gradient-to-color: #ed8936;
+}
+
+.hover\:to-orange-600:hover {
+  --gradient-to-color: #dd6b20;
+}
+
+.hover\:to-orange-700:hover {
+  --gradient-to-color: #c05621;
+}
+
+.hover\:to-orange-800:hover {
+  --gradient-to-color: #9c4221;
+}
+
+.hover\:to-orange-900:hover {
+  --gradient-to-color: #7b341e;
+}
+
+.hover\:to-yellow-100:hover {
+  --gradient-to-color: #fffff0;
+}
+
+.hover\:to-yellow-200:hover {
+  --gradient-to-color: #fefcbf;
+}
+
+.hover\:to-yellow-300:hover {
+  --gradient-to-color: #faf089;
+}
+
+.hover\:to-yellow-400:hover {
+  --gradient-to-color: #f6e05e;
+}
+
+.hover\:to-yellow-500:hover {
+  --gradient-to-color: #ecc94b;
+}
+
+.hover\:to-yellow-600:hover {
+  --gradient-to-color: #d69e2e;
+}
+
+.hover\:to-yellow-700:hover {
+  --gradient-to-color: #b7791f;
+}
+
+.hover\:to-yellow-800:hover {
+  --gradient-to-color: #975a16;
+}
+
+.hover\:to-yellow-900:hover {
+  --gradient-to-color: #744210;
+}
+
+.hover\:to-green-100:hover {
+  --gradient-to-color: #f0fff4;
+}
+
+.hover\:to-green-200:hover {
+  --gradient-to-color: #c6f6d5;
+}
+
+.hover\:to-green-300:hover {
+  --gradient-to-color: #9ae6b4;
+}
+
+.hover\:to-green-400:hover {
+  --gradient-to-color: #68d391;
+}
+
+.hover\:to-green-500:hover {
+  --gradient-to-color: #48bb78;
+}
+
+.hover\:to-green-600:hover {
+  --gradient-to-color: #38a169;
+}
+
+.hover\:to-green-700:hover {
+  --gradient-to-color: #2f855a;
+}
+
+.hover\:to-green-800:hover {
+  --gradient-to-color: #276749;
+}
+
+.hover\:to-green-900:hover {
+  --gradient-to-color: #22543d;
+}
+
+.hover\:to-teal-100:hover {
+  --gradient-to-color: #e6fffa;
+}
+
+.hover\:to-teal-200:hover {
+  --gradient-to-color: #b2f5ea;
+}
+
+.hover\:to-teal-300:hover {
+  --gradient-to-color: #81e6d9;
+}
+
+.hover\:to-teal-400:hover {
+  --gradient-to-color: #4fd1c5;
+}
+
+.hover\:to-teal-500:hover {
+  --gradient-to-color: #38b2ac;
+}
+
+.hover\:to-teal-600:hover {
+  --gradient-to-color: #319795;
+}
+
+.hover\:to-teal-700:hover {
+  --gradient-to-color: #2c7a7b;
+}
+
+.hover\:to-teal-800:hover {
+  --gradient-to-color: #285e61;
+}
+
+.hover\:to-teal-900:hover {
+  --gradient-to-color: #234e52;
+}
+
+.hover\:to-blue-100:hover {
+  --gradient-to-color: #ebf8ff;
+}
+
+.hover\:to-blue-200:hover {
+  --gradient-to-color: #bee3f8;
+}
+
+.hover\:to-blue-300:hover {
+  --gradient-to-color: #90cdf4;
+}
+
+.hover\:to-blue-400:hover {
+  --gradient-to-color: #63b3ed;
+}
+
+.hover\:to-blue-500:hover {
+  --gradient-to-color: #4299e1;
+}
+
+.hover\:to-blue-600:hover {
+  --gradient-to-color: #3182ce;
+}
+
+.hover\:to-blue-700:hover {
+  --gradient-to-color: #2b6cb0;
+}
+
+.hover\:to-blue-800:hover {
+  --gradient-to-color: #2c5282;
+}
+
+.hover\:to-blue-900:hover {
+  --gradient-to-color: #2a4365;
+}
+
+.hover\:to-indigo-100:hover {
+  --gradient-to-color: #ebf4ff;
+}
+
+.hover\:to-indigo-200:hover {
+  --gradient-to-color: #c3dafe;
+}
+
+.hover\:to-indigo-300:hover {
+  --gradient-to-color: #a3bffa;
+}
+
+.hover\:to-indigo-400:hover {
+  --gradient-to-color: #7f9cf5;
+}
+
+.hover\:to-indigo-500:hover {
+  --gradient-to-color: #667eea;
+}
+
+.hover\:to-indigo-600:hover {
+  --gradient-to-color: #5a67d8;
+}
+
+.hover\:to-indigo-700:hover {
+  --gradient-to-color: #4c51bf;
+}
+
+.hover\:to-indigo-800:hover {
+  --gradient-to-color: #434190;
+}
+
+.hover\:to-indigo-900:hover {
+  --gradient-to-color: #3c366b;
+}
+
+.hover\:to-purple-100:hover {
+  --gradient-to-color: #faf5ff;
+}
+
+.hover\:to-purple-200:hover {
+  --gradient-to-color: #e9d8fd;
+}
+
+.hover\:to-purple-300:hover {
+  --gradient-to-color: #d6bcfa;
+}
+
+.hover\:to-purple-400:hover {
+  --gradient-to-color: #b794f4;
+}
+
+.hover\:to-purple-500:hover {
+  --gradient-to-color: #9f7aea;
+}
+
+.hover\:to-purple-600:hover {
+  --gradient-to-color: #805ad5;
+}
+
+.hover\:to-purple-700:hover {
+  --gradient-to-color: #6b46c1;
+}
+
+.hover\:to-purple-800:hover {
+  --gradient-to-color: #553c9a;
+}
+
+.hover\:to-purple-900:hover {
+  --gradient-to-color: #44337a;
+}
+
+.hover\:to-pink-100:hover {
+  --gradient-to-color: #fff5f7;
+}
+
+.hover\:to-pink-200:hover {
+  --gradient-to-color: #fed7e2;
+}
+
+.hover\:to-pink-300:hover {
+  --gradient-to-color: #fbb6ce;
+}
+
+.hover\:to-pink-400:hover {
+  --gradient-to-color: #f687b3;
+}
+
+.hover\:to-pink-500:hover {
+  --gradient-to-color: #ed64a6;
+}
+
+.hover\:to-pink-600:hover {
+  --gradient-to-color: #d53f8c;
+}
+
+.hover\:to-pink-700:hover {
+  --gradient-to-color: #b83280;
+}
+
+.hover\:to-pink-800:hover {
+  --gradient-to-color: #97266d;
+}
+
+.hover\:to-pink-900:hover {
+  --gradient-to-color: #702459;
+}
+
+.focus\:from-transparent:focus {
+  --gradient-from-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.focus\:from-current:focus {
+  --gradient-from-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.focus\:from-black:focus {
+  --gradient-from-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.focus\:from-white:focus {
+  --gradient-from-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.focus\:from-gray-100:focus {
+  --gradient-from-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.focus\:from-gray-200:focus {
+  --gradient-from-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.focus\:from-gray-300:focus {
+  --gradient-from-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.focus\:from-gray-400:focus {
+  --gradient-from-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.focus\:from-gray-500:focus {
+  --gradient-from-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.focus\:from-gray-600:focus {
+  --gradient-from-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.focus\:from-gray-700:focus {
+  --gradient-from-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.focus\:from-gray-800:focus {
+  --gradient-from-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.focus\:from-gray-900:focus {
+  --gradient-from-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.focus\:from-red-100:focus {
+  --gradient-from-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.focus\:from-red-200:focus {
+  --gradient-from-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.focus\:from-red-300:focus {
+  --gradient-from-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.focus\:from-red-400:focus {
+  --gradient-from-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.focus\:from-red-500:focus {
+  --gradient-from-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.focus\:from-red-600:focus {
+  --gradient-from-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.focus\:from-red-700:focus {
+  --gradient-from-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.focus\:from-red-800:focus {
+  --gradient-from-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.focus\:from-red-900:focus {
+  --gradient-from-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.focus\:from-orange-100:focus {
+  --gradient-from-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.focus\:from-orange-200:focus {
+  --gradient-from-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.focus\:from-orange-300:focus {
+  --gradient-from-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.focus\:from-orange-400:focus {
+  --gradient-from-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.focus\:from-orange-500:focus {
+  --gradient-from-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.focus\:from-orange-600:focus {
+  --gradient-from-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.focus\:from-orange-700:focus {
+  --gradient-from-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.focus\:from-orange-800:focus {
+  --gradient-from-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.focus\:from-orange-900:focus {
+  --gradient-from-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.focus\:from-yellow-100:focus {
+  --gradient-from-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.focus\:from-yellow-200:focus {
+  --gradient-from-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.focus\:from-yellow-300:focus {
+  --gradient-from-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.focus\:from-yellow-400:focus {
+  --gradient-from-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.focus\:from-yellow-500:focus {
+  --gradient-from-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.focus\:from-yellow-600:focus {
+  --gradient-from-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.focus\:from-yellow-700:focus {
+  --gradient-from-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.focus\:from-yellow-800:focus {
+  --gradient-from-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.focus\:from-yellow-900:focus {
+  --gradient-from-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.focus\:from-green-100:focus {
+  --gradient-from-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.focus\:from-green-200:focus {
+  --gradient-from-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.focus\:from-green-300:focus {
+  --gradient-from-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.focus\:from-green-400:focus {
+  --gradient-from-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.focus\:from-green-500:focus {
+  --gradient-from-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.focus\:from-green-600:focus {
+  --gradient-from-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.focus\:from-green-700:focus {
+  --gradient-from-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.focus\:from-green-800:focus {
+  --gradient-from-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.focus\:from-green-900:focus {
+  --gradient-from-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.focus\:from-teal-100:focus {
+  --gradient-from-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.focus\:from-teal-200:focus {
+  --gradient-from-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.focus\:from-teal-300:focus {
+  --gradient-from-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.focus\:from-teal-400:focus {
+  --gradient-from-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.focus\:from-teal-500:focus {
+  --gradient-from-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.focus\:from-teal-600:focus {
+  --gradient-from-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.focus\:from-teal-700:focus {
+  --gradient-from-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.focus\:from-teal-800:focus {
+  --gradient-from-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.focus\:from-teal-900:focus {
+  --gradient-from-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.focus\:from-blue-100:focus {
+  --gradient-from-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.focus\:from-blue-200:focus {
+  --gradient-from-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.focus\:from-blue-300:focus {
+  --gradient-from-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.focus\:from-blue-400:focus {
+  --gradient-from-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.focus\:from-blue-500:focus {
+  --gradient-from-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.focus\:from-blue-600:focus {
+  --gradient-from-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.focus\:from-blue-700:focus {
+  --gradient-from-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.focus\:from-blue-800:focus {
+  --gradient-from-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.focus\:from-blue-900:focus {
+  --gradient-from-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.focus\:from-indigo-100:focus {
+  --gradient-from-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.focus\:from-indigo-200:focus {
+  --gradient-from-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.focus\:from-indigo-300:focus {
+  --gradient-from-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.focus\:from-indigo-400:focus {
+  --gradient-from-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.focus\:from-indigo-500:focus {
+  --gradient-from-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.focus\:from-indigo-600:focus {
+  --gradient-from-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.focus\:from-indigo-700:focus {
+  --gradient-from-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.focus\:from-indigo-800:focus {
+  --gradient-from-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.focus\:from-indigo-900:focus {
+  --gradient-from-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.focus\:from-purple-100:focus {
+  --gradient-from-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.focus\:from-purple-200:focus {
+  --gradient-from-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.focus\:from-purple-300:focus {
+  --gradient-from-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.focus\:from-purple-400:focus {
+  --gradient-from-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.focus\:from-purple-500:focus {
+  --gradient-from-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.focus\:from-purple-600:focus {
+  --gradient-from-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.focus\:from-purple-700:focus {
+  --gradient-from-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.focus\:from-purple-800:focus {
+  --gradient-from-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.focus\:from-purple-900:focus {
+  --gradient-from-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.focus\:from-pink-100:focus {
+  --gradient-from-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.focus\:from-pink-200:focus {
+  --gradient-from-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.focus\:from-pink-300:focus {
+  --gradient-from-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.focus\:from-pink-400:focus {
+  --gradient-from-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.focus\:from-pink-500:focus {
+  --gradient-from-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.focus\:from-pink-600:focus {
+  --gradient-from-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.focus\:from-pink-700:focus {
+  --gradient-from-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.focus\:from-pink-800:focus {
+  --gradient-from-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.focus\:from-pink-900:focus {
+  --gradient-from-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.focus\:via-transparent:focus {
+  --gradient-via-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.focus\:via-current:focus {
+  --gradient-via-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.focus\:via-black:focus {
+  --gradient-via-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.focus\:via-white:focus {
+  --gradient-via-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.focus\:via-gray-100:focus {
+  --gradient-via-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.focus\:via-gray-200:focus {
+  --gradient-via-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.focus\:via-gray-300:focus {
+  --gradient-via-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.focus\:via-gray-400:focus {
+  --gradient-via-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.focus\:via-gray-500:focus {
+  --gradient-via-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.focus\:via-gray-600:focus {
+  --gradient-via-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.focus\:via-gray-700:focus {
+  --gradient-via-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.focus\:via-gray-800:focus {
+  --gradient-via-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.focus\:via-gray-900:focus {
+  --gradient-via-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.focus\:via-red-100:focus {
+  --gradient-via-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.focus\:via-red-200:focus {
+  --gradient-via-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.focus\:via-red-300:focus {
+  --gradient-via-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.focus\:via-red-400:focus {
+  --gradient-via-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.focus\:via-red-500:focus {
+  --gradient-via-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.focus\:via-red-600:focus {
+  --gradient-via-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.focus\:via-red-700:focus {
+  --gradient-via-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.focus\:via-red-800:focus {
+  --gradient-via-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.focus\:via-red-900:focus {
+  --gradient-via-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.focus\:via-orange-100:focus {
+  --gradient-via-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.focus\:via-orange-200:focus {
+  --gradient-via-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.focus\:via-orange-300:focus {
+  --gradient-via-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.focus\:via-orange-400:focus {
+  --gradient-via-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.focus\:via-orange-500:focus {
+  --gradient-via-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.focus\:via-orange-600:focus {
+  --gradient-via-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.focus\:via-orange-700:focus {
+  --gradient-via-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.focus\:via-orange-800:focus {
+  --gradient-via-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.focus\:via-orange-900:focus {
+  --gradient-via-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.focus\:via-yellow-100:focus {
+  --gradient-via-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.focus\:via-yellow-200:focus {
+  --gradient-via-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.focus\:via-yellow-300:focus {
+  --gradient-via-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.focus\:via-yellow-400:focus {
+  --gradient-via-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.focus\:via-yellow-500:focus {
+  --gradient-via-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.focus\:via-yellow-600:focus {
+  --gradient-via-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.focus\:via-yellow-700:focus {
+  --gradient-via-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.focus\:via-yellow-800:focus {
+  --gradient-via-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.focus\:via-yellow-900:focus {
+  --gradient-via-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.focus\:via-green-100:focus {
+  --gradient-via-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.focus\:via-green-200:focus {
+  --gradient-via-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.focus\:via-green-300:focus {
+  --gradient-via-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.focus\:via-green-400:focus {
+  --gradient-via-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.focus\:via-green-500:focus {
+  --gradient-via-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.focus\:via-green-600:focus {
+  --gradient-via-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.focus\:via-green-700:focus {
+  --gradient-via-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.focus\:via-green-800:focus {
+  --gradient-via-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.focus\:via-green-900:focus {
+  --gradient-via-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.focus\:via-teal-100:focus {
+  --gradient-via-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.focus\:via-teal-200:focus {
+  --gradient-via-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.focus\:via-teal-300:focus {
+  --gradient-via-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.focus\:via-teal-400:focus {
+  --gradient-via-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.focus\:via-teal-500:focus {
+  --gradient-via-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.focus\:via-teal-600:focus {
+  --gradient-via-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.focus\:via-teal-700:focus {
+  --gradient-via-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.focus\:via-teal-800:focus {
+  --gradient-via-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.focus\:via-teal-900:focus {
+  --gradient-via-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.focus\:via-blue-100:focus {
+  --gradient-via-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.focus\:via-blue-200:focus {
+  --gradient-via-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.focus\:via-blue-300:focus {
+  --gradient-via-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.focus\:via-blue-400:focus {
+  --gradient-via-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.focus\:via-blue-500:focus {
+  --gradient-via-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.focus\:via-blue-600:focus {
+  --gradient-via-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.focus\:via-blue-700:focus {
+  --gradient-via-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.focus\:via-blue-800:focus {
+  --gradient-via-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.focus\:via-blue-900:focus {
+  --gradient-via-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.focus\:via-indigo-100:focus {
+  --gradient-via-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.focus\:via-indigo-200:focus {
+  --gradient-via-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.focus\:via-indigo-300:focus {
+  --gradient-via-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.focus\:via-indigo-400:focus {
+  --gradient-via-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.focus\:via-indigo-500:focus {
+  --gradient-via-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.focus\:via-indigo-600:focus {
+  --gradient-via-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.focus\:via-indigo-700:focus {
+  --gradient-via-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.focus\:via-indigo-800:focus {
+  --gradient-via-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.focus\:via-indigo-900:focus {
+  --gradient-via-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.focus\:via-purple-100:focus {
+  --gradient-via-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.focus\:via-purple-200:focus {
+  --gradient-via-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.focus\:via-purple-300:focus {
+  --gradient-via-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.focus\:via-purple-400:focus {
+  --gradient-via-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.focus\:via-purple-500:focus {
+  --gradient-via-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.focus\:via-purple-600:focus {
+  --gradient-via-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.focus\:via-purple-700:focus {
+  --gradient-via-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.focus\:via-purple-800:focus {
+  --gradient-via-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.focus\:via-purple-900:focus {
+  --gradient-via-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.focus\:via-pink-100:focus {
+  --gradient-via-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.focus\:via-pink-200:focus {
+  --gradient-via-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.focus\:via-pink-300:focus {
+  --gradient-via-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.focus\:via-pink-400:focus {
+  --gradient-via-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.focus\:via-pink-500:focus {
+  --gradient-via-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.focus\:via-pink-600:focus {
+  --gradient-via-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.focus\:via-pink-700:focus {
+  --gradient-via-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.focus\:via-pink-800:focus {
+  --gradient-via-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.focus\:via-pink-900:focus {
+  --gradient-via-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.focus\:to-transparent:focus {
+  --gradient-to-color: transparent;
+}
+
+.focus\:to-current:focus {
+  --gradient-to-color: currentColor;
+}
+
+.focus\:to-black:focus {
+  --gradient-to-color: #000;
+}
+
+.focus\:to-white:focus {
+  --gradient-to-color: #fff;
+}
+
+.focus\:to-gray-100:focus {
+  --gradient-to-color: #f7fafc;
+}
+
+.focus\:to-gray-200:focus {
+  --gradient-to-color: #edf2f7;
+}
+
+.focus\:to-gray-300:focus {
+  --gradient-to-color: #e2e8f0;
+}
+
+.focus\:to-gray-400:focus {
+  --gradient-to-color: #cbd5e0;
+}
+
+.focus\:to-gray-500:focus {
+  --gradient-to-color: #a0aec0;
+}
+
+.focus\:to-gray-600:focus {
+  --gradient-to-color: #718096;
+}
+
+.focus\:to-gray-700:focus {
+  --gradient-to-color: #4a5568;
+}
+
+.focus\:to-gray-800:focus {
+  --gradient-to-color: #2d3748;
+}
+
+.focus\:to-gray-900:focus {
+  --gradient-to-color: #1a202c;
+}
+
+.focus\:to-red-100:focus {
+  --gradient-to-color: #fff5f5;
+}
+
+.focus\:to-red-200:focus {
+  --gradient-to-color: #fed7d7;
+}
+
+.focus\:to-red-300:focus {
+  --gradient-to-color: #feb2b2;
+}
+
+.focus\:to-red-400:focus {
+  --gradient-to-color: #fc8181;
+}
+
+.focus\:to-red-500:focus {
+  --gradient-to-color: #f56565;
+}
+
+.focus\:to-red-600:focus {
+  --gradient-to-color: #e53e3e;
+}
+
+.focus\:to-red-700:focus {
+  --gradient-to-color: #c53030;
+}
+
+.focus\:to-red-800:focus {
+  --gradient-to-color: #9b2c2c;
+}
+
+.focus\:to-red-900:focus {
+  --gradient-to-color: #742a2a;
+}
+
+.focus\:to-orange-100:focus {
+  --gradient-to-color: #fffaf0;
+}
+
+.focus\:to-orange-200:focus {
+  --gradient-to-color: #feebc8;
+}
+
+.focus\:to-orange-300:focus {
+  --gradient-to-color: #fbd38d;
+}
+
+.focus\:to-orange-400:focus {
+  --gradient-to-color: #f6ad55;
+}
+
+.focus\:to-orange-500:focus {
+  --gradient-to-color: #ed8936;
+}
+
+.focus\:to-orange-600:focus {
+  --gradient-to-color: #dd6b20;
+}
+
+.focus\:to-orange-700:focus {
+  --gradient-to-color: #c05621;
+}
+
+.focus\:to-orange-800:focus {
+  --gradient-to-color: #9c4221;
+}
+
+.focus\:to-orange-900:focus {
+  --gradient-to-color: #7b341e;
+}
+
+.focus\:to-yellow-100:focus {
+  --gradient-to-color: #fffff0;
+}
+
+.focus\:to-yellow-200:focus {
+  --gradient-to-color: #fefcbf;
+}
+
+.focus\:to-yellow-300:focus {
+  --gradient-to-color: #faf089;
+}
+
+.focus\:to-yellow-400:focus {
+  --gradient-to-color: #f6e05e;
+}
+
+.focus\:to-yellow-500:focus {
+  --gradient-to-color: #ecc94b;
+}
+
+.focus\:to-yellow-600:focus {
+  --gradient-to-color: #d69e2e;
+}
+
+.focus\:to-yellow-700:focus {
+  --gradient-to-color: #b7791f;
+}
+
+.focus\:to-yellow-800:focus {
+  --gradient-to-color: #975a16;
+}
+
+.focus\:to-yellow-900:focus {
+  --gradient-to-color: #744210;
+}
+
+.focus\:to-green-100:focus {
+  --gradient-to-color: #f0fff4;
+}
+
+.focus\:to-green-200:focus {
+  --gradient-to-color: #c6f6d5;
+}
+
+.focus\:to-green-300:focus {
+  --gradient-to-color: #9ae6b4;
+}
+
+.focus\:to-green-400:focus {
+  --gradient-to-color: #68d391;
+}
+
+.focus\:to-green-500:focus {
+  --gradient-to-color: #48bb78;
+}
+
+.focus\:to-green-600:focus {
+  --gradient-to-color: #38a169;
+}
+
+.focus\:to-green-700:focus {
+  --gradient-to-color: #2f855a;
+}
+
+.focus\:to-green-800:focus {
+  --gradient-to-color: #276749;
+}
+
+.focus\:to-green-900:focus {
+  --gradient-to-color: #22543d;
+}
+
+.focus\:to-teal-100:focus {
+  --gradient-to-color: #e6fffa;
+}
+
+.focus\:to-teal-200:focus {
+  --gradient-to-color: #b2f5ea;
+}
+
+.focus\:to-teal-300:focus {
+  --gradient-to-color: #81e6d9;
+}
+
+.focus\:to-teal-400:focus {
+  --gradient-to-color: #4fd1c5;
+}
+
+.focus\:to-teal-500:focus {
+  --gradient-to-color: #38b2ac;
+}
+
+.focus\:to-teal-600:focus {
+  --gradient-to-color: #319795;
+}
+
+.focus\:to-teal-700:focus {
+  --gradient-to-color: #2c7a7b;
+}
+
+.focus\:to-teal-800:focus {
+  --gradient-to-color: #285e61;
+}
+
+.focus\:to-teal-900:focus {
+  --gradient-to-color: #234e52;
+}
+
+.focus\:to-blue-100:focus {
+  --gradient-to-color: #ebf8ff;
+}
+
+.focus\:to-blue-200:focus {
+  --gradient-to-color: #bee3f8;
+}
+
+.focus\:to-blue-300:focus {
+  --gradient-to-color: #90cdf4;
+}
+
+.focus\:to-blue-400:focus {
+  --gradient-to-color: #63b3ed;
+}
+
+.focus\:to-blue-500:focus {
+  --gradient-to-color: #4299e1;
+}
+
+.focus\:to-blue-600:focus {
+  --gradient-to-color: #3182ce;
+}
+
+.focus\:to-blue-700:focus {
+  --gradient-to-color: #2b6cb0;
+}
+
+.focus\:to-blue-800:focus {
+  --gradient-to-color: #2c5282;
+}
+
+.focus\:to-blue-900:focus {
+  --gradient-to-color: #2a4365;
+}
+
+.focus\:to-indigo-100:focus {
+  --gradient-to-color: #ebf4ff;
+}
+
+.focus\:to-indigo-200:focus {
+  --gradient-to-color: #c3dafe;
+}
+
+.focus\:to-indigo-300:focus {
+  --gradient-to-color: #a3bffa;
+}
+
+.focus\:to-indigo-400:focus {
+  --gradient-to-color: #7f9cf5;
+}
+
+.focus\:to-indigo-500:focus {
+  --gradient-to-color: #667eea;
+}
+
+.focus\:to-indigo-600:focus {
+  --gradient-to-color: #5a67d8;
+}
+
+.focus\:to-indigo-700:focus {
+  --gradient-to-color: #4c51bf;
+}
+
+.focus\:to-indigo-800:focus {
+  --gradient-to-color: #434190;
+}
+
+.focus\:to-indigo-900:focus {
+  --gradient-to-color: #3c366b;
+}
+
+.focus\:to-purple-100:focus {
+  --gradient-to-color: #faf5ff;
+}
+
+.focus\:to-purple-200:focus {
+  --gradient-to-color: #e9d8fd;
+}
+
+.focus\:to-purple-300:focus {
+  --gradient-to-color: #d6bcfa;
+}
+
+.focus\:to-purple-400:focus {
+  --gradient-to-color: #b794f4;
+}
+
+.focus\:to-purple-500:focus {
+  --gradient-to-color: #9f7aea;
+}
+
+.focus\:to-purple-600:focus {
+  --gradient-to-color: #805ad5;
+}
+
+.focus\:to-purple-700:focus {
+  --gradient-to-color: #6b46c1;
+}
+
+.focus\:to-purple-800:focus {
+  --gradient-to-color: #553c9a;
+}
+
+.focus\:to-purple-900:focus {
+  --gradient-to-color: #44337a;
+}
+
+.focus\:to-pink-100:focus {
+  --gradient-to-color: #fff5f7;
+}
+
+.focus\:to-pink-200:focus {
+  --gradient-to-color: #fed7e2;
+}
+
+.focus\:to-pink-300:focus {
+  --gradient-to-color: #fbb6ce;
+}
+
+.focus\:to-pink-400:focus {
+  --gradient-to-color: #f687b3;
+}
+
+.focus\:to-pink-500:focus {
+  --gradient-to-color: #ed64a6;
+}
+
+.focus\:to-pink-600:focus {
+  --gradient-to-color: #d53f8c;
+}
+
+.focus\:to-pink-700:focus {
+  --gradient-to-color: #b83280;
+}
+
+.focus\:to-pink-800:focus {
+  --gradient-to-color: #97266d;
+}
+
+.focus\:to-pink-900:focus {
+  --gradient-to-color: #702459;
+}
+
+.bg-opacity-0 {
+  --bg-opacity: 0;
+}
+
+.bg-opacity-25 {
+  --bg-opacity: 0.25;
+}
+
+.bg-opacity-50 {
+  --bg-opacity: 0.5;
+}
+
+.bg-opacity-75 {
+  --bg-opacity: 0.75;
+}
+
+.bg-opacity-100 {
+  --bg-opacity: 1;
+}
+
+.hover\:bg-opacity-0:hover {
+  --bg-opacity: 0;
+}
+
+.hover\:bg-opacity-25:hover {
+  --bg-opacity: 0.25;
+}
+
+.hover\:bg-opacity-50:hover {
+  --bg-opacity: 0.5;
+}
+
+.hover\:bg-opacity-75:hover {
+  --bg-opacity: 0.75;
+}
+
+.hover\:bg-opacity-100:hover {
+  --bg-opacity: 1;
+}
+
+.focus\:bg-opacity-0:focus {
+  --bg-opacity: 0;
+}
+
+.focus\:bg-opacity-25:focus {
+  --bg-opacity: 0.25;
+}
+
+.focus\:bg-opacity-50:focus {
+  --bg-opacity: 0.5;
+}
+
+.focus\:bg-opacity-75:focus {
+  --bg-opacity: 0.75;
+}
+
+.focus\:bg-opacity-100:focus {
+  --bg-opacity: 1;
+}
+
+.bg-bottom {
+  background-position: bottom;
+}
+
+.bg-center {
+  background-position: center;
+}
+
+.bg-left {
+  background-position: left;
+}
+
+.bg-left-bottom {
+  background-position: left bottom;
+}
+
+.bg-left-top {
+  background-position: left top;
+}
+
+.bg-right {
+  background-position: right;
+}
+
+.bg-right-bottom {
+  background-position: right bottom;
+}
+
+.bg-right-top {
+  background-position: right top;
+}
+
+.bg-top {
+  background-position: top;
+}
+
+.bg-repeat {
+  background-repeat: repeat;
+}
+
+.bg-no-repeat {
+  background-repeat: no-repeat;
+}
+
+.bg-repeat-x {
+  background-repeat: repeat-x;
+}
+
+.bg-repeat-y {
+  background-repeat: repeat-y;
+}
+
+.bg-repeat-round {
+  background-repeat: round;
+}
+
+.bg-repeat-space {
+  background-repeat: space;
+}
+
+.bg-auto {
+  background-size: auto;
+}
+
+.bg-cover {
+  background-size: cover;
+}
+
+.bg-contain {
+  background-size: contain;
+}
+
+.border-collapse {
+  border-collapse: collapse;
+}
+
+.border-separate {
+  border-collapse: separate;
+}
+
+.border-transparent {
+  border-color: transparent;
+}
+
+.border-current {
+  border-color: currentColor;
+}
+
+.border-black {
+  --border-opacity: 1;
+  border-color: #000;
+  border-color: rgba(0, 0, 0, var(--border-opacity));
+}
+
+.border-white {
+  --border-opacity: 1;
+  border-color: #fff;
+  border-color: rgba(255, 255, 255, var(--border-opacity));
+}
+
+.border-gray-100 {
+  --border-opacity: 1;
+  border-color: #f7fafc;
+  border-color: rgba(247, 250, 252, var(--border-opacity));
+}
+
+.border-gray-200 {
+  --border-opacity: 1;
+  border-color: #edf2f7;
+  border-color: rgba(237, 242, 247, var(--border-opacity));
+}
+
+.border-gray-300 {
+  --border-opacity: 1;
+  border-color: #e2e8f0;
+  border-color: rgba(226, 232, 240, var(--border-opacity));
+}
+
+.border-gray-400 {
+  --border-opacity: 1;
+  border-color: #cbd5e0;
+  border-color: rgba(203, 213, 224, var(--border-opacity));
+}
+
+.border-gray-500 {
+  --border-opacity: 1;
+  border-color: #a0aec0;
+  border-color: rgba(160, 174, 192, var(--border-opacity));
+}
+
+.border-gray-600 {
+  --border-opacity: 1;
+  border-color: #718096;
+  border-color: rgba(113, 128, 150, var(--border-opacity));
+}
+
+.border-gray-700 {
+  --border-opacity: 1;
+  border-color: #4a5568;
+  border-color: rgba(74, 85, 104, var(--border-opacity));
+}
+
+.border-gray-800 {
+  --border-opacity: 1;
+  border-color: #2d3748;
+  border-color: rgba(45, 55, 72, var(--border-opacity));
+}
+
+.border-gray-900 {
+  --border-opacity: 1;
+  border-color: #1a202c;
+  border-color: rgba(26, 32, 44, var(--border-opacity));
+}
+
+.border-red-100 {
+  --border-opacity: 1;
+  border-color: #fff5f5;
+  border-color: rgba(255, 245, 245, var(--border-opacity));
+}
+
+.border-red-200 {
+  --border-opacity: 1;
+  border-color: #fed7d7;
+  border-color: rgba(254, 215, 215, var(--border-opacity));
+}
+
+.border-red-300 {
+  --border-opacity: 1;
+  border-color: #feb2b2;
+  border-color: rgba(254, 178, 178, var(--border-opacity));
+}
+
+.border-red-400 {
+  --border-opacity: 1;
+  border-color: #fc8181;
+  border-color: rgba(252, 129, 129, var(--border-opacity));
+}
+
+.border-red-500 {
+  --border-opacity: 1;
+  border-color: #f56565;
+  border-color: rgba(245, 101, 101, var(--border-opacity));
+}
+
+.border-red-600 {
+  --border-opacity: 1;
+  border-color: #e53e3e;
+  border-color: rgba(229, 62, 62, var(--border-opacity));
+}
+
+.border-red-700 {
+  --border-opacity: 1;
+  border-color: #c53030;
+  border-color: rgba(197, 48, 48, var(--border-opacity));
+}
+
+.border-red-800 {
+  --border-opacity: 1;
+  border-color: #9b2c2c;
+  border-color: rgba(155, 44, 44, var(--border-opacity));
+}
+
+.border-red-900 {
+  --border-opacity: 1;
+  border-color: #742a2a;
+  border-color: rgba(116, 42, 42, var(--border-opacity));
+}
+
+.border-orange-100 {
+  --border-opacity: 1;
+  border-color: #fffaf0;
+  border-color: rgba(255, 250, 240, var(--border-opacity));
+}
+
+.border-orange-200 {
+  --border-opacity: 1;
+  border-color: #feebc8;
+  border-color: rgba(254, 235, 200, var(--border-opacity));
+}
+
+.border-orange-300 {
+  --border-opacity: 1;
+  border-color: #fbd38d;
+  border-color: rgba(251, 211, 141, var(--border-opacity));
+}
+
+.border-orange-400 {
+  --border-opacity: 1;
+  border-color: #f6ad55;
+  border-color: rgba(246, 173, 85, var(--border-opacity));
+}
+
+.border-orange-500 {
+  --border-opacity: 1;
+  border-color: #ed8936;
+  border-color: rgba(237, 137, 54, var(--border-opacity));
+}
+
+.border-orange-600 {
+  --border-opacity: 1;
+  border-color: #dd6b20;
+  border-color: rgba(221, 107, 32, var(--border-opacity));
+}
+
+.border-orange-700 {
+  --border-opacity: 1;
+  border-color: #c05621;
+  border-color: rgba(192, 86, 33, var(--border-opacity));
+}
+
+.border-orange-800 {
+  --border-opacity: 1;
+  border-color: #9c4221;
+  border-color: rgba(156, 66, 33, var(--border-opacity));
+}
+
+.border-orange-900 {
+  --border-opacity: 1;
+  border-color: #7b341e;
+  border-color: rgba(123, 52, 30, var(--border-opacity));
+}
+
+.border-yellow-100 {
+  --border-opacity: 1;
+  border-color: #fffff0;
+  border-color: rgba(255, 255, 240, var(--border-opacity));
+}
+
+.border-yellow-200 {
+  --border-opacity: 1;
+  border-color: #fefcbf;
+  border-color: rgba(254, 252, 191, var(--border-opacity));
+}
+
+.border-yellow-300 {
+  --border-opacity: 1;
+  border-color: #faf089;
+  border-color: rgba(250, 240, 137, var(--border-opacity));
+}
+
+.border-yellow-400 {
+  --border-opacity: 1;
+  border-color: #f6e05e;
+  border-color: rgba(246, 224, 94, var(--border-opacity));
+}
+
+.border-yellow-500 {
+  --border-opacity: 1;
+  border-color: #ecc94b;
+  border-color: rgba(236, 201, 75, var(--border-opacity));
+}
+
+.border-yellow-600 {
+  --border-opacity: 1;
+  border-color: #d69e2e;
+  border-color: rgba(214, 158, 46, var(--border-opacity));
+}
+
+.border-yellow-700 {
+  --border-opacity: 1;
+  border-color: #b7791f;
+  border-color: rgba(183, 121, 31, var(--border-opacity));
+}
+
+.border-yellow-800 {
+  --border-opacity: 1;
+  border-color: #975a16;
+  border-color: rgba(151, 90, 22, var(--border-opacity));
+}
+
+.border-yellow-900 {
+  --border-opacity: 1;
+  border-color: #744210;
+  border-color: rgba(116, 66, 16, var(--border-opacity));
+}
+
+.border-green-100 {
+  --border-opacity: 1;
+  border-color: #f0fff4;
+  border-color: rgba(240, 255, 244, var(--border-opacity));
+}
+
+.border-green-200 {
+  --border-opacity: 1;
+  border-color: #c6f6d5;
+  border-color: rgba(198, 246, 213, var(--border-opacity));
+}
+
+.border-green-300 {
+  --border-opacity: 1;
+  border-color: #9ae6b4;
+  border-color: rgba(154, 230, 180, var(--border-opacity));
+}
+
+.border-green-400 {
+  --border-opacity: 1;
+  border-color: #68d391;
+  border-color: rgba(104, 211, 145, var(--border-opacity));
+}
+
+.border-green-500 {
+  --border-opacity: 1;
+  border-color: #48bb78;
+  border-color: rgba(72, 187, 120, var(--border-opacity));
+}
+
+.border-green-600 {
+  --border-opacity: 1;
+  border-color: #38a169;
+  border-color: rgba(56, 161, 105, var(--border-opacity));
+}
+
+.border-green-700 {
+  --border-opacity: 1;
+  border-color: #2f855a;
+  border-color: rgba(47, 133, 90, var(--border-opacity));
+}
+
+.border-green-800 {
+  --border-opacity: 1;
+  border-color: #276749;
+  border-color: rgba(39, 103, 73, var(--border-opacity));
+}
+
+.border-green-900 {
+  --border-opacity: 1;
+  border-color: #22543d;
+  border-color: rgba(34, 84, 61, var(--border-opacity));
+}
+
+.border-teal-100 {
+  --border-opacity: 1;
+  border-color: #e6fffa;
+  border-color: rgba(230, 255, 250, var(--border-opacity));
+}
+
+.border-teal-200 {
+  --border-opacity: 1;
+  border-color: #b2f5ea;
+  border-color: rgba(178, 245, 234, var(--border-opacity));
+}
+
+.border-teal-300 {
+  --border-opacity: 1;
+  border-color: #81e6d9;
+  border-color: rgba(129, 230, 217, var(--border-opacity));
+}
+
+.border-teal-400 {
+  --border-opacity: 1;
+  border-color: #4fd1c5;
+  border-color: rgba(79, 209, 197, var(--border-opacity));
+}
+
+.border-teal-500 {
+  --border-opacity: 1;
+  border-color: #38b2ac;
+  border-color: rgba(56, 178, 172, var(--border-opacity));
+}
+
+.border-teal-600 {
+  --border-opacity: 1;
+  border-color: #319795;
+  border-color: rgba(49, 151, 149, var(--border-opacity));
+}
+
+.border-teal-700 {
+  --border-opacity: 1;
+  border-color: #2c7a7b;
+  border-color: rgba(44, 122, 123, var(--border-opacity));
+}
+
+.border-teal-800 {
+  --border-opacity: 1;
+  border-color: #285e61;
+  border-color: rgba(40, 94, 97, var(--border-opacity));
+}
+
+.border-teal-900 {
+  --border-opacity: 1;
+  border-color: #234e52;
+  border-color: rgba(35, 78, 82, var(--border-opacity));
+}
+
+.border-blue-100 {
+  --border-opacity: 1;
+  border-color: #ebf8ff;
+  border-color: rgba(235, 248, 255, var(--border-opacity));
+}
+
+.border-blue-200 {
+  --border-opacity: 1;
+  border-color: #bee3f8;
+  border-color: rgba(190, 227, 248, var(--border-opacity));
+}
+
+.border-blue-300 {
+  --border-opacity: 1;
+  border-color: #90cdf4;
+  border-color: rgba(144, 205, 244, var(--border-opacity));
+}
+
+.border-blue-400 {
+  --border-opacity: 1;
+  border-color: #63b3ed;
+  border-color: rgba(99, 179, 237, var(--border-opacity));
+}
+
+.border-blue-500 {
+  --border-opacity: 1;
+  border-color: #4299e1;
+  border-color: rgba(66, 153, 225, var(--border-opacity));
+}
+
+.border-blue-600 {
+  --border-opacity: 1;
+  border-color: #3182ce;
+  border-color: rgba(49, 130, 206, var(--border-opacity));
+}
+
+.border-blue-700 {
+  --border-opacity: 1;
+  border-color: #2b6cb0;
+  border-color: rgba(43, 108, 176, var(--border-opacity));
+}
+
+.border-blue-800 {
+  --border-opacity: 1;
+  border-color: #2c5282;
+  border-color: rgba(44, 82, 130, var(--border-opacity));
+}
+
+.border-blue-900 {
+  --border-opacity: 1;
+  border-color: #2a4365;
+  border-color: rgba(42, 67, 101, var(--border-opacity));
+}
+
+.border-indigo-100 {
+  --border-opacity: 1;
+  border-color: #ebf4ff;
+  border-color: rgba(235, 244, 255, var(--border-opacity));
+}
+
+.border-indigo-200 {
+  --border-opacity: 1;
+  border-color: #c3dafe;
+  border-color: rgba(195, 218, 254, var(--border-opacity));
+}
+
+.border-indigo-300 {
+  --border-opacity: 1;
+  border-color: #a3bffa;
+  border-color: rgba(163, 191, 250, var(--border-opacity));
+}
+
+.border-indigo-400 {
+  --border-opacity: 1;
+  border-color: #7f9cf5;
+  border-color: rgba(127, 156, 245, var(--border-opacity));
+}
+
+.border-indigo-500 {
+  --border-opacity: 1;
+  border-color: #667eea;
+  border-color: rgba(102, 126, 234, var(--border-opacity));
+}
+
+.border-indigo-600 {
+  --border-opacity: 1;
+  border-color: #5a67d8;
+  border-color: rgba(90, 103, 216, var(--border-opacity));
+}
+
+.border-indigo-700 {
+  --border-opacity: 1;
+  border-color: #4c51bf;
+  border-color: rgba(76, 81, 191, var(--border-opacity));
+}
+
+.border-indigo-800 {
+  --border-opacity: 1;
+  border-color: #434190;
+  border-color: rgba(67, 65, 144, var(--border-opacity));
+}
+
+.border-indigo-900 {
+  --border-opacity: 1;
+  border-color: #3c366b;
+  border-color: rgba(60, 54, 107, var(--border-opacity));
+}
+
+.border-purple-100 {
+  --border-opacity: 1;
+  border-color: #faf5ff;
+  border-color: rgba(250, 245, 255, var(--border-opacity));
+}
+
+.border-purple-200 {
+  --border-opacity: 1;
+  border-color: #e9d8fd;
+  border-color: rgba(233, 216, 253, var(--border-opacity));
+}
+
+.border-purple-300 {
+  --border-opacity: 1;
+  border-color: #d6bcfa;
+  border-color: rgba(214, 188, 250, var(--border-opacity));
+}
+
+.border-purple-400 {
+  --border-opacity: 1;
+  border-color: #b794f4;
+  border-color: rgba(183, 148, 244, var(--border-opacity));
+}
+
+.border-purple-500 {
+  --border-opacity: 1;
+  border-color: #9f7aea;
+  border-color: rgba(159, 122, 234, var(--border-opacity));
+}
+
+.border-purple-600 {
+  --border-opacity: 1;
+  border-color: #805ad5;
+  border-color: rgba(128, 90, 213, var(--border-opacity));
+}
+
+.border-purple-700 {
+  --border-opacity: 1;
+  border-color: #6b46c1;
+  border-color: rgba(107, 70, 193, var(--border-opacity));
+}
+
+.border-purple-800 {
+  --border-opacity: 1;
+  border-color: #553c9a;
+  border-color: rgba(85, 60, 154, var(--border-opacity));
+}
+
+.border-purple-900 {
+  --border-opacity: 1;
+  border-color: #44337a;
+  border-color: rgba(68, 51, 122, var(--border-opacity));
+}
+
+.border-pink-100 {
+  --border-opacity: 1;
+  border-color: #fff5f7;
+  border-color: rgba(255, 245, 247, var(--border-opacity));
+}
+
+.border-pink-200 {
+  --border-opacity: 1;
+  border-color: #fed7e2;
+  border-color: rgba(254, 215, 226, var(--border-opacity));
+}
+
+.border-pink-300 {
+  --border-opacity: 1;
+  border-color: #fbb6ce;
+  border-color: rgba(251, 182, 206, var(--border-opacity));
+}
+
+.border-pink-400 {
+  --border-opacity: 1;
+  border-color: #f687b3;
+  border-color: rgba(246, 135, 179, var(--border-opacity));
+}
+
+.border-pink-500 {
+  --border-opacity: 1;
+  border-color: #ed64a6;
+  border-color: rgba(237, 100, 166, var(--border-opacity));
+}
+
+.border-pink-600 {
+  --border-opacity: 1;
+  border-color: #d53f8c;
+  border-color: rgba(213, 63, 140, var(--border-opacity));
+}
+
+.border-pink-700 {
+  --border-opacity: 1;
+  border-color: #b83280;
+  border-color: rgba(184, 50, 128, var(--border-opacity));
+}
+
+.border-pink-800 {
+  --border-opacity: 1;
+  border-color: #97266d;
+  border-color: rgba(151, 38, 109, var(--border-opacity));
+}
+
+.border-pink-900 {
+  --border-opacity: 1;
+  border-color: #702459;
+  border-color: rgba(112, 36, 89, var(--border-opacity));
+}
+
+.hover\:border-transparent:hover {
+  border-color: transparent;
+}
+
+.hover\:border-current:hover {
+  border-color: currentColor;
+}
+
+.hover\:border-black:hover {
+  --border-opacity: 1;
+  border-color: #000;
+  border-color: rgba(0, 0, 0, var(--border-opacity));
+}
+
+.hover\:border-white:hover {
+  --border-opacity: 1;
+  border-color: #fff;
+  border-color: rgba(255, 255, 255, var(--border-opacity));
+}
+
+.hover\:border-gray-100:hover {
+  --border-opacity: 1;
+  border-color: #f7fafc;
+  border-color: rgba(247, 250, 252, var(--border-opacity));
+}
+
+.hover\:border-gray-200:hover {
+  --border-opacity: 1;
+  border-color: #edf2f7;
+  border-color: rgba(237, 242, 247, var(--border-opacity));
+}
+
+.hover\:border-gray-300:hover {
+  --border-opacity: 1;
+  border-color: #e2e8f0;
+  border-color: rgba(226, 232, 240, var(--border-opacity));
+}
+
+.hover\:border-gray-400:hover {
+  --border-opacity: 1;
+  border-color: #cbd5e0;
+  border-color: rgba(203, 213, 224, var(--border-opacity));
+}
+
+.hover\:border-gray-500:hover {
+  --border-opacity: 1;
+  border-color: #a0aec0;
+  border-color: rgba(160, 174, 192, var(--border-opacity));
+}
+
+.hover\:border-gray-600:hover {
+  --border-opacity: 1;
+  border-color: #718096;
+  border-color: rgba(113, 128, 150, var(--border-opacity));
+}
+
+.hover\:border-gray-700:hover {
+  --border-opacity: 1;
+  border-color: #4a5568;
+  border-color: rgba(74, 85, 104, var(--border-opacity));
+}
+
+.hover\:border-gray-800:hover {
+  --border-opacity: 1;
+  border-color: #2d3748;
+  border-color: rgba(45, 55, 72, var(--border-opacity));
+}
+
+.hover\:border-gray-900:hover {
+  --border-opacity: 1;
+  border-color: #1a202c;
+  border-color: rgba(26, 32, 44, var(--border-opacity));
+}
+
+.hover\:border-red-100:hover {
+  --border-opacity: 1;
+  border-color: #fff5f5;
+  border-color: rgba(255, 245, 245, var(--border-opacity));
+}
+
+.hover\:border-red-200:hover {
+  --border-opacity: 1;
+  border-color: #fed7d7;
+  border-color: rgba(254, 215, 215, var(--border-opacity));
+}
+
+.hover\:border-red-300:hover {
+  --border-opacity: 1;
+  border-color: #feb2b2;
+  border-color: rgba(254, 178, 178, var(--border-opacity));
+}
+
+.hover\:border-red-400:hover {
+  --border-opacity: 1;
+  border-color: #fc8181;
+  border-color: rgba(252, 129, 129, var(--border-opacity));
+}
+
+.hover\:border-red-500:hover {
+  --border-opacity: 1;
+  border-color: #f56565;
+  border-color: rgba(245, 101, 101, var(--border-opacity));
+}
+
+.hover\:border-red-600:hover {
+  --border-opacity: 1;
+  border-color: #e53e3e;
+  border-color: rgba(229, 62, 62, var(--border-opacity));
+}
+
+.hover\:border-red-700:hover {
+  --border-opacity: 1;
+  border-color: #c53030;
+  border-color: rgba(197, 48, 48, var(--border-opacity));
+}
+
+.hover\:border-red-800:hover {
+  --border-opacity: 1;
+  border-color: #9b2c2c;
+  border-color: rgba(155, 44, 44, var(--border-opacity));
+}
+
+.hover\:border-red-900:hover {
+  --border-opacity: 1;
+  border-color: #742a2a;
+  border-color: rgba(116, 42, 42, var(--border-opacity));
+}
+
+.hover\:border-orange-100:hover {
+  --border-opacity: 1;
+  border-color: #fffaf0;
+  border-color: rgba(255, 250, 240, var(--border-opacity));
+}
+
+.hover\:border-orange-200:hover {
+  --border-opacity: 1;
+  border-color: #feebc8;
+  border-color: rgba(254, 235, 200, var(--border-opacity));
+}
+
+.hover\:border-orange-300:hover {
+  --border-opacity: 1;
+  border-color: #fbd38d;
+  border-color: rgba(251, 211, 141, var(--border-opacity));
+}
+
+.hover\:border-orange-400:hover {
+  --border-opacity: 1;
+  border-color: #f6ad55;
+  border-color: rgba(246, 173, 85, var(--border-opacity));
+}
+
+.hover\:border-orange-500:hover {
+  --border-opacity: 1;
+  border-color: #ed8936;
+  border-color: rgba(237, 137, 54, var(--border-opacity));
+}
+
+.hover\:border-orange-600:hover {
+  --border-opacity: 1;
+  border-color: #dd6b20;
+  border-color: rgba(221, 107, 32, var(--border-opacity));
+}
+
+.hover\:border-orange-700:hover {
+  --border-opacity: 1;
+  border-color: #c05621;
+  border-color: rgba(192, 86, 33, var(--border-opacity));
+}
+
+.hover\:border-orange-800:hover {
+  --border-opacity: 1;
+  border-color: #9c4221;
+  border-color: rgba(156, 66, 33, var(--border-opacity));
+}
+
+.hover\:border-orange-900:hover {
+  --border-opacity: 1;
+  border-color: #7b341e;
+  border-color: rgba(123, 52, 30, var(--border-opacity));
+}
+
+.hover\:border-yellow-100:hover {
+  --border-opacity: 1;
+  border-color: #fffff0;
+  border-color: rgba(255, 255, 240, var(--border-opacity));
+}
+
+.hover\:border-yellow-200:hover {
+  --border-opacity: 1;
+  border-color: #fefcbf;
+  border-color: rgba(254, 252, 191, var(--border-opacity));
+}
+
+.hover\:border-yellow-300:hover {
+  --border-opacity: 1;
+  border-color: #faf089;
+  border-color: rgba(250, 240, 137, var(--border-opacity));
+}
+
+.hover\:border-yellow-400:hover {
+  --border-opacity: 1;
+  border-color: #f6e05e;
+  border-color: rgba(246, 224, 94, var(--border-opacity));
+}
+
+.hover\:border-yellow-500:hover {
+  --border-opacity: 1;
+  border-color: #ecc94b;
+  border-color: rgba(236, 201, 75, var(--border-opacity));
+}
+
+.hover\:border-yellow-600:hover {
+  --border-opacity: 1;
+  border-color: #d69e2e;
+  border-color: rgba(214, 158, 46, var(--border-opacity));
+}
+
+.hover\:border-yellow-700:hover {
+  --border-opacity: 1;
+  border-color: #b7791f;
+  border-color: rgba(183, 121, 31, var(--border-opacity));
+}
+
+.hover\:border-yellow-800:hover {
+  --border-opacity: 1;
+  border-color: #975a16;
+  border-color: rgba(151, 90, 22, var(--border-opacity));
+}
+
+.hover\:border-yellow-900:hover {
+  --border-opacity: 1;
+  border-color: #744210;
+  border-color: rgba(116, 66, 16, var(--border-opacity));
+}
+
+.hover\:border-green-100:hover {
+  --border-opacity: 1;
+  border-color: #f0fff4;
+  border-color: rgba(240, 255, 244, var(--border-opacity));
+}
+
+.hover\:border-green-200:hover {
+  --border-opacity: 1;
+  border-color: #c6f6d5;
+  border-color: rgba(198, 246, 213, var(--border-opacity));
+}
+
+.hover\:border-green-300:hover {
+  --border-opacity: 1;
+  border-color: #9ae6b4;
+  border-color: rgba(154, 230, 180, var(--border-opacity));
+}
+
+.hover\:border-green-400:hover {
+  --border-opacity: 1;
+  border-color: #68d391;
+  border-color: rgba(104, 211, 145, var(--border-opacity));
+}
+
+.hover\:border-green-500:hover {
+  --border-opacity: 1;
+  border-color: #48bb78;
+  border-color: rgba(72, 187, 120, var(--border-opacity));
+}
+
+.hover\:border-green-600:hover {
+  --border-opacity: 1;
+  border-color: #38a169;
+  border-color: rgba(56, 161, 105, var(--border-opacity));
+}
+
+.hover\:border-green-700:hover {
+  --border-opacity: 1;
+  border-color: #2f855a;
+  border-color: rgba(47, 133, 90, var(--border-opacity));
+}
+
+.hover\:border-green-800:hover {
+  --border-opacity: 1;
+  border-color: #276749;
+  border-color: rgba(39, 103, 73, var(--border-opacity));
+}
+
+.hover\:border-green-900:hover {
+  --border-opacity: 1;
+  border-color: #22543d;
+  border-color: rgba(34, 84, 61, var(--border-opacity));
+}
+
+.hover\:border-teal-100:hover {
+  --border-opacity: 1;
+  border-color: #e6fffa;
+  border-color: rgba(230, 255, 250, var(--border-opacity));
+}
+
+.hover\:border-teal-200:hover {
+  --border-opacity: 1;
+  border-color: #b2f5ea;
+  border-color: rgba(178, 245, 234, var(--border-opacity));
+}
+
+.hover\:border-teal-300:hover {
+  --border-opacity: 1;
+  border-color: #81e6d9;
+  border-color: rgba(129, 230, 217, var(--border-opacity));
+}
+
+.hover\:border-teal-400:hover {
+  --border-opacity: 1;
+  border-color: #4fd1c5;
+  border-color: rgba(79, 209, 197, var(--border-opacity));
+}
+
+.hover\:border-teal-500:hover {
+  --border-opacity: 1;
+  border-color: #38b2ac;
+  border-color: rgba(56, 178, 172, var(--border-opacity));
+}
+
+.hover\:border-teal-600:hover {
+  --border-opacity: 1;
+  border-color: #319795;
+  border-color: rgba(49, 151, 149, var(--border-opacity));
+}
+
+.hover\:border-teal-700:hover {
+  --border-opacity: 1;
+  border-color: #2c7a7b;
+  border-color: rgba(44, 122, 123, var(--border-opacity));
+}
+
+.hover\:border-teal-800:hover {
+  --border-opacity: 1;
+  border-color: #285e61;
+  border-color: rgba(40, 94, 97, var(--border-opacity));
+}
+
+.hover\:border-teal-900:hover {
+  --border-opacity: 1;
+  border-color: #234e52;
+  border-color: rgba(35, 78, 82, var(--border-opacity));
+}
+
+.hover\:border-blue-100:hover {
+  --border-opacity: 1;
+  border-color: #ebf8ff;
+  border-color: rgba(235, 248, 255, var(--border-opacity));
+}
+
+.hover\:border-blue-200:hover {
+  --border-opacity: 1;
+  border-color: #bee3f8;
+  border-color: rgba(190, 227, 248, var(--border-opacity));
+}
+
+.hover\:border-blue-300:hover {
+  --border-opacity: 1;
+  border-color: #90cdf4;
+  border-color: rgba(144, 205, 244, var(--border-opacity));
+}
+
+.hover\:border-blue-400:hover {
+  --border-opacity: 1;
+  border-color: #63b3ed;
+  border-color: rgba(99, 179, 237, var(--border-opacity));
+}
+
+.hover\:border-blue-500:hover {
+  --border-opacity: 1;
+  border-color: #4299e1;
+  border-color: rgba(66, 153, 225, var(--border-opacity));
+}
+
+.hover\:border-blue-600:hover {
+  --border-opacity: 1;
+  border-color: #3182ce;
+  border-color: rgba(49, 130, 206, var(--border-opacity));
+}
+
+.hover\:border-blue-700:hover {
+  --border-opacity: 1;
+  border-color: #2b6cb0;
+  border-color: rgba(43, 108, 176, var(--border-opacity));
+}
+
+.hover\:border-blue-800:hover {
+  --border-opacity: 1;
+  border-color: #2c5282;
+  border-color: rgba(44, 82, 130, var(--border-opacity));
+}
+
+.hover\:border-blue-900:hover {
+  --border-opacity: 1;
+  border-color: #2a4365;
+  border-color: rgba(42, 67, 101, var(--border-opacity));
+}
+
+.hover\:border-indigo-100:hover {
+  --border-opacity: 1;
+  border-color: #ebf4ff;
+  border-color: rgba(235, 244, 255, var(--border-opacity));
+}
+
+.hover\:border-indigo-200:hover {
+  --border-opacity: 1;
+  border-color: #c3dafe;
+  border-color: rgba(195, 218, 254, var(--border-opacity));
+}
+
+.hover\:border-indigo-300:hover {
+  --border-opacity: 1;
+  border-color: #a3bffa;
+  border-color: rgba(163, 191, 250, var(--border-opacity));
+}
+
+.hover\:border-indigo-400:hover {
+  --border-opacity: 1;
+  border-color: #7f9cf5;
+  border-color: rgba(127, 156, 245, var(--border-opacity));
+}
+
+.hover\:border-indigo-500:hover {
+  --border-opacity: 1;
+  border-color: #667eea;
+  border-color: rgba(102, 126, 234, var(--border-opacity));
+}
+
+.hover\:border-indigo-600:hover {
+  --border-opacity: 1;
+  border-color: #5a67d8;
+  border-color: rgba(90, 103, 216, var(--border-opacity));
+}
+
+.hover\:border-indigo-700:hover {
+  --border-opacity: 1;
+  border-color: #4c51bf;
+  border-color: rgba(76, 81, 191, var(--border-opacity));
+}
+
+.hover\:border-indigo-800:hover {
+  --border-opacity: 1;
+  border-color: #434190;
+  border-color: rgba(67, 65, 144, var(--border-opacity));
+}
+
+.hover\:border-indigo-900:hover {
+  --border-opacity: 1;
+  border-color: #3c366b;
+  border-color: rgba(60, 54, 107, var(--border-opacity));
+}
+
+.hover\:border-purple-100:hover {
+  --border-opacity: 1;
+  border-color: #faf5ff;
+  border-color: rgba(250, 245, 255, var(--border-opacity));
+}
+
+.hover\:border-purple-200:hover {
+  --border-opacity: 1;
+  border-color: #e9d8fd;
+  border-color: rgba(233, 216, 253, var(--border-opacity));
+}
+
+.hover\:border-purple-300:hover {
+  --border-opacity: 1;
+  border-color: #d6bcfa;
+  border-color: rgba(214, 188, 250, var(--border-opacity));
+}
+
+.hover\:border-purple-400:hover {
+  --border-opacity: 1;
+  border-color: #b794f4;
+  border-color: rgba(183, 148, 244, var(--border-opacity));
+}
+
+.hover\:border-purple-500:hover {
+  --border-opacity: 1;
+  border-color: #9f7aea;
+  border-color: rgba(159, 122, 234, var(--border-opacity));
+}
+
+.hover\:border-purple-600:hover {
+  --border-opacity: 1;
+  border-color: #805ad5;
+  border-color: rgba(128, 90, 213, var(--border-opacity));
+}
+
+.hover\:border-purple-700:hover {
+  --border-opacity: 1;
+  border-color: #6b46c1;
+  border-color: rgba(107, 70, 193, var(--border-opacity));
+}
+
+.hover\:border-purple-800:hover {
+  --border-opacity: 1;
+  border-color: #553c9a;
+  border-color: rgba(85, 60, 154, var(--border-opacity));
+}
+
+.hover\:border-purple-900:hover {
+  --border-opacity: 1;
+  border-color: #44337a;
+  border-color: rgba(68, 51, 122, var(--border-opacity));
+}
+
+.hover\:border-pink-100:hover {
+  --border-opacity: 1;
+  border-color: #fff5f7;
+  border-color: rgba(255, 245, 247, var(--border-opacity));
+}
+
+.hover\:border-pink-200:hover {
+  --border-opacity: 1;
+  border-color: #fed7e2;
+  border-color: rgba(254, 215, 226, var(--border-opacity));
+}
+
+.hover\:border-pink-300:hover {
+  --border-opacity: 1;
+  border-color: #fbb6ce;
+  border-color: rgba(251, 182, 206, var(--border-opacity));
+}
+
+.hover\:border-pink-400:hover {
+  --border-opacity: 1;
+  border-color: #f687b3;
+  border-color: rgba(246, 135, 179, var(--border-opacity));
+}
+
+.hover\:border-pink-500:hover {
+  --border-opacity: 1;
+  border-color: #ed64a6;
+  border-color: rgba(237, 100, 166, var(--border-opacity));
+}
+
+.hover\:border-pink-600:hover {
+  --border-opacity: 1;
+  border-color: #d53f8c;
+  border-color: rgba(213, 63, 140, var(--border-opacity));
+}
+
+.hover\:border-pink-700:hover {
+  --border-opacity: 1;
+  border-color: #b83280;
+  border-color: rgba(184, 50, 128, var(--border-opacity));
+}
+
+.hover\:border-pink-800:hover {
+  --border-opacity: 1;
+  border-color: #97266d;
+  border-color: rgba(151, 38, 109, var(--border-opacity));
+}
+
+.hover\:border-pink-900:hover {
+  --border-opacity: 1;
+  border-color: #702459;
+  border-color: rgba(112, 36, 89, var(--border-opacity));
+}
+
+.focus\:border-transparent:focus {
+  border-color: transparent;
+}
+
+.focus\:border-current:focus {
+  border-color: currentColor;
+}
+
+.focus\:border-black:focus {
+  --border-opacity: 1;
+  border-color: #000;
+  border-color: rgba(0, 0, 0, var(--border-opacity));
+}
+
+.focus\:border-white:focus {
+  --border-opacity: 1;
+  border-color: #fff;
+  border-color: rgba(255, 255, 255, var(--border-opacity));
+}
+
+.focus\:border-gray-100:focus {
+  --border-opacity: 1;
+  border-color: #f7fafc;
+  border-color: rgba(247, 250, 252, var(--border-opacity));
+}
+
+.focus\:border-gray-200:focus {
+  --border-opacity: 1;
+  border-color: #edf2f7;
+  border-color: rgba(237, 242, 247, var(--border-opacity));
+}
+
+.focus\:border-gray-300:focus {
+  --border-opacity: 1;
+  border-color: #e2e8f0;
+  border-color: rgba(226, 232, 240, var(--border-opacity));
+}
+
+.focus\:border-gray-400:focus {
+  --border-opacity: 1;
+  border-color: #cbd5e0;
+  border-color: rgba(203, 213, 224, var(--border-opacity));
+}
+
+.focus\:border-gray-500:focus {
+  --border-opacity: 1;
+  border-color: #a0aec0;
+  border-color: rgba(160, 174, 192, var(--border-opacity));
+}
+
+.focus\:border-gray-600:focus {
+  --border-opacity: 1;
+  border-color: #718096;
+  border-color: rgba(113, 128, 150, var(--border-opacity));
+}
+
+.focus\:border-gray-700:focus {
+  --border-opacity: 1;
+  border-color: #4a5568;
+  border-color: rgba(74, 85, 104, var(--border-opacity));
+}
+
+.focus\:border-gray-800:focus {
+  --border-opacity: 1;
+  border-color: #2d3748;
+  border-color: rgba(45, 55, 72, var(--border-opacity));
+}
+
+.focus\:border-gray-900:focus {
+  --border-opacity: 1;
+  border-color: #1a202c;
+  border-color: rgba(26, 32, 44, var(--border-opacity));
+}
+
+.focus\:border-red-100:focus {
+  --border-opacity: 1;
+  border-color: #fff5f5;
+  border-color: rgba(255, 245, 245, var(--border-opacity));
+}
+
+.focus\:border-red-200:focus {
+  --border-opacity: 1;
+  border-color: #fed7d7;
+  border-color: rgba(254, 215, 215, var(--border-opacity));
+}
+
+.focus\:border-red-300:focus {
+  --border-opacity: 1;
+  border-color: #feb2b2;
+  border-color: rgba(254, 178, 178, var(--border-opacity));
+}
+
+.focus\:border-red-400:focus {
+  --border-opacity: 1;
+  border-color: #fc8181;
+  border-color: rgba(252, 129, 129, var(--border-opacity));
+}
+
+.focus\:border-red-500:focus {
+  --border-opacity: 1;
+  border-color: #f56565;
+  border-color: rgba(245, 101, 101, var(--border-opacity));
+}
+
+.focus\:border-red-600:focus {
+  --border-opacity: 1;
+  border-color: #e53e3e;
+  border-color: rgba(229, 62, 62, var(--border-opacity));
+}
+
+.focus\:border-red-700:focus {
+  --border-opacity: 1;
+  border-color: #c53030;
+  border-color: rgba(197, 48, 48, var(--border-opacity));
+}
+
+.focus\:border-red-800:focus {
+  --border-opacity: 1;
+  border-color: #9b2c2c;
+  border-color: rgba(155, 44, 44, var(--border-opacity));
+}
+
+.focus\:border-red-900:focus {
+  --border-opacity: 1;
+  border-color: #742a2a;
+  border-color: rgba(116, 42, 42, var(--border-opacity));
+}
+
+.focus\:border-orange-100:focus {
+  --border-opacity: 1;
+  border-color: #fffaf0;
+  border-color: rgba(255, 250, 240, var(--border-opacity));
+}
+
+.focus\:border-orange-200:focus {
+  --border-opacity: 1;
+  border-color: #feebc8;
+  border-color: rgba(254, 235, 200, var(--border-opacity));
+}
+
+.focus\:border-orange-300:focus {
+  --border-opacity: 1;
+  border-color: #fbd38d;
+  border-color: rgba(251, 211, 141, var(--border-opacity));
+}
+
+.focus\:border-orange-400:focus {
+  --border-opacity: 1;
+  border-color: #f6ad55;
+  border-color: rgba(246, 173, 85, var(--border-opacity));
+}
+
+.focus\:border-orange-500:focus {
+  --border-opacity: 1;
+  border-color: #ed8936;
+  border-color: rgba(237, 137, 54, var(--border-opacity));
+}
+
+.focus\:border-orange-600:focus {
+  --border-opacity: 1;
+  border-color: #dd6b20;
+  border-color: rgba(221, 107, 32, var(--border-opacity));
+}
+
+.focus\:border-orange-700:focus {
+  --border-opacity: 1;
+  border-color: #c05621;
+  border-color: rgba(192, 86, 33, var(--border-opacity));
+}
+
+.focus\:border-orange-800:focus {
+  --border-opacity: 1;
+  border-color: #9c4221;
+  border-color: rgba(156, 66, 33, var(--border-opacity));
+}
+
+.focus\:border-orange-900:focus {
+  --border-opacity: 1;
+  border-color: #7b341e;
+  border-color: rgba(123, 52, 30, var(--border-opacity));
+}
+
+.focus\:border-yellow-100:focus {
+  --border-opacity: 1;
+  border-color: #fffff0;
+  border-color: rgba(255, 255, 240, var(--border-opacity));
+}
+
+.focus\:border-yellow-200:focus {
+  --border-opacity: 1;
+  border-color: #fefcbf;
+  border-color: rgba(254, 252, 191, var(--border-opacity));
+}
+
+.focus\:border-yellow-300:focus {
+  --border-opacity: 1;
+  border-color: #faf089;
+  border-color: rgba(250, 240, 137, var(--border-opacity));
+}
+
+.focus\:border-yellow-400:focus {
+  --border-opacity: 1;
+  border-color: #f6e05e;
+  border-color: rgba(246, 224, 94, var(--border-opacity));
+}
+
+.focus\:border-yellow-500:focus {
+  --border-opacity: 1;
+  border-color: #ecc94b;
+  border-color: rgba(236, 201, 75, var(--border-opacity));
+}
+
+.focus\:border-yellow-600:focus {
+  --border-opacity: 1;
+  border-color: #d69e2e;
+  border-color: rgba(214, 158, 46, var(--border-opacity));
+}
+
+.focus\:border-yellow-700:focus {
+  --border-opacity: 1;
+  border-color: #b7791f;
+  border-color: rgba(183, 121, 31, var(--border-opacity));
+}
+
+.focus\:border-yellow-800:focus {
+  --border-opacity: 1;
+  border-color: #975a16;
+  border-color: rgba(151, 90, 22, var(--border-opacity));
+}
+
+.focus\:border-yellow-900:focus {
+  --border-opacity: 1;
+  border-color: #744210;
+  border-color: rgba(116, 66, 16, var(--border-opacity));
+}
+
+.focus\:border-green-100:focus {
+  --border-opacity: 1;
+  border-color: #f0fff4;
+  border-color: rgba(240, 255, 244, var(--border-opacity));
+}
+
+.focus\:border-green-200:focus {
+  --border-opacity: 1;
+  border-color: #c6f6d5;
+  border-color: rgba(198, 246, 213, var(--border-opacity));
+}
+
+.focus\:border-green-300:focus {
+  --border-opacity: 1;
+  border-color: #9ae6b4;
+  border-color: rgba(154, 230, 180, var(--border-opacity));
+}
+
+.focus\:border-green-400:focus {
+  --border-opacity: 1;
+  border-color: #68d391;
+  border-color: rgba(104, 211, 145, var(--border-opacity));
+}
+
+.focus\:border-green-500:focus {
+  --border-opacity: 1;
+  border-color: #48bb78;
+  border-color: rgba(72, 187, 120, var(--border-opacity));
+}
+
+.focus\:border-green-600:focus {
+  --border-opacity: 1;
+  border-color: #38a169;
+  border-color: rgba(56, 161, 105, var(--border-opacity));
+}
+
+.focus\:border-green-700:focus {
+  --border-opacity: 1;
+  border-color: #2f855a;
+  border-color: rgba(47, 133, 90, var(--border-opacity));
+}
+
+.focus\:border-green-800:focus {
+  --border-opacity: 1;
+  border-color: #276749;
+  border-color: rgba(39, 103, 73, var(--border-opacity));
+}
+
+.focus\:border-green-900:focus {
+  --border-opacity: 1;
+  border-color: #22543d;
+  border-color: rgba(34, 84, 61, var(--border-opacity));
+}
+
+.focus\:border-teal-100:focus {
+  --border-opacity: 1;
+  border-color: #e6fffa;
+  border-color: rgba(230, 255, 250, var(--border-opacity));
+}
+
+.focus\:border-teal-200:focus {
+  --border-opacity: 1;
+  border-color: #b2f5ea;
+  border-color: rgba(178, 245, 234, var(--border-opacity));
+}
+
+.focus\:border-teal-300:focus {
+  --border-opacity: 1;
+  border-color: #81e6d9;
+  border-color: rgba(129, 230, 217, var(--border-opacity));
+}
+
+.focus\:border-teal-400:focus {
+  --border-opacity: 1;
+  border-color: #4fd1c5;
+  border-color: rgba(79, 209, 197, var(--border-opacity));
+}
+
+.focus\:border-teal-500:focus {
+  --border-opacity: 1;
+  border-color: #38b2ac;
+  border-color: rgba(56, 178, 172, var(--border-opacity));
+}
+
+.focus\:border-teal-600:focus {
+  --border-opacity: 1;
+  border-color: #319795;
+  border-color: rgba(49, 151, 149, var(--border-opacity));
+}
+
+.focus\:border-teal-700:focus {
+  --border-opacity: 1;
+  border-color: #2c7a7b;
+  border-color: rgba(44, 122, 123, var(--border-opacity));
+}
+
+.focus\:border-teal-800:focus {
+  --border-opacity: 1;
+  border-color: #285e61;
+  border-color: rgba(40, 94, 97, var(--border-opacity));
+}
+
+.focus\:border-teal-900:focus {
+  --border-opacity: 1;
+  border-color: #234e52;
+  border-color: rgba(35, 78, 82, var(--border-opacity));
+}
+
+.focus\:border-blue-100:focus {
+  --border-opacity: 1;
+  border-color: #ebf8ff;
+  border-color: rgba(235, 248, 255, var(--border-opacity));
+}
+
+.focus\:border-blue-200:focus {
+  --border-opacity: 1;
+  border-color: #bee3f8;
+  border-color: rgba(190, 227, 248, var(--border-opacity));
+}
+
+.focus\:border-blue-300:focus {
+  --border-opacity: 1;
+  border-color: #90cdf4;
+  border-color: rgba(144, 205, 244, var(--border-opacity));
+}
+
+.focus\:border-blue-400:focus {
+  --border-opacity: 1;
+  border-color: #63b3ed;
+  border-color: rgba(99, 179, 237, var(--border-opacity));
+}
+
+.focus\:border-blue-500:focus {
+  --border-opacity: 1;
+  border-color: #4299e1;
+  border-color: rgba(66, 153, 225, var(--border-opacity));
+}
+
+.focus\:border-blue-600:focus {
+  --border-opacity: 1;
+  border-color: #3182ce;
+  border-color: rgba(49, 130, 206, var(--border-opacity));
+}
+
+.focus\:border-blue-700:focus {
+  --border-opacity: 1;
+  border-color: #2b6cb0;
+  border-color: rgba(43, 108, 176, var(--border-opacity));
+}
+
+.focus\:border-blue-800:focus {
+  --border-opacity: 1;
+  border-color: #2c5282;
+  border-color: rgba(44, 82, 130, var(--border-opacity));
+}
+
+.focus\:border-blue-900:focus {
+  --border-opacity: 1;
+  border-color: #2a4365;
+  border-color: rgba(42, 67, 101, var(--border-opacity));
+}
+
+.focus\:border-indigo-100:focus {
+  --border-opacity: 1;
+  border-color: #ebf4ff;
+  border-color: rgba(235, 244, 255, var(--border-opacity));
+}
+
+.focus\:border-indigo-200:focus {
+  --border-opacity: 1;
+  border-color: #c3dafe;
+  border-color: rgba(195, 218, 254, var(--border-opacity));
+}
+
+.focus\:border-indigo-300:focus {
+  --border-opacity: 1;
+  border-color: #a3bffa;
+  border-color: rgba(163, 191, 250, var(--border-opacity));
+}
+
+.focus\:border-indigo-400:focus {
+  --border-opacity: 1;
+  border-color: #7f9cf5;
+  border-color: rgba(127, 156, 245, var(--border-opacity));
+}
+
+.focus\:border-indigo-500:focus {
+  --border-opacity: 1;
+  border-color: #667eea;
+  border-color: rgba(102, 126, 234, var(--border-opacity));
+}
+
+.focus\:border-indigo-600:focus {
+  --border-opacity: 1;
+  border-color: #5a67d8;
+  border-color: rgba(90, 103, 216, var(--border-opacity));
+}
+
+.focus\:border-indigo-700:focus {
+  --border-opacity: 1;
+  border-color: #4c51bf;
+  border-color: rgba(76, 81, 191, var(--border-opacity));
+}
+
+.focus\:border-indigo-800:focus {
+  --border-opacity: 1;
+  border-color: #434190;
+  border-color: rgba(67, 65, 144, var(--border-opacity));
+}
+
+.focus\:border-indigo-900:focus {
+  --border-opacity: 1;
+  border-color: #3c366b;
+  border-color: rgba(60, 54, 107, var(--border-opacity));
+}
+
+.focus\:border-purple-100:focus {
+  --border-opacity: 1;
+  border-color: #faf5ff;
+  border-color: rgba(250, 245, 255, var(--border-opacity));
+}
+
+.focus\:border-purple-200:focus {
+  --border-opacity: 1;
+  border-color: #e9d8fd;
+  border-color: rgba(233, 216, 253, var(--border-opacity));
+}
+
+.focus\:border-purple-300:focus {
+  --border-opacity: 1;
+  border-color: #d6bcfa;
+  border-color: rgba(214, 188, 250, var(--border-opacity));
+}
+
+.focus\:border-purple-400:focus {
+  --border-opacity: 1;
+  border-color: #b794f4;
+  border-color: rgba(183, 148, 244, var(--border-opacity));
+}
+
+.focus\:border-purple-500:focus {
+  --border-opacity: 1;
+  border-color: #9f7aea;
+  border-color: rgba(159, 122, 234, var(--border-opacity));
+}
+
+.focus\:border-purple-600:focus {
+  --border-opacity: 1;
+  border-color: #805ad5;
+  border-color: rgba(128, 90, 213, var(--border-opacity));
+}
+
+.focus\:border-purple-700:focus {
+  --border-opacity: 1;
+  border-color: #6b46c1;
+  border-color: rgba(107, 70, 193, var(--border-opacity));
+}
+
+.focus\:border-purple-800:focus {
+  --border-opacity: 1;
+  border-color: #553c9a;
+  border-color: rgba(85, 60, 154, var(--border-opacity));
+}
+
+.focus\:border-purple-900:focus {
+  --border-opacity: 1;
+  border-color: #44337a;
+  border-color: rgba(68, 51, 122, var(--border-opacity));
+}
+
+.focus\:border-pink-100:focus {
+  --border-opacity: 1;
+  border-color: #fff5f7;
+  border-color: rgba(255, 245, 247, var(--border-opacity));
+}
+
+.focus\:border-pink-200:focus {
+  --border-opacity: 1;
+  border-color: #fed7e2;
+  border-color: rgba(254, 215, 226, var(--border-opacity));
+}
+
+.focus\:border-pink-300:focus {
+  --border-opacity: 1;
+  border-color: #fbb6ce;
+  border-color: rgba(251, 182, 206, var(--border-opacity));
+}
+
+.focus\:border-pink-400:focus {
+  --border-opacity: 1;
+  border-color: #f687b3;
+  border-color: rgba(246, 135, 179, var(--border-opacity));
+}
+
+.focus\:border-pink-500:focus {
+  --border-opacity: 1;
+  border-color: #ed64a6;
+  border-color: rgba(237, 100, 166, var(--border-opacity));
+}
+
+.focus\:border-pink-600:focus {
+  --border-opacity: 1;
+  border-color: #d53f8c;
+  border-color: rgba(213, 63, 140, var(--border-opacity));
+}
+
+.focus\:border-pink-700:focus {
+  --border-opacity: 1;
+  border-color: #b83280;
+  border-color: rgba(184, 50, 128, var(--border-opacity));
+}
+
+.focus\:border-pink-800:focus {
+  --border-opacity: 1;
+  border-color: #97266d;
+  border-color: rgba(151, 38, 109, var(--border-opacity));
+}
+
+.focus\:border-pink-900:focus {
+  --border-opacity: 1;
+  border-color: #702459;
+  border-color: rgba(112, 36, 89, var(--border-opacity));
+}
+
+.border-opacity-0 {
+  --border-opacity: 0;
+}
+
+.border-opacity-25 {
+  --border-opacity: 0.25;
+}
+
+.border-opacity-50 {
+  --border-opacity: 0.5;
+}
+
+.border-opacity-75 {
+  --border-opacity: 0.75;
+}
+
+.border-opacity-100 {
+  --border-opacity: 1;
+}
+
+.hover\:border-opacity-0:hover {
+  --border-opacity: 0;
+}
+
+.hover\:border-opacity-25:hover {
+  --border-opacity: 0.25;
+}
+
+.hover\:border-opacity-50:hover {
+  --border-opacity: 0.5;
+}
+
+.hover\:border-opacity-75:hover {
+  --border-opacity: 0.75;
+}
+
+.hover\:border-opacity-100:hover {
+  --border-opacity: 1;
+}
+
+.focus\:border-opacity-0:focus {
+  --border-opacity: 0;
+}
+
+.focus\:border-opacity-25:focus {
+  --border-opacity: 0.25;
+}
+
+.focus\:border-opacity-50:focus {
+  --border-opacity: 0.5;
+}
+
+.focus\:border-opacity-75:focus {
+  --border-opacity: 0.75;
+}
+
+.focus\:border-opacity-100:focus {
+  --border-opacity: 1;
+}
+
+.rounded-none {
+  border-radius: 0;
+}
+
+.rounded-sm {
+  border-radius: 0.125rem;
+}
+
+.rounded {
+  border-radius: 0.25rem;
+}
+
+.rounded-md {
+  border-radius: 0.375rem;
+}
+
+.rounded-lg {
+  border-radius: 0.5rem;
+}
+
+.rounded-full {
+  border-radius: 9999px;
+}
+
+.rounded-t-none {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+
+.rounded-r-none {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+
+.rounded-b-none {
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+.rounded-l-none {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+.rounded-t-sm {
+  border-top-left-radius: 0.125rem;
+  border-top-right-radius: 0.125rem;
+}
+
+.rounded-r-sm {
+  border-top-right-radius: 0.125rem;
+  border-bottom-right-radius: 0.125rem;
+}
+
+.rounded-b-sm {
+  border-bottom-right-radius: 0.125rem;
+  border-bottom-left-radius: 0.125rem;
+}
+
+.rounded-l-sm {
+  border-top-left-radius: 0.125rem;
+  border-bottom-left-radius: 0.125rem;
+}
+
+.rounded-t {
+  border-top-left-radius: 0.25rem;
+  border-top-right-radius: 0.25rem;
+}
+
+.rounded-r {
+  border-top-right-radius: 0.25rem;
+  border-bottom-right-radius: 0.25rem;
+}
+
+.rounded-b {
+  border-bottom-right-radius: 0.25rem;
+  border-bottom-left-radius: 0.25rem;
+}
+
+.rounded-l {
+  border-top-left-radius: 0.25rem;
+  border-bottom-left-radius: 0.25rem;
+}
+
+.rounded-t-md {
+  border-top-left-radius: 0.375rem;
+  border-top-right-radius: 0.375rem;
+}
+
+.rounded-r-md {
+  border-top-right-radius: 0.375rem;
+  border-bottom-right-radius: 0.375rem;
+}
+
+.rounded-b-md {
+  border-bottom-right-radius: 0.375rem;
+  border-bottom-left-radius: 0.375rem;
+}
+
+.rounded-l-md {
+  border-top-left-radius: 0.375rem;
+  border-bottom-left-radius: 0.375rem;
+}
+
+.rounded-t-lg {
+  border-top-left-radius: 0.5rem;
+  border-top-right-radius: 0.5rem;
+}
+
+.rounded-r-lg {
+  border-top-right-radius: 0.5rem;
+  border-bottom-right-radius: 0.5rem;
+}
+
+.rounded-b-lg {
+  border-bottom-right-radius: 0.5rem;
+  border-bottom-left-radius: 0.5rem;
+}
+
+.rounded-l-lg {
+  border-top-left-radius: 0.5rem;
+  border-bottom-left-radius: 0.5rem;
+}
+
+.rounded-t-full {
+  border-top-left-radius: 9999px;
+  border-top-right-radius: 9999px;
+}
+
+.rounded-r-full {
+  border-top-right-radius: 9999px;
+  border-bottom-right-radius: 9999px;
+}
+
+.rounded-b-full {
+  border-bottom-right-radius: 9999px;
+  border-bottom-left-radius: 9999px;
+}
+
+.rounded-l-full {
+  border-top-left-radius: 9999px;
+  border-bottom-left-radius: 9999px;
+}
+
+.rounded-tl-none {
+  border-top-left-radius: 0;
+}
+
+.rounded-tr-none {
+  border-top-right-radius: 0;
+}
+
+.rounded-br-none {
+  border-bottom-right-radius: 0;
+}
+
+.rounded-bl-none {
+  border-bottom-left-radius: 0;
+}
+
+.rounded-tl-sm {
+  border-top-left-radius: 0.125rem;
+}
+
+.rounded-tr-sm {
+  border-top-right-radius: 0.125rem;
+}
+
+.rounded-br-sm {
+  border-bottom-right-radius: 0.125rem;
+}
+
+.rounded-bl-sm {
+  border-bottom-left-radius: 0.125rem;
+}
+
+.rounded-tl {
+  border-top-left-radius: 0.25rem;
+}
+
+.rounded-tr {
+  border-top-right-radius: 0.25rem;
+}
+
+.rounded-br {
+  border-bottom-right-radius: 0.25rem;
+}
+
+.rounded-bl {
+  border-bottom-left-radius: 0.25rem;
+}
+
+.rounded-tl-md {
+  border-top-left-radius: 0.375rem;
+}
+
+.rounded-tr-md {
+  border-top-right-radius: 0.375rem;
+}
+
+.rounded-br-md {
+  border-bottom-right-radius: 0.375rem;
+}
+
+.rounded-bl-md {
+  border-bottom-left-radius: 0.375rem;
+}
+
+.rounded-tl-lg {
+  border-top-left-radius: 0.5rem;
+}
+
+.rounded-tr-lg {
+  border-top-right-radius: 0.5rem;
+}
+
+.rounded-br-lg {
+  border-bottom-right-radius: 0.5rem;
+}
+
+.rounded-bl-lg {
+  border-bottom-left-radius: 0.5rem;
+}
+
+.rounded-tl-full {
+  border-top-left-radius: 9999px;
+}
+
+.rounded-tr-full {
+  border-top-right-radius: 9999px;
+}
+
+.rounded-br-full {
+  border-bottom-right-radius: 9999px;
+}
+
+.rounded-bl-full {
+  border-bottom-left-radius: 9999px;
+}
+
+.border-solid {
+  border-style: solid;
+}
+
+.border-dashed {
+  border-style: dashed;
+}
+
+.border-dotted {
+  border-style: dotted;
+}
+
+.border-double {
+  border-style: double;
+}
+
+.border-none {
+  border-style: none;
+}
+
+.border-0 {
+  border-width: 0;
+}
+
+.border-2 {
+  border-width: 2px;
+}
+
+.border-4 {
+  border-width: 4px;
+}
+
+.border-8 {
+  border-width: 8px;
+}
+
+.border {
+  border-width: 1px;
+}
+
+.border-t-0 {
+  border-top-width: 0;
+}
+
+.border-r-0 {
+  border-right-width: 0;
+}
+
+.border-b-0 {
+  border-bottom-width: 0;
+}
+
+.border-l-0 {
+  border-left-width: 0;
+}
+
+.border-t-2 {
+  border-top-width: 2px;
+}
+
+.border-r-2 {
+  border-right-width: 2px;
+}
+
+.border-b-2 {
+  border-bottom-width: 2px;
+}
+
+.border-l-2 {
+  border-left-width: 2px;
+}
+
+.border-t-4 {
+  border-top-width: 4px;
+}
+
+.border-r-4 {
+  border-right-width: 4px;
+}
+
+.border-b-4 {
+  border-bottom-width: 4px;
+}
+
+.border-l-4 {
+  border-left-width: 4px;
+}
+
+.border-t-8 {
+  border-top-width: 8px;
+}
+
+.border-r-8 {
+  border-right-width: 8px;
+}
+
+.border-b-8 {
+  border-bottom-width: 8px;
+}
+
+.border-l-8 {
+  border-left-width: 8px;
+}
+
+.border-t {
+  border-top-width: 1px;
+}
+
+.border-r {
+  border-right-width: 1px;
+}
+
+.border-b {
+  border-bottom-width: 1px;
+}
+
+.border-l {
+  border-left-width: 1px;
+}
+
+.box-border {
+  box-sizing: border-box;
+}
+
+.box-content {
+  box-sizing: content-box;
+}
+
+.cursor-auto {
+  cursor: auto;
+}
+
+.cursor-default {
+  cursor: default;
+}
+
+.cursor-pointer {
+  cursor: pointer;
+}
+
+.cursor-wait {
+  cursor: wait;
+}
+
+.cursor-text {
+  cursor: text;
+}
+
+.cursor-move {
+  cursor: move;
+}
+
+.cursor-not-allowed {
+  cursor: not-allowed;
+}
+
+.block {
+  display: block;
+}
+
+.inline-block {
+  display: inline-block;
+}
+
+.inline {
+  display: inline;
+}
+
+.flex {
+  display: flex;
+}
+
+.inline-flex {
+  display: inline-flex;
+}
+
+.table {
+  display: table;
+}
+
+.table-caption {
+  display: table-caption;
+}
+
+.table-cell {
+  display: table-cell;
+}
+
+.table-column {
+  display: table-column;
+}
+
+.table-column-group {
+  display: table-column-group;
+}
+
+.table-footer-group {
+  display: table-footer-group;
+}
+
+.table-header-group {
+  display: table-header-group;
+}
+
+.table-row-group {
+  display: table-row-group;
+}
+
+.table-row {
+  display: table-row;
+}
+
+.flow-root {
+  display: flow-root;
+}
+
+.grid {
+  display: grid;
+}
+
+.inline-grid {
+  display: inline-grid;
+}
+
+.contents {
+  display: contents;
+}
+
+.hidden {
+  display: none;
+}
+
+.flex-row {
+  flex-direction: row;
+}
+
+.flex-row-reverse {
+  flex-direction: row-reverse;
+}
+
+.flex-col {
+  flex-direction: column;
+}
+
+.flex-col-reverse {
+  flex-direction: column-reverse;
+}
+
+.flex-wrap {
+  flex-wrap: wrap;
+}
+
+.flex-wrap-reverse {
+  flex-wrap: wrap-reverse;
+}
+
+.flex-no-wrap {
+  flex-wrap: nowrap;
+}
+
+.place-items-auto {
+  place-items: auto;
+}
+
+.place-items-start {
+  place-items: start;
+}
+
+.place-items-end {
+  place-items: end;
+}
+
+.place-items-center {
+  place-items: center;
+}
+
+.place-items-stretch {
+  place-items: stretch;
+}
+
+.place-content-center {
+  place-content: center;
+}
+
+.place-content-start {
+  place-content: start;
+}
+
+.place-content-end {
+  place-content: end;
+}
+
+.place-content-between {
+  place-content: space-between;
+}
+
+.place-content-around {
+  place-content: space-around;
+}
+
+.place-content-evenly {
+  place-content: space-evenly;
+}
+
+.place-content-stretch {
+  place-content: stretch;
+}
+
+.place-self-auto {
+  place-self: auto;
+}
+
+.place-self-start {
+  place-self: start;
+}
+
+.place-self-end {
+  place-self: end;
+}
+
+.place-self-center {
+  place-self: center;
+}
+
+.place-self-stretch {
+  place-self: stretch;
+}
+
+.items-start {
+  align-items: flex-start;
+}
+
+.items-end {
+  align-items: flex-end;
+}
+
+.items-center {
+  align-items: center;
+}
+
+.items-baseline {
+  align-items: baseline;
+}
+
+.items-stretch {
+  align-items: stretch;
+}
+
+.content-center {
+  align-content: center;
+}
+
+.content-start {
+  align-content: flex-start;
+}
+
+.content-end {
+  align-content: flex-end;
+}
+
+.content-between {
+  align-content: space-between;
+}
+
+.content-around {
+  align-content: space-around;
+}
+
+.content-evenly {
+  align-content: space-evenly;
+}
+
+.self-auto {
+  align-self: auto;
+}
+
+.self-start {
+  align-self: flex-start;
+}
+
+.self-end {
+  align-self: flex-end;
+}
+
+.self-center {
+  align-self: center;
+}
+
+.self-stretch {
+  align-self: stretch;
+}
+
+.justify-items-auto {
+  justify-items: auto;
+}
+
+.justify-items-start {
+  justify-items: start;
+}
+
+.justify-items-end {
+  justify-items: end;
+}
+
+.justify-items-center {
+  justify-items: center;
+}
+
+.justify-items-stretch {
+  justify-items: stretch;
+}
+
+.justify-start {
+  justify-content: flex-start;
+}
+
+.justify-end {
+  justify-content: flex-end;
+}
+
+.justify-center {
+  justify-content: center;
+}
+
+.justify-between {
+  justify-content: space-between;
+}
+
+.justify-around {
+  justify-content: space-around;
+}
+
+.justify-evenly {
+  justify-content: space-evenly;
+}
+
+.justify-self-auto {
+  justify-self: auto;
+}
+
+.justify-self-start {
+  justify-self: start;
+}
+
+.justify-self-end {
+  justify-self: end;
+}
+
+.justify-self-center {
+  justify-self: center;
+}
+
+.justify-self-stretch {
+  justify-self: stretch;
+}
+
+.flex-1 {
+  flex: 1 1 0%;
+}
+
+.flex-auto {
+  flex: 1 1 auto;
+}
+
+.flex-initial {
+  flex: 0 1 auto;
+}
+
+.flex-none {
+  flex: none;
+}
+
+.flex-grow-0 {
+  flex-grow: 0;
+}
+
+.flex-grow {
+  flex-grow: 1;
+}
+
+.flex-shrink-0 {
+  flex-shrink: 0;
+}
+
+.flex-shrink {
+  flex-shrink: 1;
+}
+
+.order-1 {
+  order: 1;
+}
+
+.order-2 {
+  order: 2;
+}
+
+.order-3 {
+  order: 3;
+}
+
+.order-4 {
+  order: 4;
+}
+
+.order-5 {
+  order: 5;
+}
+
+.order-6 {
+  order: 6;
+}
+
+.order-7 {
+  order: 7;
+}
+
+.order-8 {
+  order: 8;
+}
+
+.order-9 {
+  order: 9;
+}
+
+.order-10 {
+  order: 10;
+}
+
+.order-11 {
+  order: 11;
+}
+
+.order-12 {
+  order: 12;
+}
+
+.order-first {
+  order: -9999;
+}
+
+.order-last {
+  order: 9999;
+}
+
+.order-none {
+  order: 0;
+}
+
+.float-right {
+  float: right;
+}
+
+.float-left {
+  float: left;
+}
+
+.float-none {
+  float: none;
+}
+
+.clearfix:after {
+  content: "";
+  display: table;
+  clear: both;
+}
+
+.clear-left {
+  clear: left;
+}
+
+.clear-right {
+  clear: right;
+}
+
+.clear-both {
+  clear: both;
+}
+
+.clear-none {
+  clear: none;
+}
+
+.font-sans {
+  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+}
+
+.font-serif {
+  font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+}
+
+.font-mono {
+  font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+.font-hairline {
+  font-weight: 100;
+}
+
+.font-thin {
+  font-weight: 200;
+}
+
+.font-light {
+  font-weight: 300;
+}
+
+.font-normal {
+  font-weight: 400;
+}
+
+.font-medium {
+  font-weight: 500;
+}
+
+.font-semibold {
+  font-weight: 600;
+}
+
+.font-bold {
+  font-weight: 700;
+}
+
+.font-extrabold {
+  font-weight: 800;
+}
+
+.font-black {
+  font-weight: 900;
+}
+
+.hover\:font-hairline:hover {
+  font-weight: 100;
+}
+
+.hover\:font-thin:hover {
+  font-weight: 200;
+}
+
+.hover\:font-light:hover {
+  font-weight: 300;
+}
+
+.hover\:font-normal:hover {
+  font-weight: 400;
+}
+
+.hover\:font-medium:hover {
+  font-weight: 500;
+}
+
+.hover\:font-semibold:hover {
+  font-weight: 600;
+}
+
+.hover\:font-bold:hover {
+  font-weight: 700;
+}
+
+.hover\:font-extrabold:hover {
+  font-weight: 800;
+}
+
+.hover\:font-black:hover {
+  font-weight: 900;
+}
+
+.focus\:font-hairline:focus {
+  font-weight: 100;
+}
+
+.focus\:font-thin:focus {
+  font-weight: 200;
+}
+
+.focus\:font-light:focus {
+  font-weight: 300;
+}
+
+.focus\:font-normal:focus {
+  font-weight: 400;
+}
+
+.focus\:font-medium:focus {
+  font-weight: 500;
+}
+
+.focus\:font-semibold:focus {
+  font-weight: 600;
+}
+
+.focus\:font-bold:focus {
+  font-weight: 700;
+}
+
+.focus\:font-extrabold:focus {
+  font-weight: 800;
+}
+
+.focus\:font-black:focus {
+  font-weight: 900;
+}
+
+.h-0 {
+  height: 0;
+}
+
+.h-1 {
+  height: 0.25rem;
+}
+
+.h-2 {
+  height: 0.5rem;
+}
+
+.h-3 {
+  height: 0.75rem;
+}
+
+.h-4 {
+  height: 1rem;
+}
+
+.h-5 {
+  height: 1.25rem;
+}
+
+.h-6 {
+  height: 1.5rem;
+}
+
+.h-8 {
+  height: 2rem;
+}
+
+.h-10 {
+  height: 2.5rem;
+}
+
+.h-12 {
+  height: 3rem;
+}
+
+.h-16 {
+  height: 4rem;
+}
+
+.h-20 {
+  height: 5rem;
+}
+
+.h-24 {
+  height: 6rem;
+}
+
+.h-32 {
+  height: 8rem;
+}
+
+.h-40 {
+  height: 10rem;
+}
+
+.h-48 {
+  height: 12rem;
+}
+
+.h-56 {
+  height: 14rem;
+}
+
+.h-64 {
+  height: 16rem;
+}
+
+.h-auto {
+  height: auto;
+}
+
+.h-px {
+  height: 1px;
+}
+
+.h-full {
+  height: 100%;
+}
+
+.h-screen {
+  height: 100vh;
+}
+
+.text-xs {
+  font-size: 0.75rem;
+}
+
+.text-sm {
+  font-size: 0.875rem;
+}
+
+.text-base {
+  font-size: 1rem;
+}
+
+.text-lg {
+  font-size: 1.125rem;
+}
+
+.text-xl {
+  font-size: 1.25rem;
+}
+
+.text-2xl {
+  font-size: 1.5rem;
+}
+
+.text-3xl {
+  font-size: 1.875rem;
+}
+
+.text-4xl {
+  font-size: 2.25rem;
+}
+
+.text-5xl {
+  font-size: 3rem;
+}
+
+.text-6xl {
+  font-size: 4rem;
+}
+
+.leading-3 {
+  line-height: .75rem;
+}
+
+.leading-4 {
+  line-height: 1rem;
+}
+
+.leading-5 {
+  line-height: 1.25rem;
+}
+
+.leading-6 {
+  line-height: 1.5rem;
+}
+
+.leading-7 {
+  line-height: 1.75rem;
+}
+
+.leading-8 {
+  line-height: 2rem;
+}
+
+.leading-9 {
+  line-height: 2.25rem;
+}
+
+.leading-10 {
+  line-height: 2.5rem;
+}
+
+.leading-none {
+  line-height: 1;
+}
+
+.leading-tight {
+  line-height: 1.25;
+}
+
+.leading-snug {
+  line-height: 1.375;
+}
+
+.leading-normal {
+  line-height: 1.5;
+}
+
+.leading-relaxed {
+  line-height: 1.625;
+}
+
+.leading-loose {
+  line-height: 2;
+}
+
+.list-inside {
+  list-style-position: inside;
+}
+
+.list-outside {
+  list-style-position: outside;
+}
+
+.list-none {
+  list-style-type: none;
+}
+
+.list-disc {
+  list-style-type: disc;
+}
+
+.list-decimal {
+  list-style-type: decimal;
+}
+
+.m-0 {
+  margin: 0;
+}
+
+.m-1 {
+  margin: 0.25rem;
+}
+
+.m-2 {
+  margin: 0.5rem;
+}
+
+.m-3 {
+  margin: 0.75rem;
+}
+
+.m-4 {
+  margin: 1rem;
+}
+
+.m-5 {
+  margin: 1.25rem;
+}
+
+.m-6 {
+  margin: 1.5rem;
+}
+
+.m-8 {
+  margin: 2rem;
+}
+
+.m-10 {
+  margin: 2.5rem;
+}
+
+.m-12 {
+  margin: 3rem;
+}
+
+.m-16 {
+  margin: 4rem;
+}
+
+.m-20 {
+  margin: 5rem;
+}
+
+.m-24 {
+  margin: 6rem;
+}
+
+.m-32 {
+  margin: 8rem;
+}
+
+.m-40 {
+  margin: 10rem;
+}
+
+.m-48 {
+  margin: 12rem;
+}
+
+.m-56 {
+  margin: 14rem;
+}
+
+.m-64 {
+  margin: 16rem;
+}
+
+.m-auto {
+  margin: auto;
+}
+
+.m-px {
+  margin: 1px;
+}
+
+.-m-1 {
+  margin: -0.25rem;
+}
+
+.-m-2 {
+  margin: -0.5rem;
+}
+
+.-m-3 {
+  margin: -0.75rem;
+}
+
+.-m-4 {
+  margin: -1rem;
+}
+
+.-m-5 {
+  margin: -1.25rem;
+}
+
+.-m-6 {
+  margin: -1.5rem;
+}
+
+.-m-8 {
+  margin: -2rem;
+}
+
+.-m-10 {
+  margin: -2.5rem;
+}
+
+.-m-12 {
+  margin: -3rem;
+}
+
+.-m-16 {
+  margin: -4rem;
+}
+
+.-m-20 {
+  margin: -5rem;
+}
+
+.-m-24 {
+  margin: -6rem;
+}
+
+.-m-32 {
+  margin: -8rem;
+}
+
+.-m-40 {
+  margin: -10rem;
+}
+
+.-m-48 {
+  margin: -12rem;
+}
+
+.-m-56 {
+  margin: -14rem;
+}
+
+.-m-64 {
+  margin: -16rem;
+}
+
+.-m-px {
+  margin: -1px;
+}
+
+.my-0 {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+.mx-0 {
+  margin-left: 0;
+  margin-right: 0;
+}
+
+.my-1 {
+  margin-top: 0.25rem;
+  margin-bottom: 0.25rem;
+}
+
+.mx-1 {
+  margin-left: 0.25rem;
+  margin-right: 0.25rem;
+}
+
+.my-2 {
+  margin-top: 0.5rem;
+  margin-bottom: 0.5rem;
+}
+
+.mx-2 {
+  margin-left: 0.5rem;
+  margin-right: 0.5rem;
+}
+
+.my-3 {
+  margin-top: 0.75rem;
+  margin-bottom: 0.75rem;
+}
+
+.mx-3 {
+  margin-left: 0.75rem;
+  margin-right: 0.75rem;
+}
+
+.my-4 {
+  margin-top: 1rem;
+  margin-bottom: 1rem;
+}
+
+.mx-4 {
+  margin-left: 1rem;
+  margin-right: 1rem;
+}
+
+.my-5 {
+  margin-top: 1.25rem;
+  margin-bottom: 1.25rem;
+}
+
+.mx-5 {
+  margin-left: 1.25rem;
+  margin-right: 1.25rem;
+}
+
+.my-6 {
+  margin-top: 1.5rem;
+  margin-bottom: 1.5rem;
+}
+
+.mx-6 {
+  margin-left: 1.5rem;
+  margin-right: 1.5rem;
+}
+
+.my-8 {
+  margin-top: 2rem;
+  margin-bottom: 2rem;
+}
+
+.mx-8 {
+  margin-left: 2rem;
+  margin-right: 2rem;
+}
+
+.my-10 {
+  margin-top: 2.5rem;
+  margin-bottom: 2.5rem;
+}
+
+.mx-10 {
+  margin-left: 2.5rem;
+  margin-right: 2.5rem;
+}
+
+.my-12 {
+  margin-top: 3rem;
+  margin-bottom: 3rem;
+}
+
+.mx-12 {
+  margin-left: 3rem;
+  margin-right: 3rem;
+}
+
+.my-16 {
+  margin-top: 4rem;
+  margin-bottom: 4rem;
+}
+
+.mx-16 {
+  margin-left: 4rem;
+  margin-right: 4rem;
+}
+
+.my-20 {
+  margin-top: 5rem;
+  margin-bottom: 5rem;
+}
+
+.mx-20 {
+  margin-left: 5rem;
+  margin-right: 5rem;
+}
+
+.my-24 {
+  margin-top: 6rem;
+  margin-bottom: 6rem;
+}
+
+.mx-24 {
+  margin-left: 6rem;
+  margin-right: 6rem;
+}
+
+.my-32 {
+  margin-top: 8rem;
+  margin-bottom: 8rem;
+}
+
+.mx-32 {
+  margin-left: 8rem;
+  margin-right: 8rem;
+}
+
+.my-40 {
+  margin-top: 10rem;
+  margin-bottom: 10rem;
+}
+
+.mx-40 {
+  margin-left: 10rem;
+  margin-right: 10rem;
+}
+
+.my-48 {
+  margin-top: 12rem;
+  margin-bottom: 12rem;
+}
+
+.mx-48 {
+  margin-left: 12rem;
+  margin-right: 12rem;
+}
+
+.my-56 {
+  margin-top: 14rem;
+  margin-bottom: 14rem;
+}
+
+.mx-56 {
+  margin-left: 14rem;
+  margin-right: 14rem;
+}
+
+.my-64 {
+  margin-top: 16rem;
+  margin-bottom: 16rem;
+}
+
+.mx-64 {
+  margin-left: 16rem;
+  margin-right: 16rem;
+}
+
+.my-auto {
+  margin-top: auto;
+  margin-bottom: auto;
+}
+
+.mx-auto {
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.my-px {
+  margin-top: 1px;
+  margin-bottom: 1px;
+}
+
+.mx-px {
+  margin-left: 1px;
+  margin-right: 1px;
+}
+
+.-my-1 {
+  margin-top: -0.25rem;
+  margin-bottom: -0.25rem;
+}
+
+.-mx-1 {
+  margin-left: -0.25rem;
+  margin-right: -0.25rem;
+}
+
+.-my-2 {
+  margin-top: -0.5rem;
+  margin-bottom: -0.5rem;
+}
+
+.-mx-2 {
+  margin-left: -0.5rem;
+  margin-right: -0.5rem;
+}
+
+.-my-3 {
+  margin-top: -0.75rem;
+  margin-bottom: -0.75rem;
+}
+
+.-mx-3 {
+  margin-left: -0.75rem;
+  margin-right: -0.75rem;
+}
+
+.-my-4 {
+  margin-top: -1rem;
+  margin-bottom: -1rem;
+}
+
+.-mx-4 {
+  margin-left: -1rem;
+  margin-right: -1rem;
+}
+
+.-my-5 {
+  margin-top: -1.25rem;
+  margin-bottom: -1.25rem;
+}
+
+.-mx-5 {
+  margin-left: -1.25rem;
+  margin-right: -1.25rem;
+}
+
+.-my-6 {
+  margin-top: -1.5rem;
+  margin-bottom: -1.5rem;
+}
+
+.-mx-6 {
+  margin-left: -1.5rem;
+  margin-right: -1.5rem;
+}
+
+.-my-8 {
+  margin-top: -2rem;
+  margin-bottom: -2rem;
+}
+
+.-mx-8 {
+  margin-left: -2rem;
+  margin-right: -2rem;
+}
+
+.-my-10 {
+  margin-top: -2.5rem;
+  margin-bottom: -2.5rem;
+}
+
+.-mx-10 {
+  margin-left: -2.5rem;
+  margin-right: -2.5rem;
+}
+
+.-my-12 {
+  margin-top: -3rem;
+  margin-bottom: -3rem;
+}
+
+.-mx-12 {
+  margin-left: -3rem;
+  margin-right: -3rem;
+}
+
+.-my-16 {
+  margin-top: -4rem;
+  margin-bottom: -4rem;
+}
+
+.-mx-16 {
+  margin-left: -4rem;
+  margin-right: -4rem;
+}
+
+.-my-20 {
+  margin-top: -5rem;
+  margin-bottom: -5rem;
+}
+
+.-mx-20 {
+  margin-left: -5rem;
+  margin-right: -5rem;
+}
+
+.-my-24 {
+  margin-top: -6rem;
+  margin-bottom: -6rem;
+}
+
+.-mx-24 {
+  margin-left: -6rem;
+  margin-right: -6rem;
+}
+
+.-my-32 {
+  margin-top: -8rem;
+  margin-bottom: -8rem;
+}
+
+.-mx-32 {
+  margin-left: -8rem;
+  margin-right: -8rem;
+}
+
+.-my-40 {
+  margin-top: -10rem;
+  margin-bottom: -10rem;
+}
+
+.-mx-40 {
+  margin-left: -10rem;
+  margin-right: -10rem;
+}
+
+.-my-48 {
+  margin-top: -12rem;
+  margin-bottom: -12rem;
+}
+
+.-mx-48 {
+  margin-left: -12rem;
+  margin-right: -12rem;
+}
+
+.-my-56 {
+  margin-top: -14rem;
+  margin-bottom: -14rem;
+}
+
+.-mx-56 {
+  margin-left: -14rem;
+  margin-right: -14rem;
+}
+
+.-my-64 {
+  margin-top: -16rem;
+  margin-bottom: -16rem;
+}
+
+.-mx-64 {
+  margin-left: -16rem;
+  margin-right: -16rem;
+}
+
+.-my-px {
+  margin-top: -1px;
+  margin-bottom: -1px;
+}
+
+.-mx-px {
+  margin-left: -1px;
+  margin-right: -1px;
+}
+
+.mt-0 {
+  margin-top: 0;
+}
+
+.mr-0 {
+  margin-right: 0;
+}
+
+.mb-0 {
+  margin-bottom: 0;
+}
+
+.ml-0 {
+  margin-left: 0;
+}
+
+.mt-1 {
+  margin-top: 0.25rem;
+}
+
+.mr-1 {
+  margin-right: 0.25rem;
+}
+
+.mb-1 {
+  margin-bottom: 0.25rem;
+}
+
+.ml-1 {
+  margin-left: 0.25rem;
+}
+
+.mt-2 {
+  margin-top: 0.5rem;
+}
+
+.mr-2 {
+  margin-right: 0.5rem;
+}
+
+.mb-2 {
+  margin-bottom: 0.5rem;
+}
+
+.ml-2 {
+  margin-left: 0.5rem;
+}
+
+.mt-3 {
+  margin-top: 0.75rem;
+}
+
+.mr-3 {
+  margin-right: 0.75rem;
+}
+
+.mb-3 {
+  margin-bottom: 0.75rem;
+}
+
+.ml-3 {
+  margin-left: 0.75rem;
+}
+
+.mt-4 {
+  margin-top: 1rem;
+}
+
+.mr-4 {
+  margin-right: 1rem;
+}
+
+.mb-4 {
+  margin-bottom: 1rem;
+}
+
+.ml-4 {
+  margin-left: 1rem;
+}
+
+.mt-5 {
+  margin-top: 1.25rem;
+}
+
+.mr-5 {
+  margin-right: 1.25rem;
+}
+
+.mb-5 {
+  margin-bottom: 1.25rem;
+}
+
+.ml-5 {
+  margin-left: 1.25rem;
+}
+
+.mt-6 {
+  margin-top: 1.5rem;
+}
+
+.mr-6 {
+  margin-right: 1.5rem;
+}
+
+.mb-6 {
+  margin-bottom: 1.5rem;
+}
+
+.ml-6 {
+  margin-left: 1.5rem;
+}
+
+.mt-8 {
+  margin-top: 2rem;
+}
+
+.mr-8 {
+  margin-right: 2rem;
+}
+
+.mb-8 {
+  margin-bottom: 2rem;
+}
+
+.ml-8 {
+  margin-left: 2rem;
+}
+
+.mt-10 {
+  margin-top: 2.5rem;
+}
+
+.mr-10 {
+  margin-right: 2.5rem;
+}
+
+.mb-10 {
+  margin-bottom: 2.5rem;
+}
+
+.ml-10 {
+  margin-left: 2.5rem;
+}
+
+.mt-12 {
+  margin-top: 3rem;
+}
+
+.mr-12 {
+  margin-right: 3rem;
+}
+
+.mb-12 {
+  margin-bottom: 3rem;
+}
+
+.ml-12 {
+  margin-left: 3rem;
+}
+
+.mt-16 {
+  margin-top: 4rem;
+}
+
+.mr-16 {
+  margin-right: 4rem;
+}
+
+.mb-16 {
+  margin-bottom: 4rem;
+}
+
+.ml-16 {
+  margin-left: 4rem;
+}
+
+.mt-20 {
+  margin-top: 5rem;
+}
+
+.mr-20 {
+  margin-right: 5rem;
+}
+
+.mb-20 {
+  margin-bottom: 5rem;
+}
+
+.ml-20 {
+  margin-left: 5rem;
+}
+
+.mt-24 {
+  margin-top: 6rem;
+}
+
+.mr-24 {
+  margin-right: 6rem;
+}
+
+.mb-24 {
+  margin-bottom: 6rem;
+}
+
+.ml-24 {
+  margin-left: 6rem;
+}
+
+.mt-32 {
+  margin-top: 8rem;
+}
+
+.mr-32 {
+  margin-right: 8rem;
+}
+
+.mb-32 {
+  margin-bottom: 8rem;
+}
+
+.ml-32 {
+  margin-left: 8rem;
+}
+
+.mt-40 {
+  margin-top: 10rem;
+}
+
+.mr-40 {
+  margin-right: 10rem;
+}
+
+.mb-40 {
+  margin-bottom: 10rem;
+}
+
+.ml-40 {
+  margin-left: 10rem;
+}
+
+.mt-48 {
+  margin-top: 12rem;
+}
+
+.mr-48 {
+  margin-right: 12rem;
+}
+
+.mb-48 {
+  margin-bottom: 12rem;
+}
+
+.ml-48 {
+  margin-left: 12rem;
+}
+
+.mt-56 {
+  margin-top: 14rem;
+}
+
+.mr-56 {
+  margin-right: 14rem;
+}
+
+.mb-56 {
+  margin-bottom: 14rem;
+}
+
+.ml-56 {
+  margin-left: 14rem;
+}
+
+.mt-64 {
+  margin-top: 16rem;
+}
+
+.mr-64 {
+  margin-right: 16rem;
+}
+
+.mb-64 {
+  margin-bottom: 16rem;
+}
+
+.ml-64 {
+  margin-left: 16rem;
+}
+
+.mt-auto {
+  margin-top: auto;
+}
+
+.mr-auto {
+  margin-right: auto;
+}
+
+.mb-auto {
+  margin-bottom: auto;
+}
+
+.ml-auto {
+  margin-left: auto;
+}
+
+.mt-px {
+  margin-top: 1px;
+}
+
+.mr-px {
+  margin-right: 1px;
+}
+
+.mb-px {
+  margin-bottom: 1px;
+}
+
+.ml-px {
+  margin-left: 1px;
+}
+
+.-mt-1 {
+  margin-top: -0.25rem;
+}
+
+.-mr-1 {
+  margin-right: -0.25rem;
+}
+
+.-mb-1 {
+  margin-bottom: -0.25rem;
+}
+
+.-ml-1 {
+  margin-left: -0.25rem;
+}
+
+.-mt-2 {
+  margin-top: -0.5rem;
+}
+
+.-mr-2 {
+  margin-right: -0.5rem;
+}
+
+.-mb-2 {
+  margin-bottom: -0.5rem;
+}
+
+.-ml-2 {
+  margin-left: -0.5rem;
+}
+
+.-mt-3 {
+  margin-top: -0.75rem;
+}
+
+.-mr-3 {
+  margin-right: -0.75rem;
+}
+
+.-mb-3 {
+  margin-bottom: -0.75rem;
+}
+
+.-ml-3 {
+  margin-left: -0.75rem;
+}
+
+.-mt-4 {
+  margin-top: -1rem;
+}
+
+.-mr-4 {
+  margin-right: -1rem;
+}
+
+.-mb-4 {
+  margin-bottom: -1rem;
+}
+
+.-ml-4 {
+  margin-left: -1rem;
+}
+
+.-mt-5 {
+  margin-top: -1.25rem;
+}
+
+.-mr-5 {
+  margin-right: -1.25rem;
+}
+
+.-mb-5 {
+  margin-bottom: -1.25rem;
+}
+
+.-ml-5 {
+  margin-left: -1.25rem;
+}
+
+.-mt-6 {
+  margin-top: -1.5rem;
+}
+
+.-mr-6 {
+  margin-right: -1.5rem;
+}
+
+.-mb-6 {
+  margin-bottom: -1.5rem;
+}
+
+.-ml-6 {
+  margin-left: -1.5rem;
+}
+
+.-mt-8 {
+  margin-top: -2rem;
+}
+
+.-mr-8 {
+  margin-right: -2rem;
+}
+
+.-mb-8 {
+  margin-bottom: -2rem;
+}
+
+.-ml-8 {
+  margin-left: -2rem;
+}
+
+.-mt-10 {
+  margin-top: -2.5rem;
+}
+
+.-mr-10 {
+  margin-right: -2.5rem;
+}
+
+.-mb-10 {
+  margin-bottom: -2.5rem;
+}
+
+.-ml-10 {
+  margin-left: -2.5rem;
+}
+
+.-mt-12 {
+  margin-top: -3rem;
+}
+
+.-mr-12 {
+  margin-right: -3rem;
+}
+
+.-mb-12 {
+  margin-bottom: -3rem;
+}
+
+.-ml-12 {
+  margin-left: -3rem;
+}
+
+.-mt-16 {
+  margin-top: -4rem;
+}
+
+.-mr-16 {
+  margin-right: -4rem;
+}
+
+.-mb-16 {
+  margin-bottom: -4rem;
+}
+
+.-ml-16 {
+  margin-left: -4rem;
+}
+
+.-mt-20 {
+  margin-top: -5rem;
+}
+
+.-mr-20 {
+  margin-right: -5rem;
+}
+
+.-mb-20 {
+  margin-bottom: -5rem;
+}
+
+.-ml-20 {
+  margin-left: -5rem;
+}
+
+.-mt-24 {
+  margin-top: -6rem;
+}
+
+.-mr-24 {
+  margin-right: -6rem;
+}
+
+.-mb-24 {
+  margin-bottom: -6rem;
+}
+
+.-ml-24 {
+  margin-left: -6rem;
+}
+
+.-mt-32 {
+  margin-top: -8rem;
+}
+
+.-mr-32 {
+  margin-right: -8rem;
+}
+
+.-mb-32 {
+  margin-bottom: -8rem;
+}
+
+.-ml-32 {
+  margin-left: -8rem;
+}
+
+.-mt-40 {
+  margin-top: -10rem;
+}
+
+.-mr-40 {
+  margin-right: -10rem;
+}
+
+.-mb-40 {
+  margin-bottom: -10rem;
+}
+
+.-ml-40 {
+  margin-left: -10rem;
+}
+
+.-mt-48 {
+  margin-top: -12rem;
+}
+
+.-mr-48 {
+  margin-right: -12rem;
+}
+
+.-mb-48 {
+  margin-bottom: -12rem;
+}
+
+.-ml-48 {
+  margin-left: -12rem;
+}
+
+.-mt-56 {
+  margin-top: -14rem;
+}
+
+.-mr-56 {
+  margin-right: -14rem;
+}
+
+.-mb-56 {
+  margin-bottom: -14rem;
+}
+
+.-ml-56 {
+  margin-left: -14rem;
+}
+
+.-mt-64 {
+  margin-top: -16rem;
+}
+
+.-mr-64 {
+  margin-right: -16rem;
+}
+
+.-mb-64 {
+  margin-bottom: -16rem;
+}
+
+.-ml-64 {
+  margin-left: -16rem;
+}
+
+.-mt-px {
+  margin-top: -1px;
+}
+
+.-mr-px {
+  margin-right: -1px;
+}
+
+.-mb-px {
+  margin-bottom: -1px;
+}
+
+.-ml-px {
+  margin-left: -1px;
+}
+
+.max-h-full {
+  max-height: 100%;
+}
+
+.max-h-screen {
+  max-height: 100vh;
+}
+
+.max-w-none {
+  max-width: none;
+}
+
+.max-w-xs {
+  max-width: 20rem;
+}
+
+.max-w-sm {
+  max-width: 24rem;
+}
+
+.max-w-md {
+  max-width: 28rem;
+}
+
+.max-w-lg {
+  max-width: 32rem;
+}
+
+.max-w-xl {
+  max-width: 36rem;
+}
+
+.max-w-2xl {
+  max-width: 42rem;
+}
+
+.max-w-3xl {
+  max-width: 48rem;
+}
+
+.max-w-4xl {
+  max-width: 56rem;
+}
+
+.max-w-5xl {
+  max-width: 64rem;
+}
+
+.max-w-6xl {
+  max-width: 72rem;
+}
+
+.max-w-full {
+  max-width: 100%;
+}
+
+.max-w-screen-sm {
+  max-width: 640px;
+}
+
+.max-w-screen-md {
+  max-width: 768px;
+}
+
+.max-w-screen-lg {
+  max-width: 1024px;
+}
+
+.max-w-screen-xl {
+  max-width: 1280px;
+}
+
+.min-h-0 {
+  min-height: 0;
+}
+
+.min-h-full {
+  min-height: 100%;
+}
+
+.min-h-screen {
+  min-height: 100vh;
+}
+
+.min-w-0 {
+  min-width: 0;
+}
+
+.min-w-full {
+  min-width: 100%;
+}
+
+.object-contain {
+  -o-object-fit: contain;
+     object-fit: contain;
+}
+
+.object-cover {
+  -o-object-fit: cover;
+     object-fit: cover;
+}
+
+.object-fill {
+  -o-object-fit: fill;
+     object-fit: fill;
+}
+
+.object-none {
+  -o-object-fit: none;
+     object-fit: none;
+}
+
+.object-scale-down {
+  -o-object-fit: scale-down;
+     object-fit: scale-down;
+}
+
+.object-bottom {
+  -o-object-position: bottom;
+     object-position: bottom;
+}
+
+.object-center {
+  -o-object-position: center;
+     object-position: center;
+}
+
+.object-left {
+  -o-object-position: left;
+     object-position: left;
+}
+
+.object-left-bottom {
+  -o-object-position: left bottom;
+     object-position: left bottom;
+}
+
+.object-left-top {
+  -o-object-position: left top;
+     object-position: left top;
+}
+
+.object-right {
+  -o-object-position: right;
+     object-position: right;
+}
+
+.object-right-bottom {
+  -o-object-position: right bottom;
+     object-position: right bottom;
+}
+
+.object-right-top {
+  -o-object-position: right top;
+     object-position: right top;
+}
+
+.object-top {
+  -o-object-position: top;
+     object-position: top;
+}
+
+.opacity-0 {
+  opacity: 0;
+}
+
+.opacity-25 {
+  opacity: 0.25;
+}
+
+.opacity-50 {
+  opacity: 0.5;
+}
+
+.opacity-75 {
+  opacity: 0.75;
+}
+
+.opacity-100 {
+  opacity: 1;
+}
+
+.hover\:opacity-0:hover {
+  opacity: 0;
+}
+
+.hover\:opacity-25:hover {
+  opacity: 0.25;
+}
+
+.hover\:opacity-50:hover {
+  opacity: 0.5;
+}
+
+.hover\:opacity-75:hover {
+  opacity: 0.75;
+}
+
+.hover\:opacity-100:hover {
+  opacity: 1;
+}
+
+.focus\:opacity-0:focus {
+  opacity: 0;
+}
+
+.focus\:opacity-25:focus {
+  opacity: 0.25;
+}
+
+.focus\:opacity-50:focus {
+  opacity: 0.5;
+}
+
+.focus\:opacity-75:focus {
+  opacity: 0.75;
+}
+
+.focus\:opacity-100:focus {
+  opacity: 1;
+}
+
+.outline-none {
+  outline: 0;
+}
+
+.focus\:outline-none:focus {
+  outline: 0;
+}
+
+.overflow-auto {
+  overflow: auto;
+}
+
+.overflow-hidden {
+  overflow: hidden;
+}
+
+.overflow-visible {
+  overflow: visible;
+}
+
+.overflow-scroll {
+  overflow: scroll;
+}
+
+.overflow-x-auto {
+  overflow-x: auto;
+}
+
+.overflow-y-auto {
+  overflow-y: auto;
+}
+
+.overflow-x-hidden {
+  overflow-x: hidden;
+}
+
+.overflow-y-hidden {
+  overflow-y: hidden;
+}
+
+.overflow-x-visible {
+  overflow-x: visible;
+}
+
+.overflow-y-visible {
+  overflow-y: visible;
+}
+
+.overflow-x-scroll {
+  overflow-x: scroll;
+}
+
+.overflow-y-scroll {
+  overflow-y: scroll;
+}
+
+.scrolling-touch {
+  -webkit-overflow-scrolling: touch;
+}
+
+.scrolling-auto {
+  -webkit-overflow-scrolling: auto;
+}
+
+.overscroll-auto {
+  -ms-scroll-chaining: chained;
+      overscroll-behavior: auto;
+}
+
+.overscroll-contain {
+  -ms-scroll-chaining: none;
+      overscroll-behavior: contain;
+}
+
+.overscroll-none {
+  -ms-scroll-chaining: none;
+      overscroll-behavior: none;
+}
+
+.overscroll-y-auto {
+  overscroll-behavior-y: auto;
+}
+
+.overscroll-y-contain {
+  overscroll-behavior-y: contain;
+}
+
+.overscroll-y-none {
+  overscroll-behavior-y: none;
+}
+
+.overscroll-x-auto {
+  overscroll-behavior-x: auto;
+}
+
+.overscroll-x-contain {
+  overscroll-behavior-x: contain;
+}
+
+.overscroll-x-none {
+  overscroll-behavior-x: none;
+}
+
+.p-0 {
+  padding: 0;
+}
+
+.p-1 {
+  padding: 0.25rem;
+}
+
+.p-2 {
+  padding: 0.5rem;
+}
+
+.p-3 {
+  padding: 0.75rem;
+}
+
+.p-4 {
+  padding: 1rem;
+}
+
+.p-5 {
+  padding: 1.25rem;
+}
+
+.p-6 {
+  padding: 1.5rem;
+}
+
+.p-8 {
+  padding: 2rem;
+}
+
+.p-10 {
+  padding: 2.5rem;
+}
+
+.p-12 {
+  padding: 3rem;
+}
+
+.p-16 {
+  padding: 4rem;
+}
+
+.p-20 {
+  padding: 5rem;
+}
+
+.p-24 {
+  padding: 6rem;
+}
+
+.p-32 {
+  padding: 8rem;
+}
+
+.p-40 {
+  padding: 10rem;
+}
+
+.p-48 {
+  padding: 12rem;
+}
+
+.p-56 {
+  padding: 14rem;
+}
+
+.p-64 {
+  padding: 16rem;
+}
+
+.p-px {
+  padding: 1px;
+}
+
+.py-0 {
+  padding-top: 0;
+  padding-bottom: 0;
+}
+
+.px-0 {
+  padding-left: 0;
+  padding-right: 0;
+}
+
+.py-1 {
+  padding-top: 0.25rem;
+  padding-bottom: 0.25rem;
+}
+
+.px-1 {
+  padding-left: 0.25rem;
+  padding-right: 0.25rem;
+}
+
+.py-2 {
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+}
+
+.px-2 {
+  padding-left: 0.5rem;
+  padding-right: 0.5rem;
+}
+
+.py-3 {
+  padding-top: 0.75rem;
+  padding-bottom: 0.75rem;
+}
+
+.px-3 {
+  padding-left: 0.75rem;
+  padding-right: 0.75rem;
+}
+
+.py-4 {
+  padding-top: 1rem;
+  padding-bottom: 1rem;
+}
+
+.px-4 {
+  padding-left: 1rem;
+  padding-right: 1rem;
+}
+
+.py-5 {
+  padding-top: 1.25rem;
+  padding-bottom: 1.25rem;
+}
+
+.px-5 {
+  padding-left: 1.25rem;
+  padding-right: 1.25rem;
+}
+
+.py-6 {
+  padding-top: 1.5rem;
+  padding-bottom: 1.5rem;
+}
+
+.px-6 {
+  padding-left: 1.5rem;
+  padding-right: 1.5rem;
+}
+
+.py-8 {
+  padding-top: 2rem;
+  padding-bottom: 2rem;
+}
+
+.px-8 {
+  padding-left: 2rem;
+  padding-right: 2rem;
+}
+
+.py-10 {
+  padding-top: 2.5rem;
+  padding-bottom: 2.5rem;
+}
+
+.px-10 {
+  padding-left: 2.5rem;
+  padding-right: 2.5rem;
+}
+
+.py-12 {
+  padding-top: 3rem;
+  padding-bottom: 3rem;
+}
+
+.px-12 {
+  padding-left: 3rem;
+  padding-right: 3rem;
+}
+
+.py-16 {
+  padding-top: 4rem;
+  padding-bottom: 4rem;
+}
+
+.px-16 {
+  padding-left: 4rem;
+  padding-right: 4rem;
+}
+
+.py-20 {
+  padding-top: 5rem;
+  padding-bottom: 5rem;
+}
+
+.px-20 {
+  padding-left: 5rem;
+  padding-right: 5rem;
+}
+
+.py-24 {
+  padding-top: 6rem;
+  padding-bottom: 6rem;
+}
+
+.px-24 {
+  padding-left: 6rem;
+  padding-right: 6rem;
+}
+
+.py-32 {
+  padding-top: 8rem;
+  padding-bottom: 8rem;
+}
+
+.px-32 {
+  padding-left: 8rem;
+  padding-right: 8rem;
+}
+
+.py-40 {
+  padding-top: 10rem;
+  padding-bottom: 10rem;
+}
+
+.px-40 {
+  padding-left: 10rem;
+  padding-right: 10rem;
+}
+
+.py-48 {
+  padding-top: 12rem;
+  padding-bottom: 12rem;
+}
+
+.px-48 {
+  padding-left: 12rem;
+  padding-right: 12rem;
+}
+
+.py-56 {
+  padding-top: 14rem;
+  padding-bottom: 14rem;
+}
+
+.px-56 {
+  padding-left: 14rem;
+  padding-right: 14rem;
+}
+
+.py-64 {
+  padding-top: 16rem;
+  padding-bottom: 16rem;
+}
+
+.px-64 {
+  padding-left: 16rem;
+  padding-right: 16rem;
+}
+
+.py-px {
+  padding-top: 1px;
+  padding-bottom: 1px;
+}
+
+.px-px {
+  padding-left: 1px;
+  padding-right: 1px;
+}
+
+.pt-0 {
+  padding-top: 0;
+}
+
+.pr-0 {
+  padding-right: 0;
+}
+
+.pb-0 {
+  padding-bottom: 0;
+}
+
+.pl-0 {
+  padding-left: 0;
+}
+
+.pt-1 {
+  padding-top: 0.25rem;
+}
+
+.pr-1 {
+  padding-right: 0.25rem;
+}
+
+.pb-1 {
+  padding-bottom: 0.25rem;
+}
+
+.pl-1 {
+  padding-left: 0.25rem;
+}
+
+.pt-2 {
+  padding-top: 0.5rem;
+}
+
+.pr-2 {
+  padding-right: 0.5rem;
+}
+
+.pb-2 {
+  padding-bottom: 0.5rem;
+}
+
+.pl-2 {
+  padding-left: 0.5rem;
+}
+
+.pt-3 {
+  padding-top: 0.75rem;
+}
+
+.pr-3 {
+  padding-right: 0.75rem;
+}
+
+.pb-3 {
+  padding-bottom: 0.75rem;
+}
+
+.pl-3 {
+  padding-left: 0.75rem;
+}
+
+.pt-4 {
+  padding-top: 1rem;
+}
+
+.pr-4 {
+  padding-right: 1rem;
+}
+
+.pb-4 {
+  padding-bottom: 1rem;
+}
+
+.pl-4 {
+  padding-left: 1rem;
+}
+
+.pt-5 {
+  padding-top: 1.25rem;
+}
+
+.pr-5 {
+  padding-right: 1.25rem;
+}
+
+.pb-5 {
+  padding-bottom: 1.25rem;
+}
+
+.pl-5 {
+  padding-left: 1.25rem;
+}
+
+.pt-6 {
+  padding-top: 1.5rem;
+}
+
+.pr-6 {
+  padding-right: 1.5rem;
+}
+
+.pb-6 {
+  padding-bottom: 1.5rem;
+}
+
+.pl-6 {
+  padding-left: 1.5rem;
+}
+
+.pt-8 {
+  padding-top: 2rem;
+}
+
+.pr-8 {
+  padding-right: 2rem;
+}
+
+.pb-8 {
+  padding-bottom: 2rem;
+}
+
+.pl-8 {
+  padding-left: 2rem;
+}
+
+.pt-10 {
+  padding-top: 2.5rem;
+}
+
+.pr-10 {
+  padding-right: 2.5rem;
+}
+
+.pb-10 {
+  padding-bottom: 2.5rem;
+}
+
+.pl-10 {
+  padding-left: 2.5rem;
+}
+
+.pt-12 {
+  padding-top: 3rem;
+}
+
+.pr-12 {
+  padding-right: 3rem;
+}
+
+.pb-12 {
+  padding-bottom: 3rem;
+}
+
+.pl-12 {
+  padding-left: 3rem;
+}
+
+.pt-16 {
+  padding-top: 4rem;
+}
+
+.pr-16 {
+  padding-right: 4rem;
+}
+
+.pb-16 {
+  padding-bottom: 4rem;
+}
+
+.pl-16 {
+  padding-left: 4rem;
+}
+
+.pt-20 {
+  padding-top: 5rem;
+}
+
+.pr-20 {
+  padding-right: 5rem;
+}
+
+.pb-20 {
+  padding-bottom: 5rem;
+}
+
+.pl-20 {
+  padding-left: 5rem;
+}
+
+.pt-24 {
+  padding-top: 6rem;
+}
+
+.pr-24 {
+  padding-right: 6rem;
+}
+
+.pb-24 {
+  padding-bottom: 6rem;
+}
+
+.pl-24 {
+  padding-left: 6rem;
+}
+
+.pt-32 {
+  padding-top: 8rem;
+}
+
+.pr-32 {
+  padding-right: 8rem;
+}
+
+.pb-32 {
+  padding-bottom: 8rem;
+}
+
+.pl-32 {
+  padding-left: 8rem;
+}
+
+.pt-40 {
+  padding-top: 10rem;
+}
+
+.pr-40 {
+  padding-right: 10rem;
+}
+
+.pb-40 {
+  padding-bottom: 10rem;
+}
+
+.pl-40 {
+  padding-left: 10rem;
+}
+
+.pt-48 {
+  padding-top: 12rem;
+}
+
+.pr-48 {
+  padding-right: 12rem;
+}
+
+.pb-48 {
+  padding-bottom: 12rem;
+}
+
+.pl-48 {
+  padding-left: 12rem;
+}
+
+.pt-56 {
+  padding-top: 14rem;
+}
+
+.pr-56 {
+  padding-right: 14rem;
+}
+
+.pb-56 {
+  padding-bottom: 14rem;
+}
+
+.pl-56 {
+  padding-left: 14rem;
+}
+
+.pt-64 {
+  padding-top: 16rem;
+}
+
+.pr-64 {
+  padding-right: 16rem;
+}
+
+.pb-64 {
+  padding-bottom: 16rem;
+}
+
+.pl-64 {
+  padding-left: 16rem;
+}
+
+.pt-px {
+  padding-top: 1px;
+}
+
+.pr-px {
+  padding-right: 1px;
+}
+
+.pb-px {
+  padding-bottom: 1px;
+}
+
+.pl-px {
+  padding-left: 1px;
+}
+
+.placeholder-transparent::-moz-placeholder {
+  color: transparent;
+}
+
+.placeholder-transparent:-ms-input-placeholder {
+  color: transparent;
+}
+
+.placeholder-transparent::placeholder {
+  color: transparent;
+}
+
+.placeholder-current::-moz-placeholder {
+  color: currentColor;
+}
+
+.placeholder-current:-ms-input-placeholder {
+  color: currentColor;
+}
+
+.placeholder-current::placeholder {
+  color: currentColor;
+}
+
+.placeholder-black::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.placeholder-black:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.placeholder-black::placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.placeholder-white::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.placeholder-white:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.placeholder-white::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.placeholder-gray-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.placeholder-gray-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.placeholder-gray-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.placeholder-gray-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.placeholder-gray-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.placeholder-gray-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.placeholder-gray-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.placeholder-gray-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.placeholder-gray-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.placeholder-gray-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.placeholder-gray-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.placeholder-gray-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.placeholder-gray-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.placeholder-gray-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.placeholder-gray-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.placeholder-gray-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.placeholder-gray-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.placeholder-gray-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.placeholder-gray-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.placeholder-gray-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.placeholder-gray-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.placeholder-gray-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.placeholder-gray-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.placeholder-gray-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.placeholder-gray-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.placeholder-gray-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.placeholder-gray-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.placeholder-red-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.placeholder-red-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.placeholder-red-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.placeholder-red-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.placeholder-red-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.placeholder-red-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.placeholder-red-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.placeholder-red-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.placeholder-red-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.placeholder-red-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.placeholder-red-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.placeholder-red-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.placeholder-red-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.placeholder-red-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.placeholder-red-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.placeholder-red-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.placeholder-red-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.placeholder-red-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.placeholder-red-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.placeholder-red-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.placeholder-red-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.placeholder-red-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.placeholder-red-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.placeholder-red-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.placeholder-red-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.placeholder-red-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.placeholder-red-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.placeholder-orange-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.placeholder-orange-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.placeholder-orange-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.placeholder-orange-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.placeholder-orange-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.placeholder-orange-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.placeholder-orange-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.placeholder-orange-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.placeholder-orange-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.placeholder-orange-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.placeholder-orange-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.placeholder-orange-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.placeholder-orange-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.placeholder-orange-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.placeholder-orange-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.placeholder-orange-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.placeholder-orange-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.placeholder-orange-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.placeholder-orange-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.placeholder-orange-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.placeholder-orange-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.placeholder-green-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.placeholder-green-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.placeholder-green-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.placeholder-green-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.placeholder-green-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.placeholder-green-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.placeholder-green-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.placeholder-green-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.placeholder-green-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.placeholder-green-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.placeholder-green-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.placeholder-green-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.placeholder-green-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.placeholder-green-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.placeholder-green-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.placeholder-green-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.placeholder-green-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.placeholder-green-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.placeholder-green-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.placeholder-green-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.placeholder-green-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.placeholder-green-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.placeholder-green-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.placeholder-green-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.placeholder-green-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.placeholder-green-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.placeholder-green-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.placeholder-teal-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.placeholder-teal-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.placeholder-teal-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.placeholder-teal-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.placeholder-teal-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.placeholder-teal-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.placeholder-teal-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.placeholder-teal-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.placeholder-teal-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.placeholder-teal-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.placeholder-teal-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.placeholder-teal-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.placeholder-teal-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.placeholder-teal-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.placeholder-teal-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.placeholder-teal-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.placeholder-teal-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.placeholder-teal-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.placeholder-teal-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.placeholder-teal-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.placeholder-teal-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.placeholder-teal-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.placeholder-teal-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.placeholder-teal-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.placeholder-teal-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.placeholder-teal-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.placeholder-teal-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.placeholder-blue-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.placeholder-blue-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.placeholder-blue-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.placeholder-blue-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.placeholder-blue-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.placeholder-blue-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.placeholder-blue-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.placeholder-blue-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.placeholder-blue-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.placeholder-blue-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.placeholder-blue-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.placeholder-blue-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.placeholder-blue-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.placeholder-blue-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.placeholder-blue-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.placeholder-blue-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.placeholder-blue-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.placeholder-blue-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.placeholder-blue-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.placeholder-blue-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.placeholder-blue-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.placeholder-blue-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.placeholder-blue-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.placeholder-blue-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.placeholder-blue-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.placeholder-blue-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.placeholder-blue-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.placeholder-purple-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.placeholder-purple-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.placeholder-purple-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.placeholder-purple-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.placeholder-purple-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.placeholder-purple-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.placeholder-purple-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.placeholder-purple-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.placeholder-purple-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.placeholder-purple-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.placeholder-purple-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.placeholder-purple-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.placeholder-purple-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.placeholder-purple-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.placeholder-purple-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.placeholder-purple-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.placeholder-purple-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.placeholder-purple-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.placeholder-purple-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.placeholder-purple-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.placeholder-purple-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.placeholder-purple-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.placeholder-purple-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.placeholder-purple-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.placeholder-purple-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.placeholder-purple-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.placeholder-purple-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.placeholder-pink-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.placeholder-pink-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.placeholder-pink-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.placeholder-pink-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.placeholder-pink-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.placeholder-pink-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.placeholder-pink-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.placeholder-pink-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.placeholder-pink-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.placeholder-pink-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.placeholder-pink-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.placeholder-pink-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.placeholder-pink-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.placeholder-pink-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.placeholder-pink-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.placeholder-pink-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.placeholder-pink-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.placeholder-pink-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.placeholder-pink-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.placeholder-pink-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.placeholder-pink-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.placeholder-pink-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.placeholder-pink-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.placeholder-pink-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.placeholder-pink-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.placeholder-pink-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.placeholder-pink-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-transparent:focus::-moz-placeholder {
+  color: transparent;
+}
+
+.focus\:placeholder-transparent:focus:-ms-input-placeholder {
+  color: transparent;
+}
+
+.focus\:placeholder-transparent:focus::placeholder {
+  color: transparent;
+}
+
+.focus\:placeholder-current:focus::-moz-placeholder {
+  color: currentColor;
+}
+
+.focus\:placeholder-current:focus:-ms-input-placeholder {
+  color: currentColor;
+}
+
+.focus\:placeholder-current:focus::placeholder {
+  color: currentColor;
+}
+
+.focus\:placeholder-black:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-black:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-black:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-white:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-white:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-white:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.placeholder-opacity-0::-moz-placeholder {
+  --placeholder-opacity: 0;
+}
+
+.placeholder-opacity-0:-ms-input-placeholder {
+  --placeholder-opacity: 0;
+}
+
+.placeholder-opacity-0::placeholder {
+  --placeholder-opacity: 0;
+}
+
+.placeholder-opacity-25::-moz-placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.placeholder-opacity-25:-ms-input-placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.placeholder-opacity-25::placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.placeholder-opacity-50::-moz-placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.placeholder-opacity-50:-ms-input-placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.placeholder-opacity-50::placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.placeholder-opacity-75::-moz-placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.placeholder-opacity-75:-ms-input-placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.placeholder-opacity-75::placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.placeholder-opacity-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+}
+
+.placeholder-opacity-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+}
+
+.placeholder-opacity-100::placeholder {
+  --placeholder-opacity: 1;
+}
+
+.focus\:placeholder-opacity-0:focus::-moz-placeholder {
+  --placeholder-opacity: 0;
+}
+
+.focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+  --placeholder-opacity: 0;
+}
+
+.focus\:placeholder-opacity-0:focus::placeholder {
+  --placeholder-opacity: 0;
+}
+
+.focus\:placeholder-opacity-25:focus::-moz-placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.focus\:placeholder-opacity-25:focus::placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.focus\:placeholder-opacity-50:focus::-moz-placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.focus\:placeholder-opacity-50:focus::placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.focus\:placeholder-opacity-75:focus::-moz-placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.focus\:placeholder-opacity-75:focus::placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.focus\:placeholder-opacity-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+}
+
+.focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+}
+
+.focus\:placeholder-opacity-100:focus::placeholder {
+  --placeholder-opacity: 1;
+}
+
+.pointer-events-none {
+  pointer-events: none;
+}
+
+.pointer-events-auto {
+  pointer-events: auto;
+}
+
+.static {
+  position: static;
+}
+
+.fixed {
+  position: fixed;
+}
+
+.absolute {
+  position: absolute;
+}
+
+.relative {
+  position: relative;
+}
+
+.sticky {
+  position: -webkit-sticky;
+  position: sticky;
+}
+
+.inset-0 {
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+}
+
+.inset-auto {
+  top: auto;
+  right: auto;
+  bottom: auto;
+  left: auto;
+}
+
+.inset-y-0 {
+  top: 0;
+  bottom: 0;
+}
+
+.inset-x-0 {
+  right: 0;
+  left: 0;
+}
+
+.inset-y-auto {
+  top: auto;
+  bottom: auto;
+}
+
+.inset-x-auto {
+  right: auto;
+  left: auto;
+}
+
+.top-0 {
+  top: 0;
+}
+
+.right-0 {
+  right: 0;
+}
+
+.bottom-0 {
+  bottom: 0;
+}
+
+.left-0 {
+  left: 0;
+}
+
+.top-auto {
+  top: auto;
+}
+
+.right-auto {
+  right: auto;
+}
+
+.bottom-auto {
+  bottom: auto;
+}
+
+.left-auto {
+  left: auto;
+}
+
+.resize-none {
+  resize: none;
+}
+
+.resize-y {
+  resize: vertical;
+}
+
+.resize-x {
+  resize: horizontal;
+}
+
+.resize {
+  resize: both;
+}
+
+.shadow-xs {
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+}
+
+.shadow-sm {
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+}
+
+.shadow {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+}
+
+.shadow-md {
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+}
+
+.shadow-lg {
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+}
+
+.shadow-xl {
+  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+.shadow-2xl {
+  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+}
+
+.shadow-inner {
+  box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+}
+
+.shadow-outline {
+  box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+}
+
+.shadow-none {
+  box-shadow: none;
+}
+
+.hover\:shadow-xs:hover {
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+}
+
+.hover\:shadow-sm:hover {
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+}
+
+.hover\:shadow:hover {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+}
+
+.hover\:shadow-md:hover {
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+}
+
+.hover\:shadow-lg:hover {
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+}
+
+.hover\:shadow-xl:hover {
+  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+.hover\:shadow-2xl:hover {
+  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+}
+
+.hover\:shadow-inner:hover {
+  box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+}
+
+.hover\:shadow-outline:hover {
+  box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+}
+
+.hover\:shadow-none:hover {
+  box-shadow: none;
+}
+
+.focus\:shadow-xs:focus {
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+}
+
+.focus\:shadow-sm:focus {
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+}
+
+.focus\:shadow:focus {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+}
+
+.focus\:shadow-md:focus {
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+}
+
+.focus\:shadow-lg:focus {
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+}
+
+.focus\:shadow-xl:focus {
+  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+.focus\:shadow-2xl:focus {
+  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+}
+
+.focus\:shadow-inner:focus {
+  box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+}
+
+.focus\:shadow-outline:focus {
+  box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+}
+
+.focus\:shadow-none:focus {
+  box-shadow: none;
+}
+
+.fill-current {
+  fill: currentColor;
+}
+
+.stroke-current {
+  stroke: currentColor;
+}
+
+.stroke-0 {
+  stroke-width: 0;
+}
+
+.stroke-1 {
+  stroke-width: 1;
+}
+
+.stroke-2 {
+  stroke-width: 2;
+}
+
+.table-auto {
+  table-layout: auto;
+}
+
+.table-fixed {
+  table-layout: fixed;
+}
+
+.text-left {
+  text-align: left;
+}
+
+.text-center {
+  text-align: center;
+}
+
+.text-right {
+  text-align: right;
+}
+
+.text-justify {
+  text-align: justify;
+}
+
+.text-transparent {
+  color: transparent;
+}
+
+.text-current {
+  color: currentColor;
+}
+
+.text-black {
+  --text-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--text-opacity));
+}
+
+.text-white {
+  --text-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--text-opacity));
+}
+
+.text-gray-100 {
+  --text-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--text-opacity));
+}
+
+.text-gray-200 {
+  --text-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--text-opacity));
+}
+
+.text-gray-300 {
+  --text-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--text-opacity));
+}
+
+.text-gray-400 {
+  --text-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--text-opacity));
+}
+
+.text-gray-500 {
+  --text-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--text-opacity));
+}
+
+.text-gray-600 {
+  --text-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--text-opacity));
+}
+
+.text-gray-700 {
+  --text-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--text-opacity));
+}
+
+.text-gray-800 {
+  --text-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--text-opacity));
+}
+
+.text-gray-900 {
+  --text-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--text-opacity));
+}
+
+.text-red-100 {
+  --text-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--text-opacity));
+}
+
+.text-red-200 {
+  --text-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--text-opacity));
+}
+
+.text-red-300 {
+  --text-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--text-opacity));
+}
+
+.text-red-400 {
+  --text-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--text-opacity));
+}
+
+.text-red-500 {
+  --text-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--text-opacity));
+}
+
+.text-red-600 {
+  --text-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--text-opacity));
+}
+
+.text-red-700 {
+  --text-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--text-opacity));
+}
+
+.text-red-800 {
+  --text-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--text-opacity));
+}
+
+.text-red-900 {
+  --text-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--text-opacity));
+}
+
+.text-orange-100 {
+  --text-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--text-opacity));
+}
+
+.text-orange-200 {
+  --text-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--text-opacity));
+}
+
+.text-orange-300 {
+  --text-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--text-opacity));
+}
+
+.text-orange-400 {
+  --text-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--text-opacity));
+}
+
+.text-orange-500 {
+  --text-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--text-opacity));
+}
+
+.text-orange-600 {
+  --text-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--text-opacity));
+}
+
+.text-orange-700 {
+  --text-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--text-opacity));
+}
+
+.text-orange-800 {
+  --text-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--text-opacity));
+}
+
+.text-orange-900 {
+  --text-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--text-opacity));
+}
+
+.text-yellow-100 {
+  --text-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--text-opacity));
+}
+
+.text-yellow-200 {
+  --text-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--text-opacity));
+}
+
+.text-yellow-300 {
+  --text-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--text-opacity));
+}
+
+.text-yellow-400 {
+  --text-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--text-opacity));
+}
+
+.text-yellow-500 {
+  --text-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--text-opacity));
+}
+
+.text-yellow-600 {
+  --text-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--text-opacity));
+}
+
+.text-yellow-700 {
+  --text-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--text-opacity));
+}
+
+.text-yellow-800 {
+  --text-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--text-opacity));
+}
+
+.text-yellow-900 {
+  --text-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--text-opacity));
+}
+
+.text-green-100 {
+  --text-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--text-opacity));
+}
+
+.text-green-200 {
+  --text-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--text-opacity));
+}
+
+.text-green-300 {
+  --text-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--text-opacity));
+}
+
+.text-green-400 {
+  --text-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--text-opacity));
+}
+
+.text-green-500 {
+  --text-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--text-opacity));
+}
+
+.text-green-600 {
+  --text-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--text-opacity));
+}
+
+.text-green-700 {
+  --text-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--text-opacity));
+}
+
+.text-green-800 {
+  --text-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--text-opacity));
+}
+
+.text-green-900 {
+  --text-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--text-opacity));
+}
+
+.text-teal-100 {
+  --text-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--text-opacity));
+}
+
+.text-teal-200 {
+  --text-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--text-opacity));
+}
+
+.text-teal-300 {
+  --text-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--text-opacity));
+}
+
+.text-teal-400 {
+  --text-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--text-opacity));
+}
+
+.text-teal-500 {
+  --text-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--text-opacity));
+}
+
+.text-teal-600 {
+  --text-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--text-opacity));
+}
+
+.text-teal-700 {
+  --text-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--text-opacity));
+}
+
+.text-teal-800 {
+  --text-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--text-opacity));
+}
+
+.text-teal-900 {
+  --text-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--text-opacity));
+}
+
+.text-blue-100 {
+  --text-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--text-opacity));
+}
+
+.text-blue-200 {
+  --text-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--text-opacity));
+}
+
+.text-blue-300 {
+  --text-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--text-opacity));
+}
+
+.text-blue-400 {
+  --text-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--text-opacity));
+}
+
+.text-blue-500 {
+  --text-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--text-opacity));
+}
+
+.text-blue-600 {
+  --text-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--text-opacity));
+}
+
+.text-blue-700 {
+  --text-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--text-opacity));
+}
+
+.text-blue-800 {
+  --text-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--text-opacity));
+}
+
+.text-blue-900 {
+  --text-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--text-opacity));
+}
+
+.text-indigo-100 {
+  --text-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--text-opacity));
+}
+
+.text-indigo-200 {
+  --text-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--text-opacity));
+}
+
+.text-indigo-300 {
+  --text-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--text-opacity));
+}
+
+.text-indigo-400 {
+  --text-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--text-opacity));
+}
+
+.text-indigo-500 {
+  --text-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--text-opacity));
+}
+
+.text-indigo-600 {
+  --text-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--text-opacity));
+}
+
+.text-indigo-700 {
+  --text-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--text-opacity));
+}
+
+.text-indigo-800 {
+  --text-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--text-opacity));
+}
+
+.text-indigo-900 {
+  --text-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--text-opacity));
+}
+
+.text-purple-100 {
+  --text-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--text-opacity));
+}
+
+.text-purple-200 {
+  --text-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--text-opacity));
+}
+
+.text-purple-300 {
+  --text-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--text-opacity));
+}
+
+.text-purple-400 {
+  --text-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--text-opacity));
+}
+
+.text-purple-500 {
+  --text-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--text-opacity));
+}
+
+.text-purple-600 {
+  --text-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--text-opacity));
+}
+
+.text-purple-700 {
+  --text-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--text-opacity));
+}
+
+.text-purple-800 {
+  --text-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--text-opacity));
+}
+
+.text-purple-900 {
+  --text-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--text-opacity));
+}
+
+.text-pink-100 {
+  --text-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--text-opacity));
+}
+
+.text-pink-200 {
+  --text-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--text-opacity));
+}
+
+.text-pink-300 {
+  --text-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--text-opacity));
+}
+
+.text-pink-400 {
+  --text-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--text-opacity));
+}
+
+.text-pink-500 {
+  --text-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--text-opacity));
+}
+
+.text-pink-600 {
+  --text-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--text-opacity));
+}
+
+.text-pink-700 {
+  --text-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--text-opacity));
+}
+
+.text-pink-800 {
+  --text-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--text-opacity));
+}
+
+.text-pink-900 {
+  --text-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--text-opacity));
+}
+
+.hover\:text-transparent:hover {
+  color: transparent;
+}
+
+.hover\:text-current:hover {
+  color: currentColor;
+}
+
+.hover\:text-black:hover {
+  --text-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--text-opacity));
+}
+
+.hover\:text-white:hover {
+  --text-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--text-opacity));
+}
+
+.hover\:text-gray-100:hover {
+  --text-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--text-opacity));
+}
+
+.hover\:text-gray-200:hover {
+  --text-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--text-opacity));
+}
+
+.hover\:text-gray-300:hover {
+  --text-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--text-opacity));
+}
+
+.hover\:text-gray-400:hover {
+  --text-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--text-opacity));
+}
+
+.hover\:text-gray-500:hover {
+  --text-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--text-opacity));
+}
+
+.hover\:text-gray-600:hover {
+  --text-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--text-opacity));
+}
+
+.hover\:text-gray-700:hover {
+  --text-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--text-opacity));
+}
+
+.hover\:text-gray-800:hover {
+  --text-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--text-opacity));
+}
+
+.hover\:text-gray-900:hover {
+  --text-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--text-opacity));
+}
+
+.hover\:text-red-100:hover {
+  --text-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--text-opacity));
+}
+
+.hover\:text-red-200:hover {
+  --text-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--text-opacity));
+}
+
+.hover\:text-red-300:hover {
+  --text-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--text-opacity));
+}
+
+.hover\:text-red-400:hover {
+  --text-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--text-opacity));
+}
+
+.hover\:text-red-500:hover {
+  --text-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--text-opacity));
+}
+
+.hover\:text-red-600:hover {
+  --text-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--text-opacity));
+}
+
+.hover\:text-red-700:hover {
+  --text-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--text-opacity));
+}
+
+.hover\:text-red-800:hover {
+  --text-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--text-opacity));
+}
+
+.hover\:text-red-900:hover {
+  --text-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--text-opacity));
+}
+
+.hover\:text-orange-100:hover {
+  --text-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--text-opacity));
+}
+
+.hover\:text-orange-200:hover {
+  --text-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--text-opacity));
+}
+
+.hover\:text-orange-300:hover {
+  --text-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--text-opacity));
+}
+
+.hover\:text-orange-400:hover {
+  --text-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--text-opacity));
+}
+
+.hover\:text-orange-500:hover {
+  --text-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--text-opacity));
+}
+
+.hover\:text-orange-600:hover {
+  --text-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--text-opacity));
+}
+
+.hover\:text-orange-700:hover {
+  --text-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--text-opacity));
+}
+
+.hover\:text-orange-800:hover {
+  --text-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--text-opacity));
+}
+
+.hover\:text-orange-900:hover {
+  --text-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--text-opacity));
+}
+
+.hover\:text-yellow-100:hover {
+  --text-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--text-opacity));
+}
+
+.hover\:text-yellow-200:hover {
+  --text-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--text-opacity));
+}
+
+.hover\:text-yellow-300:hover {
+  --text-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--text-opacity));
+}
+
+.hover\:text-yellow-400:hover {
+  --text-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--text-opacity));
+}
+
+.hover\:text-yellow-500:hover {
+  --text-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--text-opacity));
+}
+
+.hover\:text-yellow-600:hover {
+  --text-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--text-opacity));
+}
+
+.hover\:text-yellow-700:hover {
+  --text-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--text-opacity));
+}
+
+.hover\:text-yellow-800:hover {
+  --text-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--text-opacity));
+}
+
+.hover\:text-yellow-900:hover {
+  --text-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--text-opacity));
+}
+
+.hover\:text-green-100:hover {
+  --text-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--text-opacity));
+}
+
+.hover\:text-green-200:hover {
+  --text-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--text-opacity));
+}
+
+.hover\:text-green-300:hover {
+  --text-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--text-opacity));
+}
+
+.hover\:text-green-400:hover {
+  --text-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--text-opacity));
+}
+
+.hover\:text-green-500:hover {
+  --text-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--text-opacity));
+}
+
+.hover\:text-green-600:hover {
+  --text-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--text-opacity));
+}
+
+.hover\:text-green-700:hover {
+  --text-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--text-opacity));
+}
+
+.hover\:text-green-800:hover {
+  --text-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--text-opacity));
+}
+
+.hover\:text-green-900:hover {
+  --text-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--text-opacity));
+}
+
+.hover\:text-teal-100:hover {
+  --text-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--text-opacity));
+}
+
+.hover\:text-teal-200:hover {
+  --text-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--text-opacity));
+}
+
+.hover\:text-teal-300:hover {
+  --text-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--text-opacity));
+}
+
+.hover\:text-teal-400:hover {
+  --text-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--text-opacity));
+}
+
+.hover\:text-teal-500:hover {
+  --text-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--text-opacity));
+}
+
+.hover\:text-teal-600:hover {
+  --text-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--text-opacity));
+}
+
+.hover\:text-teal-700:hover {
+  --text-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--text-opacity));
+}
+
+.hover\:text-teal-800:hover {
+  --text-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--text-opacity));
+}
+
+.hover\:text-teal-900:hover {
+  --text-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--text-opacity));
+}
+
+.hover\:text-blue-100:hover {
+  --text-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--text-opacity));
+}
+
+.hover\:text-blue-200:hover {
+  --text-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--text-opacity));
+}
+
+.hover\:text-blue-300:hover {
+  --text-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--text-opacity));
+}
+
+.hover\:text-blue-400:hover {
+  --text-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--text-opacity));
+}
+
+.hover\:text-blue-500:hover {
+  --text-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--text-opacity));
+}
+
+.hover\:text-blue-600:hover {
+  --text-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--text-opacity));
+}
+
+.hover\:text-blue-700:hover {
+  --text-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--text-opacity));
+}
+
+.hover\:text-blue-800:hover {
+  --text-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--text-opacity));
+}
+
+.hover\:text-blue-900:hover {
+  --text-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--text-opacity));
+}
+
+.hover\:text-indigo-100:hover {
+  --text-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--text-opacity));
+}
+
+.hover\:text-indigo-200:hover {
+  --text-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--text-opacity));
+}
+
+.hover\:text-indigo-300:hover {
+  --text-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--text-opacity));
+}
+
+.hover\:text-indigo-400:hover {
+  --text-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--text-opacity));
+}
+
+.hover\:text-indigo-500:hover {
+  --text-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--text-opacity));
+}
+
+.hover\:text-indigo-600:hover {
+  --text-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--text-opacity));
+}
+
+.hover\:text-indigo-700:hover {
+  --text-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--text-opacity));
+}
+
+.hover\:text-indigo-800:hover {
+  --text-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--text-opacity));
+}
+
+.hover\:text-indigo-900:hover {
+  --text-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--text-opacity));
+}
+
+.hover\:text-purple-100:hover {
+  --text-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--text-opacity));
+}
+
+.hover\:text-purple-200:hover {
+  --text-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--text-opacity));
+}
+
+.hover\:text-purple-300:hover {
+  --text-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--text-opacity));
+}
+
+.hover\:text-purple-400:hover {
+  --text-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--text-opacity));
+}
+
+.hover\:text-purple-500:hover {
+  --text-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--text-opacity));
+}
+
+.hover\:text-purple-600:hover {
+  --text-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--text-opacity));
+}
+
+.hover\:text-purple-700:hover {
+  --text-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--text-opacity));
+}
+
+.hover\:text-purple-800:hover {
+  --text-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--text-opacity));
+}
+
+.hover\:text-purple-900:hover {
+  --text-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--text-opacity));
+}
+
+.hover\:text-pink-100:hover {
+  --text-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--text-opacity));
+}
+
+.hover\:text-pink-200:hover {
+  --text-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--text-opacity));
+}
+
+.hover\:text-pink-300:hover {
+  --text-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--text-opacity));
+}
+
+.hover\:text-pink-400:hover {
+  --text-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--text-opacity));
+}
+
+.hover\:text-pink-500:hover {
+  --text-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--text-opacity));
+}
+
+.hover\:text-pink-600:hover {
+  --text-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--text-opacity));
+}
+
+.hover\:text-pink-700:hover {
+  --text-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--text-opacity));
+}
+
+.hover\:text-pink-800:hover {
+  --text-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--text-opacity));
+}
+
+.hover\:text-pink-900:hover {
+  --text-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--text-opacity));
+}
+
+.focus\:text-transparent:focus {
+  color: transparent;
+}
+
+.focus\:text-current:focus {
+  color: currentColor;
+}
+
+.focus\:text-black:focus {
+  --text-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--text-opacity));
+}
+
+.focus\:text-white:focus {
+  --text-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--text-opacity));
+}
+
+.focus\:text-gray-100:focus {
+  --text-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--text-opacity));
+}
+
+.focus\:text-gray-200:focus {
+  --text-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--text-opacity));
+}
+
+.focus\:text-gray-300:focus {
+  --text-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--text-opacity));
+}
+
+.focus\:text-gray-400:focus {
+  --text-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--text-opacity));
+}
+
+.focus\:text-gray-500:focus {
+  --text-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--text-opacity));
+}
+
+.focus\:text-gray-600:focus {
+  --text-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--text-opacity));
+}
+
+.focus\:text-gray-700:focus {
+  --text-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--text-opacity));
+}
+
+.focus\:text-gray-800:focus {
+  --text-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--text-opacity));
+}
+
+.focus\:text-gray-900:focus {
+  --text-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--text-opacity));
+}
+
+.focus\:text-red-100:focus {
+  --text-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--text-opacity));
+}
+
+.focus\:text-red-200:focus {
+  --text-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--text-opacity));
+}
+
+.focus\:text-red-300:focus {
+  --text-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--text-opacity));
+}
+
+.focus\:text-red-400:focus {
+  --text-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--text-opacity));
+}
+
+.focus\:text-red-500:focus {
+  --text-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--text-opacity));
+}
+
+.focus\:text-red-600:focus {
+  --text-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--text-opacity));
+}
+
+.focus\:text-red-700:focus {
+  --text-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--text-opacity));
+}
+
+.focus\:text-red-800:focus {
+  --text-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--text-opacity));
+}
+
+.focus\:text-red-900:focus {
+  --text-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--text-opacity));
+}
+
+.focus\:text-orange-100:focus {
+  --text-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--text-opacity));
+}
+
+.focus\:text-orange-200:focus {
+  --text-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--text-opacity));
+}
+
+.focus\:text-orange-300:focus {
+  --text-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--text-opacity));
+}
+
+.focus\:text-orange-400:focus {
+  --text-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--text-opacity));
+}
+
+.focus\:text-orange-500:focus {
+  --text-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--text-opacity));
+}
+
+.focus\:text-orange-600:focus {
+  --text-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--text-opacity));
+}
+
+.focus\:text-orange-700:focus {
+  --text-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--text-opacity));
+}
+
+.focus\:text-orange-800:focus {
+  --text-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--text-opacity));
+}
+
+.focus\:text-orange-900:focus {
+  --text-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--text-opacity));
+}
+
+.focus\:text-yellow-100:focus {
+  --text-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--text-opacity));
+}
+
+.focus\:text-yellow-200:focus {
+  --text-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--text-opacity));
+}
+
+.focus\:text-yellow-300:focus {
+  --text-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--text-opacity));
+}
+
+.focus\:text-yellow-400:focus {
+  --text-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--text-opacity));
+}
+
+.focus\:text-yellow-500:focus {
+  --text-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--text-opacity));
+}
+
+.focus\:text-yellow-600:focus {
+  --text-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--text-opacity));
+}
+
+.focus\:text-yellow-700:focus {
+  --text-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--text-opacity));
+}
+
+.focus\:text-yellow-800:focus {
+  --text-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--text-opacity));
+}
+
+.focus\:text-yellow-900:focus {
+  --text-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--text-opacity));
+}
+
+.focus\:text-green-100:focus {
+  --text-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--text-opacity));
+}
+
+.focus\:text-green-200:focus {
+  --text-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--text-opacity));
+}
+
+.focus\:text-green-300:focus {
+  --text-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--text-opacity));
+}
+
+.focus\:text-green-400:focus {
+  --text-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--text-opacity));
+}
+
+.focus\:text-green-500:focus {
+  --text-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--text-opacity));
+}
+
+.focus\:text-green-600:focus {
+  --text-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--text-opacity));
+}
+
+.focus\:text-green-700:focus {
+  --text-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--text-opacity));
+}
+
+.focus\:text-green-800:focus {
+  --text-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--text-opacity));
+}
+
+.focus\:text-green-900:focus {
+  --text-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--text-opacity));
+}
+
+.focus\:text-teal-100:focus {
+  --text-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--text-opacity));
+}
+
+.focus\:text-teal-200:focus {
+  --text-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--text-opacity));
+}
+
+.focus\:text-teal-300:focus {
+  --text-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--text-opacity));
+}
+
+.focus\:text-teal-400:focus {
+  --text-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--text-opacity));
+}
+
+.focus\:text-teal-500:focus {
+  --text-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--text-opacity));
+}
+
+.focus\:text-teal-600:focus {
+  --text-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--text-opacity));
+}
+
+.focus\:text-teal-700:focus {
+  --text-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--text-opacity));
+}
+
+.focus\:text-teal-800:focus {
+  --text-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--text-opacity));
+}
+
+.focus\:text-teal-900:focus {
+  --text-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--text-opacity));
+}
+
+.focus\:text-blue-100:focus {
+  --text-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--text-opacity));
+}
+
+.focus\:text-blue-200:focus {
+  --text-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--text-opacity));
+}
+
+.focus\:text-blue-300:focus {
+  --text-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--text-opacity));
+}
+
+.focus\:text-blue-400:focus {
+  --text-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--text-opacity));
+}
+
+.focus\:text-blue-500:focus {
+  --text-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--text-opacity));
+}
+
+.focus\:text-blue-600:focus {
+  --text-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--text-opacity));
+}
+
+.focus\:text-blue-700:focus {
+  --text-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--text-opacity));
+}
+
+.focus\:text-blue-800:focus {
+  --text-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--text-opacity));
+}
+
+.focus\:text-blue-900:focus {
+  --text-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--text-opacity));
+}
+
+.focus\:text-indigo-100:focus {
+  --text-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--text-opacity));
+}
+
+.focus\:text-indigo-200:focus {
+  --text-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--text-opacity));
+}
+
+.focus\:text-indigo-300:focus {
+  --text-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--text-opacity));
+}
+
+.focus\:text-indigo-400:focus {
+  --text-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--text-opacity));
+}
+
+.focus\:text-indigo-500:focus {
+  --text-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--text-opacity));
+}
+
+.focus\:text-indigo-600:focus {
+  --text-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--text-opacity));
+}
+
+.focus\:text-indigo-700:focus {
+  --text-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--text-opacity));
+}
+
+.focus\:text-indigo-800:focus {
+  --text-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--text-opacity));
+}
+
+.focus\:text-indigo-900:focus {
+  --text-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--text-opacity));
+}
+
+.focus\:text-purple-100:focus {
+  --text-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--text-opacity));
+}
+
+.focus\:text-purple-200:focus {
+  --text-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--text-opacity));
+}
+
+.focus\:text-purple-300:focus {
+  --text-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--text-opacity));
+}
+
+.focus\:text-purple-400:focus {
+  --text-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--text-opacity));
+}
+
+.focus\:text-purple-500:focus {
+  --text-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--text-opacity));
+}
+
+.focus\:text-purple-600:focus {
+  --text-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--text-opacity));
+}
+
+.focus\:text-purple-700:focus {
+  --text-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--text-opacity));
+}
+
+.focus\:text-purple-800:focus {
+  --text-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--text-opacity));
+}
+
+.focus\:text-purple-900:focus {
+  --text-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--text-opacity));
+}
+
+.focus\:text-pink-100:focus {
+  --text-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--text-opacity));
+}
+
+.focus\:text-pink-200:focus {
+  --text-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--text-opacity));
+}
+
+.focus\:text-pink-300:focus {
+  --text-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--text-opacity));
+}
+
+.focus\:text-pink-400:focus {
+  --text-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--text-opacity));
+}
+
+.focus\:text-pink-500:focus {
+  --text-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--text-opacity));
+}
+
+.focus\:text-pink-600:focus {
+  --text-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--text-opacity));
+}
+
+.focus\:text-pink-700:focus {
+  --text-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--text-opacity));
+}
+
+.focus\:text-pink-800:focus {
+  --text-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--text-opacity));
+}
+
+.focus\:text-pink-900:focus {
+  --text-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--text-opacity));
+}
+
+.text-opacity-0 {
+  --text-opacity: 0;
+}
+
+.text-opacity-25 {
+  --text-opacity: 0.25;
+}
+
+.text-opacity-50 {
+  --text-opacity: 0.5;
+}
+
+.text-opacity-75 {
+  --text-opacity: 0.75;
+}
+
+.text-opacity-100 {
+  --text-opacity: 1;
+}
+
+.hover\:text-opacity-0:hover {
+  --text-opacity: 0;
+}
+
+.hover\:text-opacity-25:hover {
+  --text-opacity: 0.25;
+}
+
+.hover\:text-opacity-50:hover {
+  --text-opacity: 0.5;
+}
+
+.hover\:text-opacity-75:hover {
+  --text-opacity: 0.75;
+}
+
+.hover\:text-opacity-100:hover {
+  --text-opacity: 1;
+}
+
+.focus\:text-opacity-0:focus {
+  --text-opacity: 0;
+}
+
+.focus\:text-opacity-25:focus {
+  --text-opacity: 0.25;
+}
+
+.focus\:text-opacity-50:focus {
+  --text-opacity: 0.5;
+}
+
+.focus\:text-opacity-75:focus {
+  --text-opacity: 0.75;
+}
+
+.focus\:text-opacity-100:focus {
+  --text-opacity: 1;
+}
+
+.italic {
+  font-style: italic;
+}
+
+.not-italic {
+  font-style: normal;
+}
+
+.uppercase {
+  text-transform: uppercase;
+}
+
+.lowercase {
+  text-transform: lowercase;
+}
+
+.capitalize {
+  text-transform: capitalize;
+}
+
+.normal-case {
+  text-transform: none;
+}
+
+.underline {
+  text-decoration: underline;
+}
+
+.line-through {
+  text-decoration: line-through;
+}
+
+.no-underline {
+  text-decoration: none;
+}
+
+.hover\:underline:hover {
+  text-decoration: underline;
+}
+
+.hover\:line-through:hover {
+  text-decoration: line-through;
+}
+
+.hover\:no-underline:hover {
+  text-decoration: none;
+}
+
+.focus\:underline:focus {
+  text-decoration: underline;
+}
+
+.focus\:line-through:focus {
+  text-decoration: line-through;
+}
+
+.focus\:no-underline:focus {
+  text-decoration: none;
+}
+
+.antialiased {
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.subpixel-antialiased {
+  -webkit-font-smoothing: auto;
+  -moz-osx-font-smoothing: auto;
+}
+
+.ordinal, .slashed-zero, .lining-nums, .oldstyle-nums, .proportional-nums, .tabular-nums, .diagonal-fractions, .stacked-fractions {
+  --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+  --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+  --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+  --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+  --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+  font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+}
+
+.normal-nums {
+  font-variant-numeric: normal;
+}
+
+.ordinal {
+  --font-variant-numeric-ordinal: ordinal;
+}
+
+.slashed-zero {
+  --font-variant-numeric-slashed-zero: slashed-zero;
+}
+
+.lining-nums {
+  --font-variant-numeric-figure: lining-nums;
+}
+
+.oldstyle-nums {
+  --font-variant-numeric-figure: oldstyle-nums;
+}
+
+.proportional-nums {
+  --font-variant-numeric-spacing: proportional-nums;
+}
+
+.tabular-nums {
+  --font-variant-numeric-spacing: tabular-nums;
+}
+
+.diagonal-fractions {
+  --font-variant-numeric-fraction: diagonal-fractions;
+}
+
+.stacked-fractions {
+  --font-variant-numeric-fraction: stacked-fractions;
+}
+
+.tracking-tighter {
+  letter-spacing: -0.05em;
+}
+
+.tracking-tight {
+  letter-spacing: -0.025em;
+}
+
+.tracking-normal {
+  letter-spacing: 0;
+}
+
+.tracking-wide {
+  letter-spacing: 0.025em;
+}
+
+.tracking-wider {
+  letter-spacing: 0.05em;
+}
+
+.tracking-widest {
+  letter-spacing: 0.1em;
+}
+
+.select-none {
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+
+.select-text {
+  -webkit-user-select: text;
+     -moz-user-select: text;
+      -ms-user-select: text;
+          user-select: text;
+}
+
+.select-all {
+  -webkit-user-select: all;
+     -moz-user-select: all;
+      -ms-user-select: all;
+          user-select: all;
+}
+
+.select-auto {
+  -webkit-user-select: auto;
+     -moz-user-select: auto;
+      -ms-user-select: auto;
+          user-select: auto;
+}
+
+.align-baseline {
+  vertical-align: baseline;
+}
+
+.align-top {
+  vertical-align: top;
+}
+
+.align-middle {
+  vertical-align: middle;
+}
+
+.align-bottom {
+  vertical-align: bottom;
+}
+
+.align-text-top {
+  vertical-align: text-top;
+}
+
+.align-text-bottom {
+  vertical-align: text-bottom;
+}
+
+.visible {
+  visibility: visible;
+}
+
+.invisible {
+  visibility: hidden;
+}
+
+.whitespace-normal {
+  white-space: normal;
+}
+
+.whitespace-no-wrap {
+  white-space: nowrap;
+}
+
+.whitespace-pre {
+  white-space: pre;
+}
+
+.whitespace-pre-line {
+  white-space: pre-line;
+}
+
+.whitespace-pre-wrap {
+  white-space: pre-wrap;
+}
+
+.break-normal {
+  overflow-wrap: normal;
+  word-break: normal;
+}
+
+.break-words {
+  overflow-wrap: break-word;
+}
+
+.break-all {
+  word-break: break-all;
+}
+
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.w-0 {
+  width: 0;
+}
+
+.w-1 {
+  width: 0.25rem;
+}
+
+.w-2 {
+  width: 0.5rem;
+}
+
+.w-3 {
+  width: 0.75rem;
+}
+
+.w-4 {
+  width: 1rem;
+}
+
+.w-5 {
+  width: 1.25rem;
+}
+
+.w-6 {
+  width: 1.5rem;
+}
+
+.w-8 {
+  width: 2rem;
+}
+
+.w-10 {
+  width: 2.5rem;
+}
+
+.w-12 {
+  width: 3rem;
+}
+
+.w-16 {
+  width: 4rem;
+}
+
+.w-20 {
+  width: 5rem;
+}
+
+.w-24 {
+  width: 6rem;
+}
+
+.w-32 {
+  width: 8rem;
+}
+
+.w-40 {
+  width: 10rem;
+}
+
+.w-48 {
+  width: 12rem;
+}
+
+.w-56 {
+  width: 14rem;
+}
+
+.w-64 {
+  width: 16rem;
+}
+
+.w-auto {
+  width: auto;
+}
+
+.w-px {
+  width: 1px;
+}
+
+.w-1\/2 {
+  width: 50%;
+}
+
+.w-1\/3 {
+  width: 33.333333%;
+}
+
+.w-2\/3 {
+  width: 66.666667%;
+}
+
+.w-1\/4 {
+  width: 25%;
+}
+
+.w-2\/4 {
+  width: 50%;
+}
+
+.w-3\/4 {
+  width: 75%;
+}
+
+.w-1\/5 {
+  width: 20%;
+}
+
+.w-2\/5 {
+  width: 40%;
+}
+
+.w-3\/5 {
+  width: 60%;
+}
+
+.w-4\/5 {
+  width: 80%;
+}
+
+.w-1\/6 {
+  width: 16.666667%;
+}
+
+.w-2\/6 {
+  width: 33.333333%;
+}
+
+.w-3\/6 {
+  width: 50%;
+}
+
+.w-4\/6 {
+  width: 66.666667%;
+}
+
+.w-5\/6 {
+  width: 83.333333%;
+}
+
+.w-1\/12 {
+  width: 8.333333%;
+}
+
+.w-2\/12 {
+  width: 16.666667%;
+}
+
+.w-3\/12 {
+  width: 25%;
+}
+
+.w-4\/12 {
+  width: 33.333333%;
+}
+
+.w-5\/12 {
+  width: 41.666667%;
+}
+
+.w-6\/12 {
+  width: 50%;
+}
+
+.w-7\/12 {
+  width: 58.333333%;
+}
+
+.w-8\/12 {
+  width: 66.666667%;
+}
+
+.w-9\/12 {
+  width: 75%;
+}
+
+.w-10\/12 {
+  width: 83.333333%;
+}
+
+.w-11\/12 {
+  width: 91.666667%;
+}
+
+.w-full {
+  width: 100%;
+}
+
+.w-screen {
+  width: 100vw;
+}
+
+.z-0 {
+  z-index: 0;
+}
+
+.z-10 {
+  z-index: 10;
+}
+
+.z-20 {
+  z-index: 20;
+}
+
+.z-30 {
+  z-index: 30;
+}
+
+.z-40 {
+  z-index: 40;
+}
+
+.z-50 {
+  z-index: 50;
+}
+
+.z-auto {
+  z-index: auto;
+}
+
+.gap-0 {
+  grid-gap: 0;
+  gap: 0;
+}
+
+.gap-1 {
+  grid-gap: 0.25rem;
+  gap: 0.25rem;
+}
+
+.gap-2 {
+  grid-gap: 0.5rem;
+  gap: 0.5rem;
+}
+
+.gap-3 {
+  grid-gap: 0.75rem;
+  gap: 0.75rem;
+}
+
+.gap-4 {
+  grid-gap: 1rem;
+  gap: 1rem;
+}
+
+.gap-5 {
+  grid-gap: 1.25rem;
+  gap: 1.25rem;
+}
+
+.gap-6 {
+  grid-gap: 1.5rem;
+  gap: 1.5rem;
+}
+
+.gap-8 {
+  grid-gap: 2rem;
+  gap: 2rem;
+}
+
+.gap-10 {
+  grid-gap: 2.5rem;
+  gap: 2.5rem;
+}
+
+.gap-12 {
+  grid-gap: 3rem;
+  gap: 3rem;
+}
+
+.gap-16 {
+  grid-gap: 4rem;
+  gap: 4rem;
+}
+
+.gap-20 {
+  grid-gap: 5rem;
+  gap: 5rem;
+}
+
+.gap-24 {
+  grid-gap: 6rem;
+  gap: 6rem;
+}
+
+.gap-32 {
+  grid-gap: 8rem;
+  gap: 8rem;
+}
+
+.gap-40 {
+  grid-gap: 10rem;
+  gap: 10rem;
+}
+
+.gap-48 {
+  grid-gap: 12rem;
+  gap: 12rem;
+}
+
+.gap-56 {
+  grid-gap: 14rem;
+  gap: 14rem;
+}
+
+.gap-64 {
+  grid-gap: 16rem;
+  gap: 16rem;
+}
+
+.gap-px {
+  grid-gap: 1px;
+  gap: 1px;
+}
+
+.col-gap-0 {
+  grid-column-gap: 0;
+  -moz-column-gap: 0;
+       column-gap: 0;
+}
+
+.col-gap-1 {
+  grid-column-gap: 0.25rem;
+  -moz-column-gap: 0.25rem;
+       column-gap: 0.25rem;
+}
+
+.col-gap-2 {
+  grid-column-gap: 0.5rem;
+  -moz-column-gap: 0.5rem;
+       column-gap: 0.5rem;
+}
+
+.col-gap-3 {
+  grid-column-gap: 0.75rem;
+  -moz-column-gap: 0.75rem;
+       column-gap: 0.75rem;
+}
+
+.col-gap-4 {
+  grid-column-gap: 1rem;
+  -moz-column-gap: 1rem;
+       column-gap: 1rem;
+}
+
+.col-gap-5 {
+  grid-column-gap: 1.25rem;
+  -moz-column-gap: 1.25rem;
+       column-gap: 1.25rem;
+}
+
+.col-gap-6 {
+  grid-column-gap: 1.5rem;
+  -moz-column-gap: 1.5rem;
+       column-gap: 1.5rem;
+}
+
+.col-gap-8 {
+  grid-column-gap: 2rem;
+  -moz-column-gap: 2rem;
+       column-gap: 2rem;
+}
+
+.col-gap-10 {
+  grid-column-gap: 2.5rem;
+  -moz-column-gap: 2.5rem;
+       column-gap: 2.5rem;
+}
+
+.col-gap-12 {
+  grid-column-gap: 3rem;
+  -moz-column-gap: 3rem;
+       column-gap: 3rem;
+}
+
+.col-gap-16 {
+  grid-column-gap: 4rem;
+  -moz-column-gap: 4rem;
+       column-gap: 4rem;
+}
+
+.col-gap-20 {
+  grid-column-gap: 5rem;
+  -moz-column-gap: 5rem;
+       column-gap: 5rem;
+}
+
+.col-gap-24 {
+  grid-column-gap: 6rem;
+  -moz-column-gap: 6rem;
+       column-gap: 6rem;
+}
+
+.col-gap-32 {
+  grid-column-gap: 8rem;
+  -moz-column-gap: 8rem;
+       column-gap: 8rem;
+}
+
+.col-gap-40 {
+  grid-column-gap: 10rem;
+  -moz-column-gap: 10rem;
+       column-gap: 10rem;
+}
+
+.col-gap-48 {
+  grid-column-gap: 12rem;
+  -moz-column-gap: 12rem;
+       column-gap: 12rem;
+}
+
+.col-gap-56 {
+  grid-column-gap: 14rem;
+  -moz-column-gap: 14rem;
+       column-gap: 14rem;
+}
+
+.col-gap-64 {
+  grid-column-gap: 16rem;
+  -moz-column-gap: 16rem;
+       column-gap: 16rem;
+}
+
+.col-gap-px {
+  grid-column-gap: 1px;
+  -moz-column-gap: 1px;
+       column-gap: 1px;
+}
+
+.gap-x-0 {
+  grid-column-gap: 0;
+  -moz-column-gap: 0;
+       column-gap: 0;
+}
+
+.gap-x-1 {
+  grid-column-gap: 0.25rem;
+  -moz-column-gap: 0.25rem;
+       column-gap: 0.25rem;
+}
+
+.gap-x-2 {
+  grid-column-gap: 0.5rem;
+  -moz-column-gap: 0.5rem;
+       column-gap: 0.5rem;
+}
+
+.gap-x-3 {
+  grid-column-gap: 0.75rem;
+  -moz-column-gap: 0.75rem;
+       column-gap: 0.75rem;
+}
+
+.gap-x-4 {
+  grid-column-gap: 1rem;
+  -moz-column-gap: 1rem;
+       column-gap: 1rem;
+}
+
+.gap-x-5 {
+  grid-column-gap: 1.25rem;
+  -moz-column-gap: 1.25rem;
+       column-gap: 1.25rem;
+}
+
+.gap-x-6 {
+  grid-column-gap: 1.5rem;
+  -moz-column-gap: 1.5rem;
+       column-gap: 1.5rem;
+}
+
+.gap-x-8 {
+  grid-column-gap: 2rem;
+  -moz-column-gap: 2rem;
+       column-gap: 2rem;
+}
+
+.gap-x-10 {
+  grid-column-gap: 2.5rem;
+  -moz-column-gap: 2.5rem;
+       column-gap: 2.5rem;
+}
+
+.gap-x-12 {
+  grid-column-gap: 3rem;
+  -moz-column-gap: 3rem;
+       column-gap: 3rem;
+}
+
+.gap-x-16 {
+  grid-column-gap: 4rem;
+  -moz-column-gap: 4rem;
+       column-gap: 4rem;
+}
+
+.gap-x-20 {
+  grid-column-gap: 5rem;
+  -moz-column-gap: 5rem;
+       column-gap: 5rem;
+}
+
+.gap-x-24 {
+  grid-column-gap: 6rem;
+  -moz-column-gap: 6rem;
+       column-gap: 6rem;
+}
+
+.gap-x-32 {
+  grid-column-gap: 8rem;
+  -moz-column-gap: 8rem;
+       column-gap: 8rem;
+}
+
+.gap-x-40 {
+  grid-column-gap: 10rem;
+  -moz-column-gap: 10rem;
+       column-gap: 10rem;
+}
+
+.gap-x-48 {
+  grid-column-gap: 12rem;
+  -moz-column-gap: 12rem;
+       column-gap: 12rem;
+}
+
+.gap-x-56 {
+  grid-column-gap: 14rem;
+  -moz-column-gap: 14rem;
+       column-gap: 14rem;
+}
+
+.gap-x-64 {
+  grid-column-gap: 16rem;
+  -moz-column-gap: 16rem;
+       column-gap: 16rem;
+}
+
+.gap-x-px {
+  grid-column-gap: 1px;
+  -moz-column-gap: 1px;
+       column-gap: 1px;
+}
+
+.row-gap-0 {
+  grid-row-gap: 0;
+  row-gap: 0;
+}
+
+.row-gap-1 {
+  grid-row-gap: 0.25rem;
+  row-gap: 0.25rem;
+}
+
+.row-gap-2 {
+  grid-row-gap: 0.5rem;
+  row-gap: 0.5rem;
+}
+
+.row-gap-3 {
+  grid-row-gap: 0.75rem;
+  row-gap: 0.75rem;
+}
+
+.row-gap-4 {
+  grid-row-gap: 1rem;
+  row-gap: 1rem;
+}
+
+.row-gap-5 {
+  grid-row-gap: 1.25rem;
+  row-gap: 1.25rem;
+}
+
+.row-gap-6 {
+  grid-row-gap: 1.5rem;
+  row-gap: 1.5rem;
+}
+
+.row-gap-8 {
+  grid-row-gap: 2rem;
+  row-gap: 2rem;
+}
+
+.row-gap-10 {
+  grid-row-gap: 2.5rem;
+  row-gap: 2.5rem;
+}
+
+.row-gap-12 {
+  grid-row-gap: 3rem;
+  row-gap: 3rem;
+}
+
+.row-gap-16 {
+  grid-row-gap: 4rem;
+  row-gap: 4rem;
+}
+
+.row-gap-20 {
+  grid-row-gap: 5rem;
+  row-gap: 5rem;
+}
+
+.row-gap-24 {
+  grid-row-gap: 6rem;
+  row-gap: 6rem;
+}
+
+.row-gap-32 {
+  grid-row-gap: 8rem;
+  row-gap: 8rem;
+}
+
+.row-gap-40 {
+  grid-row-gap: 10rem;
+  row-gap: 10rem;
+}
+
+.row-gap-48 {
+  grid-row-gap: 12rem;
+  row-gap: 12rem;
+}
+
+.row-gap-56 {
+  grid-row-gap: 14rem;
+  row-gap: 14rem;
+}
+
+.row-gap-64 {
+  grid-row-gap: 16rem;
+  row-gap: 16rem;
+}
+
+.row-gap-px {
+  grid-row-gap: 1px;
+  row-gap: 1px;
+}
+
+.gap-y-0 {
+  grid-row-gap: 0;
+  row-gap: 0;
+}
+
+.gap-y-1 {
+  grid-row-gap: 0.25rem;
+  row-gap: 0.25rem;
+}
+
+.gap-y-2 {
+  grid-row-gap: 0.5rem;
+  row-gap: 0.5rem;
+}
+
+.gap-y-3 {
+  grid-row-gap: 0.75rem;
+  row-gap: 0.75rem;
+}
+
+.gap-y-4 {
+  grid-row-gap: 1rem;
+  row-gap: 1rem;
+}
+
+.gap-y-5 {
+  grid-row-gap: 1.25rem;
+  row-gap: 1.25rem;
+}
+
+.gap-y-6 {
+  grid-row-gap: 1.5rem;
+  row-gap: 1.5rem;
+}
+
+.gap-y-8 {
+  grid-row-gap: 2rem;
+  row-gap: 2rem;
+}
+
+.gap-y-10 {
+  grid-row-gap: 2.5rem;
+  row-gap: 2.5rem;
+}
+
+.gap-y-12 {
+  grid-row-gap: 3rem;
+  row-gap: 3rem;
+}
+
+.gap-y-16 {
+  grid-row-gap: 4rem;
+  row-gap: 4rem;
+}
+
+.gap-y-20 {
+  grid-row-gap: 5rem;
+  row-gap: 5rem;
+}
+
+.gap-y-24 {
+  grid-row-gap: 6rem;
+  row-gap: 6rem;
+}
+
+.gap-y-32 {
+  grid-row-gap: 8rem;
+  row-gap: 8rem;
+}
+
+.gap-y-40 {
+  grid-row-gap: 10rem;
+  row-gap: 10rem;
+}
+
+.gap-y-48 {
+  grid-row-gap: 12rem;
+  row-gap: 12rem;
+}
+
+.gap-y-56 {
+  grid-row-gap: 14rem;
+  row-gap: 14rem;
+}
+
+.gap-y-64 {
+  grid-row-gap: 16rem;
+  row-gap: 16rem;
+}
+
+.gap-y-px {
+  grid-row-gap: 1px;
+  row-gap: 1px;
+}
+
+.grid-flow-row {
+  grid-auto-flow: row;
+}
+
+.grid-flow-col {
+  grid-auto-flow: column;
+}
+
+.grid-flow-row-dense {
+  grid-auto-flow: row dense;
+}
+
+.grid-flow-col-dense {
+  grid-auto-flow: column dense;
+}
+
+.grid-cols-1 {
+  grid-template-columns: repeat(1, minmax(0, 1fr));
+}
+
+.grid-cols-2 {
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+.grid-cols-3 {
+  grid-template-columns: repeat(3, minmax(0, 1fr));
+}
+
+.grid-cols-4 {
+  grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+
+.grid-cols-5 {
+  grid-template-columns: repeat(5, minmax(0, 1fr));
+}
+
+.grid-cols-6 {
+  grid-template-columns: repeat(6, minmax(0, 1fr));
+}
+
+.grid-cols-7 {
+  grid-template-columns: repeat(7, minmax(0, 1fr));
+}
+
+.grid-cols-8 {
+  grid-template-columns: repeat(8, minmax(0, 1fr));
+}
+
+.grid-cols-9 {
+  grid-template-columns: repeat(9, minmax(0, 1fr));
+}
+
+.grid-cols-10 {
+  grid-template-columns: repeat(10, minmax(0, 1fr));
+}
+
+.grid-cols-11 {
+  grid-template-columns: repeat(11, minmax(0, 1fr));
+}
+
+.grid-cols-12 {
+  grid-template-columns: repeat(12, minmax(0, 1fr));
+}
+
+.grid-cols-none {
+  grid-template-columns: none;
+}
+
+.col-auto {
+  grid-column: auto;
+}
+
+.col-span-1 {
+  grid-column: span 1 / span 1;
+}
+
+.col-span-2 {
+  grid-column: span 2 / span 2;
+}
+
+.col-span-3 {
+  grid-column: span 3 / span 3;
+}
+
+.col-span-4 {
+  grid-column: span 4 / span 4;
+}
+
+.col-span-5 {
+  grid-column: span 5 / span 5;
+}
+
+.col-span-6 {
+  grid-column: span 6 / span 6;
+}
+
+.col-span-7 {
+  grid-column: span 7 / span 7;
+}
+
+.col-span-8 {
+  grid-column: span 8 / span 8;
+}
+
+.col-span-9 {
+  grid-column: span 9 / span 9;
+}
+
+.col-span-10 {
+  grid-column: span 10 / span 10;
+}
+
+.col-span-11 {
+  grid-column: span 11 / span 11;
+}
+
+.col-span-12 {
+  grid-column: span 12 / span 12;
+}
+
+.col-start-1 {
+  grid-column-start: 1;
+}
+
+.col-start-2 {
+  grid-column-start: 2;
+}
+
+.col-start-3 {
+  grid-column-start: 3;
+}
+
+.col-start-4 {
+  grid-column-start: 4;
+}
+
+.col-start-5 {
+  grid-column-start: 5;
+}
+
+.col-start-6 {
+  grid-column-start: 6;
+}
+
+.col-start-7 {
+  grid-column-start: 7;
+}
+
+.col-start-8 {
+  grid-column-start: 8;
+}
+
+.col-start-9 {
+  grid-column-start: 9;
+}
+
+.col-start-10 {
+  grid-column-start: 10;
+}
+
+.col-start-11 {
+  grid-column-start: 11;
+}
+
+.col-start-12 {
+  grid-column-start: 12;
+}
+
+.col-start-13 {
+  grid-column-start: 13;
+}
+
+.col-start-auto {
+  grid-column-start: auto;
+}
+
+.col-end-1 {
+  grid-column-end: 1;
+}
+
+.col-end-2 {
+  grid-column-end: 2;
+}
+
+.col-end-3 {
+  grid-column-end: 3;
+}
+
+.col-end-4 {
+  grid-column-end: 4;
+}
+
+.col-end-5 {
+  grid-column-end: 5;
+}
+
+.col-end-6 {
+  grid-column-end: 6;
+}
+
+.col-end-7 {
+  grid-column-end: 7;
+}
+
+.col-end-8 {
+  grid-column-end: 8;
+}
+
+.col-end-9 {
+  grid-column-end: 9;
+}
+
+.col-end-10 {
+  grid-column-end: 10;
+}
+
+.col-end-11 {
+  grid-column-end: 11;
+}
+
+.col-end-12 {
+  grid-column-end: 12;
+}
+
+.col-end-13 {
+  grid-column-end: 13;
+}
+
+.col-end-auto {
+  grid-column-end: auto;
+}
+
+.grid-rows-1 {
+  grid-template-rows: repeat(1, minmax(0, 1fr));
+}
+
+.grid-rows-2 {
+  grid-template-rows: repeat(2, minmax(0, 1fr));
+}
+
+.grid-rows-3 {
+  grid-template-rows: repeat(3, minmax(0, 1fr));
+}
+
+.grid-rows-4 {
+  grid-template-rows: repeat(4, minmax(0, 1fr));
+}
+
+.grid-rows-5 {
+  grid-template-rows: repeat(5, minmax(0, 1fr));
+}
+
+.grid-rows-6 {
+  grid-template-rows: repeat(6, minmax(0, 1fr));
+}
+
+.grid-rows-none {
+  grid-template-rows: none;
+}
+
+.row-auto {
+  grid-row: auto;
+}
+
+.row-span-1 {
+  grid-row: span 1 / span 1;
+}
+
+.row-span-2 {
+  grid-row: span 2 / span 2;
+}
+
+.row-span-3 {
+  grid-row: span 3 / span 3;
+}
+
+.row-span-4 {
+  grid-row: span 4 / span 4;
+}
+
+.row-span-5 {
+  grid-row: span 5 / span 5;
+}
+
+.row-span-6 {
+  grid-row: span 6 / span 6;
+}
+
+.row-start-1 {
+  grid-row-start: 1;
+}
+
+.row-start-2 {
+  grid-row-start: 2;
+}
+
+.row-start-3 {
+  grid-row-start: 3;
+}
+
+.row-start-4 {
+  grid-row-start: 4;
+}
+
+.row-start-5 {
+  grid-row-start: 5;
+}
+
+.row-start-6 {
+  grid-row-start: 6;
+}
+
+.row-start-7 {
+  grid-row-start: 7;
+}
+
+.row-start-auto {
+  grid-row-start: auto;
+}
+
+.row-end-1 {
+  grid-row-end: 1;
+}
+
+.row-end-2 {
+  grid-row-end: 2;
+}
+
+.row-end-3 {
+  grid-row-end: 3;
+}
+
+.row-end-4 {
+  grid-row-end: 4;
+}
+
+.row-end-5 {
+  grid-row-end: 5;
+}
+
+.row-end-6 {
+  grid-row-end: 6;
+}
+
+.row-end-7 {
+  grid-row-end: 7;
+}
+
+.row-end-auto {
+  grid-row-end: auto;
+}
+
+.transform {
+  --transform-translate-x: 0;
+  --transform-translate-y: 0;
+  --transform-rotate: 0;
+  --transform-skew-x: 0;
+  --transform-skew-y: 0;
+  --transform-scale-x: 1;
+  --transform-scale-y: 1;
+  transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+}
+
+.transform-none {
+  transform: none;
+}
+
+.origin-center {
+  transform-origin: center;
+}
+
+.origin-top {
+  transform-origin: top;
+}
+
+.origin-top-right {
+  transform-origin: top right;
+}
+
+.origin-right {
+  transform-origin: right;
+}
+
+.origin-bottom-right {
+  transform-origin: bottom right;
+}
+
+.origin-bottom {
+  transform-origin: bottom;
+}
+
+.origin-bottom-left {
+  transform-origin: bottom left;
+}
+
+.origin-left {
+  transform-origin: left;
+}
+
+.origin-top-left {
+  transform-origin: top left;
+}
+
+.scale-0 {
+  --transform-scale-x: 0;
+  --transform-scale-y: 0;
+}
+
+.scale-50 {
+  --transform-scale-x: .5;
+  --transform-scale-y: .5;
+}
+
+.scale-75 {
+  --transform-scale-x: .75;
+  --transform-scale-y: .75;
+}
+
+.scale-90 {
+  --transform-scale-x: .9;
+  --transform-scale-y: .9;
+}
+
+.scale-95 {
+  --transform-scale-x: .95;
+  --transform-scale-y: .95;
+}
+
+.scale-100 {
+  --transform-scale-x: 1;
+  --transform-scale-y: 1;
+}
+
+.scale-105 {
+  --transform-scale-x: 1.05;
+  --transform-scale-y: 1.05;
+}
+
+.scale-110 {
+  --transform-scale-x: 1.1;
+  --transform-scale-y: 1.1;
+}
+
+.scale-125 {
+  --transform-scale-x: 1.25;
+  --transform-scale-y: 1.25;
+}
+
+.scale-150 {
+  --transform-scale-x: 1.5;
+  --transform-scale-y: 1.5;
+}
+
+.scale-x-0 {
+  --transform-scale-x: 0;
+}
+
+.scale-x-50 {
+  --transform-scale-x: .5;
+}
+
+.scale-x-75 {
+  --transform-scale-x: .75;
+}
+
+.scale-x-90 {
+  --transform-scale-x: .9;
+}
+
+.scale-x-95 {
+  --transform-scale-x: .95;
+}
+
+.scale-x-100 {
+  --transform-scale-x: 1;
+}
+
+.scale-x-105 {
+  --transform-scale-x: 1.05;
+}
+
+.scale-x-110 {
+  --transform-scale-x: 1.1;
+}
+
+.scale-x-125 {
+  --transform-scale-x: 1.25;
+}
+
+.scale-x-150 {
+  --transform-scale-x: 1.5;
+}
+
+.scale-y-0 {
+  --transform-scale-y: 0;
+}
+
+.scale-y-50 {
+  --transform-scale-y: .5;
+}
+
+.scale-y-75 {
+  --transform-scale-y: .75;
+}
+
+.scale-y-90 {
+  --transform-scale-y: .9;
+}
+
+.scale-y-95 {
+  --transform-scale-y: .95;
+}
+
+.scale-y-100 {
+  --transform-scale-y: 1;
+}
+
+.scale-y-105 {
+  --transform-scale-y: 1.05;
+}
+
+.scale-y-110 {
+  --transform-scale-y: 1.1;
+}
+
+.scale-y-125 {
+  --transform-scale-y: 1.25;
+}
+
+.scale-y-150 {
+  --transform-scale-y: 1.5;
+}
+
+.hover\:scale-0:hover {
+  --transform-scale-x: 0;
+  --transform-scale-y: 0;
+}
+
+.hover\:scale-50:hover {
+  --transform-scale-x: .5;
+  --transform-scale-y: .5;
+}
+
+.hover\:scale-75:hover {
+  --transform-scale-x: .75;
+  --transform-scale-y: .75;
+}
+
+.hover\:scale-90:hover {
+  --transform-scale-x: .9;
+  --transform-scale-y: .9;
+}
+
+.hover\:scale-95:hover {
+  --transform-scale-x: .95;
+  --transform-scale-y: .95;
+}
+
+.hover\:scale-100:hover {
+  --transform-scale-x: 1;
+  --transform-scale-y: 1;
+}
+
+.hover\:scale-105:hover {
+  --transform-scale-x: 1.05;
+  --transform-scale-y: 1.05;
+}
+
+.hover\:scale-110:hover {
+  --transform-scale-x: 1.1;
+  --transform-scale-y: 1.1;
+}
+
+.hover\:scale-125:hover {
+  --transform-scale-x: 1.25;
+  --transform-scale-y: 1.25;
+}
+
+.hover\:scale-150:hover {
+  --transform-scale-x: 1.5;
+  --transform-scale-y: 1.5;
+}
+
+.hover\:scale-x-0:hover {
+  --transform-scale-x: 0;
+}
+
+.hover\:scale-x-50:hover {
+  --transform-scale-x: .5;
+}
+
+.hover\:scale-x-75:hover {
+  --transform-scale-x: .75;
+}
+
+.hover\:scale-x-90:hover {
+  --transform-scale-x: .9;
+}
+
+.hover\:scale-x-95:hover {
+  --transform-scale-x: .95;
+}
+
+.hover\:scale-x-100:hover {
+  --transform-scale-x: 1;
+}
+
+.hover\:scale-x-105:hover {
+  --transform-scale-x: 1.05;
+}
+
+.hover\:scale-x-110:hover {
+  --transform-scale-x: 1.1;
+}
+
+.hover\:scale-x-125:hover {
+  --transform-scale-x: 1.25;
+}
+
+.hover\:scale-x-150:hover {
+  --transform-scale-x: 1.5;
+}
+
+.hover\:scale-y-0:hover {
+  --transform-scale-y: 0;
+}
+
+.hover\:scale-y-50:hover {
+  --transform-scale-y: .5;
+}
+
+.hover\:scale-y-75:hover {
+  --transform-scale-y: .75;
+}
+
+.hover\:scale-y-90:hover {
+  --transform-scale-y: .9;
+}
+
+.hover\:scale-y-95:hover {
+  --transform-scale-y: .95;
+}
+
+.hover\:scale-y-100:hover {
+  --transform-scale-y: 1;
+}
+
+.hover\:scale-y-105:hover {
+  --transform-scale-y: 1.05;
+}
+
+.hover\:scale-y-110:hover {
+  --transform-scale-y: 1.1;
+}
+
+.hover\:scale-y-125:hover {
+  --transform-scale-y: 1.25;
+}
+
+.hover\:scale-y-150:hover {
+  --transform-scale-y: 1.5;
+}
+
+.focus\:scale-0:focus {
+  --transform-scale-x: 0;
+  --transform-scale-y: 0;
+}
+
+.focus\:scale-50:focus {
+  --transform-scale-x: .5;
+  --transform-scale-y: .5;
+}
+
+.focus\:scale-75:focus {
+  --transform-scale-x: .75;
+  --transform-scale-y: .75;
+}
+
+.focus\:scale-90:focus {
+  --transform-scale-x: .9;
+  --transform-scale-y: .9;
+}
+
+.focus\:scale-95:focus {
+  --transform-scale-x: .95;
+  --transform-scale-y: .95;
+}
+
+.focus\:scale-100:focus {
+  --transform-scale-x: 1;
+  --transform-scale-y: 1;
+}
+
+.focus\:scale-105:focus {
+  --transform-scale-x: 1.05;
+  --transform-scale-y: 1.05;
+}
+
+.focus\:scale-110:focus {
+  --transform-scale-x: 1.1;
+  --transform-scale-y: 1.1;
+}
+
+.focus\:scale-125:focus {
+  --transform-scale-x: 1.25;
+  --transform-scale-y: 1.25;
+}
+
+.focus\:scale-150:focus {
+  --transform-scale-x: 1.5;
+  --transform-scale-y: 1.5;
+}
+
+.focus\:scale-x-0:focus {
+  --transform-scale-x: 0;
+}
+
+.focus\:scale-x-50:focus {
+  --transform-scale-x: .5;
+}
+
+.focus\:scale-x-75:focus {
+  --transform-scale-x: .75;
+}
+
+.focus\:scale-x-90:focus {
+  --transform-scale-x: .9;
+}
+
+.focus\:scale-x-95:focus {
+  --transform-scale-x: .95;
+}
+
+.focus\:scale-x-100:focus {
+  --transform-scale-x: 1;
+}
+
+.focus\:scale-x-105:focus {
+  --transform-scale-x: 1.05;
+}
+
+.focus\:scale-x-110:focus {
+  --transform-scale-x: 1.1;
+}
+
+.focus\:scale-x-125:focus {
+  --transform-scale-x: 1.25;
+}
+
+.focus\:scale-x-150:focus {
+  --transform-scale-x: 1.5;
+}
+
+.focus\:scale-y-0:focus {
+  --transform-scale-y: 0;
+}
+
+.focus\:scale-y-50:focus {
+  --transform-scale-y: .5;
+}
+
+.focus\:scale-y-75:focus {
+  --transform-scale-y: .75;
+}
+
+.focus\:scale-y-90:focus {
+  --transform-scale-y: .9;
+}
+
+.focus\:scale-y-95:focus {
+  --transform-scale-y: .95;
+}
+
+.focus\:scale-y-100:focus {
+  --transform-scale-y: 1;
+}
+
+.focus\:scale-y-105:focus {
+  --transform-scale-y: 1.05;
+}
+
+.focus\:scale-y-110:focus {
+  --transform-scale-y: 1.1;
+}
+
+.focus\:scale-y-125:focus {
+  --transform-scale-y: 1.25;
+}
+
+.focus\:scale-y-150:focus {
+  --transform-scale-y: 1.5;
+}
+
+.rotate-0 {
+  --transform-rotate: 0;
+}
+
+.rotate-45 {
+  --transform-rotate: 45deg;
+}
+
+.rotate-90 {
+  --transform-rotate: 90deg;
+}
+
+.rotate-180 {
+  --transform-rotate: 180deg;
+}
+
+.-rotate-180 {
+  --transform-rotate: -180deg;
+}
+
+.-rotate-90 {
+  --transform-rotate: -90deg;
+}
+
+.-rotate-45 {
+  --transform-rotate: -45deg;
+}
+
+.hover\:rotate-0:hover {
+  --transform-rotate: 0;
+}
+
+.hover\:rotate-45:hover {
+  --transform-rotate: 45deg;
+}
+
+.hover\:rotate-90:hover {
+  --transform-rotate: 90deg;
+}
+
+.hover\:rotate-180:hover {
+  --transform-rotate: 180deg;
+}
+
+.hover\:-rotate-180:hover {
+  --transform-rotate: -180deg;
+}
+
+.hover\:-rotate-90:hover {
+  --transform-rotate: -90deg;
+}
+
+.hover\:-rotate-45:hover {
+  --transform-rotate: -45deg;
+}
+
+.focus\:rotate-0:focus {
+  --transform-rotate: 0;
+}
+
+.focus\:rotate-45:focus {
+  --transform-rotate: 45deg;
+}
+
+.focus\:rotate-90:focus {
+  --transform-rotate: 90deg;
+}
+
+.focus\:rotate-180:focus {
+  --transform-rotate: 180deg;
+}
+
+.focus\:-rotate-180:focus {
+  --transform-rotate: -180deg;
+}
+
+.focus\:-rotate-90:focus {
+  --transform-rotate: -90deg;
+}
+
+.focus\:-rotate-45:focus {
+  --transform-rotate: -45deg;
+}
+
+.translate-x-0 {
+  --transform-translate-x: 0;
+}
+
+.translate-x-1 {
+  --transform-translate-x: 0.25rem;
+}
+
+.translate-x-2 {
+  --transform-translate-x: 0.5rem;
+}
+
+.translate-x-3 {
+  --transform-translate-x: 0.75rem;
+}
+
+.translate-x-4 {
+  --transform-translate-x: 1rem;
+}
+
+.translate-x-5 {
+  --transform-translate-x: 1.25rem;
+}
+
+.translate-x-6 {
+  --transform-translate-x: 1.5rem;
+}
+
+.translate-x-8 {
+  --transform-translate-x: 2rem;
+}
+
+.translate-x-10 {
+  --transform-translate-x: 2.5rem;
+}
+
+.translate-x-12 {
+  --transform-translate-x: 3rem;
+}
+
+.translate-x-16 {
+  --transform-translate-x: 4rem;
+}
+
+.translate-x-20 {
+  --transform-translate-x: 5rem;
+}
+
+.translate-x-24 {
+  --transform-translate-x: 6rem;
+}
+
+.translate-x-32 {
+  --transform-translate-x: 8rem;
+}
+
+.translate-x-40 {
+  --transform-translate-x: 10rem;
+}
+
+.translate-x-48 {
+  --transform-translate-x: 12rem;
+}
+
+.translate-x-56 {
+  --transform-translate-x: 14rem;
+}
+
+.translate-x-64 {
+  --transform-translate-x: 16rem;
+}
+
+.translate-x-px {
+  --transform-translate-x: 1px;
+}
+
+.-translate-x-1 {
+  --transform-translate-x: -0.25rem;
+}
+
+.-translate-x-2 {
+  --transform-translate-x: -0.5rem;
+}
+
+.-translate-x-3 {
+  --transform-translate-x: -0.75rem;
+}
+
+.-translate-x-4 {
+  --transform-translate-x: -1rem;
+}
+
+.-translate-x-5 {
+  --transform-translate-x: -1.25rem;
+}
+
+.-translate-x-6 {
+  --transform-translate-x: -1.5rem;
+}
+
+.-translate-x-8 {
+  --transform-translate-x: -2rem;
+}
+
+.-translate-x-10 {
+  --transform-translate-x: -2.5rem;
+}
+
+.-translate-x-12 {
+  --transform-translate-x: -3rem;
+}
+
+.-translate-x-16 {
+  --transform-translate-x: -4rem;
+}
+
+.-translate-x-20 {
+  --transform-translate-x: -5rem;
+}
+
+.-translate-x-24 {
+  --transform-translate-x: -6rem;
+}
+
+.-translate-x-32 {
+  --transform-translate-x: -8rem;
+}
+
+.-translate-x-40 {
+  --transform-translate-x: -10rem;
+}
+
+.-translate-x-48 {
+  --transform-translate-x: -12rem;
+}
+
+.-translate-x-56 {
+  --transform-translate-x: -14rem;
+}
+
+.-translate-x-64 {
+  --transform-translate-x: -16rem;
+}
+
+.-translate-x-px {
+  --transform-translate-x: -1px;
+}
+
+.-translate-x-full {
+  --transform-translate-x: -100%;
+}
+
+.-translate-x-1\/2 {
+  --transform-translate-x: -50%;
+}
+
+.translate-x-1\/2 {
+  --transform-translate-x: 50%;
+}
+
+.translate-x-full {
+  --transform-translate-x: 100%;
+}
+
+.translate-y-0 {
+  --transform-translate-y: 0;
+}
+
+.translate-y-1 {
+  --transform-translate-y: 0.25rem;
+}
+
+.translate-y-2 {
+  --transform-translate-y: 0.5rem;
+}
+
+.translate-y-3 {
+  --transform-translate-y: 0.75rem;
+}
+
+.translate-y-4 {
+  --transform-translate-y: 1rem;
+}
+
+.translate-y-5 {
+  --transform-translate-y: 1.25rem;
+}
+
+.translate-y-6 {
+  --transform-translate-y: 1.5rem;
+}
+
+.translate-y-8 {
+  --transform-translate-y: 2rem;
+}
+
+.translate-y-10 {
+  --transform-translate-y: 2.5rem;
+}
+
+.translate-y-12 {
+  --transform-translate-y: 3rem;
+}
+
+.translate-y-16 {
+  --transform-translate-y: 4rem;
+}
+
+.translate-y-20 {
+  --transform-translate-y: 5rem;
+}
+
+.translate-y-24 {
+  --transform-translate-y: 6rem;
+}
+
+.translate-y-32 {
+  --transform-translate-y: 8rem;
+}
+
+.translate-y-40 {
+  --transform-translate-y: 10rem;
+}
+
+.translate-y-48 {
+  --transform-translate-y: 12rem;
+}
+
+.translate-y-56 {
+  --transform-translate-y: 14rem;
+}
+
+.translate-y-64 {
+  --transform-translate-y: 16rem;
+}
+
+.translate-y-px {
+  --transform-translate-y: 1px;
+}
+
+.-translate-y-1 {
+  --transform-translate-y: -0.25rem;
+}
+
+.-translate-y-2 {
+  --transform-translate-y: -0.5rem;
+}
+
+.-translate-y-3 {
+  --transform-translate-y: -0.75rem;
+}
+
+.-translate-y-4 {
+  --transform-translate-y: -1rem;
+}
+
+.-translate-y-5 {
+  --transform-translate-y: -1.25rem;
+}
+
+.-translate-y-6 {
+  --transform-translate-y: -1.5rem;
+}
+
+.-translate-y-8 {
+  --transform-translate-y: -2rem;
+}
+
+.-translate-y-10 {
+  --transform-translate-y: -2.5rem;
+}
+
+.-translate-y-12 {
+  --transform-translate-y: -3rem;
+}
+
+.-translate-y-16 {
+  --transform-translate-y: -4rem;
+}
+
+.-translate-y-20 {
+  --transform-translate-y: -5rem;
+}
+
+.-translate-y-24 {
+  --transform-translate-y: -6rem;
+}
+
+.-translate-y-32 {
+  --transform-translate-y: -8rem;
+}
+
+.-translate-y-40 {
+  --transform-translate-y: -10rem;
+}
+
+.-translate-y-48 {
+  --transform-translate-y: -12rem;
+}
+
+.-translate-y-56 {
+  --transform-translate-y: -14rem;
+}
+
+.-translate-y-64 {
+  --transform-translate-y: -16rem;
+}
+
+.-translate-y-px {
+  --transform-translate-y: -1px;
+}
+
+.-translate-y-full {
+  --transform-translate-y: -100%;
+}
+
+.-translate-y-1\/2 {
+  --transform-translate-y: -50%;
+}
+
+.translate-y-1\/2 {
+  --transform-translate-y: 50%;
+}
+
+.translate-y-full {
+  --transform-translate-y: 100%;
+}
+
+.hover\:translate-x-0:hover {
+  --transform-translate-x: 0;
+}
+
+.hover\:translate-x-1:hover {
+  --transform-translate-x: 0.25rem;
+}
+
+.hover\:translate-x-2:hover {
+  --transform-translate-x: 0.5rem;
+}
+
+.hover\:translate-x-3:hover {
+  --transform-translate-x: 0.75rem;
+}
+
+.hover\:translate-x-4:hover {
+  --transform-translate-x: 1rem;
+}
+
+.hover\:translate-x-5:hover {
+  --transform-translate-x: 1.25rem;
+}
+
+.hover\:translate-x-6:hover {
+  --transform-translate-x: 1.5rem;
+}
+
+.hover\:translate-x-8:hover {
+  --transform-translate-x: 2rem;
+}
+
+.hover\:translate-x-10:hover {
+  --transform-translate-x: 2.5rem;
+}
+
+.hover\:translate-x-12:hover {
+  --transform-translate-x: 3rem;
+}
+
+.hover\:translate-x-16:hover {
+  --transform-translate-x: 4rem;
+}
+
+.hover\:translate-x-20:hover {
+  --transform-translate-x: 5rem;
+}
+
+.hover\:translate-x-24:hover {
+  --transform-translate-x: 6rem;
+}
+
+.hover\:translate-x-32:hover {
+  --transform-translate-x: 8rem;
+}
+
+.hover\:translate-x-40:hover {
+  --transform-translate-x: 10rem;
+}
+
+.hover\:translate-x-48:hover {
+  --transform-translate-x: 12rem;
+}
+
+.hover\:translate-x-56:hover {
+  --transform-translate-x: 14rem;
+}
+
+.hover\:translate-x-64:hover {
+  --transform-translate-x: 16rem;
+}
+
+.hover\:translate-x-px:hover {
+  --transform-translate-x: 1px;
+}
+
+.hover\:-translate-x-1:hover {
+  --transform-translate-x: -0.25rem;
+}
+
+.hover\:-translate-x-2:hover {
+  --transform-translate-x: -0.5rem;
+}
+
+.hover\:-translate-x-3:hover {
+  --transform-translate-x: -0.75rem;
+}
+
+.hover\:-translate-x-4:hover {
+  --transform-translate-x: -1rem;
+}
+
+.hover\:-translate-x-5:hover {
+  --transform-translate-x: -1.25rem;
+}
+
+.hover\:-translate-x-6:hover {
+  --transform-translate-x: -1.5rem;
+}
+
+.hover\:-translate-x-8:hover {
+  --transform-translate-x: -2rem;
+}
+
+.hover\:-translate-x-10:hover {
+  --transform-translate-x: -2.5rem;
+}
+
+.hover\:-translate-x-12:hover {
+  --transform-translate-x: -3rem;
+}
+
+.hover\:-translate-x-16:hover {
+  --transform-translate-x: -4rem;
+}
+
+.hover\:-translate-x-20:hover {
+  --transform-translate-x: -5rem;
+}
+
+.hover\:-translate-x-24:hover {
+  --transform-translate-x: -6rem;
+}
+
+.hover\:-translate-x-32:hover {
+  --transform-translate-x: -8rem;
+}
+
+.hover\:-translate-x-40:hover {
+  --transform-translate-x: -10rem;
+}
+
+.hover\:-translate-x-48:hover {
+  --transform-translate-x: -12rem;
+}
+
+.hover\:-translate-x-56:hover {
+  --transform-translate-x: -14rem;
+}
+
+.hover\:-translate-x-64:hover {
+  --transform-translate-x: -16rem;
+}
+
+.hover\:-translate-x-px:hover {
+  --transform-translate-x: -1px;
+}
+
+.hover\:-translate-x-full:hover {
+  --transform-translate-x: -100%;
+}
+
+.hover\:-translate-x-1\/2:hover {
+  --transform-translate-x: -50%;
+}
+
+.hover\:translate-x-1\/2:hover {
+  --transform-translate-x: 50%;
+}
+
+.hover\:translate-x-full:hover {
+  --transform-translate-x: 100%;
+}
+
+.hover\:translate-y-0:hover {
+  --transform-translate-y: 0;
+}
+
+.hover\:translate-y-1:hover {
+  --transform-translate-y: 0.25rem;
+}
+
+.hover\:translate-y-2:hover {
+  --transform-translate-y: 0.5rem;
+}
+
+.hover\:translate-y-3:hover {
+  --transform-translate-y: 0.75rem;
+}
+
+.hover\:translate-y-4:hover {
+  --transform-translate-y: 1rem;
+}
+
+.hover\:translate-y-5:hover {
+  --transform-translate-y: 1.25rem;
+}
+
+.hover\:translate-y-6:hover {
+  --transform-translate-y: 1.5rem;
+}
+
+.hover\:translate-y-8:hover {
+  --transform-translate-y: 2rem;
+}
+
+.hover\:translate-y-10:hover {
+  --transform-translate-y: 2.5rem;
+}
+
+.hover\:translate-y-12:hover {
+  --transform-translate-y: 3rem;
+}
+
+.hover\:translate-y-16:hover {
+  --transform-translate-y: 4rem;
+}
+
+.hover\:translate-y-20:hover {
+  --transform-translate-y: 5rem;
+}
+
+.hover\:translate-y-24:hover {
+  --transform-translate-y: 6rem;
+}
+
+.hover\:translate-y-32:hover {
+  --transform-translate-y: 8rem;
+}
+
+.hover\:translate-y-40:hover {
+  --transform-translate-y: 10rem;
+}
+
+.hover\:translate-y-48:hover {
+  --transform-translate-y: 12rem;
+}
+
+.hover\:translate-y-56:hover {
+  --transform-translate-y: 14rem;
+}
+
+.hover\:translate-y-64:hover {
+  --transform-translate-y: 16rem;
+}
+
+.hover\:translate-y-px:hover {
+  --transform-translate-y: 1px;
+}
+
+.hover\:-translate-y-1:hover {
+  --transform-translate-y: -0.25rem;
+}
+
+.hover\:-translate-y-2:hover {
+  --transform-translate-y: -0.5rem;
+}
+
+.hover\:-translate-y-3:hover {
+  --transform-translate-y: -0.75rem;
+}
+
+.hover\:-translate-y-4:hover {
+  --transform-translate-y: -1rem;
+}
+
+.hover\:-translate-y-5:hover {
+  --transform-translate-y: -1.25rem;
+}
+
+.hover\:-translate-y-6:hover {
+  --transform-translate-y: -1.5rem;
+}
+
+.hover\:-translate-y-8:hover {
+  --transform-translate-y: -2rem;
+}
+
+.hover\:-translate-y-10:hover {
+  --transform-translate-y: -2.5rem;
+}
+
+.hover\:-translate-y-12:hover {
+  --transform-translate-y: -3rem;
+}
+
+.hover\:-translate-y-16:hover {
+  --transform-translate-y: -4rem;
+}
+
+.hover\:-translate-y-20:hover {
+  --transform-translate-y: -5rem;
+}
+
+.hover\:-translate-y-24:hover {
+  --transform-translate-y: -6rem;
+}
+
+.hover\:-translate-y-32:hover {
+  --transform-translate-y: -8rem;
+}
+
+.hover\:-translate-y-40:hover {
+  --transform-translate-y: -10rem;
+}
+
+.hover\:-translate-y-48:hover {
+  --transform-translate-y: -12rem;
+}
+
+.hover\:-translate-y-56:hover {
+  --transform-translate-y: -14rem;
+}
+
+.hover\:-translate-y-64:hover {
+  --transform-translate-y: -16rem;
+}
+
+.hover\:-translate-y-px:hover {
+  --transform-translate-y: -1px;
+}
+
+.hover\:-translate-y-full:hover {
+  --transform-translate-y: -100%;
+}
+
+.hover\:-translate-y-1\/2:hover {
+  --transform-translate-y: -50%;
+}
+
+.hover\:translate-y-1\/2:hover {
+  --transform-translate-y: 50%;
+}
+
+.hover\:translate-y-full:hover {
+  --transform-translate-y: 100%;
+}
+
+.focus\:translate-x-0:focus {
+  --transform-translate-x: 0;
+}
+
+.focus\:translate-x-1:focus {
+  --transform-translate-x: 0.25rem;
+}
+
+.focus\:translate-x-2:focus {
+  --transform-translate-x: 0.5rem;
+}
+
+.focus\:translate-x-3:focus {
+  --transform-translate-x: 0.75rem;
+}
+
+.focus\:translate-x-4:focus {
+  --transform-translate-x: 1rem;
+}
+
+.focus\:translate-x-5:focus {
+  --transform-translate-x: 1.25rem;
+}
+
+.focus\:translate-x-6:focus {
+  --transform-translate-x: 1.5rem;
+}
+
+.focus\:translate-x-8:focus {
+  --transform-translate-x: 2rem;
+}
+
+.focus\:translate-x-10:focus {
+  --transform-translate-x: 2.5rem;
+}
+
+.focus\:translate-x-12:focus {
+  --transform-translate-x: 3rem;
+}
+
+.focus\:translate-x-16:focus {
+  --transform-translate-x: 4rem;
+}
+
+.focus\:translate-x-20:focus {
+  --transform-translate-x: 5rem;
+}
+
+.focus\:translate-x-24:focus {
+  --transform-translate-x: 6rem;
+}
+
+.focus\:translate-x-32:focus {
+  --transform-translate-x: 8rem;
+}
+
+.focus\:translate-x-40:focus {
+  --transform-translate-x: 10rem;
+}
+
+.focus\:translate-x-48:focus {
+  --transform-translate-x: 12rem;
+}
+
+.focus\:translate-x-56:focus {
+  --transform-translate-x: 14rem;
+}
+
+.focus\:translate-x-64:focus {
+  --transform-translate-x: 16rem;
+}
+
+.focus\:translate-x-px:focus {
+  --transform-translate-x: 1px;
+}
+
+.focus\:-translate-x-1:focus {
+  --transform-translate-x: -0.25rem;
+}
+
+.focus\:-translate-x-2:focus {
+  --transform-translate-x: -0.5rem;
+}
+
+.focus\:-translate-x-3:focus {
+  --transform-translate-x: -0.75rem;
+}
+
+.focus\:-translate-x-4:focus {
+  --transform-translate-x: -1rem;
+}
+
+.focus\:-translate-x-5:focus {
+  --transform-translate-x: -1.25rem;
+}
+
+.focus\:-translate-x-6:focus {
+  --transform-translate-x: -1.5rem;
+}
+
+.focus\:-translate-x-8:focus {
+  --transform-translate-x: -2rem;
+}
+
+.focus\:-translate-x-10:focus {
+  --transform-translate-x: -2.5rem;
+}
+
+.focus\:-translate-x-12:focus {
+  --transform-translate-x: -3rem;
+}
+
+.focus\:-translate-x-16:focus {
+  --transform-translate-x: -4rem;
+}
+
+.focus\:-translate-x-20:focus {
+  --transform-translate-x: -5rem;
+}
+
+.focus\:-translate-x-24:focus {
+  --transform-translate-x: -6rem;
+}
+
+.focus\:-translate-x-32:focus {
+  --transform-translate-x: -8rem;
+}
+
+.focus\:-translate-x-40:focus {
+  --transform-translate-x: -10rem;
+}
+
+.focus\:-translate-x-48:focus {
+  --transform-translate-x: -12rem;
+}
+
+.focus\:-translate-x-56:focus {
+  --transform-translate-x: -14rem;
+}
+
+.focus\:-translate-x-64:focus {
+  --transform-translate-x: -16rem;
+}
+
+.focus\:-translate-x-px:focus {
+  --transform-translate-x: -1px;
+}
+
+.focus\:-translate-x-full:focus {
+  --transform-translate-x: -100%;
+}
+
+.focus\:-translate-x-1\/2:focus {
+  --transform-translate-x: -50%;
+}
+
+.focus\:translate-x-1\/2:focus {
+  --transform-translate-x: 50%;
+}
+
+.focus\:translate-x-full:focus {
+  --transform-translate-x: 100%;
+}
+
+.focus\:translate-y-0:focus {
+  --transform-translate-y: 0;
+}
+
+.focus\:translate-y-1:focus {
+  --transform-translate-y: 0.25rem;
+}
+
+.focus\:translate-y-2:focus {
+  --transform-translate-y: 0.5rem;
+}
+
+.focus\:translate-y-3:focus {
+  --transform-translate-y: 0.75rem;
+}
+
+.focus\:translate-y-4:focus {
+  --transform-translate-y: 1rem;
+}
+
+.focus\:translate-y-5:focus {
+  --transform-translate-y: 1.25rem;
+}
+
+.focus\:translate-y-6:focus {
+  --transform-translate-y: 1.5rem;
+}
+
+.focus\:translate-y-8:focus {
+  --transform-translate-y: 2rem;
+}
+
+.focus\:translate-y-10:focus {
+  --transform-translate-y: 2.5rem;
+}
+
+.focus\:translate-y-12:focus {
+  --transform-translate-y: 3rem;
+}
+
+.focus\:translate-y-16:focus {
+  --transform-translate-y: 4rem;
+}
+
+.focus\:translate-y-20:focus {
+  --transform-translate-y: 5rem;
+}
+
+.focus\:translate-y-24:focus {
+  --transform-translate-y: 6rem;
+}
+
+.focus\:translate-y-32:focus {
+  --transform-translate-y: 8rem;
+}
+
+.focus\:translate-y-40:focus {
+  --transform-translate-y: 10rem;
+}
+
+.focus\:translate-y-48:focus {
+  --transform-translate-y: 12rem;
+}
+
+.focus\:translate-y-56:focus {
+  --transform-translate-y: 14rem;
+}
+
+.focus\:translate-y-64:focus {
+  --transform-translate-y: 16rem;
+}
+
+.focus\:translate-y-px:focus {
+  --transform-translate-y: 1px;
+}
+
+.focus\:-translate-y-1:focus {
+  --transform-translate-y: -0.25rem;
+}
+
+.focus\:-translate-y-2:focus {
+  --transform-translate-y: -0.5rem;
+}
+
+.focus\:-translate-y-3:focus {
+  --transform-translate-y: -0.75rem;
+}
+
+.focus\:-translate-y-4:focus {
+  --transform-translate-y: -1rem;
+}
+
+.focus\:-translate-y-5:focus {
+  --transform-translate-y: -1.25rem;
+}
+
+.focus\:-translate-y-6:focus {
+  --transform-translate-y: -1.5rem;
+}
+
+.focus\:-translate-y-8:focus {
+  --transform-translate-y: -2rem;
+}
+
+.focus\:-translate-y-10:focus {
+  --transform-translate-y: -2.5rem;
+}
+
+.focus\:-translate-y-12:focus {
+  --transform-translate-y: -3rem;
+}
+
+.focus\:-translate-y-16:focus {
+  --transform-translate-y: -4rem;
+}
+
+.focus\:-translate-y-20:focus {
+  --transform-translate-y: -5rem;
+}
+
+.focus\:-translate-y-24:focus {
+  --transform-translate-y: -6rem;
+}
+
+.focus\:-translate-y-32:focus {
+  --transform-translate-y: -8rem;
+}
+
+.focus\:-translate-y-40:focus {
+  --transform-translate-y: -10rem;
+}
+
+.focus\:-translate-y-48:focus {
+  --transform-translate-y: -12rem;
+}
+
+.focus\:-translate-y-56:focus {
+  --transform-translate-y: -14rem;
+}
+
+.focus\:-translate-y-64:focus {
+  --transform-translate-y: -16rem;
+}
+
+.focus\:-translate-y-px:focus {
+  --transform-translate-y: -1px;
+}
+
+.focus\:-translate-y-full:focus {
+  --transform-translate-y: -100%;
+}
+
+.focus\:-translate-y-1\/2:focus {
+  --transform-translate-y: -50%;
+}
+
+.focus\:translate-y-1\/2:focus {
+  --transform-translate-y: 50%;
+}
+
+.focus\:translate-y-full:focus {
+  --transform-translate-y: 100%;
+}
+
+.skew-x-0 {
+  --transform-skew-x: 0;
+}
+
+.skew-x-3 {
+  --transform-skew-x: 3deg;
+}
+
+.skew-x-6 {
+  --transform-skew-x: 6deg;
+}
+
+.skew-x-12 {
+  --transform-skew-x: 12deg;
+}
+
+.-skew-x-12 {
+  --transform-skew-x: -12deg;
+}
+
+.-skew-x-6 {
+  --transform-skew-x: -6deg;
+}
+
+.-skew-x-3 {
+  --transform-skew-x: -3deg;
+}
+
+.skew-y-0 {
+  --transform-skew-y: 0;
+}
+
+.skew-y-3 {
+  --transform-skew-y: 3deg;
+}
+
+.skew-y-6 {
+  --transform-skew-y: 6deg;
+}
+
+.skew-y-12 {
+  --transform-skew-y: 12deg;
+}
+
+.-skew-y-12 {
+  --transform-skew-y: -12deg;
+}
+
+.-skew-y-6 {
+  --transform-skew-y: -6deg;
+}
+
+.-skew-y-3 {
+  --transform-skew-y: -3deg;
+}
+
+.hover\:skew-x-0:hover {
+  --transform-skew-x: 0;
+}
+
+.hover\:skew-x-3:hover {
+  --transform-skew-x: 3deg;
+}
+
+.hover\:skew-x-6:hover {
+  --transform-skew-x: 6deg;
+}
+
+.hover\:skew-x-12:hover {
+  --transform-skew-x: 12deg;
+}
+
+.hover\:-skew-x-12:hover {
+  --transform-skew-x: -12deg;
+}
+
+.hover\:-skew-x-6:hover {
+  --transform-skew-x: -6deg;
+}
+
+.hover\:-skew-x-3:hover {
+  --transform-skew-x: -3deg;
+}
+
+.hover\:skew-y-0:hover {
+  --transform-skew-y: 0;
+}
+
+.hover\:skew-y-3:hover {
+  --transform-skew-y: 3deg;
+}
+
+.hover\:skew-y-6:hover {
+  --transform-skew-y: 6deg;
+}
+
+.hover\:skew-y-12:hover {
+  --transform-skew-y: 12deg;
+}
+
+.hover\:-skew-y-12:hover {
+  --transform-skew-y: -12deg;
+}
+
+.hover\:-skew-y-6:hover {
+  --transform-skew-y: -6deg;
+}
+
+.hover\:-skew-y-3:hover {
+  --transform-skew-y: -3deg;
+}
+
+.focus\:skew-x-0:focus {
+  --transform-skew-x: 0;
+}
+
+.focus\:skew-x-3:focus {
+  --transform-skew-x: 3deg;
+}
+
+.focus\:skew-x-6:focus {
+  --transform-skew-x: 6deg;
+}
+
+.focus\:skew-x-12:focus {
+  --transform-skew-x: 12deg;
+}
+
+.focus\:-skew-x-12:focus {
+  --transform-skew-x: -12deg;
+}
+
+.focus\:-skew-x-6:focus {
+  --transform-skew-x: -6deg;
+}
+
+.focus\:-skew-x-3:focus {
+  --transform-skew-x: -3deg;
+}
+
+.focus\:skew-y-0:focus {
+  --transform-skew-y: 0;
+}
+
+.focus\:skew-y-3:focus {
+  --transform-skew-y: 3deg;
+}
+
+.focus\:skew-y-6:focus {
+  --transform-skew-y: 6deg;
+}
+
+.focus\:skew-y-12:focus {
+  --transform-skew-y: 12deg;
+}
+
+.focus\:-skew-y-12:focus {
+  --transform-skew-y: -12deg;
+}
+
+.focus\:-skew-y-6:focus {
+  --transform-skew-y: -6deg;
+}
+
+.focus\:-skew-y-3:focus {
+  --transform-skew-y: -3deg;
+}
+
+.transition-none {
+  transition-property: none;
+}
+
+.transition-all {
+  transition-property: all;
+}
+
+.transition {
+  transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+}
+
+.transition-colors {
+  transition-property: background-color, border-color, color, fill, stroke;
+}
+
+.transition-opacity {
+  transition-property: opacity;
+}
+
+.transition-shadow {
+  transition-property: box-shadow;
+}
+
+.transition-transform {
+  transition-property: transform;
+}
+
+.ease-linear {
+  transition-timing-function: linear;
+}
+
+.ease-in {
+  transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+}
+
+.ease-out {
+  transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+}
+
+.ease-in-out {
+  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.duration-75 {
+  transition-duration: 75ms;
+}
+
+.duration-100 {
+  transition-duration: 100ms;
+}
+
+.duration-150 {
+  transition-duration: 150ms;
+}
+
+.duration-200 {
+  transition-duration: 200ms;
+}
+
+.duration-300 {
+  transition-duration: 300ms;
+}
+
+.duration-500 {
+  transition-duration: 500ms;
+}
+
+.duration-700 {
+  transition-duration: 700ms;
+}
+
+.duration-1000 {
+  transition-duration: 1000ms;
+}
+
+.delay-75 {
+  transition-delay: 75ms;
+}
+
+.delay-100 {
+  transition-delay: 100ms;
+}
+
+.delay-150 {
+  transition-delay: 150ms;
+}
+
+.delay-200 {
+  transition-delay: 200ms;
+}
+
+.delay-300 {
+  transition-delay: 300ms;
+}
+
+.delay-500 {
+  transition-delay: 500ms;
+}
+
+.delay-700 {
+  transition-delay: 700ms;
+}
+
+.delay-1000 {
+  transition-delay: 1000ms;
+}
+
+@-webkit-keyframes spin {
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes spin {
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+@-webkit-keyframes ping {
+  75%, 100% {
+    transform: scale(2);
+    opacity: 0;
+  }
+}
+
+@keyframes ping {
+  75%, 100% {
+    transform: scale(2);
+    opacity: 0;
+  }
+}
+
+@-webkit-keyframes pulse {
+  50% {
+    opacity: .5;
+  }
+}
+
+@keyframes pulse {
+  50% {
+    opacity: .5;
+  }
+}
+
+@-webkit-keyframes bounce {
+  0%, 100% {
+    transform: translateY(-25%);
+    -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);
+            animation-timing-function: cubic-bezier(0.8,0,1,1);
+  }
+
+  50% {
+    transform: none;
+    -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);
+            animation-timing-function: cubic-bezier(0,0,0.2,1);
+  }
+}
+
+@keyframes bounce {
+  0%, 100% {
+    transform: translateY(-25%);
+    -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);
+            animation-timing-function: cubic-bezier(0.8,0,1,1);
+  }
+
+  50% {
+    transform: none;
+    -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);
+            animation-timing-function: cubic-bezier(0,0,0.2,1);
+  }
+}
+
+.animate-none {
+  -webkit-animation: none;
+          animation: none;
+}
+
+.animate-spin {
+  -webkit-animation: spin 1s linear infinite;
+          animation: spin 1s linear infinite;
+}
+
+.animate-ping {
+  -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+          animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+}
+
+.animate-pulse {
+  -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+          animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+}
+
+.animate-bounce {
+  -webkit-animation: bounce 1s infinite;
+          animation: bounce 1s infinite;
+}
+
+@media (min-width: 640px) {
+  .sm\:container {
+    width: 100%;
+  }
+
+  @media (min-width: 640px) {
+    .sm\:container {
+      max-width: 640px;
+    }
+  }
+
+  @media (min-width: 768px) {
+    .sm\:container {
+      max-width: 768px;
+    }
+  }
+
+  @media (min-width: 1024px) {
+    .sm\:container {
+      max-width: 1024px;
+    }
+  }
+
+  @media (min-width: 1280px) {
+    .sm\:container {
+      max-width: 1280px;
+    }
+  }
+
+  .sm\:space-y-0 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0px * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-0 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0px * var(--space-x-reverse));
+    margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.25rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.25rem * var(--space-x-reverse));
+    margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.5rem * var(--space-x-reverse));
+    margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.75rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.75rem * var(--space-x-reverse));
+    margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1rem * var(--space-x-reverse));
+    margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.25rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.25rem * var(--space-x-reverse));
+    margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.5rem * var(--space-x-reverse));
+    margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2rem * var(--space-x-reverse));
+    margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2.5rem * var(--space-x-reverse));
+    margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(3rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(3rem * var(--space-x-reverse));
+    margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(4rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(4rem * var(--space-x-reverse));
+    margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(5rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(5rem * var(--space-x-reverse));
+    margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(6rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(6rem * var(--space-x-reverse));
+    margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(8rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(8rem * var(--space-x-reverse));
+    margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(10rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(10rem * var(--space-x-reverse));
+    margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(12rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(12rem * var(--space-x-reverse));
+    margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(14rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(14rem * var(--space-x-reverse));
+    margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(16rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(16rem * var(--space-x-reverse));
+    margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1px * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1px * var(--space-x-reverse));
+    margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.25rem * var(--space-x-reverse));
+    margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.5rem * var(--space-x-reverse));
+    margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.75rem * var(--space-x-reverse));
+    margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1rem * var(--space-x-reverse));
+    margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.25rem * var(--space-x-reverse));
+    margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.5rem * var(--space-x-reverse));
+    margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2rem * var(--space-x-reverse));
+    margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2.5rem * var(--space-x-reverse));
+    margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-3rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-3rem * var(--space-x-reverse));
+    margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-4rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-4rem * var(--space-x-reverse));
+    margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-5rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-5rem * var(--space-x-reverse));
+    margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-6rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-6rem * var(--space-x-reverse));
+    margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-8rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-8rem * var(--space-x-reverse));
+    margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-10rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-10rem * var(--space-x-reverse));
+    margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-12rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-12rem * var(--space-x-reverse));
+    margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-14rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-14rem * var(--space-x-reverse));
+    margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-16rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-16rem * var(--space-x-reverse));
+    margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1px * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1px * var(--space-x-reverse));
+    margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-reverse > :not(template) ~ :not(template) {
+    --space-y-reverse: 1;
+  }
+
+  .sm\:space-x-reverse > :not(template) ~ :not(template) {
+    --space-x-reverse: 1;
+  }
+
+  .sm\:divide-y-0 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(0px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x-0 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(0px * var(--divide-x-reverse));
+    border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y-2 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(2px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x-2 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(2px * var(--divide-x-reverse));
+    border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y-4 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(4px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x-4 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(4px * var(--divide-x-reverse));
+    border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y-8 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(8px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x-8 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(8px * var(--divide-x-reverse));
+    border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(1px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(1px * var(--divide-x-reverse));
+    border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y-reverse > :not(template) ~ :not(template) {
+    --divide-y-reverse: 1;
+  }
+
+  .sm\:divide-x-reverse > :not(template) ~ :not(template) {
+    --divide-x-reverse: 1;
+  }
+
+  .sm\:divide-transparent > :not(template) ~ :not(template) {
+    border-color: transparent;
+  }
+
+  .sm\:divide-current > :not(template) ~ :not(template) {
+    border-color: currentColor;
+  }
+
+  .sm\:divide-black > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--divide-opacity));
+  }
+
+  .sm\:divide-white > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--divide-opacity));
+  }
+
+  .sm\:divide-solid > :not(template) ~ :not(template) {
+    border-style: solid;
+  }
+
+  .sm\:divide-dashed > :not(template) ~ :not(template) {
+    border-style: dashed;
+  }
+
+  .sm\:divide-dotted > :not(template) ~ :not(template) {
+    border-style: dotted;
+  }
+
+  .sm\:divide-double > :not(template) ~ :not(template) {
+    border-style: double;
+  }
+
+  .sm\:divide-none > :not(template) ~ :not(template) {
+    border-style: none;
+  }
+
+  .sm\:divide-opacity-0 > :not(template) ~ :not(template) {
+    --divide-opacity: 0;
+  }
+
+  .sm\:divide-opacity-25 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.25;
+  }
+
+  .sm\:divide-opacity-50 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.5;
+  }
+
+  .sm\:divide-opacity-75 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.75;
+  }
+
+  .sm\:divide-opacity-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+  }
+
+  .sm\:sr-only {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .sm\:not-sr-only {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .sm\:focus\:sr-only:focus {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .sm\:focus\:not-sr-only:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .sm\:appearance-none {
+    -webkit-appearance: none;
+       -moz-appearance: none;
+            appearance: none;
+  }
+
+  .sm\:bg-fixed {
+    background-attachment: fixed;
+  }
+
+  .sm\:bg-local {
+    background-attachment: local;
+  }
+
+  .sm\:bg-scroll {
+    background-attachment: scroll;
+  }
+
+  .sm\:bg-clip-border {
+    background-clip: border-box;
+  }
+
+  .sm\:bg-clip-padding {
+    background-clip: padding-box;
+  }
+
+  .sm\:bg-clip-content {
+    background-clip: content-box;
+  }
+
+  .sm\:bg-clip-text {
+    -webkit-background-clip: text;
+            background-clip: text;
+  }
+
+  .sm\:bg-transparent {
+    background-color: transparent;
+  }
+
+  .sm\:bg-current {
+    background-color: currentColor;
+  }
+
+  .sm\:bg-black {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .sm\:bg-white {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-100 {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-200 {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-300 {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-400 {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-500 {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-600 {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-700 {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-800 {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-900 {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-200 {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-300 {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-400 {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-500 {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-600 {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-700 {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-800 {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-900 {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-100 {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-200 {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-300 {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-400 {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-500 {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-600 {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-700 {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-800 {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-900 {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-100 {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-200 {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-300 {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-400 {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-500 {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-600 {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-700 {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-800 {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-900 {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-100 {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-200 {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-300 {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-400 {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-500 {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-600 {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-700 {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-800 {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-900 {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-100 {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-200 {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-300 {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-400 {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-500 {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-600 {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-700 {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-800 {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-900 {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-100 {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-200 {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-300 {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-400 {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-500 {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-600 {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-700 {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-800 {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-900 {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-100 {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-200 {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-300 {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-400 {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-500 {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-600 {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-700 {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-800 {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-900 {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-100 {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-200 {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-300 {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-400 {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-500 {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-600 {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-700 {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-800 {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-900 {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-200 {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-300 {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-400 {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-500 {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-600 {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-700 {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-800 {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-900 {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-transparent:hover {
+    background-color: transparent;
+  }
+
+  .sm\:hover\:bg-current:hover {
+    background-color: currentColor;
+  }
+
+  .sm\:hover\:bg-black:hover {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-white:hover {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-100:hover {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-200:hover {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-300:hover {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-400:hover {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-500:hover {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-600:hover {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-700:hover {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-800:hover {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-900:hover {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-300:hover {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-400:hover {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-500:hover {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-600:hover {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-700:hover {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-800:hover {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-900:hover {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-200:hover {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-600:hover {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-700:hover {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-800:hover {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-900:hover {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-200:hover {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-300:hover {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-500:hover {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-600:hover {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-700:hover {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-800:hover {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-900:hover {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-100:hover {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-200:hover {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-300:hover {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-400:hover {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-500:hover {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-600:hover {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-700:hover {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-800:hover {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-900:hover {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-100:hover {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-200:hover {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-300:hover {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-400:hover {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-500:hover {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-600:hover {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-700:hover {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-800:hover {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-900:hover {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-200:hover {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-300:hover {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-400:hover {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-500:hover {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-600:hover {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-700:hover {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-800:hover {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-900:hover {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-200:hover {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-300:hover {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-400:hover {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-500:hover {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-600:hover {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-700:hover {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-800:hover {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-900:hover {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-100:hover {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-200:hover {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-300:hover {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-400:hover {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-500:hover {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-600:hover {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-700:hover {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-800:hover {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-900:hover {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-400:hover {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-600:hover {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-700:hover {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-800:hover {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-900:hover {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-transparent:focus {
+    background-color: transparent;
+  }
+
+  .sm\:focus\:bg-current:focus {
+    background-color: currentColor;
+  }
+
+  .sm\:focus\:bg-black:focus {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-white:focus {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-100:focus {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-200:focus {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-300:focus {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-400:focus {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-500:focus {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-600:focus {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-700:focus {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-800:focus {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-900:focus {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-300:focus {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-400:focus {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-500:focus {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-600:focus {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-700:focus {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-800:focus {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-900:focus {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-200:focus {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-600:focus {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-700:focus {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-800:focus {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-900:focus {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-200:focus {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-300:focus {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-500:focus {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-600:focus {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-700:focus {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-800:focus {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-900:focus {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-100:focus {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-200:focus {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-300:focus {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-400:focus {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-500:focus {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-600:focus {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-700:focus {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-800:focus {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-900:focus {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-100:focus {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-200:focus {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-300:focus {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-400:focus {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-500:focus {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-600:focus {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-700:focus {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-800:focus {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-900:focus {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-200:focus {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-300:focus {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-400:focus {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-500:focus {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-600:focus {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-700:focus {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-800:focus {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-900:focus {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-200:focus {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-300:focus {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-400:focus {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-500:focus {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-600:focus {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-700:focus {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-800:focus {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-900:focus {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-100:focus {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-200:focus {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-300:focus {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-400:focus {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-500:focus {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-600:focus {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-700:focus {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-800:focus {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-900:focus {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-400:focus {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-600:focus {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-700:focus {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-800:focus {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-900:focus {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .sm\:bg-none {
+    background-image: none;
+  }
+
+  .sm\:bg-gradient-to-t {
+    background-image: linear-gradient(to top, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-tr {
+    background-image: linear-gradient(to top right, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-r {
+    background-image: linear-gradient(to right, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-br {
+    background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-b {
+    background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-bl {
+    background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-l {
+    background-image: linear-gradient(to left, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-tl {
+    background-image: linear-gradient(to top left, var(--gradient-color-stops));
+  }
+
+  .sm\:from-transparent {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:from-current {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:from-black {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:from-white {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:from-gray-100 {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:from-gray-200 {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:from-gray-300 {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:from-gray-400 {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:from-gray-500 {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:from-gray-600 {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:from-gray-700 {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:from-gray-800 {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:from-gray-900 {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:from-red-100 {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:from-red-200 {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:from-red-300 {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:from-red-400 {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:from-red-500 {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:from-red-600 {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:from-red-700 {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:from-red-800 {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:from-red-900 {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:from-orange-100 {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:from-orange-200 {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:from-orange-300 {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:from-orange-400 {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:from-orange-500 {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:from-orange-600 {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:from-orange-700 {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:from-orange-800 {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:from-orange-900 {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:from-yellow-100 {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:from-yellow-200 {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:from-yellow-300 {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:from-yellow-400 {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:from-yellow-500 {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:from-yellow-600 {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:from-yellow-700 {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:from-yellow-800 {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:from-yellow-900 {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:from-green-100 {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:from-green-200 {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:from-green-300 {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:from-green-400 {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:from-green-500 {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:from-green-600 {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:from-green-700 {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:from-green-800 {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:from-green-900 {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:from-teal-100 {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:from-teal-200 {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:from-teal-300 {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:from-teal-400 {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:from-teal-500 {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:from-teal-600 {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:from-teal-700 {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:from-teal-800 {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:from-teal-900 {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:from-blue-100 {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:from-blue-200 {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:from-blue-300 {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:from-blue-400 {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:from-blue-500 {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:from-blue-600 {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:from-blue-700 {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:from-blue-800 {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:from-blue-900 {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:from-indigo-100 {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:from-indigo-200 {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:from-indigo-300 {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:from-indigo-400 {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:from-indigo-500 {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:from-indigo-600 {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:from-indigo-700 {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:from-indigo-800 {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:from-indigo-900 {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:from-purple-100 {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:from-purple-200 {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:from-purple-300 {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:from-purple-400 {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:from-purple-500 {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:from-purple-600 {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:from-purple-700 {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:from-purple-800 {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:from-purple-900 {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:from-pink-100 {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:from-pink-200 {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:from-pink-300 {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:from-pink-400 {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:from-pink-500 {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:from-pink-600 {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:from-pink-700 {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:from-pink-800 {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:from-pink-900 {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:via-transparent {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:via-current {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:via-black {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:via-white {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:via-gray-100 {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:via-gray-200 {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:via-gray-300 {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:via-gray-400 {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:via-gray-500 {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:via-gray-600 {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:via-gray-700 {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:via-gray-800 {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:via-gray-900 {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:via-red-100 {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:via-red-200 {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:via-red-300 {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:via-red-400 {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:via-red-500 {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:via-red-600 {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:via-red-700 {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:via-red-800 {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:via-red-900 {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:via-orange-100 {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:via-orange-200 {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:via-orange-300 {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:via-orange-400 {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:via-orange-500 {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:via-orange-600 {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:via-orange-700 {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:via-orange-800 {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:via-orange-900 {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:via-yellow-100 {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:via-yellow-200 {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:via-yellow-300 {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:via-yellow-400 {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:via-yellow-500 {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:via-yellow-600 {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:via-yellow-700 {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:via-yellow-800 {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:via-yellow-900 {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:via-green-100 {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:via-green-200 {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:via-green-300 {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:via-green-400 {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:via-green-500 {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:via-green-600 {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:via-green-700 {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:via-green-800 {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:via-green-900 {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:via-teal-100 {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:via-teal-200 {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:via-teal-300 {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:via-teal-400 {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:via-teal-500 {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:via-teal-600 {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:via-teal-700 {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:via-teal-800 {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:via-teal-900 {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:via-blue-100 {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:via-blue-200 {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:via-blue-300 {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:via-blue-400 {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:via-blue-500 {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:via-blue-600 {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:via-blue-700 {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:via-blue-800 {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:via-blue-900 {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:via-indigo-100 {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:via-indigo-200 {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:via-indigo-300 {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:via-indigo-400 {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:via-indigo-500 {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:via-indigo-600 {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:via-indigo-700 {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:via-indigo-800 {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:via-indigo-900 {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:via-purple-100 {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:via-purple-200 {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:via-purple-300 {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:via-purple-400 {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:via-purple-500 {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:via-purple-600 {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:via-purple-700 {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:via-purple-800 {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:via-purple-900 {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:via-pink-100 {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:via-pink-200 {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:via-pink-300 {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:via-pink-400 {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:via-pink-500 {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:via-pink-600 {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:via-pink-700 {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:via-pink-800 {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:via-pink-900 {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:to-transparent {
+    --gradient-to-color: transparent;
+  }
+
+  .sm\:to-current {
+    --gradient-to-color: currentColor;
+  }
+
+  .sm\:to-black {
+    --gradient-to-color: #000;
+  }
+
+  .sm\:to-white {
+    --gradient-to-color: #fff;
+  }
+
+  .sm\:to-gray-100 {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .sm\:to-gray-200 {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .sm\:to-gray-300 {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .sm\:to-gray-400 {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .sm\:to-gray-500 {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .sm\:to-gray-600 {
+    --gradient-to-color: #718096;
+  }
+
+  .sm\:to-gray-700 {
+    --gradient-to-color: #4a5568;
+  }
+
+  .sm\:to-gray-800 {
+    --gradient-to-color: #2d3748;
+  }
+
+  .sm\:to-gray-900 {
+    --gradient-to-color: #1a202c;
+  }
+
+  .sm\:to-red-100 {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .sm\:to-red-200 {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .sm\:to-red-300 {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .sm\:to-red-400 {
+    --gradient-to-color: #fc8181;
+  }
+
+  .sm\:to-red-500 {
+    --gradient-to-color: #f56565;
+  }
+
+  .sm\:to-red-600 {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .sm\:to-red-700 {
+    --gradient-to-color: #c53030;
+  }
+
+  .sm\:to-red-800 {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .sm\:to-red-900 {
+    --gradient-to-color: #742a2a;
+  }
+
+  .sm\:to-orange-100 {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .sm\:to-orange-200 {
+    --gradient-to-color: #feebc8;
+  }
+
+  .sm\:to-orange-300 {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .sm\:to-orange-400 {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .sm\:to-orange-500 {
+    --gradient-to-color: #ed8936;
+  }
+
+  .sm\:to-orange-600 {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .sm\:to-orange-700 {
+    --gradient-to-color: #c05621;
+  }
+
+  .sm\:to-orange-800 {
+    --gradient-to-color: #9c4221;
+  }
+
+  .sm\:to-orange-900 {
+    --gradient-to-color: #7b341e;
+  }
+
+  .sm\:to-yellow-100 {
+    --gradient-to-color: #fffff0;
+  }
+
+  .sm\:to-yellow-200 {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .sm\:to-yellow-300 {
+    --gradient-to-color: #faf089;
+  }
+
+  .sm\:to-yellow-400 {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .sm\:to-yellow-500 {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .sm\:to-yellow-600 {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .sm\:to-yellow-700 {
+    --gradient-to-color: #b7791f;
+  }
+
+  .sm\:to-yellow-800 {
+    --gradient-to-color: #975a16;
+  }
+
+  .sm\:to-yellow-900 {
+    --gradient-to-color: #744210;
+  }
+
+  .sm\:to-green-100 {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .sm\:to-green-200 {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .sm\:to-green-300 {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .sm\:to-green-400 {
+    --gradient-to-color: #68d391;
+  }
+
+  .sm\:to-green-500 {
+    --gradient-to-color: #48bb78;
+  }
+
+  .sm\:to-green-600 {
+    --gradient-to-color: #38a169;
+  }
+
+  .sm\:to-green-700 {
+    --gradient-to-color: #2f855a;
+  }
+
+  .sm\:to-green-800 {
+    --gradient-to-color: #276749;
+  }
+
+  .sm\:to-green-900 {
+    --gradient-to-color: #22543d;
+  }
+
+  .sm\:to-teal-100 {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .sm\:to-teal-200 {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .sm\:to-teal-300 {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .sm\:to-teal-400 {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .sm\:to-teal-500 {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .sm\:to-teal-600 {
+    --gradient-to-color: #319795;
+  }
+
+  .sm\:to-teal-700 {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .sm\:to-teal-800 {
+    --gradient-to-color: #285e61;
+  }
+
+  .sm\:to-teal-900 {
+    --gradient-to-color: #234e52;
+  }
+
+  .sm\:to-blue-100 {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .sm\:to-blue-200 {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .sm\:to-blue-300 {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .sm\:to-blue-400 {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .sm\:to-blue-500 {
+    --gradient-to-color: #4299e1;
+  }
+
+  .sm\:to-blue-600 {
+    --gradient-to-color: #3182ce;
+  }
+
+  .sm\:to-blue-700 {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .sm\:to-blue-800 {
+    --gradient-to-color: #2c5282;
+  }
+
+  .sm\:to-blue-900 {
+    --gradient-to-color: #2a4365;
+  }
+
+  .sm\:to-indigo-100 {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .sm\:to-indigo-200 {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .sm\:to-indigo-300 {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .sm\:to-indigo-400 {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .sm\:to-indigo-500 {
+    --gradient-to-color: #667eea;
+  }
+
+  .sm\:to-indigo-600 {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .sm\:to-indigo-700 {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .sm\:to-indigo-800 {
+    --gradient-to-color: #434190;
+  }
+
+  .sm\:to-indigo-900 {
+    --gradient-to-color: #3c366b;
+  }
+
+  .sm\:to-purple-100 {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .sm\:to-purple-200 {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .sm\:to-purple-300 {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .sm\:to-purple-400 {
+    --gradient-to-color: #b794f4;
+  }
+
+  .sm\:to-purple-500 {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .sm\:to-purple-600 {
+    --gradient-to-color: #805ad5;
+  }
+
+  .sm\:to-purple-700 {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .sm\:to-purple-800 {
+    --gradient-to-color: #553c9a;
+  }
+
+  .sm\:to-purple-900 {
+    --gradient-to-color: #44337a;
+  }
+
+  .sm\:to-pink-100 {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .sm\:to-pink-200 {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .sm\:to-pink-300 {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .sm\:to-pink-400 {
+    --gradient-to-color: #f687b3;
+  }
+
+  .sm\:to-pink-500 {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .sm\:to-pink-600 {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .sm\:to-pink-700 {
+    --gradient-to-color: #b83280;
+  }
+
+  .sm\:to-pink-800 {
+    --gradient-to-color: #97266d;
+  }
+
+  .sm\:to-pink-900 {
+    --gradient-to-color: #702459;
+  }
+
+  .sm\:hover\:from-transparent:hover {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:hover\:from-current:hover {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:hover\:from-black:hover {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:hover\:from-white:hover {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:hover\:from-gray-100:hover {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:hover\:from-gray-200:hover {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:hover\:from-gray-300:hover {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:hover\:from-gray-400:hover {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:hover\:from-gray-500:hover {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:hover\:from-gray-600:hover {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:hover\:from-gray-700:hover {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:hover\:from-gray-800:hover {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:hover\:from-gray-900:hover {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:hover\:from-red-100:hover {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:hover\:from-red-200:hover {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:hover\:from-red-300:hover {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:hover\:from-red-400:hover {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:hover\:from-red-500:hover {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:hover\:from-red-600:hover {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:hover\:from-red-700:hover {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:hover\:from-red-800:hover {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:hover\:from-red-900:hover {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:hover\:from-orange-100:hover {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:hover\:from-orange-200:hover {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:hover\:from-orange-300:hover {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:hover\:from-orange-400:hover {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:hover\:from-orange-500:hover {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:hover\:from-orange-600:hover {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:hover\:from-orange-700:hover {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:hover\:from-orange-800:hover {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:hover\:from-orange-900:hover {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:hover\:from-yellow-100:hover {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:hover\:from-yellow-200:hover {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:hover\:from-yellow-300:hover {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:hover\:from-yellow-400:hover {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:hover\:from-yellow-500:hover {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:hover\:from-yellow-600:hover {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:hover\:from-yellow-700:hover {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:hover\:from-yellow-800:hover {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:hover\:from-yellow-900:hover {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:hover\:from-green-100:hover {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:hover\:from-green-200:hover {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:hover\:from-green-300:hover {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:hover\:from-green-400:hover {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:hover\:from-green-500:hover {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:hover\:from-green-600:hover {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:hover\:from-green-700:hover {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:hover\:from-green-800:hover {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:hover\:from-green-900:hover {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:hover\:from-teal-100:hover {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:hover\:from-teal-200:hover {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:hover\:from-teal-300:hover {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:hover\:from-teal-400:hover {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:hover\:from-teal-500:hover {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:hover\:from-teal-600:hover {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:hover\:from-teal-700:hover {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:hover\:from-teal-800:hover {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:hover\:from-teal-900:hover {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:hover\:from-blue-100:hover {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:hover\:from-blue-200:hover {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:hover\:from-blue-300:hover {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:hover\:from-blue-400:hover {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:hover\:from-blue-500:hover {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:hover\:from-blue-600:hover {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:hover\:from-blue-700:hover {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:hover\:from-blue-800:hover {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:hover\:from-blue-900:hover {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:hover\:from-indigo-100:hover {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:hover\:from-indigo-200:hover {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:hover\:from-indigo-300:hover {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:hover\:from-indigo-400:hover {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:hover\:from-indigo-500:hover {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:hover\:from-indigo-600:hover {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:hover\:from-indigo-700:hover {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:hover\:from-indigo-800:hover {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:hover\:from-indigo-900:hover {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:hover\:from-purple-100:hover {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:hover\:from-purple-200:hover {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:hover\:from-purple-300:hover {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:hover\:from-purple-400:hover {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:hover\:from-purple-500:hover {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:hover\:from-purple-600:hover {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:hover\:from-purple-700:hover {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:hover\:from-purple-800:hover {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:hover\:from-purple-900:hover {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:hover\:from-pink-100:hover {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:hover\:from-pink-200:hover {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:hover\:from-pink-300:hover {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:hover\:from-pink-400:hover {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:hover\:from-pink-500:hover {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:hover\:from-pink-600:hover {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:hover\:from-pink-700:hover {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:hover\:from-pink-800:hover {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:hover\:from-pink-900:hover {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:hover\:via-transparent:hover {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:hover\:via-current:hover {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:hover\:via-black:hover {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:hover\:via-white:hover {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:hover\:via-gray-100:hover {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:hover\:via-gray-200:hover {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:hover\:via-gray-300:hover {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:hover\:via-gray-400:hover {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:hover\:via-gray-500:hover {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:hover\:via-gray-600:hover {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:hover\:via-gray-700:hover {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:hover\:via-gray-800:hover {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:hover\:via-gray-900:hover {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:hover\:via-red-100:hover {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:hover\:via-red-200:hover {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:hover\:via-red-300:hover {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:hover\:via-red-400:hover {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:hover\:via-red-500:hover {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:hover\:via-red-600:hover {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:hover\:via-red-700:hover {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:hover\:via-red-800:hover {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:hover\:via-red-900:hover {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:hover\:via-orange-100:hover {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:hover\:via-orange-200:hover {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:hover\:via-orange-300:hover {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:hover\:via-orange-400:hover {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:hover\:via-orange-500:hover {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:hover\:via-orange-600:hover {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:hover\:via-orange-700:hover {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:hover\:via-orange-800:hover {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:hover\:via-orange-900:hover {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:hover\:via-yellow-100:hover {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:hover\:via-yellow-200:hover {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:hover\:via-yellow-300:hover {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:hover\:via-yellow-400:hover {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:hover\:via-yellow-500:hover {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:hover\:via-yellow-600:hover {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:hover\:via-yellow-700:hover {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:hover\:via-yellow-800:hover {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:hover\:via-yellow-900:hover {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:hover\:via-green-100:hover {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:hover\:via-green-200:hover {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:hover\:via-green-300:hover {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:hover\:via-green-400:hover {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:hover\:via-green-500:hover {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:hover\:via-green-600:hover {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:hover\:via-green-700:hover {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:hover\:via-green-800:hover {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:hover\:via-green-900:hover {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:hover\:via-teal-100:hover {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:hover\:via-teal-200:hover {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:hover\:via-teal-300:hover {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:hover\:via-teal-400:hover {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:hover\:via-teal-500:hover {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:hover\:via-teal-600:hover {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:hover\:via-teal-700:hover {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:hover\:via-teal-800:hover {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:hover\:via-teal-900:hover {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:hover\:via-blue-100:hover {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:hover\:via-blue-200:hover {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:hover\:via-blue-300:hover {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:hover\:via-blue-400:hover {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:hover\:via-blue-500:hover {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:hover\:via-blue-600:hover {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:hover\:via-blue-700:hover {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:hover\:via-blue-800:hover {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:hover\:via-blue-900:hover {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:hover\:via-indigo-100:hover {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:hover\:via-indigo-200:hover {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:hover\:via-indigo-300:hover {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:hover\:via-indigo-400:hover {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:hover\:via-indigo-500:hover {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:hover\:via-indigo-600:hover {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:hover\:via-indigo-700:hover {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:hover\:via-indigo-800:hover {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:hover\:via-indigo-900:hover {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:hover\:via-purple-100:hover {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:hover\:via-purple-200:hover {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:hover\:via-purple-300:hover {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:hover\:via-purple-400:hover {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:hover\:via-purple-500:hover {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:hover\:via-purple-600:hover {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:hover\:via-purple-700:hover {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:hover\:via-purple-800:hover {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:hover\:via-purple-900:hover {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:hover\:via-pink-100:hover {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:hover\:via-pink-200:hover {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:hover\:via-pink-300:hover {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:hover\:via-pink-400:hover {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:hover\:via-pink-500:hover {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:hover\:via-pink-600:hover {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:hover\:via-pink-700:hover {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:hover\:via-pink-800:hover {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:hover\:via-pink-900:hover {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:hover\:to-transparent:hover {
+    --gradient-to-color: transparent;
+  }
+
+  .sm\:hover\:to-current:hover {
+    --gradient-to-color: currentColor;
+  }
+
+  .sm\:hover\:to-black:hover {
+    --gradient-to-color: #000;
+  }
+
+  .sm\:hover\:to-white:hover {
+    --gradient-to-color: #fff;
+  }
+
+  .sm\:hover\:to-gray-100:hover {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .sm\:hover\:to-gray-200:hover {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .sm\:hover\:to-gray-300:hover {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .sm\:hover\:to-gray-400:hover {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .sm\:hover\:to-gray-500:hover {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .sm\:hover\:to-gray-600:hover {
+    --gradient-to-color: #718096;
+  }
+
+  .sm\:hover\:to-gray-700:hover {
+    --gradient-to-color: #4a5568;
+  }
+
+  .sm\:hover\:to-gray-800:hover {
+    --gradient-to-color: #2d3748;
+  }
+
+  .sm\:hover\:to-gray-900:hover {
+    --gradient-to-color: #1a202c;
+  }
+
+  .sm\:hover\:to-red-100:hover {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .sm\:hover\:to-red-200:hover {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .sm\:hover\:to-red-300:hover {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .sm\:hover\:to-red-400:hover {
+    --gradient-to-color: #fc8181;
+  }
+
+  .sm\:hover\:to-red-500:hover {
+    --gradient-to-color: #f56565;
+  }
+
+  .sm\:hover\:to-red-600:hover {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .sm\:hover\:to-red-700:hover {
+    --gradient-to-color: #c53030;
+  }
+
+  .sm\:hover\:to-red-800:hover {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .sm\:hover\:to-red-900:hover {
+    --gradient-to-color: #742a2a;
+  }
+
+  .sm\:hover\:to-orange-100:hover {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .sm\:hover\:to-orange-200:hover {
+    --gradient-to-color: #feebc8;
+  }
+
+  .sm\:hover\:to-orange-300:hover {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .sm\:hover\:to-orange-400:hover {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .sm\:hover\:to-orange-500:hover {
+    --gradient-to-color: #ed8936;
+  }
+
+  .sm\:hover\:to-orange-600:hover {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .sm\:hover\:to-orange-700:hover {
+    --gradient-to-color: #c05621;
+  }
+
+  .sm\:hover\:to-orange-800:hover {
+    --gradient-to-color: #9c4221;
+  }
+
+  .sm\:hover\:to-orange-900:hover {
+    --gradient-to-color: #7b341e;
+  }
+
+  .sm\:hover\:to-yellow-100:hover {
+    --gradient-to-color: #fffff0;
+  }
+
+  .sm\:hover\:to-yellow-200:hover {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .sm\:hover\:to-yellow-300:hover {
+    --gradient-to-color: #faf089;
+  }
+
+  .sm\:hover\:to-yellow-400:hover {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .sm\:hover\:to-yellow-500:hover {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .sm\:hover\:to-yellow-600:hover {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .sm\:hover\:to-yellow-700:hover {
+    --gradient-to-color: #b7791f;
+  }
+
+  .sm\:hover\:to-yellow-800:hover {
+    --gradient-to-color: #975a16;
+  }
+
+  .sm\:hover\:to-yellow-900:hover {
+    --gradient-to-color: #744210;
+  }
+
+  .sm\:hover\:to-green-100:hover {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .sm\:hover\:to-green-200:hover {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .sm\:hover\:to-green-300:hover {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .sm\:hover\:to-green-400:hover {
+    --gradient-to-color: #68d391;
+  }
+
+  .sm\:hover\:to-green-500:hover {
+    --gradient-to-color: #48bb78;
+  }
+
+  .sm\:hover\:to-green-600:hover {
+    --gradient-to-color: #38a169;
+  }
+
+  .sm\:hover\:to-green-700:hover {
+    --gradient-to-color: #2f855a;
+  }
+
+  .sm\:hover\:to-green-800:hover {
+    --gradient-to-color: #276749;
+  }
+
+  .sm\:hover\:to-green-900:hover {
+    --gradient-to-color: #22543d;
+  }
+
+  .sm\:hover\:to-teal-100:hover {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .sm\:hover\:to-teal-200:hover {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .sm\:hover\:to-teal-300:hover {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .sm\:hover\:to-teal-400:hover {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .sm\:hover\:to-teal-500:hover {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .sm\:hover\:to-teal-600:hover {
+    --gradient-to-color: #319795;
+  }
+
+  .sm\:hover\:to-teal-700:hover {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .sm\:hover\:to-teal-800:hover {
+    --gradient-to-color: #285e61;
+  }
+
+  .sm\:hover\:to-teal-900:hover {
+    --gradient-to-color: #234e52;
+  }
+
+  .sm\:hover\:to-blue-100:hover {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .sm\:hover\:to-blue-200:hover {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .sm\:hover\:to-blue-300:hover {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .sm\:hover\:to-blue-400:hover {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .sm\:hover\:to-blue-500:hover {
+    --gradient-to-color: #4299e1;
+  }
+
+  .sm\:hover\:to-blue-600:hover {
+    --gradient-to-color: #3182ce;
+  }
+
+  .sm\:hover\:to-blue-700:hover {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .sm\:hover\:to-blue-800:hover {
+    --gradient-to-color: #2c5282;
+  }
+
+  .sm\:hover\:to-blue-900:hover {
+    --gradient-to-color: #2a4365;
+  }
+
+  .sm\:hover\:to-indigo-100:hover {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .sm\:hover\:to-indigo-200:hover {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .sm\:hover\:to-indigo-300:hover {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .sm\:hover\:to-indigo-400:hover {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .sm\:hover\:to-indigo-500:hover {
+    --gradient-to-color: #667eea;
+  }
+
+  .sm\:hover\:to-indigo-600:hover {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .sm\:hover\:to-indigo-700:hover {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .sm\:hover\:to-indigo-800:hover {
+    --gradient-to-color: #434190;
+  }
+
+  .sm\:hover\:to-indigo-900:hover {
+    --gradient-to-color: #3c366b;
+  }
+
+  .sm\:hover\:to-purple-100:hover {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .sm\:hover\:to-purple-200:hover {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .sm\:hover\:to-purple-300:hover {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .sm\:hover\:to-purple-400:hover {
+    --gradient-to-color: #b794f4;
+  }
+
+  .sm\:hover\:to-purple-500:hover {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .sm\:hover\:to-purple-600:hover {
+    --gradient-to-color: #805ad5;
+  }
+
+  .sm\:hover\:to-purple-700:hover {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .sm\:hover\:to-purple-800:hover {
+    --gradient-to-color: #553c9a;
+  }
+
+  .sm\:hover\:to-purple-900:hover {
+    --gradient-to-color: #44337a;
+  }
+
+  .sm\:hover\:to-pink-100:hover {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .sm\:hover\:to-pink-200:hover {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .sm\:hover\:to-pink-300:hover {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .sm\:hover\:to-pink-400:hover {
+    --gradient-to-color: #f687b3;
+  }
+
+  .sm\:hover\:to-pink-500:hover {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .sm\:hover\:to-pink-600:hover {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .sm\:hover\:to-pink-700:hover {
+    --gradient-to-color: #b83280;
+  }
+
+  .sm\:hover\:to-pink-800:hover {
+    --gradient-to-color: #97266d;
+  }
+
+  .sm\:hover\:to-pink-900:hover {
+    --gradient-to-color: #702459;
+  }
+
+  .sm\:focus\:from-transparent:focus {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:focus\:from-current:focus {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:focus\:from-black:focus {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:focus\:from-white:focus {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:focus\:from-gray-100:focus {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:focus\:from-gray-200:focus {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:focus\:from-gray-300:focus {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:focus\:from-gray-400:focus {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:focus\:from-gray-500:focus {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:focus\:from-gray-600:focus {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:focus\:from-gray-700:focus {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:focus\:from-gray-800:focus {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:focus\:from-gray-900:focus {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:focus\:from-red-100:focus {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:focus\:from-red-200:focus {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:focus\:from-red-300:focus {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:focus\:from-red-400:focus {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:focus\:from-red-500:focus {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:focus\:from-red-600:focus {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:focus\:from-red-700:focus {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:focus\:from-red-800:focus {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:focus\:from-red-900:focus {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:focus\:from-orange-100:focus {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:focus\:from-orange-200:focus {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:focus\:from-orange-300:focus {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:focus\:from-orange-400:focus {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:focus\:from-orange-500:focus {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:focus\:from-orange-600:focus {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:focus\:from-orange-700:focus {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:focus\:from-orange-800:focus {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:focus\:from-orange-900:focus {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:focus\:from-yellow-100:focus {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:focus\:from-yellow-200:focus {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:focus\:from-yellow-300:focus {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:focus\:from-yellow-400:focus {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:focus\:from-yellow-500:focus {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:focus\:from-yellow-600:focus {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:focus\:from-yellow-700:focus {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:focus\:from-yellow-800:focus {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:focus\:from-yellow-900:focus {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:focus\:from-green-100:focus {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:focus\:from-green-200:focus {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:focus\:from-green-300:focus {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:focus\:from-green-400:focus {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:focus\:from-green-500:focus {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:focus\:from-green-600:focus {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:focus\:from-green-700:focus {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:focus\:from-green-800:focus {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:focus\:from-green-900:focus {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:focus\:from-teal-100:focus {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:focus\:from-teal-200:focus {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:focus\:from-teal-300:focus {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:focus\:from-teal-400:focus {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:focus\:from-teal-500:focus {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:focus\:from-teal-600:focus {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:focus\:from-teal-700:focus {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:focus\:from-teal-800:focus {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:focus\:from-teal-900:focus {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:focus\:from-blue-100:focus {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:focus\:from-blue-200:focus {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:focus\:from-blue-300:focus {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:focus\:from-blue-400:focus {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:focus\:from-blue-500:focus {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:focus\:from-blue-600:focus {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:focus\:from-blue-700:focus {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:focus\:from-blue-800:focus {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:focus\:from-blue-900:focus {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:focus\:from-indigo-100:focus {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:focus\:from-indigo-200:focus {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:focus\:from-indigo-300:focus {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:focus\:from-indigo-400:focus {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:focus\:from-indigo-500:focus {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:focus\:from-indigo-600:focus {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:focus\:from-indigo-700:focus {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:focus\:from-indigo-800:focus {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:focus\:from-indigo-900:focus {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:focus\:from-purple-100:focus {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:focus\:from-purple-200:focus {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:focus\:from-purple-300:focus {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:focus\:from-purple-400:focus {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:focus\:from-purple-500:focus {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:focus\:from-purple-600:focus {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:focus\:from-purple-700:focus {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:focus\:from-purple-800:focus {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:focus\:from-purple-900:focus {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:focus\:from-pink-100:focus {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:focus\:from-pink-200:focus {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:focus\:from-pink-300:focus {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:focus\:from-pink-400:focus {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:focus\:from-pink-500:focus {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:focus\:from-pink-600:focus {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:focus\:from-pink-700:focus {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:focus\:from-pink-800:focus {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:focus\:from-pink-900:focus {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:focus\:via-transparent:focus {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:focus\:via-current:focus {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:focus\:via-black:focus {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:focus\:via-white:focus {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:focus\:via-gray-100:focus {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:focus\:via-gray-200:focus {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:focus\:via-gray-300:focus {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:focus\:via-gray-400:focus {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:focus\:via-gray-500:focus {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:focus\:via-gray-600:focus {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:focus\:via-gray-700:focus {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:focus\:via-gray-800:focus {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:focus\:via-gray-900:focus {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:focus\:via-red-100:focus {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:focus\:via-red-200:focus {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:focus\:via-red-300:focus {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:focus\:via-red-400:focus {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:focus\:via-red-500:focus {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:focus\:via-red-600:focus {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:focus\:via-red-700:focus {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:focus\:via-red-800:focus {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:focus\:via-red-900:focus {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:focus\:via-orange-100:focus {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:focus\:via-orange-200:focus {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:focus\:via-orange-300:focus {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:focus\:via-orange-400:focus {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:focus\:via-orange-500:focus {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:focus\:via-orange-600:focus {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:focus\:via-orange-700:focus {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:focus\:via-orange-800:focus {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:focus\:via-orange-900:focus {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:focus\:via-yellow-100:focus {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:focus\:via-yellow-200:focus {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:focus\:via-yellow-300:focus {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:focus\:via-yellow-400:focus {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:focus\:via-yellow-500:focus {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:focus\:via-yellow-600:focus {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:focus\:via-yellow-700:focus {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:focus\:via-yellow-800:focus {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:focus\:via-yellow-900:focus {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:focus\:via-green-100:focus {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:focus\:via-green-200:focus {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:focus\:via-green-300:focus {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:focus\:via-green-400:focus {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:focus\:via-green-500:focus {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:focus\:via-green-600:focus {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:focus\:via-green-700:focus {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:focus\:via-green-800:focus {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:focus\:via-green-900:focus {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:focus\:via-teal-100:focus {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:focus\:via-teal-200:focus {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:focus\:via-teal-300:focus {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:focus\:via-teal-400:focus {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:focus\:via-teal-500:focus {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:focus\:via-teal-600:focus {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:focus\:via-teal-700:focus {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:focus\:via-teal-800:focus {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:focus\:via-teal-900:focus {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:focus\:via-blue-100:focus {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:focus\:via-blue-200:focus {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:focus\:via-blue-300:focus {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:focus\:via-blue-400:focus {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:focus\:via-blue-500:focus {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:focus\:via-blue-600:focus {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:focus\:via-blue-700:focus {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:focus\:via-blue-800:focus {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:focus\:via-blue-900:focus {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:focus\:via-indigo-100:focus {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:focus\:via-indigo-200:focus {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:focus\:via-indigo-300:focus {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:focus\:via-indigo-400:focus {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:focus\:via-indigo-500:focus {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:focus\:via-indigo-600:focus {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:focus\:via-indigo-700:focus {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:focus\:via-indigo-800:focus {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:focus\:via-indigo-900:focus {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:focus\:via-purple-100:focus {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:focus\:via-purple-200:focus {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:focus\:via-purple-300:focus {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:focus\:via-purple-400:focus {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:focus\:via-purple-500:focus {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:focus\:via-purple-600:focus {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:focus\:via-purple-700:focus {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:focus\:via-purple-800:focus {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:focus\:via-purple-900:focus {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:focus\:via-pink-100:focus {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:focus\:via-pink-200:focus {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:focus\:via-pink-300:focus {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:focus\:via-pink-400:focus {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:focus\:via-pink-500:focus {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:focus\:via-pink-600:focus {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:focus\:via-pink-700:focus {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:focus\:via-pink-800:focus {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:focus\:via-pink-900:focus {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:focus\:to-transparent:focus {
+    --gradient-to-color: transparent;
+  }
+
+  .sm\:focus\:to-current:focus {
+    --gradient-to-color: currentColor;
+  }
+
+  .sm\:focus\:to-black:focus {
+    --gradient-to-color: #000;
+  }
+
+  .sm\:focus\:to-white:focus {
+    --gradient-to-color: #fff;
+  }
+
+  .sm\:focus\:to-gray-100:focus {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .sm\:focus\:to-gray-200:focus {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .sm\:focus\:to-gray-300:focus {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .sm\:focus\:to-gray-400:focus {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .sm\:focus\:to-gray-500:focus {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .sm\:focus\:to-gray-600:focus {
+    --gradient-to-color: #718096;
+  }
+
+  .sm\:focus\:to-gray-700:focus {
+    --gradient-to-color: #4a5568;
+  }
+
+  .sm\:focus\:to-gray-800:focus {
+    --gradient-to-color: #2d3748;
+  }
+
+  .sm\:focus\:to-gray-900:focus {
+    --gradient-to-color: #1a202c;
+  }
+
+  .sm\:focus\:to-red-100:focus {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .sm\:focus\:to-red-200:focus {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .sm\:focus\:to-red-300:focus {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .sm\:focus\:to-red-400:focus {
+    --gradient-to-color: #fc8181;
+  }
+
+  .sm\:focus\:to-red-500:focus {
+    --gradient-to-color: #f56565;
+  }
+
+  .sm\:focus\:to-red-600:focus {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .sm\:focus\:to-red-700:focus {
+    --gradient-to-color: #c53030;
+  }
+
+  .sm\:focus\:to-red-800:focus {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .sm\:focus\:to-red-900:focus {
+    --gradient-to-color: #742a2a;
+  }
+
+  .sm\:focus\:to-orange-100:focus {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .sm\:focus\:to-orange-200:focus {
+    --gradient-to-color: #feebc8;
+  }
+
+  .sm\:focus\:to-orange-300:focus {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .sm\:focus\:to-orange-400:focus {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .sm\:focus\:to-orange-500:focus {
+    --gradient-to-color: #ed8936;
+  }
+
+  .sm\:focus\:to-orange-600:focus {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .sm\:focus\:to-orange-700:focus {
+    --gradient-to-color: #c05621;
+  }
+
+  .sm\:focus\:to-orange-800:focus {
+    --gradient-to-color: #9c4221;
+  }
+
+  .sm\:focus\:to-orange-900:focus {
+    --gradient-to-color: #7b341e;
+  }
+
+  .sm\:focus\:to-yellow-100:focus {
+    --gradient-to-color: #fffff0;
+  }
+
+  .sm\:focus\:to-yellow-200:focus {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .sm\:focus\:to-yellow-300:focus {
+    --gradient-to-color: #faf089;
+  }
+
+  .sm\:focus\:to-yellow-400:focus {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .sm\:focus\:to-yellow-500:focus {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .sm\:focus\:to-yellow-600:focus {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .sm\:focus\:to-yellow-700:focus {
+    --gradient-to-color: #b7791f;
+  }
+
+  .sm\:focus\:to-yellow-800:focus {
+    --gradient-to-color: #975a16;
+  }
+
+  .sm\:focus\:to-yellow-900:focus {
+    --gradient-to-color: #744210;
+  }
+
+  .sm\:focus\:to-green-100:focus {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .sm\:focus\:to-green-200:focus {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .sm\:focus\:to-green-300:focus {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .sm\:focus\:to-green-400:focus {
+    --gradient-to-color: #68d391;
+  }
+
+  .sm\:focus\:to-green-500:focus {
+    --gradient-to-color: #48bb78;
+  }
+
+  .sm\:focus\:to-green-600:focus {
+    --gradient-to-color: #38a169;
+  }
+
+  .sm\:focus\:to-green-700:focus {
+    --gradient-to-color: #2f855a;
+  }
+
+  .sm\:focus\:to-green-800:focus {
+    --gradient-to-color: #276749;
+  }
+
+  .sm\:focus\:to-green-900:focus {
+    --gradient-to-color: #22543d;
+  }
+
+  .sm\:focus\:to-teal-100:focus {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .sm\:focus\:to-teal-200:focus {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .sm\:focus\:to-teal-300:focus {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .sm\:focus\:to-teal-400:focus {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .sm\:focus\:to-teal-500:focus {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .sm\:focus\:to-teal-600:focus {
+    --gradient-to-color: #319795;
+  }
+
+  .sm\:focus\:to-teal-700:focus {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .sm\:focus\:to-teal-800:focus {
+    --gradient-to-color: #285e61;
+  }
+
+  .sm\:focus\:to-teal-900:focus {
+    --gradient-to-color: #234e52;
+  }
+
+  .sm\:focus\:to-blue-100:focus {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .sm\:focus\:to-blue-200:focus {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .sm\:focus\:to-blue-300:focus {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .sm\:focus\:to-blue-400:focus {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .sm\:focus\:to-blue-500:focus {
+    --gradient-to-color: #4299e1;
+  }
+
+  .sm\:focus\:to-blue-600:focus {
+    --gradient-to-color: #3182ce;
+  }
+
+  .sm\:focus\:to-blue-700:focus {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .sm\:focus\:to-blue-800:focus {
+    --gradient-to-color: #2c5282;
+  }
+
+  .sm\:focus\:to-blue-900:focus {
+    --gradient-to-color: #2a4365;
+  }
+
+  .sm\:focus\:to-indigo-100:focus {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .sm\:focus\:to-indigo-200:focus {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .sm\:focus\:to-indigo-300:focus {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .sm\:focus\:to-indigo-400:focus {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .sm\:focus\:to-indigo-500:focus {
+    --gradient-to-color: #667eea;
+  }
+
+  .sm\:focus\:to-indigo-600:focus {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .sm\:focus\:to-indigo-700:focus {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .sm\:focus\:to-indigo-800:focus {
+    --gradient-to-color: #434190;
+  }
+
+  .sm\:focus\:to-indigo-900:focus {
+    --gradient-to-color: #3c366b;
+  }
+
+  .sm\:focus\:to-purple-100:focus {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .sm\:focus\:to-purple-200:focus {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .sm\:focus\:to-purple-300:focus {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .sm\:focus\:to-purple-400:focus {
+    --gradient-to-color: #b794f4;
+  }
+
+  .sm\:focus\:to-purple-500:focus {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .sm\:focus\:to-purple-600:focus {
+    --gradient-to-color: #805ad5;
+  }
+
+  .sm\:focus\:to-purple-700:focus {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .sm\:focus\:to-purple-800:focus {
+    --gradient-to-color: #553c9a;
+  }
+
+  .sm\:focus\:to-purple-900:focus {
+    --gradient-to-color: #44337a;
+  }
+
+  .sm\:focus\:to-pink-100:focus {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .sm\:focus\:to-pink-200:focus {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .sm\:focus\:to-pink-300:focus {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .sm\:focus\:to-pink-400:focus {
+    --gradient-to-color: #f687b3;
+  }
+
+  .sm\:focus\:to-pink-500:focus {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .sm\:focus\:to-pink-600:focus {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .sm\:focus\:to-pink-700:focus {
+    --gradient-to-color: #b83280;
+  }
+
+  .sm\:focus\:to-pink-800:focus {
+    --gradient-to-color: #97266d;
+  }
+
+  .sm\:focus\:to-pink-900:focus {
+    --gradient-to-color: #702459;
+  }
+
+  .sm\:bg-opacity-0 {
+    --bg-opacity: 0;
+  }
+
+  .sm\:bg-opacity-25 {
+    --bg-opacity: 0.25;
+  }
+
+  .sm\:bg-opacity-50 {
+    --bg-opacity: 0.5;
+  }
+
+  .sm\:bg-opacity-75 {
+    --bg-opacity: 0.75;
+  }
+
+  .sm\:bg-opacity-100 {
+    --bg-opacity: 1;
+  }
+
+  .sm\:hover\:bg-opacity-0:hover {
+    --bg-opacity: 0;
+  }
+
+  .sm\:hover\:bg-opacity-25:hover {
+    --bg-opacity: 0.25;
+  }
+
+  .sm\:hover\:bg-opacity-50:hover {
+    --bg-opacity: 0.5;
+  }
+
+  .sm\:hover\:bg-opacity-75:hover {
+    --bg-opacity: 0.75;
+  }
+
+  .sm\:hover\:bg-opacity-100:hover {
+    --bg-opacity: 1;
+  }
+
+  .sm\:focus\:bg-opacity-0:focus {
+    --bg-opacity: 0;
+  }
+
+  .sm\:focus\:bg-opacity-25:focus {
+    --bg-opacity: 0.25;
+  }
+
+  .sm\:focus\:bg-opacity-50:focus {
+    --bg-opacity: 0.5;
+  }
+
+  .sm\:focus\:bg-opacity-75:focus {
+    --bg-opacity: 0.75;
+  }
+
+  .sm\:focus\:bg-opacity-100:focus {
+    --bg-opacity: 1;
+  }
+
+  .sm\:bg-bottom {
+    background-position: bottom;
+  }
+
+  .sm\:bg-center {
+    background-position: center;
+  }
+
+  .sm\:bg-left {
+    background-position: left;
+  }
+
+  .sm\:bg-left-bottom {
+    background-position: left bottom;
+  }
+
+  .sm\:bg-left-top {
+    background-position: left top;
+  }
+
+  .sm\:bg-right {
+    background-position: right;
+  }
+
+  .sm\:bg-right-bottom {
+    background-position: right bottom;
+  }
+
+  .sm\:bg-right-top {
+    background-position: right top;
+  }
+
+  .sm\:bg-top {
+    background-position: top;
+  }
+
+  .sm\:bg-repeat {
+    background-repeat: repeat;
+  }
+
+  .sm\:bg-no-repeat {
+    background-repeat: no-repeat;
+  }
+
+  .sm\:bg-repeat-x {
+    background-repeat: repeat-x;
+  }
+
+  .sm\:bg-repeat-y {
+    background-repeat: repeat-y;
+  }
+
+  .sm\:bg-repeat-round {
+    background-repeat: round;
+  }
+
+  .sm\:bg-repeat-space {
+    background-repeat: space;
+  }
+
+  .sm\:bg-auto {
+    background-size: auto;
+  }
+
+  .sm\:bg-cover {
+    background-size: cover;
+  }
+
+  .sm\:bg-contain {
+    background-size: contain;
+  }
+
+  .sm\:border-collapse {
+    border-collapse: collapse;
+  }
+
+  .sm\:border-separate {
+    border-collapse: separate;
+  }
+
+  .sm\:border-transparent {
+    border-color: transparent;
+  }
+
+  .sm\:border-current {
+    border-color: currentColor;
+  }
+
+  .sm\:border-black {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .sm\:border-white {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .sm\:border-gray-100 {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .sm\:border-gray-200 {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .sm\:border-gray-300 {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .sm\:border-gray-400 {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .sm\:border-gray-500 {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .sm\:border-gray-600 {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .sm\:border-gray-700 {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .sm\:border-gray-800 {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .sm\:border-gray-900 {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .sm\:border-red-100 {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .sm\:border-red-200 {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .sm\:border-red-300 {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .sm\:border-red-400 {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .sm\:border-red-500 {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .sm\:border-red-600 {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .sm\:border-red-700 {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .sm\:border-red-800 {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .sm\:border-red-900 {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .sm\:border-orange-100 {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .sm\:border-orange-200 {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .sm\:border-orange-300 {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .sm\:border-orange-400 {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .sm\:border-orange-500 {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .sm\:border-orange-600 {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .sm\:border-orange-700 {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .sm\:border-orange-800 {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .sm\:border-orange-900 {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-100 {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-200 {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-300 {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-400 {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-500 {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-600 {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-700 {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-800 {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-900 {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .sm\:border-green-100 {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .sm\:border-green-200 {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .sm\:border-green-300 {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .sm\:border-green-400 {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .sm\:border-green-500 {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .sm\:border-green-600 {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .sm\:border-green-700 {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .sm\:border-green-800 {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .sm\:border-green-900 {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .sm\:border-teal-100 {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .sm\:border-teal-200 {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .sm\:border-teal-300 {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .sm\:border-teal-400 {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .sm\:border-teal-500 {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .sm\:border-teal-600 {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .sm\:border-teal-700 {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .sm\:border-teal-800 {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .sm\:border-teal-900 {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .sm\:border-blue-100 {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .sm\:border-blue-200 {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .sm\:border-blue-300 {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .sm\:border-blue-400 {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .sm\:border-blue-500 {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .sm\:border-blue-600 {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .sm\:border-blue-700 {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .sm\:border-blue-800 {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .sm\:border-blue-900 {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-100 {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-200 {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-300 {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-400 {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-500 {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-600 {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-700 {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-800 {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-900 {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .sm\:border-purple-100 {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .sm\:border-purple-200 {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .sm\:border-purple-300 {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .sm\:border-purple-400 {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .sm\:border-purple-500 {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .sm\:border-purple-600 {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .sm\:border-purple-700 {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .sm\:border-purple-800 {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .sm\:border-purple-900 {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .sm\:border-pink-100 {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .sm\:border-pink-200 {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .sm\:border-pink-300 {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .sm\:border-pink-400 {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .sm\:border-pink-500 {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .sm\:border-pink-600 {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .sm\:border-pink-700 {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .sm\:border-pink-800 {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .sm\:border-pink-900 {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-transparent:hover {
+    border-color: transparent;
+  }
+
+  .sm\:hover\:border-current:hover {
+    border-color: currentColor;
+  }
+
+  .sm\:hover\:border-black:hover {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-white:hover {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-100:hover {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-200:hover {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-300:hover {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-400:hover {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-500:hover {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-600:hover {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-700:hover {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-800:hover {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-900:hover {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-300:hover {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-400:hover {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-500:hover {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-600:hover {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-700:hover {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-800:hover {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-900:hover {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-100:hover {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-200:hover {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-300:hover {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-400:hover {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-500:hover {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-600:hover {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-700:hover {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-800:hover {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-900:hover {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-100:hover {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-200:hover {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-300:hover {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-400:hover {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-500:hover {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-600:hover {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-700:hover {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-800:hover {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-900:hover {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-100:hover {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-200:hover {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-300:hover {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-400:hover {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-500:hover {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-600:hover {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-700:hover {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-800:hover {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-900:hover {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-100:hover {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-200:hover {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-300:hover {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-400:hover {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-500:hover {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-600:hover {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-700:hover {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-800:hover {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-900:hover {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-200:hover {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-300:hover {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-400:hover {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-500:hover {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-600:hover {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-700:hover {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-800:hover {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-900:hover {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-200:hover {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-300:hover {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-400:hover {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-500:hover {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-600:hover {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-700:hover {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-800:hover {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-900:hover {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-100:hover {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-200:hover {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-300:hover {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-400:hover {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-500:hover {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-600:hover {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-700:hover {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-800:hover {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-900:hover {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-300:hover {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-400:hover {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-500:hover {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-600:hover {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-700:hover {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-800:hover {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-900:hover {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-transparent:focus {
+    border-color: transparent;
+  }
+
+  .sm\:focus\:border-current:focus {
+    border-color: currentColor;
+  }
+
+  .sm\:focus\:border-black:focus {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-white:focus {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-100:focus {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-200:focus {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-300:focus {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-400:focus {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-500:focus {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-600:focus {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-700:focus {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-800:focus {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-900:focus {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-300:focus {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-400:focus {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-500:focus {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-600:focus {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-700:focus {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-800:focus {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-900:focus {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-100:focus {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-200:focus {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-300:focus {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-400:focus {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-500:focus {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-600:focus {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-700:focus {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-800:focus {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-900:focus {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-100:focus {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-200:focus {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-300:focus {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-400:focus {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-500:focus {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-600:focus {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-700:focus {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-800:focus {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-900:focus {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-100:focus {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-200:focus {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-300:focus {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-400:focus {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-500:focus {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-600:focus {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-700:focus {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-800:focus {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-900:focus {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-100:focus {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-200:focus {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-300:focus {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-400:focus {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-500:focus {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-600:focus {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-700:focus {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-800:focus {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-900:focus {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-200:focus {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-300:focus {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-400:focus {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-500:focus {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-600:focus {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-700:focus {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-800:focus {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-900:focus {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-200:focus {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-300:focus {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-400:focus {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-500:focus {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-600:focus {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-700:focus {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-800:focus {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-900:focus {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-100:focus {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-200:focus {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-300:focus {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-400:focus {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-500:focus {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-600:focus {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-700:focus {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-800:focus {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-900:focus {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-300:focus {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-400:focus {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-500:focus {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-600:focus {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-700:focus {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-800:focus {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-900:focus {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .sm\:border-opacity-0 {
+    --border-opacity: 0;
+  }
+
+  .sm\:border-opacity-25 {
+    --border-opacity: 0.25;
+  }
+
+  .sm\:border-opacity-50 {
+    --border-opacity: 0.5;
+  }
+
+  .sm\:border-opacity-75 {
+    --border-opacity: 0.75;
+  }
+
+  .sm\:border-opacity-100 {
+    --border-opacity: 1;
+  }
+
+  .sm\:hover\:border-opacity-0:hover {
+    --border-opacity: 0;
+  }
+
+  .sm\:hover\:border-opacity-25:hover {
+    --border-opacity: 0.25;
+  }
+
+  .sm\:hover\:border-opacity-50:hover {
+    --border-opacity: 0.5;
+  }
+
+  .sm\:hover\:border-opacity-75:hover {
+    --border-opacity: 0.75;
+  }
+
+  .sm\:hover\:border-opacity-100:hover {
+    --border-opacity: 1;
+  }
+
+  .sm\:focus\:border-opacity-0:focus {
+    --border-opacity: 0;
+  }
+
+  .sm\:focus\:border-opacity-25:focus {
+    --border-opacity: 0.25;
+  }
+
+  .sm\:focus\:border-opacity-50:focus {
+    --border-opacity: 0.5;
+  }
+
+  .sm\:focus\:border-opacity-75:focus {
+    --border-opacity: 0.75;
+  }
+
+  .sm\:focus\:border-opacity-100:focus {
+    --border-opacity: 1;
+  }
+
+  .sm\:rounded-none {
+    border-radius: 0;
+  }
+
+  .sm\:rounded-sm {
+    border-radius: 0.125rem;
+  }
+
+  .sm\:rounded {
+    border-radius: 0.25rem;
+  }
+
+  .sm\:rounded-md {
+    border-radius: 0.375rem;
+  }
+
+  .sm\:rounded-lg {
+    border-radius: 0.5rem;
+  }
+
+  .sm\:rounded-full {
+    border-radius: 9999px;
+  }
+
+  .sm\:rounded-t-none {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+  }
+
+  .sm\:rounded-r-none {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+  }
+
+  .sm\:rounded-b-none {
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .sm\:rounded-l-none {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .sm\:rounded-t-sm {
+    border-top-left-radius: 0.125rem;
+    border-top-right-radius: 0.125rem;
+  }
+
+  .sm\:rounded-r-sm {
+    border-top-right-radius: 0.125rem;
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .sm\:rounded-b-sm {
+    border-bottom-right-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .sm\:rounded-l-sm {
+    border-top-left-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .sm\:rounded-t {
+    border-top-left-radius: 0.25rem;
+    border-top-right-radius: 0.25rem;
+  }
+
+  .sm\:rounded-r {
+    border-top-right-radius: 0.25rem;
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .sm\:rounded-b {
+    border-bottom-right-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .sm\:rounded-l {
+    border-top-left-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .sm\:rounded-t-md {
+    border-top-left-radius: 0.375rem;
+    border-top-right-radius: 0.375rem;
+  }
+
+  .sm\:rounded-r-md {
+    border-top-right-radius: 0.375rem;
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .sm\:rounded-b-md {
+    border-bottom-right-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .sm\:rounded-l-md {
+    border-top-left-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .sm\:rounded-t-lg {
+    border-top-left-radius: 0.5rem;
+    border-top-right-radius: 0.5rem;
+  }
+
+  .sm\:rounded-r-lg {
+    border-top-right-radius: 0.5rem;
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .sm\:rounded-b-lg {
+    border-bottom-right-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .sm\:rounded-l-lg {
+    border-top-left-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .sm\:rounded-t-full {
+    border-top-left-radius: 9999px;
+    border-top-right-radius: 9999px;
+  }
+
+  .sm\:rounded-r-full {
+    border-top-right-radius: 9999px;
+    border-bottom-right-radius: 9999px;
+  }
+
+  .sm\:rounded-b-full {
+    border-bottom-right-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .sm\:rounded-l-full {
+    border-top-left-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .sm\:rounded-tl-none {
+    border-top-left-radius: 0;
+  }
+
+  .sm\:rounded-tr-none {
+    border-top-right-radius: 0;
+  }
+
+  .sm\:rounded-br-none {
+    border-bottom-right-radius: 0;
+  }
+
+  .sm\:rounded-bl-none {
+    border-bottom-left-radius: 0;
+  }
+
+  .sm\:rounded-tl-sm {
+    border-top-left-radius: 0.125rem;
+  }
+
+  .sm\:rounded-tr-sm {
+    border-top-right-radius: 0.125rem;
+  }
+
+  .sm\:rounded-br-sm {
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .sm\:rounded-bl-sm {
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .sm\:rounded-tl {
+    border-top-left-radius: 0.25rem;
+  }
+
+  .sm\:rounded-tr {
+    border-top-right-radius: 0.25rem;
+  }
+
+  .sm\:rounded-br {
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .sm\:rounded-bl {
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .sm\:rounded-tl-md {
+    border-top-left-radius: 0.375rem;
+  }
+
+  .sm\:rounded-tr-md {
+    border-top-right-radius: 0.375rem;
+  }
+
+  .sm\:rounded-br-md {
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .sm\:rounded-bl-md {
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .sm\:rounded-tl-lg {
+    border-top-left-radius: 0.5rem;
+  }
+
+  .sm\:rounded-tr-lg {
+    border-top-right-radius: 0.5rem;
+  }
+
+  .sm\:rounded-br-lg {
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .sm\:rounded-bl-lg {
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .sm\:rounded-tl-full {
+    border-top-left-radius: 9999px;
+  }
+
+  .sm\:rounded-tr-full {
+    border-top-right-radius: 9999px;
+  }
+
+  .sm\:rounded-br-full {
+    border-bottom-right-radius: 9999px;
+  }
+
+  .sm\:rounded-bl-full {
+    border-bottom-left-radius: 9999px;
+  }
+
+  .sm\:border-solid {
+    border-style: solid;
+  }
+
+  .sm\:border-dashed {
+    border-style: dashed;
+  }
+
+  .sm\:border-dotted {
+    border-style: dotted;
+  }
+
+  .sm\:border-double {
+    border-style: double;
+  }
+
+  .sm\:border-none {
+    border-style: none;
+  }
+
+  .sm\:border-0 {
+    border-width: 0;
+  }
+
+  .sm\:border-2 {
+    border-width: 2px;
+  }
+
+  .sm\:border-4 {
+    border-width: 4px;
+  }
+
+  .sm\:border-8 {
+    border-width: 8px;
+  }
+
+  .sm\:border {
+    border-width: 1px;
+  }
+
+  .sm\:border-t-0 {
+    border-top-width: 0;
+  }
+
+  .sm\:border-r-0 {
+    border-right-width: 0;
+  }
+
+  .sm\:border-b-0 {
+    border-bottom-width: 0;
+  }
+
+  .sm\:border-l-0 {
+    border-left-width: 0;
+  }
+
+  .sm\:border-t-2 {
+    border-top-width: 2px;
+  }
+
+  .sm\:border-r-2 {
+    border-right-width: 2px;
+  }
+
+  .sm\:border-b-2 {
+    border-bottom-width: 2px;
+  }
+
+  .sm\:border-l-2 {
+    border-left-width: 2px;
+  }
+
+  .sm\:border-t-4 {
+    border-top-width: 4px;
+  }
+
+  .sm\:border-r-4 {
+    border-right-width: 4px;
+  }
+
+  .sm\:border-b-4 {
+    border-bottom-width: 4px;
+  }
+
+  .sm\:border-l-4 {
+    border-left-width: 4px;
+  }
+
+  .sm\:border-t-8 {
+    border-top-width: 8px;
+  }
+
+  .sm\:border-r-8 {
+    border-right-width: 8px;
+  }
+
+  .sm\:border-b-8 {
+    border-bottom-width: 8px;
+  }
+
+  .sm\:border-l-8 {
+    border-left-width: 8px;
+  }
+
+  .sm\:border-t {
+    border-top-width: 1px;
+  }
+
+  .sm\:border-r {
+    border-right-width: 1px;
+  }
+
+  .sm\:border-b {
+    border-bottom-width: 1px;
+  }
+
+  .sm\:border-l {
+    border-left-width: 1px;
+  }
+
+  .sm\:box-border {
+    box-sizing: border-box;
+  }
+
+  .sm\:box-content {
+    box-sizing: content-box;
+  }
+
+  .sm\:cursor-auto {
+    cursor: auto;
+  }
+
+  .sm\:cursor-default {
+    cursor: default;
+  }
+
+  .sm\:cursor-pointer {
+    cursor: pointer;
+  }
+
+  .sm\:cursor-wait {
+    cursor: wait;
+  }
+
+  .sm\:cursor-text {
+    cursor: text;
+  }
+
+  .sm\:cursor-move {
+    cursor: move;
+  }
+
+  .sm\:cursor-not-allowed {
+    cursor: not-allowed;
+  }
+
+  .sm\:block {
+    display: block;
+  }
+
+  .sm\:inline-block {
+    display: inline-block;
+  }
+
+  .sm\:inline {
+    display: inline;
+  }
+
+  .sm\:flex {
+    display: flex;
+  }
+
+  .sm\:inline-flex {
+    display: inline-flex;
+  }
+
+  .sm\:table {
+    display: table;
+  }
+
+  .sm\:table-caption {
+    display: table-caption;
+  }
+
+  .sm\:table-cell {
+    display: table-cell;
+  }
+
+  .sm\:table-column {
+    display: table-column;
+  }
+
+  .sm\:table-column-group {
+    display: table-column-group;
+  }
+
+  .sm\:table-footer-group {
+    display: table-footer-group;
+  }
+
+  .sm\:table-header-group {
+    display: table-header-group;
+  }
+
+  .sm\:table-row-group {
+    display: table-row-group;
+  }
+
+  .sm\:table-row {
+    display: table-row;
+  }
+
+  .sm\:flow-root {
+    display: flow-root;
+  }
+
+  .sm\:grid {
+    display: grid;
+  }
+
+  .sm\:inline-grid {
+    display: inline-grid;
+  }
+
+  .sm\:contents {
+    display: contents;
+  }
+
+  .sm\:hidden {
+    display: none;
+  }
+
+  .sm\:flex-row {
+    flex-direction: row;
+  }
+
+  .sm\:flex-row-reverse {
+    flex-direction: row-reverse;
+  }
+
+  .sm\:flex-col {
+    flex-direction: column;
+  }
+
+  .sm\:flex-col-reverse {
+    flex-direction: column-reverse;
+  }
+
+  .sm\:flex-wrap {
+    flex-wrap: wrap;
+  }
+
+  .sm\:flex-wrap-reverse {
+    flex-wrap: wrap-reverse;
+  }
+
+  .sm\:flex-no-wrap {
+    flex-wrap: nowrap;
+  }
+
+  .sm\:place-items-auto {
+    place-items: auto;
+  }
+
+  .sm\:place-items-start {
+    place-items: start;
+  }
+
+  .sm\:place-items-end {
+    place-items: end;
+  }
+
+  .sm\:place-items-center {
+    place-items: center;
+  }
+
+  .sm\:place-items-stretch {
+    place-items: stretch;
+  }
+
+  .sm\:place-content-center {
+    place-content: center;
+  }
+
+  .sm\:place-content-start {
+    place-content: start;
+  }
+
+  .sm\:place-content-end {
+    place-content: end;
+  }
+
+  .sm\:place-content-between {
+    place-content: space-between;
+  }
+
+  .sm\:place-content-around {
+    place-content: space-around;
+  }
+
+  .sm\:place-content-evenly {
+    place-content: space-evenly;
+  }
+
+  .sm\:place-content-stretch {
+    place-content: stretch;
+  }
+
+  .sm\:place-self-auto {
+    place-self: auto;
+  }
+
+  .sm\:place-self-start {
+    place-self: start;
+  }
+
+  .sm\:place-self-end {
+    place-self: end;
+  }
+
+  .sm\:place-self-center {
+    place-self: center;
+  }
+
+  .sm\:place-self-stretch {
+    place-self: stretch;
+  }
+
+  .sm\:items-start {
+    align-items: flex-start;
+  }
+
+  .sm\:items-end {
+    align-items: flex-end;
+  }
+
+  .sm\:items-center {
+    align-items: center;
+  }
+
+  .sm\:items-baseline {
+    align-items: baseline;
+  }
+
+  .sm\:items-stretch {
+    align-items: stretch;
+  }
+
+  .sm\:content-center {
+    align-content: center;
+  }
+
+  .sm\:content-start {
+    align-content: flex-start;
+  }
+
+  .sm\:content-end {
+    align-content: flex-end;
+  }
+
+  .sm\:content-between {
+    align-content: space-between;
+  }
+
+  .sm\:content-around {
+    align-content: space-around;
+  }
+
+  .sm\:content-evenly {
+    align-content: space-evenly;
+  }
+
+  .sm\:self-auto {
+    align-self: auto;
+  }
+
+  .sm\:self-start {
+    align-self: flex-start;
+  }
+
+  .sm\:self-end {
+    align-self: flex-end;
+  }
+
+  .sm\:self-center {
+    align-self: center;
+  }
+
+  .sm\:self-stretch {
+    align-self: stretch;
+  }
+
+  .sm\:justify-items-auto {
+    justify-items: auto;
+  }
+
+  .sm\:justify-items-start {
+    justify-items: start;
+  }
+
+  .sm\:justify-items-end {
+    justify-items: end;
+  }
+
+  .sm\:justify-items-center {
+    justify-items: center;
+  }
+
+  .sm\:justify-items-stretch {
+    justify-items: stretch;
+  }
+
+  .sm\:justify-start {
+    justify-content: flex-start;
+  }
+
+  .sm\:justify-end {
+    justify-content: flex-end;
+  }
+
+  .sm\:justify-center {
+    justify-content: center;
+  }
+
+  .sm\:justify-between {
+    justify-content: space-between;
+  }
+
+  .sm\:justify-around {
+    justify-content: space-around;
+  }
+
+  .sm\:justify-evenly {
+    justify-content: space-evenly;
+  }
+
+  .sm\:justify-self-auto {
+    justify-self: auto;
+  }
+
+  .sm\:justify-self-start {
+    justify-self: start;
+  }
+
+  .sm\:justify-self-end {
+    justify-self: end;
+  }
+
+  .sm\:justify-self-center {
+    justify-self: center;
+  }
+
+  .sm\:justify-self-stretch {
+    justify-self: stretch;
+  }
+
+  .sm\:flex-1 {
+    flex: 1 1 0%;
+  }
+
+  .sm\:flex-auto {
+    flex: 1 1 auto;
+  }
+
+  .sm\:flex-initial {
+    flex: 0 1 auto;
+  }
+
+  .sm\:flex-none {
+    flex: none;
+  }
+
+  .sm\:flex-grow-0 {
+    flex-grow: 0;
+  }
+
+  .sm\:flex-grow {
+    flex-grow: 1;
+  }
+
+  .sm\:flex-shrink-0 {
+    flex-shrink: 0;
+  }
+
+  .sm\:flex-shrink {
+    flex-shrink: 1;
+  }
+
+  .sm\:order-1 {
+    order: 1;
+  }
+
+  .sm\:order-2 {
+    order: 2;
+  }
+
+  .sm\:order-3 {
+    order: 3;
+  }
+
+  .sm\:order-4 {
+    order: 4;
+  }
+
+  .sm\:order-5 {
+    order: 5;
+  }
+
+  .sm\:order-6 {
+    order: 6;
+  }
+
+  .sm\:order-7 {
+    order: 7;
+  }
+
+  .sm\:order-8 {
+    order: 8;
+  }
+
+  .sm\:order-9 {
+    order: 9;
+  }
+
+  .sm\:order-10 {
+    order: 10;
+  }
+
+  .sm\:order-11 {
+    order: 11;
+  }
+
+  .sm\:order-12 {
+    order: 12;
+  }
+
+  .sm\:order-first {
+    order: -9999;
+  }
+
+  .sm\:order-last {
+    order: 9999;
+  }
+
+  .sm\:order-none {
+    order: 0;
+  }
+
+  .sm\:float-right {
+    float: right;
+  }
+
+  .sm\:float-left {
+    float: left;
+  }
+
+  .sm\:float-none {
+    float: none;
+  }
+
+  .sm\:clearfix:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+
+  .sm\:clear-left {
+    clear: left;
+  }
+
+  .sm\:clear-right {
+    clear: right;
+  }
+
+  .sm\:clear-both {
+    clear: both;
+  }
+
+  .sm\:clear-none {
+    clear: none;
+  }
+
+  .sm\:font-sans {
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  }
+
+  .sm\:font-serif {
+    font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+  }
+
+  .sm\:font-mono {
+    font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+  }
+
+  .sm\:font-hairline {
+    font-weight: 100;
+  }
+
+  .sm\:font-thin {
+    font-weight: 200;
+  }
+
+  .sm\:font-light {
+    font-weight: 300;
+  }
+
+  .sm\:font-normal {
+    font-weight: 400;
+  }
+
+  .sm\:font-medium {
+    font-weight: 500;
+  }
+
+  .sm\:font-semibold {
+    font-weight: 600;
+  }
+
+  .sm\:font-bold {
+    font-weight: 700;
+  }
+
+  .sm\:font-extrabold {
+    font-weight: 800;
+  }
+
+  .sm\:font-black {
+    font-weight: 900;
+  }
+
+  .sm\:hover\:font-hairline:hover {
+    font-weight: 100;
+  }
+
+  .sm\:hover\:font-thin:hover {
+    font-weight: 200;
+  }
+
+  .sm\:hover\:font-light:hover {
+    font-weight: 300;
+  }
+
+  .sm\:hover\:font-normal:hover {
+    font-weight: 400;
+  }
+
+  .sm\:hover\:font-medium:hover {
+    font-weight: 500;
+  }
+
+  .sm\:hover\:font-semibold:hover {
+    font-weight: 600;
+  }
+
+  .sm\:hover\:font-bold:hover {
+    font-weight: 700;
+  }
+
+  .sm\:hover\:font-extrabold:hover {
+    font-weight: 800;
+  }
+
+  .sm\:hover\:font-black:hover {
+    font-weight: 900;
+  }
+
+  .sm\:focus\:font-hairline:focus {
+    font-weight: 100;
+  }
+
+  .sm\:focus\:font-thin:focus {
+    font-weight: 200;
+  }
+
+  .sm\:focus\:font-light:focus {
+    font-weight: 300;
+  }
+
+  .sm\:focus\:font-normal:focus {
+    font-weight: 400;
+  }
+
+  .sm\:focus\:font-medium:focus {
+    font-weight: 500;
+  }
+
+  .sm\:focus\:font-semibold:focus {
+    font-weight: 600;
+  }
+
+  .sm\:focus\:font-bold:focus {
+    font-weight: 700;
+  }
+
+  .sm\:focus\:font-extrabold:focus {
+    font-weight: 800;
+  }
+
+  .sm\:focus\:font-black:focus {
+    font-weight: 900;
+  }
+
+  .sm\:h-0 {
+    height: 0;
+  }
+
+  .sm\:h-1 {
+    height: 0.25rem;
+  }
+
+  .sm\:h-2 {
+    height: 0.5rem;
+  }
+
+  .sm\:h-3 {
+    height: 0.75rem;
+  }
+
+  .sm\:h-4 {
+    height: 1rem;
+  }
+
+  .sm\:h-5 {
+    height: 1.25rem;
+  }
+
+  .sm\:h-6 {
+    height: 1.5rem;
+  }
+
+  .sm\:h-8 {
+    height: 2rem;
+  }
+
+  .sm\:h-10 {
+    height: 2.5rem;
+  }
+
+  .sm\:h-12 {
+    height: 3rem;
+  }
+
+  .sm\:h-16 {
+    height: 4rem;
+  }
+
+  .sm\:h-20 {
+    height: 5rem;
+  }
+
+  .sm\:h-24 {
+    height: 6rem;
+  }
+
+  .sm\:h-32 {
+    height: 8rem;
+  }
+
+  .sm\:h-40 {
+    height: 10rem;
+  }
+
+  .sm\:h-48 {
+    height: 12rem;
+  }
+
+  .sm\:h-56 {
+    height: 14rem;
+  }
+
+  .sm\:h-64 {
+    height: 16rem;
+  }
+
+  .sm\:h-auto {
+    height: auto;
+  }
+
+  .sm\:h-px {
+    height: 1px;
+  }
+
+  .sm\:h-full {
+    height: 100%;
+  }
+
+  .sm\:h-screen {
+    height: 100vh;
+  }
+
+  .sm\:text-xs {
+    font-size: 0.75rem;
+  }
+
+  .sm\:text-sm {
+    font-size: 0.875rem;
+  }
+
+  .sm\:text-base {
+    font-size: 1rem;
+  }
+
+  .sm\:text-lg {
+    font-size: 1.125rem;
+  }
+
+  .sm\:text-xl {
+    font-size: 1.25rem;
+  }
+
+  .sm\:text-2xl {
+    font-size: 1.5rem;
+  }
+
+  .sm\:text-3xl {
+    font-size: 1.875rem;
+  }
+
+  .sm\:text-4xl {
+    font-size: 2.25rem;
+  }
+
+  .sm\:text-5xl {
+    font-size: 3rem;
+  }
+
+  .sm\:text-6xl {
+    font-size: 4rem;
+  }
+
+  .sm\:leading-3 {
+    line-height: .75rem;
+  }
+
+  .sm\:leading-4 {
+    line-height: 1rem;
+  }
+
+  .sm\:leading-5 {
+    line-height: 1.25rem;
+  }
+
+  .sm\:leading-6 {
+    line-height: 1.5rem;
+  }
+
+  .sm\:leading-7 {
+    line-height: 1.75rem;
+  }
+
+  .sm\:leading-8 {
+    line-height: 2rem;
+  }
+
+  .sm\:leading-9 {
+    line-height: 2.25rem;
+  }
+
+  .sm\:leading-10 {
+    line-height: 2.5rem;
+  }
+
+  .sm\:leading-none {
+    line-height: 1;
+  }
+
+  .sm\:leading-tight {
+    line-height: 1.25;
+  }
+
+  .sm\:leading-snug {
+    line-height: 1.375;
+  }
+
+  .sm\:leading-normal {
+    line-height: 1.5;
+  }
+
+  .sm\:leading-relaxed {
+    line-height: 1.625;
+  }
+
+  .sm\:leading-loose {
+    line-height: 2;
+  }
+
+  .sm\:list-inside {
+    list-style-position: inside;
+  }
+
+  .sm\:list-outside {
+    list-style-position: outside;
+  }
+
+  .sm\:list-none {
+    list-style-type: none;
+  }
+
+  .sm\:list-disc {
+    list-style-type: disc;
+  }
+
+  .sm\:list-decimal {
+    list-style-type: decimal;
+  }
+
+  .sm\:m-0 {
+    margin: 0;
+  }
+
+  .sm\:m-1 {
+    margin: 0.25rem;
+  }
+
+  .sm\:m-2 {
+    margin: 0.5rem;
+  }
+
+  .sm\:m-3 {
+    margin: 0.75rem;
+  }
+
+  .sm\:m-4 {
+    margin: 1rem;
+  }
+
+  .sm\:m-5 {
+    margin: 1.25rem;
+  }
+
+  .sm\:m-6 {
+    margin: 1.5rem;
+  }
+
+  .sm\:m-8 {
+    margin: 2rem;
+  }
+
+  .sm\:m-10 {
+    margin: 2.5rem;
+  }
+
+  .sm\:m-12 {
+    margin: 3rem;
+  }
+
+  .sm\:m-16 {
+    margin: 4rem;
+  }
+
+  .sm\:m-20 {
+    margin: 5rem;
+  }
+
+  .sm\:m-24 {
+    margin: 6rem;
+  }
+
+  .sm\:m-32 {
+    margin: 8rem;
+  }
+
+  .sm\:m-40 {
+    margin: 10rem;
+  }
+
+  .sm\:m-48 {
+    margin: 12rem;
+  }
+
+  .sm\:m-56 {
+    margin: 14rem;
+  }
+
+  .sm\:m-64 {
+    margin: 16rem;
+  }
+
+  .sm\:m-auto {
+    margin: auto;
+  }
+
+  .sm\:m-px {
+    margin: 1px;
+  }
+
+  .sm\:-m-1 {
+    margin: -0.25rem;
+  }
+
+  .sm\:-m-2 {
+    margin: -0.5rem;
+  }
+
+  .sm\:-m-3 {
+    margin: -0.75rem;
+  }
+
+  .sm\:-m-4 {
+    margin: -1rem;
+  }
+
+  .sm\:-m-5 {
+    margin: -1.25rem;
+  }
+
+  .sm\:-m-6 {
+    margin: -1.5rem;
+  }
+
+  .sm\:-m-8 {
+    margin: -2rem;
+  }
+
+  .sm\:-m-10 {
+    margin: -2.5rem;
+  }
+
+  .sm\:-m-12 {
+    margin: -3rem;
+  }
+
+  .sm\:-m-16 {
+    margin: -4rem;
+  }
+
+  .sm\:-m-20 {
+    margin: -5rem;
+  }
+
+  .sm\:-m-24 {
+    margin: -6rem;
+  }
+
+  .sm\:-m-32 {
+    margin: -8rem;
+  }
+
+  .sm\:-m-40 {
+    margin: -10rem;
+  }
+
+  .sm\:-m-48 {
+    margin: -12rem;
+  }
+
+  .sm\:-m-56 {
+    margin: -14rem;
+  }
+
+  .sm\:-m-64 {
+    margin: -16rem;
+  }
+
+  .sm\:-m-px {
+    margin: -1px;
+  }
+
+  .sm\:my-0 {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .sm\:mx-0 {
+    margin-left: 0;
+    margin-right: 0;
+  }
+
+  .sm\:my-1 {
+    margin-top: 0.25rem;
+    margin-bottom: 0.25rem;
+  }
+
+  .sm\:mx-1 {
+    margin-left: 0.25rem;
+    margin-right: 0.25rem;
+  }
+
+  .sm\:my-2 {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+  }
+
+  .sm\:mx-2 {
+    margin-left: 0.5rem;
+    margin-right: 0.5rem;
+  }
+
+  .sm\:my-3 {
+    margin-top: 0.75rem;
+    margin-bottom: 0.75rem;
+  }
+
+  .sm\:mx-3 {
+    margin-left: 0.75rem;
+    margin-right: 0.75rem;
+  }
+
+  .sm\:my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+  }
+
+  .sm\:mx-4 {
+    margin-left: 1rem;
+    margin-right: 1rem;
+  }
+
+  .sm\:my-5 {
+    margin-top: 1.25rem;
+    margin-bottom: 1.25rem;
+  }
+
+  .sm\:mx-5 {
+    margin-left: 1.25rem;
+    margin-right: 1.25rem;
+  }
+
+  .sm\:my-6 {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+
+  .sm\:mx-6 {
+    margin-left: 1.5rem;
+    margin-right: 1.5rem;
+  }
+
+  .sm\:my-8 {
+    margin-top: 2rem;
+    margin-bottom: 2rem;
+  }
+
+  .sm\:mx-8 {
+    margin-left: 2rem;
+    margin-right: 2rem;
+  }
+
+  .sm\:my-10 {
+    margin-top: 2.5rem;
+    margin-bottom: 2.5rem;
+  }
+
+  .sm\:mx-10 {
+    margin-left: 2.5rem;
+    margin-right: 2.5rem;
+  }
+
+  .sm\:my-12 {
+    margin-top: 3rem;
+    margin-bottom: 3rem;
+  }
+
+  .sm\:mx-12 {
+    margin-left: 3rem;
+    margin-right: 3rem;
+  }
+
+  .sm\:my-16 {
+    margin-top: 4rem;
+    margin-bottom: 4rem;
+  }
+
+  .sm\:mx-16 {
+    margin-left: 4rem;
+    margin-right: 4rem;
+  }
+
+  .sm\:my-20 {
+    margin-top: 5rem;
+    margin-bottom: 5rem;
+  }
+
+  .sm\:mx-20 {
+    margin-left: 5rem;
+    margin-right: 5rem;
+  }
+
+  .sm\:my-24 {
+    margin-top: 6rem;
+    margin-bottom: 6rem;
+  }
+
+  .sm\:mx-24 {
+    margin-left: 6rem;
+    margin-right: 6rem;
+  }
+
+  .sm\:my-32 {
+    margin-top: 8rem;
+    margin-bottom: 8rem;
+  }
+
+  .sm\:mx-32 {
+    margin-left: 8rem;
+    margin-right: 8rem;
+  }
+
+  .sm\:my-40 {
+    margin-top: 10rem;
+    margin-bottom: 10rem;
+  }
+
+  .sm\:mx-40 {
+    margin-left: 10rem;
+    margin-right: 10rem;
+  }
+
+  .sm\:my-48 {
+    margin-top: 12rem;
+    margin-bottom: 12rem;
+  }
+
+  .sm\:mx-48 {
+    margin-left: 12rem;
+    margin-right: 12rem;
+  }
+
+  .sm\:my-56 {
+    margin-top: 14rem;
+    margin-bottom: 14rem;
+  }
+
+  .sm\:mx-56 {
+    margin-left: 14rem;
+    margin-right: 14rem;
+  }
+
+  .sm\:my-64 {
+    margin-top: 16rem;
+    margin-bottom: 16rem;
+  }
+
+  .sm\:mx-64 {
+    margin-left: 16rem;
+    margin-right: 16rem;
+  }
+
+  .sm\:my-auto {
+    margin-top: auto;
+    margin-bottom: auto;
+  }
+
+  .sm\:mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .sm\:my-px {
+    margin-top: 1px;
+    margin-bottom: 1px;
+  }
+
+  .sm\:mx-px {
+    margin-left: 1px;
+    margin-right: 1px;
+  }
+
+  .sm\:-my-1 {
+    margin-top: -0.25rem;
+    margin-bottom: -0.25rem;
+  }
+
+  .sm\:-mx-1 {
+    margin-left: -0.25rem;
+    margin-right: -0.25rem;
+  }
+
+  .sm\:-my-2 {
+    margin-top: -0.5rem;
+    margin-bottom: -0.5rem;
+  }
+
+  .sm\:-mx-2 {
+    margin-left: -0.5rem;
+    margin-right: -0.5rem;
+  }
+
+  .sm\:-my-3 {
+    margin-top: -0.75rem;
+    margin-bottom: -0.75rem;
+  }
+
+  .sm\:-mx-3 {
+    margin-left: -0.75rem;
+    margin-right: -0.75rem;
+  }
+
+  .sm\:-my-4 {
+    margin-top: -1rem;
+    margin-bottom: -1rem;
+  }
+
+  .sm\:-mx-4 {
+    margin-left: -1rem;
+    margin-right: -1rem;
+  }
+
+  .sm\:-my-5 {
+    margin-top: -1.25rem;
+    margin-bottom: -1.25rem;
+  }
+
+  .sm\:-mx-5 {
+    margin-left: -1.25rem;
+    margin-right: -1.25rem;
+  }
+
+  .sm\:-my-6 {
+    margin-top: -1.5rem;
+    margin-bottom: -1.5rem;
+  }
+
+  .sm\:-mx-6 {
+    margin-left: -1.5rem;
+    margin-right: -1.5rem;
+  }
+
+  .sm\:-my-8 {
+    margin-top: -2rem;
+    margin-bottom: -2rem;
+  }
+
+  .sm\:-mx-8 {
+    margin-left: -2rem;
+    margin-right: -2rem;
+  }
+
+  .sm\:-my-10 {
+    margin-top: -2.5rem;
+    margin-bottom: -2.5rem;
+  }
+
+  .sm\:-mx-10 {
+    margin-left: -2.5rem;
+    margin-right: -2.5rem;
+  }
+
+  .sm\:-my-12 {
+    margin-top: -3rem;
+    margin-bottom: -3rem;
+  }
+
+  .sm\:-mx-12 {
+    margin-left: -3rem;
+    margin-right: -3rem;
+  }
+
+  .sm\:-my-16 {
+    margin-top: -4rem;
+    margin-bottom: -4rem;
+  }
+
+  .sm\:-mx-16 {
+    margin-left: -4rem;
+    margin-right: -4rem;
+  }
+
+  .sm\:-my-20 {
+    margin-top: -5rem;
+    margin-bottom: -5rem;
+  }
+
+  .sm\:-mx-20 {
+    margin-left: -5rem;
+    margin-right: -5rem;
+  }
+
+  .sm\:-my-24 {
+    margin-top: -6rem;
+    margin-bottom: -6rem;
+  }
+
+  .sm\:-mx-24 {
+    margin-left: -6rem;
+    margin-right: -6rem;
+  }
+
+  .sm\:-my-32 {
+    margin-top: -8rem;
+    margin-bottom: -8rem;
+  }
+
+  .sm\:-mx-32 {
+    margin-left: -8rem;
+    margin-right: -8rem;
+  }
+
+  .sm\:-my-40 {
+    margin-top: -10rem;
+    margin-bottom: -10rem;
+  }
+
+  .sm\:-mx-40 {
+    margin-left: -10rem;
+    margin-right: -10rem;
+  }
+
+  .sm\:-my-48 {
+    margin-top: -12rem;
+    margin-bottom: -12rem;
+  }
+
+  .sm\:-mx-48 {
+    margin-left: -12rem;
+    margin-right: -12rem;
+  }
+
+  .sm\:-my-56 {
+    margin-top: -14rem;
+    margin-bottom: -14rem;
+  }
+
+  .sm\:-mx-56 {
+    margin-left: -14rem;
+    margin-right: -14rem;
+  }
+
+  .sm\:-my-64 {
+    margin-top: -16rem;
+    margin-bottom: -16rem;
+  }
+
+  .sm\:-mx-64 {
+    margin-left: -16rem;
+    margin-right: -16rem;
+  }
+
+  .sm\:-my-px {
+    margin-top: -1px;
+    margin-bottom: -1px;
+  }
+
+  .sm\:-mx-px {
+    margin-left: -1px;
+    margin-right: -1px;
+  }
+
+  .sm\:mt-0 {
+    margin-top: 0;
+  }
+
+  .sm\:mr-0 {
+    margin-right: 0;
+  }
+
+  .sm\:mb-0 {
+    margin-bottom: 0;
+  }
+
+  .sm\:ml-0 {
+    margin-left: 0;
+  }
+
+  .sm\:mt-1 {
+    margin-top: 0.25rem;
+  }
+
+  .sm\:mr-1 {
+    margin-right: 0.25rem;
+  }
+
+  .sm\:mb-1 {
+    margin-bottom: 0.25rem;
+  }
+
+  .sm\:ml-1 {
+    margin-left: 0.25rem;
+  }
+
+  .sm\:mt-2 {
+    margin-top: 0.5rem;
+  }
+
+  .sm\:mr-2 {
+    margin-right: 0.5rem;
+  }
+
+  .sm\:mb-2 {
+    margin-bottom: 0.5rem;
+  }
+
+  .sm\:ml-2 {
+    margin-left: 0.5rem;
+  }
+
+  .sm\:mt-3 {
+    margin-top: 0.75rem;
+  }
+
+  .sm\:mr-3 {
+    margin-right: 0.75rem;
+  }
+
+  .sm\:mb-3 {
+    margin-bottom: 0.75rem;
+  }
+
+  .sm\:ml-3 {
+    margin-left: 0.75rem;
+  }
+
+  .sm\:mt-4 {
+    margin-top: 1rem;
+  }
+
+  .sm\:mr-4 {
+    margin-right: 1rem;
+  }
+
+  .sm\:mb-4 {
+    margin-bottom: 1rem;
+  }
+
+  .sm\:ml-4 {
+    margin-left: 1rem;
+  }
+
+  .sm\:mt-5 {
+    margin-top: 1.25rem;
+  }
+
+  .sm\:mr-5 {
+    margin-right: 1.25rem;
+  }
+
+  .sm\:mb-5 {
+    margin-bottom: 1.25rem;
+  }
+
+  .sm\:ml-5 {
+    margin-left: 1.25rem;
+  }
+
+  .sm\:mt-6 {
+    margin-top: 1.5rem;
+  }
+
+  .sm\:mr-6 {
+    margin-right: 1.5rem;
+  }
+
+  .sm\:mb-6 {
+    margin-bottom: 1.5rem;
+  }
+
+  .sm\:ml-6 {
+    margin-left: 1.5rem;
+  }
+
+  .sm\:mt-8 {
+    margin-top: 2rem;
+  }
+
+  .sm\:mr-8 {
+    margin-right: 2rem;
+  }
+
+  .sm\:mb-8 {
+    margin-bottom: 2rem;
+  }
+
+  .sm\:ml-8 {
+    margin-left: 2rem;
+  }
+
+  .sm\:mt-10 {
+    margin-top: 2.5rem;
+  }
+
+  .sm\:mr-10 {
+    margin-right: 2.5rem;
+  }
+
+  .sm\:mb-10 {
+    margin-bottom: 2.5rem;
+  }
+
+  .sm\:ml-10 {
+    margin-left: 2.5rem;
+  }
+
+  .sm\:mt-12 {
+    margin-top: 3rem;
+  }
+
+  .sm\:mr-12 {
+    margin-right: 3rem;
+  }
+
+  .sm\:mb-12 {
+    margin-bottom: 3rem;
+  }
+
+  .sm\:ml-12 {
+    margin-left: 3rem;
+  }
+
+  .sm\:mt-16 {
+    margin-top: 4rem;
+  }
+
+  .sm\:mr-16 {
+    margin-right: 4rem;
+  }
+
+  .sm\:mb-16 {
+    margin-bottom: 4rem;
+  }
+
+  .sm\:ml-16 {
+    margin-left: 4rem;
+  }
+
+  .sm\:mt-20 {
+    margin-top: 5rem;
+  }
+
+  .sm\:mr-20 {
+    margin-right: 5rem;
+  }
+
+  .sm\:mb-20 {
+    margin-bottom: 5rem;
+  }
+
+  .sm\:ml-20 {
+    margin-left: 5rem;
+  }
+
+  .sm\:mt-24 {
+    margin-top: 6rem;
+  }
+
+  .sm\:mr-24 {
+    margin-right: 6rem;
+  }
+
+  .sm\:mb-24 {
+    margin-bottom: 6rem;
+  }
+
+  .sm\:ml-24 {
+    margin-left: 6rem;
+  }
+
+  .sm\:mt-32 {
+    margin-top: 8rem;
+  }
+
+  .sm\:mr-32 {
+    margin-right: 8rem;
+  }
+
+  .sm\:mb-32 {
+    margin-bottom: 8rem;
+  }
+
+  .sm\:ml-32 {
+    margin-left: 8rem;
+  }
+
+  .sm\:mt-40 {
+    margin-top: 10rem;
+  }
+
+  .sm\:mr-40 {
+    margin-right: 10rem;
+  }
+
+  .sm\:mb-40 {
+    margin-bottom: 10rem;
+  }
+
+  .sm\:ml-40 {
+    margin-left: 10rem;
+  }
+
+  .sm\:mt-48 {
+    margin-top: 12rem;
+  }
+
+  .sm\:mr-48 {
+    margin-right: 12rem;
+  }
+
+  .sm\:mb-48 {
+    margin-bottom: 12rem;
+  }
+
+  .sm\:ml-48 {
+    margin-left: 12rem;
+  }
+
+  .sm\:mt-56 {
+    margin-top: 14rem;
+  }
+
+  .sm\:mr-56 {
+    margin-right: 14rem;
+  }
+
+  .sm\:mb-56 {
+    margin-bottom: 14rem;
+  }
+
+  .sm\:ml-56 {
+    margin-left: 14rem;
+  }
+
+  .sm\:mt-64 {
+    margin-top: 16rem;
+  }
+
+  .sm\:mr-64 {
+    margin-right: 16rem;
+  }
+
+  .sm\:mb-64 {
+    margin-bottom: 16rem;
+  }
+
+  .sm\:ml-64 {
+    margin-left: 16rem;
+  }
+
+  .sm\:mt-auto {
+    margin-top: auto;
+  }
+
+  .sm\:mr-auto {
+    margin-right: auto;
+  }
+
+  .sm\:mb-auto {
+    margin-bottom: auto;
+  }
+
+  .sm\:ml-auto {
+    margin-left: auto;
+  }
+
+  .sm\:mt-px {
+    margin-top: 1px;
+  }
+
+  .sm\:mr-px {
+    margin-right: 1px;
+  }
+
+  .sm\:mb-px {
+    margin-bottom: 1px;
+  }
+
+  .sm\:ml-px {
+    margin-left: 1px;
+  }
+
+  .sm\:-mt-1 {
+    margin-top: -0.25rem;
+  }
+
+  .sm\:-mr-1 {
+    margin-right: -0.25rem;
+  }
+
+  .sm\:-mb-1 {
+    margin-bottom: -0.25rem;
+  }
+
+  .sm\:-ml-1 {
+    margin-left: -0.25rem;
+  }
+
+  .sm\:-mt-2 {
+    margin-top: -0.5rem;
+  }
+
+  .sm\:-mr-2 {
+    margin-right: -0.5rem;
+  }
+
+  .sm\:-mb-2 {
+    margin-bottom: -0.5rem;
+  }
+
+  .sm\:-ml-2 {
+    margin-left: -0.5rem;
+  }
+
+  .sm\:-mt-3 {
+    margin-top: -0.75rem;
+  }
+
+  .sm\:-mr-3 {
+    margin-right: -0.75rem;
+  }
+
+  .sm\:-mb-3 {
+    margin-bottom: -0.75rem;
+  }
+
+  .sm\:-ml-3 {
+    margin-left: -0.75rem;
+  }
+
+  .sm\:-mt-4 {
+    margin-top: -1rem;
+  }
+
+  .sm\:-mr-4 {
+    margin-right: -1rem;
+  }
+
+  .sm\:-mb-4 {
+    margin-bottom: -1rem;
+  }
+
+  .sm\:-ml-4 {
+    margin-left: -1rem;
+  }
+
+  .sm\:-mt-5 {
+    margin-top: -1.25rem;
+  }
+
+  .sm\:-mr-5 {
+    margin-right: -1.25rem;
+  }
+
+  .sm\:-mb-5 {
+    margin-bottom: -1.25rem;
+  }
+
+  .sm\:-ml-5 {
+    margin-left: -1.25rem;
+  }
+
+  .sm\:-mt-6 {
+    margin-top: -1.5rem;
+  }
+
+  .sm\:-mr-6 {
+    margin-right: -1.5rem;
+  }
+
+  .sm\:-mb-6 {
+    margin-bottom: -1.5rem;
+  }
+
+  .sm\:-ml-6 {
+    margin-left: -1.5rem;
+  }
+
+  .sm\:-mt-8 {
+    margin-top: -2rem;
+  }
+
+  .sm\:-mr-8 {
+    margin-right: -2rem;
+  }
+
+  .sm\:-mb-8 {
+    margin-bottom: -2rem;
+  }
+
+  .sm\:-ml-8 {
+    margin-left: -2rem;
+  }
+
+  .sm\:-mt-10 {
+    margin-top: -2.5rem;
+  }
+
+  .sm\:-mr-10 {
+    margin-right: -2.5rem;
+  }
+
+  .sm\:-mb-10 {
+    margin-bottom: -2.5rem;
+  }
+
+  .sm\:-ml-10 {
+    margin-left: -2.5rem;
+  }
+
+  .sm\:-mt-12 {
+    margin-top: -3rem;
+  }
+
+  .sm\:-mr-12 {
+    margin-right: -3rem;
+  }
+
+  .sm\:-mb-12 {
+    margin-bottom: -3rem;
+  }
+
+  .sm\:-ml-12 {
+    margin-left: -3rem;
+  }
+
+  .sm\:-mt-16 {
+    margin-top: -4rem;
+  }
+
+  .sm\:-mr-16 {
+    margin-right: -4rem;
+  }
+
+  .sm\:-mb-16 {
+    margin-bottom: -4rem;
+  }
+
+  .sm\:-ml-16 {
+    margin-left: -4rem;
+  }
+
+  .sm\:-mt-20 {
+    margin-top: -5rem;
+  }
+
+  .sm\:-mr-20 {
+    margin-right: -5rem;
+  }
+
+  .sm\:-mb-20 {
+    margin-bottom: -5rem;
+  }
+
+  .sm\:-ml-20 {
+    margin-left: -5rem;
+  }
+
+  .sm\:-mt-24 {
+    margin-top: -6rem;
+  }
+
+  .sm\:-mr-24 {
+    margin-right: -6rem;
+  }
+
+  .sm\:-mb-24 {
+    margin-bottom: -6rem;
+  }
+
+  .sm\:-ml-24 {
+    margin-left: -6rem;
+  }
+
+  .sm\:-mt-32 {
+    margin-top: -8rem;
+  }
+
+  .sm\:-mr-32 {
+    margin-right: -8rem;
+  }
+
+  .sm\:-mb-32 {
+    margin-bottom: -8rem;
+  }
+
+  .sm\:-ml-32 {
+    margin-left: -8rem;
+  }
+
+  .sm\:-mt-40 {
+    margin-top: -10rem;
+  }
+
+  .sm\:-mr-40 {
+    margin-right: -10rem;
+  }
+
+  .sm\:-mb-40 {
+    margin-bottom: -10rem;
+  }
+
+  .sm\:-ml-40 {
+    margin-left: -10rem;
+  }
+
+  .sm\:-mt-48 {
+    margin-top: -12rem;
+  }
+
+  .sm\:-mr-48 {
+    margin-right: -12rem;
+  }
+
+  .sm\:-mb-48 {
+    margin-bottom: -12rem;
+  }
+
+  .sm\:-ml-48 {
+    margin-left: -12rem;
+  }
+
+  .sm\:-mt-56 {
+    margin-top: -14rem;
+  }
+
+  .sm\:-mr-56 {
+    margin-right: -14rem;
+  }
+
+  .sm\:-mb-56 {
+    margin-bottom: -14rem;
+  }
+
+  .sm\:-ml-56 {
+    margin-left: -14rem;
+  }
+
+  .sm\:-mt-64 {
+    margin-top: -16rem;
+  }
+
+  .sm\:-mr-64 {
+    margin-right: -16rem;
+  }
+
+  .sm\:-mb-64 {
+    margin-bottom: -16rem;
+  }
+
+  .sm\:-ml-64 {
+    margin-left: -16rem;
+  }
+
+  .sm\:-mt-px {
+    margin-top: -1px;
+  }
+
+  .sm\:-mr-px {
+    margin-right: -1px;
+  }
+
+  .sm\:-mb-px {
+    margin-bottom: -1px;
+  }
+
+  .sm\:-ml-px {
+    margin-left: -1px;
+  }
+
+  .sm\:max-h-full {
+    max-height: 100%;
+  }
+
+  .sm\:max-h-screen {
+    max-height: 100vh;
+  }
+
+  .sm\:max-w-none {
+    max-width: none;
+  }
+
+  .sm\:max-w-xs {
+    max-width: 20rem;
+  }
+
+  .sm\:max-w-sm {
+    max-width: 24rem;
+  }
+
+  .sm\:max-w-md {
+    max-width: 28rem;
+  }
+
+  .sm\:max-w-lg {
+    max-width: 32rem;
+  }
+
+  .sm\:max-w-xl {
+    max-width: 36rem;
+  }
+
+  .sm\:max-w-2xl {
+    max-width: 42rem;
+  }
+
+  .sm\:max-w-3xl {
+    max-width: 48rem;
+  }
+
+  .sm\:max-w-4xl {
+    max-width: 56rem;
+  }
+
+  .sm\:max-w-5xl {
+    max-width: 64rem;
+  }
+
+  .sm\:max-w-6xl {
+    max-width: 72rem;
+  }
+
+  .sm\:max-w-full {
+    max-width: 100%;
+  }
+
+  .sm\:max-w-screen-sm {
+    max-width: 640px;
+  }
+
+  .sm\:max-w-screen-md {
+    max-width: 768px;
+  }
+
+  .sm\:max-w-screen-lg {
+    max-width: 1024px;
+  }
+
+  .sm\:max-w-screen-xl {
+    max-width: 1280px;
+  }
+
+  .sm\:min-h-0 {
+    min-height: 0;
+  }
+
+  .sm\:min-h-full {
+    min-height: 100%;
+  }
+
+  .sm\:min-h-screen {
+    min-height: 100vh;
+  }
+
+  .sm\:min-w-0 {
+    min-width: 0;
+  }
+
+  .sm\:min-w-full {
+    min-width: 100%;
+  }
+
+  .sm\:object-contain {
+    -o-object-fit: contain;
+       object-fit: contain;
+  }
+
+  .sm\:object-cover {
+    -o-object-fit: cover;
+       object-fit: cover;
+  }
+
+  .sm\:object-fill {
+    -o-object-fit: fill;
+       object-fit: fill;
+  }
+
+  .sm\:object-none {
+    -o-object-fit: none;
+       object-fit: none;
+  }
+
+  .sm\:object-scale-down {
+    -o-object-fit: scale-down;
+       object-fit: scale-down;
+  }
+
+  .sm\:object-bottom {
+    -o-object-position: bottom;
+       object-position: bottom;
+  }
+
+  .sm\:object-center {
+    -o-object-position: center;
+       object-position: center;
+  }
+
+  .sm\:object-left {
+    -o-object-position: left;
+       object-position: left;
+  }
+
+  .sm\:object-left-bottom {
+    -o-object-position: left bottom;
+       object-position: left bottom;
+  }
+
+  .sm\:object-left-top {
+    -o-object-position: left top;
+       object-position: left top;
+  }
+
+  .sm\:object-right {
+    -o-object-position: right;
+       object-position: right;
+  }
+
+  .sm\:object-right-bottom {
+    -o-object-position: right bottom;
+       object-position: right bottom;
+  }
+
+  .sm\:object-right-top {
+    -o-object-position: right top;
+       object-position: right top;
+  }
+
+  .sm\:object-top {
+    -o-object-position: top;
+       object-position: top;
+  }
+
+  .sm\:opacity-0 {
+    opacity: 0;
+  }
+
+  .sm\:opacity-25 {
+    opacity: 0.25;
+  }
+
+  .sm\:opacity-50 {
+    opacity: 0.5;
+  }
+
+  .sm\:opacity-75 {
+    opacity: 0.75;
+  }
+
+  .sm\:opacity-100 {
+    opacity: 1;
+  }
+
+  .sm\:hover\:opacity-0:hover {
+    opacity: 0;
+  }
+
+  .sm\:hover\:opacity-25:hover {
+    opacity: 0.25;
+  }
+
+  .sm\:hover\:opacity-50:hover {
+    opacity: 0.5;
+  }
+
+  .sm\:hover\:opacity-75:hover {
+    opacity: 0.75;
+  }
+
+  .sm\:hover\:opacity-100:hover {
+    opacity: 1;
+  }
+
+  .sm\:focus\:opacity-0:focus {
+    opacity: 0;
+  }
+
+  .sm\:focus\:opacity-25:focus {
+    opacity: 0.25;
+  }
+
+  .sm\:focus\:opacity-50:focus {
+    opacity: 0.5;
+  }
+
+  .sm\:focus\:opacity-75:focus {
+    opacity: 0.75;
+  }
+
+  .sm\:focus\:opacity-100:focus {
+    opacity: 1;
+  }
+
+  .sm\:outline-none {
+    outline: 0;
+  }
+
+  .sm\:focus\:outline-none:focus {
+    outline: 0;
+  }
+
+  .sm\:overflow-auto {
+    overflow: auto;
+  }
+
+  .sm\:overflow-hidden {
+    overflow: hidden;
+  }
+
+  .sm\:overflow-visible {
+    overflow: visible;
+  }
+
+  .sm\:overflow-scroll {
+    overflow: scroll;
+  }
+
+  .sm\:overflow-x-auto {
+    overflow-x: auto;
+  }
+
+  .sm\:overflow-y-auto {
+    overflow-y: auto;
+  }
+
+  .sm\:overflow-x-hidden {
+    overflow-x: hidden;
+  }
+
+  .sm\:overflow-y-hidden {
+    overflow-y: hidden;
+  }
+
+  .sm\:overflow-x-visible {
+    overflow-x: visible;
+  }
+
+  .sm\:overflow-y-visible {
+    overflow-y: visible;
+  }
+
+  .sm\:overflow-x-scroll {
+    overflow-x: scroll;
+  }
+
+  .sm\:overflow-y-scroll {
+    overflow-y: scroll;
+  }
+
+  .sm\:scrolling-touch {
+    -webkit-overflow-scrolling: touch;
+  }
+
+  .sm\:scrolling-auto {
+    -webkit-overflow-scrolling: auto;
+  }
+
+  .sm\:overscroll-auto {
+    -ms-scroll-chaining: chained;
+        overscroll-behavior: auto;
+  }
+
+  .sm\:overscroll-contain {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: contain;
+  }
+
+  .sm\:overscroll-none {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: none;
+  }
+
+  .sm\:overscroll-y-auto {
+    overscroll-behavior-y: auto;
+  }
+
+  .sm\:overscroll-y-contain {
+    overscroll-behavior-y: contain;
+  }
+
+  .sm\:overscroll-y-none {
+    overscroll-behavior-y: none;
+  }
+
+  .sm\:overscroll-x-auto {
+    overscroll-behavior-x: auto;
+  }
+
+  .sm\:overscroll-x-contain {
+    overscroll-behavior-x: contain;
+  }
+
+  .sm\:overscroll-x-none {
+    overscroll-behavior-x: none;
+  }
+
+  .sm\:p-0 {
+    padding: 0;
+  }
+
+  .sm\:p-1 {
+    padding: 0.25rem;
+  }
+
+  .sm\:p-2 {
+    padding: 0.5rem;
+  }
+
+  .sm\:p-3 {
+    padding: 0.75rem;
+  }
+
+  .sm\:p-4 {
+    padding: 1rem;
+  }
+
+  .sm\:p-5 {
+    padding: 1.25rem;
+  }
+
+  .sm\:p-6 {
+    padding: 1.5rem;
+  }
+
+  .sm\:p-8 {
+    padding: 2rem;
+  }
+
+  .sm\:p-10 {
+    padding: 2.5rem;
+  }
+
+  .sm\:p-12 {
+    padding: 3rem;
+  }
+
+  .sm\:p-16 {
+    padding: 4rem;
+  }
+
+  .sm\:p-20 {
+    padding: 5rem;
+  }
+
+  .sm\:p-24 {
+    padding: 6rem;
+  }
+
+  .sm\:p-32 {
+    padding: 8rem;
+  }
+
+  .sm\:p-40 {
+    padding: 10rem;
+  }
+
+  .sm\:p-48 {
+    padding: 12rem;
+  }
+
+  .sm\:p-56 {
+    padding: 14rem;
+  }
+
+  .sm\:p-64 {
+    padding: 16rem;
+  }
+
+  .sm\:p-px {
+    padding: 1px;
+  }
+
+  .sm\:py-0 {
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+
+  .sm\:px-0 {
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  .sm\:py-1 {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+  }
+
+  .sm\:px-1 {
+    padding-left: 0.25rem;
+    padding-right: 0.25rem;
+  }
+
+  .sm\:py-2 {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
+
+  .sm\:px-2 {
+    padding-left: 0.5rem;
+    padding-right: 0.5rem;
+  }
+
+  .sm\:py-3 {
+    padding-top: 0.75rem;
+    padding-bottom: 0.75rem;
+  }
+
+  .sm\:px-3 {
+    padding-left: 0.75rem;
+    padding-right: 0.75rem;
+  }
+
+  .sm\:py-4 {
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+  }
+
+  .sm\:px-4 {
+    padding-left: 1rem;
+    padding-right: 1rem;
+  }
+
+  .sm\:py-5 {
+    padding-top: 1.25rem;
+    padding-bottom: 1.25rem;
+  }
+
+  .sm\:px-5 {
+    padding-left: 1.25rem;
+    padding-right: 1.25rem;
+  }
+
+  .sm\:py-6 {
+    padding-top: 1.5rem;
+    padding-bottom: 1.5rem;
+  }
+
+  .sm\:px-6 {
+    padding-left: 1.5rem;
+    padding-right: 1.5rem;
+  }
+
+  .sm\:py-8 {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
+
+  .sm\:px-8 {
+    padding-left: 2rem;
+    padding-right: 2rem;
+  }
+
+  .sm\:py-10 {
+    padding-top: 2.5rem;
+    padding-bottom: 2.5rem;
+  }
+
+  .sm\:px-10 {
+    padding-left: 2.5rem;
+    padding-right: 2.5rem;
+  }
+
+  .sm\:py-12 {
+    padding-top: 3rem;
+    padding-bottom: 3rem;
+  }
+
+  .sm\:px-12 {
+    padding-left: 3rem;
+    padding-right: 3rem;
+  }
+
+  .sm\:py-16 {
+    padding-top: 4rem;
+    padding-bottom: 4rem;
+  }
+
+  .sm\:px-16 {
+    padding-left: 4rem;
+    padding-right: 4rem;
+  }
+
+  .sm\:py-20 {
+    padding-top: 5rem;
+    padding-bottom: 5rem;
+  }
+
+  .sm\:px-20 {
+    padding-left: 5rem;
+    padding-right: 5rem;
+  }
+
+  .sm\:py-24 {
+    padding-top: 6rem;
+    padding-bottom: 6rem;
+  }
+
+  .sm\:px-24 {
+    padding-left: 6rem;
+    padding-right: 6rem;
+  }
+
+  .sm\:py-32 {
+    padding-top: 8rem;
+    padding-bottom: 8rem;
+  }
+
+  .sm\:px-32 {
+    padding-left: 8rem;
+    padding-right: 8rem;
+  }
+
+  .sm\:py-40 {
+    padding-top: 10rem;
+    padding-bottom: 10rem;
+  }
+
+  .sm\:px-40 {
+    padding-left: 10rem;
+    padding-right: 10rem;
+  }
+
+  .sm\:py-48 {
+    padding-top: 12rem;
+    padding-bottom: 12rem;
+  }
+
+  .sm\:px-48 {
+    padding-left: 12rem;
+    padding-right: 12rem;
+  }
+
+  .sm\:py-56 {
+    padding-top: 14rem;
+    padding-bottom: 14rem;
+  }
+
+  .sm\:px-56 {
+    padding-left: 14rem;
+    padding-right: 14rem;
+  }
+
+  .sm\:py-64 {
+    padding-top: 16rem;
+    padding-bottom: 16rem;
+  }
+
+  .sm\:px-64 {
+    padding-left: 16rem;
+    padding-right: 16rem;
+  }
+
+  .sm\:py-px {
+    padding-top: 1px;
+    padding-bottom: 1px;
+  }
+
+  .sm\:px-px {
+    padding-left: 1px;
+    padding-right: 1px;
+  }
+
+  .sm\:pt-0 {
+    padding-top: 0;
+  }
+
+  .sm\:pr-0 {
+    padding-right: 0;
+  }
+
+  .sm\:pb-0 {
+    padding-bottom: 0;
+  }
+
+  .sm\:pl-0 {
+    padding-left: 0;
+  }
+
+  .sm\:pt-1 {
+    padding-top: 0.25rem;
+  }
+
+  .sm\:pr-1 {
+    padding-right: 0.25rem;
+  }
+
+  .sm\:pb-1 {
+    padding-bottom: 0.25rem;
+  }
+
+  .sm\:pl-1 {
+    padding-left: 0.25rem;
+  }
+
+  .sm\:pt-2 {
+    padding-top: 0.5rem;
+  }
+
+  .sm\:pr-2 {
+    padding-right: 0.5rem;
+  }
+
+  .sm\:pb-2 {
+    padding-bottom: 0.5rem;
+  }
+
+  .sm\:pl-2 {
+    padding-left: 0.5rem;
+  }
+
+  .sm\:pt-3 {
+    padding-top: 0.75rem;
+  }
+
+  .sm\:pr-3 {
+    padding-right: 0.75rem;
+  }
+
+  .sm\:pb-3 {
+    padding-bottom: 0.75rem;
+  }
+
+  .sm\:pl-3 {
+    padding-left: 0.75rem;
+  }
+
+  .sm\:pt-4 {
+    padding-top: 1rem;
+  }
+
+  .sm\:pr-4 {
+    padding-right: 1rem;
+  }
+
+  .sm\:pb-4 {
+    padding-bottom: 1rem;
+  }
+
+  .sm\:pl-4 {
+    padding-left: 1rem;
+  }
+
+  .sm\:pt-5 {
+    padding-top: 1.25rem;
+  }
+
+  .sm\:pr-5 {
+    padding-right: 1.25rem;
+  }
+
+  .sm\:pb-5 {
+    padding-bottom: 1.25rem;
+  }
+
+  .sm\:pl-5 {
+    padding-left: 1.25rem;
+  }
+
+  .sm\:pt-6 {
+    padding-top: 1.5rem;
+  }
+
+  .sm\:pr-6 {
+    padding-right: 1.5rem;
+  }
+
+  .sm\:pb-6 {
+    padding-bottom: 1.5rem;
+  }
+
+  .sm\:pl-6 {
+    padding-left: 1.5rem;
+  }
+
+  .sm\:pt-8 {
+    padding-top: 2rem;
+  }
+
+  .sm\:pr-8 {
+    padding-right: 2rem;
+  }
+
+  .sm\:pb-8 {
+    padding-bottom: 2rem;
+  }
+
+  .sm\:pl-8 {
+    padding-left: 2rem;
+  }
+
+  .sm\:pt-10 {
+    padding-top: 2.5rem;
+  }
+
+  .sm\:pr-10 {
+    padding-right: 2.5rem;
+  }
+
+  .sm\:pb-10 {
+    padding-bottom: 2.5rem;
+  }
+
+  .sm\:pl-10 {
+    padding-left: 2.5rem;
+  }
+
+  .sm\:pt-12 {
+    padding-top: 3rem;
+  }
+
+  .sm\:pr-12 {
+    padding-right: 3rem;
+  }
+
+  .sm\:pb-12 {
+    padding-bottom: 3rem;
+  }
+
+  .sm\:pl-12 {
+    padding-left: 3rem;
+  }
+
+  .sm\:pt-16 {
+    padding-top: 4rem;
+  }
+
+  .sm\:pr-16 {
+    padding-right: 4rem;
+  }
+
+  .sm\:pb-16 {
+    padding-bottom: 4rem;
+  }
+
+  .sm\:pl-16 {
+    padding-left: 4rem;
+  }
+
+  .sm\:pt-20 {
+    padding-top: 5rem;
+  }
+
+  .sm\:pr-20 {
+    padding-right: 5rem;
+  }
+
+  .sm\:pb-20 {
+    padding-bottom: 5rem;
+  }
+
+  .sm\:pl-20 {
+    padding-left: 5rem;
+  }
+
+  .sm\:pt-24 {
+    padding-top: 6rem;
+  }
+
+  .sm\:pr-24 {
+    padding-right: 6rem;
+  }
+
+  .sm\:pb-24 {
+    padding-bottom: 6rem;
+  }
+
+  .sm\:pl-24 {
+    padding-left: 6rem;
+  }
+
+  .sm\:pt-32 {
+    padding-top: 8rem;
+  }
+
+  .sm\:pr-32 {
+    padding-right: 8rem;
+  }
+
+  .sm\:pb-32 {
+    padding-bottom: 8rem;
+  }
+
+  .sm\:pl-32 {
+    padding-left: 8rem;
+  }
+
+  .sm\:pt-40 {
+    padding-top: 10rem;
+  }
+
+  .sm\:pr-40 {
+    padding-right: 10rem;
+  }
+
+  .sm\:pb-40 {
+    padding-bottom: 10rem;
+  }
+
+  .sm\:pl-40 {
+    padding-left: 10rem;
+  }
+
+  .sm\:pt-48 {
+    padding-top: 12rem;
+  }
+
+  .sm\:pr-48 {
+    padding-right: 12rem;
+  }
+
+  .sm\:pb-48 {
+    padding-bottom: 12rem;
+  }
+
+  .sm\:pl-48 {
+    padding-left: 12rem;
+  }
+
+  .sm\:pt-56 {
+    padding-top: 14rem;
+  }
+
+  .sm\:pr-56 {
+    padding-right: 14rem;
+  }
+
+  .sm\:pb-56 {
+    padding-bottom: 14rem;
+  }
+
+  .sm\:pl-56 {
+    padding-left: 14rem;
+  }
+
+  .sm\:pt-64 {
+    padding-top: 16rem;
+  }
+
+  .sm\:pr-64 {
+    padding-right: 16rem;
+  }
+
+  .sm\:pb-64 {
+    padding-bottom: 16rem;
+  }
+
+  .sm\:pl-64 {
+    padding-left: 16rem;
+  }
+
+  .sm\:pt-px {
+    padding-top: 1px;
+  }
+
+  .sm\:pr-px {
+    padding-right: 1px;
+  }
+
+  .sm\:pb-px {
+    padding-bottom: 1px;
+  }
+
+  .sm\:pl-px {
+    padding-left: 1px;
+  }
+
+  .sm\:placeholder-transparent::-moz-placeholder {
+    color: transparent;
+  }
+
+  .sm\:placeholder-transparent:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .sm\:placeholder-transparent::placeholder {
+    color: transparent;
+  }
+
+  .sm\:placeholder-current::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .sm\:placeholder-current:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .sm\:placeholder-current::placeholder {
+    color: currentColor;
+  }
+
+  .sm\:placeholder-black::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-black:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-black::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-white::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-white:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-white::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-transparent:focus::-moz-placeholder {
+    color: transparent;
+  }
+
+  .sm\:focus\:placeholder-transparent:focus:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .sm\:focus\:placeholder-transparent:focus::placeholder {
+    color: transparent;
+  }
+
+  .sm\:focus\:placeholder-current:focus::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .sm\:focus\:placeholder-current:focus:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .sm\:focus\:placeholder-current:focus::placeholder {
+    color: currentColor;
+  }
+
+  .sm\:focus\:placeholder-black:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-black:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-black:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-white:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-white:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-white:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-opacity-0::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:placeholder-opacity-0:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:placeholder-opacity-0::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:placeholder-opacity-25::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:placeholder-opacity-25:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:placeholder-opacity-25::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:placeholder-opacity-50::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:placeholder-opacity-50:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:placeholder-opacity-50::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:placeholder-opacity-75::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:placeholder-opacity-75:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:placeholder-opacity-75::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:placeholder-opacity-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:placeholder-opacity-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:placeholder-opacity-100::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:focus\:placeholder-opacity-0:focus::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:focus\:placeholder-opacity-0:focus::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:focus\:placeholder-opacity-25:focus::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:focus\:placeholder-opacity-25:focus::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:focus\:placeholder-opacity-50:focus::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:focus\:placeholder-opacity-50:focus::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:focus\:placeholder-opacity-75:focus::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:focus\:placeholder-opacity-75:focus::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:focus\:placeholder-opacity-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:focus\:placeholder-opacity-100:focus::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:pointer-events-none {
+    pointer-events: none;
+  }
+
+  .sm\:pointer-events-auto {
+    pointer-events: auto;
+  }
+
+  .sm\:static {
+    position: static;
+  }
+
+  .sm\:fixed {
+    position: fixed;
+  }
+
+  .sm\:absolute {
+    position: absolute;
+  }
+
+  .sm\:relative {
+    position: relative;
+  }
+
+  .sm\:sticky {
+    position: -webkit-sticky;
+    position: sticky;
+  }
+
+  .sm\:inset-0 {
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+  }
+
+  .sm\:inset-auto {
+    top: auto;
+    right: auto;
+    bottom: auto;
+    left: auto;
+  }
+
+  .sm\:inset-y-0 {
+    top: 0;
+    bottom: 0;
+  }
+
+  .sm\:inset-x-0 {
+    right: 0;
+    left: 0;
+  }
+
+  .sm\:inset-y-auto {
+    top: auto;
+    bottom: auto;
+  }
+
+  .sm\:inset-x-auto {
+    right: auto;
+    left: auto;
+  }
+
+  .sm\:top-0 {
+    top: 0;
+  }
+
+  .sm\:right-0 {
+    right: 0;
+  }
+
+  .sm\:bottom-0 {
+    bottom: 0;
+  }
+
+  .sm\:left-0 {
+    left: 0;
+  }
+
+  .sm\:top-auto {
+    top: auto;
+  }
+
+  .sm\:right-auto {
+    right: auto;
+  }
+
+  .sm\:bottom-auto {
+    bottom: auto;
+  }
+
+  .sm\:left-auto {
+    left: auto;
+  }
+
+  .sm\:resize-none {
+    resize: none;
+  }
+
+  .sm\:resize-y {
+    resize: vertical;
+  }
+
+  .sm\:resize-x {
+    resize: horizontal;
+  }
+
+  .sm\:resize {
+    resize: both;
+  }
+
+  .sm\:shadow-xs {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:shadow-sm {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:shadow {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:shadow-md {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:shadow-lg {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:shadow-xl {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .sm\:shadow-2xl {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .sm\:shadow-inner {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:shadow-outline {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .sm\:shadow-none {
+    box-shadow: none;
+  }
+
+  .sm\:hover\:shadow-xs:hover {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:hover\:shadow-sm:hover {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:hover\:shadow:hover {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:hover\:shadow-md:hover {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:hover\:shadow-lg:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:hover\:shadow-xl:hover {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .sm\:hover\:shadow-2xl:hover {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .sm\:hover\:shadow-inner:hover {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:hover\:shadow-outline:hover {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .sm\:hover\:shadow-none:hover {
+    box-shadow: none;
+  }
+
+  .sm\:focus\:shadow-xs:focus {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:focus\:shadow-sm:focus {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:focus\:shadow:focus {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:focus\:shadow-md:focus {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:focus\:shadow-lg:focus {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:focus\:shadow-xl:focus {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .sm\:focus\:shadow-2xl:focus {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .sm\:focus\:shadow-inner:focus {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:focus\:shadow-outline:focus {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .sm\:focus\:shadow-none:focus {
+    box-shadow: none;
+  }
+
+  .sm\:fill-current {
+    fill: currentColor;
+  }
+
+  .sm\:stroke-current {
+    stroke: currentColor;
+  }
+
+  .sm\:stroke-0 {
+    stroke-width: 0;
+  }
+
+  .sm\:stroke-1 {
+    stroke-width: 1;
+  }
+
+  .sm\:stroke-2 {
+    stroke-width: 2;
+  }
+
+  .sm\:table-auto {
+    table-layout: auto;
+  }
+
+  .sm\:table-fixed {
+    table-layout: fixed;
+  }
+
+  .sm\:text-left {
+    text-align: left;
+  }
+
+  .sm\:text-center {
+    text-align: center;
+  }
+
+  .sm\:text-right {
+    text-align: right;
+  }
+
+  .sm\:text-justify {
+    text-align: justify;
+  }
+
+  .sm\:text-transparent {
+    color: transparent;
+  }
+
+  .sm\:text-current {
+    color: currentColor;
+  }
+
+  .sm\:text-black {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .sm\:text-white {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .sm\:text-gray-100 {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .sm\:text-gray-200 {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .sm\:text-gray-300 {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .sm\:text-gray-400 {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .sm\:text-gray-500 {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .sm\:text-gray-600 {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .sm\:text-gray-700 {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .sm\:text-gray-800 {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .sm\:text-gray-900 {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .sm\:text-red-100 {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .sm\:text-red-200 {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .sm\:text-red-300 {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .sm\:text-red-400 {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .sm\:text-red-500 {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .sm\:text-red-600 {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .sm\:text-red-700 {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .sm\:text-red-800 {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .sm\:text-red-900 {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .sm\:text-orange-100 {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .sm\:text-orange-200 {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .sm\:text-orange-300 {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .sm\:text-orange-400 {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .sm\:text-orange-500 {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .sm\:text-orange-600 {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .sm\:text-orange-700 {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .sm\:text-orange-800 {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .sm\:text-orange-900 {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-100 {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-200 {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-300 {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-400 {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-500 {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-600 {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-700 {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-800 {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-900 {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .sm\:text-green-100 {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .sm\:text-green-200 {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .sm\:text-green-300 {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .sm\:text-green-400 {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .sm\:text-green-500 {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .sm\:text-green-600 {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .sm\:text-green-700 {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .sm\:text-green-800 {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .sm\:text-green-900 {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .sm\:text-teal-100 {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .sm\:text-teal-200 {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .sm\:text-teal-300 {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .sm\:text-teal-400 {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .sm\:text-teal-500 {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .sm\:text-teal-600 {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .sm\:text-teal-700 {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .sm\:text-teal-800 {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .sm\:text-teal-900 {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .sm\:text-blue-100 {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .sm\:text-blue-200 {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .sm\:text-blue-300 {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .sm\:text-blue-400 {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .sm\:text-blue-500 {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .sm\:text-blue-600 {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .sm\:text-blue-700 {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .sm\:text-blue-800 {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .sm\:text-blue-900 {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-100 {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-200 {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-300 {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-400 {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-500 {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-600 {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-700 {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-800 {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-900 {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .sm\:text-purple-100 {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .sm\:text-purple-200 {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .sm\:text-purple-300 {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .sm\:text-purple-400 {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .sm\:text-purple-500 {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .sm\:text-purple-600 {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .sm\:text-purple-700 {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .sm\:text-purple-800 {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .sm\:text-purple-900 {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .sm\:text-pink-100 {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .sm\:text-pink-200 {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .sm\:text-pink-300 {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .sm\:text-pink-400 {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .sm\:text-pink-500 {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .sm\:text-pink-600 {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .sm\:text-pink-700 {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .sm\:text-pink-800 {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .sm\:text-pink-900 {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-transparent:hover {
+    color: transparent;
+  }
+
+  .sm\:hover\:text-current:hover {
+    color: currentColor;
+  }
+
+  .sm\:hover\:text-black:hover {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-white:hover {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-100:hover {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-200:hover {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-300:hover {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-400:hover {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-500:hover {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-600:hover {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-700:hover {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-800:hover {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-900:hover {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-100:hover {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-200:hover {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-300:hover {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-400:hover {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-500:hover {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-600:hover {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-700:hover {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-800:hover {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-900:hover {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-100:hover {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-200:hover {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-300:hover {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-400:hover {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-500:hover {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-600:hover {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-700:hover {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-800:hover {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-900:hover {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-100:hover {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-200:hover {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-300:hover {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-400:hover {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-500:hover {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-600:hover {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-700:hover {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-800:hover {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-900:hover {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-100:hover {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-200:hover {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-300:hover {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-400:hover {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-500:hover {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-600:hover {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-700:hover {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-800:hover {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-900:hover {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-100:hover {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-200:hover {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-300:hover {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-400:hover {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-500:hover {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-600:hover {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-700:hover {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-800:hover {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-900:hover {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-100:hover {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-200:hover {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-300:hover {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-400:hover {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-500:hover {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-600:hover {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-700:hover {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-800:hover {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-900:hover {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-100:hover {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-200:hover {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-300:hover {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-400:hover {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-500:hover {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-600:hover {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-700:hover {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-800:hover {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-900:hover {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-100:hover {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-200:hover {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-300:hover {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-400:hover {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-500:hover {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-600:hover {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-700:hover {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-800:hover {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-900:hover {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-100:hover {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-200:hover {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-300:hover {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-400:hover {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-500:hover {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-600:hover {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-700:hover {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-800:hover {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-900:hover {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-transparent:focus {
+    color: transparent;
+  }
+
+  .sm\:focus\:text-current:focus {
+    color: currentColor;
+  }
+
+  .sm\:focus\:text-black:focus {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-white:focus {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-100:focus {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-200:focus {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-300:focus {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-400:focus {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-500:focus {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-600:focus {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-700:focus {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-800:focus {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-900:focus {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-100:focus {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-200:focus {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-300:focus {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-400:focus {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-500:focus {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-600:focus {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-700:focus {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-800:focus {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-900:focus {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-100:focus {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-200:focus {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-300:focus {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-400:focus {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-500:focus {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-600:focus {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-700:focus {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-800:focus {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-900:focus {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-100:focus {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-200:focus {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-300:focus {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-400:focus {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-500:focus {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-600:focus {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-700:focus {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-800:focus {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-900:focus {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-100:focus {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-200:focus {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-300:focus {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-400:focus {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-500:focus {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-600:focus {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-700:focus {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-800:focus {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-900:focus {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-100:focus {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-200:focus {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-300:focus {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-400:focus {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-500:focus {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-600:focus {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-700:focus {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-800:focus {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-900:focus {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-100:focus {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-200:focus {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-300:focus {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-400:focus {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-500:focus {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-600:focus {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-700:focus {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-800:focus {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-900:focus {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-100:focus {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-200:focus {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-300:focus {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-400:focus {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-500:focus {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-600:focus {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-700:focus {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-800:focus {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-900:focus {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-100:focus {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-200:focus {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-300:focus {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-400:focus {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-500:focus {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-600:focus {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-700:focus {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-800:focus {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-900:focus {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-100:focus {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-200:focus {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-300:focus {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-400:focus {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-500:focus {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-600:focus {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-700:focus {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-800:focus {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-900:focus {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .sm\:text-opacity-0 {
+    --text-opacity: 0;
+  }
+
+  .sm\:text-opacity-25 {
+    --text-opacity: 0.25;
+  }
+
+  .sm\:text-opacity-50 {
+    --text-opacity: 0.5;
+  }
+
+  .sm\:text-opacity-75 {
+    --text-opacity: 0.75;
+  }
+
+  .sm\:text-opacity-100 {
+    --text-opacity: 1;
+  }
+
+  .sm\:hover\:text-opacity-0:hover {
+    --text-opacity: 0;
+  }
+
+  .sm\:hover\:text-opacity-25:hover {
+    --text-opacity: 0.25;
+  }
+
+  .sm\:hover\:text-opacity-50:hover {
+    --text-opacity: 0.5;
+  }
+
+  .sm\:hover\:text-opacity-75:hover {
+    --text-opacity: 0.75;
+  }
+
+  .sm\:hover\:text-opacity-100:hover {
+    --text-opacity: 1;
+  }
+
+  .sm\:focus\:text-opacity-0:focus {
+    --text-opacity: 0;
+  }
+
+  .sm\:focus\:text-opacity-25:focus {
+    --text-opacity: 0.25;
+  }
+
+  .sm\:focus\:text-opacity-50:focus {
+    --text-opacity: 0.5;
+  }
+
+  .sm\:focus\:text-opacity-75:focus {
+    --text-opacity: 0.75;
+  }
+
+  .sm\:focus\:text-opacity-100:focus {
+    --text-opacity: 1;
+  }
+
+  .sm\:italic {
+    font-style: italic;
+  }
+
+  .sm\:not-italic {
+    font-style: normal;
+  }
+
+  .sm\:uppercase {
+    text-transform: uppercase;
+  }
+
+  .sm\:lowercase {
+    text-transform: lowercase;
+  }
+
+  .sm\:capitalize {
+    text-transform: capitalize;
+  }
+
+  .sm\:normal-case {
+    text-transform: none;
+  }
+
+  .sm\:underline {
+    text-decoration: underline;
+  }
+
+  .sm\:line-through {
+    text-decoration: line-through;
+  }
+
+  .sm\:no-underline {
+    text-decoration: none;
+  }
+
+  .sm\:hover\:underline:hover {
+    text-decoration: underline;
+  }
+
+  .sm\:hover\:line-through:hover {
+    text-decoration: line-through;
+  }
+
+  .sm\:hover\:no-underline:hover {
+    text-decoration: none;
+  }
+
+  .sm\:focus\:underline:focus {
+    text-decoration: underline;
+  }
+
+  .sm\:focus\:line-through:focus {
+    text-decoration: line-through;
+  }
+
+  .sm\:focus\:no-underline:focus {
+    text-decoration: none;
+  }
+
+  .sm\:antialiased {
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+
+  .sm\:subpixel-antialiased {
+    -webkit-font-smoothing: auto;
+    -moz-osx-font-smoothing: auto;
+  }
+
+  .sm\:ordinal, .sm\:slashed-zero, .sm\:lining-nums, .sm\:oldstyle-nums, .sm\:proportional-nums, .sm\:tabular-nums, .sm\:diagonal-fractions, .sm\:stacked-fractions {
+    --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+    font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+  }
+
+  .sm\:normal-nums {
+    font-variant-numeric: normal;
+  }
+
+  .sm\:ordinal {
+    --font-variant-numeric-ordinal: ordinal;
+  }
+
+  .sm\:slashed-zero {
+    --font-variant-numeric-slashed-zero: slashed-zero;
+  }
+
+  .sm\:lining-nums {
+    --font-variant-numeric-figure: lining-nums;
+  }
+
+  .sm\:oldstyle-nums {
+    --font-variant-numeric-figure: oldstyle-nums;
+  }
+
+  .sm\:proportional-nums {
+    --font-variant-numeric-spacing: proportional-nums;
+  }
+
+  .sm\:tabular-nums {
+    --font-variant-numeric-spacing: tabular-nums;
+  }
+
+  .sm\:diagonal-fractions {
+    --font-variant-numeric-fraction: diagonal-fractions;
+  }
+
+  .sm\:stacked-fractions {
+    --font-variant-numeric-fraction: stacked-fractions;
+  }
+
+  .sm\:tracking-tighter {
+    letter-spacing: -0.05em;
+  }
+
+  .sm\:tracking-tight {
+    letter-spacing: -0.025em;
+  }
+
+  .sm\:tracking-normal {
+    letter-spacing: 0;
+  }
+
+  .sm\:tracking-wide {
+    letter-spacing: 0.025em;
+  }
+
+  .sm\:tracking-wider {
+    letter-spacing: 0.05em;
+  }
+
+  .sm\:tracking-widest {
+    letter-spacing: 0.1em;
+  }
+
+  .sm\:select-none {
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+  }
+
+  .sm\:select-text {
+    -webkit-user-select: text;
+       -moz-user-select: text;
+        -ms-user-select: text;
+            user-select: text;
+  }
+
+  .sm\:select-all {
+    -webkit-user-select: all;
+       -moz-user-select: all;
+        -ms-user-select: all;
+            user-select: all;
+  }
+
+  .sm\:select-auto {
+    -webkit-user-select: auto;
+       -moz-user-select: auto;
+        -ms-user-select: auto;
+            user-select: auto;
+  }
+
+  .sm\:align-baseline {
+    vertical-align: baseline;
+  }
+
+  .sm\:align-top {
+    vertical-align: top;
+  }
+
+  .sm\:align-middle {
+    vertical-align: middle;
+  }
+
+  .sm\:align-bottom {
+    vertical-align: bottom;
+  }
+
+  .sm\:align-text-top {
+    vertical-align: text-top;
+  }
+
+  .sm\:align-text-bottom {
+    vertical-align: text-bottom;
+  }
+
+  .sm\:visible {
+    visibility: visible;
+  }
+
+  .sm\:invisible {
+    visibility: hidden;
+  }
+
+  .sm\:whitespace-normal {
+    white-space: normal;
+  }
+
+  .sm\:whitespace-no-wrap {
+    white-space: nowrap;
+  }
+
+  .sm\:whitespace-pre {
+    white-space: pre;
+  }
+
+  .sm\:whitespace-pre-line {
+    white-space: pre-line;
+  }
+
+  .sm\:whitespace-pre-wrap {
+    white-space: pre-wrap;
+  }
+
+  .sm\:break-normal {
+    overflow-wrap: normal;
+    word-break: normal;
+  }
+
+  .sm\:break-words {
+    overflow-wrap: break-word;
+  }
+
+  .sm\:break-all {
+    word-break: break-all;
+  }
+
+  .sm\:truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .sm\:w-0 {
+    width: 0;
+  }
+
+  .sm\:w-1 {
+    width: 0.25rem;
+  }
+
+  .sm\:w-2 {
+    width: 0.5rem;
+  }
+
+  .sm\:w-3 {
+    width: 0.75rem;
+  }
+
+  .sm\:w-4 {
+    width: 1rem;
+  }
+
+  .sm\:w-5 {
+    width: 1.25rem;
+  }
+
+  .sm\:w-6 {
+    width: 1.5rem;
+  }
+
+  .sm\:w-8 {
+    width: 2rem;
+  }
+
+  .sm\:w-10 {
+    width: 2.5rem;
+  }
+
+  .sm\:w-12 {
+    width: 3rem;
+  }
+
+  .sm\:w-16 {
+    width: 4rem;
+  }
+
+  .sm\:w-20 {
+    width: 5rem;
+  }
+
+  .sm\:w-24 {
+    width: 6rem;
+  }
+
+  .sm\:w-32 {
+    width: 8rem;
+  }
+
+  .sm\:w-40 {
+    width: 10rem;
+  }
+
+  .sm\:w-48 {
+    width: 12rem;
+  }
+
+  .sm\:w-56 {
+    width: 14rem;
+  }
+
+  .sm\:w-64 {
+    width: 16rem;
+  }
+
+  .sm\:w-auto {
+    width: auto;
+  }
+
+  .sm\:w-px {
+    width: 1px;
+  }
+
+  .sm\:w-1\/2 {
+    width: 50%;
+  }
+
+  .sm\:w-1\/3 {
+    width: 33.333333%;
+  }
+
+  .sm\:w-2\/3 {
+    width: 66.666667%;
+  }
+
+  .sm\:w-1\/4 {
+    width: 25%;
+  }
+
+  .sm\:w-2\/4 {
+    width: 50%;
+  }
+
+  .sm\:w-3\/4 {
+    width: 75%;
+  }
+
+  .sm\:w-1\/5 {
+    width: 20%;
+  }
+
+  .sm\:w-2\/5 {
+    width: 40%;
+  }
+
+  .sm\:w-3\/5 {
+    width: 60%;
+  }
+
+  .sm\:w-4\/5 {
+    width: 80%;
+  }
+
+  .sm\:w-1\/6 {
+    width: 16.666667%;
+  }
+
+  .sm\:w-2\/6 {
+    width: 33.333333%;
+  }
+
+  .sm\:w-3\/6 {
+    width: 50%;
+  }
+
+  .sm\:w-4\/6 {
+    width: 66.666667%;
+  }
+
+  .sm\:w-5\/6 {
+    width: 83.333333%;
+  }
+
+  .sm\:w-1\/12 {
+    width: 8.333333%;
+  }
+
+  .sm\:w-2\/12 {
+    width: 16.666667%;
+  }
+
+  .sm\:w-3\/12 {
+    width: 25%;
+  }
+
+  .sm\:w-4\/12 {
+    width: 33.333333%;
+  }
+
+  .sm\:w-5\/12 {
+    width: 41.666667%;
+  }
+
+  .sm\:w-6\/12 {
+    width: 50%;
+  }
+
+  .sm\:w-7\/12 {
+    width: 58.333333%;
+  }
+
+  .sm\:w-8\/12 {
+    width: 66.666667%;
+  }
+
+  .sm\:w-9\/12 {
+    width: 75%;
+  }
+
+  .sm\:w-10\/12 {
+    width: 83.333333%;
+  }
+
+  .sm\:w-11\/12 {
+    width: 91.666667%;
+  }
+
+  .sm\:w-full {
+    width: 100%;
+  }
+
+  .sm\:w-screen {
+    width: 100vw;
+  }
+
+  .sm\:z-0 {
+    z-index: 0;
+  }
+
+  .sm\:z-10 {
+    z-index: 10;
+  }
+
+  .sm\:z-20 {
+    z-index: 20;
+  }
+
+  .sm\:z-30 {
+    z-index: 30;
+  }
+
+  .sm\:z-40 {
+    z-index: 40;
+  }
+
+  .sm\:z-50 {
+    z-index: 50;
+  }
+
+  .sm\:z-auto {
+    z-index: auto;
+  }
+
+  .sm\:gap-0 {
+    grid-gap: 0;
+    gap: 0;
+  }
+
+  .sm\:gap-1 {
+    grid-gap: 0.25rem;
+    gap: 0.25rem;
+  }
+
+  .sm\:gap-2 {
+    grid-gap: 0.5rem;
+    gap: 0.5rem;
+  }
+
+  .sm\:gap-3 {
+    grid-gap: 0.75rem;
+    gap: 0.75rem;
+  }
+
+  .sm\:gap-4 {
+    grid-gap: 1rem;
+    gap: 1rem;
+  }
+
+  .sm\:gap-5 {
+    grid-gap: 1.25rem;
+    gap: 1.25rem;
+  }
+
+  .sm\:gap-6 {
+    grid-gap: 1.5rem;
+    gap: 1.5rem;
+  }
+
+  .sm\:gap-8 {
+    grid-gap: 2rem;
+    gap: 2rem;
+  }
+
+  .sm\:gap-10 {
+    grid-gap: 2.5rem;
+    gap: 2.5rem;
+  }
+
+  .sm\:gap-12 {
+    grid-gap: 3rem;
+    gap: 3rem;
+  }
+
+  .sm\:gap-16 {
+    grid-gap: 4rem;
+    gap: 4rem;
+  }
+
+  .sm\:gap-20 {
+    grid-gap: 5rem;
+    gap: 5rem;
+  }
+
+  .sm\:gap-24 {
+    grid-gap: 6rem;
+    gap: 6rem;
+  }
+
+  .sm\:gap-32 {
+    grid-gap: 8rem;
+    gap: 8rem;
+  }
+
+  .sm\:gap-40 {
+    grid-gap: 10rem;
+    gap: 10rem;
+  }
+
+  .sm\:gap-48 {
+    grid-gap: 12rem;
+    gap: 12rem;
+  }
+
+  .sm\:gap-56 {
+    grid-gap: 14rem;
+    gap: 14rem;
+  }
+
+  .sm\:gap-64 {
+    grid-gap: 16rem;
+    gap: 16rem;
+  }
+
+  .sm\:gap-px {
+    grid-gap: 1px;
+    gap: 1px;
+  }
+
+  .sm\:col-gap-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .sm\:col-gap-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .sm\:col-gap-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .sm\:col-gap-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .sm\:col-gap-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .sm\:col-gap-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .sm\:col-gap-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .sm\:col-gap-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .sm\:col-gap-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .sm\:col-gap-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .sm\:col-gap-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .sm\:col-gap-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .sm\:col-gap-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .sm\:col-gap-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .sm\:col-gap-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .sm\:col-gap-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .sm\:col-gap-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .sm\:col-gap-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .sm\:col-gap-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .sm\:gap-x-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .sm\:gap-x-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .sm\:gap-x-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .sm\:gap-x-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .sm\:gap-x-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .sm\:gap-x-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .sm\:gap-x-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .sm\:gap-x-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .sm\:gap-x-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .sm\:gap-x-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .sm\:gap-x-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .sm\:gap-x-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .sm\:gap-x-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .sm\:gap-x-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .sm\:gap-x-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .sm\:gap-x-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .sm\:gap-x-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .sm\:gap-x-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .sm\:gap-x-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .sm\:row-gap-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .sm\:row-gap-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .sm\:row-gap-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .sm\:row-gap-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .sm\:row-gap-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .sm\:row-gap-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .sm\:row-gap-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .sm\:row-gap-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .sm\:row-gap-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .sm\:row-gap-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .sm\:row-gap-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .sm\:row-gap-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .sm\:row-gap-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .sm\:row-gap-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .sm\:row-gap-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .sm\:row-gap-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .sm\:row-gap-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .sm\:row-gap-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .sm\:row-gap-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .sm\:gap-y-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .sm\:gap-y-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .sm\:gap-y-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .sm\:gap-y-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .sm\:gap-y-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .sm\:gap-y-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .sm\:gap-y-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .sm\:gap-y-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .sm\:gap-y-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .sm\:gap-y-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .sm\:gap-y-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .sm\:gap-y-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .sm\:gap-y-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .sm\:gap-y-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .sm\:gap-y-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .sm\:gap-y-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .sm\:gap-y-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .sm\:gap-y-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .sm\:gap-y-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .sm\:grid-flow-row {
+    grid-auto-flow: row;
+  }
+
+  .sm\:grid-flow-col {
+    grid-auto-flow: column;
+  }
+
+  .sm\:grid-flow-row-dense {
+    grid-auto-flow: row dense;
+  }
+
+  .sm\:grid-flow-col-dense {
+    grid-auto-flow: column dense;
+  }
+
+  .sm\:grid-cols-1 {
+    grid-template-columns: repeat(1, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-2 {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-3 {
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-4 {
+    grid-template-columns: repeat(4, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-5 {
+    grid-template-columns: repeat(5, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-6 {
+    grid-template-columns: repeat(6, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-7 {
+    grid-template-columns: repeat(7, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-8 {
+    grid-template-columns: repeat(8, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-9 {
+    grid-template-columns: repeat(9, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-10 {
+    grid-template-columns: repeat(10, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-11 {
+    grid-template-columns: repeat(11, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-12 {
+    grid-template-columns: repeat(12, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-none {
+    grid-template-columns: none;
+  }
+
+  .sm\:col-auto {
+    grid-column: auto;
+  }
+
+  .sm\:col-span-1 {
+    grid-column: span 1 / span 1;
+  }
+
+  .sm\:col-span-2 {
+    grid-column: span 2 / span 2;
+  }
+
+  .sm\:col-span-3 {
+    grid-column: span 3 / span 3;
+  }
+
+  .sm\:col-span-4 {
+    grid-column: span 4 / span 4;
+  }
+
+  .sm\:col-span-5 {
+    grid-column: span 5 / span 5;
+  }
+
+  .sm\:col-span-6 {
+    grid-column: span 6 / span 6;
+  }
+
+  .sm\:col-span-7 {
+    grid-column: span 7 / span 7;
+  }
+
+  .sm\:col-span-8 {
+    grid-column: span 8 / span 8;
+  }
+
+  .sm\:col-span-9 {
+    grid-column: span 9 / span 9;
+  }
+
+  .sm\:col-span-10 {
+    grid-column: span 10 / span 10;
+  }
+
+  .sm\:col-span-11 {
+    grid-column: span 11 / span 11;
+  }
+
+  .sm\:col-span-12 {
+    grid-column: span 12 / span 12;
+  }
+
+  .sm\:col-start-1 {
+    grid-column-start: 1;
+  }
+
+  .sm\:col-start-2 {
+    grid-column-start: 2;
+  }
+
+  .sm\:col-start-3 {
+    grid-column-start: 3;
+  }
+
+  .sm\:col-start-4 {
+    grid-column-start: 4;
+  }
+
+  .sm\:col-start-5 {
+    grid-column-start: 5;
+  }
+
+  .sm\:col-start-6 {
+    grid-column-start: 6;
+  }
+
+  .sm\:col-start-7 {
+    grid-column-start: 7;
+  }
+
+  .sm\:col-start-8 {
+    grid-column-start: 8;
+  }
+
+  .sm\:col-start-9 {
+    grid-column-start: 9;
+  }
+
+  .sm\:col-start-10 {
+    grid-column-start: 10;
+  }
+
+  .sm\:col-start-11 {
+    grid-column-start: 11;
+  }
+
+  .sm\:col-start-12 {
+    grid-column-start: 12;
+  }
+
+  .sm\:col-start-13 {
+    grid-column-start: 13;
+  }
+
+  .sm\:col-start-auto {
+    grid-column-start: auto;
+  }
+
+  .sm\:col-end-1 {
+    grid-column-end: 1;
+  }
+
+  .sm\:col-end-2 {
+    grid-column-end: 2;
+  }
+
+  .sm\:col-end-3 {
+    grid-column-end: 3;
+  }
+
+  .sm\:col-end-4 {
+    grid-column-end: 4;
+  }
+
+  .sm\:col-end-5 {
+    grid-column-end: 5;
+  }
+
+  .sm\:col-end-6 {
+    grid-column-end: 6;
+  }
+
+  .sm\:col-end-7 {
+    grid-column-end: 7;
+  }
+
+  .sm\:col-end-8 {
+    grid-column-end: 8;
+  }
+
+  .sm\:col-end-9 {
+    grid-column-end: 9;
+  }
+
+  .sm\:col-end-10 {
+    grid-column-end: 10;
+  }
+
+  .sm\:col-end-11 {
+    grid-column-end: 11;
+  }
+
+  .sm\:col-end-12 {
+    grid-column-end: 12;
+  }
+
+  .sm\:col-end-13 {
+    grid-column-end: 13;
+  }
+
+  .sm\:col-end-auto {
+    grid-column-end: auto;
+  }
+
+  .sm\:grid-rows-1 {
+    grid-template-rows: repeat(1, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-2 {
+    grid-template-rows: repeat(2, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-3 {
+    grid-template-rows: repeat(3, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-4 {
+    grid-template-rows: repeat(4, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-5 {
+    grid-template-rows: repeat(5, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-6 {
+    grid-template-rows: repeat(6, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-none {
+    grid-template-rows: none;
+  }
+
+  .sm\:row-auto {
+    grid-row: auto;
+  }
+
+  .sm\:row-span-1 {
+    grid-row: span 1 / span 1;
+  }
+
+  .sm\:row-span-2 {
+    grid-row: span 2 / span 2;
+  }
+
+  .sm\:row-span-3 {
+    grid-row: span 3 / span 3;
+  }
+
+  .sm\:row-span-4 {
+    grid-row: span 4 / span 4;
+  }
+
+  .sm\:row-span-5 {
+    grid-row: span 5 / span 5;
+  }
+
+  .sm\:row-span-6 {
+    grid-row: span 6 / span 6;
+  }
+
+  .sm\:row-start-1 {
+    grid-row-start: 1;
+  }
+
+  .sm\:row-start-2 {
+    grid-row-start: 2;
+  }
+
+  .sm\:row-start-3 {
+    grid-row-start: 3;
+  }
+
+  .sm\:row-start-4 {
+    grid-row-start: 4;
+  }
+
+  .sm\:row-start-5 {
+    grid-row-start: 5;
+  }
+
+  .sm\:row-start-6 {
+    grid-row-start: 6;
+  }
+
+  .sm\:row-start-7 {
+    grid-row-start: 7;
+  }
+
+  .sm\:row-start-auto {
+    grid-row-start: auto;
+  }
+
+  .sm\:row-end-1 {
+    grid-row-end: 1;
+  }
+
+  .sm\:row-end-2 {
+    grid-row-end: 2;
+  }
+
+  .sm\:row-end-3 {
+    grid-row-end: 3;
+  }
+
+  .sm\:row-end-4 {
+    grid-row-end: 4;
+  }
+
+  .sm\:row-end-5 {
+    grid-row-end: 5;
+  }
+
+  .sm\:row-end-6 {
+    grid-row-end: 6;
+  }
+
+  .sm\:row-end-7 {
+    grid-row-end: 7;
+  }
+
+  .sm\:row-end-auto {
+    grid-row-end: auto;
+  }
+
+  .sm\:transform {
+    --transform-translate-x: 0;
+    --transform-translate-y: 0;
+    --transform-rotate: 0;
+    --transform-skew-x: 0;
+    --transform-skew-y: 0;
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+    transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+  }
+
+  .sm\:transform-none {
+    transform: none;
+  }
+
+  .sm\:origin-center {
+    transform-origin: center;
+  }
+
+  .sm\:origin-top {
+    transform-origin: top;
+  }
+
+  .sm\:origin-top-right {
+    transform-origin: top right;
+  }
+
+  .sm\:origin-right {
+    transform-origin: right;
+  }
+
+  .sm\:origin-bottom-right {
+    transform-origin: bottom right;
+  }
+
+  .sm\:origin-bottom {
+    transform-origin: bottom;
+  }
+
+  .sm\:origin-bottom-left {
+    transform-origin: bottom left;
+  }
+
+  .sm\:origin-left {
+    transform-origin: left;
+  }
+
+  .sm\:origin-top-left {
+    transform-origin: top left;
+  }
+
+  .sm\:scale-0 {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .sm\:scale-50 {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .sm\:scale-75 {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .sm\:scale-90 {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .sm\:scale-95 {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .sm\:scale-100 {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .sm\:scale-105 {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:scale-110 {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:scale-125 {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:scale-150 {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:scale-x-0 {
+    --transform-scale-x: 0;
+  }
+
+  .sm\:scale-x-50 {
+    --transform-scale-x: .5;
+  }
+
+  .sm\:scale-x-75 {
+    --transform-scale-x: .75;
+  }
+
+  .sm\:scale-x-90 {
+    --transform-scale-x: .9;
+  }
+
+  .sm\:scale-x-95 {
+    --transform-scale-x: .95;
+  }
+
+  .sm\:scale-x-100 {
+    --transform-scale-x: 1;
+  }
+
+  .sm\:scale-x-105 {
+    --transform-scale-x: 1.05;
+  }
+
+  .sm\:scale-x-110 {
+    --transform-scale-x: 1.1;
+  }
+
+  .sm\:scale-x-125 {
+    --transform-scale-x: 1.25;
+  }
+
+  .sm\:scale-x-150 {
+    --transform-scale-x: 1.5;
+  }
+
+  .sm\:scale-y-0 {
+    --transform-scale-y: 0;
+  }
+
+  .sm\:scale-y-50 {
+    --transform-scale-y: .5;
+  }
+
+  .sm\:scale-y-75 {
+    --transform-scale-y: .75;
+  }
+
+  .sm\:scale-y-90 {
+    --transform-scale-y: .9;
+  }
+
+  .sm\:scale-y-95 {
+    --transform-scale-y: .95;
+  }
+
+  .sm\:scale-y-100 {
+    --transform-scale-y: 1;
+  }
+
+  .sm\:scale-y-105 {
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:scale-y-110 {
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:scale-y-125 {
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:scale-y-150 {
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:hover\:scale-0:hover {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .sm\:hover\:scale-50:hover {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .sm\:hover\:scale-75:hover {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .sm\:hover\:scale-90:hover {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .sm\:hover\:scale-95:hover {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .sm\:hover\:scale-100:hover {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .sm\:hover\:scale-105:hover {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:hover\:scale-110:hover {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:hover\:scale-125:hover {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:hover\:scale-150:hover {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:hover\:scale-x-0:hover {
+    --transform-scale-x: 0;
+  }
+
+  .sm\:hover\:scale-x-50:hover {
+    --transform-scale-x: .5;
+  }
+
+  .sm\:hover\:scale-x-75:hover {
+    --transform-scale-x: .75;
+  }
+
+  .sm\:hover\:scale-x-90:hover {
+    --transform-scale-x: .9;
+  }
+
+  .sm\:hover\:scale-x-95:hover {
+    --transform-scale-x: .95;
+  }
+
+  .sm\:hover\:scale-x-100:hover {
+    --transform-scale-x: 1;
+  }
+
+  .sm\:hover\:scale-x-105:hover {
+    --transform-scale-x: 1.05;
+  }
+
+  .sm\:hover\:scale-x-110:hover {
+    --transform-scale-x: 1.1;
+  }
+
+  .sm\:hover\:scale-x-125:hover {
+    --transform-scale-x: 1.25;
+  }
+
+  .sm\:hover\:scale-x-150:hover {
+    --transform-scale-x: 1.5;
+  }
+
+  .sm\:hover\:scale-y-0:hover {
+    --transform-scale-y: 0;
+  }
+
+  .sm\:hover\:scale-y-50:hover {
+    --transform-scale-y: .5;
+  }
+
+  .sm\:hover\:scale-y-75:hover {
+    --transform-scale-y: .75;
+  }
+
+  .sm\:hover\:scale-y-90:hover {
+    --transform-scale-y: .9;
+  }
+
+  .sm\:hover\:scale-y-95:hover {
+    --transform-scale-y: .95;
+  }
+
+  .sm\:hover\:scale-y-100:hover {
+    --transform-scale-y: 1;
+  }
+
+  .sm\:hover\:scale-y-105:hover {
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:hover\:scale-y-110:hover {
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:hover\:scale-y-125:hover {
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:hover\:scale-y-150:hover {
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:focus\:scale-0:focus {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .sm\:focus\:scale-50:focus {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .sm\:focus\:scale-75:focus {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .sm\:focus\:scale-90:focus {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .sm\:focus\:scale-95:focus {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .sm\:focus\:scale-100:focus {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .sm\:focus\:scale-105:focus {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:focus\:scale-110:focus {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:focus\:scale-125:focus {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:focus\:scale-150:focus {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:focus\:scale-x-0:focus {
+    --transform-scale-x: 0;
+  }
+
+  .sm\:focus\:scale-x-50:focus {
+    --transform-scale-x: .5;
+  }
+
+  .sm\:focus\:scale-x-75:focus {
+    --transform-scale-x: .75;
+  }
+
+  .sm\:focus\:scale-x-90:focus {
+    --transform-scale-x: .9;
+  }
+
+  .sm\:focus\:scale-x-95:focus {
+    --transform-scale-x: .95;
+  }
+
+  .sm\:focus\:scale-x-100:focus {
+    --transform-scale-x: 1;
+  }
+
+  .sm\:focus\:scale-x-105:focus {
+    --transform-scale-x: 1.05;
+  }
+
+  .sm\:focus\:scale-x-110:focus {
+    --transform-scale-x: 1.1;
+  }
+
+  .sm\:focus\:scale-x-125:focus {
+    --transform-scale-x: 1.25;
+  }
+
+  .sm\:focus\:scale-x-150:focus {
+    --transform-scale-x: 1.5;
+  }
+
+  .sm\:focus\:scale-y-0:focus {
+    --transform-scale-y: 0;
+  }
+
+  .sm\:focus\:scale-y-50:focus {
+    --transform-scale-y: .5;
+  }
+
+  .sm\:focus\:scale-y-75:focus {
+    --transform-scale-y: .75;
+  }
+
+  .sm\:focus\:scale-y-90:focus {
+    --transform-scale-y: .9;
+  }
+
+  .sm\:focus\:scale-y-95:focus {
+    --transform-scale-y: .95;
+  }
+
+  .sm\:focus\:scale-y-100:focus {
+    --transform-scale-y: 1;
+  }
+
+  .sm\:focus\:scale-y-105:focus {
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:focus\:scale-y-110:focus {
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:focus\:scale-y-125:focus {
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:focus\:scale-y-150:focus {
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:rotate-0 {
+    --transform-rotate: 0;
+  }
+
+  .sm\:rotate-45 {
+    --transform-rotate: 45deg;
+  }
+
+  .sm\:rotate-90 {
+    --transform-rotate: 90deg;
+  }
+
+  .sm\:rotate-180 {
+    --transform-rotate: 180deg;
+  }
+
+  .sm\:-rotate-180 {
+    --transform-rotate: -180deg;
+  }
+
+  .sm\:-rotate-90 {
+    --transform-rotate: -90deg;
+  }
+
+  .sm\:-rotate-45 {
+    --transform-rotate: -45deg;
+  }
+
+  .sm\:hover\:rotate-0:hover {
+    --transform-rotate: 0;
+  }
+
+  .sm\:hover\:rotate-45:hover {
+    --transform-rotate: 45deg;
+  }
+
+  .sm\:hover\:rotate-90:hover {
+    --transform-rotate: 90deg;
+  }
+
+  .sm\:hover\:rotate-180:hover {
+    --transform-rotate: 180deg;
+  }
+
+  .sm\:hover\:-rotate-180:hover {
+    --transform-rotate: -180deg;
+  }
+
+  .sm\:hover\:-rotate-90:hover {
+    --transform-rotate: -90deg;
+  }
+
+  .sm\:hover\:-rotate-45:hover {
+    --transform-rotate: -45deg;
+  }
+
+  .sm\:focus\:rotate-0:focus {
+    --transform-rotate: 0;
+  }
+
+  .sm\:focus\:rotate-45:focus {
+    --transform-rotate: 45deg;
+  }
+
+  .sm\:focus\:rotate-90:focus {
+    --transform-rotate: 90deg;
+  }
+
+  .sm\:focus\:rotate-180:focus {
+    --transform-rotate: 180deg;
+  }
+
+  .sm\:focus\:-rotate-180:focus {
+    --transform-rotate: -180deg;
+  }
+
+  .sm\:focus\:-rotate-90:focus {
+    --transform-rotate: -90deg;
+  }
+
+  .sm\:focus\:-rotate-45:focus {
+    --transform-rotate: -45deg;
+  }
+
+  .sm\:translate-x-0 {
+    --transform-translate-x: 0;
+  }
+
+  .sm\:translate-x-1 {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .sm\:translate-x-2 {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .sm\:translate-x-3 {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .sm\:translate-x-4 {
+    --transform-translate-x: 1rem;
+  }
+
+  .sm\:translate-x-5 {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .sm\:translate-x-6 {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .sm\:translate-x-8 {
+    --transform-translate-x: 2rem;
+  }
+
+  .sm\:translate-x-10 {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .sm\:translate-x-12 {
+    --transform-translate-x: 3rem;
+  }
+
+  .sm\:translate-x-16 {
+    --transform-translate-x: 4rem;
+  }
+
+  .sm\:translate-x-20 {
+    --transform-translate-x: 5rem;
+  }
+
+  .sm\:translate-x-24 {
+    --transform-translate-x: 6rem;
+  }
+
+  .sm\:translate-x-32 {
+    --transform-translate-x: 8rem;
+  }
+
+  .sm\:translate-x-40 {
+    --transform-translate-x: 10rem;
+  }
+
+  .sm\:translate-x-48 {
+    --transform-translate-x: 12rem;
+  }
+
+  .sm\:translate-x-56 {
+    --transform-translate-x: 14rem;
+  }
+
+  .sm\:translate-x-64 {
+    --transform-translate-x: 16rem;
+  }
+
+  .sm\:translate-x-px {
+    --transform-translate-x: 1px;
+  }
+
+  .sm\:-translate-x-1 {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .sm\:-translate-x-2 {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .sm\:-translate-x-3 {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .sm\:-translate-x-4 {
+    --transform-translate-x: -1rem;
+  }
+
+  .sm\:-translate-x-5 {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .sm\:-translate-x-6 {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .sm\:-translate-x-8 {
+    --transform-translate-x: -2rem;
+  }
+
+  .sm\:-translate-x-10 {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .sm\:-translate-x-12 {
+    --transform-translate-x: -3rem;
+  }
+
+  .sm\:-translate-x-16 {
+    --transform-translate-x: -4rem;
+  }
+
+  .sm\:-translate-x-20 {
+    --transform-translate-x: -5rem;
+  }
+
+  .sm\:-translate-x-24 {
+    --transform-translate-x: -6rem;
+  }
+
+  .sm\:-translate-x-32 {
+    --transform-translate-x: -8rem;
+  }
+
+  .sm\:-translate-x-40 {
+    --transform-translate-x: -10rem;
+  }
+
+  .sm\:-translate-x-48 {
+    --transform-translate-x: -12rem;
+  }
+
+  .sm\:-translate-x-56 {
+    --transform-translate-x: -14rem;
+  }
+
+  .sm\:-translate-x-64 {
+    --transform-translate-x: -16rem;
+  }
+
+  .sm\:-translate-x-px {
+    --transform-translate-x: -1px;
+  }
+
+  .sm\:-translate-x-full {
+    --transform-translate-x: -100%;
+  }
+
+  .sm\:-translate-x-1\/2 {
+    --transform-translate-x: -50%;
+  }
+
+  .sm\:translate-x-1\/2 {
+    --transform-translate-x: 50%;
+  }
+
+  .sm\:translate-x-full {
+    --transform-translate-x: 100%;
+  }
+
+  .sm\:translate-y-0 {
+    --transform-translate-y: 0;
+  }
+
+  .sm\:translate-y-1 {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .sm\:translate-y-2 {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .sm\:translate-y-3 {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .sm\:translate-y-4 {
+    --transform-translate-y: 1rem;
+  }
+
+  .sm\:translate-y-5 {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .sm\:translate-y-6 {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .sm\:translate-y-8 {
+    --transform-translate-y: 2rem;
+  }
+
+  .sm\:translate-y-10 {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .sm\:translate-y-12 {
+    --transform-translate-y: 3rem;
+  }
+
+  .sm\:translate-y-16 {
+    --transform-translate-y: 4rem;
+  }
+
+  .sm\:translate-y-20 {
+    --transform-translate-y: 5rem;
+  }
+
+  .sm\:translate-y-24 {
+    --transform-translate-y: 6rem;
+  }
+
+  .sm\:translate-y-32 {
+    --transform-translate-y: 8rem;
+  }
+
+  .sm\:translate-y-40 {
+    --transform-translate-y: 10rem;
+  }
+
+  .sm\:translate-y-48 {
+    --transform-translate-y: 12rem;
+  }
+
+  .sm\:translate-y-56 {
+    --transform-translate-y: 14rem;
+  }
+
+  .sm\:translate-y-64 {
+    --transform-translate-y: 16rem;
+  }
+
+  .sm\:translate-y-px {
+    --transform-translate-y: 1px;
+  }
+
+  .sm\:-translate-y-1 {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .sm\:-translate-y-2 {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .sm\:-translate-y-3 {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .sm\:-translate-y-4 {
+    --transform-translate-y: -1rem;
+  }
+
+  .sm\:-translate-y-5 {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .sm\:-translate-y-6 {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .sm\:-translate-y-8 {
+    --transform-translate-y: -2rem;
+  }
+
+  .sm\:-translate-y-10 {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .sm\:-translate-y-12 {
+    --transform-translate-y: -3rem;
+  }
+
+  .sm\:-translate-y-16 {
+    --transform-translate-y: -4rem;
+  }
+
+  .sm\:-translate-y-20 {
+    --transform-translate-y: -5rem;
+  }
+
+  .sm\:-translate-y-24 {
+    --transform-translate-y: -6rem;
+  }
+
+  .sm\:-translate-y-32 {
+    --transform-translate-y: -8rem;
+  }
+
+  .sm\:-translate-y-40 {
+    --transform-translate-y: -10rem;
+  }
+
+  .sm\:-translate-y-48 {
+    --transform-translate-y: -12rem;
+  }
+
+  .sm\:-translate-y-56 {
+    --transform-translate-y: -14rem;
+  }
+
+  .sm\:-translate-y-64 {
+    --transform-translate-y: -16rem;
+  }
+
+  .sm\:-translate-y-px {
+    --transform-translate-y: -1px;
+  }
+
+  .sm\:-translate-y-full {
+    --transform-translate-y: -100%;
+  }
+
+  .sm\:-translate-y-1\/2 {
+    --transform-translate-y: -50%;
+  }
+
+  .sm\:translate-y-1\/2 {
+    --transform-translate-y: 50%;
+  }
+
+  .sm\:translate-y-full {
+    --transform-translate-y: 100%;
+  }
+
+  .sm\:hover\:translate-x-0:hover {
+    --transform-translate-x: 0;
+  }
+
+  .sm\:hover\:translate-x-1:hover {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .sm\:hover\:translate-x-2:hover {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .sm\:hover\:translate-x-3:hover {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .sm\:hover\:translate-x-4:hover {
+    --transform-translate-x: 1rem;
+  }
+
+  .sm\:hover\:translate-x-5:hover {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .sm\:hover\:translate-x-6:hover {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .sm\:hover\:translate-x-8:hover {
+    --transform-translate-x: 2rem;
+  }
+
+  .sm\:hover\:translate-x-10:hover {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .sm\:hover\:translate-x-12:hover {
+    --transform-translate-x: 3rem;
+  }
+
+  .sm\:hover\:translate-x-16:hover {
+    --transform-translate-x: 4rem;
+  }
+
+  .sm\:hover\:translate-x-20:hover {
+    --transform-translate-x: 5rem;
+  }
+
+  .sm\:hover\:translate-x-24:hover {
+    --transform-translate-x: 6rem;
+  }
+
+  .sm\:hover\:translate-x-32:hover {
+    --transform-translate-x: 8rem;
+  }
+
+  .sm\:hover\:translate-x-40:hover {
+    --transform-translate-x: 10rem;
+  }
+
+  .sm\:hover\:translate-x-48:hover {
+    --transform-translate-x: 12rem;
+  }
+
+  .sm\:hover\:translate-x-56:hover {
+    --transform-translate-x: 14rem;
+  }
+
+  .sm\:hover\:translate-x-64:hover {
+    --transform-translate-x: 16rem;
+  }
+
+  .sm\:hover\:translate-x-px:hover {
+    --transform-translate-x: 1px;
+  }
+
+  .sm\:hover\:-translate-x-1:hover {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .sm\:hover\:-translate-x-2:hover {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .sm\:hover\:-translate-x-3:hover {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .sm\:hover\:-translate-x-4:hover {
+    --transform-translate-x: -1rem;
+  }
+
+  .sm\:hover\:-translate-x-5:hover {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .sm\:hover\:-translate-x-6:hover {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .sm\:hover\:-translate-x-8:hover {
+    --transform-translate-x: -2rem;
+  }
+
+  .sm\:hover\:-translate-x-10:hover {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .sm\:hover\:-translate-x-12:hover {
+    --transform-translate-x: -3rem;
+  }
+
+  .sm\:hover\:-translate-x-16:hover {
+    --transform-translate-x: -4rem;
+  }
+
+  .sm\:hover\:-translate-x-20:hover {
+    --transform-translate-x: -5rem;
+  }
+
+  .sm\:hover\:-translate-x-24:hover {
+    --transform-translate-x: -6rem;
+  }
+
+  .sm\:hover\:-translate-x-32:hover {
+    --transform-translate-x: -8rem;
+  }
+
+  .sm\:hover\:-translate-x-40:hover {
+    --transform-translate-x: -10rem;
+  }
+
+  .sm\:hover\:-translate-x-48:hover {
+    --transform-translate-x: -12rem;
+  }
+
+  .sm\:hover\:-translate-x-56:hover {
+    --transform-translate-x: -14rem;
+  }
+
+  .sm\:hover\:-translate-x-64:hover {
+    --transform-translate-x: -16rem;
+  }
+
+  .sm\:hover\:-translate-x-px:hover {
+    --transform-translate-x: -1px;
+  }
+
+  .sm\:hover\:-translate-x-full:hover {
+    --transform-translate-x: -100%;
+  }
+
+  .sm\:hover\:-translate-x-1\/2:hover {
+    --transform-translate-x: -50%;
+  }
+
+  .sm\:hover\:translate-x-1\/2:hover {
+    --transform-translate-x: 50%;
+  }
+
+  .sm\:hover\:translate-x-full:hover {
+    --transform-translate-x: 100%;
+  }
+
+  .sm\:hover\:translate-y-0:hover {
+    --transform-translate-y: 0;
+  }
+
+  .sm\:hover\:translate-y-1:hover {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .sm\:hover\:translate-y-2:hover {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .sm\:hover\:translate-y-3:hover {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .sm\:hover\:translate-y-4:hover {
+    --transform-translate-y: 1rem;
+  }
+
+  .sm\:hover\:translate-y-5:hover {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .sm\:hover\:translate-y-6:hover {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .sm\:hover\:translate-y-8:hover {
+    --transform-translate-y: 2rem;
+  }
+
+  .sm\:hover\:translate-y-10:hover {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .sm\:hover\:translate-y-12:hover {
+    --transform-translate-y: 3rem;
+  }
+
+  .sm\:hover\:translate-y-16:hover {
+    --transform-translate-y: 4rem;
+  }
+
+  .sm\:hover\:translate-y-20:hover {
+    --transform-translate-y: 5rem;
+  }
+
+  .sm\:hover\:translate-y-24:hover {
+    --transform-translate-y: 6rem;
+  }
+
+  .sm\:hover\:translate-y-32:hover {
+    --transform-translate-y: 8rem;
+  }
+
+  .sm\:hover\:translate-y-40:hover {
+    --transform-translate-y: 10rem;
+  }
+
+  .sm\:hover\:translate-y-48:hover {
+    --transform-translate-y: 12rem;
+  }
+
+  .sm\:hover\:translate-y-56:hover {
+    --transform-translate-y: 14rem;
+  }
+
+  .sm\:hover\:translate-y-64:hover {
+    --transform-translate-y: 16rem;
+  }
+
+  .sm\:hover\:translate-y-px:hover {
+    --transform-translate-y: 1px;
+  }
+
+  .sm\:hover\:-translate-y-1:hover {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .sm\:hover\:-translate-y-2:hover {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .sm\:hover\:-translate-y-3:hover {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .sm\:hover\:-translate-y-4:hover {
+    --transform-translate-y: -1rem;
+  }
+
+  .sm\:hover\:-translate-y-5:hover {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .sm\:hover\:-translate-y-6:hover {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .sm\:hover\:-translate-y-8:hover {
+    --transform-translate-y: -2rem;
+  }
+
+  .sm\:hover\:-translate-y-10:hover {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .sm\:hover\:-translate-y-12:hover {
+    --transform-translate-y: -3rem;
+  }
+
+  .sm\:hover\:-translate-y-16:hover {
+    --transform-translate-y: -4rem;
+  }
+
+  .sm\:hover\:-translate-y-20:hover {
+    --transform-translate-y: -5rem;
+  }
+
+  .sm\:hover\:-translate-y-24:hover {
+    --transform-translate-y: -6rem;
+  }
+
+  .sm\:hover\:-translate-y-32:hover {
+    --transform-translate-y: -8rem;
+  }
+
+  .sm\:hover\:-translate-y-40:hover {
+    --transform-translate-y: -10rem;
+  }
+
+  .sm\:hover\:-translate-y-48:hover {
+    --transform-translate-y: -12rem;
+  }
+
+  .sm\:hover\:-translate-y-56:hover {
+    --transform-translate-y: -14rem;
+  }
+
+  .sm\:hover\:-translate-y-64:hover {
+    --transform-translate-y: -16rem;
+  }
+
+  .sm\:hover\:-translate-y-px:hover {
+    --transform-translate-y: -1px;
+  }
+
+  .sm\:hover\:-translate-y-full:hover {
+    --transform-translate-y: -100%;
+  }
+
+  .sm\:hover\:-translate-y-1\/2:hover {
+    --transform-translate-y: -50%;
+  }
+
+  .sm\:hover\:translate-y-1\/2:hover {
+    --transform-translate-y: 50%;
+  }
+
+  .sm\:hover\:translate-y-full:hover {
+    --transform-translate-y: 100%;
+  }
+
+  .sm\:focus\:translate-x-0:focus {
+    --transform-translate-x: 0;
+  }
+
+  .sm\:focus\:translate-x-1:focus {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .sm\:focus\:translate-x-2:focus {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .sm\:focus\:translate-x-3:focus {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .sm\:focus\:translate-x-4:focus {
+    --transform-translate-x: 1rem;
+  }
+
+  .sm\:focus\:translate-x-5:focus {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .sm\:focus\:translate-x-6:focus {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .sm\:focus\:translate-x-8:focus {
+    --transform-translate-x: 2rem;
+  }
+
+  .sm\:focus\:translate-x-10:focus {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .sm\:focus\:translate-x-12:focus {
+    --transform-translate-x: 3rem;
+  }
+
+  .sm\:focus\:translate-x-16:focus {
+    --transform-translate-x: 4rem;
+  }
+
+  .sm\:focus\:translate-x-20:focus {
+    --transform-translate-x: 5rem;
+  }
+
+  .sm\:focus\:translate-x-24:focus {
+    --transform-translate-x: 6rem;
+  }
+
+  .sm\:focus\:translate-x-32:focus {
+    --transform-translate-x: 8rem;
+  }
+
+  .sm\:focus\:translate-x-40:focus {
+    --transform-translate-x: 10rem;
+  }
+
+  .sm\:focus\:translate-x-48:focus {
+    --transform-translate-x: 12rem;
+  }
+
+  .sm\:focus\:translate-x-56:focus {
+    --transform-translate-x: 14rem;
+  }
+
+  .sm\:focus\:translate-x-64:focus {
+    --transform-translate-x: 16rem;
+  }
+
+  .sm\:focus\:translate-x-px:focus {
+    --transform-translate-x: 1px;
+  }
+
+  .sm\:focus\:-translate-x-1:focus {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .sm\:focus\:-translate-x-2:focus {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .sm\:focus\:-translate-x-3:focus {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .sm\:focus\:-translate-x-4:focus {
+    --transform-translate-x: -1rem;
+  }
+
+  .sm\:focus\:-translate-x-5:focus {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .sm\:focus\:-translate-x-6:focus {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .sm\:focus\:-translate-x-8:focus {
+    --transform-translate-x: -2rem;
+  }
+
+  .sm\:focus\:-translate-x-10:focus {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .sm\:focus\:-translate-x-12:focus {
+    --transform-translate-x: -3rem;
+  }
+
+  .sm\:focus\:-translate-x-16:focus {
+    --transform-translate-x: -4rem;
+  }
+
+  .sm\:focus\:-translate-x-20:focus {
+    --transform-translate-x: -5rem;
+  }
+
+  .sm\:focus\:-translate-x-24:focus {
+    --transform-translate-x: -6rem;
+  }
+
+  .sm\:focus\:-translate-x-32:focus {
+    --transform-translate-x: -8rem;
+  }
+
+  .sm\:focus\:-translate-x-40:focus {
+    --transform-translate-x: -10rem;
+  }
+
+  .sm\:focus\:-translate-x-48:focus {
+    --transform-translate-x: -12rem;
+  }
+
+  .sm\:focus\:-translate-x-56:focus {
+    --transform-translate-x: -14rem;
+  }
+
+  .sm\:focus\:-translate-x-64:focus {
+    --transform-translate-x: -16rem;
+  }
+
+  .sm\:focus\:-translate-x-px:focus {
+    --transform-translate-x: -1px;
+  }
+
+  .sm\:focus\:-translate-x-full:focus {
+    --transform-translate-x: -100%;
+  }
+
+  .sm\:focus\:-translate-x-1\/2:focus {
+    --transform-translate-x: -50%;
+  }
+
+  .sm\:focus\:translate-x-1\/2:focus {
+    --transform-translate-x: 50%;
+  }
+
+  .sm\:focus\:translate-x-full:focus {
+    --transform-translate-x: 100%;
+  }
+
+  .sm\:focus\:translate-y-0:focus {
+    --transform-translate-y: 0;
+  }
+
+  .sm\:focus\:translate-y-1:focus {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .sm\:focus\:translate-y-2:focus {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .sm\:focus\:translate-y-3:focus {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .sm\:focus\:translate-y-4:focus {
+    --transform-translate-y: 1rem;
+  }
+
+  .sm\:focus\:translate-y-5:focus {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .sm\:focus\:translate-y-6:focus {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .sm\:focus\:translate-y-8:focus {
+    --transform-translate-y: 2rem;
+  }
+
+  .sm\:focus\:translate-y-10:focus {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .sm\:focus\:translate-y-12:focus {
+    --transform-translate-y: 3rem;
+  }
+
+  .sm\:focus\:translate-y-16:focus {
+    --transform-translate-y: 4rem;
+  }
+
+  .sm\:focus\:translate-y-20:focus {
+    --transform-translate-y: 5rem;
+  }
+
+  .sm\:focus\:translate-y-24:focus {
+    --transform-translate-y: 6rem;
+  }
+
+  .sm\:focus\:translate-y-32:focus {
+    --transform-translate-y: 8rem;
+  }
+
+  .sm\:focus\:translate-y-40:focus {
+    --transform-translate-y: 10rem;
+  }
+
+  .sm\:focus\:translate-y-48:focus {
+    --transform-translate-y: 12rem;
+  }
+
+  .sm\:focus\:translate-y-56:focus {
+    --transform-translate-y: 14rem;
+  }
+
+  .sm\:focus\:translate-y-64:focus {
+    --transform-translate-y: 16rem;
+  }
+
+  .sm\:focus\:translate-y-px:focus {
+    --transform-translate-y: 1px;
+  }
+
+  .sm\:focus\:-translate-y-1:focus {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .sm\:focus\:-translate-y-2:focus {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .sm\:focus\:-translate-y-3:focus {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .sm\:focus\:-translate-y-4:focus {
+    --transform-translate-y: -1rem;
+  }
+
+  .sm\:focus\:-translate-y-5:focus {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .sm\:focus\:-translate-y-6:focus {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .sm\:focus\:-translate-y-8:focus {
+    --transform-translate-y: -2rem;
+  }
+
+  .sm\:focus\:-translate-y-10:focus {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .sm\:focus\:-translate-y-12:focus {
+    --transform-translate-y: -3rem;
+  }
+
+  .sm\:focus\:-translate-y-16:focus {
+    --transform-translate-y: -4rem;
+  }
+
+  .sm\:focus\:-translate-y-20:focus {
+    --transform-translate-y: -5rem;
+  }
+
+  .sm\:focus\:-translate-y-24:focus {
+    --transform-translate-y: -6rem;
+  }
+
+  .sm\:focus\:-translate-y-32:focus {
+    --transform-translate-y: -8rem;
+  }
+
+  .sm\:focus\:-translate-y-40:focus {
+    --transform-translate-y: -10rem;
+  }
+
+  .sm\:focus\:-translate-y-48:focus {
+    --transform-translate-y: -12rem;
+  }
+
+  .sm\:focus\:-translate-y-56:focus {
+    --transform-translate-y: -14rem;
+  }
+
+  .sm\:focus\:-translate-y-64:focus {
+    --transform-translate-y: -16rem;
+  }
+
+  .sm\:focus\:-translate-y-px:focus {
+    --transform-translate-y: -1px;
+  }
+
+  .sm\:focus\:-translate-y-full:focus {
+    --transform-translate-y: -100%;
+  }
+
+  .sm\:focus\:-translate-y-1\/2:focus {
+    --transform-translate-y: -50%;
+  }
+
+  .sm\:focus\:translate-y-1\/2:focus {
+    --transform-translate-y: 50%;
+  }
+
+  .sm\:focus\:translate-y-full:focus {
+    --transform-translate-y: 100%;
+  }
+
+  .sm\:skew-x-0 {
+    --transform-skew-x: 0;
+  }
+
+  .sm\:skew-x-3 {
+    --transform-skew-x: 3deg;
+  }
+
+  .sm\:skew-x-6 {
+    --transform-skew-x: 6deg;
+  }
+
+  .sm\:skew-x-12 {
+    --transform-skew-x: 12deg;
+  }
+
+  .sm\:-skew-x-12 {
+    --transform-skew-x: -12deg;
+  }
+
+  .sm\:-skew-x-6 {
+    --transform-skew-x: -6deg;
+  }
+
+  .sm\:-skew-x-3 {
+    --transform-skew-x: -3deg;
+  }
+
+  .sm\:skew-y-0 {
+    --transform-skew-y: 0;
+  }
+
+  .sm\:skew-y-3 {
+    --transform-skew-y: 3deg;
+  }
+
+  .sm\:skew-y-6 {
+    --transform-skew-y: 6deg;
+  }
+
+  .sm\:skew-y-12 {
+    --transform-skew-y: 12deg;
+  }
+
+  .sm\:-skew-y-12 {
+    --transform-skew-y: -12deg;
+  }
+
+  .sm\:-skew-y-6 {
+    --transform-skew-y: -6deg;
+  }
+
+  .sm\:-skew-y-3 {
+    --transform-skew-y: -3deg;
+  }
+
+  .sm\:hover\:skew-x-0:hover {
+    --transform-skew-x: 0;
+  }
+
+  .sm\:hover\:skew-x-3:hover {
+    --transform-skew-x: 3deg;
+  }
+
+  .sm\:hover\:skew-x-6:hover {
+    --transform-skew-x: 6deg;
+  }
+
+  .sm\:hover\:skew-x-12:hover {
+    --transform-skew-x: 12deg;
+  }
+
+  .sm\:hover\:-skew-x-12:hover {
+    --transform-skew-x: -12deg;
+  }
+
+  .sm\:hover\:-skew-x-6:hover {
+    --transform-skew-x: -6deg;
+  }
+
+  .sm\:hover\:-skew-x-3:hover {
+    --transform-skew-x: -3deg;
+  }
+
+  .sm\:hover\:skew-y-0:hover {
+    --transform-skew-y: 0;
+  }
+
+  .sm\:hover\:skew-y-3:hover {
+    --transform-skew-y: 3deg;
+  }
+
+  .sm\:hover\:skew-y-6:hover {
+    --transform-skew-y: 6deg;
+  }
+
+  .sm\:hover\:skew-y-12:hover {
+    --transform-skew-y: 12deg;
+  }
+
+  .sm\:hover\:-skew-y-12:hover {
+    --transform-skew-y: -12deg;
+  }
+
+  .sm\:hover\:-skew-y-6:hover {
+    --transform-skew-y: -6deg;
+  }
+
+  .sm\:hover\:-skew-y-3:hover {
+    --transform-skew-y: -3deg;
+  }
+
+  .sm\:focus\:skew-x-0:focus {
+    --transform-skew-x: 0;
+  }
+
+  .sm\:focus\:skew-x-3:focus {
+    --transform-skew-x: 3deg;
+  }
+
+  .sm\:focus\:skew-x-6:focus {
+    --transform-skew-x: 6deg;
+  }
+
+  .sm\:focus\:skew-x-12:focus {
+    --transform-skew-x: 12deg;
+  }
+
+  .sm\:focus\:-skew-x-12:focus {
+    --transform-skew-x: -12deg;
+  }
+
+  .sm\:focus\:-skew-x-6:focus {
+    --transform-skew-x: -6deg;
+  }
+
+  .sm\:focus\:-skew-x-3:focus {
+    --transform-skew-x: -3deg;
+  }
+
+  .sm\:focus\:skew-y-0:focus {
+    --transform-skew-y: 0;
+  }
+
+  .sm\:focus\:skew-y-3:focus {
+    --transform-skew-y: 3deg;
+  }
+
+  .sm\:focus\:skew-y-6:focus {
+    --transform-skew-y: 6deg;
+  }
+
+  .sm\:focus\:skew-y-12:focus {
+    --transform-skew-y: 12deg;
+  }
+
+  .sm\:focus\:-skew-y-12:focus {
+    --transform-skew-y: -12deg;
+  }
+
+  .sm\:focus\:-skew-y-6:focus {
+    --transform-skew-y: -6deg;
+  }
+
+  .sm\:focus\:-skew-y-3:focus {
+    --transform-skew-y: -3deg;
+  }
+
+  .sm\:transition-none {
+    transition-property: none;
+  }
+
+  .sm\:transition-all {
+    transition-property: all;
+  }
+
+  .sm\:transition {
+    transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+  }
+
+  .sm\:transition-colors {
+    transition-property: background-color, border-color, color, fill, stroke;
+  }
+
+  .sm\:transition-opacity {
+    transition-property: opacity;
+  }
+
+  .sm\:transition-shadow {
+    transition-property: box-shadow;
+  }
+
+  .sm\:transition-transform {
+    transition-property: transform;
+  }
+
+  .sm\:ease-linear {
+    transition-timing-function: linear;
+  }
+
+  .sm\:ease-in {
+    transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+  }
+
+  .sm\:ease-out {
+    transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+  }
+
+  .sm\:ease-in-out {
+    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .sm\:duration-75 {
+    transition-duration: 75ms;
+  }
+
+  .sm\:duration-100 {
+    transition-duration: 100ms;
+  }
+
+  .sm\:duration-150 {
+    transition-duration: 150ms;
+  }
+
+  .sm\:duration-200 {
+    transition-duration: 200ms;
+  }
+
+  .sm\:duration-300 {
+    transition-duration: 300ms;
+  }
+
+  .sm\:duration-500 {
+    transition-duration: 500ms;
+  }
+
+  .sm\:duration-700 {
+    transition-duration: 700ms;
+  }
+
+  .sm\:duration-1000 {
+    transition-duration: 1000ms;
+  }
+
+  .sm\:delay-75 {
+    transition-delay: 75ms;
+  }
+
+  .sm\:delay-100 {
+    transition-delay: 100ms;
+  }
+
+  .sm\:delay-150 {
+    transition-delay: 150ms;
+  }
+
+  .sm\:delay-200 {
+    transition-delay: 200ms;
+  }
+
+  .sm\:delay-300 {
+    transition-delay: 300ms;
+  }
+
+  .sm\:delay-500 {
+    transition-delay: 500ms;
+  }
+
+  .sm\:delay-700 {
+    transition-delay: 700ms;
+  }
+
+  .sm\:delay-1000 {
+    transition-delay: 1000ms;
+  }
+
+  .sm\:animate-none {
+    -webkit-animation: none;
+            animation: none;
+  }
+
+  .sm\:animate-spin {
+    -webkit-animation: spin 1s linear infinite;
+            animation: spin 1s linear infinite;
+  }
+
+  .sm\:animate-ping {
+    -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+            animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+  }
+
+  .sm\:animate-pulse {
+    -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+            animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+  }
+
+  .sm\:animate-bounce {
+    -webkit-animation: bounce 1s infinite;
+            animation: bounce 1s infinite;
+  }
+}
+
+@media (min-width: 768px) {
+  .md\:container {
+    width: 100%;
+  }
+
+  @media (min-width: 640px) {
+    .md\:container {
+      max-width: 640px;
+    }
+  }
+
+  @media (min-width: 768px) {
+    .md\:container {
+      max-width: 768px;
+    }
+  }
+
+  @media (min-width: 1024px) {
+    .md\:container {
+      max-width: 1024px;
+    }
+  }
+
+  @media (min-width: 1280px) {
+    .md\:container {
+      max-width: 1280px;
+    }
+  }
+
+  .md\:space-y-0 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0px * var(--space-y-reverse));
+  }
+
+  .md\:space-x-0 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0px * var(--space-x-reverse));
+    margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.25rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.25rem * var(--space-x-reverse));
+    margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.5rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.5rem * var(--space-x-reverse));
+    margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.75rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.75rem * var(--space-x-reverse));
+    margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1rem * var(--space-x-reverse));
+    margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.25rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.25rem * var(--space-x-reverse));
+    margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.5rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.5rem * var(--space-x-reverse));
+    margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2rem * var(--space-x-reverse));
+    margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2.5rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2.5rem * var(--space-x-reverse));
+    margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(3rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(3rem * var(--space-x-reverse));
+    margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(4rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(4rem * var(--space-x-reverse));
+    margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(5rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(5rem * var(--space-x-reverse));
+    margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(6rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(6rem * var(--space-x-reverse));
+    margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(8rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(8rem * var(--space-x-reverse));
+    margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(10rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(10rem * var(--space-x-reverse));
+    margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(12rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(12rem * var(--space-x-reverse));
+    margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(14rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(14rem * var(--space-x-reverse));
+    margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(16rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(16rem * var(--space-x-reverse));
+    margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1px * var(--space-y-reverse));
+  }
+
+  .md\:space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1px * var(--space-x-reverse));
+    margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.25rem * var(--space-x-reverse));
+    margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.5rem * var(--space-x-reverse));
+    margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.75rem * var(--space-x-reverse));
+    margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1rem * var(--space-x-reverse));
+    margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.25rem * var(--space-x-reverse));
+    margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.5rem * var(--space-x-reverse));
+    margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2rem * var(--space-x-reverse));
+    margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2.5rem * var(--space-x-reverse));
+    margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-3rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-3rem * var(--space-x-reverse));
+    margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-4rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-4rem * var(--space-x-reverse));
+    margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-5rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-5rem * var(--space-x-reverse));
+    margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-6rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-6rem * var(--space-x-reverse));
+    margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-8rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-8rem * var(--space-x-reverse));
+    margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-10rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-10rem * var(--space-x-reverse));
+    margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-12rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-12rem * var(--space-x-reverse));
+    margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-14rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-14rem * var(--space-x-reverse));
+    margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-16rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-16rem * var(--space-x-reverse));
+    margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1px * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1px * var(--space-x-reverse));
+    margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-reverse > :not(template) ~ :not(template) {
+    --space-y-reverse: 1;
+  }
+
+  .md\:space-x-reverse > :not(template) ~ :not(template) {
+    --space-x-reverse: 1;
+  }
+
+  .md\:divide-y-0 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(0px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x-0 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(0px * var(--divide-x-reverse));
+    border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y-2 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(2px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x-2 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(2px * var(--divide-x-reverse));
+    border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y-4 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(4px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x-4 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(4px * var(--divide-x-reverse));
+    border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y-8 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(8px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x-8 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(8px * var(--divide-x-reverse));
+    border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(1px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(1px * var(--divide-x-reverse));
+    border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y-reverse > :not(template) ~ :not(template) {
+    --divide-y-reverse: 1;
+  }
+
+  .md\:divide-x-reverse > :not(template) ~ :not(template) {
+    --divide-x-reverse: 1;
+  }
+
+  .md\:divide-transparent > :not(template) ~ :not(template) {
+    border-color: transparent;
+  }
+
+  .md\:divide-current > :not(template) ~ :not(template) {
+    border-color: currentColor;
+  }
+
+  .md\:divide-black > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--divide-opacity));
+  }
+
+  .md\:divide-white > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--divide-opacity));
+  }
+
+  .md\:divide-red-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--divide-opacity));
+  }
+
+  .md\:divide-red-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--divide-opacity));
+  }
+
+  .md\:divide-red-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--divide-opacity));
+  }
+
+  .md\:divide-red-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--divide-opacity));
+  }
+
+  .md\:divide-red-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--divide-opacity));
+  }
+
+  .md\:divide-red-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--divide-opacity));
+  }
+
+  .md\:divide-red-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--divide-opacity));
+  }
+
+  .md\:divide-red-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--divide-opacity));
+  }
+
+  .md\:divide-red-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--divide-opacity));
+  }
+
+  .md\:divide-green-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--divide-opacity));
+  }
+
+  .md\:divide-green-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--divide-opacity));
+  }
+
+  .md\:divide-green-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--divide-opacity));
+  }
+
+  .md\:divide-green-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--divide-opacity));
+  }
+
+  .md\:divide-green-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--divide-opacity));
+  }
+
+  .md\:divide-green-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--divide-opacity));
+  }
+
+  .md\:divide-green-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--divide-opacity));
+  }
+
+  .md\:divide-green-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--divide-opacity));
+  }
+
+  .md\:divide-green-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--divide-opacity));
+  }
+
+  .md\:divide-solid > :not(template) ~ :not(template) {
+    border-style: solid;
+  }
+
+  .md\:divide-dashed > :not(template) ~ :not(template) {
+    border-style: dashed;
+  }
+
+  .md\:divide-dotted > :not(template) ~ :not(template) {
+    border-style: dotted;
+  }
+
+  .md\:divide-double > :not(template) ~ :not(template) {
+    border-style: double;
+  }
+
+  .md\:divide-none > :not(template) ~ :not(template) {
+    border-style: none;
+  }
+
+  .md\:divide-opacity-0 > :not(template) ~ :not(template) {
+    --divide-opacity: 0;
+  }
+
+  .md\:divide-opacity-25 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.25;
+  }
+
+  .md\:divide-opacity-50 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.5;
+  }
+
+  .md\:divide-opacity-75 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.75;
+  }
+
+  .md\:divide-opacity-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+  }
+
+  .md\:sr-only {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .md\:not-sr-only {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .md\:focus\:sr-only:focus {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .md\:focus\:not-sr-only:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .md\:appearance-none {
+    -webkit-appearance: none;
+       -moz-appearance: none;
+            appearance: none;
+  }
+
+  .md\:bg-fixed {
+    background-attachment: fixed;
+  }
+
+  .md\:bg-local {
+    background-attachment: local;
+  }
+
+  .md\:bg-scroll {
+    background-attachment: scroll;
+  }
+
+  .md\:bg-clip-border {
+    background-clip: border-box;
+  }
+
+  .md\:bg-clip-padding {
+    background-clip: padding-box;
+  }
+
+  .md\:bg-clip-content {
+    background-clip: content-box;
+  }
+
+  .md\:bg-clip-text {
+    -webkit-background-clip: text;
+            background-clip: text;
+  }
+
+  .md\:bg-transparent {
+    background-color: transparent;
+  }
+
+  .md\:bg-current {
+    background-color: currentColor;
+  }
+
+  .md\:bg-black {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .md\:bg-white {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-100 {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-200 {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-300 {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-400 {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-500 {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-600 {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-700 {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-800 {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-900 {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .md\:bg-red-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .md\:bg-red-200 {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .md\:bg-red-300 {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .md\:bg-red-400 {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .md\:bg-red-500 {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .md\:bg-red-600 {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .md\:bg-red-700 {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .md\:bg-red-800 {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .md\:bg-red-900 {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-100 {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-200 {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-300 {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-400 {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-500 {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-600 {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-700 {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-800 {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-900 {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-100 {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-200 {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-300 {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-400 {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-500 {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-600 {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-700 {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-800 {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-900 {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .md\:bg-green-100 {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .md\:bg-green-200 {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .md\:bg-green-300 {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .md\:bg-green-400 {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .md\:bg-green-500 {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .md\:bg-green-600 {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .md\:bg-green-700 {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .md\:bg-green-800 {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .md\:bg-green-900 {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-100 {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-200 {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-300 {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-400 {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-500 {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-600 {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-700 {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-800 {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-900 {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-100 {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-200 {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-300 {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-400 {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-500 {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-600 {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-700 {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-800 {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-900 {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-100 {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-200 {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-300 {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-400 {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-500 {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-600 {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-700 {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-800 {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-900 {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-100 {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-200 {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-300 {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-400 {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-500 {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-600 {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-700 {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-800 {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-900 {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-200 {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-300 {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-400 {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-500 {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-600 {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-700 {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-800 {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-900 {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-transparent:hover {
+    background-color: transparent;
+  }
+
+  .md\:hover\:bg-current:hover {
+    background-color: currentColor;
+  }
+
+  .md\:hover\:bg-black:hover {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-white:hover {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-100:hover {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-200:hover {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-300:hover {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-400:hover {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-500:hover {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-600:hover {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-700:hover {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-800:hover {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-900:hover {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-300:hover {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-400:hover {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-500:hover {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-600:hover {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-700:hover {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-800:hover {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-900:hover {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-200:hover {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-600:hover {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-700:hover {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-800:hover {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-900:hover {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-200:hover {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-300:hover {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-500:hover {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-600:hover {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-700:hover {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-800:hover {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-900:hover {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-100:hover {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-200:hover {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-300:hover {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-400:hover {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-500:hover {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-600:hover {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-700:hover {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-800:hover {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-900:hover {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-100:hover {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-200:hover {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-300:hover {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-400:hover {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-500:hover {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-600:hover {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-700:hover {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-800:hover {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-900:hover {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-200:hover {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-300:hover {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-400:hover {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-500:hover {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-600:hover {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-700:hover {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-800:hover {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-900:hover {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-200:hover {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-300:hover {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-400:hover {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-500:hover {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-600:hover {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-700:hover {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-800:hover {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-900:hover {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-100:hover {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-200:hover {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-300:hover {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-400:hover {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-500:hover {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-600:hover {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-700:hover {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-800:hover {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-900:hover {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-400:hover {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-600:hover {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-700:hover {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-800:hover {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-900:hover {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-transparent:focus {
+    background-color: transparent;
+  }
+
+  .md\:focus\:bg-current:focus {
+    background-color: currentColor;
+  }
+
+  .md\:focus\:bg-black:focus {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-white:focus {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-100:focus {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-200:focus {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-300:focus {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-400:focus {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-500:focus {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-600:focus {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-700:focus {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-800:focus {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-900:focus {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-300:focus {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-400:focus {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-500:focus {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-600:focus {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-700:focus {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-800:focus {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-900:focus {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-200:focus {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-600:focus {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-700:focus {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-800:focus {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-900:focus {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-200:focus {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-300:focus {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-500:focus {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-600:focus {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-700:focus {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-800:focus {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-900:focus {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-100:focus {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-200:focus {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-300:focus {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-400:focus {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-500:focus {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-600:focus {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-700:focus {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-800:focus {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-900:focus {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-100:focus {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-200:focus {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-300:focus {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-400:focus {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-500:focus {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-600:focus {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-700:focus {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-800:focus {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-900:focus {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-200:focus {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-300:focus {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-400:focus {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-500:focus {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-600:focus {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-700:focus {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-800:focus {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-900:focus {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-200:focus {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-300:focus {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-400:focus {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-500:focus {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-600:focus {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-700:focus {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-800:focus {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-900:focus {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-100:focus {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-200:focus {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-300:focus {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-400:focus {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-500:focus {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-600:focus {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-700:focus {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-800:focus {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-900:focus {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-400:focus {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-600:focus {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-700:focus {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-800:focus {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-900:focus {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .md\:bg-none {
+    background-image: none;
+  }
+
+  .md\:bg-gradient-to-t {
+    background-image: linear-gradient(to top, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-tr {
+    background-image: linear-gradient(to top right, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-r {
+    background-image: linear-gradient(to right, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-br {
+    background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-b {
+    background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-bl {
+    background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-l {
+    background-image: linear-gradient(to left, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-tl {
+    background-image: linear-gradient(to top left, var(--gradient-color-stops));
+  }
+
+  .md\:from-transparent {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:from-current {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:from-black {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:from-white {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:from-gray-100 {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:from-gray-200 {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:from-gray-300 {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:from-gray-400 {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:from-gray-500 {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:from-gray-600 {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:from-gray-700 {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:from-gray-800 {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:from-gray-900 {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:from-red-100 {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:from-red-200 {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:from-red-300 {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:from-red-400 {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:from-red-500 {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:from-red-600 {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:from-red-700 {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:from-red-800 {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:from-red-900 {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:from-orange-100 {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:from-orange-200 {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:from-orange-300 {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:from-orange-400 {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:from-orange-500 {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:from-orange-600 {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:from-orange-700 {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:from-orange-800 {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:from-orange-900 {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:from-yellow-100 {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:from-yellow-200 {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:from-yellow-300 {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:from-yellow-400 {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:from-yellow-500 {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:from-yellow-600 {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:from-yellow-700 {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:from-yellow-800 {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:from-yellow-900 {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:from-green-100 {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:from-green-200 {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:from-green-300 {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:from-green-400 {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:from-green-500 {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:from-green-600 {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:from-green-700 {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:from-green-800 {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:from-green-900 {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:from-teal-100 {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:from-teal-200 {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:from-teal-300 {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:from-teal-400 {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:from-teal-500 {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:from-teal-600 {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:from-teal-700 {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:from-teal-800 {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:from-teal-900 {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:from-blue-100 {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:from-blue-200 {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:from-blue-300 {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:from-blue-400 {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:from-blue-500 {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:from-blue-600 {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:from-blue-700 {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:from-blue-800 {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:from-blue-900 {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:from-indigo-100 {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:from-indigo-200 {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:from-indigo-300 {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:from-indigo-400 {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:from-indigo-500 {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:from-indigo-600 {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:from-indigo-700 {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:from-indigo-800 {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:from-indigo-900 {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:from-purple-100 {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:from-purple-200 {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:from-purple-300 {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:from-purple-400 {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:from-purple-500 {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:from-purple-600 {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:from-purple-700 {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:from-purple-800 {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:from-purple-900 {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:from-pink-100 {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:from-pink-200 {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:from-pink-300 {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:from-pink-400 {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:from-pink-500 {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:from-pink-600 {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:from-pink-700 {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:from-pink-800 {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:from-pink-900 {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:via-transparent {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:via-current {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:via-black {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:via-white {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:via-gray-100 {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:via-gray-200 {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:via-gray-300 {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:via-gray-400 {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:via-gray-500 {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:via-gray-600 {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:via-gray-700 {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:via-gray-800 {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:via-gray-900 {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:via-red-100 {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:via-red-200 {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:via-red-300 {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:via-red-400 {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:via-red-500 {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:via-red-600 {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:via-red-700 {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:via-red-800 {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:via-red-900 {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:via-orange-100 {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:via-orange-200 {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:via-orange-300 {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:via-orange-400 {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:via-orange-500 {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:via-orange-600 {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:via-orange-700 {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:via-orange-800 {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:via-orange-900 {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:via-yellow-100 {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:via-yellow-200 {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:via-yellow-300 {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:via-yellow-400 {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:via-yellow-500 {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:via-yellow-600 {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:via-yellow-700 {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:via-yellow-800 {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:via-yellow-900 {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:via-green-100 {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:via-green-200 {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:via-green-300 {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:via-green-400 {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:via-green-500 {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:via-green-600 {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:via-green-700 {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:via-green-800 {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:via-green-900 {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:via-teal-100 {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:via-teal-200 {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:via-teal-300 {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:via-teal-400 {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:via-teal-500 {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:via-teal-600 {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:via-teal-700 {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:via-teal-800 {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:via-teal-900 {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:via-blue-100 {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:via-blue-200 {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:via-blue-300 {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:via-blue-400 {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:via-blue-500 {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:via-blue-600 {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:via-blue-700 {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:via-blue-800 {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:via-blue-900 {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:via-indigo-100 {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:via-indigo-200 {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:via-indigo-300 {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:via-indigo-400 {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:via-indigo-500 {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:via-indigo-600 {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:via-indigo-700 {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:via-indigo-800 {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:via-indigo-900 {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:via-purple-100 {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:via-purple-200 {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:via-purple-300 {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:via-purple-400 {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:via-purple-500 {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:via-purple-600 {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:via-purple-700 {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:via-purple-800 {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:via-purple-900 {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:via-pink-100 {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:via-pink-200 {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:via-pink-300 {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:via-pink-400 {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:via-pink-500 {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:via-pink-600 {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:via-pink-700 {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:via-pink-800 {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:via-pink-900 {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:to-transparent {
+    --gradient-to-color: transparent;
+  }
+
+  .md\:to-current {
+    --gradient-to-color: currentColor;
+  }
+
+  .md\:to-black {
+    --gradient-to-color: #000;
+  }
+
+  .md\:to-white {
+    --gradient-to-color: #fff;
+  }
+
+  .md\:to-gray-100 {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .md\:to-gray-200 {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .md\:to-gray-300 {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .md\:to-gray-400 {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .md\:to-gray-500 {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .md\:to-gray-600 {
+    --gradient-to-color: #718096;
+  }
+
+  .md\:to-gray-700 {
+    --gradient-to-color: #4a5568;
+  }
+
+  .md\:to-gray-800 {
+    --gradient-to-color: #2d3748;
+  }
+
+  .md\:to-gray-900 {
+    --gradient-to-color: #1a202c;
+  }
+
+  .md\:to-red-100 {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .md\:to-red-200 {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .md\:to-red-300 {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .md\:to-red-400 {
+    --gradient-to-color: #fc8181;
+  }
+
+  .md\:to-red-500 {
+    --gradient-to-color: #f56565;
+  }
+
+  .md\:to-red-600 {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .md\:to-red-700 {
+    --gradient-to-color: #c53030;
+  }
+
+  .md\:to-red-800 {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .md\:to-red-900 {
+    --gradient-to-color: #742a2a;
+  }
+
+  .md\:to-orange-100 {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .md\:to-orange-200 {
+    --gradient-to-color: #feebc8;
+  }
+
+  .md\:to-orange-300 {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .md\:to-orange-400 {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .md\:to-orange-500 {
+    --gradient-to-color: #ed8936;
+  }
+
+  .md\:to-orange-600 {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .md\:to-orange-700 {
+    --gradient-to-color: #c05621;
+  }
+
+  .md\:to-orange-800 {
+    --gradient-to-color: #9c4221;
+  }
+
+  .md\:to-orange-900 {
+    --gradient-to-color: #7b341e;
+  }
+
+  .md\:to-yellow-100 {
+    --gradient-to-color: #fffff0;
+  }
+
+  .md\:to-yellow-200 {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .md\:to-yellow-300 {
+    --gradient-to-color: #faf089;
+  }
+
+  .md\:to-yellow-400 {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .md\:to-yellow-500 {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .md\:to-yellow-600 {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .md\:to-yellow-700 {
+    --gradient-to-color: #b7791f;
+  }
+
+  .md\:to-yellow-800 {
+    --gradient-to-color: #975a16;
+  }
+
+  .md\:to-yellow-900 {
+    --gradient-to-color: #744210;
+  }
+
+  .md\:to-green-100 {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .md\:to-green-200 {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .md\:to-green-300 {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .md\:to-green-400 {
+    --gradient-to-color: #68d391;
+  }
+
+  .md\:to-green-500 {
+    --gradient-to-color: #48bb78;
+  }
+
+  .md\:to-green-600 {
+    --gradient-to-color: #38a169;
+  }
+
+  .md\:to-green-700 {
+    --gradient-to-color: #2f855a;
+  }
+
+  .md\:to-green-800 {
+    --gradient-to-color: #276749;
+  }
+
+  .md\:to-green-900 {
+    --gradient-to-color: #22543d;
+  }
+
+  .md\:to-teal-100 {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .md\:to-teal-200 {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .md\:to-teal-300 {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .md\:to-teal-400 {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .md\:to-teal-500 {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .md\:to-teal-600 {
+    --gradient-to-color: #319795;
+  }
+
+  .md\:to-teal-700 {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .md\:to-teal-800 {
+    --gradient-to-color: #285e61;
+  }
+
+  .md\:to-teal-900 {
+    --gradient-to-color: #234e52;
+  }
+
+  .md\:to-blue-100 {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .md\:to-blue-200 {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .md\:to-blue-300 {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .md\:to-blue-400 {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .md\:to-blue-500 {
+    --gradient-to-color: #4299e1;
+  }
+
+  .md\:to-blue-600 {
+    --gradient-to-color: #3182ce;
+  }
+
+  .md\:to-blue-700 {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .md\:to-blue-800 {
+    --gradient-to-color: #2c5282;
+  }
+
+  .md\:to-blue-900 {
+    --gradient-to-color: #2a4365;
+  }
+
+  .md\:to-indigo-100 {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .md\:to-indigo-200 {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .md\:to-indigo-300 {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .md\:to-indigo-400 {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .md\:to-indigo-500 {
+    --gradient-to-color: #667eea;
+  }
+
+  .md\:to-indigo-600 {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .md\:to-indigo-700 {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .md\:to-indigo-800 {
+    --gradient-to-color: #434190;
+  }
+
+  .md\:to-indigo-900 {
+    --gradient-to-color: #3c366b;
+  }
+
+  .md\:to-purple-100 {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .md\:to-purple-200 {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .md\:to-purple-300 {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .md\:to-purple-400 {
+    --gradient-to-color: #b794f4;
+  }
+
+  .md\:to-purple-500 {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .md\:to-purple-600 {
+    --gradient-to-color: #805ad5;
+  }
+
+  .md\:to-purple-700 {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .md\:to-purple-800 {
+    --gradient-to-color: #553c9a;
+  }
+
+  .md\:to-purple-900 {
+    --gradient-to-color: #44337a;
+  }
+
+  .md\:to-pink-100 {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .md\:to-pink-200 {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .md\:to-pink-300 {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .md\:to-pink-400 {
+    --gradient-to-color: #f687b3;
+  }
+
+  .md\:to-pink-500 {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .md\:to-pink-600 {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .md\:to-pink-700 {
+    --gradient-to-color: #b83280;
+  }
+
+  .md\:to-pink-800 {
+    --gradient-to-color: #97266d;
+  }
+
+  .md\:to-pink-900 {
+    --gradient-to-color: #702459;
+  }
+
+  .md\:hover\:from-transparent:hover {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:hover\:from-current:hover {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:hover\:from-black:hover {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:hover\:from-white:hover {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:hover\:from-gray-100:hover {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:hover\:from-gray-200:hover {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:hover\:from-gray-300:hover {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:hover\:from-gray-400:hover {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:hover\:from-gray-500:hover {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:hover\:from-gray-600:hover {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:hover\:from-gray-700:hover {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:hover\:from-gray-800:hover {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:hover\:from-gray-900:hover {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:hover\:from-red-100:hover {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:hover\:from-red-200:hover {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:hover\:from-red-300:hover {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:hover\:from-red-400:hover {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:hover\:from-red-500:hover {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:hover\:from-red-600:hover {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:hover\:from-red-700:hover {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:hover\:from-red-800:hover {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:hover\:from-red-900:hover {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:hover\:from-orange-100:hover {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:hover\:from-orange-200:hover {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:hover\:from-orange-300:hover {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:hover\:from-orange-400:hover {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:hover\:from-orange-500:hover {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:hover\:from-orange-600:hover {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:hover\:from-orange-700:hover {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:hover\:from-orange-800:hover {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:hover\:from-orange-900:hover {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:hover\:from-yellow-100:hover {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:hover\:from-yellow-200:hover {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:hover\:from-yellow-300:hover {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:hover\:from-yellow-400:hover {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:hover\:from-yellow-500:hover {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:hover\:from-yellow-600:hover {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:hover\:from-yellow-700:hover {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:hover\:from-yellow-800:hover {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:hover\:from-yellow-900:hover {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:hover\:from-green-100:hover {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:hover\:from-green-200:hover {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:hover\:from-green-300:hover {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:hover\:from-green-400:hover {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:hover\:from-green-500:hover {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:hover\:from-green-600:hover {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:hover\:from-green-700:hover {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:hover\:from-green-800:hover {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:hover\:from-green-900:hover {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:hover\:from-teal-100:hover {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:hover\:from-teal-200:hover {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:hover\:from-teal-300:hover {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:hover\:from-teal-400:hover {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:hover\:from-teal-500:hover {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:hover\:from-teal-600:hover {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:hover\:from-teal-700:hover {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:hover\:from-teal-800:hover {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:hover\:from-teal-900:hover {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:hover\:from-blue-100:hover {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:hover\:from-blue-200:hover {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:hover\:from-blue-300:hover {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:hover\:from-blue-400:hover {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:hover\:from-blue-500:hover {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:hover\:from-blue-600:hover {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:hover\:from-blue-700:hover {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:hover\:from-blue-800:hover {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:hover\:from-blue-900:hover {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:hover\:from-indigo-100:hover {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:hover\:from-indigo-200:hover {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:hover\:from-indigo-300:hover {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:hover\:from-indigo-400:hover {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:hover\:from-indigo-500:hover {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:hover\:from-indigo-600:hover {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:hover\:from-indigo-700:hover {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:hover\:from-indigo-800:hover {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:hover\:from-indigo-900:hover {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:hover\:from-purple-100:hover {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:hover\:from-purple-200:hover {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:hover\:from-purple-300:hover {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:hover\:from-purple-400:hover {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:hover\:from-purple-500:hover {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:hover\:from-purple-600:hover {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:hover\:from-purple-700:hover {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:hover\:from-purple-800:hover {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:hover\:from-purple-900:hover {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:hover\:from-pink-100:hover {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:hover\:from-pink-200:hover {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:hover\:from-pink-300:hover {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:hover\:from-pink-400:hover {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:hover\:from-pink-500:hover {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:hover\:from-pink-600:hover {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:hover\:from-pink-700:hover {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:hover\:from-pink-800:hover {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:hover\:from-pink-900:hover {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:hover\:via-transparent:hover {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:hover\:via-current:hover {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:hover\:via-black:hover {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:hover\:via-white:hover {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:hover\:via-gray-100:hover {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:hover\:via-gray-200:hover {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:hover\:via-gray-300:hover {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:hover\:via-gray-400:hover {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:hover\:via-gray-500:hover {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:hover\:via-gray-600:hover {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:hover\:via-gray-700:hover {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:hover\:via-gray-800:hover {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:hover\:via-gray-900:hover {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:hover\:via-red-100:hover {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:hover\:via-red-200:hover {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:hover\:via-red-300:hover {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:hover\:via-red-400:hover {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:hover\:via-red-500:hover {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:hover\:via-red-600:hover {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:hover\:via-red-700:hover {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:hover\:via-red-800:hover {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:hover\:via-red-900:hover {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:hover\:via-orange-100:hover {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:hover\:via-orange-200:hover {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:hover\:via-orange-300:hover {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:hover\:via-orange-400:hover {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:hover\:via-orange-500:hover {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:hover\:via-orange-600:hover {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:hover\:via-orange-700:hover {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:hover\:via-orange-800:hover {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:hover\:via-orange-900:hover {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:hover\:via-yellow-100:hover {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:hover\:via-yellow-200:hover {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:hover\:via-yellow-300:hover {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:hover\:via-yellow-400:hover {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:hover\:via-yellow-500:hover {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:hover\:via-yellow-600:hover {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:hover\:via-yellow-700:hover {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:hover\:via-yellow-800:hover {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:hover\:via-yellow-900:hover {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:hover\:via-green-100:hover {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:hover\:via-green-200:hover {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:hover\:via-green-300:hover {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:hover\:via-green-400:hover {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:hover\:via-green-500:hover {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:hover\:via-green-600:hover {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:hover\:via-green-700:hover {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:hover\:via-green-800:hover {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:hover\:via-green-900:hover {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:hover\:via-teal-100:hover {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:hover\:via-teal-200:hover {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:hover\:via-teal-300:hover {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:hover\:via-teal-400:hover {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:hover\:via-teal-500:hover {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:hover\:via-teal-600:hover {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:hover\:via-teal-700:hover {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:hover\:via-teal-800:hover {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:hover\:via-teal-900:hover {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:hover\:via-blue-100:hover {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:hover\:via-blue-200:hover {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:hover\:via-blue-300:hover {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:hover\:via-blue-400:hover {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:hover\:via-blue-500:hover {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:hover\:via-blue-600:hover {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:hover\:via-blue-700:hover {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:hover\:via-blue-800:hover {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:hover\:via-blue-900:hover {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:hover\:via-indigo-100:hover {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:hover\:via-indigo-200:hover {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:hover\:via-indigo-300:hover {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:hover\:via-indigo-400:hover {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:hover\:via-indigo-500:hover {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:hover\:via-indigo-600:hover {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:hover\:via-indigo-700:hover {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:hover\:via-indigo-800:hover {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:hover\:via-indigo-900:hover {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:hover\:via-purple-100:hover {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:hover\:via-purple-200:hover {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:hover\:via-purple-300:hover {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:hover\:via-purple-400:hover {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:hover\:via-purple-500:hover {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:hover\:via-purple-600:hover {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:hover\:via-purple-700:hover {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:hover\:via-purple-800:hover {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:hover\:via-purple-900:hover {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:hover\:via-pink-100:hover {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:hover\:via-pink-200:hover {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:hover\:via-pink-300:hover {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:hover\:via-pink-400:hover {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:hover\:via-pink-500:hover {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:hover\:via-pink-600:hover {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:hover\:via-pink-700:hover {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:hover\:via-pink-800:hover {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:hover\:via-pink-900:hover {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:hover\:to-transparent:hover {
+    --gradient-to-color: transparent;
+  }
+
+  .md\:hover\:to-current:hover {
+    --gradient-to-color: currentColor;
+  }
+
+  .md\:hover\:to-black:hover {
+    --gradient-to-color: #000;
+  }
+
+  .md\:hover\:to-white:hover {
+    --gradient-to-color: #fff;
+  }
+
+  .md\:hover\:to-gray-100:hover {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .md\:hover\:to-gray-200:hover {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .md\:hover\:to-gray-300:hover {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .md\:hover\:to-gray-400:hover {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .md\:hover\:to-gray-500:hover {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .md\:hover\:to-gray-600:hover {
+    --gradient-to-color: #718096;
+  }
+
+  .md\:hover\:to-gray-700:hover {
+    --gradient-to-color: #4a5568;
+  }
+
+  .md\:hover\:to-gray-800:hover {
+    --gradient-to-color: #2d3748;
+  }
+
+  .md\:hover\:to-gray-900:hover {
+    --gradient-to-color: #1a202c;
+  }
+
+  .md\:hover\:to-red-100:hover {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .md\:hover\:to-red-200:hover {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .md\:hover\:to-red-300:hover {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .md\:hover\:to-red-400:hover {
+    --gradient-to-color: #fc8181;
+  }
+
+  .md\:hover\:to-red-500:hover {
+    --gradient-to-color: #f56565;
+  }
+
+  .md\:hover\:to-red-600:hover {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .md\:hover\:to-red-700:hover {
+    --gradient-to-color: #c53030;
+  }
+
+  .md\:hover\:to-red-800:hover {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .md\:hover\:to-red-900:hover {
+    --gradient-to-color: #742a2a;
+  }
+
+  .md\:hover\:to-orange-100:hover {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .md\:hover\:to-orange-200:hover {
+    --gradient-to-color: #feebc8;
+  }
+
+  .md\:hover\:to-orange-300:hover {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .md\:hover\:to-orange-400:hover {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .md\:hover\:to-orange-500:hover {
+    --gradient-to-color: #ed8936;
+  }
+
+  .md\:hover\:to-orange-600:hover {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .md\:hover\:to-orange-700:hover {
+    --gradient-to-color: #c05621;
+  }
+
+  .md\:hover\:to-orange-800:hover {
+    --gradient-to-color: #9c4221;
+  }
+
+  .md\:hover\:to-orange-900:hover {
+    --gradient-to-color: #7b341e;
+  }
+
+  .md\:hover\:to-yellow-100:hover {
+    --gradient-to-color: #fffff0;
+  }
+
+  .md\:hover\:to-yellow-200:hover {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .md\:hover\:to-yellow-300:hover {
+    --gradient-to-color: #faf089;
+  }
+
+  .md\:hover\:to-yellow-400:hover {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .md\:hover\:to-yellow-500:hover {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .md\:hover\:to-yellow-600:hover {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .md\:hover\:to-yellow-700:hover {
+    --gradient-to-color: #b7791f;
+  }
+
+  .md\:hover\:to-yellow-800:hover {
+    --gradient-to-color: #975a16;
+  }
+
+  .md\:hover\:to-yellow-900:hover {
+    --gradient-to-color: #744210;
+  }
+
+  .md\:hover\:to-green-100:hover {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .md\:hover\:to-green-200:hover {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .md\:hover\:to-green-300:hover {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .md\:hover\:to-green-400:hover {
+    --gradient-to-color: #68d391;
+  }
+
+  .md\:hover\:to-green-500:hover {
+    --gradient-to-color: #48bb78;
+  }
+
+  .md\:hover\:to-green-600:hover {
+    --gradient-to-color: #38a169;
+  }
+
+  .md\:hover\:to-green-700:hover {
+    --gradient-to-color: #2f855a;
+  }
+
+  .md\:hover\:to-green-800:hover {
+    --gradient-to-color: #276749;
+  }
+
+  .md\:hover\:to-green-900:hover {
+    --gradient-to-color: #22543d;
+  }
+
+  .md\:hover\:to-teal-100:hover {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .md\:hover\:to-teal-200:hover {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .md\:hover\:to-teal-300:hover {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .md\:hover\:to-teal-400:hover {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .md\:hover\:to-teal-500:hover {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .md\:hover\:to-teal-600:hover {
+    --gradient-to-color: #319795;
+  }
+
+  .md\:hover\:to-teal-700:hover {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .md\:hover\:to-teal-800:hover {
+    --gradient-to-color: #285e61;
+  }
+
+  .md\:hover\:to-teal-900:hover {
+    --gradient-to-color: #234e52;
+  }
+
+  .md\:hover\:to-blue-100:hover {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .md\:hover\:to-blue-200:hover {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .md\:hover\:to-blue-300:hover {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .md\:hover\:to-blue-400:hover {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .md\:hover\:to-blue-500:hover {
+    --gradient-to-color: #4299e1;
+  }
+
+  .md\:hover\:to-blue-600:hover {
+    --gradient-to-color: #3182ce;
+  }
+
+  .md\:hover\:to-blue-700:hover {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .md\:hover\:to-blue-800:hover {
+    --gradient-to-color: #2c5282;
+  }
+
+  .md\:hover\:to-blue-900:hover {
+    --gradient-to-color: #2a4365;
+  }
+
+  .md\:hover\:to-indigo-100:hover {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .md\:hover\:to-indigo-200:hover {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .md\:hover\:to-indigo-300:hover {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .md\:hover\:to-indigo-400:hover {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .md\:hover\:to-indigo-500:hover {
+    --gradient-to-color: #667eea;
+  }
+
+  .md\:hover\:to-indigo-600:hover {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .md\:hover\:to-indigo-700:hover {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .md\:hover\:to-indigo-800:hover {
+    --gradient-to-color: #434190;
+  }
+
+  .md\:hover\:to-indigo-900:hover {
+    --gradient-to-color: #3c366b;
+  }
+
+  .md\:hover\:to-purple-100:hover {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .md\:hover\:to-purple-200:hover {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .md\:hover\:to-purple-300:hover {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .md\:hover\:to-purple-400:hover {
+    --gradient-to-color: #b794f4;
+  }
+
+  .md\:hover\:to-purple-500:hover {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .md\:hover\:to-purple-600:hover {
+    --gradient-to-color: #805ad5;
+  }
+
+  .md\:hover\:to-purple-700:hover {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .md\:hover\:to-purple-800:hover {
+    --gradient-to-color: #553c9a;
+  }
+
+  .md\:hover\:to-purple-900:hover {
+    --gradient-to-color: #44337a;
+  }
+
+  .md\:hover\:to-pink-100:hover {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .md\:hover\:to-pink-200:hover {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .md\:hover\:to-pink-300:hover {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .md\:hover\:to-pink-400:hover {
+    --gradient-to-color: #f687b3;
+  }
+
+  .md\:hover\:to-pink-500:hover {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .md\:hover\:to-pink-600:hover {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .md\:hover\:to-pink-700:hover {
+    --gradient-to-color: #b83280;
+  }
+
+  .md\:hover\:to-pink-800:hover {
+    --gradient-to-color: #97266d;
+  }
+
+  .md\:hover\:to-pink-900:hover {
+    --gradient-to-color: #702459;
+  }
+
+  .md\:focus\:from-transparent:focus {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:focus\:from-current:focus {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:focus\:from-black:focus {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:focus\:from-white:focus {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:focus\:from-gray-100:focus {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:focus\:from-gray-200:focus {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:focus\:from-gray-300:focus {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:focus\:from-gray-400:focus {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:focus\:from-gray-500:focus {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:focus\:from-gray-600:focus {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:focus\:from-gray-700:focus {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:focus\:from-gray-800:focus {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:focus\:from-gray-900:focus {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:focus\:from-red-100:focus {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:focus\:from-red-200:focus {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:focus\:from-red-300:focus {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:focus\:from-red-400:focus {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:focus\:from-red-500:focus {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:focus\:from-red-600:focus {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:focus\:from-red-700:focus {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:focus\:from-red-800:focus {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:focus\:from-red-900:focus {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:focus\:from-orange-100:focus {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:focus\:from-orange-200:focus {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:focus\:from-orange-300:focus {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:focus\:from-orange-400:focus {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:focus\:from-orange-500:focus {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:focus\:from-orange-600:focus {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:focus\:from-orange-700:focus {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:focus\:from-orange-800:focus {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:focus\:from-orange-900:focus {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:focus\:from-yellow-100:focus {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:focus\:from-yellow-200:focus {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:focus\:from-yellow-300:focus {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:focus\:from-yellow-400:focus {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:focus\:from-yellow-500:focus {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:focus\:from-yellow-600:focus {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:focus\:from-yellow-700:focus {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:focus\:from-yellow-800:focus {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:focus\:from-yellow-900:focus {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:focus\:from-green-100:focus {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:focus\:from-green-200:focus {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:focus\:from-green-300:focus {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:focus\:from-green-400:focus {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:focus\:from-green-500:focus {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:focus\:from-green-600:focus {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:focus\:from-green-700:focus {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:focus\:from-green-800:focus {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:focus\:from-green-900:focus {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:focus\:from-teal-100:focus {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:focus\:from-teal-200:focus {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:focus\:from-teal-300:focus {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:focus\:from-teal-400:focus {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:focus\:from-teal-500:focus {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:focus\:from-teal-600:focus {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:focus\:from-teal-700:focus {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:focus\:from-teal-800:focus {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:focus\:from-teal-900:focus {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:focus\:from-blue-100:focus {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:focus\:from-blue-200:focus {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:focus\:from-blue-300:focus {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:focus\:from-blue-400:focus {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:focus\:from-blue-500:focus {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:focus\:from-blue-600:focus {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:focus\:from-blue-700:focus {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:focus\:from-blue-800:focus {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:focus\:from-blue-900:focus {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:focus\:from-indigo-100:focus {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:focus\:from-indigo-200:focus {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:focus\:from-indigo-300:focus {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:focus\:from-indigo-400:focus {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:focus\:from-indigo-500:focus {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:focus\:from-indigo-600:focus {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:focus\:from-indigo-700:focus {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:focus\:from-indigo-800:focus {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:focus\:from-indigo-900:focus {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:focus\:from-purple-100:focus {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:focus\:from-purple-200:focus {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:focus\:from-purple-300:focus {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:focus\:from-purple-400:focus {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:focus\:from-purple-500:focus {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:focus\:from-purple-600:focus {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:focus\:from-purple-700:focus {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:focus\:from-purple-800:focus {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:focus\:from-purple-900:focus {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:focus\:from-pink-100:focus {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:focus\:from-pink-200:focus {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:focus\:from-pink-300:focus {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:focus\:from-pink-400:focus {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:focus\:from-pink-500:focus {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:focus\:from-pink-600:focus {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:focus\:from-pink-700:focus {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:focus\:from-pink-800:focus {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:focus\:from-pink-900:focus {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:focus\:via-transparent:focus {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:focus\:via-current:focus {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:focus\:via-black:focus {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:focus\:via-white:focus {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:focus\:via-gray-100:focus {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:focus\:via-gray-200:focus {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:focus\:via-gray-300:focus {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:focus\:via-gray-400:focus {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:focus\:via-gray-500:focus {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:focus\:via-gray-600:focus {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:focus\:via-gray-700:focus {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:focus\:via-gray-800:focus {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:focus\:via-gray-900:focus {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:focus\:via-red-100:focus {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:focus\:via-red-200:focus {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:focus\:via-red-300:focus {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:focus\:via-red-400:focus {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:focus\:via-red-500:focus {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:focus\:via-red-600:focus {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:focus\:via-red-700:focus {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:focus\:via-red-800:focus {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:focus\:via-red-900:focus {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:focus\:via-orange-100:focus {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:focus\:via-orange-200:focus {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:focus\:via-orange-300:focus {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:focus\:via-orange-400:focus {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:focus\:via-orange-500:focus {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:focus\:via-orange-600:focus {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:focus\:via-orange-700:focus {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:focus\:via-orange-800:focus {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:focus\:via-orange-900:focus {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:focus\:via-yellow-100:focus {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:focus\:via-yellow-200:focus {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:focus\:via-yellow-300:focus {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:focus\:via-yellow-400:focus {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:focus\:via-yellow-500:focus {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:focus\:via-yellow-600:focus {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:focus\:via-yellow-700:focus {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:focus\:via-yellow-800:focus {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:focus\:via-yellow-900:focus {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:focus\:via-green-100:focus {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:focus\:via-green-200:focus {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:focus\:via-green-300:focus {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:focus\:via-green-400:focus {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:focus\:via-green-500:focus {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:focus\:via-green-600:focus {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:focus\:via-green-700:focus {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:focus\:via-green-800:focus {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:focus\:via-green-900:focus {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:focus\:via-teal-100:focus {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:focus\:via-teal-200:focus {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:focus\:via-teal-300:focus {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:focus\:via-teal-400:focus {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:focus\:via-teal-500:focus {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:focus\:via-teal-600:focus {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:focus\:via-teal-700:focus {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:focus\:via-teal-800:focus {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:focus\:via-teal-900:focus {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:focus\:via-blue-100:focus {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:focus\:via-blue-200:focus {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:focus\:via-blue-300:focus {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:focus\:via-blue-400:focus {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:focus\:via-blue-500:focus {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:focus\:via-blue-600:focus {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:focus\:via-blue-700:focus {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:focus\:via-blue-800:focus {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:focus\:via-blue-900:focus {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:focus\:via-indigo-100:focus {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:focus\:via-indigo-200:focus {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:focus\:via-indigo-300:focus {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:focus\:via-indigo-400:focus {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:focus\:via-indigo-500:focus {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:focus\:via-indigo-600:focus {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:focus\:via-indigo-700:focus {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:focus\:via-indigo-800:focus {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:focus\:via-indigo-900:focus {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:focus\:via-purple-100:focus {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:focus\:via-purple-200:focus {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:focus\:via-purple-300:focus {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:focus\:via-purple-400:focus {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:focus\:via-purple-500:focus {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:focus\:via-purple-600:focus {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:focus\:via-purple-700:focus {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:focus\:via-purple-800:focus {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:focus\:via-purple-900:focus {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:focus\:via-pink-100:focus {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:focus\:via-pink-200:focus {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:focus\:via-pink-300:focus {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:focus\:via-pink-400:focus {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:focus\:via-pink-500:focus {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:focus\:via-pink-600:focus {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:focus\:via-pink-700:focus {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:focus\:via-pink-800:focus {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:focus\:via-pink-900:focus {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:focus\:to-transparent:focus {
+    --gradient-to-color: transparent;
+  }
+
+  .md\:focus\:to-current:focus {
+    --gradient-to-color: currentColor;
+  }
+
+  .md\:focus\:to-black:focus {
+    --gradient-to-color: #000;
+  }
+
+  .md\:focus\:to-white:focus {
+    --gradient-to-color: #fff;
+  }
+
+  .md\:focus\:to-gray-100:focus {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .md\:focus\:to-gray-200:focus {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .md\:focus\:to-gray-300:focus {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .md\:focus\:to-gray-400:focus {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .md\:focus\:to-gray-500:focus {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .md\:focus\:to-gray-600:focus {
+    --gradient-to-color: #718096;
+  }
+
+  .md\:focus\:to-gray-700:focus {
+    --gradient-to-color: #4a5568;
+  }
+
+  .md\:focus\:to-gray-800:focus {
+    --gradient-to-color: #2d3748;
+  }
+
+  .md\:focus\:to-gray-900:focus {
+    --gradient-to-color: #1a202c;
+  }
+
+  .md\:focus\:to-red-100:focus {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .md\:focus\:to-red-200:focus {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .md\:focus\:to-red-300:focus {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .md\:focus\:to-red-400:focus {
+    --gradient-to-color: #fc8181;
+  }
+
+  .md\:focus\:to-red-500:focus {
+    --gradient-to-color: #f56565;
+  }
+
+  .md\:focus\:to-red-600:focus {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .md\:focus\:to-red-700:focus {
+    --gradient-to-color: #c53030;
+  }
+
+  .md\:focus\:to-red-800:focus {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .md\:focus\:to-red-900:focus {
+    --gradient-to-color: #742a2a;
+  }
+
+  .md\:focus\:to-orange-100:focus {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .md\:focus\:to-orange-200:focus {
+    --gradient-to-color: #feebc8;
+  }
+
+  .md\:focus\:to-orange-300:focus {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .md\:focus\:to-orange-400:focus {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .md\:focus\:to-orange-500:focus {
+    --gradient-to-color: #ed8936;
+  }
+
+  .md\:focus\:to-orange-600:focus {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .md\:focus\:to-orange-700:focus {
+    --gradient-to-color: #c05621;
+  }
+
+  .md\:focus\:to-orange-800:focus {
+    --gradient-to-color: #9c4221;
+  }
+
+  .md\:focus\:to-orange-900:focus {
+    --gradient-to-color: #7b341e;
+  }
+
+  .md\:focus\:to-yellow-100:focus {
+    --gradient-to-color: #fffff0;
+  }
+
+  .md\:focus\:to-yellow-200:focus {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .md\:focus\:to-yellow-300:focus {
+    --gradient-to-color: #faf089;
+  }
+
+  .md\:focus\:to-yellow-400:focus {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .md\:focus\:to-yellow-500:focus {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .md\:focus\:to-yellow-600:focus {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .md\:focus\:to-yellow-700:focus {
+    --gradient-to-color: #b7791f;
+  }
+
+  .md\:focus\:to-yellow-800:focus {
+    --gradient-to-color: #975a16;
+  }
+
+  .md\:focus\:to-yellow-900:focus {
+    --gradient-to-color: #744210;
+  }
+
+  .md\:focus\:to-green-100:focus {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .md\:focus\:to-green-200:focus {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .md\:focus\:to-green-300:focus {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .md\:focus\:to-green-400:focus {
+    --gradient-to-color: #68d391;
+  }
+
+  .md\:focus\:to-green-500:focus {
+    --gradient-to-color: #48bb78;
+  }
+
+  .md\:focus\:to-green-600:focus {
+    --gradient-to-color: #38a169;
+  }
+
+  .md\:focus\:to-green-700:focus {
+    --gradient-to-color: #2f855a;
+  }
+
+  .md\:focus\:to-green-800:focus {
+    --gradient-to-color: #276749;
+  }
+
+  .md\:focus\:to-green-900:focus {
+    --gradient-to-color: #22543d;
+  }
+
+  .md\:focus\:to-teal-100:focus {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .md\:focus\:to-teal-200:focus {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .md\:focus\:to-teal-300:focus {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .md\:focus\:to-teal-400:focus {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .md\:focus\:to-teal-500:focus {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .md\:focus\:to-teal-600:focus {
+    --gradient-to-color: #319795;
+  }
+
+  .md\:focus\:to-teal-700:focus {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .md\:focus\:to-teal-800:focus {
+    --gradient-to-color: #285e61;
+  }
+
+  .md\:focus\:to-teal-900:focus {
+    --gradient-to-color: #234e52;
+  }
+
+  .md\:focus\:to-blue-100:focus {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .md\:focus\:to-blue-200:focus {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .md\:focus\:to-blue-300:focus {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .md\:focus\:to-blue-400:focus {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .md\:focus\:to-blue-500:focus {
+    --gradient-to-color: #4299e1;
+  }
+
+  .md\:focus\:to-blue-600:focus {
+    --gradient-to-color: #3182ce;
+  }
+
+  .md\:focus\:to-blue-700:focus {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .md\:focus\:to-blue-800:focus {
+    --gradient-to-color: #2c5282;
+  }
+
+  .md\:focus\:to-blue-900:focus {
+    --gradient-to-color: #2a4365;
+  }
+
+  .md\:focus\:to-indigo-100:focus {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .md\:focus\:to-indigo-200:focus {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .md\:focus\:to-indigo-300:focus {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .md\:focus\:to-indigo-400:focus {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .md\:focus\:to-indigo-500:focus {
+    --gradient-to-color: #667eea;
+  }
+
+  .md\:focus\:to-indigo-600:focus {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .md\:focus\:to-indigo-700:focus {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .md\:focus\:to-indigo-800:focus {
+    --gradient-to-color: #434190;
+  }
+
+  .md\:focus\:to-indigo-900:focus {
+    --gradient-to-color: #3c366b;
+  }
+
+  .md\:focus\:to-purple-100:focus {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .md\:focus\:to-purple-200:focus {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .md\:focus\:to-purple-300:focus {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .md\:focus\:to-purple-400:focus {
+    --gradient-to-color: #b794f4;
+  }
+
+  .md\:focus\:to-purple-500:focus {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .md\:focus\:to-purple-600:focus {
+    --gradient-to-color: #805ad5;
+  }
+
+  .md\:focus\:to-purple-700:focus {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .md\:focus\:to-purple-800:focus {
+    --gradient-to-color: #553c9a;
+  }
+
+  .md\:focus\:to-purple-900:focus {
+    --gradient-to-color: #44337a;
+  }
+
+  .md\:focus\:to-pink-100:focus {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .md\:focus\:to-pink-200:focus {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .md\:focus\:to-pink-300:focus {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .md\:focus\:to-pink-400:focus {
+    --gradient-to-color: #f687b3;
+  }
+
+  .md\:focus\:to-pink-500:focus {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .md\:focus\:to-pink-600:focus {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .md\:focus\:to-pink-700:focus {
+    --gradient-to-color: #b83280;
+  }
+
+  .md\:focus\:to-pink-800:focus {
+    --gradient-to-color: #97266d;
+  }
+
+  .md\:focus\:to-pink-900:focus {
+    --gradient-to-color: #702459;
+  }
+
+  .md\:bg-opacity-0 {
+    --bg-opacity: 0;
+  }
+
+  .md\:bg-opacity-25 {
+    --bg-opacity: 0.25;
+  }
+
+  .md\:bg-opacity-50 {
+    --bg-opacity: 0.5;
+  }
+
+  .md\:bg-opacity-75 {
+    --bg-opacity: 0.75;
+  }
+
+  .md\:bg-opacity-100 {
+    --bg-opacity: 1;
+  }
+
+  .md\:hover\:bg-opacity-0:hover {
+    --bg-opacity: 0;
+  }
+
+  .md\:hover\:bg-opacity-25:hover {
+    --bg-opacity: 0.25;
+  }
+
+  .md\:hover\:bg-opacity-50:hover {
+    --bg-opacity: 0.5;
+  }
+
+  .md\:hover\:bg-opacity-75:hover {
+    --bg-opacity: 0.75;
+  }
+
+  .md\:hover\:bg-opacity-100:hover {
+    --bg-opacity: 1;
+  }
+
+  .md\:focus\:bg-opacity-0:focus {
+    --bg-opacity: 0;
+  }
+
+  .md\:focus\:bg-opacity-25:focus {
+    --bg-opacity: 0.25;
+  }
+
+  .md\:focus\:bg-opacity-50:focus {
+    --bg-opacity: 0.5;
+  }
+
+  .md\:focus\:bg-opacity-75:focus {
+    --bg-opacity: 0.75;
+  }
+
+  .md\:focus\:bg-opacity-100:focus {
+    --bg-opacity: 1;
+  }
+
+  .md\:bg-bottom {
+    background-position: bottom;
+  }
+
+  .md\:bg-center {
+    background-position: center;
+  }
+
+  .md\:bg-left {
+    background-position: left;
+  }
+
+  .md\:bg-left-bottom {
+    background-position: left bottom;
+  }
+
+  .md\:bg-left-top {
+    background-position: left top;
+  }
+
+  .md\:bg-right {
+    background-position: right;
+  }
+
+  .md\:bg-right-bottom {
+    background-position: right bottom;
+  }
+
+  .md\:bg-right-top {
+    background-position: right top;
+  }
+
+  .md\:bg-top {
+    background-position: top;
+  }
+
+  .md\:bg-repeat {
+    background-repeat: repeat;
+  }
+
+  .md\:bg-no-repeat {
+    background-repeat: no-repeat;
+  }
+
+  .md\:bg-repeat-x {
+    background-repeat: repeat-x;
+  }
+
+  .md\:bg-repeat-y {
+    background-repeat: repeat-y;
+  }
+
+  .md\:bg-repeat-round {
+    background-repeat: round;
+  }
+
+  .md\:bg-repeat-space {
+    background-repeat: space;
+  }
+
+  .md\:bg-auto {
+    background-size: auto;
+  }
+
+  .md\:bg-cover {
+    background-size: cover;
+  }
+
+  .md\:bg-contain {
+    background-size: contain;
+  }
+
+  .md\:border-collapse {
+    border-collapse: collapse;
+  }
+
+  .md\:border-separate {
+    border-collapse: separate;
+  }
+
+  .md\:border-transparent {
+    border-color: transparent;
+  }
+
+  .md\:border-current {
+    border-color: currentColor;
+  }
+
+  .md\:border-black {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .md\:border-white {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .md\:border-gray-100 {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .md\:border-gray-200 {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .md\:border-gray-300 {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .md\:border-gray-400 {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .md\:border-gray-500 {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .md\:border-gray-600 {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .md\:border-gray-700 {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .md\:border-gray-800 {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .md\:border-gray-900 {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .md\:border-red-100 {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .md\:border-red-200 {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .md\:border-red-300 {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .md\:border-red-400 {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .md\:border-red-500 {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .md\:border-red-600 {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .md\:border-red-700 {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .md\:border-red-800 {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .md\:border-red-900 {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .md\:border-orange-100 {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .md\:border-orange-200 {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .md\:border-orange-300 {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .md\:border-orange-400 {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .md\:border-orange-500 {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .md\:border-orange-600 {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .md\:border-orange-700 {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .md\:border-orange-800 {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .md\:border-orange-900 {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .md\:border-yellow-100 {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .md\:border-yellow-200 {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .md\:border-yellow-300 {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .md\:border-yellow-400 {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .md\:border-yellow-500 {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .md\:border-yellow-600 {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .md\:border-yellow-700 {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .md\:border-yellow-800 {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .md\:border-yellow-900 {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .md\:border-green-100 {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .md\:border-green-200 {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .md\:border-green-300 {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .md\:border-green-400 {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .md\:border-green-500 {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .md\:border-green-600 {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .md\:border-green-700 {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .md\:border-green-800 {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .md\:border-green-900 {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .md\:border-teal-100 {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .md\:border-teal-200 {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .md\:border-teal-300 {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .md\:border-teal-400 {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .md\:border-teal-500 {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .md\:border-teal-600 {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .md\:border-teal-700 {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .md\:border-teal-800 {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .md\:border-teal-900 {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .md\:border-blue-100 {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .md\:border-blue-200 {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .md\:border-blue-300 {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .md\:border-blue-400 {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .md\:border-blue-500 {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .md\:border-blue-600 {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .md\:border-blue-700 {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .md\:border-blue-800 {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .md\:border-blue-900 {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .md\:border-indigo-100 {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .md\:border-indigo-200 {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .md\:border-indigo-300 {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .md\:border-indigo-400 {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .md\:border-indigo-500 {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .md\:border-indigo-600 {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .md\:border-indigo-700 {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .md\:border-indigo-800 {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .md\:border-indigo-900 {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .md\:border-purple-100 {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .md\:border-purple-200 {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .md\:border-purple-300 {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .md\:border-purple-400 {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .md\:border-purple-500 {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .md\:border-purple-600 {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .md\:border-purple-700 {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .md\:border-purple-800 {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .md\:border-purple-900 {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .md\:border-pink-100 {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .md\:border-pink-200 {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .md\:border-pink-300 {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .md\:border-pink-400 {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .md\:border-pink-500 {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .md\:border-pink-600 {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .md\:border-pink-700 {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .md\:border-pink-800 {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .md\:border-pink-900 {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .md\:hover\:border-transparent:hover {
+    border-color: transparent;
+  }
+
+  .md\:hover\:border-current:hover {
+    border-color: currentColor;
+  }
+
+  .md\:hover\:border-black:hover {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .md\:hover\:border-white:hover {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-100:hover {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-200:hover {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-300:hover {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-400:hover {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-500:hover {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-600:hover {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-700:hover {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-800:hover {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-900:hover {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-300:hover {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-400:hover {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-500:hover {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-600:hover {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-700:hover {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-800:hover {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-900:hover {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-100:hover {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-200:hover {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-300:hover {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-400:hover {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-500:hover {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-600:hover {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-700:hover {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-800:hover {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-900:hover {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-100:hover {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-200:hover {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-300:hover {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-400:hover {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-500:hover {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-600:hover {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-700:hover {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-800:hover {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-900:hover {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-100:hover {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-200:hover {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-300:hover {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-400:hover {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-500:hover {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-600:hover {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-700:hover {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-800:hover {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-900:hover {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-100:hover {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-200:hover {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-300:hover {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-400:hover {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-500:hover {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-600:hover {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-700:hover {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-800:hover {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-900:hover {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-200:hover {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-300:hover {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-400:hover {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-500:hover {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-600:hover {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-700:hover {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-800:hover {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-900:hover {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-200:hover {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-300:hover {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-400:hover {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-500:hover {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-600:hover {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-700:hover {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-800:hover {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-900:hover {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-100:hover {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-200:hover {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-300:hover {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-400:hover {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-500:hover {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-600:hover {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-700:hover {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-800:hover {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-900:hover {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-300:hover {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-400:hover {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-500:hover {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-600:hover {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-700:hover {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-800:hover {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-900:hover {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .md\:focus\:border-transparent:focus {
+    border-color: transparent;
+  }
+
+  .md\:focus\:border-current:focus {
+    border-color: currentColor;
+  }
+
+  .md\:focus\:border-black:focus {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .md\:focus\:border-white:focus {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-100:focus {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-200:focus {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-300:focus {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-400:focus {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-500:focus {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-600:focus {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-700:focus {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-800:focus {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-900:focus {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-300:focus {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-400:focus {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-500:focus {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-600:focus {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-700:focus {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-800:focus {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-900:focus {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-100:focus {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-200:focus {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-300:focus {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-400:focus {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-500:focus {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-600:focus {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-700:focus {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-800:focus {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-900:focus {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-100:focus {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-200:focus {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-300:focus {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-400:focus {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-500:focus {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-600:focus {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-700:focus {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-800:focus {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-900:focus {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-100:focus {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-200:focus {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-300:focus {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-400:focus {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-500:focus {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-600:focus {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-700:focus {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-800:focus {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-900:focus {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-100:focus {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-200:focus {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-300:focus {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-400:focus {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-500:focus {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-600:focus {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-700:focus {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-800:focus {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-900:focus {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-200:focus {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-300:focus {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-400:focus {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-500:focus {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-600:focus {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-700:focus {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-800:focus {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-900:focus {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-200:focus {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-300:focus {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-400:focus {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-500:focus {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-600:focus {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-700:focus {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-800:focus {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-900:focus {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-100:focus {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-200:focus {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-300:focus {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-400:focus {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-500:focus {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-600:focus {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-700:focus {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-800:focus {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-900:focus {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-300:focus {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-400:focus {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-500:focus {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-600:focus {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-700:focus {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-800:focus {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-900:focus {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .md\:border-opacity-0 {
+    --border-opacity: 0;
+  }
+
+  .md\:border-opacity-25 {
+    --border-opacity: 0.25;
+  }
+
+  .md\:border-opacity-50 {
+    --border-opacity: 0.5;
+  }
+
+  .md\:border-opacity-75 {
+    --border-opacity: 0.75;
+  }
+
+  .md\:border-opacity-100 {
+    --border-opacity: 1;
+  }
+
+  .md\:hover\:border-opacity-0:hover {
+    --border-opacity: 0;
+  }
+
+  .md\:hover\:border-opacity-25:hover {
+    --border-opacity: 0.25;
+  }
+
+  .md\:hover\:border-opacity-50:hover {
+    --border-opacity: 0.5;
+  }
+
+  .md\:hover\:border-opacity-75:hover {
+    --border-opacity: 0.75;
+  }
+
+  .md\:hover\:border-opacity-100:hover {
+    --border-opacity: 1;
+  }
+
+  .md\:focus\:border-opacity-0:focus {
+    --border-opacity: 0;
+  }
+
+  .md\:focus\:border-opacity-25:focus {
+    --border-opacity: 0.25;
+  }
+
+  .md\:focus\:border-opacity-50:focus {
+    --border-opacity: 0.5;
+  }
+
+  .md\:focus\:border-opacity-75:focus {
+    --border-opacity: 0.75;
+  }
+
+  .md\:focus\:border-opacity-100:focus {
+    --border-opacity: 1;
+  }
+
+  .md\:rounded-none {
+    border-radius: 0;
+  }
+
+  .md\:rounded-sm {
+    border-radius: 0.125rem;
+  }
+
+  .md\:rounded {
+    border-radius: 0.25rem;
+  }
+
+  .md\:rounded-md {
+    border-radius: 0.375rem;
+  }
+
+  .md\:rounded-lg {
+    border-radius: 0.5rem;
+  }
+
+  .md\:rounded-full {
+    border-radius: 9999px;
+  }
+
+  .md\:rounded-t-none {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+  }
+
+  .md\:rounded-r-none {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+  }
+
+  .md\:rounded-b-none {
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .md\:rounded-l-none {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .md\:rounded-t-sm {
+    border-top-left-radius: 0.125rem;
+    border-top-right-radius: 0.125rem;
+  }
+
+  .md\:rounded-r-sm {
+    border-top-right-radius: 0.125rem;
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .md\:rounded-b-sm {
+    border-bottom-right-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .md\:rounded-l-sm {
+    border-top-left-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .md\:rounded-t {
+    border-top-left-radius: 0.25rem;
+    border-top-right-radius: 0.25rem;
+  }
+
+  .md\:rounded-r {
+    border-top-right-radius: 0.25rem;
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .md\:rounded-b {
+    border-bottom-right-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .md\:rounded-l {
+    border-top-left-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .md\:rounded-t-md {
+    border-top-left-radius: 0.375rem;
+    border-top-right-radius: 0.375rem;
+  }
+
+  .md\:rounded-r-md {
+    border-top-right-radius: 0.375rem;
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .md\:rounded-b-md {
+    border-bottom-right-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .md\:rounded-l-md {
+    border-top-left-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .md\:rounded-t-lg {
+    border-top-left-radius: 0.5rem;
+    border-top-right-radius: 0.5rem;
+  }
+
+  .md\:rounded-r-lg {
+    border-top-right-radius: 0.5rem;
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .md\:rounded-b-lg {
+    border-bottom-right-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .md\:rounded-l-lg {
+    border-top-left-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .md\:rounded-t-full {
+    border-top-left-radius: 9999px;
+    border-top-right-radius: 9999px;
+  }
+
+  .md\:rounded-r-full {
+    border-top-right-radius: 9999px;
+    border-bottom-right-radius: 9999px;
+  }
+
+  .md\:rounded-b-full {
+    border-bottom-right-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .md\:rounded-l-full {
+    border-top-left-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .md\:rounded-tl-none {
+    border-top-left-radius: 0;
+  }
+
+  .md\:rounded-tr-none {
+    border-top-right-radius: 0;
+  }
+
+  .md\:rounded-br-none {
+    border-bottom-right-radius: 0;
+  }
+
+  .md\:rounded-bl-none {
+    border-bottom-left-radius: 0;
+  }
+
+  .md\:rounded-tl-sm {
+    border-top-left-radius: 0.125rem;
+  }
+
+  .md\:rounded-tr-sm {
+    border-top-right-radius: 0.125rem;
+  }
+
+  .md\:rounded-br-sm {
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .md\:rounded-bl-sm {
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .md\:rounded-tl {
+    border-top-left-radius: 0.25rem;
+  }
+
+  .md\:rounded-tr {
+    border-top-right-radius: 0.25rem;
+  }
+
+  .md\:rounded-br {
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .md\:rounded-bl {
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .md\:rounded-tl-md {
+    border-top-left-radius: 0.375rem;
+  }
+
+  .md\:rounded-tr-md {
+    border-top-right-radius: 0.375rem;
+  }
+
+  .md\:rounded-br-md {
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .md\:rounded-bl-md {
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .md\:rounded-tl-lg {
+    border-top-left-radius: 0.5rem;
+  }
+
+  .md\:rounded-tr-lg {
+    border-top-right-radius: 0.5rem;
+  }
+
+  .md\:rounded-br-lg {
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .md\:rounded-bl-lg {
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .md\:rounded-tl-full {
+    border-top-left-radius: 9999px;
+  }
+
+  .md\:rounded-tr-full {
+    border-top-right-radius: 9999px;
+  }
+
+  .md\:rounded-br-full {
+    border-bottom-right-radius: 9999px;
+  }
+
+  .md\:rounded-bl-full {
+    border-bottom-left-radius: 9999px;
+  }
+
+  .md\:border-solid {
+    border-style: solid;
+  }
+
+  .md\:border-dashed {
+    border-style: dashed;
+  }
+
+  .md\:border-dotted {
+    border-style: dotted;
+  }
+
+  .md\:border-double {
+    border-style: double;
+  }
+
+  .md\:border-none {
+    border-style: none;
+  }
+
+  .md\:border-0 {
+    border-width: 0;
+  }
+
+  .md\:border-2 {
+    border-width: 2px;
+  }
+
+  .md\:border-4 {
+    border-width: 4px;
+  }
+
+  .md\:border-8 {
+    border-width: 8px;
+  }
+
+  .md\:border {
+    border-width: 1px;
+  }
+
+  .md\:border-t-0 {
+    border-top-width: 0;
+  }
+
+  .md\:border-r-0 {
+    border-right-width: 0;
+  }
+
+  .md\:border-b-0 {
+    border-bottom-width: 0;
+  }
+
+  .md\:border-l-0 {
+    border-left-width: 0;
+  }
+
+  .md\:border-t-2 {
+    border-top-width: 2px;
+  }
+
+  .md\:border-r-2 {
+    border-right-width: 2px;
+  }
+
+  .md\:border-b-2 {
+    border-bottom-width: 2px;
+  }
+
+  .md\:border-l-2 {
+    border-left-width: 2px;
+  }
+
+  .md\:border-t-4 {
+    border-top-width: 4px;
+  }
+
+  .md\:border-r-4 {
+    border-right-width: 4px;
+  }
+
+  .md\:border-b-4 {
+    border-bottom-width: 4px;
+  }
+
+  .md\:border-l-4 {
+    border-left-width: 4px;
+  }
+
+  .md\:border-t-8 {
+    border-top-width: 8px;
+  }
+
+  .md\:border-r-8 {
+    border-right-width: 8px;
+  }
+
+  .md\:border-b-8 {
+    border-bottom-width: 8px;
+  }
+
+  .md\:border-l-8 {
+    border-left-width: 8px;
+  }
+
+  .md\:border-t {
+    border-top-width: 1px;
+  }
+
+  .md\:border-r {
+    border-right-width: 1px;
+  }
+
+  .md\:border-b {
+    border-bottom-width: 1px;
+  }
+
+  .md\:border-l {
+    border-left-width: 1px;
+  }
+
+  .md\:box-border {
+    box-sizing: border-box;
+  }
+
+  .md\:box-content {
+    box-sizing: content-box;
+  }
+
+  .md\:cursor-auto {
+    cursor: auto;
+  }
+
+  .md\:cursor-default {
+    cursor: default;
+  }
+
+  .md\:cursor-pointer {
+    cursor: pointer;
+  }
+
+  .md\:cursor-wait {
+    cursor: wait;
+  }
+
+  .md\:cursor-text {
+    cursor: text;
+  }
+
+  .md\:cursor-move {
+    cursor: move;
+  }
+
+  .md\:cursor-not-allowed {
+    cursor: not-allowed;
+  }
+
+  .md\:block {
+    display: block;
+  }
+
+  .md\:inline-block {
+    display: inline-block;
+  }
+
+  .md\:inline {
+    display: inline;
+  }
+
+  .md\:flex {
+    display: flex;
+  }
+
+  .md\:inline-flex {
+    display: inline-flex;
+  }
+
+  .md\:table {
+    display: table;
+  }
+
+  .md\:table-caption {
+    display: table-caption;
+  }
+
+  .md\:table-cell {
+    display: table-cell;
+  }
+
+  .md\:table-column {
+    display: table-column;
+  }
+
+  .md\:table-column-group {
+    display: table-column-group;
+  }
+
+  .md\:table-footer-group {
+    display: table-footer-group;
+  }
+
+  .md\:table-header-group {
+    display: table-header-group;
+  }
+
+  .md\:table-row-group {
+    display: table-row-group;
+  }
+
+  .md\:table-row {
+    display: table-row;
+  }
+
+  .md\:flow-root {
+    display: flow-root;
+  }
+
+  .md\:grid {
+    display: grid;
+  }
+
+  .md\:inline-grid {
+    display: inline-grid;
+  }
+
+  .md\:contents {
+    display: contents;
+  }
+
+  .md\:hidden {
+    display: none;
+  }
+
+  .md\:flex-row {
+    flex-direction: row;
+  }
+
+  .md\:flex-row-reverse {
+    flex-direction: row-reverse;
+  }
+
+  .md\:flex-col {
+    flex-direction: column;
+  }
+
+  .md\:flex-col-reverse {
+    flex-direction: column-reverse;
+  }
+
+  .md\:flex-wrap {
+    flex-wrap: wrap;
+  }
+
+  .md\:flex-wrap-reverse {
+    flex-wrap: wrap-reverse;
+  }
+
+  .md\:flex-no-wrap {
+    flex-wrap: nowrap;
+  }
+
+  .md\:place-items-auto {
+    place-items: auto;
+  }
+
+  .md\:place-items-start {
+    place-items: start;
+  }
+
+  .md\:place-items-end {
+    place-items: end;
+  }
+
+  .md\:place-items-center {
+    place-items: center;
+  }
+
+  .md\:place-items-stretch {
+    place-items: stretch;
+  }
+
+  .md\:place-content-center {
+    place-content: center;
+  }
+
+  .md\:place-content-start {
+    place-content: start;
+  }
+
+  .md\:place-content-end {
+    place-content: end;
+  }
+
+  .md\:place-content-between {
+    place-content: space-between;
+  }
+
+  .md\:place-content-around {
+    place-content: space-around;
+  }
+
+  .md\:place-content-evenly {
+    place-content: space-evenly;
+  }
+
+  .md\:place-content-stretch {
+    place-content: stretch;
+  }
+
+  .md\:place-self-auto {
+    place-self: auto;
+  }
+
+  .md\:place-self-start {
+    place-self: start;
+  }
+
+  .md\:place-self-end {
+    place-self: end;
+  }
+
+  .md\:place-self-center {
+    place-self: center;
+  }
+
+  .md\:place-self-stretch {
+    place-self: stretch;
+  }
+
+  .md\:items-start {
+    align-items: flex-start;
+  }
+
+  .md\:items-end {
+    align-items: flex-end;
+  }
+
+  .md\:items-center {
+    align-items: center;
+  }
+
+  .md\:items-baseline {
+    align-items: baseline;
+  }
+
+  .md\:items-stretch {
+    align-items: stretch;
+  }
+
+  .md\:content-center {
+    align-content: center;
+  }
+
+  .md\:content-start {
+    align-content: flex-start;
+  }
+
+  .md\:content-end {
+    align-content: flex-end;
+  }
+
+  .md\:content-between {
+    align-content: space-between;
+  }
+
+  .md\:content-around {
+    align-content: space-around;
+  }
+
+  .md\:content-evenly {
+    align-content: space-evenly;
+  }
+
+  .md\:self-auto {
+    align-self: auto;
+  }
+
+  .md\:self-start {
+    align-self: flex-start;
+  }
+
+  .md\:self-end {
+    align-self: flex-end;
+  }
+
+  .md\:self-center {
+    align-self: center;
+  }
+
+  .md\:self-stretch {
+    align-self: stretch;
+  }
+
+  .md\:justify-items-auto {
+    justify-items: auto;
+  }
+
+  .md\:justify-items-start {
+    justify-items: start;
+  }
+
+  .md\:justify-items-end {
+    justify-items: end;
+  }
+
+  .md\:justify-items-center {
+    justify-items: center;
+  }
+
+  .md\:justify-items-stretch {
+    justify-items: stretch;
+  }
+
+  .md\:justify-start {
+    justify-content: flex-start;
+  }
+
+  .md\:justify-end {
+    justify-content: flex-end;
+  }
+
+  .md\:justify-center {
+    justify-content: center;
+  }
+
+  .md\:justify-between {
+    justify-content: space-between;
+  }
+
+  .md\:justify-around {
+    justify-content: space-around;
+  }
+
+  .md\:justify-evenly {
+    justify-content: space-evenly;
+  }
+
+  .md\:justify-self-auto {
+    justify-self: auto;
+  }
+
+  .md\:justify-self-start {
+    justify-self: start;
+  }
+
+  .md\:justify-self-end {
+    justify-self: end;
+  }
+
+  .md\:justify-self-center {
+    justify-self: center;
+  }
+
+  .md\:justify-self-stretch {
+    justify-self: stretch;
+  }
+
+  .md\:flex-1 {
+    flex: 1 1 0%;
+  }
+
+  .md\:flex-auto {
+    flex: 1 1 auto;
+  }
+
+  .md\:flex-initial {
+    flex: 0 1 auto;
+  }
+
+  .md\:flex-none {
+    flex: none;
+  }
+
+  .md\:flex-grow-0 {
+    flex-grow: 0;
+  }
+
+  .md\:flex-grow {
+    flex-grow: 1;
+  }
+
+  .md\:flex-shrink-0 {
+    flex-shrink: 0;
+  }
+
+  .md\:flex-shrink {
+    flex-shrink: 1;
+  }
+
+  .md\:order-1 {
+    order: 1;
+  }
+
+  .md\:order-2 {
+    order: 2;
+  }
+
+  .md\:order-3 {
+    order: 3;
+  }
+
+  .md\:order-4 {
+    order: 4;
+  }
+
+  .md\:order-5 {
+    order: 5;
+  }
+
+  .md\:order-6 {
+    order: 6;
+  }
+
+  .md\:order-7 {
+    order: 7;
+  }
+
+  .md\:order-8 {
+    order: 8;
+  }
+
+  .md\:order-9 {
+    order: 9;
+  }
+
+  .md\:order-10 {
+    order: 10;
+  }
+
+  .md\:order-11 {
+    order: 11;
+  }
+
+  .md\:order-12 {
+    order: 12;
+  }
+
+  .md\:order-first {
+    order: -9999;
+  }
+
+  .md\:order-last {
+    order: 9999;
+  }
+
+  .md\:order-none {
+    order: 0;
+  }
+
+  .md\:float-right {
+    float: right;
+  }
+
+  .md\:float-left {
+    float: left;
+  }
+
+  .md\:float-none {
+    float: none;
+  }
+
+  .md\:clearfix:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+
+  .md\:clear-left {
+    clear: left;
+  }
+
+  .md\:clear-right {
+    clear: right;
+  }
+
+  .md\:clear-both {
+    clear: both;
+  }
+
+  .md\:clear-none {
+    clear: none;
+  }
+
+  .md\:font-sans {
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  }
+
+  .md\:font-serif {
+    font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+  }
+
+  .md\:font-mono {
+    font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+  }
+
+  .md\:font-hairline {
+    font-weight: 100;
+  }
+
+  .md\:font-thin {
+    font-weight: 200;
+  }
+
+  .md\:font-light {
+    font-weight: 300;
+  }
+
+  .md\:font-normal {
+    font-weight: 400;
+  }
+
+  .md\:font-medium {
+    font-weight: 500;
+  }
+
+  .md\:font-semibold {
+    font-weight: 600;
+  }
+
+  .md\:font-bold {
+    font-weight: 700;
+  }
+
+  .md\:font-extrabold {
+    font-weight: 800;
+  }
+
+  .md\:font-black {
+    font-weight: 900;
+  }
+
+  .md\:hover\:font-hairline:hover {
+    font-weight: 100;
+  }
+
+  .md\:hover\:font-thin:hover {
+    font-weight: 200;
+  }
+
+  .md\:hover\:font-light:hover {
+    font-weight: 300;
+  }
+
+  .md\:hover\:font-normal:hover {
+    font-weight: 400;
+  }
+
+  .md\:hover\:font-medium:hover {
+    font-weight: 500;
+  }
+
+  .md\:hover\:font-semibold:hover {
+    font-weight: 600;
+  }
+
+  .md\:hover\:font-bold:hover {
+    font-weight: 700;
+  }
+
+  .md\:hover\:font-extrabold:hover {
+    font-weight: 800;
+  }
+
+  .md\:hover\:font-black:hover {
+    font-weight: 900;
+  }
+
+  .md\:focus\:font-hairline:focus {
+    font-weight: 100;
+  }
+
+  .md\:focus\:font-thin:focus {
+    font-weight: 200;
+  }
+
+  .md\:focus\:font-light:focus {
+    font-weight: 300;
+  }
+
+  .md\:focus\:font-normal:focus {
+    font-weight: 400;
+  }
+
+  .md\:focus\:font-medium:focus {
+    font-weight: 500;
+  }
+
+  .md\:focus\:font-semibold:focus {
+    font-weight: 600;
+  }
+
+  .md\:focus\:font-bold:focus {
+    font-weight: 700;
+  }
+
+  .md\:focus\:font-extrabold:focus {
+    font-weight: 800;
+  }
+
+  .md\:focus\:font-black:focus {
+    font-weight: 900;
+  }
+
+  .md\:h-0 {
+    height: 0;
+  }
+
+  .md\:h-1 {
+    height: 0.25rem;
+  }
+
+  .md\:h-2 {
+    height: 0.5rem;
+  }
+
+  .md\:h-3 {
+    height: 0.75rem;
+  }
+
+  .md\:h-4 {
+    height: 1rem;
+  }
+
+  .md\:h-5 {
+    height: 1.25rem;
+  }
+
+  .md\:h-6 {
+    height: 1.5rem;
+  }
+
+  .md\:h-8 {
+    height: 2rem;
+  }
+
+  .md\:h-10 {
+    height: 2.5rem;
+  }
+
+  .md\:h-12 {
+    height: 3rem;
+  }
+
+  .md\:h-16 {
+    height: 4rem;
+  }
+
+  .md\:h-20 {
+    height: 5rem;
+  }
+
+  .md\:h-24 {
+    height: 6rem;
+  }
+
+  .md\:h-32 {
+    height: 8rem;
+  }
+
+  .md\:h-40 {
+    height: 10rem;
+  }
+
+  .md\:h-48 {
+    height: 12rem;
+  }
+
+  .md\:h-56 {
+    height: 14rem;
+  }
+
+  .md\:h-64 {
+    height: 16rem;
+  }
+
+  .md\:h-auto {
+    height: auto;
+  }
+
+  .md\:h-px {
+    height: 1px;
+  }
+
+  .md\:h-full {
+    height: 100%;
+  }
+
+  .md\:h-screen {
+    height: 100vh;
+  }
+
+  .md\:text-xs {
+    font-size: 0.75rem;
+  }
+
+  .md\:text-sm {
+    font-size: 0.875rem;
+  }
+
+  .md\:text-base {
+    font-size: 1rem;
+  }
+
+  .md\:text-lg {
+    font-size: 1.125rem;
+  }
+
+  .md\:text-xl {
+    font-size: 1.25rem;
+  }
+
+  .md\:text-2xl {
+    font-size: 1.5rem;
+  }
+
+  .md\:text-3xl {
+    font-size: 1.875rem;
+  }
+
+  .md\:text-4xl {
+    font-size: 2.25rem;
+  }
+
+  .md\:text-5xl {
+    font-size: 3rem;
+  }
+
+  .md\:text-6xl {
+    font-size: 4rem;
+  }
+
+  .md\:leading-3 {
+    line-height: .75rem;
+  }
+
+  .md\:leading-4 {
+    line-height: 1rem;
+  }
+
+  .md\:leading-5 {
+    line-height: 1.25rem;
+  }
+
+  .md\:leading-6 {
+    line-height: 1.5rem;
+  }
+
+  .md\:leading-7 {
+    line-height: 1.75rem;
+  }
+
+  .md\:leading-8 {
+    line-height: 2rem;
+  }
+
+  .md\:leading-9 {
+    line-height: 2.25rem;
+  }
+
+  .md\:leading-10 {
+    line-height: 2.5rem;
+  }
+
+  .md\:leading-none {
+    line-height: 1;
+  }
+
+  .md\:leading-tight {
+    line-height: 1.25;
+  }
+
+  .md\:leading-snug {
+    line-height: 1.375;
+  }
+
+  .md\:leading-normal {
+    line-height: 1.5;
+  }
+
+  .md\:leading-relaxed {
+    line-height: 1.625;
+  }
+
+  .md\:leading-loose {
+    line-height: 2;
+  }
+
+  .md\:list-inside {
+    list-style-position: inside;
+  }
+
+  .md\:list-outside {
+    list-style-position: outside;
+  }
+
+  .md\:list-none {
+    list-style-type: none;
+  }
+
+  .md\:list-disc {
+    list-style-type: disc;
+  }
+
+  .md\:list-decimal {
+    list-style-type: decimal;
+  }
+
+  .md\:m-0 {
+    margin: 0;
+  }
+
+  .md\:m-1 {
+    margin: 0.25rem;
+  }
+
+  .md\:m-2 {
+    margin: 0.5rem;
+  }
+
+  .md\:m-3 {
+    margin: 0.75rem;
+  }
+
+  .md\:m-4 {
+    margin: 1rem;
+  }
+
+  .md\:m-5 {
+    margin: 1.25rem;
+  }
+
+  .md\:m-6 {
+    margin: 1.5rem;
+  }
+
+  .md\:m-8 {
+    margin: 2rem;
+  }
+
+  .md\:m-10 {
+    margin: 2.5rem;
+  }
+
+  .md\:m-12 {
+    margin: 3rem;
+  }
+
+  .md\:m-16 {
+    margin: 4rem;
+  }
+
+  .md\:m-20 {
+    margin: 5rem;
+  }
+
+  .md\:m-24 {
+    margin: 6rem;
+  }
+
+  .md\:m-32 {
+    margin: 8rem;
+  }
+
+  .md\:m-40 {
+    margin: 10rem;
+  }
+
+  .md\:m-48 {
+    margin: 12rem;
+  }
+
+  .md\:m-56 {
+    margin: 14rem;
+  }
+
+  .md\:m-64 {
+    margin: 16rem;
+  }
+
+  .md\:m-auto {
+    margin: auto;
+  }
+
+  .md\:m-px {
+    margin: 1px;
+  }
+
+  .md\:-m-1 {
+    margin: -0.25rem;
+  }
+
+  .md\:-m-2 {
+    margin: -0.5rem;
+  }
+
+  .md\:-m-3 {
+    margin: -0.75rem;
+  }
+
+  .md\:-m-4 {
+    margin: -1rem;
+  }
+
+  .md\:-m-5 {
+    margin: -1.25rem;
+  }
+
+  .md\:-m-6 {
+    margin: -1.5rem;
+  }
+
+  .md\:-m-8 {
+    margin: -2rem;
+  }
+
+  .md\:-m-10 {
+    margin: -2.5rem;
+  }
+
+  .md\:-m-12 {
+    margin: -3rem;
+  }
+
+  .md\:-m-16 {
+    margin: -4rem;
+  }
+
+  .md\:-m-20 {
+    margin: -5rem;
+  }
+
+  .md\:-m-24 {
+    margin: -6rem;
+  }
+
+  .md\:-m-32 {
+    margin: -8rem;
+  }
+
+  .md\:-m-40 {
+    margin: -10rem;
+  }
+
+  .md\:-m-48 {
+    margin: -12rem;
+  }
+
+  .md\:-m-56 {
+    margin: -14rem;
+  }
+
+  .md\:-m-64 {
+    margin: -16rem;
+  }
+
+  .md\:-m-px {
+    margin: -1px;
+  }
+
+  .md\:my-0 {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .md\:mx-0 {
+    margin-left: 0;
+    margin-right: 0;
+  }
+
+  .md\:my-1 {
+    margin-top: 0.25rem;
+    margin-bottom: 0.25rem;
+  }
+
+  .md\:mx-1 {
+    margin-left: 0.25rem;
+    margin-right: 0.25rem;
+  }
+
+  .md\:my-2 {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+  }
+
+  .md\:mx-2 {
+    margin-left: 0.5rem;
+    margin-right: 0.5rem;
+  }
+
+  .md\:my-3 {
+    margin-top: 0.75rem;
+    margin-bottom: 0.75rem;
+  }
+
+  .md\:mx-3 {
+    margin-left: 0.75rem;
+    margin-right: 0.75rem;
+  }
+
+  .md\:my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+  }
+
+  .md\:mx-4 {
+    margin-left: 1rem;
+    margin-right: 1rem;
+  }
+
+  .md\:my-5 {
+    margin-top: 1.25rem;
+    margin-bottom: 1.25rem;
+  }
+
+  .md\:mx-5 {
+    margin-left: 1.25rem;
+    margin-right: 1.25rem;
+  }
+
+  .md\:my-6 {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+
+  .md\:mx-6 {
+    margin-left: 1.5rem;
+    margin-right: 1.5rem;
+  }
+
+  .md\:my-8 {
+    margin-top: 2rem;
+    margin-bottom: 2rem;
+  }
+
+  .md\:mx-8 {
+    margin-left: 2rem;
+    margin-right: 2rem;
+  }
+
+  .md\:my-10 {
+    margin-top: 2.5rem;
+    margin-bottom: 2.5rem;
+  }
+
+  .md\:mx-10 {
+    margin-left: 2.5rem;
+    margin-right: 2.5rem;
+  }
+
+  .md\:my-12 {
+    margin-top: 3rem;
+    margin-bottom: 3rem;
+  }
+
+  .md\:mx-12 {
+    margin-left: 3rem;
+    margin-right: 3rem;
+  }
+
+  .md\:my-16 {
+    margin-top: 4rem;
+    margin-bottom: 4rem;
+  }
+
+  .md\:mx-16 {
+    margin-left: 4rem;
+    margin-right: 4rem;
+  }
+
+  .md\:my-20 {
+    margin-top: 5rem;
+    margin-bottom: 5rem;
+  }
+
+  .md\:mx-20 {
+    margin-left: 5rem;
+    margin-right: 5rem;
+  }
+
+  .md\:my-24 {
+    margin-top: 6rem;
+    margin-bottom: 6rem;
+  }
+
+  .md\:mx-24 {
+    margin-left: 6rem;
+    margin-right: 6rem;
+  }
+
+  .md\:my-32 {
+    margin-top: 8rem;
+    margin-bottom: 8rem;
+  }
+
+  .md\:mx-32 {
+    margin-left: 8rem;
+    margin-right: 8rem;
+  }
+
+  .md\:my-40 {
+    margin-top: 10rem;
+    margin-bottom: 10rem;
+  }
+
+  .md\:mx-40 {
+    margin-left: 10rem;
+    margin-right: 10rem;
+  }
+
+  .md\:my-48 {
+    margin-top: 12rem;
+    margin-bottom: 12rem;
+  }
+
+  .md\:mx-48 {
+    margin-left: 12rem;
+    margin-right: 12rem;
+  }
+
+  .md\:my-56 {
+    margin-top: 14rem;
+    margin-bottom: 14rem;
+  }
+
+  .md\:mx-56 {
+    margin-left: 14rem;
+    margin-right: 14rem;
+  }
+
+  .md\:my-64 {
+    margin-top: 16rem;
+    margin-bottom: 16rem;
+  }
+
+  .md\:mx-64 {
+    margin-left: 16rem;
+    margin-right: 16rem;
+  }
+
+  .md\:my-auto {
+    margin-top: auto;
+    margin-bottom: auto;
+  }
+
+  .md\:mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .md\:my-px {
+    margin-top: 1px;
+    margin-bottom: 1px;
+  }
+
+  .md\:mx-px {
+    margin-left: 1px;
+    margin-right: 1px;
+  }
+
+  .md\:-my-1 {
+    margin-top: -0.25rem;
+    margin-bottom: -0.25rem;
+  }
+
+  .md\:-mx-1 {
+    margin-left: -0.25rem;
+    margin-right: -0.25rem;
+  }
+
+  .md\:-my-2 {
+    margin-top: -0.5rem;
+    margin-bottom: -0.5rem;
+  }
+
+  .md\:-mx-2 {
+    margin-left: -0.5rem;
+    margin-right: -0.5rem;
+  }
+
+  .md\:-my-3 {
+    margin-top: -0.75rem;
+    margin-bottom: -0.75rem;
+  }
+
+  .md\:-mx-3 {
+    margin-left: -0.75rem;
+    margin-right: -0.75rem;
+  }
+
+  .md\:-my-4 {
+    margin-top: -1rem;
+    margin-bottom: -1rem;
+  }
+
+  .md\:-mx-4 {
+    margin-left: -1rem;
+    margin-right: -1rem;
+  }
+
+  .md\:-my-5 {
+    margin-top: -1.25rem;
+    margin-bottom: -1.25rem;
+  }
+
+  .md\:-mx-5 {
+    margin-left: -1.25rem;
+    margin-right: -1.25rem;
+  }
+
+  .md\:-my-6 {
+    margin-top: -1.5rem;
+    margin-bottom: -1.5rem;
+  }
+
+  .md\:-mx-6 {
+    margin-left: -1.5rem;
+    margin-right: -1.5rem;
+  }
+
+  .md\:-my-8 {
+    margin-top: -2rem;
+    margin-bottom: -2rem;
+  }
+
+  .md\:-mx-8 {
+    margin-left: -2rem;
+    margin-right: -2rem;
+  }
+
+  .md\:-my-10 {
+    margin-top: -2.5rem;
+    margin-bottom: -2.5rem;
+  }
+
+  .md\:-mx-10 {
+    margin-left: -2.5rem;
+    margin-right: -2.5rem;
+  }
+
+  .md\:-my-12 {
+    margin-top: -3rem;
+    margin-bottom: -3rem;
+  }
+
+  .md\:-mx-12 {
+    margin-left: -3rem;
+    margin-right: -3rem;
+  }
+
+  .md\:-my-16 {
+    margin-top: -4rem;
+    margin-bottom: -4rem;
+  }
+
+  .md\:-mx-16 {
+    margin-left: -4rem;
+    margin-right: -4rem;
+  }
+
+  .md\:-my-20 {
+    margin-top: -5rem;
+    margin-bottom: -5rem;
+  }
+
+  .md\:-mx-20 {
+    margin-left: -5rem;
+    margin-right: -5rem;
+  }
+
+  .md\:-my-24 {
+    margin-top: -6rem;
+    margin-bottom: -6rem;
+  }
+
+  .md\:-mx-24 {
+    margin-left: -6rem;
+    margin-right: -6rem;
+  }
+
+  .md\:-my-32 {
+    margin-top: -8rem;
+    margin-bottom: -8rem;
+  }
+
+  .md\:-mx-32 {
+    margin-left: -8rem;
+    margin-right: -8rem;
+  }
+
+  .md\:-my-40 {
+    margin-top: -10rem;
+    margin-bottom: -10rem;
+  }
+
+  .md\:-mx-40 {
+    margin-left: -10rem;
+    margin-right: -10rem;
+  }
+
+  .md\:-my-48 {
+    margin-top: -12rem;
+    margin-bottom: -12rem;
+  }
+
+  .md\:-mx-48 {
+    margin-left: -12rem;
+    margin-right: -12rem;
+  }
+
+  .md\:-my-56 {
+    margin-top: -14rem;
+    margin-bottom: -14rem;
+  }
+
+  .md\:-mx-56 {
+    margin-left: -14rem;
+    margin-right: -14rem;
+  }
+
+  .md\:-my-64 {
+    margin-top: -16rem;
+    margin-bottom: -16rem;
+  }
+
+  .md\:-mx-64 {
+    margin-left: -16rem;
+    margin-right: -16rem;
+  }
+
+  .md\:-my-px {
+    margin-top: -1px;
+    margin-bottom: -1px;
+  }
+
+  .md\:-mx-px {
+    margin-left: -1px;
+    margin-right: -1px;
+  }
+
+  .md\:mt-0 {
+    margin-top: 0;
+  }
+
+  .md\:mr-0 {
+    margin-right: 0;
+  }
+
+  .md\:mb-0 {
+    margin-bottom: 0;
+  }
+
+  .md\:ml-0 {
+    margin-left: 0;
+  }
+
+  .md\:mt-1 {
+    margin-top: 0.25rem;
+  }
+
+  .md\:mr-1 {
+    margin-right: 0.25rem;
+  }
+
+  .md\:mb-1 {
+    margin-bottom: 0.25rem;
+  }
+
+  .md\:ml-1 {
+    margin-left: 0.25rem;
+  }
+
+  .md\:mt-2 {
+    margin-top: 0.5rem;
+  }
+
+  .md\:mr-2 {
+    margin-right: 0.5rem;
+  }
+
+  .md\:mb-2 {
+    margin-bottom: 0.5rem;
+  }
+
+  .md\:ml-2 {
+    margin-left: 0.5rem;
+  }
+
+  .md\:mt-3 {
+    margin-top: 0.75rem;
+  }
+
+  .md\:mr-3 {
+    margin-right: 0.75rem;
+  }
+
+  .md\:mb-3 {
+    margin-bottom: 0.75rem;
+  }
+
+  .md\:ml-3 {
+    margin-left: 0.75rem;
+  }
+
+  .md\:mt-4 {
+    margin-top: 1rem;
+  }
+
+  .md\:mr-4 {
+    margin-right: 1rem;
+  }
+
+  .md\:mb-4 {
+    margin-bottom: 1rem;
+  }
+
+  .md\:ml-4 {
+    margin-left: 1rem;
+  }
+
+  .md\:mt-5 {
+    margin-top: 1.25rem;
+  }
+
+  .md\:mr-5 {
+    margin-right: 1.25rem;
+  }
+
+  .md\:mb-5 {
+    margin-bottom: 1.25rem;
+  }
+
+  .md\:ml-5 {
+    margin-left: 1.25rem;
+  }
+
+  .md\:mt-6 {
+    margin-top: 1.5rem;
+  }
+
+  .md\:mr-6 {
+    margin-right: 1.5rem;
+  }
+
+  .md\:mb-6 {
+    margin-bottom: 1.5rem;
+  }
+
+  .md\:ml-6 {
+    margin-left: 1.5rem;
+  }
+
+  .md\:mt-8 {
+    margin-top: 2rem;
+  }
+
+  .md\:mr-8 {
+    margin-right: 2rem;
+  }
+
+  .md\:mb-8 {
+    margin-bottom: 2rem;
+  }
+
+  .md\:ml-8 {
+    margin-left: 2rem;
+  }
+
+  .md\:mt-10 {
+    margin-top: 2.5rem;
+  }
+
+  .md\:mr-10 {
+    margin-right: 2.5rem;
+  }
+
+  .md\:mb-10 {
+    margin-bottom: 2.5rem;
+  }
+
+  .md\:ml-10 {
+    margin-left: 2.5rem;
+  }
+
+  .md\:mt-12 {
+    margin-top: 3rem;
+  }
+
+  .md\:mr-12 {
+    margin-right: 3rem;
+  }
+
+  .md\:mb-12 {
+    margin-bottom: 3rem;
+  }
+
+  .md\:ml-12 {
+    margin-left: 3rem;
+  }
+
+  .md\:mt-16 {
+    margin-top: 4rem;
+  }
+
+  .md\:mr-16 {
+    margin-right: 4rem;
+  }
+
+  .md\:mb-16 {
+    margin-bottom: 4rem;
+  }
+
+  .md\:ml-16 {
+    margin-left: 4rem;
+  }
+
+  .md\:mt-20 {
+    margin-top: 5rem;
+  }
+
+  .md\:mr-20 {
+    margin-right: 5rem;
+  }
+
+  .md\:mb-20 {
+    margin-bottom: 5rem;
+  }
+
+  .md\:ml-20 {
+    margin-left: 5rem;
+  }
+
+  .md\:mt-24 {
+    margin-top: 6rem;
+  }
+
+  .md\:mr-24 {
+    margin-right: 6rem;
+  }
+
+  .md\:mb-24 {
+    margin-bottom: 6rem;
+  }
+
+  .md\:ml-24 {
+    margin-left: 6rem;
+  }
+
+  .md\:mt-32 {
+    margin-top: 8rem;
+  }
+
+  .md\:mr-32 {
+    margin-right: 8rem;
+  }
+
+  .md\:mb-32 {
+    margin-bottom: 8rem;
+  }
+
+  .md\:ml-32 {
+    margin-left: 8rem;
+  }
+
+  .md\:mt-40 {
+    margin-top: 10rem;
+  }
+
+  .md\:mr-40 {
+    margin-right: 10rem;
+  }
+
+  .md\:mb-40 {
+    margin-bottom: 10rem;
+  }
+
+  .md\:ml-40 {
+    margin-left: 10rem;
+  }
+
+  .md\:mt-48 {
+    margin-top: 12rem;
+  }
+
+  .md\:mr-48 {
+    margin-right: 12rem;
+  }
+
+  .md\:mb-48 {
+    margin-bottom: 12rem;
+  }
+
+  .md\:ml-48 {
+    margin-left: 12rem;
+  }
+
+  .md\:mt-56 {
+    margin-top: 14rem;
+  }
+
+  .md\:mr-56 {
+    margin-right: 14rem;
+  }
+
+  .md\:mb-56 {
+    margin-bottom: 14rem;
+  }
+
+  .md\:ml-56 {
+    margin-left: 14rem;
+  }
+
+  .md\:mt-64 {
+    margin-top: 16rem;
+  }
+
+  .md\:mr-64 {
+    margin-right: 16rem;
+  }
+
+  .md\:mb-64 {
+    margin-bottom: 16rem;
+  }
+
+  .md\:ml-64 {
+    margin-left: 16rem;
+  }
+
+  .md\:mt-auto {
+    margin-top: auto;
+  }
+
+  .md\:mr-auto {
+    margin-right: auto;
+  }
+
+  .md\:mb-auto {
+    margin-bottom: auto;
+  }
+
+  .md\:ml-auto {
+    margin-left: auto;
+  }
+
+  .md\:mt-px {
+    margin-top: 1px;
+  }
+
+  .md\:mr-px {
+    margin-right: 1px;
+  }
+
+  .md\:mb-px {
+    margin-bottom: 1px;
+  }
+
+  .md\:ml-px {
+    margin-left: 1px;
+  }
+
+  .md\:-mt-1 {
+    margin-top: -0.25rem;
+  }
+
+  .md\:-mr-1 {
+    margin-right: -0.25rem;
+  }
+
+  .md\:-mb-1 {
+    margin-bottom: -0.25rem;
+  }
+
+  .md\:-ml-1 {
+    margin-left: -0.25rem;
+  }
+
+  .md\:-mt-2 {
+    margin-top: -0.5rem;
+  }
+
+  .md\:-mr-2 {
+    margin-right: -0.5rem;
+  }
+
+  .md\:-mb-2 {
+    margin-bottom: -0.5rem;
+  }
+
+  .md\:-ml-2 {
+    margin-left: -0.5rem;
+  }
+
+  .md\:-mt-3 {
+    margin-top: -0.75rem;
+  }
+
+  .md\:-mr-3 {
+    margin-right: -0.75rem;
+  }
+
+  .md\:-mb-3 {
+    margin-bottom: -0.75rem;
+  }
+
+  .md\:-ml-3 {
+    margin-left: -0.75rem;
+  }
+
+  .md\:-mt-4 {
+    margin-top: -1rem;
+  }
+
+  .md\:-mr-4 {
+    margin-right: -1rem;
+  }
+
+  .md\:-mb-4 {
+    margin-bottom: -1rem;
+  }
+
+  .md\:-ml-4 {
+    margin-left: -1rem;
+  }
+
+  .md\:-mt-5 {
+    margin-top: -1.25rem;
+  }
+
+  .md\:-mr-5 {
+    margin-right: -1.25rem;
+  }
+
+  .md\:-mb-5 {
+    margin-bottom: -1.25rem;
+  }
+
+  .md\:-ml-5 {
+    margin-left: -1.25rem;
+  }
+
+  .md\:-mt-6 {
+    margin-top: -1.5rem;
+  }
+
+  .md\:-mr-6 {
+    margin-right: -1.5rem;
+  }
+
+  .md\:-mb-6 {
+    margin-bottom: -1.5rem;
+  }
+
+  .md\:-ml-6 {
+    margin-left: -1.5rem;
+  }
+
+  .md\:-mt-8 {
+    margin-top: -2rem;
+  }
+
+  .md\:-mr-8 {
+    margin-right: -2rem;
+  }
+
+  .md\:-mb-8 {
+    margin-bottom: -2rem;
+  }
+
+  .md\:-ml-8 {
+    margin-left: -2rem;
+  }
+
+  .md\:-mt-10 {
+    margin-top: -2.5rem;
+  }
+
+  .md\:-mr-10 {
+    margin-right: -2.5rem;
+  }
+
+  .md\:-mb-10 {
+    margin-bottom: -2.5rem;
+  }
+
+  .md\:-ml-10 {
+    margin-left: -2.5rem;
+  }
+
+  .md\:-mt-12 {
+    margin-top: -3rem;
+  }
+
+  .md\:-mr-12 {
+    margin-right: -3rem;
+  }
+
+  .md\:-mb-12 {
+    margin-bottom: -3rem;
+  }
+
+  .md\:-ml-12 {
+    margin-left: -3rem;
+  }
+
+  .md\:-mt-16 {
+    margin-top: -4rem;
+  }
+
+  .md\:-mr-16 {
+    margin-right: -4rem;
+  }
+
+  .md\:-mb-16 {
+    margin-bottom: -4rem;
+  }
+
+  .md\:-ml-16 {
+    margin-left: -4rem;
+  }
+
+  .md\:-mt-20 {
+    margin-top: -5rem;
+  }
+
+  .md\:-mr-20 {
+    margin-right: -5rem;
+  }
+
+  .md\:-mb-20 {
+    margin-bottom: -5rem;
+  }
+
+  .md\:-ml-20 {
+    margin-left: -5rem;
+  }
+
+  .md\:-mt-24 {
+    margin-top: -6rem;
+  }
+
+  .md\:-mr-24 {
+    margin-right: -6rem;
+  }
+
+  .md\:-mb-24 {
+    margin-bottom: -6rem;
+  }
+
+  .md\:-ml-24 {
+    margin-left: -6rem;
+  }
+
+  .md\:-mt-32 {
+    margin-top: -8rem;
+  }
+
+  .md\:-mr-32 {
+    margin-right: -8rem;
+  }
+
+  .md\:-mb-32 {
+    margin-bottom: -8rem;
+  }
+
+  .md\:-ml-32 {
+    margin-left: -8rem;
+  }
+
+  .md\:-mt-40 {
+    margin-top: -10rem;
+  }
+
+  .md\:-mr-40 {
+    margin-right: -10rem;
+  }
+
+  .md\:-mb-40 {
+    margin-bottom: -10rem;
+  }
+
+  .md\:-ml-40 {
+    margin-left: -10rem;
+  }
+
+  .md\:-mt-48 {
+    margin-top: -12rem;
+  }
+
+  .md\:-mr-48 {
+    margin-right: -12rem;
+  }
+
+  .md\:-mb-48 {
+    margin-bottom: -12rem;
+  }
+
+  .md\:-ml-48 {
+    margin-left: -12rem;
+  }
+
+  .md\:-mt-56 {
+    margin-top: -14rem;
+  }
+
+  .md\:-mr-56 {
+    margin-right: -14rem;
+  }
+
+  .md\:-mb-56 {
+    margin-bottom: -14rem;
+  }
+
+  .md\:-ml-56 {
+    margin-left: -14rem;
+  }
+
+  .md\:-mt-64 {
+    margin-top: -16rem;
+  }
+
+  .md\:-mr-64 {
+    margin-right: -16rem;
+  }
+
+  .md\:-mb-64 {
+    margin-bottom: -16rem;
+  }
+
+  .md\:-ml-64 {
+    margin-left: -16rem;
+  }
+
+  .md\:-mt-px {
+    margin-top: -1px;
+  }
+
+  .md\:-mr-px {
+    margin-right: -1px;
+  }
+
+  .md\:-mb-px {
+    margin-bottom: -1px;
+  }
+
+  .md\:-ml-px {
+    margin-left: -1px;
+  }
+
+  .md\:max-h-full {
+    max-height: 100%;
+  }
+
+  .md\:max-h-screen {
+    max-height: 100vh;
+  }
+
+  .md\:max-w-none {
+    max-width: none;
+  }
+
+  .md\:max-w-xs {
+    max-width: 20rem;
+  }
+
+  .md\:max-w-sm {
+    max-width: 24rem;
+  }
+
+  .md\:max-w-md {
+    max-width: 28rem;
+  }
+
+  .md\:max-w-lg {
+    max-width: 32rem;
+  }
+
+  .md\:max-w-xl {
+    max-width: 36rem;
+  }
+
+  .md\:max-w-2xl {
+    max-width: 42rem;
+  }
+
+  .md\:max-w-3xl {
+    max-width: 48rem;
+  }
+
+  .md\:max-w-4xl {
+    max-width: 56rem;
+  }
+
+  .md\:max-w-5xl {
+    max-width: 64rem;
+  }
+
+  .md\:max-w-6xl {
+    max-width: 72rem;
+  }
+
+  .md\:max-w-full {
+    max-width: 100%;
+  }
+
+  .md\:max-w-screen-sm {
+    max-width: 640px;
+  }
+
+  .md\:max-w-screen-md {
+    max-width: 768px;
+  }
+
+  .md\:max-w-screen-lg {
+    max-width: 1024px;
+  }
+
+  .md\:max-w-screen-xl {
+    max-width: 1280px;
+  }
+
+  .md\:min-h-0 {
+    min-height: 0;
+  }
+
+  .md\:min-h-full {
+    min-height: 100%;
+  }
+
+  .md\:min-h-screen {
+    min-height: 100vh;
+  }
+
+  .md\:min-w-0 {
+    min-width: 0;
+  }
+
+  .md\:min-w-full {
+    min-width: 100%;
+  }
+
+  .md\:object-contain {
+    -o-object-fit: contain;
+       object-fit: contain;
+  }
+
+  .md\:object-cover {
+    -o-object-fit: cover;
+       object-fit: cover;
+  }
+
+  .md\:object-fill {
+    -o-object-fit: fill;
+       object-fit: fill;
+  }
+
+  .md\:object-none {
+    -o-object-fit: none;
+       object-fit: none;
+  }
+
+  .md\:object-scale-down {
+    -o-object-fit: scale-down;
+       object-fit: scale-down;
+  }
+
+  .md\:object-bottom {
+    -o-object-position: bottom;
+       object-position: bottom;
+  }
+
+  .md\:object-center {
+    -o-object-position: center;
+       object-position: center;
+  }
+
+  .md\:object-left {
+    -o-object-position: left;
+       object-position: left;
+  }
+
+  .md\:object-left-bottom {
+    -o-object-position: left bottom;
+       object-position: left bottom;
+  }
+
+  .md\:object-left-top {
+    -o-object-position: left top;
+       object-position: left top;
+  }
+
+  .md\:object-right {
+    -o-object-position: right;
+       object-position: right;
+  }
+
+  .md\:object-right-bottom {
+    -o-object-position: right bottom;
+       object-position: right bottom;
+  }
+
+  .md\:object-right-top {
+    -o-object-position: right top;
+       object-position: right top;
+  }
+
+  .md\:object-top {
+    -o-object-position: top;
+       object-position: top;
+  }
+
+  .md\:opacity-0 {
+    opacity: 0;
+  }
+
+  .md\:opacity-25 {
+    opacity: 0.25;
+  }
+
+  .md\:opacity-50 {
+    opacity: 0.5;
+  }
+
+  .md\:opacity-75 {
+    opacity: 0.75;
+  }
+
+  .md\:opacity-100 {
+    opacity: 1;
+  }
+
+  .md\:hover\:opacity-0:hover {
+    opacity: 0;
+  }
+
+  .md\:hover\:opacity-25:hover {
+    opacity: 0.25;
+  }
+
+  .md\:hover\:opacity-50:hover {
+    opacity: 0.5;
+  }
+
+  .md\:hover\:opacity-75:hover {
+    opacity: 0.75;
+  }
+
+  .md\:hover\:opacity-100:hover {
+    opacity: 1;
+  }
+
+  .md\:focus\:opacity-0:focus {
+    opacity: 0;
+  }
+
+  .md\:focus\:opacity-25:focus {
+    opacity: 0.25;
+  }
+
+  .md\:focus\:opacity-50:focus {
+    opacity: 0.5;
+  }
+
+  .md\:focus\:opacity-75:focus {
+    opacity: 0.75;
+  }
+
+  .md\:focus\:opacity-100:focus {
+    opacity: 1;
+  }
+
+  .md\:outline-none {
+    outline: 0;
+  }
+
+  .md\:focus\:outline-none:focus {
+    outline: 0;
+  }
+
+  .md\:overflow-auto {
+    overflow: auto;
+  }
+
+  .md\:overflow-hidden {
+    overflow: hidden;
+  }
+
+  .md\:overflow-visible {
+    overflow: visible;
+  }
+
+  .md\:overflow-scroll {
+    overflow: scroll;
+  }
+
+  .md\:overflow-x-auto {
+    overflow-x: auto;
+  }
+
+  .md\:overflow-y-auto {
+    overflow-y: auto;
+  }
+
+  .md\:overflow-x-hidden {
+    overflow-x: hidden;
+  }
+
+  .md\:overflow-y-hidden {
+    overflow-y: hidden;
+  }
+
+  .md\:overflow-x-visible {
+    overflow-x: visible;
+  }
+
+  .md\:overflow-y-visible {
+    overflow-y: visible;
+  }
+
+  .md\:overflow-x-scroll {
+    overflow-x: scroll;
+  }
+
+  .md\:overflow-y-scroll {
+    overflow-y: scroll;
+  }
+
+  .md\:scrolling-touch {
+    -webkit-overflow-scrolling: touch;
+  }
+
+  .md\:scrolling-auto {
+    -webkit-overflow-scrolling: auto;
+  }
+
+  .md\:overscroll-auto {
+    -ms-scroll-chaining: chained;
+        overscroll-behavior: auto;
+  }
+
+  .md\:overscroll-contain {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: contain;
+  }
+
+  .md\:overscroll-none {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: none;
+  }
+
+  .md\:overscroll-y-auto {
+    overscroll-behavior-y: auto;
+  }
+
+  .md\:overscroll-y-contain {
+    overscroll-behavior-y: contain;
+  }
+
+  .md\:overscroll-y-none {
+    overscroll-behavior-y: none;
+  }
+
+  .md\:overscroll-x-auto {
+    overscroll-behavior-x: auto;
+  }
+
+  .md\:overscroll-x-contain {
+    overscroll-behavior-x: contain;
+  }
+
+  .md\:overscroll-x-none {
+    overscroll-behavior-x: none;
+  }
+
+  .md\:p-0 {
+    padding: 0;
+  }
+
+  .md\:p-1 {
+    padding: 0.25rem;
+  }
+
+  .md\:p-2 {
+    padding: 0.5rem;
+  }
+
+  .md\:p-3 {
+    padding: 0.75rem;
+  }
+
+  .md\:p-4 {
+    padding: 1rem;
+  }
+
+  .md\:p-5 {
+    padding: 1.25rem;
+  }
+
+  .md\:p-6 {
+    padding: 1.5rem;
+  }
+
+  .md\:p-8 {
+    padding: 2rem;
+  }
+
+  .md\:p-10 {
+    padding: 2.5rem;
+  }
+
+  .md\:p-12 {
+    padding: 3rem;
+  }
+
+  .md\:p-16 {
+    padding: 4rem;
+  }
+
+  .md\:p-20 {
+    padding: 5rem;
+  }
+
+  .md\:p-24 {
+    padding: 6rem;
+  }
+
+  .md\:p-32 {
+    padding: 8rem;
+  }
+
+  .md\:p-40 {
+    padding: 10rem;
+  }
+
+  .md\:p-48 {
+    padding: 12rem;
+  }
+
+  .md\:p-56 {
+    padding: 14rem;
+  }
+
+  .md\:p-64 {
+    padding: 16rem;
+  }
+
+  .md\:p-px {
+    padding: 1px;
+  }
+
+  .md\:py-0 {
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+
+  .md\:px-0 {
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  .md\:py-1 {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+  }
+
+  .md\:px-1 {
+    padding-left: 0.25rem;
+    padding-right: 0.25rem;
+  }
+
+  .md\:py-2 {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
+
+  .md\:px-2 {
+    padding-left: 0.5rem;
+    padding-right: 0.5rem;
+  }
+
+  .md\:py-3 {
+    padding-top: 0.75rem;
+    padding-bottom: 0.75rem;
+  }
+
+  .md\:px-3 {
+    padding-left: 0.75rem;
+    padding-right: 0.75rem;
+  }
+
+  .md\:py-4 {
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+  }
+
+  .md\:px-4 {
+    padding-left: 1rem;
+    padding-right: 1rem;
+  }
+
+  .md\:py-5 {
+    padding-top: 1.25rem;
+    padding-bottom: 1.25rem;
+  }
+
+  .md\:px-5 {
+    padding-left: 1.25rem;
+    padding-right: 1.25rem;
+  }
+
+  .md\:py-6 {
+    padding-top: 1.5rem;
+    padding-bottom: 1.5rem;
+  }
+
+  .md\:px-6 {
+    padding-left: 1.5rem;
+    padding-right: 1.5rem;
+  }
+
+  .md\:py-8 {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
+
+  .md\:px-8 {
+    padding-left: 2rem;
+    padding-right: 2rem;
+  }
+
+  .md\:py-10 {
+    padding-top: 2.5rem;
+    padding-bottom: 2.5rem;
+  }
+
+  .md\:px-10 {
+    padding-left: 2.5rem;
+    padding-right: 2.5rem;
+  }
+
+  .md\:py-12 {
+    padding-top: 3rem;
+    padding-bottom: 3rem;
+  }
+
+  .md\:px-12 {
+    padding-left: 3rem;
+    padding-right: 3rem;
+  }
+
+  .md\:py-16 {
+    padding-top: 4rem;
+    padding-bottom: 4rem;
+  }
+
+  .md\:px-16 {
+    padding-left: 4rem;
+    padding-right: 4rem;
+  }
+
+  .md\:py-20 {
+    padding-top: 5rem;
+    padding-bottom: 5rem;
+  }
+
+  .md\:px-20 {
+    padding-left: 5rem;
+    padding-right: 5rem;
+  }
+
+  .md\:py-24 {
+    padding-top: 6rem;
+    padding-bottom: 6rem;
+  }
+
+  .md\:px-24 {
+    padding-left: 6rem;
+    padding-right: 6rem;
+  }
+
+  .md\:py-32 {
+    padding-top: 8rem;
+    padding-bottom: 8rem;
+  }
+
+  .md\:px-32 {
+    padding-left: 8rem;
+    padding-right: 8rem;
+  }
+
+  .md\:py-40 {
+    padding-top: 10rem;
+    padding-bottom: 10rem;
+  }
+
+  .md\:px-40 {
+    padding-left: 10rem;
+    padding-right: 10rem;
+  }
+
+  .md\:py-48 {
+    padding-top: 12rem;
+    padding-bottom: 12rem;
+  }
+
+  .md\:px-48 {
+    padding-left: 12rem;
+    padding-right: 12rem;
+  }
+
+  .md\:py-56 {
+    padding-top: 14rem;
+    padding-bottom: 14rem;
+  }
+
+  .md\:px-56 {
+    padding-left: 14rem;
+    padding-right: 14rem;
+  }
+
+  .md\:py-64 {
+    padding-top: 16rem;
+    padding-bottom: 16rem;
+  }
+
+  .md\:px-64 {
+    padding-left: 16rem;
+    padding-right: 16rem;
+  }
+
+  .md\:py-px {
+    padding-top: 1px;
+    padding-bottom: 1px;
+  }
+
+  .md\:px-px {
+    padding-left: 1px;
+    padding-right: 1px;
+  }
+
+  .md\:pt-0 {
+    padding-top: 0;
+  }
+
+  .md\:pr-0 {
+    padding-right: 0;
+  }
+
+  .md\:pb-0 {
+    padding-bottom: 0;
+  }
+
+  .md\:pl-0 {
+    padding-left: 0;
+  }
+
+  .md\:pt-1 {
+    padding-top: 0.25rem;
+  }
+
+  .md\:pr-1 {
+    padding-right: 0.25rem;
+  }
+
+  .md\:pb-1 {
+    padding-bottom: 0.25rem;
+  }
+
+  .md\:pl-1 {
+    padding-left: 0.25rem;
+  }
+
+  .md\:pt-2 {
+    padding-top: 0.5rem;
+  }
+
+  .md\:pr-2 {
+    padding-right: 0.5rem;
+  }
+
+  .md\:pb-2 {
+    padding-bottom: 0.5rem;
+  }
+
+  .md\:pl-2 {
+    padding-left: 0.5rem;
+  }
+
+  .md\:pt-3 {
+    padding-top: 0.75rem;
+  }
+
+  .md\:pr-3 {
+    padding-right: 0.75rem;
+  }
+
+  .md\:pb-3 {
+    padding-bottom: 0.75rem;
+  }
+
+  .md\:pl-3 {
+    padding-left: 0.75rem;
+  }
+
+  .md\:pt-4 {
+    padding-top: 1rem;
+  }
+
+  .md\:pr-4 {
+    padding-right: 1rem;
+  }
+
+  .md\:pb-4 {
+    padding-bottom: 1rem;
+  }
+
+  .md\:pl-4 {
+    padding-left: 1rem;
+  }
+
+  .md\:pt-5 {
+    padding-top: 1.25rem;
+  }
+
+  .md\:pr-5 {
+    padding-right: 1.25rem;
+  }
+
+  .md\:pb-5 {
+    padding-bottom: 1.25rem;
+  }
+
+  .md\:pl-5 {
+    padding-left: 1.25rem;
+  }
+
+  .md\:pt-6 {
+    padding-top: 1.5rem;
+  }
+
+  .md\:pr-6 {
+    padding-right: 1.5rem;
+  }
+
+  .md\:pb-6 {
+    padding-bottom: 1.5rem;
+  }
+
+  .md\:pl-6 {
+    padding-left: 1.5rem;
+  }
+
+  .md\:pt-8 {
+    padding-top: 2rem;
+  }
+
+  .md\:pr-8 {
+    padding-right: 2rem;
+  }
+
+  .md\:pb-8 {
+    padding-bottom: 2rem;
+  }
+
+  .md\:pl-8 {
+    padding-left: 2rem;
+  }
+
+  .md\:pt-10 {
+    padding-top: 2.5rem;
+  }
+
+  .md\:pr-10 {
+    padding-right: 2.5rem;
+  }
+
+  .md\:pb-10 {
+    padding-bottom: 2.5rem;
+  }
+
+  .md\:pl-10 {
+    padding-left: 2.5rem;
+  }
+
+  .md\:pt-12 {
+    padding-top: 3rem;
+  }
+
+  .md\:pr-12 {
+    padding-right: 3rem;
+  }
+
+  .md\:pb-12 {
+    padding-bottom: 3rem;
+  }
+
+  .md\:pl-12 {
+    padding-left: 3rem;
+  }
+
+  .md\:pt-16 {
+    padding-top: 4rem;
+  }
+
+  .md\:pr-16 {
+    padding-right: 4rem;
+  }
+
+  .md\:pb-16 {
+    padding-bottom: 4rem;
+  }
+
+  .md\:pl-16 {
+    padding-left: 4rem;
+  }
+
+  .md\:pt-20 {
+    padding-top: 5rem;
+  }
+
+  .md\:pr-20 {
+    padding-right: 5rem;
+  }
+
+  .md\:pb-20 {
+    padding-bottom: 5rem;
+  }
+
+  .md\:pl-20 {
+    padding-left: 5rem;
+  }
+
+  .md\:pt-24 {
+    padding-top: 6rem;
+  }
+
+  .md\:pr-24 {
+    padding-right: 6rem;
+  }
+
+  .md\:pb-24 {
+    padding-bottom: 6rem;
+  }
+
+  .md\:pl-24 {
+    padding-left: 6rem;
+  }
+
+  .md\:pt-32 {
+    padding-top: 8rem;
+  }
+
+  .md\:pr-32 {
+    padding-right: 8rem;
+  }
+
+  .md\:pb-32 {
+    padding-bottom: 8rem;
+  }
+
+  .md\:pl-32 {
+    padding-left: 8rem;
+  }
+
+  .md\:pt-40 {
+    padding-top: 10rem;
+  }
+
+  .md\:pr-40 {
+    padding-right: 10rem;
+  }
+
+  .md\:pb-40 {
+    padding-bottom: 10rem;
+  }
+
+  .md\:pl-40 {
+    padding-left: 10rem;
+  }
+
+  .md\:pt-48 {
+    padding-top: 12rem;
+  }
+
+  .md\:pr-48 {
+    padding-right: 12rem;
+  }
+
+  .md\:pb-48 {
+    padding-bottom: 12rem;
+  }
+
+  .md\:pl-48 {
+    padding-left: 12rem;
+  }
+
+  .md\:pt-56 {
+    padding-top: 14rem;
+  }
+
+  .md\:pr-56 {
+    padding-right: 14rem;
+  }
+
+  .md\:pb-56 {
+    padding-bottom: 14rem;
+  }
+
+  .md\:pl-56 {
+    padding-left: 14rem;
+  }
+
+  .md\:pt-64 {
+    padding-top: 16rem;
+  }
+
+  .md\:pr-64 {
+    padding-right: 16rem;
+  }
+
+  .md\:pb-64 {
+    padding-bottom: 16rem;
+  }
+
+  .md\:pl-64 {
+    padding-left: 16rem;
+  }
+
+  .md\:pt-px {
+    padding-top: 1px;
+  }
+
+  .md\:pr-px {
+    padding-right: 1px;
+  }
+
+  .md\:pb-px {
+    padding-bottom: 1px;
+  }
+
+  .md\:pl-px {
+    padding-left: 1px;
+  }
+
+  .md\:placeholder-transparent::-moz-placeholder {
+    color: transparent;
+  }
+
+  .md\:placeholder-transparent:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .md\:placeholder-transparent::placeholder {
+    color: transparent;
+  }
+
+  .md\:placeholder-current::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .md\:placeholder-current:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .md\:placeholder-current::placeholder {
+    color: currentColor;
+  }
+
+  .md\:placeholder-black::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-black:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-black::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-white::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-white:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-white::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-transparent:focus::-moz-placeholder {
+    color: transparent;
+  }
+
+  .md\:focus\:placeholder-transparent:focus:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .md\:focus\:placeholder-transparent:focus::placeholder {
+    color: transparent;
+  }
+
+  .md\:focus\:placeholder-current:focus::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .md\:focus\:placeholder-current:focus:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .md\:focus\:placeholder-current:focus::placeholder {
+    color: currentColor;
+  }
+
+  .md\:focus\:placeholder-black:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-black:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-black:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-white:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-white:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-white:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-opacity-0::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:placeholder-opacity-0:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:placeholder-opacity-0::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:placeholder-opacity-25::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:placeholder-opacity-25:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:placeholder-opacity-25::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:placeholder-opacity-50::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:placeholder-opacity-50:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:placeholder-opacity-50::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:placeholder-opacity-75::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:placeholder-opacity-75:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:placeholder-opacity-75::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:placeholder-opacity-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:placeholder-opacity-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:placeholder-opacity-100::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:focus\:placeholder-opacity-0:focus::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:focus\:placeholder-opacity-0:focus::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:focus\:placeholder-opacity-25:focus::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:focus\:placeholder-opacity-25:focus::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:focus\:placeholder-opacity-50:focus::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:focus\:placeholder-opacity-50:focus::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:focus\:placeholder-opacity-75:focus::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:focus\:placeholder-opacity-75:focus::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:focus\:placeholder-opacity-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:focus\:placeholder-opacity-100:focus::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:pointer-events-none {
+    pointer-events: none;
+  }
+
+  .md\:pointer-events-auto {
+    pointer-events: auto;
+  }
+
+  .md\:static {
+    position: static;
+  }
+
+  .md\:fixed {
+    position: fixed;
+  }
+
+  .md\:absolute {
+    position: absolute;
+  }
+
+  .md\:relative {
+    position: relative;
+  }
+
+  .md\:sticky {
+    position: -webkit-sticky;
+    position: sticky;
+  }
+
+  .md\:inset-0 {
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+  }
+
+  .md\:inset-auto {
+    top: auto;
+    right: auto;
+    bottom: auto;
+    left: auto;
+  }
+
+  .md\:inset-y-0 {
+    top: 0;
+    bottom: 0;
+  }
+
+  .md\:inset-x-0 {
+    right: 0;
+    left: 0;
+  }
+
+  .md\:inset-y-auto {
+    top: auto;
+    bottom: auto;
+  }
+
+  .md\:inset-x-auto {
+    right: auto;
+    left: auto;
+  }
+
+  .md\:top-0 {
+    top: 0;
+  }
+
+  .md\:right-0 {
+    right: 0;
+  }
+
+  .md\:bottom-0 {
+    bottom: 0;
+  }
+
+  .md\:left-0 {
+    left: 0;
+  }
+
+  .md\:top-auto {
+    top: auto;
+  }
+
+  .md\:right-auto {
+    right: auto;
+  }
+
+  .md\:bottom-auto {
+    bottom: auto;
+  }
+
+  .md\:left-auto {
+    left: auto;
+  }
+
+  .md\:resize-none {
+    resize: none;
+  }
+
+  .md\:resize-y {
+    resize: vertical;
+  }
+
+  .md\:resize-x {
+    resize: horizontal;
+  }
+
+  .md\:resize {
+    resize: both;
+  }
+
+  .md\:shadow-xs {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:shadow-sm {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:shadow {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:shadow-md {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:shadow-lg {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:shadow-xl {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .md\:shadow-2xl {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .md\:shadow-inner {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:shadow-outline {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .md\:shadow-none {
+    box-shadow: none;
+  }
+
+  .md\:hover\:shadow-xs:hover {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:hover\:shadow-sm:hover {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:hover\:shadow:hover {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:hover\:shadow-md:hover {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:hover\:shadow-lg:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:hover\:shadow-xl:hover {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .md\:hover\:shadow-2xl:hover {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .md\:hover\:shadow-inner:hover {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:hover\:shadow-outline:hover {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .md\:hover\:shadow-none:hover {
+    box-shadow: none;
+  }
+
+  .md\:focus\:shadow-xs:focus {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:focus\:shadow-sm:focus {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:focus\:shadow:focus {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:focus\:shadow-md:focus {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:focus\:shadow-lg:focus {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:focus\:shadow-xl:focus {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .md\:focus\:shadow-2xl:focus {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .md\:focus\:shadow-inner:focus {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:focus\:shadow-outline:focus {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .md\:focus\:shadow-none:focus {
+    box-shadow: none;
+  }
+
+  .md\:fill-current {
+    fill: currentColor;
+  }
+
+  .md\:stroke-current {
+    stroke: currentColor;
+  }
+
+  .md\:stroke-0 {
+    stroke-width: 0;
+  }
+
+  .md\:stroke-1 {
+    stroke-width: 1;
+  }
+
+  .md\:stroke-2 {
+    stroke-width: 2;
+  }
+
+  .md\:table-auto {
+    table-layout: auto;
+  }
+
+  .md\:table-fixed {
+    table-layout: fixed;
+  }
+
+  .md\:text-left {
+    text-align: left;
+  }
+
+  .md\:text-center {
+    text-align: center;
+  }
+
+  .md\:text-right {
+    text-align: right;
+  }
+
+  .md\:text-justify {
+    text-align: justify;
+  }
+
+  .md\:text-transparent {
+    color: transparent;
+  }
+
+  .md\:text-current {
+    color: currentColor;
+  }
+
+  .md\:text-black {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .md\:text-white {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .md\:text-gray-100 {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .md\:text-gray-200 {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .md\:text-gray-300 {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .md\:text-gray-400 {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .md\:text-gray-500 {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .md\:text-gray-600 {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .md\:text-gray-700 {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .md\:text-gray-800 {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .md\:text-gray-900 {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .md\:text-red-100 {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .md\:text-red-200 {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .md\:text-red-300 {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .md\:text-red-400 {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .md\:text-red-500 {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .md\:text-red-600 {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .md\:text-red-700 {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .md\:text-red-800 {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .md\:text-red-900 {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .md\:text-orange-100 {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .md\:text-orange-200 {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .md\:text-orange-300 {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .md\:text-orange-400 {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .md\:text-orange-500 {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .md\:text-orange-600 {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .md\:text-orange-700 {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .md\:text-orange-800 {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .md\:text-orange-900 {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .md\:text-yellow-100 {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .md\:text-yellow-200 {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .md\:text-yellow-300 {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .md\:text-yellow-400 {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .md\:text-yellow-500 {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .md\:text-yellow-600 {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .md\:text-yellow-700 {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .md\:text-yellow-800 {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .md\:text-yellow-900 {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .md\:text-green-100 {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .md\:text-green-200 {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .md\:text-green-300 {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .md\:text-green-400 {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .md\:text-green-500 {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .md\:text-green-600 {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .md\:text-green-700 {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .md\:text-green-800 {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .md\:text-green-900 {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .md\:text-teal-100 {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .md\:text-teal-200 {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .md\:text-teal-300 {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .md\:text-teal-400 {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .md\:text-teal-500 {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .md\:text-teal-600 {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .md\:text-teal-700 {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .md\:text-teal-800 {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .md\:text-teal-900 {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .md\:text-blue-100 {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .md\:text-blue-200 {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .md\:text-blue-300 {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .md\:text-blue-400 {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .md\:text-blue-500 {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .md\:text-blue-600 {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .md\:text-blue-700 {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .md\:text-blue-800 {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .md\:text-blue-900 {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .md\:text-indigo-100 {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .md\:text-indigo-200 {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .md\:text-indigo-300 {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .md\:text-indigo-400 {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .md\:text-indigo-500 {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .md\:text-indigo-600 {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .md\:text-indigo-700 {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .md\:text-indigo-800 {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .md\:text-indigo-900 {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .md\:text-purple-100 {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .md\:text-purple-200 {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .md\:text-purple-300 {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .md\:text-purple-400 {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .md\:text-purple-500 {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .md\:text-purple-600 {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .md\:text-purple-700 {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .md\:text-purple-800 {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .md\:text-purple-900 {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .md\:text-pink-100 {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .md\:text-pink-200 {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .md\:text-pink-300 {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .md\:text-pink-400 {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .md\:text-pink-500 {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .md\:text-pink-600 {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .md\:text-pink-700 {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .md\:text-pink-800 {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .md\:text-pink-900 {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .md\:hover\:text-transparent:hover {
+    color: transparent;
+  }
+
+  .md\:hover\:text-current:hover {
+    color: currentColor;
+  }
+
+  .md\:hover\:text-black:hover {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .md\:hover\:text-white:hover {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-100:hover {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-200:hover {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-300:hover {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-400:hover {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-500:hover {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-600:hover {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-700:hover {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-800:hover {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-900:hover {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-100:hover {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-200:hover {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-300:hover {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-400:hover {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-500:hover {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-600:hover {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-700:hover {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-800:hover {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-900:hover {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-100:hover {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-200:hover {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-300:hover {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-400:hover {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-500:hover {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-600:hover {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-700:hover {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-800:hover {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-900:hover {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-100:hover {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-200:hover {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-300:hover {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-400:hover {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-500:hover {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-600:hover {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-700:hover {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-800:hover {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-900:hover {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-100:hover {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-200:hover {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-300:hover {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-400:hover {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-500:hover {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-600:hover {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-700:hover {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-800:hover {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-900:hover {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-100:hover {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-200:hover {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-300:hover {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-400:hover {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-500:hover {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-600:hover {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-700:hover {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-800:hover {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-900:hover {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-100:hover {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-200:hover {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-300:hover {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-400:hover {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-500:hover {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-600:hover {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-700:hover {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-800:hover {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-900:hover {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-100:hover {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-200:hover {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-300:hover {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-400:hover {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-500:hover {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-600:hover {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-700:hover {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-800:hover {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-900:hover {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-100:hover {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-200:hover {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-300:hover {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-400:hover {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-500:hover {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-600:hover {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-700:hover {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-800:hover {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-900:hover {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-100:hover {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-200:hover {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-300:hover {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-400:hover {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-500:hover {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-600:hover {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-700:hover {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-800:hover {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-900:hover {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .md\:focus\:text-transparent:focus {
+    color: transparent;
+  }
+
+  .md\:focus\:text-current:focus {
+    color: currentColor;
+  }
+
+  .md\:focus\:text-black:focus {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .md\:focus\:text-white:focus {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-100:focus {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-200:focus {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-300:focus {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-400:focus {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-500:focus {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-600:focus {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-700:focus {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-800:focus {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-900:focus {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-100:focus {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-200:focus {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-300:focus {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-400:focus {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-500:focus {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-600:focus {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-700:focus {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-800:focus {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-900:focus {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-100:focus {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-200:focus {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-300:focus {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-400:focus {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-500:focus {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-600:focus {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-700:focus {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-800:focus {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-900:focus {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-100:focus {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-200:focus {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-300:focus {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-400:focus {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-500:focus {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-600:focus {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-700:focus {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-800:focus {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-900:focus {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-100:focus {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-200:focus {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-300:focus {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-400:focus {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-500:focus {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-600:focus {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-700:focus {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-800:focus {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-900:focus {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-100:focus {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-200:focus {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-300:focus {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-400:focus {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-500:focus {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-600:focus {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-700:focus {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-800:focus {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-900:focus {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-100:focus {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-200:focus {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-300:focus {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-400:focus {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-500:focus {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-600:focus {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-700:focus {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-800:focus {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-900:focus {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-100:focus {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-200:focus {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-300:focus {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-400:focus {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-500:focus {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-600:focus {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-700:focus {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-800:focus {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-900:focus {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-100:focus {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-200:focus {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-300:focus {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-400:focus {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-500:focus {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-600:focus {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-700:focus {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-800:focus {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-900:focus {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-100:focus {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-200:focus {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-300:focus {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-400:focus {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-500:focus {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-600:focus {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-700:focus {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-800:focus {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-900:focus {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .md\:text-opacity-0 {
+    --text-opacity: 0;
+  }
+
+  .md\:text-opacity-25 {
+    --text-opacity: 0.25;
+  }
+
+  .md\:text-opacity-50 {
+    --text-opacity: 0.5;
+  }
+
+  .md\:text-opacity-75 {
+    --text-opacity: 0.75;
+  }
+
+  .md\:text-opacity-100 {
+    --text-opacity: 1;
+  }
+
+  .md\:hover\:text-opacity-0:hover {
+    --text-opacity: 0;
+  }
+
+  .md\:hover\:text-opacity-25:hover {
+    --text-opacity: 0.25;
+  }
+
+  .md\:hover\:text-opacity-50:hover {
+    --text-opacity: 0.5;
+  }
+
+  .md\:hover\:text-opacity-75:hover {
+    --text-opacity: 0.75;
+  }
+
+  .md\:hover\:text-opacity-100:hover {
+    --text-opacity: 1;
+  }
+
+  .md\:focus\:text-opacity-0:focus {
+    --text-opacity: 0;
+  }
+
+  .md\:focus\:text-opacity-25:focus {
+    --text-opacity: 0.25;
+  }
+
+  .md\:focus\:text-opacity-50:focus {
+    --text-opacity: 0.5;
+  }
+
+  .md\:focus\:text-opacity-75:focus {
+    --text-opacity: 0.75;
+  }
+
+  .md\:focus\:text-opacity-100:focus {
+    --text-opacity: 1;
+  }
+
+  .md\:italic {
+    font-style: italic;
+  }
+
+  .md\:not-italic {
+    font-style: normal;
+  }
+
+  .md\:uppercase {
+    text-transform: uppercase;
+  }
+
+  .md\:lowercase {
+    text-transform: lowercase;
+  }
+
+  .md\:capitalize {
+    text-transform: capitalize;
+  }
+
+  .md\:normal-case {
+    text-transform: none;
+  }
+
+  .md\:underline {
+    text-decoration: underline;
+  }
+
+  .md\:line-through {
+    text-decoration: line-through;
+  }
+
+  .md\:no-underline {
+    text-decoration: none;
+  }
+
+  .md\:hover\:underline:hover {
+    text-decoration: underline;
+  }
+
+  .md\:hover\:line-through:hover {
+    text-decoration: line-through;
+  }
+
+  .md\:hover\:no-underline:hover {
+    text-decoration: none;
+  }
+
+  .md\:focus\:underline:focus {
+    text-decoration: underline;
+  }
+
+  .md\:focus\:line-through:focus {
+    text-decoration: line-through;
+  }
+
+  .md\:focus\:no-underline:focus {
+    text-decoration: none;
+  }
+
+  .md\:antialiased {
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+
+  .md\:subpixel-antialiased {
+    -webkit-font-smoothing: auto;
+    -moz-osx-font-smoothing: auto;
+  }
+
+  .md\:ordinal, .md\:slashed-zero, .md\:lining-nums, .md\:oldstyle-nums, .md\:proportional-nums, .md\:tabular-nums, .md\:diagonal-fractions, .md\:stacked-fractions {
+    --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+    font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+  }
+
+  .md\:normal-nums {
+    font-variant-numeric: normal;
+  }
+
+  .md\:ordinal {
+    --font-variant-numeric-ordinal: ordinal;
+  }
+
+  .md\:slashed-zero {
+    --font-variant-numeric-slashed-zero: slashed-zero;
+  }
+
+  .md\:lining-nums {
+    --font-variant-numeric-figure: lining-nums;
+  }
+
+  .md\:oldstyle-nums {
+    --font-variant-numeric-figure: oldstyle-nums;
+  }
+
+  .md\:proportional-nums {
+    --font-variant-numeric-spacing: proportional-nums;
+  }
+
+  .md\:tabular-nums {
+    --font-variant-numeric-spacing: tabular-nums;
+  }
+
+  .md\:diagonal-fractions {
+    --font-variant-numeric-fraction: diagonal-fractions;
+  }
+
+  .md\:stacked-fractions {
+    --font-variant-numeric-fraction: stacked-fractions;
+  }
+
+  .md\:tracking-tighter {
+    letter-spacing: -0.05em;
+  }
+
+  .md\:tracking-tight {
+    letter-spacing: -0.025em;
+  }
+
+  .md\:tracking-normal {
+    letter-spacing: 0;
+  }
+
+  .md\:tracking-wide {
+    letter-spacing: 0.025em;
+  }
+
+  .md\:tracking-wider {
+    letter-spacing: 0.05em;
+  }
+
+  .md\:tracking-widest {
+    letter-spacing: 0.1em;
+  }
+
+  .md\:select-none {
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+  }
+
+  .md\:select-text {
+    -webkit-user-select: text;
+       -moz-user-select: text;
+        -ms-user-select: text;
+            user-select: text;
+  }
+
+  .md\:select-all {
+    -webkit-user-select: all;
+       -moz-user-select: all;
+        -ms-user-select: all;
+            user-select: all;
+  }
+
+  .md\:select-auto {
+    -webkit-user-select: auto;
+       -moz-user-select: auto;
+        -ms-user-select: auto;
+            user-select: auto;
+  }
+
+  .md\:align-baseline {
+    vertical-align: baseline;
+  }
+
+  .md\:align-top {
+    vertical-align: top;
+  }
+
+  .md\:align-middle {
+    vertical-align: middle;
+  }
+
+  .md\:align-bottom {
+    vertical-align: bottom;
+  }
+
+  .md\:align-text-top {
+    vertical-align: text-top;
+  }
+
+  .md\:align-text-bottom {
+    vertical-align: text-bottom;
+  }
+
+  .md\:visible {
+    visibility: visible;
+  }
+
+  .md\:invisible {
+    visibility: hidden;
+  }
+
+  .md\:whitespace-normal {
+    white-space: normal;
+  }
+
+  .md\:whitespace-no-wrap {
+    white-space: nowrap;
+  }
+
+  .md\:whitespace-pre {
+    white-space: pre;
+  }
+
+  .md\:whitespace-pre-line {
+    white-space: pre-line;
+  }
+
+  .md\:whitespace-pre-wrap {
+    white-space: pre-wrap;
+  }
+
+  .md\:break-normal {
+    overflow-wrap: normal;
+    word-break: normal;
+  }
+
+  .md\:break-words {
+    overflow-wrap: break-word;
+  }
+
+  .md\:break-all {
+    word-break: break-all;
+  }
+
+  .md\:truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .md\:w-0 {
+    width: 0;
+  }
+
+  .md\:w-1 {
+    width: 0.25rem;
+  }
+
+  .md\:w-2 {
+    width: 0.5rem;
+  }
+
+  .md\:w-3 {
+    width: 0.75rem;
+  }
+
+  .md\:w-4 {
+    width: 1rem;
+  }
+
+  .md\:w-5 {
+    width: 1.25rem;
+  }
+
+  .md\:w-6 {
+    width: 1.5rem;
+  }
+
+  .md\:w-8 {
+    width: 2rem;
+  }
+
+  .md\:w-10 {
+    width: 2.5rem;
+  }
+
+  .md\:w-12 {
+    width: 3rem;
+  }
+
+  .md\:w-16 {
+    width: 4rem;
+  }
+
+  .md\:w-20 {
+    width: 5rem;
+  }
+
+  .md\:w-24 {
+    width: 6rem;
+  }
+
+  .md\:w-32 {
+    width: 8rem;
+  }
+
+  .md\:w-40 {
+    width: 10rem;
+  }
+
+  .md\:w-48 {
+    width: 12rem;
+  }
+
+  .md\:w-56 {
+    width: 14rem;
+  }
+
+  .md\:w-64 {
+    width: 16rem;
+  }
+
+  .md\:w-auto {
+    width: auto;
+  }
+
+  .md\:w-px {
+    width: 1px;
+  }
+
+  .md\:w-1\/2 {
+    width: 50%;
+  }
+
+  .md\:w-1\/3 {
+    width: 33.333333%;
+  }
+
+  .md\:w-2\/3 {
+    width: 66.666667%;
+  }
+
+  .md\:w-1\/4 {
+    width: 25%;
+  }
+
+  .md\:w-2\/4 {
+    width: 50%;
+  }
+
+  .md\:w-3\/4 {
+    width: 75%;
+  }
+
+  .md\:w-1\/5 {
+    width: 20%;
+  }
+
+  .md\:w-2\/5 {
+    width: 40%;
+  }
+
+  .md\:w-3\/5 {
+    width: 60%;
+  }
+
+  .md\:w-4\/5 {
+    width: 80%;
+  }
+
+  .md\:w-1\/6 {
+    width: 16.666667%;
+  }
+
+  .md\:w-2\/6 {
+    width: 33.333333%;
+  }
+
+  .md\:w-3\/6 {
+    width: 50%;
+  }
+
+  .md\:w-4\/6 {
+    width: 66.666667%;
+  }
+
+  .md\:w-5\/6 {
+    width: 83.333333%;
+  }
+
+  .md\:w-1\/12 {
+    width: 8.333333%;
+  }
+
+  .md\:w-2\/12 {
+    width: 16.666667%;
+  }
+
+  .md\:w-3\/12 {
+    width: 25%;
+  }
+
+  .md\:w-4\/12 {
+    width: 33.333333%;
+  }
+
+  .md\:w-5\/12 {
+    width: 41.666667%;
+  }
+
+  .md\:w-6\/12 {
+    width: 50%;
+  }
+
+  .md\:w-7\/12 {
+    width: 58.333333%;
+  }
+
+  .md\:w-8\/12 {
+    width: 66.666667%;
+  }
+
+  .md\:w-9\/12 {
+    width: 75%;
+  }
+
+  .md\:w-10\/12 {
+    width: 83.333333%;
+  }
+
+  .md\:w-11\/12 {
+    width: 91.666667%;
+  }
+
+  .md\:w-full {
+    width: 100%;
+  }
+
+  .md\:w-screen {
+    width: 100vw;
+  }
+
+  .md\:z-0 {
+    z-index: 0;
+  }
+
+  .md\:z-10 {
+    z-index: 10;
+  }
+
+  .md\:z-20 {
+    z-index: 20;
+  }
+
+  .md\:z-30 {
+    z-index: 30;
+  }
+
+  .md\:z-40 {
+    z-index: 40;
+  }
+
+  .md\:z-50 {
+    z-index: 50;
+  }
+
+  .md\:z-auto {
+    z-index: auto;
+  }
+
+  .md\:gap-0 {
+    grid-gap: 0;
+    gap: 0;
+  }
+
+  .md\:gap-1 {
+    grid-gap: 0.25rem;
+    gap: 0.25rem;
+  }
+
+  .md\:gap-2 {
+    grid-gap: 0.5rem;
+    gap: 0.5rem;
+  }
+
+  .md\:gap-3 {
+    grid-gap: 0.75rem;
+    gap: 0.75rem;
+  }
+
+  .md\:gap-4 {
+    grid-gap: 1rem;
+    gap: 1rem;
+  }
+
+  .md\:gap-5 {
+    grid-gap: 1.25rem;
+    gap: 1.25rem;
+  }
+
+  .md\:gap-6 {
+    grid-gap: 1.5rem;
+    gap: 1.5rem;
+  }
+
+  .md\:gap-8 {
+    grid-gap: 2rem;
+    gap: 2rem;
+  }
+
+  .md\:gap-10 {
+    grid-gap: 2.5rem;
+    gap: 2.5rem;
+  }
+
+  .md\:gap-12 {
+    grid-gap: 3rem;
+    gap: 3rem;
+  }
+
+  .md\:gap-16 {
+    grid-gap: 4rem;
+    gap: 4rem;
+  }
+
+  .md\:gap-20 {
+    grid-gap: 5rem;
+    gap: 5rem;
+  }
+
+  .md\:gap-24 {
+    grid-gap: 6rem;
+    gap: 6rem;
+  }
+
+  .md\:gap-32 {
+    grid-gap: 8rem;
+    gap: 8rem;
+  }
+
+  .md\:gap-40 {
+    grid-gap: 10rem;
+    gap: 10rem;
+  }
+
+  .md\:gap-48 {
+    grid-gap: 12rem;
+    gap: 12rem;
+  }
+
+  .md\:gap-56 {
+    grid-gap: 14rem;
+    gap: 14rem;
+  }
+
+  .md\:gap-64 {
+    grid-gap: 16rem;
+    gap: 16rem;
+  }
+
+  .md\:gap-px {
+    grid-gap: 1px;
+    gap: 1px;
+  }
+
+  .md\:col-gap-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .md\:col-gap-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .md\:col-gap-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .md\:col-gap-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .md\:col-gap-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .md\:col-gap-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .md\:col-gap-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .md\:col-gap-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .md\:col-gap-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .md\:col-gap-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .md\:col-gap-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .md\:col-gap-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .md\:col-gap-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .md\:col-gap-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .md\:col-gap-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .md\:col-gap-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .md\:col-gap-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .md\:col-gap-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .md\:col-gap-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .md\:gap-x-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .md\:gap-x-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .md\:gap-x-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .md\:gap-x-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .md\:gap-x-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .md\:gap-x-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .md\:gap-x-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .md\:gap-x-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .md\:gap-x-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .md\:gap-x-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .md\:gap-x-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .md\:gap-x-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .md\:gap-x-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .md\:gap-x-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .md\:gap-x-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .md\:gap-x-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .md\:gap-x-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .md\:gap-x-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .md\:gap-x-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .md\:row-gap-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .md\:row-gap-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .md\:row-gap-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .md\:row-gap-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .md\:row-gap-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .md\:row-gap-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .md\:row-gap-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .md\:row-gap-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .md\:row-gap-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .md\:row-gap-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .md\:row-gap-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .md\:row-gap-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .md\:row-gap-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .md\:row-gap-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .md\:row-gap-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .md\:row-gap-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .md\:row-gap-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .md\:row-gap-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .md\:row-gap-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .md\:gap-y-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .md\:gap-y-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .md\:gap-y-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .md\:gap-y-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .md\:gap-y-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .md\:gap-y-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .md\:gap-y-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .md\:gap-y-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .md\:gap-y-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .md\:gap-y-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .md\:gap-y-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .md\:gap-y-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .md\:gap-y-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .md\:gap-y-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .md\:gap-y-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .md\:gap-y-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .md\:gap-y-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .md\:gap-y-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .md\:gap-y-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .md\:grid-flow-row {
+    grid-auto-flow: row;
+  }
+
+  .md\:grid-flow-col {
+    grid-auto-flow: column;
+  }
+
+  .md\:grid-flow-row-dense {
+    grid-auto-flow: row dense;
+  }
+
+  .md\:grid-flow-col-dense {
+    grid-auto-flow: column dense;
+  }
+
+  .md\:grid-cols-1 {
+    grid-template-columns: repeat(1, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-2 {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-3 {
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-4 {
+    grid-template-columns: repeat(4, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-5 {
+    grid-template-columns: repeat(5, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-6 {
+    grid-template-columns: repeat(6, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-7 {
+    grid-template-columns: repeat(7, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-8 {
+    grid-template-columns: repeat(8, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-9 {
+    grid-template-columns: repeat(9, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-10 {
+    grid-template-columns: repeat(10, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-11 {
+    grid-template-columns: repeat(11, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-12 {
+    grid-template-columns: repeat(12, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-none {
+    grid-template-columns: none;
+  }
+
+  .md\:col-auto {
+    grid-column: auto;
+  }
+
+  .md\:col-span-1 {
+    grid-column: span 1 / span 1;
+  }
+
+  .md\:col-span-2 {
+    grid-column: span 2 / span 2;
+  }
+
+  .md\:col-span-3 {
+    grid-column: span 3 / span 3;
+  }
+
+  .md\:col-span-4 {
+    grid-column: span 4 / span 4;
+  }
+
+  .md\:col-span-5 {
+    grid-column: span 5 / span 5;
+  }
+
+  .md\:col-span-6 {
+    grid-column: span 6 / span 6;
+  }
+
+  .md\:col-span-7 {
+    grid-column: span 7 / span 7;
+  }
+
+  .md\:col-span-8 {
+    grid-column: span 8 / span 8;
+  }
+
+  .md\:col-span-9 {
+    grid-column: span 9 / span 9;
+  }
+
+  .md\:col-span-10 {
+    grid-column: span 10 / span 10;
+  }
+
+  .md\:col-span-11 {
+    grid-column: span 11 / span 11;
+  }
+
+  .md\:col-span-12 {
+    grid-column: span 12 / span 12;
+  }
+
+  .md\:col-start-1 {
+    grid-column-start: 1;
+  }
+
+  .md\:col-start-2 {
+    grid-column-start: 2;
+  }
+
+  .md\:col-start-3 {
+    grid-column-start: 3;
+  }
+
+  .md\:col-start-4 {
+    grid-column-start: 4;
+  }
+
+  .md\:col-start-5 {
+    grid-column-start: 5;
+  }
+
+  .md\:col-start-6 {
+    grid-column-start: 6;
+  }
+
+  .md\:col-start-7 {
+    grid-column-start: 7;
+  }
+
+  .md\:col-start-8 {
+    grid-column-start: 8;
+  }
+
+  .md\:col-start-9 {
+    grid-column-start: 9;
+  }
+
+  .md\:col-start-10 {
+    grid-column-start: 10;
+  }
+
+  .md\:col-start-11 {
+    grid-column-start: 11;
+  }
+
+  .md\:col-start-12 {
+    grid-column-start: 12;
+  }
+
+  .md\:col-start-13 {
+    grid-column-start: 13;
+  }
+
+  .md\:col-start-auto {
+    grid-column-start: auto;
+  }
+
+  .md\:col-end-1 {
+    grid-column-end: 1;
+  }
+
+  .md\:col-end-2 {
+    grid-column-end: 2;
+  }
+
+  .md\:col-end-3 {
+    grid-column-end: 3;
+  }
+
+  .md\:col-end-4 {
+    grid-column-end: 4;
+  }
+
+  .md\:col-end-5 {
+    grid-column-end: 5;
+  }
+
+  .md\:col-end-6 {
+    grid-column-end: 6;
+  }
+
+  .md\:col-end-7 {
+    grid-column-end: 7;
+  }
+
+  .md\:col-end-8 {
+    grid-column-end: 8;
+  }
+
+  .md\:col-end-9 {
+    grid-column-end: 9;
+  }
+
+  .md\:col-end-10 {
+    grid-column-end: 10;
+  }
+
+  .md\:col-end-11 {
+    grid-column-end: 11;
+  }
+
+  .md\:col-end-12 {
+    grid-column-end: 12;
+  }
+
+  .md\:col-end-13 {
+    grid-column-end: 13;
+  }
+
+  .md\:col-end-auto {
+    grid-column-end: auto;
+  }
+
+  .md\:grid-rows-1 {
+    grid-template-rows: repeat(1, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-2 {
+    grid-template-rows: repeat(2, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-3 {
+    grid-template-rows: repeat(3, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-4 {
+    grid-template-rows: repeat(4, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-5 {
+    grid-template-rows: repeat(5, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-6 {
+    grid-template-rows: repeat(6, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-none {
+    grid-template-rows: none;
+  }
+
+  .md\:row-auto {
+    grid-row: auto;
+  }
+
+  .md\:row-span-1 {
+    grid-row: span 1 / span 1;
+  }
+
+  .md\:row-span-2 {
+    grid-row: span 2 / span 2;
+  }
+
+  .md\:row-span-3 {
+    grid-row: span 3 / span 3;
+  }
+
+  .md\:row-span-4 {
+    grid-row: span 4 / span 4;
+  }
+
+  .md\:row-span-5 {
+    grid-row: span 5 / span 5;
+  }
+
+  .md\:row-span-6 {
+    grid-row: span 6 / span 6;
+  }
+
+  .md\:row-start-1 {
+    grid-row-start: 1;
+  }
+
+  .md\:row-start-2 {
+    grid-row-start: 2;
+  }
+
+  .md\:row-start-3 {
+    grid-row-start: 3;
+  }
+
+  .md\:row-start-4 {
+    grid-row-start: 4;
+  }
+
+  .md\:row-start-5 {
+    grid-row-start: 5;
+  }
+
+  .md\:row-start-6 {
+    grid-row-start: 6;
+  }
+
+  .md\:row-start-7 {
+    grid-row-start: 7;
+  }
+
+  .md\:row-start-auto {
+    grid-row-start: auto;
+  }
+
+  .md\:row-end-1 {
+    grid-row-end: 1;
+  }
+
+  .md\:row-end-2 {
+    grid-row-end: 2;
+  }
+
+  .md\:row-end-3 {
+    grid-row-end: 3;
+  }
+
+  .md\:row-end-4 {
+    grid-row-end: 4;
+  }
+
+  .md\:row-end-5 {
+    grid-row-end: 5;
+  }
+
+  .md\:row-end-6 {
+    grid-row-end: 6;
+  }
+
+  .md\:row-end-7 {
+    grid-row-end: 7;
+  }
+
+  .md\:row-end-auto {
+    grid-row-end: auto;
+  }
+
+  .md\:transform {
+    --transform-translate-x: 0;
+    --transform-translate-y: 0;
+    --transform-rotate: 0;
+    --transform-skew-x: 0;
+    --transform-skew-y: 0;
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+    transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+  }
+
+  .md\:transform-none {
+    transform: none;
+  }
+
+  .md\:origin-center {
+    transform-origin: center;
+  }
+
+  .md\:origin-top {
+    transform-origin: top;
+  }
+
+  .md\:origin-top-right {
+    transform-origin: top right;
+  }
+
+  .md\:origin-right {
+    transform-origin: right;
+  }
+
+  .md\:origin-bottom-right {
+    transform-origin: bottom right;
+  }
+
+  .md\:origin-bottom {
+    transform-origin: bottom;
+  }
+
+  .md\:origin-bottom-left {
+    transform-origin: bottom left;
+  }
+
+  .md\:origin-left {
+    transform-origin: left;
+  }
+
+  .md\:origin-top-left {
+    transform-origin: top left;
+  }
+
+  .md\:scale-0 {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .md\:scale-50 {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .md\:scale-75 {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .md\:scale-90 {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .md\:scale-95 {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .md\:scale-100 {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .md\:scale-105 {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:scale-110 {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:scale-125 {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:scale-150 {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:scale-x-0 {
+    --transform-scale-x: 0;
+  }
+
+  .md\:scale-x-50 {
+    --transform-scale-x: .5;
+  }
+
+  .md\:scale-x-75 {
+    --transform-scale-x: .75;
+  }
+
+  .md\:scale-x-90 {
+    --transform-scale-x: .9;
+  }
+
+  .md\:scale-x-95 {
+    --transform-scale-x: .95;
+  }
+
+  .md\:scale-x-100 {
+    --transform-scale-x: 1;
+  }
+
+  .md\:scale-x-105 {
+    --transform-scale-x: 1.05;
+  }
+
+  .md\:scale-x-110 {
+    --transform-scale-x: 1.1;
+  }
+
+  .md\:scale-x-125 {
+    --transform-scale-x: 1.25;
+  }
+
+  .md\:scale-x-150 {
+    --transform-scale-x: 1.5;
+  }
+
+  .md\:scale-y-0 {
+    --transform-scale-y: 0;
+  }
+
+  .md\:scale-y-50 {
+    --transform-scale-y: .5;
+  }
+
+  .md\:scale-y-75 {
+    --transform-scale-y: .75;
+  }
+
+  .md\:scale-y-90 {
+    --transform-scale-y: .9;
+  }
+
+  .md\:scale-y-95 {
+    --transform-scale-y: .95;
+  }
+
+  .md\:scale-y-100 {
+    --transform-scale-y: 1;
+  }
+
+  .md\:scale-y-105 {
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:scale-y-110 {
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:scale-y-125 {
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:scale-y-150 {
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:hover\:scale-0:hover {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .md\:hover\:scale-50:hover {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .md\:hover\:scale-75:hover {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .md\:hover\:scale-90:hover {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .md\:hover\:scale-95:hover {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .md\:hover\:scale-100:hover {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .md\:hover\:scale-105:hover {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:hover\:scale-110:hover {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:hover\:scale-125:hover {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:hover\:scale-150:hover {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:hover\:scale-x-0:hover {
+    --transform-scale-x: 0;
+  }
+
+  .md\:hover\:scale-x-50:hover {
+    --transform-scale-x: .5;
+  }
+
+  .md\:hover\:scale-x-75:hover {
+    --transform-scale-x: .75;
+  }
+
+  .md\:hover\:scale-x-90:hover {
+    --transform-scale-x: .9;
+  }
+
+  .md\:hover\:scale-x-95:hover {
+    --transform-scale-x: .95;
+  }
+
+  .md\:hover\:scale-x-100:hover {
+    --transform-scale-x: 1;
+  }
+
+  .md\:hover\:scale-x-105:hover {
+    --transform-scale-x: 1.05;
+  }
+
+  .md\:hover\:scale-x-110:hover {
+    --transform-scale-x: 1.1;
+  }
+
+  .md\:hover\:scale-x-125:hover {
+    --transform-scale-x: 1.25;
+  }
+
+  .md\:hover\:scale-x-150:hover {
+    --transform-scale-x: 1.5;
+  }
+
+  .md\:hover\:scale-y-0:hover {
+    --transform-scale-y: 0;
+  }
+
+  .md\:hover\:scale-y-50:hover {
+    --transform-scale-y: .5;
+  }
+
+  .md\:hover\:scale-y-75:hover {
+    --transform-scale-y: .75;
+  }
+
+  .md\:hover\:scale-y-90:hover {
+    --transform-scale-y: .9;
+  }
+
+  .md\:hover\:scale-y-95:hover {
+    --transform-scale-y: .95;
+  }
+
+  .md\:hover\:scale-y-100:hover {
+    --transform-scale-y: 1;
+  }
+
+  .md\:hover\:scale-y-105:hover {
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:hover\:scale-y-110:hover {
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:hover\:scale-y-125:hover {
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:hover\:scale-y-150:hover {
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:focus\:scale-0:focus {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .md\:focus\:scale-50:focus {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .md\:focus\:scale-75:focus {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .md\:focus\:scale-90:focus {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .md\:focus\:scale-95:focus {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .md\:focus\:scale-100:focus {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .md\:focus\:scale-105:focus {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:focus\:scale-110:focus {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:focus\:scale-125:focus {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:focus\:scale-150:focus {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:focus\:scale-x-0:focus {
+    --transform-scale-x: 0;
+  }
+
+  .md\:focus\:scale-x-50:focus {
+    --transform-scale-x: .5;
+  }
+
+  .md\:focus\:scale-x-75:focus {
+    --transform-scale-x: .75;
+  }
+
+  .md\:focus\:scale-x-90:focus {
+    --transform-scale-x: .9;
+  }
+
+  .md\:focus\:scale-x-95:focus {
+    --transform-scale-x: .95;
+  }
+
+  .md\:focus\:scale-x-100:focus {
+    --transform-scale-x: 1;
+  }
+
+  .md\:focus\:scale-x-105:focus {
+    --transform-scale-x: 1.05;
+  }
+
+  .md\:focus\:scale-x-110:focus {
+    --transform-scale-x: 1.1;
+  }
+
+  .md\:focus\:scale-x-125:focus {
+    --transform-scale-x: 1.25;
+  }
+
+  .md\:focus\:scale-x-150:focus {
+    --transform-scale-x: 1.5;
+  }
+
+  .md\:focus\:scale-y-0:focus {
+    --transform-scale-y: 0;
+  }
+
+  .md\:focus\:scale-y-50:focus {
+    --transform-scale-y: .5;
+  }
+
+  .md\:focus\:scale-y-75:focus {
+    --transform-scale-y: .75;
+  }
+
+  .md\:focus\:scale-y-90:focus {
+    --transform-scale-y: .9;
+  }
+
+  .md\:focus\:scale-y-95:focus {
+    --transform-scale-y: .95;
+  }
+
+  .md\:focus\:scale-y-100:focus {
+    --transform-scale-y: 1;
+  }
+
+  .md\:focus\:scale-y-105:focus {
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:focus\:scale-y-110:focus {
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:focus\:scale-y-125:focus {
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:focus\:scale-y-150:focus {
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:rotate-0 {
+    --transform-rotate: 0;
+  }
+
+  .md\:rotate-45 {
+    --transform-rotate: 45deg;
+  }
+
+  .md\:rotate-90 {
+    --transform-rotate: 90deg;
+  }
+
+  .md\:rotate-180 {
+    --transform-rotate: 180deg;
+  }
+
+  .md\:-rotate-180 {
+    --transform-rotate: -180deg;
+  }
+
+  .md\:-rotate-90 {
+    --transform-rotate: -90deg;
+  }
+
+  .md\:-rotate-45 {
+    --transform-rotate: -45deg;
+  }
+
+  .md\:hover\:rotate-0:hover {
+    --transform-rotate: 0;
+  }
+
+  .md\:hover\:rotate-45:hover {
+    --transform-rotate: 45deg;
+  }
+
+  .md\:hover\:rotate-90:hover {
+    --transform-rotate: 90deg;
+  }
+
+  .md\:hover\:rotate-180:hover {
+    --transform-rotate: 180deg;
+  }
+
+  .md\:hover\:-rotate-180:hover {
+    --transform-rotate: -180deg;
+  }
+
+  .md\:hover\:-rotate-90:hover {
+    --transform-rotate: -90deg;
+  }
+
+  .md\:hover\:-rotate-45:hover {
+    --transform-rotate: -45deg;
+  }
+
+  .md\:focus\:rotate-0:focus {
+    --transform-rotate: 0;
+  }
+
+  .md\:focus\:rotate-45:focus {
+    --transform-rotate: 45deg;
+  }
+
+  .md\:focus\:rotate-90:focus {
+    --transform-rotate: 90deg;
+  }
+
+  .md\:focus\:rotate-180:focus {
+    --transform-rotate: 180deg;
+  }
+
+  .md\:focus\:-rotate-180:focus {
+    --transform-rotate: -180deg;
+  }
+
+  .md\:focus\:-rotate-90:focus {
+    --transform-rotate: -90deg;
+  }
+
+  .md\:focus\:-rotate-45:focus {
+    --transform-rotate: -45deg;
+  }
+
+  .md\:translate-x-0 {
+    --transform-translate-x: 0;
+  }
+
+  .md\:translate-x-1 {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .md\:translate-x-2 {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .md\:translate-x-3 {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .md\:translate-x-4 {
+    --transform-translate-x: 1rem;
+  }
+
+  .md\:translate-x-5 {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .md\:translate-x-6 {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .md\:translate-x-8 {
+    --transform-translate-x: 2rem;
+  }
+
+  .md\:translate-x-10 {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .md\:translate-x-12 {
+    --transform-translate-x: 3rem;
+  }
+
+  .md\:translate-x-16 {
+    --transform-translate-x: 4rem;
+  }
+
+  .md\:translate-x-20 {
+    --transform-translate-x: 5rem;
+  }
+
+  .md\:translate-x-24 {
+    --transform-translate-x: 6rem;
+  }
+
+  .md\:translate-x-32 {
+    --transform-translate-x: 8rem;
+  }
+
+  .md\:translate-x-40 {
+    --transform-translate-x: 10rem;
+  }
+
+  .md\:translate-x-48 {
+    --transform-translate-x: 12rem;
+  }
+
+  .md\:translate-x-56 {
+    --transform-translate-x: 14rem;
+  }
+
+  .md\:translate-x-64 {
+    --transform-translate-x: 16rem;
+  }
+
+  .md\:translate-x-px {
+    --transform-translate-x: 1px;
+  }
+
+  .md\:-translate-x-1 {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .md\:-translate-x-2 {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .md\:-translate-x-3 {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .md\:-translate-x-4 {
+    --transform-translate-x: -1rem;
+  }
+
+  .md\:-translate-x-5 {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .md\:-translate-x-6 {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .md\:-translate-x-8 {
+    --transform-translate-x: -2rem;
+  }
+
+  .md\:-translate-x-10 {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .md\:-translate-x-12 {
+    --transform-translate-x: -3rem;
+  }
+
+  .md\:-translate-x-16 {
+    --transform-translate-x: -4rem;
+  }
+
+  .md\:-translate-x-20 {
+    --transform-translate-x: -5rem;
+  }
+
+  .md\:-translate-x-24 {
+    --transform-translate-x: -6rem;
+  }
+
+  .md\:-translate-x-32 {
+    --transform-translate-x: -8rem;
+  }
+
+  .md\:-translate-x-40 {
+    --transform-translate-x: -10rem;
+  }
+
+  .md\:-translate-x-48 {
+    --transform-translate-x: -12rem;
+  }
+
+  .md\:-translate-x-56 {
+    --transform-translate-x: -14rem;
+  }
+
+  .md\:-translate-x-64 {
+    --transform-translate-x: -16rem;
+  }
+
+  .md\:-translate-x-px {
+    --transform-translate-x: -1px;
+  }
+
+  .md\:-translate-x-full {
+    --transform-translate-x: -100%;
+  }
+
+  .md\:-translate-x-1\/2 {
+    --transform-translate-x: -50%;
+  }
+
+  .md\:translate-x-1\/2 {
+    --transform-translate-x: 50%;
+  }
+
+  .md\:translate-x-full {
+    --transform-translate-x: 100%;
+  }
+
+  .md\:translate-y-0 {
+    --transform-translate-y: 0;
+  }
+
+  .md\:translate-y-1 {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .md\:translate-y-2 {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .md\:translate-y-3 {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .md\:translate-y-4 {
+    --transform-translate-y: 1rem;
+  }
+
+  .md\:translate-y-5 {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .md\:translate-y-6 {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .md\:translate-y-8 {
+    --transform-translate-y: 2rem;
+  }
+
+  .md\:translate-y-10 {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .md\:translate-y-12 {
+    --transform-translate-y: 3rem;
+  }
+
+  .md\:translate-y-16 {
+    --transform-translate-y: 4rem;
+  }
+
+  .md\:translate-y-20 {
+    --transform-translate-y: 5rem;
+  }
+
+  .md\:translate-y-24 {
+    --transform-translate-y: 6rem;
+  }
+
+  .md\:translate-y-32 {
+    --transform-translate-y: 8rem;
+  }
+
+  .md\:translate-y-40 {
+    --transform-translate-y: 10rem;
+  }
+
+  .md\:translate-y-48 {
+    --transform-translate-y: 12rem;
+  }
+
+  .md\:translate-y-56 {
+    --transform-translate-y: 14rem;
+  }
+
+  .md\:translate-y-64 {
+    --transform-translate-y: 16rem;
+  }
+
+  .md\:translate-y-px {
+    --transform-translate-y: 1px;
+  }
+
+  .md\:-translate-y-1 {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .md\:-translate-y-2 {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .md\:-translate-y-3 {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .md\:-translate-y-4 {
+    --transform-translate-y: -1rem;
+  }
+
+  .md\:-translate-y-5 {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .md\:-translate-y-6 {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .md\:-translate-y-8 {
+    --transform-translate-y: -2rem;
+  }
+
+  .md\:-translate-y-10 {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .md\:-translate-y-12 {
+    --transform-translate-y: -3rem;
+  }
+
+  .md\:-translate-y-16 {
+    --transform-translate-y: -4rem;
+  }
+
+  .md\:-translate-y-20 {
+    --transform-translate-y: -5rem;
+  }
+
+  .md\:-translate-y-24 {
+    --transform-translate-y: -6rem;
+  }
+
+  .md\:-translate-y-32 {
+    --transform-translate-y: -8rem;
+  }
+
+  .md\:-translate-y-40 {
+    --transform-translate-y: -10rem;
+  }
+
+  .md\:-translate-y-48 {
+    --transform-translate-y: -12rem;
+  }
+
+  .md\:-translate-y-56 {
+    --transform-translate-y: -14rem;
+  }
+
+  .md\:-translate-y-64 {
+    --transform-translate-y: -16rem;
+  }
+
+  .md\:-translate-y-px {
+    --transform-translate-y: -1px;
+  }
+
+  .md\:-translate-y-full {
+    --transform-translate-y: -100%;
+  }
+
+  .md\:-translate-y-1\/2 {
+    --transform-translate-y: -50%;
+  }
+
+  .md\:translate-y-1\/2 {
+    --transform-translate-y: 50%;
+  }
+
+  .md\:translate-y-full {
+    --transform-translate-y: 100%;
+  }
+
+  .md\:hover\:translate-x-0:hover {
+    --transform-translate-x: 0;
+  }
+
+  .md\:hover\:translate-x-1:hover {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .md\:hover\:translate-x-2:hover {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .md\:hover\:translate-x-3:hover {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .md\:hover\:translate-x-4:hover {
+    --transform-translate-x: 1rem;
+  }
+
+  .md\:hover\:translate-x-5:hover {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .md\:hover\:translate-x-6:hover {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .md\:hover\:translate-x-8:hover {
+    --transform-translate-x: 2rem;
+  }
+
+  .md\:hover\:translate-x-10:hover {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .md\:hover\:translate-x-12:hover {
+    --transform-translate-x: 3rem;
+  }
+
+  .md\:hover\:translate-x-16:hover {
+    --transform-translate-x: 4rem;
+  }
+
+  .md\:hover\:translate-x-20:hover {
+    --transform-translate-x: 5rem;
+  }
+
+  .md\:hover\:translate-x-24:hover {
+    --transform-translate-x: 6rem;
+  }
+
+  .md\:hover\:translate-x-32:hover {
+    --transform-translate-x: 8rem;
+  }
+
+  .md\:hover\:translate-x-40:hover {
+    --transform-translate-x: 10rem;
+  }
+
+  .md\:hover\:translate-x-48:hover {
+    --transform-translate-x: 12rem;
+  }
+
+  .md\:hover\:translate-x-56:hover {
+    --transform-translate-x: 14rem;
+  }
+
+  .md\:hover\:translate-x-64:hover {
+    --transform-translate-x: 16rem;
+  }
+
+  .md\:hover\:translate-x-px:hover {
+    --transform-translate-x: 1px;
+  }
+
+  .md\:hover\:-translate-x-1:hover {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .md\:hover\:-translate-x-2:hover {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .md\:hover\:-translate-x-3:hover {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .md\:hover\:-translate-x-4:hover {
+    --transform-translate-x: -1rem;
+  }
+
+  .md\:hover\:-translate-x-5:hover {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .md\:hover\:-translate-x-6:hover {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .md\:hover\:-translate-x-8:hover {
+    --transform-translate-x: -2rem;
+  }
+
+  .md\:hover\:-translate-x-10:hover {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .md\:hover\:-translate-x-12:hover {
+    --transform-translate-x: -3rem;
+  }
+
+  .md\:hover\:-translate-x-16:hover {
+    --transform-translate-x: -4rem;
+  }
+
+  .md\:hover\:-translate-x-20:hover {
+    --transform-translate-x: -5rem;
+  }
+
+  .md\:hover\:-translate-x-24:hover {
+    --transform-translate-x: -6rem;
+  }
+
+  .md\:hover\:-translate-x-32:hover {
+    --transform-translate-x: -8rem;
+  }
+
+  .md\:hover\:-translate-x-40:hover {
+    --transform-translate-x: -10rem;
+  }
+
+  .md\:hover\:-translate-x-48:hover {
+    --transform-translate-x: -12rem;
+  }
+
+  .md\:hover\:-translate-x-56:hover {
+    --transform-translate-x: -14rem;
+  }
+
+  .md\:hover\:-translate-x-64:hover {
+    --transform-translate-x: -16rem;
+  }
+
+  .md\:hover\:-translate-x-px:hover {
+    --transform-translate-x: -1px;
+  }
+
+  .md\:hover\:-translate-x-full:hover {
+    --transform-translate-x: -100%;
+  }
+
+  .md\:hover\:-translate-x-1\/2:hover {
+    --transform-translate-x: -50%;
+  }
+
+  .md\:hover\:translate-x-1\/2:hover {
+    --transform-translate-x: 50%;
+  }
+
+  .md\:hover\:translate-x-full:hover {
+    --transform-translate-x: 100%;
+  }
+
+  .md\:hover\:translate-y-0:hover {
+    --transform-translate-y: 0;
+  }
+
+  .md\:hover\:translate-y-1:hover {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .md\:hover\:translate-y-2:hover {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .md\:hover\:translate-y-3:hover {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .md\:hover\:translate-y-4:hover {
+    --transform-translate-y: 1rem;
+  }
+
+  .md\:hover\:translate-y-5:hover {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .md\:hover\:translate-y-6:hover {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .md\:hover\:translate-y-8:hover {
+    --transform-translate-y: 2rem;
+  }
+
+  .md\:hover\:translate-y-10:hover {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .md\:hover\:translate-y-12:hover {
+    --transform-translate-y: 3rem;
+  }
+
+  .md\:hover\:translate-y-16:hover {
+    --transform-translate-y: 4rem;
+  }
+
+  .md\:hover\:translate-y-20:hover {
+    --transform-translate-y: 5rem;
+  }
+
+  .md\:hover\:translate-y-24:hover {
+    --transform-translate-y: 6rem;
+  }
+
+  .md\:hover\:translate-y-32:hover {
+    --transform-translate-y: 8rem;
+  }
+
+  .md\:hover\:translate-y-40:hover {
+    --transform-translate-y: 10rem;
+  }
+
+  .md\:hover\:translate-y-48:hover {
+    --transform-translate-y: 12rem;
+  }
+
+  .md\:hover\:translate-y-56:hover {
+    --transform-translate-y: 14rem;
+  }
+
+  .md\:hover\:translate-y-64:hover {
+    --transform-translate-y: 16rem;
+  }
+
+  .md\:hover\:translate-y-px:hover {
+    --transform-translate-y: 1px;
+  }
+
+  .md\:hover\:-translate-y-1:hover {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .md\:hover\:-translate-y-2:hover {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .md\:hover\:-translate-y-3:hover {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .md\:hover\:-translate-y-4:hover {
+    --transform-translate-y: -1rem;
+  }
+
+  .md\:hover\:-translate-y-5:hover {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .md\:hover\:-translate-y-6:hover {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .md\:hover\:-translate-y-8:hover {
+    --transform-translate-y: -2rem;
+  }
+
+  .md\:hover\:-translate-y-10:hover {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .md\:hover\:-translate-y-12:hover {
+    --transform-translate-y: -3rem;
+  }
+
+  .md\:hover\:-translate-y-16:hover {
+    --transform-translate-y: -4rem;
+  }
+
+  .md\:hover\:-translate-y-20:hover {
+    --transform-translate-y: -5rem;
+  }
+
+  .md\:hover\:-translate-y-24:hover {
+    --transform-translate-y: -6rem;
+  }
+
+  .md\:hover\:-translate-y-32:hover {
+    --transform-translate-y: -8rem;
+  }
+
+  .md\:hover\:-translate-y-40:hover {
+    --transform-translate-y: -10rem;
+  }
+
+  .md\:hover\:-translate-y-48:hover {
+    --transform-translate-y: -12rem;
+  }
+
+  .md\:hover\:-translate-y-56:hover {
+    --transform-translate-y: -14rem;
+  }
+
+  .md\:hover\:-translate-y-64:hover {
+    --transform-translate-y: -16rem;
+  }
+
+  .md\:hover\:-translate-y-px:hover {
+    --transform-translate-y: -1px;
+  }
+
+  .md\:hover\:-translate-y-full:hover {
+    --transform-translate-y: -100%;
+  }
+
+  .md\:hover\:-translate-y-1\/2:hover {
+    --transform-translate-y: -50%;
+  }
+
+  .md\:hover\:translate-y-1\/2:hover {
+    --transform-translate-y: 50%;
+  }
+
+  .md\:hover\:translate-y-full:hover {
+    --transform-translate-y: 100%;
+  }
+
+  .md\:focus\:translate-x-0:focus {
+    --transform-translate-x: 0;
+  }
+
+  .md\:focus\:translate-x-1:focus {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .md\:focus\:translate-x-2:focus {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .md\:focus\:translate-x-3:focus {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .md\:focus\:translate-x-4:focus {
+    --transform-translate-x: 1rem;
+  }
+
+  .md\:focus\:translate-x-5:focus {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .md\:focus\:translate-x-6:focus {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .md\:focus\:translate-x-8:focus {
+    --transform-translate-x: 2rem;
+  }
+
+  .md\:focus\:translate-x-10:focus {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .md\:focus\:translate-x-12:focus {
+    --transform-translate-x: 3rem;
+  }
+
+  .md\:focus\:translate-x-16:focus {
+    --transform-translate-x: 4rem;
+  }
+
+  .md\:focus\:translate-x-20:focus {
+    --transform-translate-x: 5rem;
+  }
+
+  .md\:focus\:translate-x-24:focus {
+    --transform-translate-x: 6rem;
+  }
+
+  .md\:focus\:translate-x-32:focus {
+    --transform-translate-x: 8rem;
+  }
+
+  .md\:focus\:translate-x-40:focus {
+    --transform-translate-x: 10rem;
+  }
+
+  .md\:focus\:translate-x-48:focus {
+    --transform-translate-x: 12rem;
+  }
+
+  .md\:focus\:translate-x-56:focus {
+    --transform-translate-x: 14rem;
+  }
+
+  .md\:focus\:translate-x-64:focus {
+    --transform-translate-x: 16rem;
+  }
+
+  .md\:focus\:translate-x-px:focus {
+    --transform-translate-x: 1px;
+  }
+
+  .md\:focus\:-translate-x-1:focus {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .md\:focus\:-translate-x-2:focus {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .md\:focus\:-translate-x-3:focus {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .md\:focus\:-translate-x-4:focus {
+    --transform-translate-x: -1rem;
+  }
+
+  .md\:focus\:-translate-x-5:focus {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .md\:focus\:-translate-x-6:focus {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .md\:focus\:-translate-x-8:focus {
+    --transform-translate-x: -2rem;
+  }
+
+  .md\:focus\:-translate-x-10:focus {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .md\:focus\:-translate-x-12:focus {
+    --transform-translate-x: -3rem;
+  }
+
+  .md\:focus\:-translate-x-16:focus {
+    --transform-translate-x: -4rem;
+  }
+
+  .md\:focus\:-translate-x-20:focus {
+    --transform-translate-x: -5rem;
+  }
+
+  .md\:focus\:-translate-x-24:focus {
+    --transform-translate-x: -6rem;
+  }
+
+  .md\:focus\:-translate-x-32:focus {
+    --transform-translate-x: -8rem;
+  }
+
+  .md\:focus\:-translate-x-40:focus {
+    --transform-translate-x: -10rem;
+  }
+
+  .md\:focus\:-translate-x-48:focus {
+    --transform-translate-x: -12rem;
+  }
+
+  .md\:focus\:-translate-x-56:focus {
+    --transform-translate-x: -14rem;
+  }
+
+  .md\:focus\:-translate-x-64:focus {
+    --transform-translate-x: -16rem;
+  }
+
+  .md\:focus\:-translate-x-px:focus {
+    --transform-translate-x: -1px;
+  }
+
+  .md\:focus\:-translate-x-full:focus {
+    --transform-translate-x: -100%;
+  }
+
+  .md\:focus\:-translate-x-1\/2:focus {
+    --transform-translate-x: -50%;
+  }
+
+  .md\:focus\:translate-x-1\/2:focus {
+    --transform-translate-x: 50%;
+  }
+
+  .md\:focus\:translate-x-full:focus {
+    --transform-translate-x: 100%;
+  }
+
+  .md\:focus\:translate-y-0:focus {
+    --transform-translate-y: 0;
+  }
+
+  .md\:focus\:translate-y-1:focus {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .md\:focus\:translate-y-2:focus {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .md\:focus\:translate-y-3:focus {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .md\:focus\:translate-y-4:focus {
+    --transform-translate-y: 1rem;
+  }
+
+  .md\:focus\:translate-y-5:focus {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .md\:focus\:translate-y-6:focus {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .md\:focus\:translate-y-8:focus {
+    --transform-translate-y: 2rem;
+  }
+
+  .md\:focus\:translate-y-10:focus {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .md\:focus\:translate-y-12:focus {
+    --transform-translate-y: 3rem;
+  }
+
+  .md\:focus\:translate-y-16:focus {
+    --transform-translate-y: 4rem;
+  }
+
+  .md\:focus\:translate-y-20:focus {
+    --transform-translate-y: 5rem;
+  }
+
+  .md\:focus\:translate-y-24:focus {
+    --transform-translate-y: 6rem;
+  }
+
+  .md\:focus\:translate-y-32:focus {
+    --transform-translate-y: 8rem;
+  }
+
+  .md\:focus\:translate-y-40:focus {
+    --transform-translate-y: 10rem;
+  }
+
+  .md\:focus\:translate-y-48:focus {
+    --transform-translate-y: 12rem;
+  }
+
+  .md\:focus\:translate-y-56:focus {
+    --transform-translate-y: 14rem;
+  }
+
+  .md\:focus\:translate-y-64:focus {
+    --transform-translate-y: 16rem;
+  }
+
+  .md\:focus\:translate-y-px:focus {
+    --transform-translate-y: 1px;
+  }
+
+  .md\:focus\:-translate-y-1:focus {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .md\:focus\:-translate-y-2:focus {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .md\:focus\:-translate-y-3:focus {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .md\:focus\:-translate-y-4:focus {
+    --transform-translate-y: -1rem;
+  }
+
+  .md\:focus\:-translate-y-5:focus {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .md\:focus\:-translate-y-6:focus {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .md\:focus\:-translate-y-8:focus {
+    --transform-translate-y: -2rem;
+  }
+
+  .md\:focus\:-translate-y-10:focus {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .md\:focus\:-translate-y-12:focus {
+    --transform-translate-y: -3rem;
+  }
+
+  .md\:focus\:-translate-y-16:focus {
+    --transform-translate-y: -4rem;
+  }
+
+  .md\:focus\:-translate-y-20:focus {
+    --transform-translate-y: -5rem;
+  }
+
+  .md\:focus\:-translate-y-24:focus {
+    --transform-translate-y: -6rem;
+  }
+
+  .md\:focus\:-translate-y-32:focus {
+    --transform-translate-y: -8rem;
+  }
+
+  .md\:focus\:-translate-y-40:focus {
+    --transform-translate-y: -10rem;
+  }
+
+  .md\:focus\:-translate-y-48:focus {
+    --transform-translate-y: -12rem;
+  }
+
+  .md\:focus\:-translate-y-56:focus {
+    --transform-translate-y: -14rem;
+  }
+
+  .md\:focus\:-translate-y-64:focus {
+    --transform-translate-y: -16rem;
+  }
+
+  .md\:focus\:-translate-y-px:focus {
+    --transform-translate-y: -1px;
+  }
+
+  .md\:focus\:-translate-y-full:focus {
+    --transform-translate-y: -100%;
+  }
+
+  .md\:focus\:-translate-y-1\/2:focus {
+    --transform-translate-y: -50%;
+  }
+
+  .md\:focus\:translate-y-1\/2:focus {
+    --transform-translate-y: 50%;
+  }
+
+  .md\:focus\:translate-y-full:focus {
+    --transform-translate-y: 100%;
+  }
+
+  .md\:skew-x-0 {
+    --transform-skew-x: 0;
+  }
+
+  .md\:skew-x-3 {
+    --transform-skew-x: 3deg;
+  }
+
+  .md\:skew-x-6 {
+    --transform-skew-x: 6deg;
+  }
+
+  .md\:skew-x-12 {
+    --transform-skew-x: 12deg;
+  }
+
+  .md\:-skew-x-12 {
+    --transform-skew-x: -12deg;
+  }
+
+  .md\:-skew-x-6 {
+    --transform-skew-x: -6deg;
+  }
+
+  .md\:-skew-x-3 {
+    --transform-skew-x: -3deg;
+  }
+
+  .md\:skew-y-0 {
+    --transform-skew-y: 0;
+  }
+
+  .md\:skew-y-3 {
+    --transform-skew-y: 3deg;
+  }
+
+  .md\:skew-y-6 {
+    --transform-skew-y: 6deg;
+  }
+
+  .md\:skew-y-12 {
+    --transform-skew-y: 12deg;
+  }
+
+  .md\:-skew-y-12 {
+    --transform-skew-y: -12deg;
+  }
+
+  .md\:-skew-y-6 {
+    --transform-skew-y: -6deg;
+  }
+
+  .md\:-skew-y-3 {
+    --transform-skew-y: -3deg;
+  }
+
+  .md\:hover\:skew-x-0:hover {
+    --transform-skew-x: 0;
+  }
+
+  .md\:hover\:skew-x-3:hover {
+    --transform-skew-x: 3deg;
+  }
+
+  .md\:hover\:skew-x-6:hover {
+    --transform-skew-x: 6deg;
+  }
+
+  .md\:hover\:skew-x-12:hover {
+    --transform-skew-x: 12deg;
+  }
+
+  .md\:hover\:-skew-x-12:hover {
+    --transform-skew-x: -12deg;
+  }
+
+  .md\:hover\:-skew-x-6:hover {
+    --transform-skew-x: -6deg;
+  }
+
+  .md\:hover\:-skew-x-3:hover {
+    --transform-skew-x: -3deg;
+  }
+
+  .md\:hover\:skew-y-0:hover {
+    --transform-skew-y: 0;
+  }
+
+  .md\:hover\:skew-y-3:hover {
+    --transform-skew-y: 3deg;
+  }
+
+  .md\:hover\:skew-y-6:hover {
+    --transform-skew-y: 6deg;
+  }
+
+  .md\:hover\:skew-y-12:hover {
+    --transform-skew-y: 12deg;
+  }
+
+  .md\:hover\:-skew-y-12:hover {
+    --transform-skew-y: -12deg;
+  }
+
+  .md\:hover\:-skew-y-6:hover {
+    --transform-skew-y: -6deg;
+  }
+
+  .md\:hover\:-skew-y-3:hover {
+    --transform-skew-y: -3deg;
+  }
+
+  .md\:focus\:skew-x-0:focus {
+    --transform-skew-x: 0;
+  }
+
+  .md\:focus\:skew-x-3:focus {
+    --transform-skew-x: 3deg;
+  }
+
+  .md\:focus\:skew-x-6:focus {
+    --transform-skew-x: 6deg;
+  }
+
+  .md\:focus\:skew-x-12:focus {
+    --transform-skew-x: 12deg;
+  }
+
+  .md\:focus\:-skew-x-12:focus {
+    --transform-skew-x: -12deg;
+  }
+
+  .md\:focus\:-skew-x-6:focus {
+    --transform-skew-x: -6deg;
+  }
+
+  .md\:focus\:-skew-x-3:focus {
+    --transform-skew-x: -3deg;
+  }
+
+  .md\:focus\:skew-y-0:focus {
+    --transform-skew-y: 0;
+  }
+
+  .md\:focus\:skew-y-3:focus {
+    --transform-skew-y: 3deg;
+  }
+
+  .md\:focus\:skew-y-6:focus {
+    --transform-skew-y: 6deg;
+  }
+
+  .md\:focus\:skew-y-12:focus {
+    --transform-skew-y: 12deg;
+  }
+
+  .md\:focus\:-skew-y-12:focus {
+    --transform-skew-y: -12deg;
+  }
+
+  .md\:focus\:-skew-y-6:focus {
+    --transform-skew-y: -6deg;
+  }
+
+  .md\:focus\:-skew-y-3:focus {
+    --transform-skew-y: -3deg;
+  }
+
+  .md\:transition-none {
+    transition-property: none;
+  }
+
+  .md\:transition-all {
+    transition-property: all;
+  }
+
+  .md\:transition {
+    transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+  }
+
+  .md\:transition-colors {
+    transition-property: background-color, border-color, color, fill, stroke;
+  }
+
+  .md\:transition-opacity {
+    transition-property: opacity;
+  }
+
+  .md\:transition-shadow {
+    transition-property: box-shadow;
+  }
+
+  .md\:transition-transform {
+    transition-property: transform;
+  }
+
+  .md\:ease-linear {
+    transition-timing-function: linear;
+  }
+
+  .md\:ease-in {
+    transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+  }
+
+  .md\:ease-out {
+    transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+  }
+
+  .md\:ease-in-out {
+    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .md\:duration-75 {
+    transition-duration: 75ms;
+  }
+
+  .md\:duration-100 {
+    transition-duration: 100ms;
+  }
+
+  .md\:duration-150 {
+    transition-duration: 150ms;
+  }
+
+  .md\:duration-200 {
+    transition-duration: 200ms;
+  }
+
+  .md\:duration-300 {
+    transition-duration: 300ms;
+  }
+
+  .md\:duration-500 {
+    transition-duration: 500ms;
+  }
+
+  .md\:duration-700 {
+    transition-duration: 700ms;
+  }
+
+  .md\:duration-1000 {
+    transition-duration: 1000ms;
+  }
+
+  .md\:delay-75 {
+    transition-delay: 75ms;
+  }
+
+  .md\:delay-100 {
+    transition-delay: 100ms;
+  }
+
+  .md\:delay-150 {
+    transition-delay: 150ms;
+  }
+
+  .md\:delay-200 {
+    transition-delay: 200ms;
+  }
+
+  .md\:delay-300 {
+    transition-delay: 300ms;
+  }
+
+  .md\:delay-500 {
+    transition-delay: 500ms;
+  }
+
+  .md\:delay-700 {
+    transition-delay: 700ms;
+  }
+
+  .md\:delay-1000 {
+    transition-delay: 1000ms;
+  }
+
+  .md\:animate-none {
+    -webkit-animation: none;
+            animation: none;
+  }
+
+  .md\:animate-spin {
+    -webkit-animation: spin 1s linear infinite;
+            animation: spin 1s linear infinite;
+  }
+
+  .md\:animate-ping {
+    -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+            animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+  }
+
+  .md\:animate-pulse {
+    -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+            animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+  }
+
+  .md\:animate-bounce {
+    -webkit-animation: bounce 1s infinite;
+            animation: bounce 1s infinite;
+  }
+}
+
+@media (min-width: 1024px) {
+  .lg\:container {
+    width: 100%;
+  }
+
+  @media (min-width: 640px) {
+    .lg\:container {
+      max-width: 640px;
+    }
+  }
+
+  @media (min-width: 768px) {
+    .lg\:container {
+      max-width: 768px;
+    }
+  }
+
+  @media (min-width: 1024px) {
+    .lg\:container {
+      max-width: 1024px;
+    }
+  }
+
+  @media (min-width: 1280px) {
+    .lg\:container {
+      max-width: 1280px;
+    }
+  }
+
+  .lg\:space-y-0 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0px * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-0 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0px * var(--space-x-reverse));
+    margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.25rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.25rem * var(--space-x-reverse));
+    margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.5rem * var(--space-x-reverse));
+    margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.75rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.75rem * var(--space-x-reverse));
+    margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1rem * var(--space-x-reverse));
+    margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.25rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.25rem * var(--space-x-reverse));
+    margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.5rem * var(--space-x-reverse));
+    margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2rem * var(--space-x-reverse));
+    margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2.5rem * var(--space-x-reverse));
+    margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(3rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(3rem * var(--space-x-reverse));
+    margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(4rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(4rem * var(--space-x-reverse));
+    margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(5rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(5rem * var(--space-x-reverse));
+    margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(6rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(6rem * var(--space-x-reverse));
+    margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(8rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(8rem * var(--space-x-reverse));
+    margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(10rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(10rem * var(--space-x-reverse));
+    margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(12rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(12rem * var(--space-x-reverse));
+    margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(14rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(14rem * var(--space-x-reverse));
+    margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(16rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(16rem * var(--space-x-reverse));
+    margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1px * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1px * var(--space-x-reverse));
+    margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.25rem * var(--space-x-reverse));
+    margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.5rem * var(--space-x-reverse));
+    margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.75rem * var(--space-x-reverse));
+    margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1rem * var(--space-x-reverse));
+    margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.25rem * var(--space-x-reverse));
+    margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.5rem * var(--space-x-reverse));
+    margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2rem * var(--space-x-reverse));
+    margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2.5rem * var(--space-x-reverse));
+    margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-3rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-3rem * var(--space-x-reverse));
+    margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-4rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-4rem * var(--space-x-reverse));
+    margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-5rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-5rem * var(--space-x-reverse));
+    margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-6rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-6rem * var(--space-x-reverse));
+    margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-8rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-8rem * var(--space-x-reverse));
+    margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-10rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-10rem * var(--space-x-reverse));
+    margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-12rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-12rem * var(--space-x-reverse));
+    margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-14rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-14rem * var(--space-x-reverse));
+    margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-16rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-16rem * var(--space-x-reverse));
+    margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1px * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1px * var(--space-x-reverse));
+    margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-reverse > :not(template) ~ :not(template) {
+    --space-y-reverse: 1;
+  }
+
+  .lg\:space-x-reverse > :not(template) ~ :not(template) {
+    --space-x-reverse: 1;
+  }
+
+  .lg\:divide-y-0 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(0px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x-0 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(0px * var(--divide-x-reverse));
+    border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y-2 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(2px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x-2 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(2px * var(--divide-x-reverse));
+    border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y-4 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(4px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x-4 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(4px * var(--divide-x-reverse));
+    border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y-8 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(8px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x-8 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(8px * var(--divide-x-reverse));
+    border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(1px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(1px * var(--divide-x-reverse));
+    border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y-reverse > :not(template) ~ :not(template) {
+    --divide-y-reverse: 1;
+  }
+
+  .lg\:divide-x-reverse > :not(template) ~ :not(template) {
+    --divide-x-reverse: 1;
+  }
+
+  .lg\:divide-transparent > :not(template) ~ :not(template) {
+    border-color: transparent;
+  }
+
+  .lg\:divide-current > :not(template) ~ :not(template) {
+    border-color: currentColor;
+  }
+
+  .lg\:divide-black > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--divide-opacity));
+  }
+
+  .lg\:divide-white > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--divide-opacity));
+  }
+
+  .lg\:divide-solid > :not(template) ~ :not(template) {
+    border-style: solid;
+  }
+
+  .lg\:divide-dashed > :not(template) ~ :not(template) {
+    border-style: dashed;
+  }
+
+  .lg\:divide-dotted > :not(template) ~ :not(template) {
+    border-style: dotted;
+  }
+
+  .lg\:divide-double > :not(template) ~ :not(template) {
+    border-style: double;
+  }
+
+  .lg\:divide-none > :not(template) ~ :not(template) {
+    border-style: none;
+  }
+
+  .lg\:divide-opacity-0 > :not(template) ~ :not(template) {
+    --divide-opacity: 0;
+  }
+
+  .lg\:divide-opacity-25 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.25;
+  }
+
+  .lg\:divide-opacity-50 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.5;
+  }
+
+  .lg\:divide-opacity-75 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.75;
+  }
+
+  .lg\:divide-opacity-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+  }
+
+  .lg\:sr-only {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .lg\:not-sr-only {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .lg\:focus\:sr-only:focus {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .lg\:focus\:not-sr-only:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .lg\:appearance-none {
+    -webkit-appearance: none;
+       -moz-appearance: none;
+            appearance: none;
+  }
+
+  .lg\:bg-fixed {
+    background-attachment: fixed;
+  }
+
+  .lg\:bg-local {
+    background-attachment: local;
+  }
+
+  .lg\:bg-scroll {
+    background-attachment: scroll;
+  }
+
+  .lg\:bg-clip-border {
+    background-clip: border-box;
+  }
+
+  .lg\:bg-clip-padding {
+    background-clip: padding-box;
+  }
+
+  .lg\:bg-clip-content {
+    background-clip: content-box;
+  }
+
+  .lg\:bg-clip-text {
+    -webkit-background-clip: text;
+            background-clip: text;
+  }
+
+  .lg\:bg-transparent {
+    background-color: transparent;
+  }
+
+  .lg\:bg-current {
+    background-color: currentColor;
+  }
+
+  .lg\:bg-black {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .lg\:bg-white {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-100 {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-200 {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-300 {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-400 {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-500 {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-600 {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-700 {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-800 {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-900 {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-200 {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-300 {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-400 {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-500 {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-600 {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-700 {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-800 {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-900 {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-100 {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-200 {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-300 {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-400 {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-500 {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-600 {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-700 {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-800 {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-900 {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-100 {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-200 {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-300 {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-400 {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-500 {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-600 {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-700 {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-800 {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-900 {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-100 {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-200 {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-300 {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-400 {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-500 {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-600 {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-700 {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-800 {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-900 {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-100 {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-200 {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-300 {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-400 {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-500 {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-600 {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-700 {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-800 {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-900 {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-100 {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-200 {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-300 {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-400 {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-500 {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-600 {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-700 {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-800 {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-900 {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-100 {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-200 {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-300 {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-400 {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-500 {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-600 {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-700 {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-800 {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-900 {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-100 {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-200 {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-300 {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-400 {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-500 {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-600 {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-700 {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-800 {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-900 {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-200 {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-300 {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-400 {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-500 {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-600 {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-700 {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-800 {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-900 {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-transparent:hover {
+    background-color: transparent;
+  }
+
+  .lg\:hover\:bg-current:hover {
+    background-color: currentColor;
+  }
+
+  .lg\:hover\:bg-black:hover {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-white:hover {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-100:hover {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-200:hover {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-300:hover {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-400:hover {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-500:hover {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-600:hover {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-700:hover {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-800:hover {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-900:hover {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-300:hover {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-400:hover {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-500:hover {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-600:hover {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-700:hover {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-800:hover {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-900:hover {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-200:hover {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-600:hover {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-700:hover {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-800:hover {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-900:hover {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-200:hover {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-300:hover {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-500:hover {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-600:hover {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-700:hover {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-800:hover {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-900:hover {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-100:hover {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-200:hover {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-300:hover {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-400:hover {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-500:hover {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-600:hover {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-700:hover {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-800:hover {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-900:hover {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-100:hover {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-200:hover {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-300:hover {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-400:hover {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-500:hover {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-600:hover {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-700:hover {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-800:hover {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-900:hover {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-200:hover {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-300:hover {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-400:hover {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-500:hover {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-600:hover {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-700:hover {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-800:hover {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-900:hover {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-200:hover {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-300:hover {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-400:hover {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-500:hover {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-600:hover {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-700:hover {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-800:hover {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-900:hover {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-100:hover {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-200:hover {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-300:hover {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-400:hover {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-500:hover {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-600:hover {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-700:hover {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-800:hover {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-900:hover {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-400:hover {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-600:hover {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-700:hover {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-800:hover {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-900:hover {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-transparent:focus {
+    background-color: transparent;
+  }
+
+  .lg\:focus\:bg-current:focus {
+    background-color: currentColor;
+  }
+
+  .lg\:focus\:bg-black:focus {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-white:focus {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-100:focus {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-200:focus {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-300:focus {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-400:focus {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-500:focus {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-600:focus {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-700:focus {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-800:focus {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-900:focus {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-300:focus {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-400:focus {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-500:focus {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-600:focus {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-700:focus {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-800:focus {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-900:focus {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-200:focus {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-600:focus {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-700:focus {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-800:focus {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-900:focus {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-200:focus {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-300:focus {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-500:focus {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-600:focus {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-700:focus {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-800:focus {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-900:focus {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-100:focus {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-200:focus {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-300:focus {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-400:focus {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-500:focus {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-600:focus {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-700:focus {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-800:focus {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-900:focus {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-100:focus {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-200:focus {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-300:focus {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-400:focus {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-500:focus {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-600:focus {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-700:focus {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-800:focus {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-900:focus {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-200:focus {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-300:focus {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-400:focus {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-500:focus {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-600:focus {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-700:focus {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-800:focus {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-900:focus {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-200:focus {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-300:focus {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-400:focus {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-500:focus {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-600:focus {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-700:focus {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-800:focus {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-900:focus {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-100:focus {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-200:focus {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-300:focus {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-400:focus {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-500:focus {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-600:focus {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-700:focus {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-800:focus {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-900:focus {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-400:focus {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-600:focus {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-700:focus {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-800:focus {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-900:focus {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .lg\:bg-none {
+    background-image: none;
+  }
+
+  .lg\:bg-gradient-to-t {
+    background-image: linear-gradient(to top, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-tr {
+    background-image: linear-gradient(to top right, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-r {
+    background-image: linear-gradient(to right, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-br {
+    background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-b {
+    background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-bl {
+    background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-l {
+    background-image: linear-gradient(to left, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-tl {
+    background-image: linear-gradient(to top left, var(--gradient-color-stops));
+  }
+
+  .lg\:from-transparent {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:from-current {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:from-black {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:from-white {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:from-gray-100 {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:from-gray-200 {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:from-gray-300 {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:from-gray-400 {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:from-gray-500 {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:from-gray-600 {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:from-gray-700 {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:from-gray-800 {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:from-gray-900 {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:from-red-100 {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:from-red-200 {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:from-red-300 {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:from-red-400 {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:from-red-500 {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:from-red-600 {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:from-red-700 {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:from-red-800 {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:from-red-900 {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:from-orange-100 {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:from-orange-200 {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:from-orange-300 {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:from-orange-400 {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:from-orange-500 {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:from-orange-600 {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:from-orange-700 {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:from-orange-800 {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:from-orange-900 {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:from-yellow-100 {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:from-yellow-200 {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:from-yellow-300 {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:from-yellow-400 {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:from-yellow-500 {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:from-yellow-600 {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:from-yellow-700 {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:from-yellow-800 {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:from-yellow-900 {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:from-green-100 {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:from-green-200 {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:from-green-300 {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:from-green-400 {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:from-green-500 {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:from-green-600 {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:from-green-700 {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:from-green-800 {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:from-green-900 {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:from-teal-100 {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:from-teal-200 {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:from-teal-300 {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:from-teal-400 {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:from-teal-500 {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:from-teal-600 {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:from-teal-700 {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:from-teal-800 {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:from-teal-900 {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:from-blue-100 {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:from-blue-200 {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:from-blue-300 {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:from-blue-400 {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:from-blue-500 {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:from-blue-600 {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:from-blue-700 {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:from-blue-800 {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:from-blue-900 {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:from-indigo-100 {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:from-indigo-200 {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:from-indigo-300 {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:from-indigo-400 {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:from-indigo-500 {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:from-indigo-600 {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:from-indigo-700 {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:from-indigo-800 {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:from-indigo-900 {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:from-purple-100 {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:from-purple-200 {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:from-purple-300 {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:from-purple-400 {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:from-purple-500 {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:from-purple-600 {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:from-purple-700 {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:from-purple-800 {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:from-purple-900 {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:from-pink-100 {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:from-pink-200 {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:from-pink-300 {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:from-pink-400 {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:from-pink-500 {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:from-pink-600 {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:from-pink-700 {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:from-pink-800 {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:from-pink-900 {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:via-transparent {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:via-current {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:via-black {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:via-white {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:via-gray-100 {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:via-gray-200 {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:via-gray-300 {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:via-gray-400 {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:via-gray-500 {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:via-gray-600 {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:via-gray-700 {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:via-gray-800 {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:via-gray-900 {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:via-red-100 {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:via-red-200 {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:via-red-300 {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:via-red-400 {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:via-red-500 {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:via-red-600 {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:via-red-700 {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:via-red-800 {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:via-red-900 {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:via-orange-100 {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:via-orange-200 {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:via-orange-300 {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:via-orange-400 {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:via-orange-500 {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:via-orange-600 {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:via-orange-700 {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:via-orange-800 {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:via-orange-900 {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:via-yellow-100 {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:via-yellow-200 {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:via-yellow-300 {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:via-yellow-400 {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:via-yellow-500 {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:via-yellow-600 {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:via-yellow-700 {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:via-yellow-800 {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:via-yellow-900 {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:via-green-100 {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:via-green-200 {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:via-green-300 {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:via-green-400 {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:via-green-500 {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:via-green-600 {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:via-green-700 {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:via-green-800 {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:via-green-900 {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:via-teal-100 {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:via-teal-200 {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:via-teal-300 {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:via-teal-400 {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:via-teal-500 {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:via-teal-600 {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:via-teal-700 {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:via-teal-800 {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:via-teal-900 {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:via-blue-100 {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:via-blue-200 {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:via-blue-300 {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:via-blue-400 {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:via-blue-500 {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:via-blue-600 {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:via-blue-700 {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:via-blue-800 {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:via-blue-900 {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:via-indigo-100 {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:via-indigo-200 {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:via-indigo-300 {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:via-indigo-400 {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:via-indigo-500 {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:via-indigo-600 {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:via-indigo-700 {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:via-indigo-800 {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:via-indigo-900 {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:via-purple-100 {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:via-purple-200 {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:via-purple-300 {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:via-purple-400 {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:via-purple-500 {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:via-purple-600 {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:via-purple-700 {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:via-purple-800 {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:via-purple-900 {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:via-pink-100 {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:via-pink-200 {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:via-pink-300 {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:via-pink-400 {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:via-pink-500 {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:via-pink-600 {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:via-pink-700 {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:via-pink-800 {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:via-pink-900 {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:to-transparent {
+    --gradient-to-color: transparent;
+  }
+
+  .lg\:to-current {
+    --gradient-to-color: currentColor;
+  }
+
+  .lg\:to-black {
+    --gradient-to-color: #000;
+  }
+
+  .lg\:to-white {
+    --gradient-to-color: #fff;
+  }
+
+  .lg\:to-gray-100 {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .lg\:to-gray-200 {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .lg\:to-gray-300 {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .lg\:to-gray-400 {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .lg\:to-gray-500 {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .lg\:to-gray-600 {
+    --gradient-to-color: #718096;
+  }
+
+  .lg\:to-gray-700 {
+    --gradient-to-color: #4a5568;
+  }
+
+  .lg\:to-gray-800 {
+    --gradient-to-color: #2d3748;
+  }
+
+  .lg\:to-gray-900 {
+    --gradient-to-color: #1a202c;
+  }
+
+  .lg\:to-red-100 {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .lg\:to-red-200 {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .lg\:to-red-300 {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .lg\:to-red-400 {
+    --gradient-to-color: #fc8181;
+  }
+
+  .lg\:to-red-500 {
+    --gradient-to-color: #f56565;
+  }
+
+  .lg\:to-red-600 {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .lg\:to-red-700 {
+    --gradient-to-color: #c53030;
+  }
+
+  .lg\:to-red-800 {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .lg\:to-red-900 {
+    --gradient-to-color: #742a2a;
+  }
+
+  .lg\:to-orange-100 {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .lg\:to-orange-200 {
+    --gradient-to-color: #feebc8;
+  }
+
+  .lg\:to-orange-300 {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .lg\:to-orange-400 {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .lg\:to-orange-500 {
+    --gradient-to-color: #ed8936;
+  }
+
+  .lg\:to-orange-600 {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .lg\:to-orange-700 {
+    --gradient-to-color: #c05621;
+  }
+
+  .lg\:to-orange-800 {
+    --gradient-to-color: #9c4221;
+  }
+
+  .lg\:to-orange-900 {
+    --gradient-to-color: #7b341e;
+  }
+
+  .lg\:to-yellow-100 {
+    --gradient-to-color: #fffff0;
+  }
+
+  .lg\:to-yellow-200 {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .lg\:to-yellow-300 {
+    --gradient-to-color: #faf089;
+  }
+
+  .lg\:to-yellow-400 {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .lg\:to-yellow-500 {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .lg\:to-yellow-600 {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .lg\:to-yellow-700 {
+    --gradient-to-color: #b7791f;
+  }
+
+  .lg\:to-yellow-800 {
+    --gradient-to-color: #975a16;
+  }
+
+  .lg\:to-yellow-900 {
+    --gradient-to-color: #744210;
+  }
+
+  .lg\:to-green-100 {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .lg\:to-green-200 {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .lg\:to-green-300 {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .lg\:to-green-400 {
+    --gradient-to-color: #68d391;
+  }
+
+  .lg\:to-green-500 {
+    --gradient-to-color: #48bb78;
+  }
+
+  .lg\:to-green-600 {
+    --gradient-to-color: #38a169;
+  }
+
+  .lg\:to-green-700 {
+    --gradient-to-color: #2f855a;
+  }
+
+  .lg\:to-green-800 {
+    --gradient-to-color: #276749;
+  }
+
+  .lg\:to-green-900 {
+    --gradient-to-color: #22543d;
+  }
+
+  .lg\:to-teal-100 {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .lg\:to-teal-200 {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .lg\:to-teal-300 {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .lg\:to-teal-400 {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .lg\:to-teal-500 {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .lg\:to-teal-600 {
+    --gradient-to-color: #319795;
+  }
+
+  .lg\:to-teal-700 {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .lg\:to-teal-800 {
+    --gradient-to-color: #285e61;
+  }
+
+  .lg\:to-teal-900 {
+    --gradient-to-color: #234e52;
+  }
+
+  .lg\:to-blue-100 {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .lg\:to-blue-200 {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .lg\:to-blue-300 {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .lg\:to-blue-400 {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .lg\:to-blue-500 {
+    --gradient-to-color: #4299e1;
+  }
+
+  .lg\:to-blue-600 {
+    --gradient-to-color: #3182ce;
+  }
+
+  .lg\:to-blue-700 {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .lg\:to-blue-800 {
+    --gradient-to-color: #2c5282;
+  }
+
+  .lg\:to-blue-900 {
+    --gradient-to-color: #2a4365;
+  }
+
+  .lg\:to-indigo-100 {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .lg\:to-indigo-200 {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .lg\:to-indigo-300 {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .lg\:to-indigo-400 {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .lg\:to-indigo-500 {
+    --gradient-to-color: #667eea;
+  }
+
+  .lg\:to-indigo-600 {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .lg\:to-indigo-700 {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .lg\:to-indigo-800 {
+    --gradient-to-color: #434190;
+  }
+
+  .lg\:to-indigo-900 {
+    --gradient-to-color: #3c366b;
+  }
+
+  .lg\:to-purple-100 {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .lg\:to-purple-200 {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .lg\:to-purple-300 {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .lg\:to-purple-400 {
+    --gradient-to-color: #b794f4;
+  }
+
+  .lg\:to-purple-500 {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .lg\:to-purple-600 {
+    --gradient-to-color: #805ad5;
+  }
+
+  .lg\:to-purple-700 {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .lg\:to-purple-800 {
+    --gradient-to-color: #553c9a;
+  }
+
+  .lg\:to-purple-900 {
+    --gradient-to-color: #44337a;
+  }
+
+  .lg\:to-pink-100 {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .lg\:to-pink-200 {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .lg\:to-pink-300 {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .lg\:to-pink-400 {
+    --gradient-to-color: #f687b3;
+  }
+
+  .lg\:to-pink-500 {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .lg\:to-pink-600 {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .lg\:to-pink-700 {
+    --gradient-to-color: #b83280;
+  }
+
+  .lg\:to-pink-800 {
+    --gradient-to-color: #97266d;
+  }
+
+  .lg\:to-pink-900 {
+    --gradient-to-color: #702459;
+  }
+
+  .lg\:hover\:from-transparent:hover {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:hover\:from-current:hover {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:hover\:from-black:hover {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:hover\:from-white:hover {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:hover\:from-gray-100:hover {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:hover\:from-gray-200:hover {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:hover\:from-gray-300:hover {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:hover\:from-gray-400:hover {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:hover\:from-gray-500:hover {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:hover\:from-gray-600:hover {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:hover\:from-gray-700:hover {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:hover\:from-gray-800:hover {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:hover\:from-gray-900:hover {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:hover\:from-red-100:hover {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:hover\:from-red-200:hover {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:hover\:from-red-300:hover {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:hover\:from-red-400:hover {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:hover\:from-red-500:hover {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:hover\:from-red-600:hover {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:hover\:from-red-700:hover {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:hover\:from-red-800:hover {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:hover\:from-red-900:hover {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:hover\:from-orange-100:hover {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:hover\:from-orange-200:hover {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:hover\:from-orange-300:hover {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:hover\:from-orange-400:hover {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:hover\:from-orange-500:hover {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:hover\:from-orange-600:hover {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:hover\:from-orange-700:hover {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:hover\:from-orange-800:hover {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:hover\:from-orange-900:hover {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:hover\:from-yellow-100:hover {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:hover\:from-yellow-200:hover {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:hover\:from-yellow-300:hover {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:hover\:from-yellow-400:hover {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:hover\:from-yellow-500:hover {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:hover\:from-yellow-600:hover {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:hover\:from-yellow-700:hover {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:hover\:from-yellow-800:hover {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:hover\:from-yellow-900:hover {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:hover\:from-green-100:hover {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:hover\:from-green-200:hover {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:hover\:from-green-300:hover {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:hover\:from-green-400:hover {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:hover\:from-green-500:hover {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:hover\:from-green-600:hover {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:hover\:from-green-700:hover {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:hover\:from-green-800:hover {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:hover\:from-green-900:hover {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:hover\:from-teal-100:hover {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:hover\:from-teal-200:hover {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:hover\:from-teal-300:hover {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:hover\:from-teal-400:hover {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:hover\:from-teal-500:hover {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:hover\:from-teal-600:hover {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:hover\:from-teal-700:hover {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:hover\:from-teal-800:hover {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:hover\:from-teal-900:hover {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:hover\:from-blue-100:hover {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:hover\:from-blue-200:hover {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:hover\:from-blue-300:hover {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:hover\:from-blue-400:hover {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:hover\:from-blue-500:hover {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:hover\:from-blue-600:hover {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:hover\:from-blue-700:hover {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:hover\:from-blue-800:hover {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:hover\:from-blue-900:hover {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:hover\:from-indigo-100:hover {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:hover\:from-indigo-200:hover {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:hover\:from-indigo-300:hover {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:hover\:from-indigo-400:hover {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:hover\:from-indigo-500:hover {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:hover\:from-indigo-600:hover {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:hover\:from-indigo-700:hover {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:hover\:from-indigo-800:hover {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:hover\:from-indigo-900:hover {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:hover\:from-purple-100:hover {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:hover\:from-purple-200:hover {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:hover\:from-purple-300:hover {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:hover\:from-purple-400:hover {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:hover\:from-purple-500:hover {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:hover\:from-purple-600:hover {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:hover\:from-purple-700:hover {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:hover\:from-purple-800:hover {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:hover\:from-purple-900:hover {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:hover\:from-pink-100:hover {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:hover\:from-pink-200:hover {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:hover\:from-pink-300:hover {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:hover\:from-pink-400:hover {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:hover\:from-pink-500:hover {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:hover\:from-pink-600:hover {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:hover\:from-pink-700:hover {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:hover\:from-pink-800:hover {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:hover\:from-pink-900:hover {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:hover\:via-transparent:hover {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:hover\:via-current:hover {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:hover\:via-black:hover {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:hover\:via-white:hover {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:hover\:via-gray-100:hover {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:hover\:via-gray-200:hover {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:hover\:via-gray-300:hover {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:hover\:via-gray-400:hover {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:hover\:via-gray-500:hover {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:hover\:via-gray-600:hover {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:hover\:via-gray-700:hover {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:hover\:via-gray-800:hover {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:hover\:via-gray-900:hover {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:hover\:via-red-100:hover {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:hover\:via-red-200:hover {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:hover\:via-red-300:hover {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:hover\:via-red-400:hover {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:hover\:via-red-500:hover {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:hover\:via-red-600:hover {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:hover\:via-red-700:hover {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:hover\:via-red-800:hover {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:hover\:via-red-900:hover {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:hover\:via-orange-100:hover {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:hover\:via-orange-200:hover {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:hover\:via-orange-300:hover {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:hover\:via-orange-400:hover {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:hover\:via-orange-500:hover {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:hover\:via-orange-600:hover {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:hover\:via-orange-700:hover {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:hover\:via-orange-800:hover {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:hover\:via-orange-900:hover {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:hover\:via-yellow-100:hover {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:hover\:via-yellow-200:hover {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:hover\:via-yellow-300:hover {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:hover\:via-yellow-400:hover {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:hover\:via-yellow-500:hover {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:hover\:via-yellow-600:hover {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:hover\:via-yellow-700:hover {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:hover\:via-yellow-800:hover {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:hover\:via-yellow-900:hover {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:hover\:via-green-100:hover {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:hover\:via-green-200:hover {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:hover\:via-green-300:hover {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:hover\:via-green-400:hover {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:hover\:via-green-500:hover {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:hover\:via-green-600:hover {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:hover\:via-green-700:hover {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:hover\:via-green-800:hover {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:hover\:via-green-900:hover {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:hover\:via-teal-100:hover {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:hover\:via-teal-200:hover {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:hover\:via-teal-300:hover {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:hover\:via-teal-400:hover {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:hover\:via-teal-500:hover {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:hover\:via-teal-600:hover {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:hover\:via-teal-700:hover {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:hover\:via-teal-800:hover {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:hover\:via-teal-900:hover {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:hover\:via-blue-100:hover {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:hover\:via-blue-200:hover {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:hover\:via-blue-300:hover {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:hover\:via-blue-400:hover {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:hover\:via-blue-500:hover {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:hover\:via-blue-600:hover {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:hover\:via-blue-700:hover {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:hover\:via-blue-800:hover {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:hover\:via-blue-900:hover {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:hover\:via-indigo-100:hover {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:hover\:via-indigo-200:hover {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:hover\:via-indigo-300:hover {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:hover\:via-indigo-400:hover {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:hover\:via-indigo-500:hover {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:hover\:via-indigo-600:hover {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:hover\:via-indigo-700:hover {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:hover\:via-indigo-800:hover {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:hover\:via-indigo-900:hover {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:hover\:via-purple-100:hover {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:hover\:via-purple-200:hover {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:hover\:via-purple-300:hover {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:hover\:via-purple-400:hover {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:hover\:via-purple-500:hover {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:hover\:via-purple-600:hover {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:hover\:via-purple-700:hover {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:hover\:via-purple-800:hover {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:hover\:via-purple-900:hover {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:hover\:via-pink-100:hover {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:hover\:via-pink-200:hover {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:hover\:via-pink-300:hover {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:hover\:via-pink-400:hover {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:hover\:via-pink-500:hover {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:hover\:via-pink-600:hover {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:hover\:via-pink-700:hover {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:hover\:via-pink-800:hover {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:hover\:via-pink-900:hover {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:hover\:to-transparent:hover {
+    --gradient-to-color: transparent;
+  }
+
+  .lg\:hover\:to-current:hover {
+    --gradient-to-color: currentColor;
+  }
+
+  .lg\:hover\:to-black:hover {
+    --gradient-to-color: #000;
+  }
+
+  .lg\:hover\:to-white:hover {
+    --gradient-to-color: #fff;
+  }
+
+  .lg\:hover\:to-gray-100:hover {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .lg\:hover\:to-gray-200:hover {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .lg\:hover\:to-gray-300:hover {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .lg\:hover\:to-gray-400:hover {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .lg\:hover\:to-gray-500:hover {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .lg\:hover\:to-gray-600:hover {
+    --gradient-to-color: #718096;
+  }
+
+  .lg\:hover\:to-gray-700:hover {
+    --gradient-to-color: #4a5568;
+  }
+
+  .lg\:hover\:to-gray-800:hover {
+    --gradient-to-color: #2d3748;
+  }
+
+  .lg\:hover\:to-gray-900:hover {
+    --gradient-to-color: #1a202c;
+  }
+
+  .lg\:hover\:to-red-100:hover {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .lg\:hover\:to-red-200:hover {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .lg\:hover\:to-red-300:hover {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .lg\:hover\:to-red-400:hover {
+    --gradient-to-color: #fc8181;
+  }
+
+  .lg\:hover\:to-red-500:hover {
+    --gradient-to-color: #f56565;
+  }
+
+  .lg\:hover\:to-red-600:hover {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .lg\:hover\:to-red-700:hover {
+    --gradient-to-color: #c53030;
+  }
+
+  .lg\:hover\:to-red-800:hover {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .lg\:hover\:to-red-900:hover {
+    --gradient-to-color: #742a2a;
+  }
+
+  .lg\:hover\:to-orange-100:hover {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .lg\:hover\:to-orange-200:hover {
+    --gradient-to-color: #feebc8;
+  }
+
+  .lg\:hover\:to-orange-300:hover {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .lg\:hover\:to-orange-400:hover {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .lg\:hover\:to-orange-500:hover {
+    --gradient-to-color: #ed8936;
+  }
+
+  .lg\:hover\:to-orange-600:hover {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .lg\:hover\:to-orange-700:hover {
+    --gradient-to-color: #c05621;
+  }
+
+  .lg\:hover\:to-orange-800:hover {
+    --gradient-to-color: #9c4221;
+  }
+
+  .lg\:hover\:to-orange-900:hover {
+    --gradient-to-color: #7b341e;
+  }
+
+  .lg\:hover\:to-yellow-100:hover {
+    --gradient-to-color: #fffff0;
+  }
+
+  .lg\:hover\:to-yellow-200:hover {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .lg\:hover\:to-yellow-300:hover {
+    --gradient-to-color: #faf089;
+  }
+
+  .lg\:hover\:to-yellow-400:hover {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .lg\:hover\:to-yellow-500:hover {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .lg\:hover\:to-yellow-600:hover {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .lg\:hover\:to-yellow-700:hover {
+    --gradient-to-color: #b7791f;
+  }
+
+  .lg\:hover\:to-yellow-800:hover {
+    --gradient-to-color: #975a16;
+  }
+
+  .lg\:hover\:to-yellow-900:hover {
+    --gradient-to-color: #744210;
+  }
+
+  .lg\:hover\:to-green-100:hover {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .lg\:hover\:to-green-200:hover {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .lg\:hover\:to-green-300:hover {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .lg\:hover\:to-green-400:hover {
+    --gradient-to-color: #68d391;
+  }
+
+  .lg\:hover\:to-green-500:hover {
+    --gradient-to-color: #48bb78;
+  }
+
+  .lg\:hover\:to-green-600:hover {
+    --gradient-to-color: #38a169;
+  }
+
+  .lg\:hover\:to-green-700:hover {
+    --gradient-to-color: #2f855a;
+  }
+
+  .lg\:hover\:to-green-800:hover {
+    --gradient-to-color: #276749;
+  }
+
+  .lg\:hover\:to-green-900:hover {
+    --gradient-to-color: #22543d;
+  }
+
+  .lg\:hover\:to-teal-100:hover {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .lg\:hover\:to-teal-200:hover {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .lg\:hover\:to-teal-300:hover {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .lg\:hover\:to-teal-400:hover {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .lg\:hover\:to-teal-500:hover {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .lg\:hover\:to-teal-600:hover {
+    --gradient-to-color: #319795;
+  }
+
+  .lg\:hover\:to-teal-700:hover {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .lg\:hover\:to-teal-800:hover {
+    --gradient-to-color: #285e61;
+  }
+
+  .lg\:hover\:to-teal-900:hover {
+    --gradient-to-color: #234e52;
+  }
+
+  .lg\:hover\:to-blue-100:hover {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .lg\:hover\:to-blue-200:hover {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .lg\:hover\:to-blue-300:hover {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .lg\:hover\:to-blue-400:hover {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .lg\:hover\:to-blue-500:hover {
+    --gradient-to-color: #4299e1;
+  }
+
+  .lg\:hover\:to-blue-600:hover {
+    --gradient-to-color: #3182ce;
+  }
+
+  .lg\:hover\:to-blue-700:hover {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .lg\:hover\:to-blue-800:hover {
+    --gradient-to-color: #2c5282;
+  }
+
+  .lg\:hover\:to-blue-900:hover {
+    --gradient-to-color: #2a4365;
+  }
+
+  .lg\:hover\:to-indigo-100:hover {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .lg\:hover\:to-indigo-200:hover {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .lg\:hover\:to-indigo-300:hover {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .lg\:hover\:to-indigo-400:hover {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .lg\:hover\:to-indigo-500:hover {
+    --gradient-to-color: #667eea;
+  }
+
+  .lg\:hover\:to-indigo-600:hover {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .lg\:hover\:to-indigo-700:hover {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .lg\:hover\:to-indigo-800:hover {
+    --gradient-to-color: #434190;
+  }
+
+  .lg\:hover\:to-indigo-900:hover {
+    --gradient-to-color: #3c366b;
+  }
+
+  .lg\:hover\:to-purple-100:hover {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .lg\:hover\:to-purple-200:hover {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .lg\:hover\:to-purple-300:hover {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .lg\:hover\:to-purple-400:hover {
+    --gradient-to-color: #b794f4;
+  }
+
+  .lg\:hover\:to-purple-500:hover {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .lg\:hover\:to-purple-600:hover {
+    --gradient-to-color: #805ad5;
+  }
+
+  .lg\:hover\:to-purple-700:hover {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .lg\:hover\:to-purple-800:hover {
+    --gradient-to-color: #553c9a;
+  }
+
+  .lg\:hover\:to-purple-900:hover {
+    --gradient-to-color: #44337a;
+  }
+
+  .lg\:hover\:to-pink-100:hover {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .lg\:hover\:to-pink-200:hover {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .lg\:hover\:to-pink-300:hover {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .lg\:hover\:to-pink-400:hover {
+    --gradient-to-color: #f687b3;
+  }
+
+  .lg\:hover\:to-pink-500:hover {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .lg\:hover\:to-pink-600:hover {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .lg\:hover\:to-pink-700:hover {
+    --gradient-to-color: #b83280;
+  }
+
+  .lg\:hover\:to-pink-800:hover {
+    --gradient-to-color: #97266d;
+  }
+
+  .lg\:hover\:to-pink-900:hover {
+    --gradient-to-color: #702459;
+  }
+
+  .lg\:focus\:from-transparent:focus {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:focus\:from-current:focus {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:focus\:from-black:focus {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:focus\:from-white:focus {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:focus\:from-gray-100:focus {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:focus\:from-gray-200:focus {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:focus\:from-gray-300:focus {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:focus\:from-gray-400:focus {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:focus\:from-gray-500:focus {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:focus\:from-gray-600:focus {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:focus\:from-gray-700:focus {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:focus\:from-gray-800:focus {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:focus\:from-gray-900:focus {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:focus\:from-red-100:focus {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:focus\:from-red-200:focus {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:focus\:from-red-300:focus {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:focus\:from-red-400:focus {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:focus\:from-red-500:focus {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:focus\:from-red-600:focus {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:focus\:from-red-700:focus {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:focus\:from-red-800:focus {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:focus\:from-red-900:focus {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:focus\:from-orange-100:focus {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:focus\:from-orange-200:focus {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:focus\:from-orange-300:focus {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:focus\:from-orange-400:focus {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:focus\:from-orange-500:focus {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:focus\:from-orange-600:focus {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:focus\:from-orange-700:focus {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:focus\:from-orange-800:focus {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:focus\:from-orange-900:focus {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:focus\:from-yellow-100:focus {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:focus\:from-yellow-200:focus {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:focus\:from-yellow-300:focus {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:focus\:from-yellow-400:focus {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:focus\:from-yellow-500:focus {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:focus\:from-yellow-600:focus {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:focus\:from-yellow-700:focus {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:focus\:from-yellow-800:focus {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:focus\:from-yellow-900:focus {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:focus\:from-green-100:focus {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:focus\:from-green-200:focus {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:focus\:from-green-300:focus {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:focus\:from-green-400:focus {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:focus\:from-green-500:focus {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:focus\:from-green-600:focus {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:focus\:from-green-700:focus {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:focus\:from-green-800:focus {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:focus\:from-green-900:focus {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:focus\:from-teal-100:focus {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:focus\:from-teal-200:focus {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:focus\:from-teal-300:focus {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:focus\:from-teal-400:focus {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:focus\:from-teal-500:focus {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:focus\:from-teal-600:focus {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:focus\:from-teal-700:focus {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:focus\:from-teal-800:focus {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:focus\:from-teal-900:focus {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:focus\:from-blue-100:focus {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:focus\:from-blue-200:focus {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:focus\:from-blue-300:focus {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:focus\:from-blue-400:focus {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:focus\:from-blue-500:focus {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:focus\:from-blue-600:focus {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:focus\:from-blue-700:focus {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:focus\:from-blue-800:focus {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:focus\:from-blue-900:focus {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:focus\:from-indigo-100:focus {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:focus\:from-indigo-200:focus {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:focus\:from-indigo-300:focus {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:focus\:from-indigo-400:focus {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:focus\:from-indigo-500:focus {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:focus\:from-indigo-600:focus {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:focus\:from-indigo-700:focus {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:focus\:from-indigo-800:focus {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:focus\:from-indigo-900:focus {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:focus\:from-purple-100:focus {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:focus\:from-purple-200:focus {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:focus\:from-purple-300:focus {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:focus\:from-purple-400:focus {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:focus\:from-purple-500:focus {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:focus\:from-purple-600:focus {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:focus\:from-purple-700:focus {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:focus\:from-purple-800:focus {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:focus\:from-purple-900:focus {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:focus\:from-pink-100:focus {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:focus\:from-pink-200:focus {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:focus\:from-pink-300:focus {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:focus\:from-pink-400:focus {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:focus\:from-pink-500:focus {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:focus\:from-pink-600:focus {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:focus\:from-pink-700:focus {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:focus\:from-pink-800:focus {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:focus\:from-pink-900:focus {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:focus\:via-transparent:focus {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:focus\:via-current:focus {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:focus\:via-black:focus {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:focus\:via-white:focus {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:focus\:via-gray-100:focus {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:focus\:via-gray-200:focus {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:focus\:via-gray-300:focus {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:focus\:via-gray-400:focus {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:focus\:via-gray-500:focus {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:focus\:via-gray-600:focus {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:focus\:via-gray-700:focus {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:focus\:via-gray-800:focus {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:focus\:via-gray-900:focus {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:focus\:via-red-100:focus {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:focus\:via-red-200:focus {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:focus\:via-red-300:focus {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:focus\:via-red-400:focus {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:focus\:via-red-500:focus {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:focus\:via-red-600:focus {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:focus\:via-red-700:focus {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:focus\:via-red-800:focus {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:focus\:via-red-900:focus {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:focus\:via-orange-100:focus {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:focus\:via-orange-200:focus {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:focus\:via-orange-300:focus {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:focus\:via-orange-400:focus {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:focus\:via-orange-500:focus {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:focus\:via-orange-600:focus {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:focus\:via-orange-700:focus {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:focus\:via-orange-800:focus {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:focus\:via-orange-900:focus {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:focus\:via-yellow-100:focus {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:focus\:via-yellow-200:focus {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:focus\:via-yellow-300:focus {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:focus\:via-yellow-400:focus {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:focus\:via-yellow-500:focus {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:focus\:via-yellow-600:focus {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:focus\:via-yellow-700:focus {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:focus\:via-yellow-800:focus {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:focus\:via-yellow-900:focus {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:focus\:via-green-100:focus {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:focus\:via-green-200:focus {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:focus\:via-green-300:focus {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:focus\:via-green-400:focus {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:focus\:via-green-500:focus {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:focus\:via-green-600:focus {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:focus\:via-green-700:focus {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:focus\:via-green-800:focus {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:focus\:via-green-900:focus {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:focus\:via-teal-100:focus {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:focus\:via-teal-200:focus {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:focus\:via-teal-300:focus {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:focus\:via-teal-400:focus {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:focus\:via-teal-500:focus {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:focus\:via-teal-600:focus {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:focus\:via-teal-700:focus {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:focus\:via-teal-800:focus {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:focus\:via-teal-900:focus {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:focus\:via-blue-100:focus {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:focus\:via-blue-200:focus {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:focus\:via-blue-300:focus {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:focus\:via-blue-400:focus {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:focus\:via-blue-500:focus {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:focus\:via-blue-600:focus {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:focus\:via-blue-700:focus {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:focus\:via-blue-800:focus {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:focus\:via-blue-900:focus {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:focus\:via-indigo-100:focus {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:focus\:via-indigo-200:focus {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:focus\:via-indigo-300:focus {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:focus\:via-indigo-400:focus {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:focus\:via-indigo-500:focus {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:focus\:via-indigo-600:focus {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:focus\:via-indigo-700:focus {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:focus\:via-indigo-800:focus {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:focus\:via-indigo-900:focus {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:focus\:via-purple-100:focus {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:focus\:via-purple-200:focus {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:focus\:via-purple-300:focus {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:focus\:via-purple-400:focus {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:focus\:via-purple-500:focus {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:focus\:via-purple-600:focus {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:focus\:via-purple-700:focus {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:focus\:via-purple-800:focus {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:focus\:via-purple-900:focus {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:focus\:via-pink-100:focus {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:focus\:via-pink-200:focus {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:focus\:via-pink-300:focus {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:focus\:via-pink-400:focus {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:focus\:via-pink-500:focus {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:focus\:via-pink-600:focus {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:focus\:via-pink-700:focus {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:focus\:via-pink-800:focus {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:focus\:via-pink-900:focus {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:focus\:to-transparent:focus {
+    --gradient-to-color: transparent;
+  }
+
+  .lg\:focus\:to-current:focus {
+    --gradient-to-color: currentColor;
+  }
+
+  .lg\:focus\:to-black:focus {
+    --gradient-to-color: #000;
+  }
+
+  .lg\:focus\:to-white:focus {
+    --gradient-to-color: #fff;
+  }
+
+  .lg\:focus\:to-gray-100:focus {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .lg\:focus\:to-gray-200:focus {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .lg\:focus\:to-gray-300:focus {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .lg\:focus\:to-gray-400:focus {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .lg\:focus\:to-gray-500:focus {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .lg\:focus\:to-gray-600:focus {
+    --gradient-to-color: #718096;
+  }
+
+  .lg\:focus\:to-gray-700:focus {
+    --gradient-to-color: #4a5568;
+  }
+
+  .lg\:focus\:to-gray-800:focus {
+    --gradient-to-color: #2d3748;
+  }
+
+  .lg\:focus\:to-gray-900:focus {
+    --gradient-to-color: #1a202c;
+  }
+
+  .lg\:focus\:to-red-100:focus {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .lg\:focus\:to-red-200:focus {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .lg\:focus\:to-red-300:focus {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .lg\:focus\:to-red-400:focus {
+    --gradient-to-color: #fc8181;
+  }
+
+  .lg\:focus\:to-red-500:focus {
+    --gradient-to-color: #f56565;
+  }
+
+  .lg\:focus\:to-red-600:focus {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .lg\:focus\:to-red-700:focus {
+    --gradient-to-color: #c53030;
+  }
+
+  .lg\:focus\:to-red-800:focus {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .lg\:focus\:to-red-900:focus {
+    --gradient-to-color: #742a2a;
+  }
+
+  .lg\:focus\:to-orange-100:focus {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .lg\:focus\:to-orange-200:focus {
+    --gradient-to-color: #feebc8;
+  }
+
+  .lg\:focus\:to-orange-300:focus {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .lg\:focus\:to-orange-400:focus {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .lg\:focus\:to-orange-500:focus {
+    --gradient-to-color: #ed8936;
+  }
+
+  .lg\:focus\:to-orange-600:focus {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .lg\:focus\:to-orange-700:focus {
+    --gradient-to-color: #c05621;
+  }
+
+  .lg\:focus\:to-orange-800:focus {
+    --gradient-to-color: #9c4221;
+  }
+
+  .lg\:focus\:to-orange-900:focus {
+    --gradient-to-color: #7b341e;
+  }
+
+  .lg\:focus\:to-yellow-100:focus {
+    --gradient-to-color: #fffff0;
+  }
+
+  .lg\:focus\:to-yellow-200:focus {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .lg\:focus\:to-yellow-300:focus {
+    --gradient-to-color: #faf089;
+  }
+
+  .lg\:focus\:to-yellow-400:focus {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .lg\:focus\:to-yellow-500:focus {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .lg\:focus\:to-yellow-600:focus {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .lg\:focus\:to-yellow-700:focus {
+    --gradient-to-color: #b7791f;
+  }
+
+  .lg\:focus\:to-yellow-800:focus {
+    --gradient-to-color: #975a16;
+  }
+
+  .lg\:focus\:to-yellow-900:focus {
+    --gradient-to-color: #744210;
+  }
+
+  .lg\:focus\:to-green-100:focus {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .lg\:focus\:to-green-200:focus {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .lg\:focus\:to-green-300:focus {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .lg\:focus\:to-green-400:focus {
+    --gradient-to-color: #68d391;
+  }
+
+  .lg\:focus\:to-green-500:focus {
+    --gradient-to-color: #48bb78;
+  }
+
+  .lg\:focus\:to-green-600:focus {
+    --gradient-to-color: #38a169;
+  }
+
+  .lg\:focus\:to-green-700:focus {
+    --gradient-to-color: #2f855a;
+  }
+
+  .lg\:focus\:to-green-800:focus {
+    --gradient-to-color: #276749;
+  }
+
+  .lg\:focus\:to-green-900:focus {
+    --gradient-to-color: #22543d;
+  }
+
+  .lg\:focus\:to-teal-100:focus {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .lg\:focus\:to-teal-200:focus {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .lg\:focus\:to-teal-300:focus {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .lg\:focus\:to-teal-400:focus {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .lg\:focus\:to-teal-500:focus {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .lg\:focus\:to-teal-600:focus {
+    --gradient-to-color: #319795;
+  }
+
+  .lg\:focus\:to-teal-700:focus {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .lg\:focus\:to-teal-800:focus {
+    --gradient-to-color: #285e61;
+  }
+
+  .lg\:focus\:to-teal-900:focus {
+    --gradient-to-color: #234e52;
+  }
+
+  .lg\:focus\:to-blue-100:focus {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .lg\:focus\:to-blue-200:focus {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .lg\:focus\:to-blue-300:focus {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .lg\:focus\:to-blue-400:focus {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .lg\:focus\:to-blue-500:focus {
+    --gradient-to-color: #4299e1;
+  }
+
+  .lg\:focus\:to-blue-600:focus {
+    --gradient-to-color: #3182ce;
+  }
+
+  .lg\:focus\:to-blue-700:focus {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .lg\:focus\:to-blue-800:focus {
+    --gradient-to-color: #2c5282;
+  }
+
+  .lg\:focus\:to-blue-900:focus {
+    --gradient-to-color: #2a4365;
+  }
+
+  .lg\:focus\:to-indigo-100:focus {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .lg\:focus\:to-indigo-200:focus {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .lg\:focus\:to-indigo-300:focus {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .lg\:focus\:to-indigo-400:focus {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .lg\:focus\:to-indigo-500:focus {
+    --gradient-to-color: #667eea;
+  }
+
+  .lg\:focus\:to-indigo-600:focus {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .lg\:focus\:to-indigo-700:focus {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .lg\:focus\:to-indigo-800:focus {
+    --gradient-to-color: #434190;
+  }
+
+  .lg\:focus\:to-indigo-900:focus {
+    --gradient-to-color: #3c366b;
+  }
+
+  .lg\:focus\:to-purple-100:focus {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .lg\:focus\:to-purple-200:focus {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .lg\:focus\:to-purple-300:focus {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .lg\:focus\:to-purple-400:focus {
+    --gradient-to-color: #b794f4;
+  }
+
+  .lg\:focus\:to-purple-500:focus {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .lg\:focus\:to-purple-600:focus {
+    --gradient-to-color: #805ad5;
+  }
+
+  .lg\:focus\:to-purple-700:focus {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .lg\:focus\:to-purple-800:focus {
+    --gradient-to-color: #553c9a;
+  }
+
+  .lg\:focus\:to-purple-900:focus {
+    --gradient-to-color: #44337a;
+  }
+
+  .lg\:focus\:to-pink-100:focus {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .lg\:focus\:to-pink-200:focus {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .lg\:focus\:to-pink-300:focus {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .lg\:focus\:to-pink-400:focus {
+    --gradient-to-color: #f687b3;
+  }
+
+  .lg\:focus\:to-pink-500:focus {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .lg\:focus\:to-pink-600:focus {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .lg\:focus\:to-pink-700:focus {
+    --gradient-to-color: #b83280;
+  }
+
+  .lg\:focus\:to-pink-800:focus {
+    --gradient-to-color: #97266d;
+  }
+
+  .lg\:focus\:to-pink-900:focus {
+    --gradient-to-color: #702459;
+  }
+
+  .lg\:bg-opacity-0 {
+    --bg-opacity: 0;
+  }
+
+  .lg\:bg-opacity-25 {
+    --bg-opacity: 0.25;
+  }
+
+  .lg\:bg-opacity-50 {
+    --bg-opacity: 0.5;
+  }
+
+  .lg\:bg-opacity-75 {
+    --bg-opacity: 0.75;
+  }
+
+  .lg\:bg-opacity-100 {
+    --bg-opacity: 1;
+  }
+
+  .lg\:hover\:bg-opacity-0:hover {
+    --bg-opacity: 0;
+  }
+
+  .lg\:hover\:bg-opacity-25:hover {
+    --bg-opacity: 0.25;
+  }
+
+  .lg\:hover\:bg-opacity-50:hover {
+    --bg-opacity: 0.5;
+  }
+
+  .lg\:hover\:bg-opacity-75:hover {
+    --bg-opacity: 0.75;
+  }
+
+  .lg\:hover\:bg-opacity-100:hover {
+    --bg-opacity: 1;
+  }
+
+  .lg\:focus\:bg-opacity-0:focus {
+    --bg-opacity: 0;
+  }
+
+  .lg\:focus\:bg-opacity-25:focus {
+    --bg-opacity: 0.25;
+  }
+
+  .lg\:focus\:bg-opacity-50:focus {
+    --bg-opacity: 0.5;
+  }
+
+  .lg\:focus\:bg-opacity-75:focus {
+    --bg-opacity: 0.75;
+  }
+
+  .lg\:focus\:bg-opacity-100:focus {
+    --bg-opacity: 1;
+  }
+
+  .lg\:bg-bottom {
+    background-position: bottom;
+  }
+
+  .lg\:bg-center {
+    background-position: center;
+  }
+
+  .lg\:bg-left {
+    background-position: left;
+  }
+
+  .lg\:bg-left-bottom {
+    background-position: left bottom;
+  }
+
+  .lg\:bg-left-top {
+    background-position: left top;
+  }
+
+  .lg\:bg-right {
+    background-position: right;
+  }
+
+  .lg\:bg-right-bottom {
+    background-position: right bottom;
+  }
+
+  .lg\:bg-right-top {
+    background-position: right top;
+  }
+
+  .lg\:bg-top {
+    background-position: top;
+  }
+
+  .lg\:bg-repeat {
+    background-repeat: repeat;
+  }
+
+  .lg\:bg-no-repeat {
+    background-repeat: no-repeat;
+  }
+
+  .lg\:bg-repeat-x {
+    background-repeat: repeat-x;
+  }
+
+  .lg\:bg-repeat-y {
+    background-repeat: repeat-y;
+  }
+
+  .lg\:bg-repeat-round {
+    background-repeat: round;
+  }
+
+  .lg\:bg-repeat-space {
+    background-repeat: space;
+  }
+
+  .lg\:bg-auto {
+    background-size: auto;
+  }
+
+  .lg\:bg-cover {
+    background-size: cover;
+  }
+
+  .lg\:bg-contain {
+    background-size: contain;
+  }
+
+  .lg\:border-collapse {
+    border-collapse: collapse;
+  }
+
+  .lg\:border-separate {
+    border-collapse: separate;
+  }
+
+  .lg\:border-transparent {
+    border-color: transparent;
+  }
+
+  .lg\:border-current {
+    border-color: currentColor;
+  }
+
+  .lg\:border-black {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .lg\:border-white {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .lg\:border-gray-100 {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .lg\:border-gray-200 {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .lg\:border-gray-300 {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .lg\:border-gray-400 {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .lg\:border-gray-500 {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .lg\:border-gray-600 {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .lg\:border-gray-700 {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .lg\:border-gray-800 {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .lg\:border-gray-900 {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .lg\:border-red-100 {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .lg\:border-red-200 {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .lg\:border-red-300 {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .lg\:border-red-400 {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .lg\:border-red-500 {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .lg\:border-red-600 {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .lg\:border-red-700 {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .lg\:border-red-800 {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .lg\:border-red-900 {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .lg\:border-orange-100 {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .lg\:border-orange-200 {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .lg\:border-orange-300 {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .lg\:border-orange-400 {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .lg\:border-orange-500 {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .lg\:border-orange-600 {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .lg\:border-orange-700 {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .lg\:border-orange-800 {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .lg\:border-orange-900 {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-100 {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-200 {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-300 {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-400 {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-500 {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-600 {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-700 {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-800 {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-900 {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .lg\:border-green-100 {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .lg\:border-green-200 {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .lg\:border-green-300 {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .lg\:border-green-400 {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .lg\:border-green-500 {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .lg\:border-green-600 {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .lg\:border-green-700 {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .lg\:border-green-800 {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .lg\:border-green-900 {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .lg\:border-teal-100 {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .lg\:border-teal-200 {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .lg\:border-teal-300 {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .lg\:border-teal-400 {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .lg\:border-teal-500 {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .lg\:border-teal-600 {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .lg\:border-teal-700 {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .lg\:border-teal-800 {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .lg\:border-teal-900 {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .lg\:border-blue-100 {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .lg\:border-blue-200 {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .lg\:border-blue-300 {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .lg\:border-blue-400 {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .lg\:border-blue-500 {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .lg\:border-blue-600 {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .lg\:border-blue-700 {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .lg\:border-blue-800 {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .lg\:border-blue-900 {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-100 {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-200 {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-300 {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-400 {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-500 {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-600 {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-700 {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-800 {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-900 {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .lg\:border-purple-100 {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .lg\:border-purple-200 {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .lg\:border-purple-300 {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .lg\:border-purple-400 {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .lg\:border-purple-500 {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .lg\:border-purple-600 {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .lg\:border-purple-700 {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .lg\:border-purple-800 {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .lg\:border-purple-900 {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .lg\:border-pink-100 {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .lg\:border-pink-200 {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .lg\:border-pink-300 {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .lg\:border-pink-400 {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .lg\:border-pink-500 {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .lg\:border-pink-600 {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .lg\:border-pink-700 {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .lg\:border-pink-800 {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .lg\:border-pink-900 {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-transparent:hover {
+    border-color: transparent;
+  }
+
+  .lg\:hover\:border-current:hover {
+    border-color: currentColor;
+  }
+
+  .lg\:hover\:border-black:hover {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-white:hover {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-100:hover {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-200:hover {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-300:hover {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-400:hover {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-500:hover {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-600:hover {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-700:hover {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-800:hover {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-900:hover {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-300:hover {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-400:hover {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-500:hover {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-600:hover {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-700:hover {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-800:hover {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-900:hover {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-100:hover {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-200:hover {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-300:hover {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-400:hover {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-500:hover {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-600:hover {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-700:hover {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-800:hover {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-900:hover {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-100:hover {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-200:hover {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-300:hover {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-400:hover {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-500:hover {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-600:hover {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-700:hover {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-800:hover {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-900:hover {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-100:hover {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-200:hover {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-300:hover {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-400:hover {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-500:hover {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-600:hover {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-700:hover {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-800:hover {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-900:hover {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-100:hover {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-200:hover {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-300:hover {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-400:hover {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-500:hover {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-600:hover {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-700:hover {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-800:hover {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-900:hover {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-200:hover {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-300:hover {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-400:hover {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-500:hover {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-600:hover {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-700:hover {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-800:hover {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-900:hover {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-200:hover {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-300:hover {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-400:hover {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-500:hover {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-600:hover {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-700:hover {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-800:hover {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-900:hover {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-100:hover {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-200:hover {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-300:hover {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-400:hover {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-500:hover {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-600:hover {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-700:hover {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-800:hover {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-900:hover {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-300:hover {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-400:hover {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-500:hover {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-600:hover {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-700:hover {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-800:hover {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-900:hover {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-transparent:focus {
+    border-color: transparent;
+  }
+
+  .lg\:focus\:border-current:focus {
+    border-color: currentColor;
+  }
+
+  .lg\:focus\:border-black:focus {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-white:focus {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-100:focus {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-200:focus {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-300:focus {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-400:focus {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-500:focus {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-600:focus {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-700:focus {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-800:focus {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-900:focus {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-300:focus {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-400:focus {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-500:focus {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-600:focus {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-700:focus {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-800:focus {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-900:focus {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-100:focus {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-200:focus {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-300:focus {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-400:focus {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-500:focus {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-600:focus {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-700:focus {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-800:focus {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-900:focus {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-100:focus {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-200:focus {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-300:focus {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-400:focus {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-500:focus {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-600:focus {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-700:focus {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-800:focus {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-900:focus {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-100:focus {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-200:focus {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-300:focus {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-400:focus {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-500:focus {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-600:focus {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-700:focus {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-800:focus {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-900:focus {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-100:focus {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-200:focus {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-300:focus {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-400:focus {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-500:focus {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-600:focus {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-700:focus {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-800:focus {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-900:focus {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-200:focus {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-300:focus {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-400:focus {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-500:focus {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-600:focus {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-700:focus {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-800:focus {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-900:focus {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-200:focus {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-300:focus {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-400:focus {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-500:focus {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-600:focus {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-700:focus {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-800:focus {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-900:focus {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-100:focus {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-200:focus {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-300:focus {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-400:focus {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-500:focus {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-600:focus {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-700:focus {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-800:focus {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-900:focus {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-300:focus {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-400:focus {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-500:focus {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-600:focus {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-700:focus {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-800:focus {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-900:focus {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .lg\:border-opacity-0 {
+    --border-opacity: 0;
+  }
+
+  .lg\:border-opacity-25 {
+    --border-opacity: 0.25;
+  }
+
+  .lg\:border-opacity-50 {
+    --border-opacity: 0.5;
+  }
+
+  .lg\:border-opacity-75 {
+    --border-opacity: 0.75;
+  }
+
+  .lg\:border-opacity-100 {
+    --border-opacity: 1;
+  }
+
+  .lg\:hover\:border-opacity-0:hover {
+    --border-opacity: 0;
+  }
+
+  .lg\:hover\:border-opacity-25:hover {
+    --border-opacity: 0.25;
+  }
+
+  .lg\:hover\:border-opacity-50:hover {
+    --border-opacity: 0.5;
+  }
+
+  .lg\:hover\:border-opacity-75:hover {
+    --border-opacity: 0.75;
+  }
+
+  .lg\:hover\:border-opacity-100:hover {
+    --border-opacity: 1;
+  }
+
+  .lg\:focus\:border-opacity-0:focus {
+    --border-opacity: 0;
+  }
+
+  .lg\:focus\:border-opacity-25:focus {
+    --border-opacity: 0.25;
+  }
+
+  .lg\:focus\:border-opacity-50:focus {
+    --border-opacity: 0.5;
+  }
+
+  .lg\:focus\:border-opacity-75:focus {
+    --border-opacity: 0.75;
+  }
+
+  .lg\:focus\:border-opacity-100:focus {
+    --border-opacity: 1;
+  }
+
+  .lg\:rounded-none {
+    border-radius: 0;
+  }
+
+  .lg\:rounded-sm {
+    border-radius: 0.125rem;
+  }
+
+  .lg\:rounded {
+    border-radius: 0.25rem;
+  }
+
+  .lg\:rounded-md {
+    border-radius: 0.375rem;
+  }
+
+  .lg\:rounded-lg {
+    border-radius: 0.5rem;
+  }
+
+  .lg\:rounded-full {
+    border-radius: 9999px;
+  }
+
+  .lg\:rounded-t-none {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+  }
+
+  .lg\:rounded-r-none {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+  }
+
+  .lg\:rounded-b-none {
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .lg\:rounded-l-none {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .lg\:rounded-t-sm {
+    border-top-left-radius: 0.125rem;
+    border-top-right-radius: 0.125rem;
+  }
+
+  .lg\:rounded-r-sm {
+    border-top-right-radius: 0.125rem;
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .lg\:rounded-b-sm {
+    border-bottom-right-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .lg\:rounded-l-sm {
+    border-top-left-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .lg\:rounded-t {
+    border-top-left-radius: 0.25rem;
+    border-top-right-radius: 0.25rem;
+  }
+
+  .lg\:rounded-r {
+    border-top-right-radius: 0.25rem;
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .lg\:rounded-b {
+    border-bottom-right-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .lg\:rounded-l {
+    border-top-left-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .lg\:rounded-t-md {
+    border-top-left-radius: 0.375rem;
+    border-top-right-radius: 0.375rem;
+  }
+
+  .lg\:rounded-r-md {
+    border-top-right-radius: 0.375rem;
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .lg\:rounded-b-md {
+    border-bottom-right-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .lg\:rounded-l-md {
+    border-top-left-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .lg\:rounded-t-lg {
+    border-top-left-radius: 0.5rem;
+    border-top-right-radius: 0.5rem;
+  }
+
+  .lg\:rounded-r-lg {
+    border-top-right-radius: 0.5rem;
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .lg\:rounded-b-lg {
+    border-bottom-right-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .lg\:rounded-l-lg {
+    border-top-left-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .lg\:rounded-t-full {
+    border-top-left-radius: 9999px;
+    border-top-right-radius: 9999px;
+  }
+
+  .lg\:rounded-r-full {
+    border-top-right-radius: 9999px;
+    border-bottom-right-radius: 9999px;
+  }
+
+  .lg\:rounded-b-full {
+    border-bottom-right-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .lg\:rounded-l-full {
+    border-top-left-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .lg\:rounded-tl-none {
+    border-top-left-radius: 0;
+  }
+
+  .lg\:rounded-tr-none {
+    border-top-right-radius: 0;
+  }
+
+  .lg\:rounded-br-none {
+    border-bottom-right-radius: 0;
+  }
+
+  .lg\:rounded-bl-none {
+    border-bottom-left-radius: 0;
+  }
+
+  .lg\:rounded-tl-sm {
+    border-top-left-radius: 0.125rem;
+  }
+
+  .lg\:rounded-tr-sm {
+    border-top-right-radius: 0.125rem;
+  }
+
+  .lg\:rounded-br-sm {
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .lg\:rounded-bl-sm {
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .lg\:rounded-tl {
+    border-top-left-radius: 0.25rem;
+  }
+
+  .lg\:rounded-tr {
+    border-top-right-radius: 0.25rem;
+  }
+
+  .lg\:rounded-br {
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .lg\:rounded-bl {
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .lg\:rounded-tl-md {
+    border-top-left-radius: 0.375rem;
+  }
+
+  .lg\:rounded-tr-md {
+    border-top-right-radius: 0.375rem;
+  }
+
+  .lg\:rounded-br-md {
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .lg\:rounded-bl-md {
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .lg\:rounded-tl-lg {
+    border-top-left-radius: 0.5rem;
+  }
+
+  .lg\:rounded-tr-lg {
+    border-top-right-radius: 0.5rem;
+  }
+
+  .lg\:rounded-br-lg {
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .lg\:rounded-bl-lg {
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .lg\:rounded-tl-full {
+    border-top-left-radius: 9999px;
+  }
+
+  .lg\:rounded-tr-full {
+    border-top-right-radius: 9999px;
+  }
+
+  .lg\:rounded-br-full {
+    border-bottom-right-radius: 9999px;
+  }
+
+  .lg\:rounded-bl-full {
+    border-bottom-left-radius: 9999px;
+  }
+
+  .lg\:border-solid {
+    border-style: solid;
+  }
+
+  .lg\:border-dashed {
+    border-style: dashed;
+  }
+
+  .lg\:border-dotted {
+    border-style: dotted;
+  }
+
+  .lg\:border-double {
+    border-style: double;
+  }
+
+  .lg\:border-none {
+    border-style: none;
+  }
+
+  .lg\:border-0 {
+    border-width: 0;
+  }
+
+  .lg\:border-2 {
+    border-width: 2px;
+  }
+
+  .lg\:border-4 {
+    border-width: 4px;
+  }
+
+  .lg\:border-8 {
+    border-width: 8px;
+  }
+
+  .lg\:border {
+    border-width: 1px;
+  }
+
+  .lg\:border-t-0 {
+    border-top-width: 0;
+  }
+
+  .lg\:border-r-0 {
+    border-right-width: 0;
+  }
+
+  .lg\:border-b-0 {
+    border-bottom-width: 0;
+  }
+
+  .lg\:border-l-0 {
+    border-left-width: 0;
+  }
+
+  .lg\:border-t-2 {
+    border-top-width: 2px;
+  }
+
+  .lg\:border-r-2 {
+    border-right-width: 2px;
+  }
+
+  .lg\:border-b-2 {
+    border-bottom-width: 2px;
+  }
+
+  .lg\:border-l-2 {
+    border-left-width: 2px;
+  }
+
+  .lg\:border-t-4 {
+    border-top-width: 4px;
+  }
+
+  .lg\:border-r-4 {
+    border-right-width: 4px;
+  }
+
+  .lg\:border-b-4 {
+    border-bottom-width: 4px;
+  }
+
+  .lg\:border-l-4 {
+    border-left-width: 4px;
+  }
+
+  .lg\:border-t-8 {
+    border-top-width: 8px;
+  }
+
+  .lg\:border-r-8 {
+    border-right-width: 8px;
+  }
+
+  .lg\:border-b-8 {
+    border-bottom-width: 8px;
+  }
+
+  .lg\:border-l-8 {
+    border-left-width: 8px;
+  }
+
+  .lg\:border-t {
+    border-top-width: 1px;
+  }
+
+  .lg\:border-r {
+    border-right-width: 1px;
+  }
+
+  .lg\:border-b {
+    border-bottom-width: 1px;
+  }
+
+  .lg\:border-l {
+    border-left-width: 1px;
+  }
+
+  .lg\:box-border {
+    box-sizing: border-box;
+  }
+
+  .lg\:box-content {
+    box-sizing: content-box;
+  }
+
+  .lg\:cursor-auto {
+    cursor: auto;
+  }
+
+  .lg\:cursor-default {
+    cursor: default;
+  }
+
+  .lg\:cursor-pointer {
+    cursor: pointer;
+  }
+
+  .lg\:cursor-wait {
+    cursor: wait;
+  }
+
+  .lg\:cursor-text {
+    cursor: text;
+  }
+
+  .lg\:cursor-move {
+    cursor: move;
+  }
+
+  .lg\:cursor-not-allowed {
+    cursor: not-allowed;
+  }
+
+  .lg\:block {
+    display: block;
+  }
+
+  .lg\:inline-block {
+    display: inline-block;
+  }
+
+  .lg\:inline {
+    display: inline;
+  }
+
+  .lg\:flex {
+    display: flex;
+  }
+
+  .lg\:inline-flex {
+    display: inline-flex;
+  }
+
+  .lg\:table {
+    display: table;
+  }
+
+  .lg\:table-caption {
+    display: table-caption;
+  }
+
+  .lg\:table-cell {
+    display: table-cell;
+  }
+
+  .lg\:table-column {
+    display: table-column;
+  }
+
+  .lg\:table-column-group {
+    display: table-column-group;
+  }
+
+  .lg\:table-footer-group {
+    display: table-footer-group;
+  }
+
+  .lg\:table-header-group {
+    display: table-header-group;
+  }
+
+  .lg\:table-row-group {
+    display: table-row-group;
+  }
+
+  .lg\:table-row {
+    display: table-row;
+  }
+
+  .lg\:flow-root {
+    display: flow-root;
+  }
+
+  .lg\:grid {
+    display: grid;
+  }
+
+  .lg\:inline-grid {
+    display: inline-grid;
+  }
+
+  .lg\:contents {
+    display: contents;
+  }
+
+  .lg\:hidden {
+    display: none;
+  }
+
+  .lg\:flex-row {
+    flex-direction: row;
+  }
+
+  .lg\:flex-row-reverse {
+    flex-direction: row-reverse;
+  }
+
+  .lg\:flex-col {
+    flex-direction: column;
+  }
+
+  .lg\:flex-col-reverse {
+    flex-direction: column-reverse;
+  }
+
+  .lg\:flex-wrap {
+    flex-wrap: wrap;
+  }
+
+  .lg\:flex-wrap-reverse {
+    flex-wrap: wrap-reverse;
+  }
+
+  .lg\:flex-no-wrap {
+    flex-wrap: nowrap;
+  }
+
+  .lg\:place-items-auto {
+    place-items: auto;
+  }
+
+  .lg\:place-items-start {
+    place-items: start;
+  }
+
+  .lg\:place-items-end {
+    place-items: end;
+  }
+
+  .lg\:place-items-center {
+    place-items: center;
+  }
+
+  .lg\:place-items-stretch {
+    place-items: stretch;
+  }
+
+  .lg\:place-content-center {
+    place-content: center;
+  }
+
+  .lg\:place-content-start {
+    place-content: start;
+  }
+
+  .lg\:place-content-end {
+    place-content: end;
+  }
+
+  .lg\:place-content-between {
+    place-content: space-between;
+  }
+
+  .lg\:place-content-around {
+    place-content: space-around;
+  }
+
+  .lg\:place-content-evenly {
+    place-content: space-evenly;
+  }
+
+  .lg\:place-content-stretch {
+    place-content: stretch;
+  }
+
+  .lg\:place-self-auto {
+    place-self: auto;
+  }
+
+  .lg\:place-self-start {
+    place-self: start;
+  }
+
+  .lg\:place-self-end {
+    place-self: end;
+  }
+
+  .lg\:place-self-center {
+    place-self: center;
+  }
+
+  .lg\:place-self-stretch {
+    place-self: stretch;
+  }
+
+  .lg\:items-start {
+    align-items: flex-start;
+  }
+
+  .lg\:items-end {
+    align-items: flex-end;
+  }
+
+  .lg\:items-center {
+    align-items: center;
+  }
+
+  .lg\:items-baseline {
+    align-items: baseline;
+  }
+
+  .lg\:items-stretch {
+    align-items: stretch;
+  }
+
+  .lg\:content-center {
+    align-content: center;
+  }
+
+  .lg\:content-start {
+    align-content: flex-start;
+  }
+
+  .lg\:content-end {
+    align-content: flex-end;
+  }
+
+  .lg\:content-between {
+    align-content: space-between;
+  }
+
+  .lg\:content-around {
+    align-content: space-around;
+  }
+
+  .lg\:content-evenly {
+    align-content: space-evenly;
+  }
+
+  .lg\:self-auto {
+    align-self: auto;
+  }
+
+  .lg\:self-start {
+    align-self: flex-start;
+  }
+
+  .lg\:self-end {
+    align-self: flex-end;
+  }
+
+  .lg\:self-center {
+    align-self: center;
+  }
+
+  .lg\:self-stretch {
+    align-self: stretch;
+  }
+
+  .lg\:justify-items-auto {
+    justify-items: auto;
+  }
+
+  .lg\:justify-items-start {
+    justify-items: start;
+  }
+
+  .lg\:justify-items-end {
+    justify-items: end;
+  }
+
+  .lg\:justify-items-center {
+    justify-items: center;
+  }
+
+  .lg\:justify-items-stretch {
+    justify-items: stretch;
+  }
+
+  .lg\:justify-start {
+    justify-content: flex-start;
+  }
+
+  .lg\:justify-end {
+    justify-content: flex-end;
+  }
+
+  .lg\:justify-center {
+    justify-content: center;
+  }
+
+  .lg\:justify-between {
+    justify-content: space-between;
+  }
+
+  .lg\:justify-around {
+    justify-content: space-around;
+  }
+
+  .lg\:justify-evenly {
+    justify-content: space-evenly;
+  }
+
+  .lg\:justify-self-auto {
+    justify-self: auto;
+  }
+
+  .lg\:justify-self-start {
+    justify-self: start;
+  }
+
+  .lg\:justify-self-end {
+    justify-self: end;
+  }
+
+  .lg\:justify-self-center {
+    justify-self: center;
+  }
+
+  .lg\:justify-self-stretch {
+    justify-self: stretch;
+  }
+
+  .lg\:flex-1 {
+    flex: 1 1 0%;
+  }
+
+  .lg\:flex-auto {
+    flex: 1 1 auto;
+  }
+
+  .lg\:flex-initial {
+    flex: 0 1 auto;
+  }
+
+  .lg\:flex-none {
+    flex: none;
+  }
+
+  .lg\:flex-grow-0 {
+    flex-grow: 0;
+  }
+
+  .lg\:flex-grow {
+    flex-grow: 1;
+  }
+
+  .lg\:flex-shrink-0 {
+    flex-shrink: 0;
+  }
+
+  .lg\:flex-shrink {
+    flex-shrink: 1;
+  }
+
+  .lg\:order-1 {
+    order: 1;
+  }
+
+  .lg\:order-2 {
+    order: 2;
+  }
+
+  .lg\:order-3 {
+    order: 3;
+  }
+
+  .lg\:order-4 {
+    order: 4;
+  }
+
+  .lg\:order-5 {
+    order: 5;
+  }
+
+  .lg\:order-6 {
+    order: 6;
+  }
+
+  .lg\:order-7 {
+    order: 7;
+  }
+
+  .lg\:order-8 {
+    order: 8;
+  }
+
+  .lg\:order-9 {
+    order: 9;
+  }
+
+  .lg\:order-10 {
+    order: 10;
+  }
+
+  .lg\:order-11 {
+    order: 11;
+  }
+
+  .lg\:order-12 {
+    order: 12;
+  }
+
+  .lg\:order-first {
+    order: -9999;
+  }
+
+  .lg\:order-last {
+    order: 9999;
+  }
+
+  .lg\:order-none {
+    order: 0;
+  }
+
+  .lg\:float-right {
+    float: right;
+  }
+
+  .lg\:float-left {
+    float: left;
+  }
+
+  .lg\:float-none {
+    float: none;
+  }
+
+  .lg\:clearfix:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+
+  .lg\:clear-left {
+    clear: left;
+  }
+
+  .lg\:clear-right {
+    clear: right;
+  }
+
+  .lg\:clear-both {
+    clear: both;
+  }
+
+  .lg\:clear-none {
+    clear: none;
+  }
+
+  .lg\:font-sans {
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  }
+
+  .lg\:font-serif {
+    font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+  }
+
+  .lg\:font-mono {
+    font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+  }
+
+  .lg\:font-hairline {
+    font-weight: 100;
+  }
+
+  .lg\:font-thin {
+    font-weight: 200;
+  }
+
+  .lg\:font-light {
+    font-weight: 300;
+  }
+
+  .lg\:font-normal {
+    font-weight: 400;
+  }
+
+  .lg\:font-medium {
+    font-weight: 500;
+  }
+
+  .lg\:font-semibold {
+    font-weight: 600;
+  }
+
+  .lg\:font-bold {
+    font-weight: 700;
+  }
+
+  .lg\:font-extrabold {
+    font-weight: 800;
+  }
+
+  .lg\:font-black {
+    font-weight: 900;
+  }
+
+  .lg\:hover\:font-hairline:hover {
+    font-weight: 100;
+  }
+
+  .lg\:hover\:font-thin:hover {
+    font-weight: 200;
+  }
+
+  .lg\:hover\:font-light:hover {
+    font-weight: 300;
+  }
+
+  .lg\:hover\:font-normal:hover {
+    font-weight: 400;
+  }
+
+  .lg\:hover\:font-medium:hover {
+    font-weight: 500;
+  }
+
+  .lg\:hover\:font-semibold:hover {
+    font-weight: 600;
+  }
+
+  .lg\:hover\:font-bold:hover {
+    font-weight: 700;
+  }
+
+  .lg\:hover\:font-extrabold:hover {
+    font-weight: 800;
+  }
+
+  .lg\:hover\:font-black:hover {
+    font-weight: 900;
+  }
+
+  .lg\:focus\:font-hairline:focus {
+    font-weight: 100;
+  }
+
+  .lg\:focus\:font-thin:focus {
+    font-weight: 200;
+  }
+
+  .lg\:focus\:font-light:focus {
+    font-weight: 300;
+  }
+
+  .lg\:focus\:font-normal:focus {
+    font-weight: 400;
+  }
+
+  .lg\:focus\:font-medium:focus {
+    font-weight: 500;
+  }
+
+  .lg\:focus\:font-semibold:focus {
+    font-weight: 600;
+  }
+
+  .lg\:focus\:font-bold:focus {
+    font-weight: 700;
+  }
+
+  .lg\:focus\:font-extrabold:focus {
+    font-weight: 800;
+  }
+
+  .lg\:focus\:font-black:focus {
+    font-weight: 900;
+  }
+
+  .lg\:h-0 {
+    height: 0;
+  }
+
+  .lg\:h-1 {
+    height: 0.25rem;
+  }
+
+  .lg\:h-2 {
+    height: 0.5rem;
+  }
+
+  .lg\:h-3 {
+    height: 0.75rem;
+  }
+
+  .lg\:h-4 {
+    height: 1rem;
+  }
+
+  .lg\:h-5 {
+    height: 1.25rem;
+  }
+
+  .lg\:h-6 {
+    height: 1.5rem;
+  }
+
+  .lg\:h-8 {
+    height: 2rem;
+  }
+
+  .lg\:h-10 {
+    height: 2.5rem;
+  }
+
+  .lg\:h-12 {
+    height: 3rem;
+  }
+
+  .lg\:h-16 {
+    height: 4rem;
+  }
+
+  .lg\:h-20 {
+    height: 5rem;
+  }
+
+  .lg\:h-24 {
+    height: 6rem;
+  }
+
+  .lg\:h-32 {
+    height: 8rem;
+  }
+
+  .lg\:h-40 {
+    height: 10rem;
+  }
+
+  .lg\:h-48 {
+    height: 12rem;
+  }
+
+  .lg\:h-56 {
+    height: 14rem;
+  }
+
+  .lg\:h-64 {
+    height: 16rem;
+  }
+
+  .lg\:h-auto {
+    height: auto;
+  }
+
+  .lg\:h-px {
+    height: 1px;
+  }
+
+  .lg\:h-full {
+    height: 100%;
+  }
+
+  .lg\:h-screen {
+    height: 100vh;
+  }
+
+  .lg\:text-xs {
+    font-size: 0.75rem;
+  }
+
+  .lg\:text-sm {
+    font-size: 0.875rem;
+  }
+
+  .lg\:text-base {
+    font-size: 1rem;
+  }
+
+  .lg\:text-lg {
+    font-size: 1.125rem;
+  }
+
+  .lg\:text-xl {
+    font-size: 1.25rem;
+  }
+
+  .lg\:text-2xl {
+    font-size: 1.5rem;
+  }
+
+  .lg\:text-3xl {
+    font-size: 1.875rem;
+  }
+
+  .lg\:text-4xl {
+    font-size: 2.25rem;
+  }
+
+  .lg\:text-5xl {
+    font-size: 3rem;
+  }
+
+  .lg\:text-6xl {
+    font-size: 4rem;
+  }
+
+  .lg\:leading-3 {
+    line-height: .75rem;
+  }
+
+  .lg\:leading-4 {
+    line-height: 1rem;
+  }
+
+  .lg\:leading-5 {
+    line-height: 1.25rem;
+  }
+
+  .lg\:leading-6 {
+    line-height: 1.5rem;
+  }
+
+  .lg\:leading-7 {
+    line-height: 1.75rem;
+  }
+
+  .lg\:leading-8 {
+    line-height: 2rem;
+  }
+
+  .lg\:leading-9 {
+    line-height: 2.25rem;
+  }
+
+  .lg\:leading-10 {
+    line-height: 2.5rem;
+  }
+
+  .lg\:leading-none {
+    line-height: 1;
+  }
+
+  .lg\:leading-tight {
+    line-height: 1.25;
+  }
+
+  .lg\:leading-snug {
+    line-height: 1.375;
+  }
+
+  .lg\:leading-normal {
+    line-height: 1.5;
+  }
+
+  .lg\:leading-relaxed {
+    line-height: 1.625;
+  }
+
+  .lg\:leading-loose {
+    line-height: 2;
+  }
+
+  .lg\:list-inside {
+    list-style-position: inside;
+  }
+
+  .lg\:list-outside {
+    list-style-position: outside;
+  }
+
+  .lg\:list-none {
+    list-style-type: none;
+  }
+
+  .lg\:list-disc {
+    list-style-type: disc;
+  }
+
+  .lg\:list-decimal {
+    list-style-type: decimal;
+  }
+
+  .lg\:m-0 {
+    margin: 0;
+  }
+
+  .lg\:m-1 {
+    margin: 0.25rem;
+  }
+
+  .lg\:m-2 {
+    margin: 0.5rem;
+  }
+
+  .lg\:m-3 {
+    margin: 0.75rem;
+  }
+
+  .lg\:m-4 {
+    margin: 1rem;
+  }
+
+  .lg\:m-5 {
+    margin: 1.25rem;
+  }
+
+  .lg\:m-6 {
+    margin: 1.5rem;
+  }
+
+  .lg\:m-8 {
+    margin: 2rem;
+  }
+
+  .lg\:m-10 {
+    margin: 2.5rem;
+  }
+
+  .lg\:m-12 {
+    margin: 3rem;
+  }
+
+  .lg\:m-16 {
+    margin: 4rem;
+  }
+
+  .lg\:m-20 {
+    margin: 5rem;
+  }
+
+  .lg\:m-24 {
+    margin: 6rem;
+  }
+
+  .lg\:m-32 {
+    margin: 8rem;
+  }
+
+  .lg\:m-40 {
+    margin: 10rem;
+  }
+
+  .lg\:m-48 {
+    margin: 12rem;
+  }
+
+  .lg\:m-56 {
+    margin: 14rem;
+  }
+
+  .lg\:m-64 {
+    margin: 16rem;
+  }
+
+  .lg\:m-auto {
+    margin: auto;
+  }
+
+  .lg\:m-px {
+    margin: 1px;
+  }
+
+  .lg\:-m-1 {
+    margin: -0.25rem;
+  }
+
+  .lg\:-m-2 {
+    margin: -0.5rem;
+  }
+
+  .lg\:-m-3 {
+    margin: -0.75rem;
+  }
+
+  .lg\:-m-4 {
+    margin: -1rem;
+  }
+
+  .lg\:-m-5 {
+    margin: -1.25rem;
+  }
+
+  .lg\:-m-6 {
+    margin: -1.5rem;
+  }
+
+  .lg\:-m-8 {
+    margin: -2rem;
+  }
+
+  .lg\:-m-10 {
+    margin: -2.5rem;
+  }
+
+  .lg\:-m-12 {
+    margin: -3rem;
+  }
+
+  .lg\:-m-16 {
+    margin: -4rem;
+  }
+
+  .lg\:-m-20 {
+    margin: -5rem;
+  }
+
+  .lg\:-m-24 {
+    margin: -6rem;
+  }
+
+  .lg\:-m-32 {
+    margin: -8rem;
+  }
+
+  .lg\:-m-40 {
+    margin: -10rem;
+  }
+
+  .lg\:-m-48 {
+    margin: -12rem;
+  }
+
+  .lg\:-m-56 {
+    margin: -14rem;
+  }
+
+  .lg\:-m-64 {
+    margin: -16rem;
+  }
+
+  .lg\:-m-px {
+    margin: -1px;
+  }
+
+  .lg\:my-0 {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .lg\:mx-0 {
+    margin-left: 0;
+    margin-right: 0;
+  }
+
+  .lg\:my-1 {
+    margin-top: 0.25rem;
+    margin-bottom: 0.25rem;
+  }
+
+  .lg\:mx-1 {
+    margin-left: 0.25rem;
+    margin-right: 0.25rem;
+  }
+
+  .lg\:my-2 {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+  }
+
+  .lg\:mx-2 {
+    margin-left: 0.5rem;
+    margin-right: 0.5rem;
+  }
+
+  .lg\:my-3 {
+    margin-top: 0.75rem;
+    margin-bottom: 0.75rem;
+  }
+
+  .lg\:mx-3 {
+    margin-left: 0.75rem;
+    margin-right: 0.75rem;
+  }
+
+  .lg\:my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+  }
+
+  .lg\:mx-4 {
+    margin-left: 1rem;
+    margin-right: 1rem;
+  }
+
+  .lg\:my-5 {
+    margin-top: 1.25rem;
+    margin-bottom: 1.25rem;
+  }
+
+  .lg\:mx-5 {
+    margin-left: 1.25rem;
+    margin-right: 1.25rem;
+  }
+
+  .lg\:my-6 {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+
+  .lg\:mx-6 {
+    margin-left: 1.5rem;
+    margin-right: 1.5rem;
+  }
+
+  .lg\:my-8 {
+    margin-top: 2rem;
+    margin-bottom: 2rem;
+  }
+
+  .lg\:mx-8 {
+    margin-left: 2rem;
+    margin-right: 2rem;
+  }
+
+  .lg\:my-10 {
+    margin-top: 2.5rem;
+    margin-bottom: 2.5rem;
+  }
+
+  .lg\:mx-10 {
+    margin-left: 2.5rem;
+    margin-right: 2.5rem;
+  }
+
+  .lg\:my-12 {
+    margin-top: 3rem;
+    margin-bottom: 3rem;
+  }
+
+  .lg\:mx-12 {
+    margin-left: 3rem;
+    margin-right: 3rem;
+  }
+
+  .lg\:my-16 {
+    margin-top: 4rem;
+    margin-bottom: 4rem;
+  }
+
+  .lg\:mx-16 {
+    margin-left: 4rem;
+    margin-right: 4rem;
+  }
+
+  .lg\:my-20 {
+    margin-top: 5rem;
+    margin-bottom: 5rem;
+  }
+
+  .lg\:mx-20 {
+    margin-left: 5rem;
+    margin-right: 5rem;
+  }
+
+  .lg\:my-24 {
+    margin-top: 6rem;
+    margin-bottom: 6rem;
+  }
+
+  .lg\:mx-24 {
+    margin-left: 6rem;
+    margin-right: 6rem;
+  }
+
+  .lg\:my-32 {
+    margin-top: 8rem;
+    margin-bottom: 8rem;
+  }
+
+  .lg\:mx-32 {
+    margin-left: 8rem;
+    margin-right: 8rem;
+  }
+
+  .lg\:my-40 {
+    margin-top: 10rem;
+    margin-bottom: 10rem;
+  }
+
+  .lg\:mx-40 {
+    margin-left: 10rem;
+    margin-right: 10rem;
+  }
+
+  .lg\:my-48 {
+    margin-top: 12rem;
+    margin-bottom: 12rem;
+  }
+
+  .lg\:mx-48 {
+    margin-left: 12rem;
+    margin-right: 12rem;
+  }
+
+  .lg\:my-56 {
+    margin-top: 14rem;
+    margin-bottom: 14rem;
+  }
+
+  .lg\:mx-56 {
+    margin-left: 14rem;
+    margin-right: 14rem;
+  }
+
+  .lg\:my-64 {
+    margin-top: 16rem;
+    margin-bottom: 16rem;
+  }
+
+  .lg\:mx-64 {
+    margin-left: 16rem;
+    margin-right: 16rem;
+  }
+
+  .lg\:my-auto {
+    margin-top: auto;
+    margin-bottom: auto;
+  }
+
+  .lg\:mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .lg\:my-px {
+    margin-top: 1px;
+    margin-bottom: 1px;
+  }
+
+  .lg\:mx-px {
+    margin-left: 1px;
+    margin-right: 1px;
+  }
+
+  .lg\:-my-1 {
+    margin-top: -0.25rem;
+    margin-bottom: -0.25rem;
+  }
+
+  .lg\:-mx-1 {
+    margin-left: -0.25rem;
+    margin-right: -0.25rem;
+  }
+
+  .lg\:-my-2 {
+    margin-top: -0.5rem;
+    margin-bottom: -0.5rem;
+  }
+
+  .lg\:-mx-2 {
+    margin-left: -0.5rem;
+    margin-right: -0.5rem;
+  }
+
+  .lg\:-my-3 {
+    margin-top: -0.75rem;
+    margin-bottom: -0.75rem;
+  }
+
+  .lg\:-mx-3 {
+    margin-left: -0.75rem;
+    margin-right: -0.75rem;
+  }
+
+  .lg\:-my-4 {
+    margin-top: -1rem;
+    margin-bottom: -1rem;
+  }
+
+  .lg\:-mx-4 {
+    margin-left: -1rem;
+    margin-right: -1rem;
+  }
+
+  .lg\:-my-5 {
+    margin-top: -1.25rem;
+    margin-bottom: -1.25rem;
+  }
+
+  .lg\:-mx-5 {
+    margin-left: -1.25rem;
+    margin-right: -1.25rem;
+  }
+
+  .lg\:-my-6 {
+    margin-top: -1.5rem;
+    margin-bottom: -1.5rem;
+  }
+
+  .lg\:-mx-6 {
+    margin-left: -1.5rem;
+    margin-right: -1.5rem;
+  }
+
+  .lg\:-my-8 {
+    margin-top: -2rem;
+    margin-bottom: -2rem;
+  }
+
+  .lg\:-mx-8 {
+    margin-left: -2rem;
+    margin-right: -2rem;
+  }
+
+  .lg\:-my-10 {
+    margin-top: -2.5rem;
+    margin-bottom: -2.5rem;
+  }
+
+  .lg\:-mx-10 {
+    margin-left: -2.5rem;
+    margin-right: -2.5rem;
+  }
+
+  .lg\:-my-12 {
+    margin-top: -3rem;
+    margin-bottom: -3rem;
+  }
+
+  .lg\:-mx-12 {
+    margin-left: -3rem;
+    margin-right: -3rem;
+  }
+
+  .lg\:-my-16 {
+    margin-top: -4rem;
+    margin-bottom: -4rem;
+  }
+
+  .lg\:-mx-16 {
+    margin-left: -4rem;
+    margin-right: -4rem;
+  }
+
+  .lg\:-my-20 {
+    margin-top: -5rem;
+    margin-bottom: -5rem;
+  }
+
+  .lg\:-mx-20 {
+    margin-left: -5rem;
+    margin-right: -5rem;
+  }
+
+  .lg\:-my-24 {
+    margin-top: -6rem;
+    margin-bottom: -6rem;
+  }
+
+  .lg\:-mx-24 {
+    margin-left: -6rem;
+    margin-right: -6rem;
+  }
+
+  .lg\:-my-32 {
+    margin-top: -8rem;
+    margin-bottom: -8rem;
+  }
+
+  .lg\:-mx-32 {
+    margin-left: -8rem;
+    margin-right: -8rem;
+  }
+
+  .lg\:-my-40 {
+    margin-top: -10rem;
+    margin-bottom: -10rem;
+  }
+
+  .lg\:-mx-40 {
+    margin-left: -10rem;
+    margin-right: -10rem;
+  }
+
+  .lg\:-my-48 {
+    margin-top: -12rem;
+    margin-bottom: -12rem;
+  }
+
+  .lg\:-mx-48 {
+    margin-left: -12rem;
+    margin-right: -12rem;
+  }
+
+  .lg\:-my-56 {
+    margin-top: -14rem;
+    margin-bottom: -14rem;
+  }
+
+  .lg\:-mx-56 {
+    margin-left: -14rem;
+    margin-right: -14rem;
+  }
+
+  .lg\:-my-64 {
+    margin-top: -16rem;
+    margin-bottom: -16rem;
+  }
+
+  .lg\:-mx-64 {
+    margin-left: -16rem;
+    margin-right: -16rem;
+  }
+
+  .lg\:-my-px {
+    margin-top: -1px;
+    margin-bottom: -1px;
+  }
+
+  .lg\:-mx-px {
+    margin-left: -1px;
+    margin-right: -1px;
+  }
+
+  .lg\:mt-0 {
+    margin-top: 0;
+  }
+
+  .lg\:mr-0 {
+    margin-right: 0;
+  }
+
+  .lg\:mb-0 {
+    margin-bottom: 0;
+  }
+
+  .lg\:ml-0 {
+    margin-left: 0;
+  }
+
+  .lg\:mt-1 {
+    margin-top: 0.25rem;
+  }
+
+  .lg\:mr-1 {
+    margin-right: 0.25rem;
+  }
+
+  .lg\:mb-1 {
+    margin-bottom: 0.25rem;
+  }
+
+  .lg\:ml-1 {
+    margin-left: 0.25rem;
+  }
+
+  .lg\:mt-2 {
+    margin-top: 0.5rem;
+  }
+
+  .lg\:mr-2 {
+    margin-right: 0.5rem;
+  }
+
+  .lg\:mb-2 {
+    margin-bottom: 0.5rem;
+  }
+
+  .lg\:ml-2 {
+    margin-left: 0.5rem;
+  }
+
+  .lg\:mt-3 {
+    margin-top: 0.75rem;
+  }
+
+  .lg\:mr-3 {
+    margin-right: 0.75rem;
+  }
+
+  .lg\:mb-3 {
+    margin-bottom: 0.75rem;
+  }
+
+  .lg\:ml-3 {
+    margin-left: 0.75rem;
+  }
+
+  .lg\:mt-4 {
+    margin-top: 1rem;
+  }
+
+  .lg\:mr-4 {
+    margin-right: 1rem;
+  }
+
+  .lg\:mb-4 {
+    margin-bottom: 1rem;
+  }
+
+  .lg\:ml-4 {
+    margin-left: 1rem;
+  }
+
+  .lg\:mt-5 {
+    margin-top: 1.25rem;
+  }
+
+  .lg\:mr-5 {
+    margin-right: 1.25rem;
+  }
+
+  .lg\:mb-5 {
+    margin-bottom: 1.25rem;
+  }
+
+  .lg\:ml-5 {
+    margin-left: 1.25rem;
+  }
+
+  .lg\:mt-6 {
+    margin-top: 1.5rem;
+  }
+
+  .lg\:mr-6 {
+    margin-right: 1.5rem;
+  }
+
+  .lg\:mb-6 {
+    margin-bottom: 1.5rem;
+  }
+
+  .lg\:ml-6 {
+    margin-left: 1.5rem;
+  }
+
+  .lg\:mt-8 {
+    margin-top: 2rem;
+  }
+
+  .lg\:mr-8 {
+    margin-right: 2rem;
+  }
+
+  .lg\:mb-8 {
+    margin-bottom: 2rem;
+  }
+
+  .lg\:ml-8 {
+    margin-left: 2rem;
+  }
+
+  .lg\:mt-10 {
+    margin-top: 2.5rem;
+  }
+
+  .lg\:mr-10 {
+    margin-right: 2.5rem;
+  }
+
+  .lg\:mb-10 {
+    margin-bottom: 2.5rem;
+  }
+
+  .lg\:ml-10 {
+    margin-left: 2.5rem;
+  }
+
+  .lg\:mt-12 {
+    margin-top: 3rem;
+  }
+
+  .lg\:mr-12 {
+    margin-right: 3rem;
+  }
+
+  .lg\:mb-12 {
+    margin-bottom: 3rem;
+  }
+
+  .lg\:ml-12 {
+    margin-left: 3rem;
+  }
+
+  .lg\:mt-16 {
+    margin-top: 4rem;
+  }
+
+  .lg\:mr-16 {
+    margin-right: 4rem;
+  }
+
+  .lg\:mb-16 {
+    margin-bottom: 4rem;
+  }
+
+  .lg\:ml-16 {
+    margin-left: 4rem;
+  }
+
+  .lg\:mt-20 {
+    margin-top: 5rem;
+  }
+
+  .lg\:mr-20 {
+    margin-right: 5rem;
+  }
+
+  .lg\:mb-20 {
+    margin-bottom: 5rem;
+  }
+
+  .lg\:ml-20 {
+    margin-left: 5rem;
+  }
+
+  .lg\:mt-24 {
+    margin-top: 6rem;
+  }
+
+  .lg\:mr-24 {
+    margin-right: 6rem;
+  }
+
+  .lg\:mb-24 {
+    margin-bottom: 6rem;
+  }
+
+  .lg\:ml-24 {
+    margin-left: 6rem;
+  }
+
+  .lg\:mt-32 {
+    margin-top: 8rem;
+  }
+
+  .lg\:mr-32 {
+    margin-right: 8rem;
+  }
+
+  .lg\:mb-32 {
+    margin-bottom: 8rem;
+  }
+
+  .lg\:ml-32 {
+    margin-left: 8rem;
+  }
+
+  .lg\:mt-40 {
+    margin-top: 10rem;
+  }
+
+  .lg\:mr-40 {
+    margin-right: 10rem;
+  }
+
+  .lg\:mb-40 {
+    margin-bottom: 10rem;
+  }
+
+  .lg\:ml-40 {
+    margin-left: 10rem;
+  }
+
+  .lg\:mt-48 {
+    margin-top: 12rem;
+  }
+
+  .lg\:mr-48 {
+    margin-right: 12rem;
+  }
+
+  .lg\:mb-48 {
+    margin-bottom: 12rem;
+  }
+
+  .lg\:ml-48 {
+    margin-left: 12rem;
+  }
+
+  .lg\:mt-56 {
+    margin-top: 14rem;
+  }
+
+  .lg\:mr-56 {
+    margin-right: 14rem;
+  }
+
+  .lg\:mb-56 {
+    margin-bottom: 14rem;
+  }
+
+  .lg\:ml-56 {
+    margin-left: 14rem;
+  }
+
+  .lg\:mt-64 {
+    margin-top: 16rem;
+  }
+
+  .lg\:mr-64 {
+    margin-right: 16rem;
+  }
+
+  .lg\:mb-64 {
+    margin-bottom: 16rem;
+  }
+
+  .lg\:ml-64 {
+    margin-left: 16rem;
+  }
+
+  .lg\:mt-auto {
+    margin-top: auto;
+  }
+
+  .lg\:mr-auto {
+    margin-right: auto;
+  }
+
+  .lg\:mb-auto {
+    margin-bottom: auto;
+  }
+
+  .lg\:ml-auto {
+    margin-left: auto;
+  }
+
+  .lg\:mt-px {
+    margin-top: 1px;
+  }
+
+  .lg\:mr-px {
+    margin-right: 1px;
+  }
+
+  .lg\:mb-px {
+    margin-bottom: 1px;
+  }
+
+  .lg\:ml-px {
+    margin-left: 1px;
+  }
+
+  .lg\:-mt-1 {
+    margin-top: -0.25rem;
+  }
+
+  .lg\:-mr-1 {
+    margin-right: -0.25rem;
+  }
+
+  .lg\:-mb-1 {
+    margin-bottom: -0.25rem;
+  }
+
+  .lg\:-ml-1 {
+    margin-left: -0.25rem;
+  }
+
+  .lg\:-mt-2 {
+    margin-top: -0.5rem;
+  }
+
+  .lg\:-mr-2 {
+    margin-right: -0.5rem;
+  }
+
+  .lg\:-mb-2 {
+    margin-bottom: -0.5rem;
+  }
+
+  .lg\:-ml-2 {
+    margin-left: -0.5rem;
+  }
+
+  .lg\:-mt-3 {
+    margin-top: -0.75rem;
+  }
+
+  .lg\:-mr-3 {
+    margin-right: -0.75rem;
+  }
+
+  .lg\:-mb-3 {
+    margin-bottom: -0.75rem;
+  }
+
+  .lg\:-ml-3 {
+    margin-left: -0.75rem;
+  }
+
+  .lg\:-mt-4 {
+    margin-top: -1rem;
+  }
+
+  .lg\:-mr-4 {
+    margin-right: -1rem;
+  }
+
+  .lg\:-mb-4 {
+    margin-bottom: -1rem;
+  }
+
+  .lg\:-ml-4 {
+    margin-left: -1rem;
+  }
+
+  .lg\:-mt-5 {
+    margin-top: -1.25rem;
+  }
+
+  .lg\:-mr-5 {
+    margin-right: -1.25rem;
+  }
+
+  .lg\:-mb-5 {
+    margin-bottom: -1.25rem;
+  }
+
+  .lg\:-ml-5 {
+    margin-left: -1.25rem;
+  }
+
+  .lg\:-mt-6 {
+    margin-top: -1.5rem;
+  }
+
+  .lg\:-mr-6 {
+    margin-right: -1.5rem;
+  }
+
+  .lg\:-mb-6 {
+    margin-bottom: -1.5rem;
+  }
+
+  .lg\:-ml-6 {
+    margin-left: -1.5rem;
+  }
+
+  .lg\:-mt-8 {
+    margin-top: -2rem;
+  }
+
+  .lg\:-mr-8 {
+    margin-right: -2rem;
+  }
+
+  .lg\:-mb-8 {
+    margin-bottom: -2rem;
+  }
+
+  .lg\:-ml-8 {
+    margin-left: -2rem;
+  }
+
+  .lg\:-mt-10 {
+    margin-top: -2.5rem;
+  }
+
+  .lg\:-mr-10 {
+    margin-right: -2.5rem;
+  }
+
+  .lg\:-mb-10 {
+    margin-bottom: -2.5rem;
+  }
+
+  .lg\:-ml-10 {
+    margin-left: -2.5rem;
+  }
+
+  .lg\:-mt-12 {
+    margin-top: -3rem;
+  }
+
+  .lg\:-mr-12 {
+    margin-right: -3rem;
+  }
+
+  .lg\:-mb-12 {
+    margin-bottom: -3rem;
+  }
+
+  .lg\:-ml-12 {
+    margin-left: -3rem;
+  }
+
+  .lg\:-mt-16 {
+    margin-top: -4rem;
+  }
+
+  .lg\:-mr-16 {
+    margin-right: -4rem;
+  }
+
+  .lg\:-mb-16 {
+    margin-bottom: -4rem;
+  }
+
+  .lg\:-ml-16 {
+    margin-left: -4rem;
+  }
+
+  .lg\:-mt-20 {
+    margin-top: -5rem;
+  }
+
+  .lg\:-mr-20 {
+    margin-right: -5rem;
+  }
+
+  .lg\:-mb-20 {
+    margin-bottom: -5rem;
+  }
+
+  .lg\:-ml-20 {
+    margin-left: -5rem;
+  }
+
+  .lg\:-mt-24 {
+    margin-top: -6rem;
+  }
+
+  .lg\:-mr-24 {
+    margin-right: -6rem;
+  }
+
+  .lg\:-mb-24 {
+    margin-bottom: -6rem;
+  }
+
+  .lg\:-ml-24 {
+    margin-left: -6rem;
+  }
+
+  .lg\:-mt-32 {
+    margin-top: -8rem;
+  }
+
+  .lg\:-mr-32 {
+    margin-right: -8rem;
+  }
+
+  .lg\:-mb-32 {
+    margin-bottom: -8rem;
+  }
+
+  .lg\:-ml-32 {
+    margin-left: -8rem;
+  }
+
+  .lg\:-mt-40 {
+    margin-top: -10rem;
+  }
+
+  .lg\:-mr-40 {
+    margin-right: -10rem;
+  }
+
+  .lg\:-mb-40 {
+    margin-bottom: -10rem;
+  }
+
+  .lg\:-ml-40 {
+    margin-left: -10rem;
+  }
+
+  .lg\:-mt-48 {
+    margin-top: -12rem;
+  }
+
+  .lg\:-mr-48 {
+    margin-right: -12rem;
+  }
+
+  .lg\:-mb-48 {
+    margin-bottom: -12rem;
+  }
+
+  .lg\:-ml-48 {
+    margin-left: -12rem;
+  }
+
+  .lg\:-mt-56 {
+    margin-top: -14rem;
+  }
+
+  .lg\:-mr-56 {
+    margin-right: -14rem;
+  }
+
+  .lg\:-mb-56 {
+    margin-bottom: -14rem;
+  }
+
+  .lg\:-ml-56 {
+    margin-left: -14rem;
+  }
+
+  .lg\:-mt-64 {
+    margin-top: -16rem;
+  }
+
+  .lg\:-mr-64 {
+    margin-right: -16rem;
+  }
+
+  .lg\:-mb-64 {
+    margin-bottom: -16rem;
+  }
+
+  .lg\:-ml-64 {
+    margin-left: -16rem;
+  }
+
+  .lg\:-mt-px {
+    margin-top: -1px;
+  }
+
+  .lg\:-mr-px {
+    margin-right: -1px;
+  }
+
+  .lg\:-mb-px {
+    margin-bottom: -1px;
+  }
+
+  .lg\:-ml-px {
+    margin-left: -1px;
+  }
+
+  .lg\:max-h-full {
+    max-height: 100%;
+  }
+
+  .lg\:max-h-screen {
+    max-height: 100vh;
+  }
+
+  .lg\:max-w-none {
+    max-width: none;
+  }
+
+  .lg\:max-w-xs {
+    max-width: 20rem;
+  }
+
+  .lg\:max-w-sm {
+    max-width: 24rem;
+  }
+
+  .lg\:max-w-md {
+    max-width: 28rem;
+  }
+
+  .lg\:max-w-lg {
+    max-width: 32rem;
+  }
+
+  .lg\:max-w-xl {
+    max-width: 36rem;
+  }
+
+  .lg\:max-w-2xl {
+    max-width: 42rem;
+  }
+
+  .lg\:max-w-3xl {
+    max-width: 48rem;
+  }
+
+  .lg\:max-w-4xl {
+    max-width: 56rem;
+  }
+
+  .lg\:max-w-5xl {
+    max-width: 64rem;
+  }
+
+  .lg\:max-w-6xl {
+    max-width: 72rem;
+  }
+
+  .lg\:max-w-full {
+    max-width: 100%;
+  }
+
+  .lg\:max-w-screen-sm {
+    max-width: 640px;
+  }
+
+  .lg\:max-w-screen-md {
+    max-width: 768px;
+  }
+
+  .lg\:max-w-screen-lg {
+    max-width: 1024px;
+  }
+
+  .lg\:max-w-screen-xl {
+    max-width: 1280px;
+  }
+
+  .lg\:min-h-0 {
+    min-height: 0;
+  }
+
+  .lg\:min-h-full {
+    min-height: 100%;
+  }
+
+  .lg\:min-h-screen {
+    min-height: 100vh;
+  }
+
+  .lg\:min-w-0 {
+    min-width: 0;
+  }
+
+  .lg\:min-w-full {
+    min-width: 100%;
+  }
+
+  .lg\:object-contain {
+    -o-object-fit: contain;
+       object-fit: contain;
+  }
+
+  .lg\:object-cover {
+    -o-object-fit: cover;
+       object-fit: cover;
+  }
+
+  .lg\:object-fill {
+    -o-object-fit: fill;
+       object-fit: fill;
+  }
+
+  .lg\:object-none {
+    -o-object-fit: none;
+       object-fit: none;
+  }
+
+  .lg\:object-scale-down {
+    -o-object-fit: scale-down;
+       object-fit: scale-down;
+  }
+
+  .lg\:object-bottom {
+    -o-object-position: bottom;
+       object-position: bottom;
+  }
+
+  .lg\:object-center {
+    -o-object-position: center;
+       object-position: center;
+  }
+
+  .lg\:object-left {
+    -o-object-position: left;
+       object-position: left;
+  }
+
+  .lg\:object-left-bottom {
+    -o-object-position: left bottom;
+       object-position: left bottom;
+  }
+
+  .lg\:object-left-top {
+    -o-object-position: left top;
+       object-position: left top;
+  }
+
+  .lg\:object-right {
+    -o-object-position: right;
+       object-position: right;
+  }
+
+  .lg\:object-right-bottom {
+    -o-object-position: right bottom;
+       object-position: right bottom;
+  }
+
+  .lg\:object-right-top {
+    -o-object-position: right top;
+       object-position: right top;
+  }
+
+  .lg\:object-top {
+    -o-object-position: top;
+       object-position: top;
+  }
+
+  .lg\:opacity-0 {
+    opacity: 0;
+  }
+
+  .lg\:opacity-25 {
+    opacity: 0.25;
+  }
+
+  .lg\:opacity-50 {
+    opacity: 0.5;
+  }
+
+  .lg\:opacity-75 {
+    opacity: 0.75;
+  }
+
+  .lg\:opacity-100 {
+    opacity: 1;
+  }
+
+  .lg\:hover\:opacity-0:hover {
+    opacity: 0;
+  }
+
+  .lg\:hover\:opacity-25:hover {
+    opacity: 0.25;
+  }
+
+  .lg\:hover\:opacity-50:hover {
+    opacity: 0.5;
+  }
+
+  .lg\:hover\:opacity-75:hover {
+    opacity: 0.75;
+  }
+
+  .lg\:hover\:opacity-100:hover {
+    opacity: 1;
+  }
+
+  .lg\:focus\:opacity-0:focus {
+    opacity: 0;
+  }
+
+  .lg\:focus\:opacity-25:focus {
+    opacity: 0.25;
+  }
+
+  .lg\:focus\:opacity-50:focus {
+    opacity: 0.5;
+  }
+
+  .lg\:focus\:opacity-75:focus {
+    opacity: 0.75;
+  }
+
+  .lg\:focus\:opacity-100:focus {
+    opacity: 1;
+  }
+
+  .lg\:outline-none {
+    outline: 0;
+  }
+
+  .lg\:focus\:outline-none:focus {
+    outline: 0;
+  }
+
+  .lg\:overflow-auto {
+    overflow: auto;
+  }
+
+  .lg\:overflow-hidden {
+    overflow: hidden;
+  }
+
+  .lg\:overflow-visible {
+    overflow: visible;
+  }
+
+  .lg\:overflow-scroll {
+    overflow: scroll;
+  }
+
+  .lg\:overflow-x-auto {
+    overflow-x: auto;
+  }
+
+  .lg\:overflow-y-auto {
+    overflow-y: auto;
+  }
+
+  .lg\:overflow-x-hidden {
+    overflow-x: hidden;
+  }
+
+  .lg\:overflow-y-hidden {
+    overflow-y: hidden;
+  }
+
+  .lg\:overflow-x-visible {
+    overflow-x: visible;
+  }
+
+  .lg\:overflow-y-visible {
+    overflow-y: visible;
+  }
+
+  .lg\:overflow-x-scroll {
+    overflow-x: scroll;
+  }
+
+  .lg\:overflow-y-scroll {
+    overflow-y: scroll;
+  }
+
+  .lg\:scrolling-touch {
+    -webkit-overflow-scrolling: touch;
+  }
+
+  .lg\:scrolling-auto {
+    -webkit-overflow-scrolling: auto;
+  }
+
+  .lg\:overscroll-auto {
+    -ms-scroll-chaining: chained;
+        overscroll-behavior: auto;
+  }
+
+  .lg\:overscroll-contain {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: contain;
+  }
+
+  .lg\:overscroll-none {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: none;
+  }
+
+  .lg\:overscroll-y-auto {
+    overscroll-behavior-y: auto;
+  }
+
+  .lg\:overscroll-y-contain {
+    overscroll-behavior-y: contain;
+  }
+
+  .lg\:overscroll-y-none {
+    overscroll-behavior-y: none;
+  }
+
+  .lg\:overscroll-x-auto {
+    overscroll-behavior-x: auto;
+  }
+
+  .lg\:overscroll-x-contain {
+    overscroll-behavior-x: contain;
+  }
+
+  .lg\:overscroll-x-none {
+    overscroll-behavior-x: none;
+  }
+
+  .lg\:p-0 {
+    padding: 0;
+  }
+
+  .lg\:p-1 {
+    padding: 0.25rem;
+  }
+
+  .lg\:p-2 {
+    padding: 0.5rem;
+  }
+
+  .lg\:p-3 {
+    padding: 0.75rem;
+  }
+
+  .lg\:p-4 {
+    padding: 1rem;
+  }
+
+  .lg\:p-5 {
+    padding: 1.25rem;
+  }
+
+  .lg\:p-6 {
+    padding: 1.5rem;
+  }
+
+  .lg\:p-8 {
+    padding: 2rem;
+  }
+
+  .lg\:p-10 {
+    padding: 2.5rem;
+  }
+
+  .lg\:p-12 {
+    padding: 3rem;
+  }
+
+  .lg\:p-16 {
+    padding: 4rem;
+  }
+
+  .lg\:p-20 {
+    padding: 5rem;
+  }
+
+  .lg\:p-24 {
+    padding: 6rem;
+  }
+
+  .lg\:p-32 {
+    padding: 8rem;
+  }
+
+  .lg\:p-40 {
+    padding: 10rem;
+  }
+
+  .lg\:p-48 {
+    padding: 12rem;
+  }
+
+  .lg\:p-56 {
+    padding: 14rem;
+  }
+
+  .lg\:p-64 {
+    padding: 16rem;
+  }
+
+  .lg\:p-px {
+    padding: 1px;
+  }
+
+  .lg\:py-0 {
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+
+  .lg\:px-0 {
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  .lg\:py-1 {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+  }
+
+  .lg\:px-1 {
+    padding-left: 0.25rem;
+    padding-right: 0.25rem;
+  }
+
+  .lg\:py-2 {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
+
+  .lg\:px-2 {
+    padding-left: 0.5rem;
+    padding-right: 0.5rem;
+  }
+
+  .lg\:py-3 {
+    padding-top: 0.75rem;
+    padding-bottom: 0.75rem;
+  }
+
+  .lg\:px-3 {
+    padding-left: 0.75rem;
+    padding-right: 0.75rem;
+  }
+
+  .lg\:py-4 {
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+  }
+
+  .lg\:px-4 {
+    padding-left: 1rem;
+    padding-right: 1rem;
+  }
+
+  .lg\:py-5 {
+    padding-top: 1.25rem;
+    padding-bottom: 1.25rem;
+  }
+
+  .lg\:px-5 {
+    padding-left: 1.25rem;
+    padding-right: 1.25rem;
+  }
+
+  .lg\:py-6 {
+    padding-top: 1.5rem;
+    padding-bottom: 1.5rem;
+  }
+
+  .lg\:px-6 {
+    padding-left: 1.5rem;
+    padding-right: 1.5rem;
+  }
+
+  .lg\:py-8 {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
+
+  .lg\:px-8 {
+    padding-left: 2rem;
+    padding-right: 2rem;
+  }
+
+  .lg\:py-10 {
+    padding-top: 2.5rem;
+    padding-bottom: 2.5rem;
+  }
+
+  .lg\:px-10 {
+    padding-left: 2.5rem;
+    padding-right: 2.5rem;
+  }
+
+  .lg\:py-12 {
+    padding-top: 3rem;
+    padding-bottom: 3rem;
+  }
+
+  .lg\:px-12 {
+    padding-left: 3rem;
+    padding-right: 3rem;
+  }
+
+  .lg\:py-16 {
+    padding-top: 4rem;
+    padding-bottom: 4rem;
+  }
+
+  .lg\:px-16 {
+    padding-left: 4rem;
+    padding-right: 4rem;
+  }
+
+  .lg\:py-20 {
+    padding-top: 5rem;
+    padding-bottom: 5rem;
+  }
+
+  .lg\:px-20 {
+    padding-left: 5rem;
+    padding-right: 5rem;
+  }
+
+  .lg\:py-24 {
+    padding-top: 6rem;
+    padding-bottom: 6rem;
+  }
+
+  .lg\:px-24 {
+    padding-left: 6rem;
+    padding-right: 6rem;
+  }
+
+  .lg\:py-32 {
+    padding-top: 8rem;
+    padding-bottom: 8rem;
+  }
+
+  .lg\:px-32 {
+    padding-left: 8rem;
+    padding-right: 8rem;
+  }
+
+  .lg\:py-40 {
+    padding-top: 10rem;
+    padding-bottom: 10rem;
+  }
+
+  .lg\:px-40 {
+    padding-left: 10rem;
+    padding-right: 10rem;
+  }
+
+  .lg\:py-48 {
+    padding-top: 12rem;
+    padding-bottom: 12rem;
+  }
+
+  .lg\:px-48 {
+    padding-left: 12rem;
+    padding-right: 12rem;
+  }
+
+  .lg\:py-56 {
+    padding-top: 14rem;
+    padding-bottom: 14rem;
+  }
+
+  .lg\:px-56 {
+    padding-left: 14rem;
+    padding-right: 14rem;
+  }
+
+  .lg\:py-64 {
+    padding-top: 16rem;
+    padding-bottom: 16rem;
+  }
+
+  .lg\:px-64 {
+    padding-left: 16rem;
+    padding-right: 16rem;
+  }
+
+  .lg\:py-px {
+    padding-top: 1px;
+    padding-bottom: 1px;
+  }
+
+  .lg\:px-px {
+    padding-left: 1px;
+    padding-right: 1px;
+  }
+
+  .lg\:pt-0 {
+    padding-top: 0;
+  }
+
+  .lg\:pr-0 {
+    padding-right: 0;
+  }
+
+  .lg\:pb-0 {
+    padding-bottom: 0;
+  }
+
+  .lg\:pl-0 {
+    padding-left: 0;
+  }
+
+  .lg\:pt-1 {
+    padding-top: 0.25rem;
+  }
+
+  .lg\:pr-1 {
+    padding-right: 0.25rem;
+  }
+
+  .lg\:pb-1 {
+    padding-bottom: 0.25rem;
+  }
+
+  .lg\:pl-1 {
+    padding-left: 0.25rem;
+  }
+
+  .lg\:pt-2 {
+    padding-top: 0.5rem;
+  }
+
+  .lg\:pr-2 {
+    padding-right: 0.5rem;
+  }
+
+  .lg\:pb-2 {
+    padding-bottom: 0.5rem;
+  }
+
+  .lg\:pl-2 {
+    padding-left: 0.5rem;
+  }
+
+  .lg\:pt-3 {
+    padding-top: 0.75rem;
+  }
+
+  .lg\:pr-3 {
+    padding-right: 0.75rem;
+  }
+
+  .lg\:pb-3 {
+    padding-bottom: 0.75rem;
+  }
+
+  .lg\:pl-3 {
+    padding-left: 0.75rem;
+  }
+
+  .lg\:pt-4 {
+    padding-top: 1rem;
+  }
+
+  .lg\:pr-4 {
+    padding-right: 1rem;
+  }
+
+  .lg\:pb-4 {
+    padding-bottom: 1rem;
+  }
+
+  .lg\:pl-4 {
+    padding-left: 1rem;
+  }
+
+  .lg\:pt-5 {
+    padding-top: 1.25rem;
+  }
+
+  .lg\:pr-5 {
+    padding-right: 1.25rem;
+  }
+
+  .lg\:pb-5 {
+    padding-bottom: 1.25rem;
+  }
+
+  .lg\:pl-5 {
+    padding-left: 1.25rem;
+  }
+
+  .lg\:pt-6 {
+    padding-top: 1.5rem;
+  }
+
+  .lg\:pr-6 {
+    padding-right: 1.5rem;
+  }
+
+  .lg\:pb-6 {
+    padding-bottom: 1.5rem;
+  }
+
+  .lg\:pl-6 {
+    padding-left: 1.5rem;
+  }
+
+  .lg\:pt-8 {
+    padding-top: 2rem;
+  }
+
+  .lg\:pr-8 {
+    padding-right: 2rem;
+  }
+
+  .lg\:pb-8 {
+    padding-bottom: 2rem;
+  }
+
+  .lg\:pl-8 {
+    padding-left: 2rem;
+  }
+
+  .lg\:pt-10 {
+    padding-top: 2.5rem;
+  }
+
+  .lg\:pr-10 {
+    padding-right: 2.5rem;
+  }
+
+  .lg\:pb-10 {
+    padding-bottom: 2.5rem;
+  }
+
+  .lg\:pl-10 {
+    padding-left: 2.5rem;
+  }
+
+  .lg\:pt-12 {
+    padding-top: 3rem;
+  }
+
+  .lg\:pr-12 {
+    padding-right: 3rem;
+  }
+
+  .lg\:pb-12 {
+    padding-bottom: 3rem;
+  }
+
+  .lg\:pl-12 {
+    padding-left: 3rem;
+  }
+
+  .lg\:pt-16 {
+    padding-top: 4rem;
+  }
+
+  .lg\:pr-16 {
+    padding-right: 4rem;
+  }
+
+  .lg\:pb-16 {
+    padding-bottom: 4rem;
+  }
+
+  .lg\:pl-16 {
+    padding-left: 4rem;
+  }
+
+  .lg\:pt-20 {
+    padding-top: 5rem;
+  }
+
+  .lg\:pr-20 {
+    padding-right: 5rem;
+  }
+
+  .lg\:pb-20 {
+    padding-bottom: 5rem;
+  }
+
+  .lg\:pl-20 {
+    padding-left: 5rem;
+  }
+
+  .lg\:pt-24 {
+    padding-top: 6rem;
+  }
+
+  .lg\:pr-24 {
+    padding-right: 6rem;
+  }
+
+  .lg\:pb-24 {
+    padding-bottom: 6rem;
+  }
+
+  .lg\:pl-24 {
+    padding-left: 6rem;
+  }
+
+  .lg\:pt-32 {
+    padding-top: 8rem;
+  }
+
+  .lg\:pr-32 {
+    padding-right: 8rem;
+  }
+
+  .lg\:pb-32 {
+    padding-bottom: 8rem;
+  }
+
+  .lg\:pl-32 {
+    padding-left: 8rem;
+  }
+
+  .lg\:pt-40 {
+    padding-top: 10rem;
+  }
+
+  .lg\:pr-40 {
+    padding-right: 10rem;
+  }
+
+  .lg\:pb-40 {
+    padding-bottom: 10rem;
+  }
+
+  .lg\:pl-40 {
+    padding-left: 10rem;
+  }
+
+  .lg\:pt-48 {
+    padding-top: 12rem;
+  }
+
+  .lg\:pr-48 {
+    padding-right: 12rem;
+  }
+
+  .lg\:pb-48 {
+    padding-bottom: 12rem;
+  }
+
+  .lg\:pl-48 {
+    padding-left: 12rem;
+  }
+
+  .lg\:pt-56 {
+    padding-top: 14rem;
+  }
+
+  .lg\:pr-56 {
+    padding-right: 14rem;
+  }
+
+  .lg\:pb-56 {
+    padding-bottom: 14rem;
+  }
+
+  .lg\:pl-56 {
+    padding-left: 14rem;
+  }
+
+  .lg\:pt-64 {
+    padding-top: 16rem;
+  }
+
+  .lg\:pr-64 {
+    padding-right: 16rem;
+  }
+
+  .lg\:pb-64 {
+    padding-bottom: 16rem;
+  }
+
+  .lg\:pl-64 {
+    padding-left: 16rem;
+  }
+
+  .lg\:pt-px {
+    padding-top: 1px;
+  }
+
+  .lg\:pr-px {
+    padding-right: 1px;
+  }
+
+  .lg\:pb-px {
+    padding-bottom: 1px;
+  }
+
+  .lg\:pl-px {
+    padding-left: 1px;
+  }
+
+  .lg\:placeholder-transparent::-moz-placeholder {
+    color: transparent;
+  }
+
+  .lg\:placeholder-transparent:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .lg\:placeholder-transparent::placeholder {
+    color: transparent;
+  }
+
+  .lg\:placeholder-current::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .lg\:placeholder-current:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .lg\:placeholder-current::placeholder {
+    color: currentColor;
+  }
+
+  .lg\:placeholder-black::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-black:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-black::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-white::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-white:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-white::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-transparent:focus::-moz-placeholder {
+    color: transparent;
+  }
+
+  .lg\:focus\:placeholder-transparent:focus:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .lg\:focus\:placeholder-transparent:focus::placeholder {
+    color: transparent;
+  }
+
+  .lg\:focus\:placeholder-current:focus::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .lg\:focus\:placeholder-current:focus:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .lg\:focus\:placeholder-current:focus::placeholder {
+    color: currentColor;
+  }
+
+  .lg\:focus\:placeholder-black:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-black:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-black:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-white:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-white:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-white:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-opacity-0::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:placeholder-opacity-0:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:placeholder-opacity-0::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:placeholder-opacity-25::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:placeholder-opacity-25:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:placeholder-opacity-25::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:placeholder-opacity-50::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:placeholder-opacity-50:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:placeholder-opacity-50::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:placeholder-opacity-75::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:placeholder-opacity-75:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:placeholder-opacity-75::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:placeholder-opacity-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:placeholder-opacity-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:placeholder-opacity-100::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:focus\:placeholder-opacity-0:focus::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:focus\:placeholder-opacity-0:focus::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:focus\:placeholder-opacity-25:focus::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:focus\:placeholder-opacity-25:focus::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:focus\:placeholder-opacity-50:focus::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:focus\:placeholder-opacity-50:focus::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:focus\:placeholder-opacity-75:focus::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:focus\:placeholder-opacity-75:focus::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:focus\:placeholder-opacity-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:focus\:placeholder-opacity-100:focus::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:pointer-events-none {
+    pointer-events: none;
+  }
+
+  .lg\:pointer-events-auto {
+    pointer-events: auto;
+  }
+
+  .lg\:static {
+    position: static;
+  }
+
+  .lg\:fixed {
+    position: fixed;
+  }
+
+  .lg\:absolute {
+    position: absolute;
+  }
+
+  .lg\:relative {
+    position: relative;
+  }
+
+  .lg\:sticky {
+    position: -webkit-sticky;
+    position: sticky;
+  }
+
+  .lg\:inset-0 {
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+  }
+
+  .lg\:inset-auto {
+    top: auto;
+    right: auto;
+    bottom: auto;
+    left: auto;
+  }
+
+  .lg\:inset-y-0 {
+    top: 0;
+    bottom: 0;
+  }
+
+  .lg\:inset-x-0 {
+    right: 0;
+    left: 0;
+  }
+
+  .lg\:inset-y-auto {
+    top: auto;
+    bottom: auto;
+  }
+
+  .lg\:inset-x-auto {
+    right: auto;
+    left: auto;
+  }
+
+  .lg\:top-0 {
+    top: 0;
+  }
+
+  .lg\:right-0 {
+    right: 0;
+  }
+
+  .lg\:bottom-0 {
+    bottom: 0;
+  }
+
+  .lg\:left-0 {
+    left: 0;
+  }
+
+  .lg\:top-auto {
+    top: auto;
+  }
+
+  .lg\:right-auto {
+    right: auto;
+  }
+
+  .lg\:bottom-auto {
+    bottom: auto;
+  }
+
+  .lg\:left-auto {
+    left: auto;
+  }
+
+  .lg\:resize-none {
+    resize: none;
+  }
+
+  .lg\:resize-y {
+    resize: vertical;
+  }
+
+  .lg\:resize-x {
+    resize: horizontal;
+  }
+
+  .lg\:resize {
+    resize: both;
+  }
+
+  .lg\:shadow-xs {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:shadow-sm {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:shadow {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:shadow-md {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:shadow-lg {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:shadow-xl {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .lg\:shadow-2xl {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .lg\:shadow-inner {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:shadow-outline {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .lg\:shadow-none {
+    box-shadow: none;
+  }
+
+  .lg\:hover\:shadow-xs:hover {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:hover\:shadow-sm:hover {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:hover\:shadow:hover {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:hover\:shadow-md:hover {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:hover\:shadow-lg:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:hover\:shadow-xl:hover {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .lg\:hover\:shadow-2xl:hover {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .lg\:hover\:shadow-inner:hover {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:hover\:shadow-outline:hover {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .lg\:hover\:shadow-none:hover {
+    box-shadow: none;
+  }
+
+  .lg\:focus\:shadow-xs:focus {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:focus\:shadow-sm:focus {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:focus\:shadow:focus {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:focus\:shadow-md:focus {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:focus\:shadow-lg:focus {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:focus\:shadow-xl:focus {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .lg\:focus\:shadow-2xl:focus {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .lg\:focus\:shadow-inner:focus {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:focus\:shadow-outline:focus {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .lg\:focus\:shadow-none:focus {
+    box-shadow: none;
+  }
+
+  .lg\:fill-current {
+    fill: currentColor;
+  }
+
+  .lg\:stroke-current {
+    stroke: currentColor;
+  }
+
+  .lg\:stroke-0 {
+    stroke-width: 0;
+  }
+
+  .lg\:stroke-1 {
+    stroke-width: 1;
+  }
+
+  .lg\:stroke-2 {
+    stroke-width: 2;
+  }
+
+  .lg\:table-auto {
+    table-layout: auto;
+  }
+
+  .lg\:table-fixed {
+    table-layout: fixed;
+  }
+
+  .lg\:text-left {
+    text-align: left;
+  }
+
+  .lg\:text-center {
+    text-align: center;
+  }
+
+  .lg\:text-right {
+    text-align: right;
+  }
+
+  .lg\:text-justify {
+    text-align: justify;
+  }
+
+  .lg\:text-transparent {
+    color: transparent;
+  }
+
+  .lg\:text-current {
+    color: currentColor;
+  }
+
+  .lg\:text-black {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .lg\:text-white {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .lg\:text-gray-100 {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .lg\:text-gray-200 {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .lg\:text-gray-300 {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .lg\:text-gray-400 {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .lg\:text-gray-500 {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .lg\:text-gray-600 {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .lg\:text-gray-700 {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .lg\:text-gray-800 {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .lg\:text-gray-900 {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .lg\:text-red-100 {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .lg\:text-red-200 {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .lg\:text-red-300 {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .lg\:text-red-400 {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .lg\:text-red-500 {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .lg\:text-red-600 {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .lg\:text-red-700 {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .lg\:text-red-800 {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .lg\:text-red-900 {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .lg\:text-orange-100 {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .lg\:text-orange-200 {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .lg\:text-orange-300 {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .lg\:text-orange-400 {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .lg\:text-orange-500 {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .lg\:text-orange-600 {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .lg\:text-orange-700 {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .lg\:text-orange-800 {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .lg\:text-orange-900 {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-100 {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-200 {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-300 {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-400 {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-500 {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-600 {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-700 {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-800 {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-900 {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .lg\:text-green-100 {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .lg\:text-green-200 {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .lg\:text-green-300 {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .lg\:text-green-400 {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .lg\:text-green-500 {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .lg\:text-green-600 {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .lg\:text-green-700 {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .lg\:text-green-800 {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .lg\:text-green-900 {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .lg\:text-teal-100 {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .lg\:text-teal-200 {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .lg\:text-teal-300 {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .lg\:text-teal-400 {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .lg\:text-teal-500 {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .lg\:text-teal-600 {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .lg\:text-teal-700 {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .lg\:text-teal-800 {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .lg\:text-teal-900 {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .lg\:text-blue-100 {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .lg\:text-blue-200 {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .lg\:text-blue-300 {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .lg\:text-blue-400 {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .lg\:text-blue-500 {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .lg\:text-blue-600 {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .lg\:text-blue-700 {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .lg\:text-blue-800 {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .lg\:text-blue-900 {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-100 {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-200 {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-300 {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-400 {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-500 {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-600 {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-700 {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-800 {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-900 {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .lg\:text-purple-100 {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .lg\:text-purple-200 {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .lg\:text-purple-300 {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .lg\:text-purple-400 {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .lg\:text-purple-500 {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .lg\:text-purple-600 {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .lg\:text-purple-700 {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .lg\:text-purple-800 {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .lg\:text-purple-900 {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .lg\:text-pink-100 {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .lg\:text-pink-200 {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .lg\:text-pink-300 {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .lg\:text-pink-400 {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .lg\:text-pink-500 {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .lg\:text-pink-600 {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .lg\:text-pink-700 {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .lg\:text-pink-800 {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .lg\:text-pink-900 {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-transparent:hover {
+    color: transparent;
+  }
+
+  .lg\:hover\:text-current:hover {
+    color: currentColor;
+  }
+
+  .lg\:hover\:text-black:hover {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-white:hover {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-100:hover {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-200:hover {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-300:hover {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-400:hover {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-500:hover {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-600:hover {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-700:hover {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-800:hover {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-900:hover {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-100:hover {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-200:hover {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-300:hover {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-400:hover {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-500:hover {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-600:hover {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-700:hover {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-800:hover {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-900:hover {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-100:hover {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-200:hover {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-300:hover {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-400:hover {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-500:hover {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-600:hover {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-700:hover {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-800:hover {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-900:hover {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-100:hover {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-200:hover {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-300:hover {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-400:hover {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-500:hover {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-600:hover {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-700:hover {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-800:hover {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-900:hover {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-100:hover {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-200:hover {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-300:hover {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-400:hover {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-500:hover {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-600:hover {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-700:hover {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-800:hover {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-900:hover {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-100:hover {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-200:hover {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-300:hover {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-400:hover {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-500:hover {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-600:hover {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-700:hover {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-800:hover {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-900:hover {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-100:hover {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-200:hover {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-300:hover {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-400:hover {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-500:hover {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-600:hover {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-700:hover {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-800:hover {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-900:hover {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-100:hover {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-200:hover {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-300:hover {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-400:hover {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-500:hover {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-600:hover {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-700:hover {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-800:hover {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-900:hover {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-100:hover {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-200:hover {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-300:hover {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-400:hover {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-500:hover {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-600:hover {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-700:hover {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-800:hover {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-900:hover {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-100:hover {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-200:hover {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-300:hover {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-400:hover {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-500:hover {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-600:hover {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-700:hover {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-800:hover {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-900:hover {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-transparent:focus {
+    color: transparent;
+  }
+
+  .lg\:focus\:text-current:focus {
+    color: currentColor;
+  }
+
+  .lg\:focus\:text-black:focus {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-white:focus {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-100:focus {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-200:focus {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-300:focus {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-400:focus {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-500:focus {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-600:focus {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-700:focus {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-800:focus {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-900:focus {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-100:focus {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-200:focus {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-300:focus {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-400:focus {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-500:focus {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-600:focus {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-700:focus {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-800:focus {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-900:focus {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-100:focus {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-200:focus {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-300:focus {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-400:focus {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-500:focus {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-600:focus {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-700:focus {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-800:focus {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-900:focus {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-100:focus {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-200:focus {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-300:focus {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-400:focus {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-500:focus {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-600:focus {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-700:focus {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-800:focus {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-900:focus {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-100:focus {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-200:focus {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-300:focus {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-400:focus {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-500:focus {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-600:focus {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-700:focus {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-800:focus {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-900:focus {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-100:focus {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-200:focus {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-300:focus {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-400:focus {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-500:focus {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-600:focus {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-700:focus {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-800:focus {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-900:focus {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-100:focus {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-200:focus {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-300:focus {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-400:focus {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-500:focus {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-600:focus {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-700:focus {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-800:focus {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-900:focus {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-100:focus {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-200:focus {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-300:focus {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-400:focus {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-500:focus {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-600:focus {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-700:focus {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-800:focus {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-900:focus {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-100:focus {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-200:focus {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-300:focus {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-400:focus {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-500:focus {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-600:focus {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-700:focus {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-800:focus {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-900:focus {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-100:focus {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-200:focus {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-300:focus {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-400:focus {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-500:focus {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-600:focus {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-700:focus {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-800:focus {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-900:focus {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .lg\:text-opacity-0 {
+    --text-opacity: 0;
+  }
+
+  .lg\:text-opacity-25 {
+    --text-opacity: 0.25;
+  }
+
+  .lg\:text-opacity-50 {
+    --text-opacity: 0.5;
+  }
+
+  .lg\:text-opacity-75 {
+    --text-opacity: 0.75;
+  }
+
+  .lg\:text-opacity-100 {
+    --text-opacity: 1;
+  }
+
+  .lg\:hover\:text-opacity-0:hover {
+    --text-opacity: 0;
+  }
+
+  .lg\:hover\:text-opacity-25:hover {
+    --text-opacity: 0.25;
+  }
+
+  .lg\:hover\:text-opacity-50:hover {
+    --text-opacity: 0.5;
+  }
+
+  .lg\:hover\:text-opacity-75:hover {
+    --text-opacity: 0.75;
+  }
+
+  .lg\:hover\:text-opacity-100:hover {
+    --text-opacity: 1;
+  }
+
+  .lg\:focus\:text-opacity-0:focus {
+    --text-opacity: 0;
+  }
+
+  .lg\:focus\:text-opacity-25:focus {
+    --text-opacity: 0.25;
+  }
+
+  .lg\:focus\:text-opacity-50:focus {
+    --text-opacity: 0.5;
+  }
+
+  .lg\:focus\:text-opacity-75:focus {
+    --text-opacity: 0.75;
+  }
+
+  .lg\:focus\:text-opacity-100:focus {
+    --text-opacity: 1;
+  }
+
+  .lg\:italic {
+    font-style: italic;
+  }
+
+  .lg\:not-italic {
+    font-style: normal;
+  }
+
+  .lg\:uppercase {
+    text-transform: uppercase;
+  }
+
+  .lg\:lowercase {
+    text-transform: lowercase;
+  }
+
+  .lg\:capitalize {
+    text-transform: capitalize;
+  }
+
+  .lg\:normal-case {
+    text-transform: none;
+  }
+
+  .lg\:underline {
+    text-decoration: underline;
+  }
+
+  .lg\:line-through {
+    text-decoration: line-through;
+  }
+
+  .lg\:no-underline {
+    text-decoration: none;
+  }
+
+  .lg\:hover\:underline:hover {
+    text-decoration: underline;
+  }
+
+  .lg\:hover\:line-through:hover {
+    text-decoration: line-through;
+  }
+
+  .lg\:hover\:no-underline:hover {
+    text-decoration: none;
+  }
+
+  .lg\:focus\:underline:focus {
+    text-decoration: underline;
+  }
+
+  .lg\:focus\:line-through:focus {
+    text-decoration: line-through;
+  }
+
+  .lg\:focus\:no-underline:focus {
+    text-decoration: none;
+  }
+
+  .lg\:antialiased {
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+
+  .lg\:subpixel-antialiased {
+    -webkit-font-smoothing: auto;
+    -moz-osx-font-smoothing: auto;
+  }
+
+  .lg\:ordinal, .lg\:slashed-zero, .lg\:lining-nums, .lg\:oldstyle-nums, .lg\:proportional-nums, .lg\:tabular-nums, .lg\:diagonal-fractions, .lg\:stacked-fractions {
+    --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+    font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+  }
+
+  .lg\:normal-nums {
+    font-variant-numeric: normal;
+  }
+
+  .lg\:ordinal {
+    --font-variant-numeric-ordinal: ordinal;
+  }
+
+  .lg\:slashed-zero {
+    --font-variant-numeric-slashed-zero: slashed-zero;
+  }
+
+  .lg\:lining-nums {
+    --font-variant-numeric-figure: lining-nums;
+  }
+
+  .lg\:oldstyle-nums {
+    --font-variant-numeric-figure: oldstyle-nums;
+  }
+
+  .lg\:proportional-nums {
+    --font-variant-numeric-spacing: proportional-nums;
+  }
+
+  .lg\:tabular-nums {
+    --font-variant-numeric-spacing: tabular-nums;
+  }
+
+  .lg\:diagonal-fractions {
+    --font-variant-numeric-fraction: diagonal-fractions;
+  }
+
+  .lg\:stacked-fractions {
+    --font-variant-numeric-fraction: stacked-fractions;
+  }
+
+  .lg\:tracking-tighter {
+    letter-spacing: -0.05em;
+  }
+
+  .lg\:tracking-tight {
+    letter-spacing: -0.025em;
+  }
+
+  .lg\:tracking-normal {
+    letter-spacing: 0;
+  }
+
+  .lg\:tracking-wide {
+    letter-spacing: 0.025em;
+  }
+
+  .lg\:tracking-wider {
+    letter-spacing: 0.05em;
+  }
+
+  .lg\:tracking-widest {
+    letter-spacing: 0.1em;
+  }
+
+  .lg\:select-none {
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+  }
+
+  .lg\:select-text {
+    -webkit-user-select: text;
+       -moz-user-select: text;
+        -ms-user-select: text;
+            user-select: text;
+  }
+
+  .lg\:select-all {
+    -webkit-user-select: all;
+       -moz-user-select: all;
+        -ms-user-select: all;
+            user-select: all;
+  }
+
+  .lg\:select-auto {
+    -webkit-user-select: auto;
+       -moz-user-select: auto;
+        -ms-user-select: auto;
+            user-select: auto;
+  }
+
+  .lg\:align-baseline {
+    vertical-align: baseline;
+  }
+
+  .lg\:align-top {
+    vertical-align: top;
+  }
+
+  .lg\:align-middle {
+    vertical-align: middle;
+  }
+
+  .lg\:align-bottom {
+    vertical-align: bottom;
+  }
+
+  .lg\:align-text-top {
+    vertical-align: text-top;
+  }
+
+  .lg\:align-text-bottom {
+    vertical-align: text-bottom;
+  }
+
+  .lg\:visible {
+    visibility: visible;
+  }
+
+  .lg\:invisible {
+    visibility: hidden;
+  }
+
+  .lg\:whitespace-normal {
+    white-space: normal;
+  }
+
+  .lg\:whitespace-no-wrap {
+    white-space: nowrap;
+  }
+
+  .lg\:whitespace-pre {
+    white-space: pre;
+  }
+
+  .lg\:whitespace-pre-line {
+    white-space: pre-line;
+  }
+
+  .lg\:whitespace-pre-wrap {
+    white-space: pre-wrap;
+  }
+
+  .lg\:break-normal {
+    overflow-wrap: normal;
+    word-break: normal;
+  }
+
+  .lg\:break-words {
+    overflow-wrap: break-word;
+  }
+
+  .lg\:break-all {
+    word-break: break-all;
+  }
+
+  .lg\:truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .lg\:w-0 {
+    width: 0;
+  }
+
+  .lg\:w-1 {
+    width: 0.25rem;
+  }
+
+  .lg\:w-2 {
+    width: 0.5rem;
+  }
+
+  .lg\:w-3 {
+    width: 0.75rem;
+  }
+
+  .lg\:w-4 {
+    width: 1rem;
+  }
+
+  .lg\:w-5 {
+    width: 1.25rem;
+  }
+
+  .lg\:w-6 {
+    width: 1.5rem;
+  }
+
+  .lg\:w-8 {
+    width: 2rem;
+  }
+
+  .lg\:w-10 {
+    width: 2.5rem;
+  }
+
+  .lg\:w-12 {
+    width: 3rem;
+  }
+
+  .lg\:w-16 {
+    width: 4rem;
+  }
+
+  .lg\:w-20 {
+    width: 5rem;
+  }
+
+  .lg\:w-24 {
+    width: 6rem;
+  }
+
+  .lg\:w-32 {
+    width: 8rem;
+  }
+
+  .lg\:w-40 {
+    width: 10rem;
+  }
+
+  .lg\:w-48 {
+    width: 12rem;
+  }
+
+  .lg\:w-56 {
+    width: 14rem;
+  }
+
+  .lg\:w-64 {
+    width: 16rem;
+  }
+
+  .lg\:w-auto {
+    width: auto;
+  }
+
+  .lg\:w-px {
+    width: 1px;
+  }
+
+  .lg\:w-1\/2 {
+    width: 50%;
+  }
+
+  .lg\:w-1\/3 {
+    width: 33.333333%;
+  }
+
+  .lg\:w-2\/3 {
+    width: 66.666667%;
+  }
+
+  .lg\:w-1\/4 {
+    width: 25%;
+  }
+
+  .lg\:w-2\/4 {
+    width: 50%;
+  }
+
+  .lg\:w-3\/4 {
+    width: 75%;
+  }
+
+  .lg\:w-1\/5 {
+    width: 20%;
+  }
+
+  .lg\:w-2\/5 {
+    width: 40%;
+  }
+
+  .lg\:w-3\/5 {
+    width: 60%;
+  }
+
+  .lg\:w-4\/5 {
+    width: 80%;
+  }
+
+  .lg\:w-1\/6 {
+    width: 16.666667%;
+  }
+
+  .lg\:w-2\/6 {
+    width: 33.333333%;
+  }
+
+  .lg\:w-3\/6 {
+    width: 50%;
+  }
+
+  .lg\:w-4\/6 {
+    width: 66.666667%;
+  }
+
+  .lg\:w-5\/6 {
+    width: 83.333333%;
+  }
+
+  .lg\:w-1\/12 {
+    width: 8.333333%;
+  }
+
+  .lg\:w-2\/12 {
+    width: 16.666667%;
+  }
+
+  .lg\:w-3\/12 {
+    width: 25%;
+  }
+
+  .lg\:w-4\/12 {
+    width: 33.333333%;
+  }
+
+  .lg\:w-5\/12 {
+    width: 41.666667%;
+  }
+
+  .lg\:w-6\/12 {
+    width: 50%;
+  }
+
+  .lg\:w-7\/12 {
+    width: 58.333333%;
+  }
+
+  .lg\:w-8\/12 {
+    width: 66.666667%;
+  }
+
+  .lg\:w-9\/12 {
+    width: 75%;
+  }
+
+  .lg\:w-10\/12 {
+    width: 83.333333%;
+  }
+
+  .lg\:w-11\/12 {
+    width: 91.666667%;
+  }
+
+  .lg\:w-full {
+    width: 100%;
+  }
+
+  .lg\:w-screen {
+    width: 100vw;
+  }
+
+  .lg\:z-0 {
+    z-index: 0;
+  }
+
+  .lg\:z-10 {
+    z-index: 10;
+  }
+
+  .lg\:z-20 {
+    z-index: 20;
+  }
+
+  .lg\:z-30 {
+    z-index: 30;
+  }
+
+  .lg\:z-40 {
+    z-index: 40;
+  }
+
+  .lg\:z-50 {
+    z-index: 50;
+  }
+
+  .lg\:z-auto {
+    z-index: auto;
+  }
+
+  .lg\:gap-0 {
+    grid-gap: 0;
+    gap: 0;
+  }
+
+  .lg\:gap-1 {
+    grid-gap: 0.25rem;
+    gap: 0.25rem;
+  }
+
+  .lg\:gap-2 {
+    grid-gap: 0.5rem;
+    gap: 0.5rem;
+  }
+
+  .lg\:gap-3 {
+    grid-gap: 0.75rem;
+    gap: 0.75rem;
+  }
+
+  .lg\:gap-4 {
+    grid-gap: 1rem;
+    gap: 1rem;
+  }
+
+  .lg\:gap-5 {
+    grid-gap: 1.25rem;
+    gap: 1.25rem;
+  }
+
+  .lg\:gap-6 {
+    grid-gap: 1.5rem;
+    gap: 1.5rem;
+  }
+
+  .lg\:gap-8 {
+    grid-gap: 2rem;
+    gap: 2rem;
+  }
+
+  .lg\:gap-10 {
+    grid-gap: 2.5rem;
+    gap: 2.5rem;
+  }
+
+  .lg\:gap-12 {
+    grid-gap: 3rem;
+    gap: 3rem;
+  }
+
+  .lg\:gap-16 {
+    grid-gap: 4rem;
+    gap: 4rem;
+  }
+
+  .lg\:gap-20 {
+    grid-gap: 5rem;
+    gap: 5rem;
+  }
+
+  .lg\:gap-24 {
+    grid-gap: 6rem;
+    gap: 6rem;
+  }
+
+  .lg\:gap-32 {
+    grid-gap: 8rem;
+    gap: 8rem;
+  }
+
+  .lg\:gap-40 {
+    grid-gap: 10rem;
+    gap: 10rem;
+  }
+
+  .lg\:gap-48 {
+    grid-gap: 12rem;
+    gap: 12rem;
+  }
+
+  .lg\:gap-56 {
+    grid-gap: 14rem;
+    gap: 14rem;
+  }
+
+  .lg\:gap-64 {
+    grid-gap: 16rem;
+    gap: 16rem;
+  }
+
+  .lg\:gap-px {
+    grid-gap: 1px;
+    gap: 1px;
+  }
+
+  .lg\:col-gap-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .lg\:col-gap-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .lg\:col-gap-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .lg\:col-gap-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .lg\:col-gap-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .lg\:col-gap-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .lg\:col-gap-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .lg\:col-gap-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .lg\:col-gap-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .lg\:col-gap-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .lg\:col-gap-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .lg\:col-gap-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .lg\:col-gap-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .lg\:col-gap-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .lg\:col-gap-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .lg\:col-gap-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .lg\:col-gap-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .lg\:col-gap-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .lg\:col-gap-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .lg\:gap-x-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .lg\:gap-x-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .lg\:gap-x-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .lg\:gap-x-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .lg\:gap-x-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .lg\:gap-x-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .lg\:gap-x-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .lg\:gap-x-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .lg\:gap-x-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .lg\:gap-x-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .lg\:gap-x-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .lg\:gap-x-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .lg\:gap-x-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .lg\:gap-x-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .lg\:gap-x-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .lg\:gap-x-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .lg\:gap-x-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .lg\:gap-x-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .lg\:gap-x-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .lg\:row-gap-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .lg\:row-gap-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .lg\:row-gap-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .lg\:row-gap-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .lg\:row-gap-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .lg\:row-gap-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .lg\:row-gap-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .lg\:row-gap-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .lg\:row-gap-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .lg\:row-gap-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .lg\:row-gap-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .lg\:row-gap-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .lg\:row-gap-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .lg\:row-gap-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .lg\:row-gap-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .lg\:row-gap-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .lg\:row-gap-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .lg\:row-gap-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .lg\:row-gap-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .lg\:gap-y-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .lg\:gap-y-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .lg\:gap-y-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .lg\:gap-y-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .lg\:gap-y-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .lg\:gap-y-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .lg\:gap-y-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .lg\:gap-y-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .lg\:gap-y-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .lg\:gap-y-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .lg\:gap-y-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .lg\:gap-y-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .lg\:gap-y-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .lg\:gap-y-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .lg\:gap-y-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .lg\:gap-y-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .lg\:gap-y-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .lg\:gap-y-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .lg\:gap-y-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .lg\:grid-flow-row {
+    grid-auto-flow: row;
+  }
+
+  .lg\:grid-flow-col {
+    grid-auto-flow: column;
+  }
+
+  .lg\:grid-flow-row-dense {
+    grid-auto-flow: row dense;
+  }
+
+  .lg\:grid-flow-col-dense {
+    grid-auto-flow: column dense;
+  }
+
+  .lg\:grid-cols-1 {
+    grid-template-columns: repeat(1, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-2 {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-3 {
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-4 {
+    grid-template-columns: repeat(4, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-5 {
+    grid-template-columns: repeat(5, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-6 {
+    grid-template-columns: repeat(6, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-7 {
+    grid-template-columns: repeat(7, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-8 {
+    grid-template-columns: repeat(8, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-9 {
+    grid-template-columns: repeat(9, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-10 {
+    grid-template-columns: repeat(10, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-11 {
+    grid-template-columns: repeat(11, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-12 {
+    grid-template-columns: repeat(12, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-none {
+    grid-template-columns: none;
+  }
+
+  .lg\:col-auto {
+    grid-column: auto;
+  }
+
+  .lg\:col-span-1 {
+    grid-column: span 1 / span 1;
+  }
+
+  .lg\:col-span-2 {
+    grid-column: span 2 / span 2;
+  }
+
+  .lg\:col-span-3 {
+    grid-column: span 3 / span 3;
+  }
+
+  .lg\:col-span-4 {
+    grid-column: span 4 / span 4;
+  }
+
+  .lg\:col-span-5 {
+    grid-column: span 5 / span 5;
+  }
+
+  .lg\:col-span-6 {
+    grid-column: span 6 / span 6;
+  }
+
+  .lg\:col-span-7 {
+    grid-column: span 7 / span 7;
+  }
+
+  .lg\:col-span-8 {
+    grid-column: span 8 / span 8;
+  }
+
+  .lg\:col-span-9 {
+    grid-column: span 9 / span 9;
+  }
+
+  .lg\:col-span-10 {
+    grid-column: span 10 / span 10;
+  }
+
+  .lg\:col-span-11 {
+    grid-column: span 11 / span 11;
+  }
+
+  .lg\:col-span-12 {
+    grid-column: span 12 / span 12;
+  }
+
+  .lg\:col-start-1 {
+    grid-column-start: 1;
+  }
+
+  .lg\:col-start-2 {
+    grid-column-start: 2;
+  }
+
+  .lg\:col-start-3 {
+    grid-column-start: 3;
+  }
+
+  .lg\:col-start-4 {
+    grid-column-start: 4;
+  }
+
+  .lg\:col-start-5 {
+    grid-column-start: 5;
+  }
+
+  .lg\:col-start-6 {
+    grid-column-start: 6;
+  }
+
+  .lg\:col-start-7 {
+    grid-column-start: 7;
+  }
+
+  .lg\:col-start-8 {
+    grid-column-start: 8;
+  }
+
+  .lg\:col-start-9 {
+    grid-column-start: 9;
+  }
+
+  .lg\:col-start-10 {
+    grid-column-start: 10;
+  }
+
+  .lg\:col-start-11 {
+    grid-column-start: 11;
+  }
+
+  .lg\:col-start-12 {
+    grid-column-start: 12;
+  }
+
+  .lg\:col-start-13 {
+    grid-column-start: 13;
+  }
+
+  .lg\:col-start-auto {
+    grid-column-start: auto;
+  }
+
+  .lg\:col-end-1 {
+    grid-column-end: 1;
+  }
+
+  .lg\:col-end-2 {
+    grid-column-end: 2;
+  }
+
+  .lg\:col-end-3 {
+    grid-column-end: 3;
+  }
+
+  .lg\:col-end-4 {
+    grid-column-end: 4;
+  }
+
+  .lg\:col-end-5 {
+    grid-column-end: 5;
+  }
+
+  .lg\:col-end-6 {
+    grid-column-end: 6;
+  }
+
+  .lg\:col-end-7 {
+    grid-column-end: 7;
+  }
+
+  .lg\:col-end-8 {
+    grid-column-end: 8;
+  }
+
+  .lg\:col-end-9 {
+    grid-column-end: 9;
+  }
+
+  .lg\:col-end-10 {
+    grid-column-end: 10;
+  }
+
+  .lg\:col-end-11 {
+    grid-column-end: 11;
+  }
+
+  .lg\:col-end-12 {
+    grid-column-end: 12;
+  }
+
+  .lg\:col-end-13 {
+    grid-column-end: 13;
+  }
+
+  .lg\:col-end-auto {
+    grid-column-end: auto;
+  }
+
+  .lg\:grid-rows-1 {
+    grid-template-rows: repeat(1, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-2 {
+    grid-template-rows: repeat(2, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-3 {
+    grid-template-rows: repeat(3, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-4 {
+    grid-template-rows: repeat(4, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-5 {
+    grid-template-rows: repeat(5, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-6 {
+    grid-template-rows: repeat(6, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-none {
+    grid-template-rows: none;
+  }
+
+  .lg\:row-auto {
+    grid-row: auto;
+  }
+
+  .lg\:row-span-1 {
+    grid-row: span 1 / span 1;
+  }
+
+  .lg\:row-span-2 {
+    grid-row: span 2 / span 2;
+  }
+
+  .lg\:row-span-3 {
+    grid-row: span 3 / span 3;
+  }
+
+  .lg\:row-span-4 {
+    grid-row: span 4 / span 4;
+  }
+
+  .lg\:row-span-5 {
+    grid-row: span 5 / span 5;
+  }
+
+  .lg\:row-span-6 {
+    grid-row: span 6 / span 6;
+  }
+
+  .lg\:row-start-1 {
+    grid-row-start: 1;
+  }
+
+  .lg\:row-start-2 {
+    grid-row-start: 2;
+  }
+
+  .lg\:row-start-3 {
+    grid-row-start: 3;
+  }
+
+  .lg\:row-start-4 {
+    grid-row-start: 4;
+  }
+
+  .lg\:row-start-5 {
+    grid-row-start: 5;
+  }
+
+  .lg\:row-start-6 {
+    grid-row-start: 6;
+  }
+
+  .lg\:row-start-7 {
+    grid-row-start: 7;
+  }
+
+  .lg\:row-start-auto {
+    grid-row-start: auto;
+  }
+
+  .lg\:row-end-1 {
+    grid-row-end: 1;
+  }
+
+  .lg\:row-end-2 {
+    grid-row-end: 2;
+  }
+
+  .lg\:row-end-3 {
+    grid-row-end: 3;
+  }
+
+  .lg\:row-end-4 {
+    grid-row-end: 4;
+  }
+
+  .lg\:row-end-5 {
+    grid-row-end: 5;
+  }
+
+  .lg\:row-end-6 {
+    grid-row-end: 6;
+  }
+
+  .lg\:row-end-7 {
+    grid-row-end: 7;
+  }
+
+  .lg\:row-end-auto {
+    grid-row-end: auto;
+  }
+
+  .lg\:transform {
+    --transform-translate-x: 0;
+    --transform-translate-y: 0;
+    --transform-rotate: 0;
+    --transform-skew-x: 0;
+    --transform-skew-y: 0;
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+    transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+  }
+
+  .lg\:transform-none {
+    transform: none;
+  }
+
+  .lg\:origin-center {
+    transform-origin: center;
+  }
+
+  .lg\:origin-top {
+    transform-origin: top;
+  }
+
+  .lg\:origin-top-right {
+    transform-origin: top right;
+  }
+
+  .lg\:origin-right {
+    transform-origin: right;
+  }
+
+  .lg\:origin-bottom-right {
+    transform-origin: bottom right;
+  }
+
+  .lg\:origin-bottom {
+    transform-origin: bottom;
+  }
+
+  .lg\:origin-bottom-left {
+    transform-origin: bottom left;
+  }
+
+  .lg\:origin-left {
+    transform-origin: left;
+  }
+
+  .lg\:origin-top-left {
+    transform-origin: top left;
+  }
+
+  .lg\:scale-0 {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .lg\:scale-50 {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .lg\:scale-75 {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .lg\:scale-90 {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .lg\:scale-95 {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .lg\:scale-100 {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .lg\:scale-105 {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:scale-110 {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:scale-125 {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:scale-150 {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:scale-x-0 {
+    --transform-scale-x: 0;
+  }
+
+  .lg\:scale-x-50 {
+    --transform-scale-x: .5;
+  }
+
+  .lg\:scale-x-75 {
+    --transform-scale-x: .75;
+  }
+
+  .lg\:scale-x-90 {
+    --transform-scale-x: .9;
+  }
+
+  .lg\:scale-x-95 {
+    --transform-scale-x: .95;
+  }
+
+  .lg\:scale-x-100 {
+    --transform-scale-x: 1;
+  }
+
+  .lg\:scale-x-105 {
+    --transform-scale-x: 1.05;
+  }
+
+  .lg\:scale-x-110 {
+    --transform-scale-x: 1.1;
+  }
+
+  .lg\:scale-x-125 {
+    --transform-scale-x: 1.25;
+  }
+
+  .lg\:scale-x-150 {
+    --transform-scale-x: 1.5;
+  }
+
+  .lg\:scale-y-0 {
+    --transform-scale-y: 0;
+  }
+
+  .lg\:scale-y-50 {
+    --transform-scale-y: .5;
+  }
+
+  .lg\:scale-y-75 {
+    --transform-scale-y: .75;
+  }
+
+  .lg\:scale-y-90 {
+    --transform-scale-y: .9;
+  }
+
+  .lg\:scale-y-95 {
+    --transform-scale-y: .95;
+  }
+
+  .lg\:scale-y-100 {
+    --transform-scale-y: 1;
+  }
+
+  .lg\:scale-y-105 {
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:scale-y-110 {
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:scale-y-125 {
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:scale-y-150 {
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:hover\:scale-0:hover {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .lg\:hover\:scale-50:hover {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .lg\:hover\:scale-75:hover {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .lg\:hover\:scale-90:hover {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .lg\:hover\:scale-95:hover {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .lg\:hover\:scale-100:hover {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .lg\:hover\:scale-105:hover {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:hover\:scale-110:hover {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:hover\:scale-125:hover {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:hover\:scale-150:hover {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:hover\:scale-x-0:hover {
+    --transform-scale-x: 0;
+  }
+
+  .lg\:hover\:scale-x-50:hover {
+    --transform-scale-x: .5;
+  }
+
+  .lg\:hover\:scale-x-75:hover {
+    --transform-scale-x: .75;
+  }
+
+  .lg\:hover\:scale-x-90:hover {
+    --transform-scale-x: .9;
+  }
+
+  .lg\:hover\:scale-x-95:hover {
+    --transform-scale-x: .95;
+  }
+
+  .lg\:hover\:scale-x-100:hover {
+    --transform-scale-x: 1;
+  }
+
+  .lg\:hover\:scale-x-105:hover {
+    --transform-scale-x: 1.05;
+  }
+
+  .lg\:hover\:scale-x-110:hover {
+    --transform-scale-x: 1.1;
+  }
+
+  .lg\:hover\:scale-x-125:hover {
+    --transform-scale-x: 1.25;
+  }
+
+  .lg\:hover\:scale-x-150:hover {
+    --transform-scale-x: 1.5;
+  }
+
+  .lg\:hover\:scale-y-0:hover {
+    --transform-scale-y: 0;
+  }
+
+  .lg\:hover\:scale-y-50:hover {
+    --transform-scale-y: .5;
+  }
+
+  .lg\:hover\:scale-y-75:hover {
+    --transform-scale-y: .75;
+  }
+
+  .lg\:hover\:scale-y-90:hover {
+    --transform-scale-y: .9;
+  }
+
+  .lg\:hover\:scale-y-95:hover {
+    --transform-scale-y: .95;
+  }
+
+  .lg\:hover\:scale-y-100:hover {
+    --transform-scale-y: 1;
+  }
+
+  .lg\:hover\:scale-y-105:hover {
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:hover\:scale-y-110:hover {
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:hover\:scale-y-125:hover {
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:hover\:scale-y-150:hover {
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:focus\:scale-0:focus {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .lg\:focus\:scale-50:focus {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .lg\:focus\:scale-75:focus {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .lg\:focus\:scale-90:focus {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .lg\:focus\:scale-95:focus {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .lg\:focus\:scale-100:focus {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .lg\:focus\:scale-105:focus {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:focus\:scale-110:focus {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:focus\:scale-125:focus {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:focus\:scale-150:focus {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:focus\:scale-x-0:focus {
+    --transform-scale-x: 0;
+  }
+
+  .lg\:focus\:scale-x-50:focus {
+    --transform-scale-x: .5;
+  }
+
+  .lg\:focus\:scale-x-75:focus {
+    --transform-scale-x: .75;
+  }
+
+  .lg\:focus\:scale-x-90:focus {
+    --transform-scale-x: .9;
+  }
+
+  .lg\:focus\:scale-x-95:focus {
+    --transform-scale-x: .95;
+  }
+
+  .lg\:focus\:scale-x-100:focus {
+    --transform-scale-x: 1;
+  }
+
+  .lg\:focus\:scale-x-105:focus {
+    --transform-scale-x: 1.05;
+  }
+
+  .lg\:focus\:scale-x-110:focus {
+    --transform-scale-x: 1.1;
+  }
+
+  .lg\:focus\:scale-x-125:focus {
+    --transform-scale-x: 1.25;
+  }
+
+  .lg\:focus\:scale-x-150:focus {
+    --transform-scale-x: 1.5;
+  }
+
+  .lg\:focus\:scale-y-0:focus {
+    --transform-scale-y: 0;
+  }
+
+  .lg\:focus\:scale-y-50:focus {
+    --transform-scale-y: .5;
+  }
+
+  .lg\:focus\:scale-y-75:focus {
+    --transform-scale-y: .75;
+  }
+
+  .lg\:focus\:scale-y-90:focus {
+    --transform-scale-y: .9;
+  }
+
+  .lg\:focus\:scale-y-95:focus {
+    --transform-scale-y: .95;
+  }
+
+  .lg\:focus\:scale-y-100:focus {
+    --transform-scale-y: 1;
+  }
+
+  .lg\:focus\:scale-y-105:focus {
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:focus\:scale-y-110:focus {
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:focus\:scale-y-125:focus {
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:focus\:scale-y-150:focus {
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:rotate-0 {
+    --transform-rotate: 0;
+  }
+
+  .lg\:rotate-45 {
+    --transform-rotate: 45deg;
+  }
+
+  .lg\:rotate-90 {
+    --transform-rotate: 90deg;
+  }
+
+  .lg\:rotate-180 {
+    --transform-rotate: 180deg;
+  }
+
+  .lg\:-rotate-180 {
+    --transform-rotate: -180deg;
+  }
+
+  .lg\:-rotate-90 {
+    --transform-rotate: -90deg;
+  }
+
+  .lg\:-rotate-45 {
+    --transform-rotate: -45deg;
+  }
+
+  .lg\:hover\:rotate-0:hover {
+    --transform-rotate: 0;
+  }
+
+  .lg\:hover\:rotate-45:hover {
+    --transform-rotate: 45deg;
+  }
+
+  .lg\:hover\:rotate-90:hover {
+    --transform-rotate: 90deg;
+  }
+
+  .lg\:hover\:rotate-180:hover {
+    --transform-rotate: 180deg;
+  }
+
+  .lg\:hover\:-rotate-180:hover {
+    --transform-rotate: -180deg;
+  }
+
+  .lg\:hover\:-rotate-90:hover {
+    --transform-rotate: -90deg;
+  }
+
+  .lg\:hover\:-rotate-45:hover {
+    --transform-rotate: -45deg;
+  }
+
+  .lg\:focus\:rotate-0:focus {
+    --transform-rotate: 0;
+  }
+
+  .lg\:focus\:rotate-45:focus {
+    --transform-rotate: 45deg;
+  }
+
+  .lg\:focus\:rotate-90:focus {
+    --transform-rotate: 90deg;
+  }
+
+  .lg\:focus\:rotate-180:focus {
+    --transform-rotate: 180deg;
+  }
+
+  .lg\:focus\:-rotate-180:focus {
+    --transform-rotate: -180deg;
+  }
+
+  .lg\:focus\:-rotate-90:focus {
+    --transform-rotate: -90deg;
+  }
+
+  .lg\:focus\:-rotate-45:focus {
+    --transform-rotate: -45deg;
+  }
+
+  .lg\:translate-x-0 {
+    --transform-translate-x: 0;
+  }
+
+  .lg\:translate-x-1 {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .lg\:translate-x-2 {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .lg\:translate-x-3 {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .lg\:translate-x-4 {
+    --transform-translate-x: 1rem;
+  }
+
+  .lg\:translate-x-5 {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .lg\:translate-x-6 {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .lg\:translate-x-8 {
+    --transform-translate-x: 2rem;
+  }
+
+  .lg\:translate-x-10 {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .lg\:translate-x-12 {
+    --transform-translate-x: 3rem;
+  }
+
+  .lg\:translate-x-16 {
+    --transform-translate-x: 4rem;
+  }
+
+  .lg\:translate-x-20 {
+    --transform-translate-x: 5rem;
+  }
+
+  .lg\:translate-x-24 {
+    --transform-translate-x: 6rem;
+  }
+
+  .lg\:translate-x-32 {
+    --transform-translate-x: 8rem;
+  }
+
+  .lg\:translate-x-40 {
+    --transform-translate-x: 10rem;
+  }
+
+  .lg\:translate-x-48 {
+    --transform-translate-x: 12rem;
+  }
+
+  .lg\:translate-x-56 {
+    --transform-translate-x: 14rem;
+  }
+
+  .lg\:translate-x-64 {
+    --transform-translate-x: 16rem;
+  }
+
+  .lg\:translate-x-px {
+    --transform-translate-x: 1px;
+  }
+
+  .lg\:-translate-x-1 {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .lg\:-translate-x-2 {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .lg\:-translate-x-3 {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .lg\:-translate-x-4 {
+    --transform-translate-x: -1rem;
+  }
+
+  .lg\:-translate-x-5 {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .lg\:-translate-x-6 {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .lg\:-translate-x-8 {
+    --transform-translate-x: -2rem;
+  }
+
+  .lg\:-translate-x-10 {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .lg\:-translate-x-12 {
+    --transform-translate-x: -3rem;
+  }
+
+  .lg\:-translate-x-16 {
+    --transform-translate-x: -4rem;
+  }
+
+  .lg\:-translate-x-20 {
+    --transform-translate-x: -5rem;
+  }
+
+  .lg\:-translate-x-24 {
+    --transform-translate-x: -6rem;
+  }
+
+  .lg\:-translate-x-32 {
+    --transform-translate-x: -8rem;
+  }
+
+  .lg\:-translate-x-40 {
+    --transform-translate-x: -10rem;
+  }
+
+  .lg\:-translate-x-48 {
+    --transform-translate-x: -12rem;
+  }
+
+  .lg\:-translate-x-56 {
+    --transform-translate-x: -14rem;
+  }
+
+  .lg\:-translate-x-64 {
+    --transform-translate-x: -16rem;
+  }
+
+  .lg\:-translate-x-px {
+    --transform-translate-x: -1px;
+  }
+
+  .lg\:-translate-x-full {
+    --transform-translate-x: -100%;
+  }
+
+  .lg\:-translate-x-1\/2 {
+    --transform-translate-x: -50%;
+  }
+
+  .lg\:translate-x-1\/2 {
+    --transform-translate-x: 50%;
+  }
+
+  .lg\:translate-x-full {
+    --transform-translate-x: 100%;
+  }
+
+  .lg\:translate-y-0 {
+    --transform-translate-y: 0;
+  }
+
+  .lg\:translate-y-1 {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .lg\:translate-y-2 {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .lg\:translate-y-3 {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .lg\:translate-y-4 {
+    --transform-translate-y: 1rem;
+  }
+
+  .lg\:translate-y-5 {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .lg\:translate-y-6 {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .lg\:translate-y-8 {
+    --transform-translate-y: 2rem;
+  }
+
+  .lg\:translate-y-10 {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .lg\:translate-y-12 {
+    --transform-translate-y: 3rem;
+  }
+
+  .lg\:translate-y-16 {
+    --transform-translate-y: 4rem;
+  }
+
+  .lg\:translate-y-20 {
+    --transform-translate-y: 5rem;
+  }
+
+  .lg\:translate-y-24 {
+    --transform-translate-y: 6rem;
+  }
+
+  .lg\:translate-y-32 {
+    --transform-translate-y: 8rem;
+  }
+
+  .lg\:translate-y-40 {
+    --transform-translate-y: 10rem;
+  }
+
+  .lg\:translate-y-48 {
+    --transform-translate-y: 12rem;
+  }
+
+  .lg\:translate-y-56 {
+    --transform-translate-y: 14rem;
+  }
+
+  .lg\:translate-y-64 {
+    --transform-translate-y: 16rem;
+  }
+
+  .lg\:translate-y-px {
+    --transform-translate-y: 1px;
+  }
+
+  .lg\:-translate-y-1 {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .lg\:-translate-y-2 {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .lg\:-translate-y-3 {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .lg\:-translate-y-4 {
+    --transform-translate-y: -1rem;
+  }
+
+  .lg\:-translate-y-5 {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .lg\:-translate-y-6 {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .lg\:-translate-y-8 {
+    --transform-translate-y: -2rem;
+  }
+
+  .lg\:-translate-y-10 {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .lg\:-translate-y-12 {
+    --transform-translate-y: -3rem;
+  }
+
+  .lg\:-translate-y-16 {
+    --transform-translate-y: -4rem;
+  }
+
+  .lg\:-translate-y-20 {
+    --transform-translate-y: -5rem;
+  }
+
+  .lg\:-translate-y-24 {
+    --transform-translate-y: -6rem;
+  }
+
+  .lg\:-translate-y-32 {
+    --transform-translate-y: -8rem;
+  }
+
+  .lg\:-translate-y-40 {
+    --transform-translate-y: -10rem;
+  }
+
+  .lg\:-translate-y-48 {
+    --transform-translate-y: -12rem;
+  }
+
+  .lg\:-translate-y-56 {
+    --transform-translate-y: -14rem;
+  }
+
+  .lg\:-translate-y-64 {
+    --transform-translate-y: -16rem;
+  }
+
+  .lg\:-translate-y-px {
+    --transform-translate-y: -1px;
+  }
+
+  .lg\:-translate-y-full {
+    --transform-translate-y: -100%;
+  }
+
+  .lg\:-translate-y-1\/2 {
+    --transform-translate-y: -50%;
+  }
+
+  .lg\:translate-y-1\/2 {
+    --transform-translate-y: 50%;
+  }
+
+  .lg\:translate-y-full {
+    --transform-translate-y: 100%;
+  }
+
+  .lg\:hover\:translate-x-0:hover {
+    --transform-translate-x: 0;
+  }
+
+  .lg\:hover\:translate-x-1:hover {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .lg\:hover\:translate-x-2:hover {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .lg\:hover\:translate-x-3:hover {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .lg\:hover\:translate-x-4:hover {
+    --transform-translate-x: 1rem;
+  }
+
+  .lg\:hover\:translate-x-5:hover {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .lg\:hover\:translate-x-6:hover {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .lg\:hover\:translate-x-8:hover {
+    --transform-translate-x: 2rem;
+  }
+
+  .lg\:hover\:translate-x-10:hover {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .lg\:hover\:translate-x-12:hover {
+    --transform-translate-x: 3rem;
+  }
+
+  .lg\:hover\:translate-x-16:hover {
+    --transform-translate-x: 4rem;
+  }
+
+  .lg\:hover\:translate-x-20:hover {
+    --transform-translate-x: 5rem;
+  }
+
+  .lg\:hover\:translate-x-24:hover {
+    --transform-translate-x: 6rem;
+  }
+
+  .lg\:hover\:translate-x-32:hover {
+    --transform-translate-x: 8rem;
+  }
+
+  .lg\:hover\:translate-x-40:hover {
+    --transform-translate-x: 10rem;
+  }
+
+  .lg\:hover\:translate-x-48:hover {
+    --transform-translate-x: 12rem;
+  }
+
+  .lg\:hover\:translate-x-56:hover {
+    --transform-translate-x: 14rem;
+  }
+
+  .lg\:hover\:translate-x-64:hover {
+    --transform-translate-x: 16rem;
+  }
+
+  .lg\:hover\:translate-x-px:hover {
+    --transform-translate-x: 1px;
+  }
+
+  .lg\:hover\:-translate-x-1:hover {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .lg\:hover\:-translate-x-2:hover {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .lg\:hover\:-translate-x-3:hover {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .lg\:hover\:-translate-x-4:hover {
+    --transform-translate-x: -1rem;
+  }
+
+  .lg\:hover\:-translate-x-5:hover {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .lg\:hover\:-translate-x-6:hover {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .lg\:hover\:-translate-x-8:hover {
+    --transform-translate-x: -2rem;
+  }
+
+  .lg\:hover\:-translate-x-10:hover {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .lg\:hover\:-translate-x-12:hover {
+    --transform-translate-x: -3rem;
+  }
+
+  .lg\:hover\:-translate-x-16:hover {
+    --transform-translate-x: -4rem;
+  }
+
+  .lg\:hover\:-translate-x-20:hover {
+    --transform-translate-x: -5rem;
+  }
+
+  .lg\:hover\:-translate-x-24:hover {
+    --transform-translate-x: -6rem;
+  }
+
+  .lg\:hover\:-translate-x-32:hover {
+    --transform-translate-x: -8rem;
+  }
+
+  .lg\:hover\:-translate-x-40:hover {
+    --transform-translate-x: -10rem;
+  }
+
+  .lg\:hover\:-translate-x-48:hover {
+    --transform-translate-x: -12rem;
+  }
+
+  .lg\:hover\:-translate-x-56:hover {
+    --transform-translate-x: -14rem;
+  }
+
+  .lg\:hover\:-translate-x-64:hover {
+    --transform-translate-x: -16rem;
+  }
+
+  .lg\:hover\:-translate-x-px:hover {
+    --transform-translate-x: -1px;
+  }
+
+  .lg\:hover\:-translate-x-full:hover {
+    --transform-translate-x: -100%;
+  }
+
+  .lg\:hover\:-translate-x-1\/2:hover {
+    --transform-translate-x: -50%;
+  }
+
+  .lg\:hover\:translate-x-1\/2:hover {
+    --transform-translate-x: 50%;
+  }
+
+  .lg\:hover\:translate-x-full:hover {
+    --transform-translate-x: 100%;
+  }
+
+  .lg\:hover\:translate-y-0:hover {
+    --transform-translate-y: 0;
+  }
+
+  .lg\:hover\:translate-y-1:hover {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .lg\:hover\:translate-y-2:hover {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .lg\:hover\:translate-y-3:hover {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .lg\:hover\:translate-y-4:hover {
+    --transform-translate-y: 1rem;
+  }
+
+  .lg\:hover\:translate-y-5:hover {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .lg\:hover\:translate-y-6:hover {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .lg\:hover\:translate-y-8:hover {
+    --transform-translate-y: 2rem;
+  }
+
+  .lg\:hover\:translate-y-10:hover {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .lg\:hover\:translate-y-12:hover {
+    --transform-translate-y: 3rem;
+  }
+
+  .lg\:hover\:translate-y-16:hover {
+    --transform-translate-y: 4rem;
+  }
+
+  .lg\:hover\:translate-y-20:hover {
+    --transform-translate-y: 5rem;
+  }
+
+  .lg\:hover\:translate-y-24:hover {
+    --transform-translate-y: 6rem;
+  }
+
+  .lg\:hover\:translate-y-32:hover {
+    --transform-translate-y: 8rem;
+  }
+
+  .lg\:hover\:translate-y-40:hover {
+    --transform-translate-y: 10rem;
+  }
+
+  .lg\:hover\:translate-y-48:hover {
+    --transform-translate-y: 12rem;
+  }
+
+  .lg\:hover\:translate-y-56:hover {
+    --transform-translate-y: 14rem;
+  }
+
+  .lg\:hover\:translate-y-64:hover {
+    --transform-translate-y: 16rem;
+  }
+
+  .lg\:hover\:translate-y-px:hover {
+    --transform-translate-y: 1px;
+  }
+
+  .lg\:hover\:-translate-y-1:hover {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .lg\:hover\:-translate-y-2:hover {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .lg\:hover\:-translate-y-3:hover {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .lg\:hover\:-translate-y-4:hover {
+    --transform-translate-y: -1rem;
+  }
+
+  .lg\:hover\:-translate-y-5:hover {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .lg\:hover\:-translate-y-6:hover {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .lg\:hover\:-translate-y-8:hover {
+    --transform-translate-y: -2rem;
+  }
+
+  .lg\:hover\:-translate-y-10:hover {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .lg\:hover\:-translate-y-12:hover {
+    --transform-translate-y: -3rem;
+  }
+
+  .lg\:hover\:-translate-y-16:hover {
+    --transform-translate-y: -4rem;
+  }
+
+  .lg\:hover\:-translate-y-20:hover {
+    --transform-translate-y: -5rem;
+  }
+
+  .lg\:hover\:-translate-y-24:hover {
+    --transform-translate-y: -6rem;
+  }
+
+  .lg\:hover\:-translate-y-32:hover {
+    --transform-translate-y: -8rem;
+  }
+
+  .lg\:hover\:-translate-y-40:hover {
+    --transform-translate-y: -10rem;
+  }
+
+  .lg\:hover\:-translate-y-48:hover {
+    --transform-translate-y: -12rem;
+  }
+
+  .lg\:hover\:-translate-y-56:hover {
+    --transform-translate-y: -14rem;
+  }
+
+  .lg\:hover\:-translate-y-64:hover {
+    --transform-translate-y: -16rem;
+  }
+
+  .lg\:hover\:-translate-y-px:hover {
+    --transform-translate-y: -1px;
+  }
+
+  .lg\:hover\:-translate-y-full:hover {
+    --transform-translate-y: -100%;
+  }
+
+  .lg\:hover\:-translate-y-1\/2:hover {
+    --transform-translate-y: -50%;
+  }
+
+  .lg\:hover\:translate-y-1\/2:hover {
+    --transform-translate-y: 50%;
+  }
+
+  .lg\:hover\:translate-y-full:hover {
+    --transform-translate-y: 100%;
+  }
+
+  .lg\:focus\:translate-x-0:focus {
+    --transform-translate-x: 0;
+  }
+
+  .lg\:focus\:translate-x-1:focus {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .lg\:focus\:translate-x-2:focus {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .lg\:focus\:translate-x-3:focus {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .lg\:focus\:translate-x-4:focus {
+    --transform-translate-x: 1rem;
+  }
+
+  .lg\:focus\:translate-x-5:focus {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .lg\:focus\:translate-x-6:focus {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .lg\:focus\:translate-x-8:focus {
+    --transform-translate-x: 2rem;
+  }
+
+  .lg\:focus\:translate-x-10:focus {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .lg\:focus\:translate-x-12:focus {
+    --transform-translate-x: 3rem;
+  }
+
+  .lg\:focus\:translate-x-16:focus {
+    --transform-translate-x: 4rem;
+  }
+
+  .lg\:focus\:translate-x-20:focus {
+    --transform-translate-x: 5rem;
+  }
+
+  .lg\:focus\:translate-x-24:focus {
+    --transform-translate-x: 6rem;
+  }
+
+  .lg\:focus\:translate-x-32:focus {
+    --transform-translate-x: 8rem;
+  }
+
+  .lg\:focus\:translate-x-40:focus {
+    --transform-translate-x: 10rem;
+  }
+
+  .lg\:focus\:translate-x-48:focus {
+    --transform-translate-x: 12rem;
+  }
+
+  .lg\:focus\:translate-x-56:focus {
+    --transform-translate-x: 14rem;
+  }
+
+  .lg\:focus\:translate-x-64:focus {
+    --transform-translate-x: 16rem;
+  }
+
+  .lg\:focus\:translate-x-px:focus {
+    --transform-translate-x: 1px;
+  }
+
+  .lg\:focus\:-translate-x-1:focus {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .lg\:focus\:-translate-x-2:focus {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .lg\:focus\:-translate-x-3:focus {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .lg\:focus\:-translate-x-4:focus {
+    --transform-translate-x: -1rem;
+  }
+
+  .lg\:focus\:-translate-x-5:focus {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .lg\:focus\:-translate-x-6:focus {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .lg\:focus\:-translate-x-8:focus {
+    --transform-translate-x: -2rem;
+  }
+
+  .lg\:focus\:-translate-x-10:focus {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .lg\:focus\:-translate-x-12:focus {
+    --transform-translate-x: -3rem;
+  }
+
+  .lg\:focus\:-translate-x-16:focus {
+    --transform-translate-x: -4rem;
+  }
+
+  .lg\:focus\:-translate-x-20:focus {
+    --transform-translate-x: -5rem;
+  }
+
+  .lg\:focus\:-translate-x-24:focus {
+    --transform-translate-x: -6rem;
+  }
+
+  .lg\:focus\:-translate-x-32:focus {
+    --transform-translate-x: -8rem;
+  }
+
+  .lg\:focus\:-translate-x-40:focus {
+    --transform-translate-x: -10rem;
+  }
+
+  .lg\:focus\:-translate-x-48:focus {
+    --transform-translate-x: -12rem;
+  }
+
+  .lg\:focus\:-translate-x-56:focus {
+    --transform-translate-x: -14rem;
+  }
+
+  .lg\:focus\:-translate-x-64:focus {
+    --transform-translate-x: -16rem;
+  }
+
+  .lg\:focus\:-translate-x-px:focus {
+    --transform-translate-x: -1px;
+  }
+
+  .lg\:focus\:-translate-x-full:focus {
+    --transform-translate-x: -100%;
+  }
+
+  .lg\:focus\:-translate-x-1\/2:focus {
+    --transform-translate-x: -50%;
+  }
+
+  .lg\:focus\:translate-x-1\/2:focus {
+    --transform-translate-x: 50%;
+  }
+
+  .lg\:focus\:translate-x-full:focus {
+    --transform-translate-x: 100%;
+  }
+
+  .lg\:focus\:translate-y-0:focus {
+    --transform-translate-y: 0;
+  }
+
+  .lg\:focus\:translate-y-1:focus {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .lg\:focus\:translate-y-2:focus {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .lg\:focus\:translate-y-3:focus {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .lg\:focus\:translate-y-4:focus {
+    --transform-translate-y: 1rem;
+  }
+
+  .lg\:focus\:translate-y-5:focus {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .lg\:focus\:translate-y-6:focus {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .lg\:focus\:translate-y-8:focus {
+    --transform-translate-y: 2rem;
+  }
+
+  .lg\:focus\:translate-y-10:focus {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .lg\:focus\:translate-y-12:focus {
+    --transform-translate-y: 3rem;
+  }
+
+  .lg\:focus\:translate-y-16:focus {
+    --transform-translate-y: 4rem;
+  }
+
+  .lg\:focus\:translate-y-20:focus {
+    --transform-translate-y: 5rem;
+  }
+
+  .lg\:focus\:translate-y-24:focus {
+    --transform-translate-y: 6rem;
+  }
+
+  .lg\:focus\:translate-y-32:focus {
+    --transform-translate-y: 8rem;
+  }
+
+  .lg\:focus\:translate-y-40:focus {
+    --transform-translate-y: 10rem;
+  }
+
+  .lg\:focus\:translate-y-48:focus {
+    --transform-translate-y: 12rem;
+  }
+
+  .lg\:focus\:translate-y-56:focus {
+    --transform-translate-y: 14rem;
+  }
+
+  .lg\:focus\:translate-y-64:focus {
+    --transform-translate-y: 16rem;
+  }
+
+  .lg\:focus\:translate-y-px:focus {
+    --transform-translate-y: 1px;
+  }
+
+  .lg\:focus\:-translate-y-1:focus {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .lg\:focus\:-translate-y-2:focus {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .lg\:focus\:-translate-y-3:focus {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .lg\:focus\:-translate-y-4:focus {
+    --transform-translate-y: -1rem;
+  }
+
+  .lg\:focus\:-translate-y-5:focus {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .lg\:focus\:-translate-y-6:focus {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .lg\:focus\:-translate-y-8:focus {
+    --transform-translate-y: -2rem;
+  }
+
+  .lg\:focus\:-translate-y-10:focus {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .lg\:focus\:-translate-y-12:focus {
+    --transform-translate-y: -3rem;
+  }
+
+  .lg\:focus\:-translate-y-16:focus {
+    --transform-translate-y: -4rem;
+  }
+
+  .lg\:focus\:-translate-y-20:focus {
+    --transform-translate-y: -5rem;
+  }
+
+  .lg\:focus\:-translate-y-24:focus {
+    --transform-translate-y: -6rem;
+  }
+
+  .lg\:focus\:-translate-y-32:focus {
+    --transform-translate-y: -8rem;
+  }
+
+  .lg\:focus\:-translate-y-40:focus {
+    --transform-translate-y: -10rem;
+  }
+
+  .lg\:focus\:-translate-y-48:focus {
+    --transform-translate-y: -12rem;
+  }
+
+  .lg\:focus\:-translate-y-56:focus {
+    --transform-translate-y: -14rem;
+  }
+
+  .lg\:focus\:-translate-y-64:focus {
+    --transform-translate-y: -16rem;
+  }
+
+  .lg\:focus\:-translate-y-px:focus {
+    --transform-translate-y: -1px;
+  }
+
+  .lg\:focus\:-translate-y-full:focus {
+    --transform-translate-y: -100%;
+  }
+
+  .lg\:focus\:-translate-y-1\/2:focus {
+    --transform-translate-y: -50%;
+  }
+
+  .lg\:focus\:translate-y-1\/2:focus {
+    --transform-translate-y: 50%;
+  }
+
+  .lg\:focus\:translate-y-full:focus {
+    --transform-translate-y: 100%;
+  }
+
+  .lg\:skew-x-0 {
+    --transform-skew-x: 0;
+  }
+
+  .lg\:skew-x-3 {
+    --transform-skew-x: 3deg;
+  }
+
+  .lg\:skew-x-6 {
+    --transform-skew-x: 6deg;
+  }
+
+  .lg\:skew-x-12 {
+    --transform-skew-x: 12deg;
+  }
+
+  .lg\:-skew-x-12 {
+    --transform-skew-x: -12deg;
+  }
+
+  .lg\:-skew-x-6 {
+    --transform-skew-x: -6deg;
+  }
+
+  .lg\:-skew-x-3 {
+    --transform-skew-x: -3deg;
+  }
+
+  .lg\:skew-y-0 {
+    --transform-skew-y: 0;
+  }
+
+  .lg\:skew-y-3 {
+    --transform-skew-y: 3deg;
+  }
+
+  .lg\:skew-y-6 {
+    --transform-skew-y: 6deg;
+  }
+
+  .lg\:skew-y-12 {
+    --transform-skew-y: 12deg;
+  }
+
+  .lg\:-skew-y-12 {
+    --transform-skew-y: -12deg;
+  }
+
+  .lg\:-skew-y-6 {
+    --transform-skew-y: -6deg;
+  }
+
+  .lg\:-skew-y-3 {
+    --transform-skew-y: -3deg;
+  }
+
+  .lg\:hover\:skew-x-0:hover {
+    --transform-skew-x: 0;
+  }
+
+  .lg\:hover\:skew-x-3:hover {
+    --transform-skew-x: 3deg;
+  }
+
+  .lg\:hover\:skew-x-6:hover {
+    --transform-skew-x: 6deg;
+  }
+
+  .lg\:hover\:skew-x-12:hover {
+    --transform-skew-x: 12deg;
+  }
+
+  .lg\:hover\:-skew-x-12:hover {
+    --transform-skew-x: -12deg;
+  }
+
+  .lg\:hover\:-skew-x-6:hover {
+    --transform-skew-x: -6deg;
+  }
+
+  .lg\:hover\:-skew-x-3:hover {
+    --transform-skew-x: -3deg;
+  }
+
+  .lg\:hover\:skew-y-0:hover {
+    --transform-skew-y: 0;
+  }
+
+  .lg\:hover\:skew-y-3:hover {
+    --transform-skew-y: 3deg;
+  }
+
+  .lg\:hover\:skew-y-6:hover {
+    --transform-skew-y: 6deg;
+  }
+
+  .lg\:hover\:skew-y-12:hover {
+    --transform-skew-y: 12deg;
+  }
+
+  .lg\:hover\:-skew-y-12:hover {
+    --transform-skew-y: -12deg;
+  }
+
+  .lg\:hover\:-skew-y-6:hover {
+    --transform-skew-y: -6deg;
+  }
+
+  .lg\:hover\:-skew-y-3:hover {
+    --transform-skew-y: -3deg;
+  }
+
+  .lg\:focus\:skew-x-0:focus {
+    --transform-skew-x: 0;
+  }
+
+  .lg\:focus\:skew-x-3:focus {
+    --transform-skew-x: 3deg;
+  }
+
+  .lg\:focus\:skew-x-6:focus {
+    --transform-skew-x: 6deg;
+  }
+
+  .lg\:focus\:skew-x-12:focus {
+    --transform-skew-x: 12deg;
+  }
+
+  .lg\:focus\:-skew-x-12:focus {
+    --transform-skew-x: -12deg;
+  }
+
+  .lg\:focus\:-skew-x-6:focus {
+    --transform-skew-x: -6deg;
+  }
+
+  .lg\:focus\:-skew-x-3:focus {
+    --transform-skew-x: -3deg;
+  }
+
+  .lg\:focus\:skew-y-0:focus {
+    --transform-skew-y: 0;
+  }
+
+  .lg\:focus\:skew-y-3:focus {
+    --transform-skew-y: 3deg;
+  }
+
+  .lg\:focus\:skew-y-6:focus {
+    --transform-skew-y: 6deg;
+  }
+
+  .lg\:focus\:skew-y-12:focus {
+    --transform-skew-y: 12deg;
+  }
+
+  .lg\:focus\:-skew-y-12:focus {
+    --transform-skew-y: -12deg;
+  }
+
+  .lg\:focus\:-skew-y-6:focus {
+    --transform-skew-y: -6deg;
+  }
+
+  .lg\:focus\:-skew-y-3:focus {
+    --transform-skew-y: -3deg;
+  }
+
+  .lg\:transition-none {
+    transition-property: none;
+  }
+
+  .lg\:transition-all {
+    transition-property: all;
+  }
+
+  .lg\:transition {
+    transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+  }
+
+  .lg\:transition-colors {
+    transition-property: background-color, border-color, color, fill, stroke;
+  }
+
+  .lg\:transition-opacity {
+    transition-property: opacity;
+  }
+
+  .lg\:transition-shadow {
+    transition-property: box-shadow;
+  }
+
+  .lg\:transition-transform {
+    transition-property: transform;
+  }
+
+  .lg\:ease-linear {
+    transition-timing-function: linear;
+  }
+
+  .lg\:ease-in {
+    transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+  }
+
+  .lg\:ease-out {
+    transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+  }
+
+  .lg\:ease-in-out {
+    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .lg\:duration-75 {
+    transition-duration: 75ms;
+  }
+
+  .lg\:duration-100 {
+    transition-duration: 100ms;
+  }
+
+  .lg\:duration-150 {
+    transition-duration: 150ms;
+  }
+
+  .lg\:duration-200 {
+    transition-duration: 200ms;
+  }
+
+  .lg\:duration-300 {
+    transition-duration: 300ms;
+  }
+
+  .lg\:duration-500 {
+    transition-duration: 500ms;
+  }
+
+  .lg\:duration-700 {
+    transition-duration: 700ms;
+  }
+
+  .lg\:duration-1000 {
+    transition-duration: 1000ms;
+  }
+
+  .lg\:delay-75 {
+    transition-delay: 75ms;
+  }
+
+  .lg\:delay-100 {
+    transition-delay: 100ms;
+  }
+
+  .lg\:delay-150 {
+    transition-delay: 150ms;
+  }
+
+  .lg\:delay-200 {
+    transition-delay: 200ms;
+  }
+
+  .lg\:delay-300 {
+    transition-delay: 300ms;
+  }
+
+  .lg\:delay-500 {
+    transition-delay: 500ms;
+  }
+
+  .lg\:delay-700 {
+    transition-delay: 700ms;
+  }
+
+  .lg\:delay-1000 {
+    transition-delay: 1000ms;
+  }
+
+  .lg\:animate-none {
+    -webkit-animation: none;
+            animation: none;
+  }
+
+  .lg\:animate-spin {
+    -webkit-animation: spin 1s linear infinite;
+            animation: spin 1s linear infinite;
+  }
+
+  .lg\:animate-ping {
+    -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+            animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+  }
+
+  .lg\:animate-pulse {
+    -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+            animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+  }
+
+  .lg\:animate-bounce {
+    -webkit-animation: bounce 1s infinite;
+            animation: bounce 1s infinite;
+  }
+}
+
+@media (min-width: 1280px) {
+  .xl\:container {
+    width: 100%;
+  }
+
+  @media (min-width: 640px) {
+    .xl\:container {
+      max-width: 640px;
+    }
+  }
+
+  @media (min-width: 768px) {
+    .xl\:container {
+      max-width: 768px;
+    }
+  }
+
+  @media (min-width: 1024px) {
+    .xl\:container {
+      max-width: 1024px;
+    }
+  }
+
+  @media (min-width: 1280px) {
+    .xl\:container {
+      max-width: 1280px;
+    }
+  }
+
+  .xl\:space-y-0 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0px * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-0 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0px * var(--space-x-reverse));
+    margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.25rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.25rem * var(--space-x-reverse));
+    margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.5rem * var(--space-x-reverse));
+    margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.75rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.75rem * var(--space-x-reverse));
+    margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1rem * var(--space-x-reverse));
+    margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.25rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.25rem * var(--space-x-reverse));
+    margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.5rem * var(--space-x-reverse));
+    margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2rem * var(--space-x-reverse));
+    margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2.5rem * var(--space-x-reverse));
+    margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(3rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(3rem * var(--space-x-reverse));
+    margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(4rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(4rem * var(--space-x-reverse));
+    margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(5rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(5rem * var(--space-x-reverse));
+    margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(6rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(6rem * var(--space-x-reverse));
+    margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(8rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(8rem * var(--space-x-reverse));
+    margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(10rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(10rem * var(--space-x-reverse));
+    margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(12rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(12rem * var(--space-x-reverse));
+    margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(14rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(14rem * var(--space-x-reverse));
+    margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(16rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(16rem * var(--space-x-reverse));
+    margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1px * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1px * var(--space-x-reverse));
+    margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.25rem * var(--space-x-reverse));
+    margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.5rem * var(--space-x-reverse));
+    margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.75rem * var(--space-x-reverse));
+    margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1rem * var(--space-x-reverse));
+    margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.25rem * var(--space-x-reverse));
+    margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.5rem * var(--space-x-reverse));
+    margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2rem * var(--space-x-reverse));
+    margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2.5rem * var(--space-x-reverse));
+    margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-3rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-3rem * var(--space-x-reverse));
+    margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-4rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-4rem * var(--space-x-reverse));
+    margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-5rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-5rem * var(--space-x-reverse));
+    margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-6rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-6rem * var(--space-x-reverse));
+    margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-8rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-8rem * var(--space-x-reverse));
+    margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-10rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-10rem * var(--space-x-reverse));
+    margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-12rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-12rem * var(--space-x-reverse));
+    margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-14rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-14rem * var(--space-x-reverse));
+    margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-16rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-16rem * var(--space-x-reverse));
+    margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1px * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1px * var(--space-x-reverse));
+    margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-reverse > :not(template) ~ :not(template) {
+    --space-y-reverse: 1;
+  }
+
+  .xl\:space-x-reverse > :not(template) ~ :not(template) {
+    --space-x-reverse: 1;
+  }
+
+  .xl\:divide-y-0 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(0px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x-0 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(0px * var(--divide-x-reverse));
+    border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y-2 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(2px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x-2 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(2px * var(--divide-x-reverse));
+    border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y-4 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(4px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x-4 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(4px * var(--divide-x-reverse));
+    border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y-8 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(8px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x-8 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(8px * var(--divide-x-reverse));
+    border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(1px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(1px * var(--divide-x-reverse));
+    border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y-reverse > :not(template) ~ :not(template) {
+    --divide-y-reverse: 1;
+  }
+
+  .xl\:divide-x-reverse > :not(template) ~ :not(template) {
+    --divide-x-reverse: 1;
+  }
+
+  .xl\:divide-transparent > :not(template) ~ :not(template) {
+    border-color: transparent;
+  }
+
+  .xl\:divide-current > :not(template) ~ :not(template) {
+    border-color: currentColor;
+  }
+
+  .xl\:divide-black > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--divide-opacity));
+  }
+
+  .xl\:divide-white > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--divide-opacity));
+  }
+
+  .xl\:divide-solid > :not(template) ~ :not(template) {
+    border-style: solid;
+  }
+
+  .xl\:divide-dashed > :not(template) ~ :not(template) {
+    border-style: dashed;
+  }
+
+  .xl\:divide-dotted > :not(template) ~ :not(template) {
+    border-style: dotted;
+  }
+
+  .xl\:divide-double > :not(template) ~ :not(template) {
+    border-style: double;
+  }
+
+  .xl\:divide-none > :not(template) ~ :not(template) {
+    border-style: none;
+  }
+
+  .xl\:divide-opacity-0 > :not(template) ~ :not(template) {
+    --divide-opacity: 0;
+  }
+
+  .xl\:divide-opacity-25 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.25;
+  }
+
+  .xl\:divide-opacity-50 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.5;
+  }
+
+  .xl\:divide-opacity-75 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.75;
+  }
+
+  .xl\:divide-opacity-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+  }
+
+  .xl\:sr-only {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .xl\:not-sr-only {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .xl\:focus\:sr-only:focus {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .xl\:focus\:not-sr-only:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .xl\:appearance-none {
+    -webkit-appearance: none;
+       -moz-appearance: none;
+            appearance: none;
+  }
+
+  .xl\:bg-fixed {
+    background-attachment: fixed;
+  }
+
+  .xl\:bg-local {
+    background-attachment: local;
+  }
+
+  .xl\:bg-scroll {
+    background-attachment: scroll;
+  }
+
+  .xl\:bg-clip-border {
+    background-clip: border-box;
+  }
+
+  .xl\:bg-clip-padding {
+    background-clip: padding-box;
+  }
+
+  .xl\:bg-clip-content {
+    background-clip: content-box;
+  }
+
+  .xl\:bg-clip-text {
+    -webkit-background-clip: text;
+            background-clip: text;
+  }
+
+  .xl\:bg-transparent {
+    background-color: transparent;
+  }
+
+  .xl\:bg-current {
+    background-color: currentColor;
+  }
+
+  .xl\:bg-black {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .xl\:bg-white {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-100 {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-200 {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-300 {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-400 {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-500 {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-600 {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-700 {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-800 {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-900 {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-200 {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-300 {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-400 {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-500 {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-600 {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-700 {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-800 {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-900 {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-100 {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-200 {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-300 {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-400 {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-500 {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-600 {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-700 {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-800 {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-900 {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-100 {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-200 {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-300 {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-400 {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-500 {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-600 {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-700 {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-800 {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-900 {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-100 {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-200 {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-300 {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-400 {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-500 {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-600 {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-700 {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-800 {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-900 {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-100 {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-200 {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-300 {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-400 {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-500 {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-600 {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-700 {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-800 {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-900 {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-100 {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-200 {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-300 {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-400 {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-500 {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-600 {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-700 {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-800 {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-900 {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-100 {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-200 {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-300 {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-400 {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-500 {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-600 {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-700 {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-800 {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-900 {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-100 {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-200 {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-300 {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-400 {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-500 {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-600 {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-700 {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-800 {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-900 {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-200 {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-300 {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-400 {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-500 {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-600 {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-700 {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-800 {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-900 {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-transparent:hover {
+    background-color: transparent;
+  }
+
+  .xl\:hover\:bg-current:hover {
+    background-color: currentColor;
+  }
+
+  .xl\:hover\:bg-black:hover {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-white:hover {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-100:hover {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-200:hover {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-300:hover {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-400:hover {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-500:hover {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-600:hover {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-700:hover {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-800:hover {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-900:hover {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-300:hover {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-400:hover {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-500:hover {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-600:hover {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-700:hover {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-800:hover {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-900:hover {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-200:hover {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-600:hover {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-700:hover {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-800:hover {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-900:hover {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-200:hover {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-300:hover {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-500:hover {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-600:hover {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-700:hover {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-800:hover {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-900:hover {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-100:hover {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-200:hover {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-300:hover {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-400:hover {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-500:hover {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-600:hover {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-700:hover {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-800:hover {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-900:hover {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-100:hover {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-200:hover {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-300:hover {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-400:hover {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-500:hover {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-600:hover {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-700:hover {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-800:hover {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-900:hover {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-200:hover {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-300:hover {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-400:hover {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-500:hover {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-600:hover {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-700:hover {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-800:hover {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-900:hover {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-200:hover {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-300:hover {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-400:hover {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-500:hover {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-600:hover {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-700:hover {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-800:hover {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-900:hover {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-100:hover {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-200:hover {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-300:hover {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-400:hover {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-500:hover {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-600:hover {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-700:hover {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-800:hover {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-900:hover {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-400:hover {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-600:hover {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-700:hover {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-800:hover {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-900:hover {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-transparent:focus {
+    background-color: transparent;
+  }
+
+  .xl\:focus\:bg-current:focus {
+    background-color: currentColor;
+  }
+
+  .xl\:focus\:bg-black:focus {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-white:focus {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-100:focus {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-200:focus {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-300:focus {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-400:focus {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-500:focus {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-600:focus {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-700:focus {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-800:focus {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-900:focus {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-300:focus {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-400:focus {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-500:focus {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-600:focus {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-700:focus {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-800:focus {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-900:focus {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-200:focus {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-600:focus {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-700:focus {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-800:focus {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-900:focus {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-200:focus {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-300:focus {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-500:focus {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-600:focus {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-700:focus {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-800:focus {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-900:focus {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-100:focus {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-200:focus {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-300:focus {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-400:focus {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-500:focus {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-600:focus {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-700:focus {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-800:focus {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-900:focus {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-100:focus {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-200:focus {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-300:focus {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-400:focus {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-500:focus {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-600:focus {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-700:focus {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-800:focus {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-900:focus {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-200:focus {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-300:focus {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-400:focus {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-500:focus {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-600:focus {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-700:focus {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-800:focus {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-900:focus {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-200:focus {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-300:focus {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-400:focus {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-500:focus {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-600:focus {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-700:focus {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-800:focus {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-900:focus {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-100:focus {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-200:focus {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-300:focus {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-400:focus {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-500:focus {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-600:focus {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-700:focus {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-800:focus {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-900:focus {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-400:focus {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-600:focus {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-700:focus {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-800:focus {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-900:focus {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .xl\:bg-none {
+    background-image: none;
+  }
+
+  .xl\:bg-gradient-to-t {
+    background-image: linear-gradient(to top, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-tr {
+    background-image: linear-gradient(to top right, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-r {
+    background-image: linear-gradient(to right, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-br {
+    background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-b {
+    background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-bl {
+    background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-l {
+    background-image: linear-gradient(to left, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-tl {
+    background-image: linear-gradient(to top left, var(--gradient-color-stops));
+  }
+
+  .xl\:from-transparent {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:from-current {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:from-black {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:from-white {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:from-gray-100 {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:from-gray-200 {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:from-gray-300 {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:from-gray-400 {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:from-gray-500 {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:from-gray-600 {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:from-gray-700 {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:from-gray-800 {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:from-gray-900 {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:from-red-100 {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:from-red-200 {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:from-red-300 {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:from-red-400 {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:from-red-500 {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:from-red-600 {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:from-red-700 {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:from-red-800 {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:from-red-900 {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:from-orange-100 {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:from-orange-200 {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:from-orange-300 {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:from-orange-400 {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:from-orange-500 {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:from-orange-600 {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:from-orange-700 {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:from-orange-800 {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:from-orange-900 {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:from-yellow-100 {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:from-yellow-200 {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:from-yellow-300 {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:from-yellow-400 {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:from-yellow-500 {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:from-yellow-600 {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:from-yellow-700 {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:from-yellow-800 {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:from-yellow-900 {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:from-green-100 {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:from-green-200 {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:from-green-300 {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:from-green-400 {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:from-green-500 {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:from-green-600 {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:from-green-700 {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:from-green-800 {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:from-green-900 {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:from-teal-100 {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:from-teal-200 {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:from-teal-300 {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:from-teal-400 {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:from-teal-500 {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:from-teal-600 {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:from-teal-700 {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:from-teal-800 {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:from-teal-900 {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:from-blue-100 {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:from-blue-200 {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:from-blue-300 {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:from-blue-400 {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:from-blue-500 {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:from-blue-600 {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:from-blue-700 {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:from-blue-800 {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:from-blue-900 {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:from-indigo-100 {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:from-indigo-200 {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:from-indigo-300 {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:from-indigo-400 {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:from-indigo-500 {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:from-indigo-600 {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:from-indigo-700 {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:from-indigo-800 {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:from-indigo-900 {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:from-purple-100 {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:from-purple-200 {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:from-purple-300 {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:from-purple-400 {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:from-purple-500 {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:from-purple-600 {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:from-purple-700 {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:from-purple-800 {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:from-purple-900 {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:from-pink-100 {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:from-pink-200 {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:from-pink-300 {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:from-pink-400 {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:from-pink-500 {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:from-pink-600 {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:from-pink-700 {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:from-pink-800 {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:from-pink-900 {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:via-transparent {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:via-current {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:via-black {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:via-white {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:via-gray-100 {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:via-gray-200 {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:via-gray-300 {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:via-gray-400 {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:via-gray-500 {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:via-gray-600 {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:via-gray-700 {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:via-gray-800 {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:via-gray-900 {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:via-red-100 {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:via-red-200 {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:via-red-300 {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:via-red-400 {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:via-red-500 {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:via-red-600 {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:via-red-700 {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:via-red-800 {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:via-red-900 {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:via-orange-100 {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:via-orange-200 {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:via-orange-300 {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:via-orange-400 {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:via-orange-500 {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:via-orange-600 {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:via-orange-700 {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:via-orange-800 {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:via-orange-900 {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:via-yellow-100 {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:via-yellow-200 {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:via-yellow-300 {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:via-yellow-400 {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:via-yellow-500 {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:via-yellow-600 {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:via-yellow-700 {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:via-yellow-800 {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:via-yellow-900 {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:via-green-100 {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:via-green-200 {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:via-green-300 {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:via-green-400 {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:via-green-500 {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:via-green-600 {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:via-green-700 {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:via-green-800 {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:via-green-900 {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:via-teal-100 {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:via-teal-200 {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:via-teal-300 {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:via-teal-400 {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:via-teal-500 {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:via-teal-600 {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:via-teal-700 {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:via-teal-800 {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:via-teal-900 {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:via-blue-100 {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:via-blue-200 {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:via-blue-300 {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:via-blue-400 {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:via-blue-500 {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:via-blue-600 {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:via-blue-700 {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:via-blue-800 {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:via-blue-900 {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:via-indigo-100 {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:via-indigo-200 {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:via-indigo-300 {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:via-indigo-400 {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:via-indigo-500 {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:via-indigo-600 {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:via-indigo-700 {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:via-indigo-800 {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:via-indigo-900 {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:via-purple-100 {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:via-purple-200 {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:via-purple-300 {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:via-purple-400 {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:via-purple-500 {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:via-purple-600 {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:via-purple-700 {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:via-purple-800 {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:via-purple-900 {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:via-pink-100 {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:via-pink-200 {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:via-pink-300 {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:via-pink-400 {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:via-pink-500 {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:via-pink-600 {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:via-pink-700 {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:via-pink-800 {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:via-pink-900 {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:to-transparent {
+    --gradient-to-color: transparent;
+  }
+
+  .xl\:to-current {
+    --gradient-to-color: currentColor;
+  }
+
+  .xl\:to-black {
+    --gradient-to-color: #000;
+  }
+
+  .xl\:to-white {
+    --gradient-to-color: #fff;
+  }
+
+  .xl\:to-gray-100 {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .xl\:to-gray-200 {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .xl\:to-gray-300 {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .xl\:to-gray-400 {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .xl\:to-gray-500 {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .xl\:to-gray-600 {
+    --gradient-to-color: #718096;
+  }
+
+  .xl\:to-gray-700 {
+    --gradient-to-color: #4a5568;
+  }
+
+  .xl\:to-gray-800 {
+    --gradient-to-color: #2d3748;
+  }
+
+  .xl\:to-gray-900 {
+    --gradient-to-color: #1a202c;
+  }
+
+  .xl\:to-red-100 {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .xl\:to-red-200 {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .xl\:to-red-300 {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .xl\:to-red-400 {
+    --gradient-to-color: #fc8181;
+  }
+
+  .xl\:to-red-500 {
+    --gradient-to-color: #f56565;
+  }
+
+  .xl\:to-red-600 {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .xl\:to-red-700 {
+    --gradient-to-color: #c53030;
+  }
+
+  .xl\:to-red-800 {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .xl\:to-red-900 {
+    --gradient-to-color: #742a2a;
+  }
+
+  .xl\:to-orange-100 {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .xl\:to-orange-200 {
+    --gradient-to-color: #feebc8;
+  }
+
+  .xl\:to-orange-300 {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .xl\:to-orange-400 {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .xl\:to-orange-500 {
+    --gradient-to-color: #ed8936;
+  }
+
+  .xl\:to-orange-600 {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .xl\:to-orange-700 {
+    --gradient-to-color: #c05621;
+  }
+
+  .xl\:to-orange-800 {
+    --gradient-to-color: #9c4221;
+  }
+
+  .xl\:to-orange-900 {
+    --gradient-to-color: #7b341e;
+  }
+
+  .xl\:to-yellow-100 {
+    --gradient-to-color: #fffff0;
+  }
+
+  .xl\:to-yellow-200 {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .xl\:to-yellow-300 {
+    --gradient-to-color: #faf089;
+  }
+
+  .xl\:to-yellow-400 {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .xl\:to-yellow-500 {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .xl\:to-yellow-600 {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .xl\:to-yellow-700 {
+    --gradient-to-color: #b7791f;
+  }
+
+  .xl\:to-yellow-800 {
+    --gradient-to-color: #975a16;
+  }
+
+  .xl\:to-yellow-900 {
+    --gradient-to-color: #744210;
+  }
+
+  .xl\:to-green-100 {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .xl\:to-green-200 {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .xl\:to-green-300 {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .xl\:to-green-400 {
+    --gradient-to-color: #68d391;
+  }
+
+  .xl\:to-green-500 {
+    --gradient-to-color: #48bb78;
+  }
+
+  .xl\:to-green-600 {
+    --gradient-to-color: #38a169;
+  }
+
+  .xl\:to-green-700 {
+    --gradient-to-color: #2f855a;
+  }
+
+  .xl\:to-green-800 {
+    --gradient-to-color: #276749;
+  }
+
+  .xl\:to-green-900 {
+    --gradient-to-color: #22543d;
+  }
+
+  .xl\:to-teal-100 {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .xl\:to-teal-200 {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .xl\:to-teal-300 {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .xl\:to-teal-400 {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .xl\:to-teal-500 {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .xl\:to-teal-600 {
+    --gradient-to-color: #319795;
+  }
+
+  .xl\:to-teal-700 {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .xl\:to-teal-800 {
+    --gradient-to-color: #285e61;
+  }
+
+  .xl\:to-teal-900 {
+    --gradient-to-color: #234e52;
+  }
+
+  .xl\:to-blue-100 {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .xl\:to-blue-200 {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .xl\:to-blue-300 {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .xl\:to-blue-400 {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .xl\:to-blue-500 {
+    --gradient-to-color: #4299e1;
+  }
+
+  .xl\:to-blue-600 {
+    --gradient-to-color: #3182ce;
+  }
+
+  .xl\:to-blue-700 {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .xl\:to-blue-800 {
+    --gradient-to-color: #2c5282;
+  }
+
+  .xl\:to-blue-900 {
+    --gradient-to-color: #2a4365;
+  }
+
+  .xl\:to-indigo-100 {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .xl\:to-indigo-200 {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .xl\:to-indigo-300 {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .xl\:to-indigo-400 {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .xl\:to-indigo-500 {
+    --gradient-to-color: #667eea;
+  }
+
+  .xl\:to-indigo-600 {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .xl\:to-indigo-700 {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .xl\:to-indigo-800 {
+    --gradient-to-color: #434190;
+  }
+
+  .xl\:to-indigo-900 {
+    --gradient-to-color: #3c366b;
+  }
+
+  .xl\:to-purple-100 {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .xl\:to-purple-200 {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .xl\:to-purple-300 {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .xl\:to-purple-400 {
+    --gradient-to-color: #b794f4;
+  }
+
+  .xl\:to-purple-500 {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .xl\:to-purple-600 {
+    --gradient-to-color: #805ad5;
+  }
+
+  .xl\:to-purple-700 {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .xl\:to-purple-800 {
+    --gradient-to-color: #553c9a;
+  }
+
+  .xl\:to-purple-900 {
+    --gradient-to-color: #44337a;
+  }
+
+  .xl\:to-pink-100 {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .xl\:to-pink-200 {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .xl\:to-pink-300 {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .xl\:to-pink-400 {
+    --gradient-to-color: #f687b3;
+  }
+
+  .xl\:to-pink-500 {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .xl\:to-pink-600 {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .xl\:to-pink-700 {
+    --gradient-to-color: #b83280;
+  }
+
+  .xl\:to-pink-800 {
+    --gradient-to-color: #97266d;
+  }
+
+  .xl\:to-pink-900 {
+    --gradient-to-color: #702459;
+  }
+
+  .xl\:hover\:from-transparent:hover {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:hover\:from-current:hover {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:hover\:from-black:hover {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:hover\:from-white:hover {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:hover\:from-gray-100:hover {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:hover\:from-gray-200:hover {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:hover\:from-gray-300:hover {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:hover\:from-gray-400:hover {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:hover\:from-gray-500:hover {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:hover\:from-gray-600:hover {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:hover\:from-gray-700:hover {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:hover\:from-gray-800:hover {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:hover\:from-gray-900:hover {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:hover\:from-red-100:hover {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:hover\:from-red-200:hover {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:hover\:from-red-300:hover {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:hover\:from-red-400:hover {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:hover\:from-red-500:hover {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:hover\:from-red-600:hover {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:hover\:from-red-700:hover {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:hover\:from-red-800:hover {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:hover\:from-red-900:hover {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:hover\:from-orange-100:hover {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:hover\:from-orange-200:hover {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:hover\:from-orange-300:hover {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:hover\:from-orange-400:hover {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:hover\:from-orange-500:hover {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:hover\:from-orange-600:hover {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:hover\:from-orange-700:hover {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:hover\:from-orange-800:hover {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:hover\:from-orange-900:hover {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:hover\:from-yellow-100:hover {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:hover\:from-yellow-200:hover {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:hover\:from-yellow-300:hover {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:hover\:from-yellow-400:hover {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:hover\:from-yellow-500:hover {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:hover\:from-yellow-600:hover {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:hover\:from-yellow-700:hover {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:hover\:from-yellow-800:hover {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:hover\:from-yellow-900:hover {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:hover\:from-green-100:hover {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:hover\:from-green-200:hover {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:hover\:from-green-300:hover {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:hover\:from-green-400:hover {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:hover\:from-green-500:hover {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:hover\:from-green-600:hover {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:hover\:from-green-700:hover {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:hover\:from-green-800:hover {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:hover\:from-green-900:hover {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:hover\:from-teal-100:hover {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:hover\:from-teal-200:hover {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:hover\:from-teal-300:hover {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:hover\:from-teal-400:hover {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:hover\:from-teal-500:hover {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:hover\:from-teal-600:hover {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:hover\:from-teal-700:hover {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:hover\:from-teal-800:hover {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:hover\:from-teal-900:hover {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:hover\:from-blue-100:hover {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:hover\:from-blue-200:hover {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:hover\:from-blue-300:hover {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:hover\:from-blue-400:hover {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:hover\:from-blue-500:hover {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:hover\:from-blue-600:hover {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:hover\:from-blue-700:hover {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:hover\:from-blue-800:hover {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:hover\:from-blue-900:hover {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:hover\:from-indigo-100:hover {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:hover\:from-indigo-200:hover {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:hover\:from-indigo-300:hover {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:hover\:from-indigo-400:hover {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:hover\:from-indigo-500:hover {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:hover\:from-indigo-600:hover {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:hover\:from-indigo-700:hover {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:hover\:from-indigo-800:hover {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:hover\:from-indigo-900:hover {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:hover\:from-purple-100:hover {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:hover\:from-purple-200:hover {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:hover\:from-purple-300:hover {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:hover\:from-purple-400:hover {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:hover\:from-purple-500:hover {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:hover\:from-purple-600:hover {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:hover\:from-purple-700:hover {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:hover\:from-purple-800:hover {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:hover\:from-purple-900:hover {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:hover\:from-pink-100:hover {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:hover\:from-pink-200:hover {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:hover\:from-pink-300:hover {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:hover\:from-pink-400:hover {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:hover\:from-pink-500:hover {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:hover\:from-pink-600:hover {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:hover\:from-pink-700:hover {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:hover\:from-pink-800:hover {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:hover\:from-pink-900:hover {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:hover\:via-transparent:hover {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:hover\:via-current:hover {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:hover\:via-black:hover {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:hover\:via-white:hover {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:hover\:via-gray-100:hover {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:hover\:via-gray-200:hover {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:hover\:via-gray-300:hover {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:hover\:via-gray-400:hover {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:hover\:via-gray-500:hover {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:hover\:via-gray-600:hover {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:hover\:via-gray-700:hover {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:hover\:via-gray-800:hover {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:hover\:via-gray-900:hover {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:hover\:via-red-100:hover {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:hover\:via-red-200:hover {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:hover\:via-red-300:hover {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:hover\:via-red-400:hover {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:hover\:via-red-500:hover {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:hover\:via-red-600:hover {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:hover\:via-red-700:hover {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:hover\:via-red-800:hover {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:hover\:via-red-900:hover {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:hover\:via-orange-100:hover {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:hover\:via-orange-200:hover {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:hover\:via-orange-300:hover {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:hover\:via-orange-400:hover {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:hover\:via-orange-500:hover {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:hover\:via-orange-600:hover {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:hover\:via-orange-700:hover {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:hover\:via-orange-800:hover {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:hover\:via-orange-900:hover {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:hover\:via-yellow-100:hover {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:hover\:via-yellow-200:hover {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:hover\:via-yellow-300:hover {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:hover\:via-yellow-400:hover {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:hover\:via-yellow-500:hover {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:hover\:via-yellow-600:hover {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:hover\:via-yellow-700:hover {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:hover\:via-yellow-800:hover {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:hover\:via-yellow-900:hover {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:hover\:via-green-100:hover {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:hover\:via-green-200:hover {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:hover\:via-green-300:hover {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:hover\:via-green-400:hover {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:hover\:via-green-500:hover {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:hover\:via-green-600:hover {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:hover\:via-green-700:hover {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:hover\:via-green-800:hover {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:hover\:via-green-900:hover {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:hover\:via-teal-100:hover {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:hover\:via-teal-200:hover {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:hover\:via-teal-300:hover {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:hover\:via-teal-400:hover {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:hover\:via-teal-500:hover {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:hover\:via-teal-600:hover {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:hover\:via-teal-700:hover {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:hover\:via-teal-800:hover {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:hover\:via-teal-900:hover {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:hover\:via-blue-100:hover {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:hover\:via-blue-200:hover {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:hover\:via-blue-300:hover {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:hover\:via-blue-400:hover {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:hover\:via-blue-500:hover {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:hover\:via-blue-600:hover {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:hover\:via-blue-700:hover {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:hover\:via-blue-800:hover {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:hover\:via-blue-900:hover {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:hover\:via-indigo-100:hover {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:hover\:via-indigo-200:hover {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:hover\:via-indigo-300:hover {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:hover\:via-indigo-400:hover {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:hover\:via-indigo-500:hover {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:hover\:via-indigo-600:hover {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:hover\:via-indigo-700:hover {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:hover\:via-indigo-800:hover {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:hover\:via-indigo-900:hover {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:hover\:via-purple-100:hover {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:hover\:via-purple-200:hover {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:hover\:via-purple-300:hover {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:hover\:via-purple-400:hover {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:hover\:via-purple-500:hover {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:hover\:via-purple-600:hover {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:hover\:via-purple-700:hover {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:hover\:via-purple-800:hover {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:hover\:via-purple-900:hover {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:hover\:via-pink-100:hover {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:hover\:via-pink-200:hover {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:hover\:via-pink-300:hover {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:hover\:via-pink-400:hover {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:hover\:via-pink-500:hover {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:hover\:via-pink-600:hover {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:hover\:via-pink-700:hover {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:hover\:via-pink-800:hover {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:hover\:via-pink-900:hover {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:hover\:to-transparent:hover {
+    --gradient-to-color: transparent;
+  }
+
+  .xl\:hover\:to-current:hover {
+    --gradient-to-color: currentColor;
+  }
+
+  .xl\:hover\:to-black:hover {
+    --gradient-to-color: #000;
+  }
+
+  .xl\:hover\:to-white:hover {
+    --gradient-to-color: #fff;
+  }
+
+  .xl\:hover\:to-gray-100:hover {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .xl\:hover\:to-gray-200:hover {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .xl\:hover\:to-gray-300:hover {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .xl\:hover\:to-gray-400:hover {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .xl\:hover\:to-gray-500:hover {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .xl\:hover\:to-gray-600:hover {
+    --gradient-to-color: #718096;
+  }
+
+  .xl\:hover\:to-gray-700:hover {
+    --gradient-to-color: #4a5568;
+  }
+
+  .xl\:hover\:to-gray-800:hover {
+    --gradient-to-color: #2d3748;
+  }
+
+  .xl\:hover\:to-gray-900:hover {
+    --gradient-to-color: #1a202c;
+  }
+
+  .xl\:hover\:to-red-100:hover {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .xl\:hover\:to-red-200:hover {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .xl\:hover\:to-red-300:hover {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .xl\:hover\:to-red-400:hover {
+    --gradient-to-color: #fc8181;
+  }
+
+  .xl\:hover\:to-red-500:hover {
+    --gradient-to-color: #f56565;
+  }
+
+  .xl\:hover\:to-red-600:hover {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .xl\:hover\:to-red-700:hover {
+    --gradient-to-color: #c53030;
+  }
+
+  .xl\:hover\:to-red-800:hover {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .xl\:hover\:to-red-900:hover {
+    --gradient-to-color: #742a2a;
+  }
+
+  .xl\:hover\:to-orange-100:hover {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .xl\:hover\:to-orange-200:hover {
+    --gradient-to-color: #feebc8;
+  }
+
+  .xl\:hover\:to-orange-300:hover {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .xl\:hover\:to-orange-400:hover {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .xl\:hover\:to-orange-500:hover {
+    --gradient-to-color: #ed8936;
+  }
+
+  .xl\:hover\:to-orange-600:hover {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .xl\:hover\:to-orange-700:hover {
+    --gradient-to-color: #c05621;
+  }
+
+  .xl\:hover\:to-orange-800:hover {
+    --gradient-to-color: #9c4221;
+  }
+
+  .xl\:hover\:to-orange-900:hover {
+    --gradient-to-color: #7b341e;
+  }
+
+  .xl\:hover\:to-yellow-100:hover {
+    --gradient-to-color: #fffff0;
+  }
+
+  .xl\:hover\:to-yellow-200:hover {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .xl\:hover\:to-yellow-300:hover {
+    --gradient-to-color: #faf089;
+  }
+
+  .xl\:hover\:to-yellow-400:hover {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .xl\:hover\:to-yellow-500:hover {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .xl\:hover\:to-yellow-600:hover {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .xl\:hover\:to-yellow-700:hover {
+    --gradient-to-color: #b7791f;
+  }
+
+  .xl\:hover\:to-yellow-800:hover {
+    --gradient-to-color: #975a16;
+  }
+
+  .xl\:hover\:to-yellow-900:hover {
+    --gradient-to-color: #744210;
+  }
+
+  .xl\:hover\:to-green-100:hover {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .xl\:hover\:to-green-200:hover {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .xl\:hover\:to-green-300:hover {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .xl\:hover\:to-green-400:hover {
+    --gradient-to-color: #68d391;
+  }
+
+  .xl\:hover\:to-green-500:hover {
+    --gradient-to-color: #48bb78;
+  }
+
+  .xl\:hover\:to-green-600:hover {
+    --gradient-to-color: #38a169;
+  }
+
+  .xl\:hover\:to-green-700:hover {
+    --gradient-to-color: #2f855a;
+  }
+
+  .xl\:hover\:to-green-800:hover {
+    --gradient-to-color: #276749;
+  }
+
+  .xl\:hover\:to-green-900:hover {
+    --gradient-to-color: #22543d;
+  }
+
+  .xl\:hover\:to-teal-100:hover {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .xl\:hover\:to-teal-200:hover {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .xl\:hover\:to-teal-300:hover {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .xl\:hover\:to-teal-400:hover {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .xl\:hover\:to-teal-500:hover {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .xl\:hover\:to-teal-600:hover {
+    --gradient-to-color: #319795;
+  }
+
+  .xl\:hover\:to-teal-700:hover {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .xl\:hover\:to-teal-800:hover {
+    --gradient-to-color: #285e61;
+  }
+
+  .xl\:hover\:to-teal-900:hover {
+    --gradient-to-color: #234e52;
+  }
+
+  .xl\:hover\:to-blue-100:hover {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .xl\:hover\:to-blue-200:hover {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .xl\:hover\:to-blue-300:hover {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .xl\:hover\:to-blue-400:hover {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .xl\:hover\:to-blue-500:hover {
+    --gradient-to-color: #4299e1;
+  }
+
+  .xl\:hover\:to-blue-600:hover {
+    --gradient-to-color: #3182ce;
+  }
+
+  .xl\:hover\:to-blue-700:hover {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .xl\:hover\:to-blue-800:hover {
+    --gradient-to-color: #2c5282;
+  }
+
+  .xl\:hover\:to-blue-900:hover {
+    --gradient-to-color: #2a4365;
+  }
+
+  .xl\:hover\:to-indigo-100:hover {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .xl\:hover\:to-indigo-200:hover {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .xl\:hover\:to-indigo-300:hover {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .xl\:hover\:to-indigo-400:hover {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .xl\:hover\:to-indigo-500:hover {
+    --gradient-to-color: #667eea;
+  }
+
+  .xl\:hover\:to-indigo-600:hover {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .xl\:hover\:to-indigo-700:hover {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .xl\:hover\:to-indigo-800:hover {
+    --gradient-to-color: #434190;
+  }
+
+  .xl\:hover\:to-indigo-900:hover {
+    --gradient-to-color: #3c366b;
+  }
+
+  .xl\:hover\:to-purple-100:hover {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .xl\:hover\:to-purple-200:hover {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .xl\:hover\:to-purple-300:hover {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .xl\:hover\:to-purple-400:hover {
+    --gradient-to-color: #b794f4;
+  }
+
+  .xl\:hover\:to-purple-500:hover {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .xl\:hover\:to-purple-600:hover {
+    --gradient-to-color: #805ad5;
+  }
+
+  .xl\:hover\:to-purple-700:hover {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .xl\:hover\:to-purple-800:hover {
+    --gradient-to-color: #553c9a;
+  }
+
+  .xl\:hover\:to-purple-900:hover {
+    --gradient-to-color: #44337a;
+  }
+
+  .xl\:hover\:to-pink-100:hover {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .xl\:hover\:to-pink-200:hover {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .xl\:hover\:to-pink-300:hover {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .xl\:hover\:to-pink-400:hover {
+    --gradient-to-color: #f687b3;
+  }
+
+  .xl\:hover\:to-pink-500:hover {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .xl\:hover\:to-pink-600:hover {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .xl\:hover\:to-pink-700:hover {
+    --gradient-to-color: #b83280;
+  }
+
+  .xl\:hover\:to-pink-800:hover {
+    --gradient-to-color: #97266d;
+  }
+
+  .xl\:hover\:to-pink-900:hover {
+    --gradient-to-color: #702459;
+  }
+
+  .xl\:focus\:from-transparent:focus {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:focus\:from-current:focus {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:focus\:from-black:focus {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:focus\:from-white:focus {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:focus\:from-gray-100:focus {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:focus\:from-gray-200:focus {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:focus\:from-gray-300:focus {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:focus\:from-gray-400:focus {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:focus\:from-gray-500:focus {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:focus\:from-gray-600:focus {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:focus\:from-gray-700:focus {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:focus\:from-gray-800:focus {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:focus\:from-gray-900:focus {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:focus\:from-red-100:focus {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:focus\:from-red-200:focus {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:focus\:from-red-300:focus {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:focus\:from-red-400:focus {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:focus\:from-red-500:focus {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:focus\:from-red-600:focus {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:focus\:from-red-700:focus {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:focus\:from-red-800:focus {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:focus\:from-red-900:focus {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:focus\:from-orange-100:focus {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:focus\:from-orange-200:focus {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:focus\:from-orange-300:focus {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:focus\:from-orange-400:focus {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:focus\:from-orange-500:focus {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:focus\:from-orange-600:focus {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:focus\:from-orange-700:focus {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:focus\:from-orange-800:focus {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:focus\:from-orange-900:focus {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:focus\:from-yellow-100:focus {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:focus\:from-yellow-200:focus {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:focus\:from-yellow-300:focus {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:focus\:from-yellow-400:focus {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:focus\:from-yellow-500:focus {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:focus\:from-yellow-600:focus {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:focus\:from-yellow-700:focus {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:focus\:from-yellow-800:focus {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:focus\:from-yellow-900:focus {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:focus\:from-green-100:focus {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:focus\:from-green-200:focus {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:focus\:from-green-300:focus {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:focus\:from-green-400:focus {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:focus\:from-green-500:focus {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:focus\:from-green-600:focus {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:focus\:from-green-700:focus {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:focus\:from-green-800:focus {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:focus\:from-green-900:focus {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:focus\:from-teal-100:focus {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:focus\:from-teal-200:focus {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:focus\:from-teal-300:focus {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:focus\:from-teal-400:focus {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:focus\:from-teal-500:focus {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:focus\:from-teal-600:focus {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:focus\:from-teal-700:focus {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:focus\:from-teal-800:focus {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:focus\:from-teal-900:focus {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:focus\:from-blue-100:focus {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:focus\:from-blue-200:focus {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:focus\:from-blue-300:focus {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:focus\:from-blue-400:focus {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:focus\:from-blue-500:focus {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:focus\:from-blue-600:focus {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:focus\:from-blue-700:focus {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:focus\:from-blue-800:focus {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:focus\:from-blue-900:focus {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:focus\:from-indigo-100:focus {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:focus\:from-indigo-200:focus {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:focus\:from-indigo-300:focus {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:focus\:from-indigo-400:focus {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:focus\:from-indigo-500:focus {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:focus\:from-indigo-600:focus {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:focus\:from-indigo-700:focus {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:focus\:from-indigo-800:focus {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:focus\:from-indigo-900:focus {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:focus\:from-purple-100:focus {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:focus\:from-purple-200:focus {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:focus\:from-purple-300:focus {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:focus\:from-purple-400:focus {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:focus\:from-purple-500:focus {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:focus\:from-purple-600:focus {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:focus\:from-purple-700:focus {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:focus\:from-purple-800:focus {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:focus\:from-purple-900:focus {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:focus\:from-pink-100:focus {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:focus\:from-pink-200:focus {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:focus\:from-pink-300:focus {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:focus\:from-pink-400:focus {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:focus\:from-pink-500:focus {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:focus\:from-pink-600:focus {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:focus\:from-pink-700:focus {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:focus\:from-pink-800:focus {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:focus\:from-pink-900:focus {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:focus\:via-transparent:focus {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:focus\:via-current:focus {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:focus\:via-black:focus {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:focus\:via-white:focus {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:focus\:via-gray-100:focus {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:focus\:via-gray-200:focus {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:focus\:via-gray-300:focus {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:focus\:via-gray-400:focus {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:focus\:via-gray-500:focus {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:focus\:via-gray-600:focus {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:focus\:via-gray-700:focus {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:focus\:via-gray-800:focus {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:focus\:via-gray-900:focus {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:focus\:via-red-100:focus {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:focus\:via-red-200:focus {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:focus\:via-red-300:focus {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:focus\:via-red-400:focus {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:focus\:via-red-500:focus {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:focus\:via-red-600:focus {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:focus\:via-red-700:focus {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:focus\:via-red-800:focus {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:focus\:via-red-900:focus {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:focus\:via-orange-100:focus {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:focus\:via-orange-200:focus {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:focus\:via-orange-300:focus {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:focus\:via-orange-400:focus {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:focus\:via-orange-500:focus {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:focus\:via-orange-600:focus {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:focus\:via-orange-700:focus {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:focus\:via-orange-800:focus {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:focus\:via-orange-900:focus {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:focus\:via-yellow-100:focus {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:focus\:via-yellow-200:focus {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:focus\:via-yellow-300:focus {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:focus\:via-yellow-400:focus {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:focus\:via-yellow-500:focus {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:focus\:via-yellow-600:focus {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:focus\:via-yellow-700:focus {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:focus\:via-yellow-800:focus {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:focus\:via-yellow-900:focus {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:focus\:via-green-100:focus {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:focus\:via-green-200:focus {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:focus\:via-green-300:focus {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:focus\:via-green-400:focus {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:focus\:via-green-500:focus {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:focus\:via-green-600:focus {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:focus\:via-green-700:focus {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:focus\:via-green-800:focus {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:focus\:via-green-900:focus {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:focus\:via-teal-100:focus {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:focus\:via-teal-200:focus {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:focus\:via-teal-300:focus {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:focus\:via-teal-400:focus {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:focus\:via-teal-500:focus {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:focus\:via-teal-600:focus {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:focus\:via-teal-700:focus {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:focus\:via-teal-800:focus {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:focus\:via-teal-900:focus {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:focus\:via-blue-100:focus {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:focus\:via-blue-200:focus {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:focus\:via-blue-300:focus {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:focus\:via-blue-400:focus {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:focus\:via-blue-500:focus {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:focus\:via-blue-600:focus {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:focus\:via-blue-700:focus {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:focus\:via-blue-800:focus {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:focus\:via-blue-900:focus {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:focus\:via-indigo-100:focus {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:focus\:via-indigo-200:focus {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:focus\:via-indigo-300:focus {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:focus\:via-indigo-400:focus {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:focus\:via-indigo-500:focus {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:focus\:via-indigo-600:focus {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:focus\:via-indigo-700:focus {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:focus\:via-indigo-800:focus {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:focus\:via-indigo-900:focus {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:focus\:via-purple-100:focus {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:focus\:via-purple-200:focus {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:focus\:via-purple-300:focus {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:focus\:via-purple-400:focus {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:focus\:via-purple-500:focus {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:focus\:via-purple-600:focus {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:focus\:via-purple-700:focus {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:focus\:via-purple-800:focus {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:focus\:via-purple-900:focus {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:focus\:via-pink-100:focus {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:focus\:via-pink-200:focus {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:focus\:via-pink-300:focus {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:focus\:via-pink-400:focus {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:focus\:via-pink-500:focus {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:focus\:via-pink-600:focus {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:focus\:via-pink-700:focus {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:focus\:via-pink-800:focus {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:focus\:via-pink-900:focus {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:focus\:to-transparent:focus {
+    --gradient-to-color: transparent;
+  }
+
+  .xl\:focus\:to-current:focus {
+    --gradient-to-color: currentColor;
+  }
+
+  .xl\:focus\:to-black:focus {
+    --gradient-to-color: #000;
+  }
+
+  .xl\:focus\:to-white:focus {
+    --gradient-to-color: #fff;
+  }
+
+  .xl\:focus\:to-gray-100:focus {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .xl\:focus\:to-gray-200:focus {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .xl\:focus\:to-gray-300:focus {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .xl\:focus\:to-gray-400:focus {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .xl\:focus\:to-gray-500:focus {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .xl\:focus\:to-gray-600:focus {
+    --gradient-to-color: #718096;
+  }
+
+  .xl\:focus\:to-gray-700:focus {
+    --gradient-to-color: #4a5568;
+  }
+
+  .xl\:focus\:to-gray-800:focus {
+    --gradient-to-color: #2d3748;
+  }
+
+  .xl\:focus\:to-gray-900:focus {
+    --gradient-to-color: #1a202c;
+  }
+
+  .xl\:focus\:to-red-100:focus {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .xl\:focus\:to-red-200:focus {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .xl\:focus\:to-red-300:focus {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .xl\:focus\:to-red-400:focus {
+    --gradient-to-color: #fc8181;
+  }
+
+  .xl\:focus\:to-red-500:focus {
+    --gradient-to-color: #f56565;
+  }
+
+  .xl\:focus\:to-red-600:focus {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .xl\:focus\:to-red-700:focus {
+    --gradient-to-color: #c53030;
+  }
+
+  .xl\:focus\:to-red-800:focus {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .xl\:focus\:to-red-900:focus {
+    --gradient-to-color: #742a2a;
+  }
+
+  .xl\:focus\:to-orange-100:focus {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .xl\:focus\:to-orange-200:focus {
+    --gradient-to-color: #feebc8;
+  }
+
+  .xl\:focus\:to-orange-300:focus {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .xl\:focus\:to-orange-400:focus {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .xl\:focus\:to-orange-500:focus {
+    --gradient-to-color: #ed8936;
+  }
+
+  .xl\:focus\:to-orange-600:focus {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .xl\:focus\:to-orange-700:focus {
+    --gradient-to-color: #c05621;
+  }
+
+  .xl\:focus\:to-orange-800:focus {
+    --gradient-to-color: #9c4221;
+  }
+
+  .xl\:focus\:to-orange-900:focus {
+    --gradient-to-color: #7b341e;
+  }
+
+  .xl\:focus\:to-yellow-100:focus {
+    --gradient-to-color: #fffff0;
+  }
+
+  .xl\:focus\:to-yellow-200:focus {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .xl\:focus\:to-yellow-300:focus {
+    --gradient-to-color: #faf089;
+  }
+
+  .xl\:focus\:to-yellow-400:focus {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .xl\:focus\:to-yellow-500:focus {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .xl\:focus\:to-yellow-600:focus {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .xl\:focus\:to-yellow-700:focus {
+    --gradient-to-color: #b7791f;
+  }
+
+  .xl\:focus\:to-yellow-800:focus {
+    --gradient-to-color: #975a16;
+  }
+
+  .xl\:focus\:to-yellow-900:focus {
+    --gradient-to-color: #744210;
+  }
+
+  .xl\:focus\:to-green-100:focus {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .xl\:focus\:to-green-200:focus {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .xl\:focus\:to-green-300:focus {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .xl\:focus\:to-green-400:focus {
+    --gradient-to-color: #68d391;
+  }
+
+  .xl\:focus\:to-green-500:focus {
+    --gradient-to-color: #48bb78;
+  }
+
+  .xl\:focus\:to-green-600:focus {
+    --gradient-to-color: #38a169;
+  }
+
+  .xl\:focus\:to-green-700:focus {
+    --gradient-to-color: #2f855a;
+  }
+
+  .xl\:focus\:to-green-800:focus {
+    --gradient-to-color: #276749;
+  }
+
+  .xl\:focus\:to-green-900:focus {
+    --gradient-to-color: #22543d;
+  }
+
+  .xl\:focus\:to-teal-100:focus {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .xl\:focus\:to-teal-200:focus {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .xl\:focus\:to-teal-300:focus {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .xl\:focus\:to-teal-400:focus {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .xl\:focus\:to-teal-500:focus {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .xl\:focus\:to-teal-600:focus {
+    --gradient-to-color: #319795;
+  }
+
+  .xl\:focus\:to-teal-700:focus {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .xl\:focus\:to-teal-800:focus {
+    --gradient-to-color: #285e61;
+  }
+
+  .xl\:focus\:to-teal-900:focus {
+    --gradient-to-color: #234e52;
+  }
+
+  .xl\:focus\:to-blue-100:focus {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .xl\:focus\:to-blue-200:focus {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .xl\:focus\:to-blue-300:focus {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .xl\:focus\:to-blue-400:focus {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .xl\:focus\:to-blue-500:focus {
+    --gradient-to-color: #4299e1;
+  }
+
+  .xl\:focus\:to-blue-600:focus {
+    --gradient-to-color: #3182ce;
+  }
+
+  .xl\:focus\:to-blue-700:focus {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .xl\:focus\:to-blue-800:focus {
+    --gradient-to-color: #2c5282;
+  }
+
+  .xl\:focus\:to-blue-900:focus {
+    --gradient-to-color: #2a4365;
+  }
+
+  .xl\:focus\:to-indigo-100:focus {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .xl\:focus\:to-indigo-200:focus {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .xl\:focus\:to-indigo-300:focus {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .xl\:focus\:to-indigo-400:focus {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .xl\:focus\:to-indigo-500:focus {
+    --gradient-to-color: #667eea;
+  }
+
+  .xl\:focus\:to-indigo-600:focus {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .xl\:focus\:to-indigo-700:focus {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .xl\:focus\:to-indigo-800:focus {
+    --gradient-to-color: #434190;
+  }
+
+  .xl\:focus\:to-indigo-900:focus {
+    --gradient-to-color: #3c366b;
+  }
+
+  .xl\:focus\:to-purple-100:focus {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .xl\:focus\:to-purple-200:focus {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .xl\:focus\:to-purple-300:focus {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .xl\:focus\:to-purple-400:focus {
+    --gradient-to-color: #b794f4;
+  }
+
+  .xl\:focus\:to-purple-500:focus {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .xl\:focus\:to-purple-600:focus {
+    --gradient-to-color: #805ad5;
+  }
+
+  .xl\:focus\:to-purple-700:focus {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .xl\:focus\:to-purple-800:focus {
+    --gradient-to-color: #553c9a;
+  }
+
+  .xl\:focus\:to-purple-900:focus {
+    --gradient-to-color: #44337a;
+  }
+
+  .xl\:focus\:to-pink-100:focus {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .xl\:focus\:to-pink-200:focus {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .xl\:focus\:to-pink-300:focus {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .xl\:focus\:to-pink-400:focus {
+    --gradient-to-color: #f687b3;
+  }
+
+  .xl\:focus\:to-pink-500:focus {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .xl\:focus\:to-pink-600:focus {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .xl\:focus\:to-pink-700:focus {
+    --gradient-to-color: #b83280;
+  }
+
+  .xl\:focus\:to-pink-800:focus {
+    --gradient-to-color: #97266d;
+  }
+
+  .xl\:focus\:to-pink-900:focus {
+    --gradient-to-color: #702459;
+  }
+
+  .xl\:bg-opacity-0 {
+    --bg-opacity: 0;
+  }
+
+  .xl\:bg-opacity-25 {
+    --bg-opacity: 0.25;
+  }
+
+  .xl\:bg-opacity-50 {
+    --bg-opacity: 0.5;
+  }
+
+  .xl\:bg-opacity-75 {
+    --bg-opacity: 0.75;
+  }
+
+  .xl\:bg-opacity-100 {
+    --bg-opacity: 1;
+  }
+
+  .xl\:hover\:bg-opacity-0:hover {
+    --bg-opacity: 0;
+  }
+
+  .xl\:hover\:bg-opacity-25:hover {
+    --bg-opacity: 0.25;
+  }
+
+  .xl\:hover\:bg-opacity-50:hover {
+    --bg-opacity: 0.5;
+  }
+
+  .xl\:hover\:bg-opacity-75:hover {
+    --bg-opacity: 0.75;
+  }
+
+  .xl\:hover\:bg-opacity-100:hover {
+    --bg-opacity: 1;
+  }
+
+  .xl\:focus\:bg-opacity-0:focus {
+    --bg-opacity: 0;
+  }
+
+  .xl\:focus\:bg-opacity-25:focus {
+    --bg-opacity: 0.25;
+  }
+
+  .xl\:focus\:bg-opacity-50:focus {
+    --bg-opacity: 0.5;
+  }
+
+  .xl\:focus\:bg-opacity-75:focus {
+    --bg-opacity: 0.75;
+  }
+
+  .xl\:focus\:bg-opacity-100:focus {
+    --bg-opacity: 1;
+  }
+
+  .xl\:bg-bottom {
+    background-position: bottom;
+  }
+
+  .xl\:bg-center {
+    background-position: center;
+  }
+
+  .xl\:bg-left {
+    background-position: left;
+  }
+
+  .xl\:bg-left-bottom {
+    background-position: left bottom;
+  }
+
+  .xl\:bg-left-top {
+    background-position: left top;
+  }
+
+  .xl\:bg-right {
+    background-position: right;
+  }
+
+  .xl\:bg-right-bottom {
+    background-position: right bottom;
+  }
+
+  .xl\:bg-right-top {
+    background-position: right top;
+  }
+
+  .xl\:bg-top {
+    background-position: top;
+  }
+
+  .xl\:bg-repeat {
+    background-repeat: repeat;
+  }
+
+  .xl\:bg-no-repeat {
+    background-repeat: no-repeat;
+  }
+
+  .xl\:bg-repeat-x {
+    background-repeat: repeat-x;
+  }
+
+  .xl\:bg-repeat-y {
+    background-repeat: repeat-y;
+  }
+
+  .xl\:bg-repeat-round {
+    background-repeat: round;
+  }
+
+  .xl\:bg-repeat-space {
+    background-repeat: space;
+  }
+
+  .xl\:bg-auto {
+    background-size: auto;
+  }
+
+  .xl\:bg-cover {
+    background-size: cover;
+  }
+
+  .xl\:bg-contain {
+    background-size: contain;
+  }
+
+  .xl\:border-collapse {
+    border-collapse: collapse;
+  }
+
+  .xl\:border-separate {
+    border-collapse: separate;
+  }
+
+  .xl\:border-transparent {
+    border-color: transparent;
+  }
+
+  .xl\:border-current {
+    border-color: currentColor;
+  }
+
+  .xl\:border-black {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .xl\:border-white {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .xl\:border-gray-100 {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .xl\:border-gray-200 {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .xl\:border-gray-300 {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .xl\:border-gray-400 {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .xl\:border-gray-500 {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .xl\:border-gray-600 {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .xl\:border-gray-700 {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .xl\:border-gray-800 {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .xl\:border-gray-900 {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .xl\:border-red-100 {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .xl\:border-red-200 {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .xl\:border-red-300 {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .xl\:border-red-400 {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .xl\:border-red-500 {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .xl\:border-red-600 {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .xl\:border-red-700 {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .xl\:border-red-800 {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .xl\:border-red-900 {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .xl\:border-orange-100 {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .xl\:border-orange-200 {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .xl\:border-orange-300 {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .xl\:border-orange-400 {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .xl\:border-orange-500 {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .xl\:border-orange-600 {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .xl\:border-orange-700 {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .xl\:border-orange-800 {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .xl\:border-orange-900 {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-100 {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-200 {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-300 {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-400 {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-500 {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-600 {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-700 {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-800 {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-900 {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .xl\:border-green-100 {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .xl\:border-green-200 {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .xl\:border-green-300 {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .xl\:border-green-400 {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .xl\:border-green-500 {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .xl\:border-green-600 {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .xl\:border-green-700 {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .xl\:border-green-800 {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .xl\:border-green-900 {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .xl\:border-teal-100 {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .xl\:border-teal-200 {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .xl\:border-teal-300 {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .xl\:border-teal-400 {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .xl\:border-teal-500 {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .xl\:border-teal-600 {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .xl\:border-teal-700 {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .xl\:border-teal-800 {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .xl\:border-teal-900 {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .xl\:border-blue-100 {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .xl\:border-blue-200 {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .xl\:border-blue-300 {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .xl\:border-blue-400 {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .xl\:border-blue-500 {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .xl\:border-blue-600 {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .xl\:border-blue-700 {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .xl\:border-blue-800 {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .xl\:border-blue-900 {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-100 {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-200 {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-300 {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-400 {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-500 {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-600 {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-700 {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-800 {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-900 {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .xl\:border-purple-100 {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .xl\:border-purple-200 {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .xl\:border-purple-300 {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .xl\:border-purple-400 {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .xl\:border-purple-500 {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .xl\:border-purple-600 {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .xl\:border-purple-700 {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .xl\:border-purple-800 {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .xl\:border-purple-900 {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .xl\:border-pink-100 {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .xl\:border-pink-200 {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .xl\:border-pink-300 {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .xl\:border-pink-400 {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .xl\:border-pink-500 {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .xl\:border-pink-600 {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .xl\:border-pink-700 {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .xl\:border-pink-800 {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .xl\:border-pink-900 {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-transparent:hover {
+    border-color: transparent;
+  }
+
+  .xl\:hover\:border-current:hover {
+    border-color: currentColor;
+  }
+
+  .xl\:hover\:border-black:hover {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-white:hover {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-100:hover {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-200:hover {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-300:hover {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-400:hover {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-500:hover {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-600:hover {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-700:hover {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-800:hover {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-900:hover {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-300:hover {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-400:hover {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-500:hover {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-600:hover {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-700:hover {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-800:hover {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-900:hover {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-100:hover {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-200:hover {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-300:hover {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-400:hover {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-500:hover {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-600:hover {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-700:hover {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-800:hover {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-900:hover {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-100:hover {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-200:hover {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-300:hover {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-400:hover {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-500:hover {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-600:hover {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-700:hover {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-800:hover {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-900:hover {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-100:hover {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-200:hover {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-300:hover {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-400:hover {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-500:hover {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-600:hover {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-700:hover {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-800:hover {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-900:hover {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-100:hover {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-200:hover {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-300:hover {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-400:hover {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-500:hover {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-600:hover {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-700:hover {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-800:hover {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-900:hover {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-200:hover {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-300:hover {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-400:hover {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-500:hover {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-600:hover {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-700:hover {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-800:hover {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-900:hover {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-200:hover {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-300:hover {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-400:hover {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-500:hover {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-600:hover {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-700:hover {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-800:hover {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-900:hover {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-100:hover {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-200:hover {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-300:hover {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-400:hover {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-500:hover {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-600:hover {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-700:hover {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-800:hover {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-900:hover {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-300:hover {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-400:hover {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-500:hover {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-600:hover {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-700:hover {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-800:hover {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-900:hover {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-transparent:focus {
+    border-color: transparent;
+  }
+
+  .xl\:focus\:border-current:focus {
+    border-color: currentColor;
+  }
+
+  .xl\:focus\:border-black:focus {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-white:focus {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-100:focus {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-200:focus {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-300:focus {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-400:focus {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-500:focus {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-600:focus {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-700:focus {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-800:focus {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-900:focus {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-300:focus {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-400:focus {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-500:focus {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-600:focus {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-700:focus {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-800:focus {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-900:focus {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-100:focus {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-200:focus {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-300:focus {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-400:focus {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-500:focus {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-600:focus {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-700:focus {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-800:focus {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-900:focus {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-100:focus {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-200:focus {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-300:focus {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-400:focus {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-500:focus {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-600:focus {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-700:focus {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-800:focus {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-900:focus {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-100:focus {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-200:focus {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-300:focus {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-400:focus {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-500:focus {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-600:focus {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-700:focus {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-800:focus {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-900:focus {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-100:focus {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-200:focus {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-300:focus {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-400:focus {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-500:focus {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-600:focus {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-700:focus {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-800:focus {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-900:focus {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-200:focus {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-300:focus {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-400:focus {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-500:focus {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-600:focus {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-700:focus {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-800:focus {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-900:focus {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-200:focus {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-300:focus {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-400:focus {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-500:focus {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-600:focus {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-700:focus {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-800:focus {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-900:focus {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-100:focus {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-200:focus {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-300:focus {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-400:focus {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-500:focus {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-600:focus {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-700:focus {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-800:focus {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-900:focus {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-300:focus {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-400:focus {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-500:focus {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-600:focus {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-700:focus {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-800:focus {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-900:focus {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .xl\:border-opacity-0 {
+    --border-opacity: 0;
+  }
+
+  .xl\:border-opacity-25 {
+    --border-opacity: 0.25;
+  }
+
+  .xl\:border-opacity-50 {
+    --border-opacity: 0.5;
+  }
+
+  .xl\:border-opacity-75 {
+    --border-opacity: 0.75;
+  }
+
+  .xl\:border-opacity-100 {
+    --border-opacity: 1;
+  }
+
+  .xl\:hover\:border-opacity-0:hover {
+    --border-opacity: 0;
+  }
+
+  .xl\:hover\:border-opacity-25:hover {
+    --border-opacity: 0.25;
+  }
+
+  .xl\:hover\:border-opacity-50:hover {
+    --border-opacity: 0.5;
+  }
+
+  .xl\:hover\:border-opacity-75:hover {
+    --border-opacity: 0.75;
+  }
+
+  .xl\:hover\:border-opacity-100:hover {
+    --border-opacity: 1;
+  }
+
+  .xl\:focus\:border-opacity-0:focus {
+    --border-opacity: 0;
+  }
+
+  .xl\:focus\:border-opacity-25:focus {
+    --border-opacity: 0.25;
+  }
+
+  .xl\:focus\:border-opacity-50:focus {
+    --border-opacity: 0.5;
+  }
+
+  .xl\:focus\:border-opacity-75:focus {
+    --border-opacity: 0.75;
+  }
+
+  .xl\:focus\:border-opacity-100:focus {
+    --border-opacity: 1;
+  }
+
+  .xl\:rounded-none {
+    border-radius: 0;
+  }
+
+  .xl\:rounded-sm {
+    border-radius: 0.125rem;
+  }
+
+  .xl\:rounded {
+    border-radius: 0.25rem;
+  }
+
+  .xl\:rounded-md {
+    border-radius: 0.375rem;
+  }
+
+  .xl\:rounded-lg {
+    border-radius: 0.5rem;
+  }
+
+  .xl\:rounded-full {
+    border-radius: 9999px;
+  }
+
+  .xl\:rounded-t-none {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+  }
+
+  .xl\:rounded-r-none {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+  }
+
+  .xl\:rounded-b-none {
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .xl\:rounded-l-none {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .xl\:rounded-t-sm {
+    border-top-left-radius: 0.125rem;
+    border-top-right-radius: 0.125rem;
+  }
+
+  .xl\:rounded-r-sm {
+    border-top-right-radius: 0.125rem;
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .xl\:rounded-b-sm {
+    border-bottom-right-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .xl\:rounded-l-sm {
+    border-top-left-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .xl\:rounded-t {
+    border-top-left-radius: 0.25rem;
+    border-top-right-radius: 0.25rem;
+  }
+
+  .xl\:rounded-r {
+    border-top-right-radius: 0.25rem;
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .xl\:rounded-b {
+    border-bottom-right-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .xl\:rounded-l {
+    border-top-left-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .xl\:rounded-t-md {
+    border-top-left-radius: 0.375rem;
+    border-top-right-radius: 0.375rem;
+  }
+
+  .xl\:rounded-r-md {
+    border-top-right-radius: 0.375rem;
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .xl\:rounded-b-md {
+    border-bottom-right-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .xl\:rounded-l-md {
+    border-top-left-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .xl\:rounded-t-lg {
+    border-top-left-radius: 0.5rem;
+    border-top-right-radius: 0.5rem;
+  }
+
+  .xl\:rounded-r-lg {
+    border-top-right-radius: 0.5rem;
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .xl\:rounded-b-lg {
+    border-bottom-right-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .xl\:rounded-l-lg {
+    border-top-left-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .xl\:rounded-t-full {
+    border-top-left-radius: 9999px;
+    border-top-right-radius: 9999px;
+  }
+
+  .xl\:rounded-r-full {
+    border-top-right-radius: 9999px;
+    border-bottom-right-radius: 9999px;
+  }
+
+  .xl\:rounded-b-full {
+    border-bottom-right-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .xl\:rounded-l-full {
+    border-top-left-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .xl\:rounded-tl-none {
+    border-top-left-radius: 0;
+  }
+
+  .xl\:rounded-tr-none {
+    border-top-right-radius: 0;
+  }
+
+  .xl\:rounded-br-none {
+    border-bottom-right-radius: 0;
+  }
+
+  .xl\:rounded-bl-none {
+    border-bottom-left-radius: 0;
+  }
+
+  .xl\:rounded-tl-sm {
+    border-top-left-radius: 0.125rem;
+  }
+
+  .xl\:rounded-tr-sm {
+    border-top-right-radius: 0.125rem;
+  }
+
+  .xl\:rounded-br-sm {
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .xl\:rounded-bl-sm {
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .xl\:rounded-tl {
+    border-top-left-radius: 0.25rem;
+  }
+
+  .xl\:rounded-tr {
+    border-top-right-radius: 0.25rem;
+  }
+
+  .xl\:rounded-br {
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .xl\:rounded-bl {
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .xl\:rounded-tl-md {
+    border-top-left-radius: 0.375rem;
+  }
+
+  .xl\:rounded-tr-md {
+    border-top-right-radius: 0.375rem;
+  }
+
+  .xl\:rounded-br-md {
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .xl\:rounded-bl-md {
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .xl\:rounded-tl-lg {
+    border-top-left-radius: 0.5rem;
+  }
+
+  .xl\:rounded-tr-lg {
+    border-top-right-radius: 0.5rem;
+  }
+
+  .xl\:rounded-br-lg {
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .xl\:rounded-bl-lg {
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .xl\:rounded-tl-full {
+    border-top-left-radius: 9999px;
+  }
+
+  .xl\:rounded-tr-full {
+    border-top-right-radius: 9999px;
+  }
+
+  .xl\:rounded-br-full {
+    border-bottom-right-radius: 9999px;
+  }
+
+  .xl\:rounded-bl-full {
+    border-bottom-left-radius: 9999px;
+  }
+
+  .xl\:border-solid {
+    border-style: solid;
+  }
+
+  .xl\:border-dashed {
+    border-style: dashed;
+  }
+
+  .xl\:border-dotted {
+    border-style: dotted;
+  }
+
+  .xl\:border-double {
+    border-style: double;
+  }
+
+  .xl\:border-none {
+    border-style: none;
+  }
+
+  .xl\:border-0 {
+    border-width: 0;
+  }
+
+  .xl\:border-2 {
+    border-width: 2px;
+  }
+
+  .xl\:border-4 {
+    border-width: 4px;
+  }
+
+  .xl\:border-8 {
+    border-width: 8px;
+  }
+
+  .xl\:border {
+    border-width: 1px;
+  }
+
+  .xl\:border-t-0 {
+    border-top-width: 0;
+  }
+
+  .xl\:border-r-0 {
+    border-right-width: 0;
+  }
+
+  .xl\:border-b-0 {
+    border-bottom-width: 0;
+  }
+
+  .xl\:border-l-0 {
+    border-left-width: 0;
+  }
+
+  .xl\:border-t-2 {
+    border-top-width: 2px;
+  }
+
+  .xl\:border-r-2 {
+    border-right-width: 2px;
+  }
+
+  .xl\:border-b-2 {
+    border-bottom-width: 2px;
+  }
+
+  .xl\:border-l-2 {
+    border-left-width: 2px;
+  }
+
+  .xl\:border-t-4 {
+    border-top-width: 4px;
+  }
+
+  .xl\:border-r-4 {
+    border-right-width: 4px;
+  }
+
+  .xl\:border-b-4 {
+    border-bottom-width: 4px;
+  }
+
+  .xl\:border-l-4 {
+    border-left-width: 4px;
+  }
+
+  .xl\:border-t-8 {
+    border-top-width: 8px;
+  }
+
+  .xl\:border-r-8 {
+    border-right-width: 8px;
+  }
+
+  .xl\:border-b-8 {
+    border-bottom-width: 8px;
+  }
+
+  .xl\:border-l-8 {
+    border-left-width: 8px;
+  }
+
+  .xl\:border-t {
+    border-top-width: 1px;
+  }
+
+  .xl\:border-r {
+    border-right-width: 1px;
+  }
+
+  .xl\:border-b {
+    border-bottom-width: 1px;
+  }
+
+  .xl\:border-l {
+    border-left-width: 1px;
+  }
+
+  .xl\:box-border {
+    box-sizing: border-box;
+  }
+
+  .xl\:box-content {
+    box-sizing: content-box;
+  }
+
+  .xl\:cursor-auto {
+    cursor: auto;
+  }
+
+  .xl\:cursor-default {
+    cursor: default;
+  }
+
+  .xl\:cursor-pointer {
+    cursor: pointer;
+  }
+
+  .xl\:cursor-wait {
+    cursor: wait;
+  }
+
+  .xl\:cursor-text {
+    cursor: text;
+  }
+
+  .xl\:cursor-move {
+    cursor: move;
+  }
+
+  .xl\:cursor-not-allowed {
+    cursor: not-allowed;
+  }
+
+  .xl\:block {
+    display: block;
+  }
+
+  .xl\:inline-block {
+    display: inline-block;
+  }
+
+  .xl\:inline {
+    display: inline;
+  }
+
+  .xl\:flex {
+    display: flex;
+  }
+
+  .xl\:inline-flex {
+    display: inline-flex;
+  }
+
+  .xl\:table {
+    display: table;
+  }
+
+  .xl\:table-caption {
+    display: table-caption;
+  }
+
+  .xl\:table-cell {
+    display: table-cell;
+  }
+
+  .xl\:table-column {
+    display: table-column;
+  }
+
+  .xl\:table-column-group {
+    display: table-column-group;
+  }
+
+  .xl\:table-footer-group {
+    display: table-footer-group;
+  }
+
+  .xl\:table-header-group {
+    display: table-header-group;
+  }
+
+  .xl\:table-row-group {
+    display: table-row-group;
+  }
+
+  .xl\:table-row {
+    display: table-row;
+  }
+
+  .xl\:flow-root {
+    display: flow-root;
+  }
+
+  .xl\:grid {
+    display: grid;
+  }
+
+  .xl\:inline-grid {
+    display: inline-grid;
+  }
+
+  .xl\:contents {
+    display: contents;
+  }
+
+  .xl\:hidden {
+    display: none;
+  }
+
+  .xl\:flex-row {
+    flex-direction: row;
+  }
+
+  .xl\:flex-row-reverse {
+    flex-direction: row-reverse;
+  }
+
+  .xl\:flex-col {
+    flex-direction: column;
+  }
+
+  .xl\:flex-col-reverse {
+    flex-direction: column-reverse;
+  }
+
+  .xl\:flex-wrap {
+    flex-wrap: wrap;
+  }
+
+  .xl\:flex-wrap-reverse {
+    flex-wrap: wrap-reverse;
+  }
+
+  .xl\:flex-no-wrap {
+    flex-wrap: nowrap;
+  }
+
+  .xl\:place-items-auto {
+    place-items: auto;
+  }
+
+  .xl\:place-items-start {
+    place-items: start;
+  }
+
+  .xl\:place-items-end {
+    place-items: end;
+  }
+
+  .xl\:place-items-center {
+    place-items: center;
+  }
+
+  .xl\:place-items-stretch {
+    place-items: stretch;
+  }
+
+  .xl\:place-content-center {
+    place-content: center;
+  }
+
+  .xl\:place-content-start {
+    place-content: start;
+  }
+
+  .xl\:place-content-end {
+    place-content: end;
+  }
+
+  .xl\:place-content-between {
+    place-content: space-between;
+  }
+
+  .xl\:place-content-around {
+    place-content: space-around;
+  }
+
+  .xl\:place-content-evenly {
+    place-content: space-evenly;
+  }
+
+  .xl\:place-content-stretch {
+    place-content: stretch;
+  }
+
+  .xl\:place-self-auto {
+    place-self: auto;
+  }
+
+  .xl\:place-self-start {
+    place-self: start;
+  }
+
+  .xl\:place-self-end {
+    place-self: end;
+  }
+
+  .xl\:place-self-center {
+    place-self: center;
+  }
+
+  .xl\:place-self-stretch {
+    place-self: stretch;
+  }
+
+  .xl\:items-start {
+    align-items: flex-start;
+  }
+
+  .xl\:items-end {
+    align-items: flex-end;
+  }
+
+  .xl\:items-center {
+    align-items: center;
+  }
+
+  .xl\:items-baseline {
+    align-items: baseline;
+  }
+
+  .xl\:items-stretch {
+    align-items: stretch;
+  }
+
+  .xl\:content-center {
+    align-content: center;
+  }
+
+  .xl\:content-start {
+    align-content: flex-start;
+  }
+
+  .xl\:content-end {
+    align-content: flex-end;
+  }
+
+  .xl\:content-between {
+    align-content: space-between;
+  }
+
+  .xl\:content-around {
+    align-content: space-around;
+  }
+
+  .xl\:content-evenly {
+    align-content: space-evenly;
+  }
+
+  .xl\:self-auto {
+    align-self: auto;
+  }
+
+  .xl\:self-start {
+    align-self: flex-start;
+  }
+
+  .xl\:self-end {
+    align-self: flex-end;
+  }
+
+  .xl\:self-center {
+    align-self: center;
+  }
+
+  .xl\:self-stretch {
+    align-self: stretch;
+  }
+
+  .xl\:justify-items-auto {
+    justify-items: auto;
+  }
+
+  .xl\:justify-items-start {
+    justify-items: start;
+  }
+
+  .xl\:justify-items-end {
+    justify-items: end;
+  }
+
+  .xl\:justify-items-center {
+    justify-items: center;
+  }
+
+  .xl\:justify-items-stretch {
+    justify-items: stretch;
+  }
+
+  .xl\:justify-start {
+    justify-content: flex-start;
+  }
+
+  .xl\:justify-end {
+    justify-content: flex-end;
+  }
+
+  .xl\:justify-center {
+    justify-content: center;
+  }
+
+  .xl\:justify-between {
+    justify-content: space-between;
+  }
+
+  .xl\:justify-around {
+    justify-content: space-around;
+  }
+
+  .xl\:justify-evenly {
+    justify-content: space-evenly;
+  }
+
+  .xl\:justify-self-auto {
+    justify-self: auto;
+  }
+
+  .xl\:justify-self-start {
+    justify-self: start;
+  }
+
+  .xl\:justify-self-end {
+    justify-self: end;
+  }
+
+  .xl\:justify-self-center {
+    justify-self: center;
+  }
+
+  .xl\:justify-self-stretch {
+    justify-self: stretch;
+  }
+
+  .xl\:flex-1 {
+    flex: 1 1 0%;
+  }
+
+  .xl\:flex-auto {
+    flex: 1 1 auto;
+  }
+
+  .xl\:flex-initial {
+    flex: 0 1 auto;
+  }
+
+  .xl\:flex-none {
+    flex: none;
+  }
+
+  .xl\:flex-grow-0 {
+    flex-grow: 0;
+  }
+
+  .xl\:flex-grow {
+    flex-grow: 1;
+  }
+
+  .xl\:flex-shrink-0 {
+    flex-shrink: 0;
+  }
+
+  .xl\:flex-shrink {
+    flex-shrink: 1;
+  }
+
+  .xl\:order-1 {
+    order: 1;
+  }
+
+  .xl\:order-2 {
+    order: 2;
+  }
+
+  .xl\:order-3 {
+    order: 3;
+  }
+
+  .xl\:order-4 {
+    order: 4;
+  }
+
+  .xl\:order-5 {
+    order: 5;
+  }
+
+  .xl\:order-6 {
+    order: 6;
+  }
+
+  .xl\:order-7 {
+    order: 7;
+  }
+
+  .xl\:order-8 {
+    order: 8;
+  }
+
+  .xl\:order-9 {
+    order: 9;
+  }
+
+  .xl\:order-10 {
+    order: 10;
+  }
+
+  .xl\:order-11 {
+    order: 11;
+  }
+
+  .xl\:order-12 {
+    order: 12;
+  }
+
+  .xl\:order-first {
+    order: -9999;
+  }
+
+  .xl\:order-last {
+    order: 9999;
+  }
+
+  .xl\:order-none {
+    order: 0;
+  }
+
+  .xl\:float-right {
+    float: right;
+  }
+
+  .xl\:float-left {
+    float: left;
+  }
+
+  .xl\:float-none {
+    float: none;
+  }
+
+  .xl\:clearfix:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+
+  .xl\:clear-left {
+    clear: left;
+  }
+
+  .xl\:clear-right {
+    clear: right;
+  }
+
+  .xl\:clear-both {
+    clear: both;
+  }
+
+  .xl\:clear-none {
+    clear: none;
+  }
+
+  .xl\:font-sans {
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  }
+
+  .xl\:font-serif {
+    font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+  }
+
+  .xl\:font-mono {
+    font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+  }
+
+  .xl\:font-hairline {
+    font-weight: 100;
+  }
+
+  .xl\:font-thin {
+    font-weight: 200;
+  }
+
+  .xl\:font-light {
+    font-weight: 300;
+  }
+
+  .xl\:font-normal {
+    font-weight: 400;
+  }
+
+  .xl\:font-medium {
+    font-weight: 500;
+  }
+
+  .xl\:font-semibold {
+    font-weight: 600;
+  }
+
+  .xl\:font-bold {
+    font-weight: 700;
+  }
+
+  .xl\:font-extrabold {
+    font-weight: 800;
+  }
+
+  .xl\:font-black {
+    font-weight: 900;
+  }
+
+  .xl\:hover\:font-hairline:hover {
+    font-weight: 100;
+  }
+
+  .xl\:hover\:font-thin:hover {
+    font-weight: 200;
+  }
+
+  .xl\:hover\:font-light:hover {
+    font-weight: 300;
+  }
+
+  .xl\:hover\:font-normal:hover {
+    font-weight: 400;
+  }
+
+  .xl\:hover\:font-medium:hover {
+    font-weight: 500;
+  }
+
+  .xl\:hover\:font-semibold:hover {
+    font-weight: 600;
+  }
+
+  .xl\:hover\:font-bold:hover {
+    font-weight: 700;
+  }
+
+  .xl\:hover\:font-extrabold:hover {
+    font-weight: 800;
+  }
+
+  .xl\:hover\:font-black:hover {
+    font-weight: 900;
+  }
+
+  .xl\:focus\:font-hairline:focus {
+    font-weight: 100;
+  }
+
+  .xl\:focus\:font-thin:focus {
+    font-weight: 200;
+  }
+
+  .xl\:focus\:font-light:focus {
+    font-weight: 300;
+  }
+
+  .xl\:focus\:font-normal:focus {
+    font-weight: 400;
+  }
+
+  .xl\:focus\:font-medium:focus {
+    font-weight: 500;
+  }
+
+  .xl\:focus\:font-semibold:focus {
+    font-weight: 600;
+  }
+
+  .xl\:focus\:font-bold:focus {
+    font-weight: 700;
+  }
+
+  .xl\:focus\:font-extrabold:focus {
+    font-weight: 800;
+  }
+
+  .xl\:focus\:font-black:focus {
+    font-weight: 900;
+  }
+
+  .xl\:h-0 {
+    height: 0;
+  }
+
+  .xl\:h-1 {
+    height: 0.25rem;
+  }
+
+  .xl\:h-2 {
+    height: 0.5rem;
+  }
+
+  .xl\:h-3 {
+    height: 0.75rem;
+  }
+
+  .xl\:h-4 {
+    height: 1rem;
+  }
+
+  .xl\:h-5 {
+    height: 1.25rem;
+  }
+
+  .xl\:h-6 {
+    height: 1.5rem;
+  }
+
+  .xl\:h-8 {
+    height: 2rem;
+  }
+
+  .xl\:h-10 {
+    height: 2.5rem;
+  }
+
+  .xl\:h-12 {
+    height: 3rem;
+  }
+
+  .xl\:h-16 {
+    height: 4rem;
+  }
+
+  .xl\:h-20 {
+    height: 5rem;
+  }
+
+  .xl\:h-24 {
+    height: 6rem;
+  }
+
+  .xl\:h-32 {
+    height: 8rem;
+  }
+
+  .xl\:h-40 {
+    height: 10rem;
+  }
+
+  .xl\:h-48 {
+    height: 12rem;
+  }
+
+  .xl\:h-56 {
+    height: 14rem;
+  }
+
+  .xl\:h-64 {
+    height: 16rem;
+  }
+
+  .xl\:h-auto {
+    height: auto;
+  }
+
+  .xl\:h-px {
+    height: 1px;
+  }
+
+  .xl\:h-full {
+    height: 100%;
+  }
+
+  .xl\:h-screen {
+    height: 100vh;
+  }
+
+  .xl\:text-xs {
+    font-size: 0.75rem;
+  }
+
+  .xl\:text-sm {
+    font-size: 0.875rem;
+  }
+
+  .xl\:text-base {
+    font-size: 1rem;
+  }
+
+  .xl\:text-lg {
+    font-size: 1.125rem;
+  }
+
+  .xl\:text-xl {
+    font-size: 1.25rem;
+  }
+
+  .xl\:text-2xl {
+    font-size: 1.5rem;
+  }
+
+  .xl\:text-3xl {
+    font-size: 1.875rem;
+  }
+
+  .xl\:text-4xl {
+    font-size: 2.25rem;
+  }
+
+  .xl\:text-5xl {
+    font-size: 3rem;
+  }
+
+  .xl\:text-6xl {
+    font-size: 4rem;
+  }
+
+  .xl\:leading-3 {
+    line-height: .75rem;
+  }
+
+  .xl\:leading-4 {
+    line-height: 1rem;
+  }
+
+  .xl\:leading-5 {
+    line-height: 1.25rem;
+  }
+
+  .xl\:leading-6 {
+    line-height: 1.5rem;
+  }
+
+  .xl\:leading-7 {
+    line-height: 1.75rem;
+  }
+
+  .xl\:leading-8 {
+    line-height: 2rem;
+  }
+
+  .xl\:leading-9 {
+    line-height: 2.25rem;
+  }
+
+  .xl\:leading-10 {
+    line-height: 2.5rem;
+  }
+
+  .xl\:leading-none {
+    line-height: 1;
+  }
+
+  .xl\:leading-tight {
+    line-height: 1.25;
+  }
+
+  .xl\:leading-snug {
+    line-height: 1.375;
+  }
+
+  .xl\:leading-normal {
+    line-height: 1.5;
+  }
+
+  .xl\:leading-relaxed {
+    line-height: 1.625;
+  }
+
+  .xl\:leading-loose {
+    line-height: 2;
+  }
+
+  .xl\:list-inside {
+    list-style-position: inside;
+  }
+
+  .xl\:list-outside {
+    list-style-position: outside;
+  }
+
+  .xl\:list-none {
+    list-style-type: none;
+  }
+
+  .xl\:list-disc {
+    list-style-type: disc;
+  }
+
+  .xl\:list-decimal {
+    list-style-type: decimal;
+  }
+
+  .xl\:m-0 {
+    margin: 0;
+  }
+
+  .xl\:m-1 {
+    margin: 0.25rem;
+  }
+
+  .xl\:m-2 {
+    margin: 0.5rem;
+  }
+
+  .xl\:m-3 {
+    margin: 0.75rem;
+  }
+
+  .xl\:m-4 {
+    margin: 1rem;
+  }
+
+  .xl\:m-5 {
+    margin: 1.25rem;
+  }
+
+  .xl\:m-6 {
+    margin: 1.5rem;
+  }
+
+  .xl\:m-8 {
+    margin: 2rem;
+  }
+
+  .xl\:m-10 {
+    margin: 2.5rem;
+  }
+
+  .xl\:m-12 {
+    margin: 3rem;
+  }
+
+  .xl\:m-16 {
+    margin: 4rem;
+  }
+
+  .xl\:m-20 {
+    margin: 5rem;
+  }
+
+  .xl\:m-24 {
+    margin: 6rem;
+  }
+
+  .xl\:m-32 {
+    margin: 8rem;
+  }
+
+  .xl\:m-40 {
+    margin: 10rem;
+  }
+
+  .xl\:m-48 {
+    margin: 12rem;
+  }
+
+  .xl\:m-56 {
+    margin: 14rem;
+  }
+
+  .xl\:m-64 {
+    margin: 16rem;
+  }
+
+  .xl\:m-auto {
+    margin: auto;
+  }
+
+  .xl\:m-px {
+    margin: 1px;
+  }
+
+  .xl\:-m-1 {
+    margin: -0.25rem;
+  }
+
+  .xl\:-m-2 {
+    margin: -0.5rem;
+  }
+
+  .xl\:-m-3 {
+    margin: -0.75rem;
+  }
+
+  .xl\:-m-4 {
+    margin: -1rem;
+  }
+
+  .xl\:-m-5 {
+    margin: -1.25rem;
+  }
+
+  .xl\:-m-6 {
+    margin: -1.5rem;
+  }
+
+  .xl\:-m-8 {
+    margin: -2rem;
+  }
+
+  .xl\:-m-10 {
+    margin: -2.5rem;
+  }
+
+  .xl\:-m-12 {
+    margin: -3rem;
+  }
+
+  .xl\:-m-16 {
+    margin: -4rem;
+  }
+
+  .xl\:-m-20 {
+    margin: -5rem;
+  }
+
+  .xl\:-m-24 {
+    margin: -6rem;
+  }
+
+  .xl\:-m-32 {
+    margin: -8rem;
+  }
+
+  .xl\:-m-40 {
+    margin: -10rem;
+  }
+
+  .xl\:-m-48 {
+    margin: -12rem;
+  }
+
+  .xl\:-m-56 {
+    margin: -14rem;
+  }
+
+  .xl\:-m-64 {
+    margin: -16rem;
+  }
+
+  .xl\:-m-px {
+    margin: -1px;
+  }
+
+  .xl\:my-0 {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .xl\:mx-0 {
+    margin-left: 0;
+    margin-right: 0;
+  }
+
+  .xl\:my-1 {
+    margin-top: 0.25rem;
+    margin-bottom: 0.25rem;
+  }
+
+  .xl\:mx-1 {
+    margin-left: 0.25rem;
+    margin-right: 0.25rem;
+  }
+
+  .xl\:my-2 {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+  }
+
+  .xl\:mx-2 {
+    margin-left: 0.5rem;
+    margin-right: 0.5rem;
+  }
+
+  .xl\:my-3 {
+    margin-top: 0.75rem;
+    margin-bottom: 0.75rem;
+  }
+
+  .xl\:mx-3 {
+    margin-left: 0.75rem;
+    margin-right: 0.75rem;
+  }
+
+  .xl\:my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+  }
+
+  .xl\:mx-4 {
+    margin-left: 1rem;
+    margin-right: 1rem;
+  }
+
+  .xl\:my-5 {
+    margin-top: 1.25rem;
+    margin-bottom: 1.25rem;
+  }
+
+  .xl\:mx-5 {
+    margin-left: 1.25rem;
+    margin-right: 1.25rem;
+  }
+
+  .xl\:my-6 {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+
+  .xl\:mx-6 {
+    margin-left: 1.5rem;
+    margin-right: 1.5rem;
+  }
+
+  .xl\:my-8 {
+    margin-top: 2rem;
+    margin-bottom: 2rem;
+  }
+
+  .xl\:mx-8 {
+    margin-left: 2rem;
+    margin-right: 2rem;
+  }
+
+  .xl\:my-10 {
+    margin-top: 2.5rem;
+    margin-bottom: 2.5rem;
+  }
+
+  .xl\:mx-10 {
+    margin-left: 2.5rem;
+    margin-right: 2.5rem;
+  }
+
+  .xl\:my-12 {
+    margin-top: 3rem;
+    margin-bottom: 3rem;
+  }
+
+  .xl\:mx-12 {
+    margin-left: 3rem;
+    margin-right: 3rem;
+  }
+
+  .xl\:my-16 {
+    margin-top: 4rem;
+    margin-bottom: 4rem;
+  }
+
+  .xl\:mx-16 {
+    margin-left: 4rem;
+    margin-right: 4rem;
+  }
+
+  .xl\:my-20 {
+    margin-top: 5rem;
+    margin-bottom: 5rem;
+  }
+
+  .xl\:mx-20 {
+    margin-left: 5rem;
+    margin-right: 5rem;
+  }
+
+  .xl\:my-24 {
+    margin-top: 6rem;
+    margin-bottom: 6rem;
+  }
+
+  .xl\:mx-24 {
+    margin-left: 6rem;
+    margin-right: 6rem;
+  }
+
+  .xl\:my-32 {
+    margin-top: 8rem;
+    margin-bottom: 8rem;
+  }
+
+  .xl\:mx-32 {
+    margin-left: 8rem;
+    margin-right: 8rem;
+  }
+
+  .xl\:my-40 {
+    margin-top: 10rem;
+    margin-bottom: 10rem;
+  }
+
+  .xl\:mx-40 {
+    margin-left: 10rem;
+    margin-right: 10rem;
+  }
+
+  .xl\:my-48 {
+    margin-top: 12rem;
+    margin-bottom: 12rem;
+  }
+
+  .xl\:mx-48 {
+    margin-left: 12rem;
+    margin-right: 12rem;
+  }
+
+  .xl\:my-56 {
+    margin-top: 14rem;
+    margin-bottom: 14rem;
+  }
+
+  .xl\:mx-56 {
+    margin-left: 14rem;
+    margin-right: 14rem;
+  }
+
+  .xl\:my-64 {
+    margin-top: 16rem;
+    margin-bottom: 16rem;
+  }
+
+  .xl\:mx-64 {
+    margin-left: 16rem;
+    margin-right: 16rem;
+  }
+
+  .xl\:my-auto {
+    margin-top: auto;
+    margin-bottom: auto;
+  }
+
+  .xl\:mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .xl\:my-px {
+    margin-top: 1px;
+    margin-bottom: 1px;
+  }
+
+  .xl\:mx-px {
+    margin-left: 1px;
+    margin-right: 1px;
+  }
+
+  .xl\:-my-1 {
+    margin-top: -0.25rem;
+    margin-bottom: -0.25rem;
+  }
+
+  .xl\:-mx-1 {
+    margin-left: -0.25rem;
+    margin-right: -0.25rem;
+  }
+
+  .xl\:-my-2 {
+    margin-top: -0.5rem;
+    margin-bottom: -0.5rem;
+  }
+
+  .xl\:-mx-2 {
+    margin-left: -0.5rem;
+    margin-right: -0.5rem;
+  }
+
+  .xl\:-my-3 {
+    margin-top: -0.75rem;
+    margin-bottom: -0.75rem;
+  }
+
+  .xl\:-mx-3 {
+    margin-left: -0.75rem;
+    margin-right: -0.75rem;
+  }
+
+  .xl\:-my-4 {
+    margin-top: -1rem;
+    margin-bottom: -1rem;
+  }
+
+  .xl\:-mx-4 {
+    margin-left: -1rem;
+    margin-right: -1rem;
+  }
+
+  .xl\:-my-5 {
+    margin-top: -1.25rem;
+    margin-bottom: -1.25rem;
+  }
+
+  .xl\:-mx-5 {
+    margin-left: -1.25rem;
+    margin-right: -1.25rem;
+  }
+
+  .xl\:-my-6 {
+    margin-top: -1.5rem;
+    margin-bottom: -1.5rem;
+  }
+
+  .xl\:-mx-6 {
+    margin-left: -1.5rem;
+    margin-right: -1.5rem;
+  }
+
+  .xl\:-my-8 {
+    margin-top: -2rem;
+    margin-bottom: -2rem;
+  }
+
+  .xl\:-mx-8 {
+    margin-left: -2rem;
+    margin-right: -2rem;
+  }
+
+  .xl\:-my-10 {
+    margin-top: -2.5rem;
+    margin-bottom: -2.5rem;
+  }
+
+  .xl\:-mx-10 {
+    margin-left: -2.5rem;
+    margin-right: -2.5rem;
+  }
+
+  .xl\:-my-12 {
+    margin-top: -3rem;
+    margin-bottom: -3rem;
+  }
+
+  .xl\:-mx-12 {
+    margin-left: -3rem;
+    margin-right: -3rem;
+  }
+
+  .xl\:-my-16 {
+    margin-top: -4rem;
+    margin-bottom: -4rem;
+  }
+
+  .xl\:-mx-16 {
+    margin-left: -4rem;
+    margin-right: -4rem;
+  }
+
+  .xl\:-my-20 {
+    margin-top: -5rem;
+    margin-bottom: -5rem;
+  }
+
+  .xl\:-mx-20 {
+    margin-left: -5rem;
+    margin-right: -5rem;
+  }
+
+  .xl\:-my-24 {
+    margin-top: -6rem;
+    margin-bottom: -6rem;
+  }
+
+  .xl\:-mx-24 {
+    margin-left: -6rem;
+    margin-right: -6rem;
+  }
+
+  .xl\:-my-32 {
+    margin-top: -8rem;
+    margin-bottom: -8rem;
+  }
+
+  .xl\:-mx-32 {
+    margin-left: -8rem;
+    margin-right: -8rem;
+  }
+
+  .xl\:-my-40 {
+    margin-top: -10rem;
+    margin-bottom: -10rem;
+  }
+
+  .xl\:-mx-40 {
+    margin-left: -10rem;
+    margin-right: -10rem;
+  }
+
+  .xl\:-my-48 {
+    margin-top: -12rem;
+    margin-bottom: -12rem;
+  }
+
+  .xl\:-mx-48 {
+    margin-left: -12rem;
+    margin-right: -12rem;
+  }
+
+  .xl\:-my-56 {
+    margin-top: -14rem;
+    margin-bottom: -14rem;
+  }
+
+  .xl\:-mx-56 {
+    margin-left: -14rem;
+    margin-right: -14rem;
+  }
+
+  .xl\:-my-64 {
+    margin-top: -16rem;
+    margin-bottom: -16rem;
+  }
+
+  .xl\:-mx-64 {
+    margin-left: -16rem;
+    margin-right: -16rem;
+  }
+
+  .xl\:-my-px {
+    margin-top: -1px;
+    margin-bottom: -1px;
+  }
+
+  .xl\:-mx-px {
+    margin-left: -1px;
+    margin-right: -1px;
+  }
+
+  .xl\:mt-0 {
+    margin-top: 0;
+  }
+
+  .xl\:mr-0 {
+    margin-right: 0;
+  }
+
+  .xl\:mb-0 {
+    margin-bottom: 0;
+  }
+
+  .xl\:ml-0 {
+    margin-left: 0;
+  }
+
+  .xl\:mt-1 {
+    margin-top: 0.25rem;
+  }
+
+  .xl\:mr-1 {
+    margin-right: 0.25rem;
+  }
+
+  .xl\:mb-1 {
+    margin-bottom: 0.25rem;
+  }
+
+  .xl\:ml-1 {
+    margin-left: 0.25rem;
+  }
+
+  .xl\:mt-2 {
+    margin-top: 0.5rem;
+  }
+
+  .xl\:mr-2 {
+    margin-right: 0.5rem;
+  }
+
+  .xl\:mb-2 {
+    margin-bottom: 0.5rem;
+  }
+
+  .xl\:ml-2 {
+    margin-left: 0.5rem;
+  }
+
+  .xl\:mt-3 {
+    margin-top: 0.75rem;
+  }
+
+  .xl\:mr-3 {
+    margin-right: 0.75rem;
+  }
+
+  .xl\:mb-3 {
+    margin-bottom: 0.75rem;
+  }
+
+  .xl\:ml-3 {
+    margin-left: 0.75rem;
+  }
+
+  .xl\:mt-4 {
+    margin-top: 1rem;
+  }
+
+  .xl\:mr-4 {
+    margin-right: 1rem;
+  }
+
+  .xl\:mb-4 {
+    margin-bottom: 1rem;
+  }
+
+  .xl\:ml-4 {
+    margin-left: 1rem;
+  }
+
+  .xl\:mt-5 {
+    margin-top: 1.25rem;
+  }
+
+  .xl\:mr-5 {
+    margin-right: 1.25rem;
+  }
+
+  .xl\:mb-5 {
+    margin-bottom: 1.25rem;
+  }
+
+  .xl\:ml-5 {
+    margin-left: 1.25rem;
+  }
+
+  .xl\:mt-6 {
+    margin-top: 1.5rem;
+  }
+
+  .xl\:mr-6 {
+    margin-right: 1.5rem;
+  }
+
+  .xl\:mb-6 {
+    margin-bottom: 1.5rem;
+  }
+
+  .xl\:ml-6 {
+    margin-left: 1.5rem;
+  }
+
+  .xl\:mt-8 {
+    margin-top: 2rem;
+  }
+
+  .xl\:mr-8 {
+    margin-right: 2rem;
+  }
+
+  .xl\:mb-8 {
+    margin-bottom: 2rem;
+  }
+
+  .xl\:ml-8 {
+    margin-left: 2rem;
+  }
+
+  .xl\:mt-10 {
+    margin-top: 2.5rem;
+  }
+
+  .xl\:mr-10 {
+    margin-right: 2.5rem;
+  }
+
+  .xl\:mb-10 {
+    margin-bottom: 2.5rem;
+  }
+
+  .xl\:ml-10 {
+    margin-left: 2.5rem;
+  }
+
+  .xl\:mt-12 {
+    margin-top: 3rem;
+  }
+
+  .xl\:mr-12 {
+    margin-right: 3rem;
+  }
+
+  .xl\:mb-12 {
+    margin-bottom: 3rem;
+  }
+
+  .xl\:ml-12 {
+    margin-left: 3rem;
+  }
+
+  .xl\:mt-16 {
+    margin-top: 4rem;
+  }
+
+  .xl\:mr-16 {
+    margin-right: 4rem;
+  }
+
+  .xl\:mb-16 {
+    margin-bottom: 4rem;
+  }
+
+  .xl\:ml-16 {
+    margin-left: 4rem;
+  }
+
+  .xl\:mt-20 {
+    margin-top: 5rem;
+  }
+
+  .xl\:mr-20 {
+    margin-right: 5rem;
+  }
+
+  .xl\:mb-20 {
+    margin-bottom: 5rem;
+  }
+
+  .xl\:ml-20 {
+    margin-left: 5rem;
+  }
+
+  .xl\:mt-24 {
+    margin-top: 6rem;
+  }
+
+  .xl\:mr-24 {
+    margin-right: 6rem;
+  }
+
+  .xl\:mb-24 {
+    margin-bottom: 6rem;
+  }
+
+  .xl\:ml-24 {
+    margin-left: 6rem;
+  }
+
+  .xl\:mt-32 {
+    margin-top: 8rem;
+  }
+
+  .xl\:mr-32 {
+    margin-right: 8rem;
+  }
+
+  .xl\:mb-32 {
+    margin-bottom: 8rem;
+  }
+
+  .xl\:ml-32 {
+    margin-left: 8rem;
+  }
+
+  .xl\:mt-40 {
+    margin-top: 10rem;
+  }
+
+  .xl\:mr-40 {
+    margin-right: 10rem;
+  }
+
+  .xl\:mb-40 {
+    margin-bottom: 10rem;
+  }
+
+  .xl\:ml-40 {
+    margin-left: 10rem;
+  }
+
+  .xl\:mt-48 {
+    margin-top: 12rem;
+  }
+
+  .xl\:mr-48 {
+    margin-right: 12rem;
+  }
+
+  .xl\:mb-48 {
+    margin-bottom: 12rem;
+  }
+
+  .xl\:ml-48 {
+    margin-left: 12rem;
+  }
+
+  .xl\:mt-56 {
+    margin-top: 14rem;
+  }
+
+  .xl\:mr-56 {
+    margin-right: 14rem;
+  }
+
+  .xl\:mb-56 {
+    margin-bottom: 14rem;
+  }
+
+  .xl\:ml-56 {
+    margin-left: 14rem;
+  }
+
+  .xl\:mt-64 {
+    margin-top: 16rem;
+  }
+
+  .xl\:mr-64 {
+    margin-right: 16rem;
+  }
+
+  .xl\:mb-64 {
+    margin-bottom: 16rem;
+  }
+
+  .xl\:ml-64 {
+    margin-left: 16rem;
+  }
+
+  .xl\:mt-auto {
+    margin-top: auto;
+  }
+
+  .xl\:mr-auto {
+    margin-right: auto;
+  }
+
+  .xl\:mb-auto {
+    margin-bottom: auto;
+  }
+
+  .xl\:ml-auto {
+    margin-left: auto;
+  }
+
+  .xl\:mt-px {
+    margin-top: 1px;
+  }
+
+  .xl\:mr-px {
+    margin-right: 1px;
+  }
+
+  .xl\:mb-px {
+    margin-bottom: 1px;
+  }
+
+  .xl\:ml-px {
+    margin-left: 1px;
+  }
+
+  .xl\:-mt-1 {
+    margin-top: -0.25rem;
+  }
+
+  .xl\:-mr-1 {
+    margin-right: -0.25rem;
+  }
+
+  .xl\:-mb-1 {
+    margin-bottom: -0.25rem;
+  }
+
+  .xl\:-ml-1 {
+    margin-left: -0.25rem;
+  }
+
+  .xl\:-mt-2 {
+    margin-top: -0.5rem;
+  }
+
+  .xl\:-mr-2 {
+    margin-right: -0.5rem;
+  }
+
+  .xl\:-mb-2 {
+    margin-bottom: -0.5rem;
+  }
+
+  .xl\:-ml-2 {
+    margin-left: -0.5rem;
+  }
+
+  .xl\:-mt-3 {
+    margin-top: -0.75rem;
+  }
+
+  .xl\:-mr-3 {
+    margin-right: -0.75rem;
+  }
+
+  .xl\:-mb-3 {
+    margin-bottom: -0.75rem;
+  }
+
+  .xl\:-ml-3 {
+    margin-left: -0.75rem;
+  }
+
+  .xl\:-mt-4 {
+    margin-top: -1rem;
+  }
+
+  .xl\:-mr-4 {
+    margin-right: -1rem;
+  }
+
+  .xl\:-mb-4 {
+    margin-bottom: -1rem;
+  }
+
+  .xl\:-ml-4 {
+    margin-left: -1rem;
+  }
+
+  .xl\:-mt-5 {
+    margin-top: -1.25rem;
+  }
+
+  .xl\:-mr-5 {
+    margin-right: -1.25rem;
+  }
+
+  .xl\:-mb-5 {
+    margin-bottom: -1.25rem;
+  }
+
+  .xl\:-ml-5 {
+    margin-left: -1.25rem;
+  }
+
+  .xl\:-mt-6 {
+    margin-top: -1.5rem;
+  }
+
+  .xl\:-mr-6 {
+    margin-right: -1.5rem;
+  }
+
+  .xl\:-mb-6 {
+    margin-bottom: -1.5rem;
+  }
+
+  .xl\:-ml-6 {
+    margin-left: -1.5rem;
+  }
+
+  .xl\:-mt-8 {
+    margin-top: -2rem;
+  }
+
+  .xl\:-mr-8 {
+    margin-right: -2rem;
+  }
+
+  .xl\:-mb-8 {
+    margin-bottom: -2rem;
+  }
+
+  .xl\:-ml-8 {
+    margin-left: -2rem;
+  }
+
+  .xl\:-mt-10 {
+    margin-top: -2.5rem;
+  }
+
+  .xl\:-mr-10 {
+    margin-right: -2.5rem;
+  }
+
+  .xl\:-mb-10 {
+    margin-bottom: -2.5rem;
+  }
+
+  .xl\:-ml-10 {
+    margin-left: -2.5rem;
+  }
+
+  .xl\:-mt-12 {
+    margin-top: -3rem;
+  }
+
+  .xl\:-mr-12 {
+    margin-right: -3rem;
+  }
+
+  .xl\:-mb-12 {
+    margin-bottom: -3rem;
+  }
+
+  .xl\:-ml-12 {
+    margin-left: -3rem;
+  }
+
+  .xl\:-mt-16 {
+    margin-top: -4rem;
+  }
+
+  .xl\:-mr-16 {
+    margin-right: -4rem;
+  }
+
+  .xl\:-mb-16 {
+    margin-bottom: -4rem;
+  }
+
+  .xl\:-ml-16 {
+    margin-left: -4rem;
+  }
+
+  .xl\:-mt-20 {
+    margin-top: -5rem;
+  }
+
+  .xl\:-mr-20 {
+    margin-right: -5rem;
+  }
+
+  .xl\:-mb-20 {
+    margin-bottom: -5rem;
+  }
+
+  .xl\:-ml-20 {
+    margin-left: -5rem;
+  }
+
+  .xl\:-mt-24 {
+    margin-top: -6rem;
+  }
+
+  .xl\:-mr-24 {
+    margin-right: -6rem;
+  }
+
+  .xl\:-mb-24 {
+    margin-bottom: -6rem;
+  }
+
+  .xl\:-ml-24 {
+    margin-left: -6rem;
+  }
+
+  .xl\:-mt-32 {
+    margin-top: -8rem;
+  }
+
+  .xl\:-mr-32 {
+    margin-right: -8rem;
+  }
+
+  .xl\:-mb-32 {
+    margin-bottom: -8rem;
+  }
+
+  .xl\:-ml-32 {
+    margin-left: -8rem;
+  }
+
+  .xl\:-mt-40 {
+    margin-top: -10rem;
+  }
+
+  .xl\:-mr-40 {
+    margin-right: -10rem;
+  }
+
+  .xl\:-mb-40 {
+    margin-bottom: -10rem;
+  }
+
+  .xl\:-ml-40 {
+    margin-left: -10rem;
+  }
+
+  .xl\:-mt-48 {
+    margin-top: -12rem;
+  }
+
+  .xl\:-mr-48 {
+    margin-right: -12rem;
+  }
+
+  .xl\:-mb-48 {
+    margin-bottom: -12rem;
+  }
+
+  .xl\:-ml-48 {
+    margin-left: -12rem;
+  }
+
+  .xl\:-mt-56 {
+    margin-top: -14rem;
+  }
+
+  .xl\:-mr-56 {
+    margin-right: -14rem;
+  }
+
+  .xl\:-mb-56 {
+    margin-bottom: -14rem;
+  }
+
+  .xl\:-ml-56 {
+    margin-left: -14rem;
+  }
+
+  .xl\:-mt-64 {
+    margin-top: -16rem;
+  }
+
+  .xl\:-mr-64 {
+    margin-right: -16rem;
+  }
+
+  .xl\:-mb-64 {
+    margin-bottom: -16rem;
+  }
+
+  .xl\:-ml-64 {
+    margin-left: -16rem;
+  }
+
+  .xl\:-mt-px {
+    margin-top: -1px;
+  }
+
+  .xl\:-mr-px {
+    margin-right: -1px;
+  }
+
+  .xl\:-mb-px {
+    margin-bottom: -1px;
+  }
+
+  .xl\:-ml-px {
+    margin-left: -1px;
+  }
+
+  .xl\:max-h-full {
+    max-height: 100%;
+  }
+
+  .xl\:max-h-screen {
+    max-height: 100vh;
+  }
+
+  .xl\:max-w-none {
+    max-width: none;
+  }
+
+  .xl\:max-w-xs {
+    max-width: 20rem;
+  }
+
+  .xl\:max-w-sm {
+    max-width: 24rem;
+  }
+
+  .xl\:max-w-md {
+    max-width: 28rem;
+  }
+
+  .xl\:max-w-lg {
+    max-width: 32rem;
+  }
+
+  .xl\:max-w-xl {
+    max-width: 36rem;
+  }
+
+  .xl\:max-w-2xl {
+    max-width: 42rem;
+  }
+
+  .xl\:max-w-3xl {
+    max-width: 48rem;
+  }
+
+  .xl\:max-w-4xl {
+    max-width: 56rem;
+  }
+
+  .xl\:max-w-5xl {
+    max-width: 64rem;
+  }
+
+  .xl\:max-w-6xl {
+    max-width: 72rem;
+  }
+
+  .xl\:max-w-full {
+    max-width: 100%;
+  }
+
+  .xl\:max-w-screen-sm {
+    max-width: 640px;
+  }
+
+  .xl\:max-w-screen-md {
+    max-width: 768px;
+  }
+
+  .xl\:max-w-screen-lg {
+    max-width: 1024px;
+  }
+
+  .xl\:max-w-screen-xl {
+    max-width: 1280px;
+  }
+
+  .xl\:min-h-0 {
+    min-height: 0;
+  }
+
+  .xl\:min-h-full {
+    min-height: 100%;
+  }
+
+  .xl\:min-h-screen {
+    min-height: 100vh;
+  }
+
+  .xl\:min-w-0 {
+    min-width: 0;
+  }
+
+  .xl\:min-w-full {
+    min-width: 100%;
+  }
+
+  .xl\:object-contain {
+    -o-object-fit: contain;
+       object-fit: contain;
+  }
+
+  .xl\:object-cover {
+    -o-object-fit: cover;
+       object-fit: cover;
+  }
+
+  .xl\:object-fill {
+    -o-object-fit: fill;
+       object-fit: fill;
+  }
+
+  .xl\:object-none {
+    -o-object-fit: none;
+       object-fit: none;
+  }
+
+  .xl\:object-scale-down {
+    -o-object-fit: scale-down;
+       object-fit: scale-down;
+  }
+
+  .xl\:object-bottom {
+    -o-object-position: bottom;
+       object-position: bottom;
+  }
+
+  .xl\:object-center {
+    -o-object-position: center;
+       object-position: center;
+  }
+
+  .xl\:object-left {
+    -o-object-position: left;
+       object-position: left;
+  }
+
+  .xl\:object-left-bottom {
+    -o-object-position: left bottom;
+       object-position: left bottom;
+  }
+
+  .xl\:object-left-top {
+    -o-object-position: left top;
+       object-position: left top;
+  }
+
+  .xl\:object-right {
+    -o-object-position: right;
+       object-position: right;
+  }
+
+  .xl\:object-right-bottom {
+    -o-object-position: right bottom;
+       object-position: right bottom;
+  }
+
+  .xl\:object-right-top {
+    -o-object-position: right top;
+       object-position: right top;
+  }
+
+  .xl\:object-top {
+    -o-object-position: top;
+       object-position: top;
+  }
+
+  .xl\:opacity-0 {
+    opacity: 0;
+  }
+
+  .xl\:opacity-25 {
+    opacity: 0.25;
+  }
+
+  .xl\:opacity-50 {
+    opacity: 0.5;
+  }
+
+  .xl\:opacity-75 {
+    opacity: 0.75;
+  }
+
+  .xl\:opacity-100 {
+    opacity: 1;
+  }
+
+  .xl\:hover\:opacity-0:hover {
+    opacity: 0;
+  }
+
+  .xl\:hover\:opacity-25:hover {
+    opacity: 0.25;
+  }
+
+  .xl\:hover\:opacity-50:hover {
+    opacity: 0.5;
+  }
+
+  .xl\:hover\:opacity-75:hover {
+    opacity: 0.75;
+  }
+
+  .xl\:hover\:opacity-100:hover {
+    opacity: 1;
+  }
+
+  .xl\:focus\:opacity-0:focus {
+    opacity: 0;
+  }
+
+  .xl\:focus\:opacity-25:focus {
+    opacity: 0.25;
+  }
+
+  .xl\:focus\:opacity-50:focus {
+    opacity: 0.5;
+  }
+
+  .xl\:focus\:opacity-75:focus {
+    opacity: 0.75;
+  }
+
+  .xl\:focus\:opacity-100:focus {
+    opacity: 1;
+  }
+
+  .xl\:outline-none {
+    outline: 0;
+  }
+
+  .xl\:focus\:outline-none:focus {
+    outline: 0;
+  }
+
+  .xl\:overflow-auto {
+    overflow: auto;
+  }
+
+  .xl\:overflow-hidden {
+    overflow: hidden;
+  }
+
+  .xl\:overflow-visible {
+    overflow: visible;
+  }
+
+  .xl\:overflow-scroll {
+    overflow: scroll;
+  }
+
+  .xl\:overflow-x-auto {
+    overflow-x: auto;
+  }
+
+  .xl\:overflow-y-auto {
+    overflow-y: auto;
+  }
+
+  .xl\:overflow-x-hidden {
+    overflow-x: hidden;
+  }
+
+  .xl\:overflow-y-hidden {
+    overflow-y: hidden;
+  }
+
+  .xl\:overflow-x-visible {
+    overflow-x: visible;
+  }
+
+  .xl\:overflow-y-visible {
+    overflow-y: visible;
+  }
+
+  .xl\:overflow-x-scroll {
+    overflow-x: scroll;
+  }
+
+  .xl\:overflow-y-scroll {
+    overflow-y: scroll;
+  }
+
+  .xl\:scrolling-touch {
+    -webkit-overflow-scrolling: touch;
+  }
+
+  .xl\:scrolling-auto {
+    -webkit-overflow-scrolling: auto;
+  }
+
+  .xl\:overscroll-auto {
+    -ms-scroll-chaining: chained;
+        overscroll-behavior: auto;
+  }
+
+  .xl\:overscroll-contain {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: contain;
+  }
+
+  .xl\:overscroll-none {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: none;
+  }
+
+  .xl\:overscroll-y-auto {
+    overscroll-behavior-y: auto;
+  }
+
+  .xl\:overscroll-y-contain {
+    overscroll-behavior-y: contain;
+  }
+
+  .xl\:overscroll-y-none {
+    overscroll-behavior-y: none;
+  }
+
+  .xl\:overscroll-x-auto {
+    overscroll-behavior-x: auto;
+  }
+
+  .xl\:overscroll-x-contain {
+    overscroll-behavior-x: contain;
+  }
+
+  .xl\:overscroll-x-none {
+    overscroll-behavior-x: none;
+  }
+
+  .xl\:p-0 {
+    padding: 0;
+  }
+
+  .xl\:p-1 {
+    padding: 0.25rem;
+  }
+
+  .xl\:p-2 {
+    padding: 0.5rem;
+  }
+
+  .xl\:p-3 {
+    padding: 0.75rem;
+  }
+
+  .xl\:p-4 {
+    padding: 1rem;
+  }
+
+  .xl\:p-5 {
+    padding: 1.25rem;
+  }
+
+  .xl\:p-6 {
+    padding: 1.5rem;
+  }
+
+  .xl\:p-8 {
+    padding: 2rem;
+  }
+
+  .xl\:p-10 {
+    padding: 2.5rem;
+  }
+
+  .xl\:p-12 {
+    padding: 3rem;
+  }
+
+  .xl\:p-16 {
+    padding: 4rem;
+  }
+
+  .xl\:p-20 {
+    padding: 5rem;
+  }
+
+  .xl\:p-24 {
+    padding: 6rem;
+  }
+
+  .xl\:p-32 {
+    padding: 8rem;
+  }
+
+  .xl\:p-40 {
+    padding: 10rem;
+  }
+
+  .xl\:p-48 {
+    padding: 12rem;
+  }
+
+  .xl\:p-56 {
+    padding: 14rem;
+  }
+
+  .xl\:p-64 {
+    padding: 16rem;
+  }
+
+  .xl\:p-px {
+    padding: 1px;
+  }
+
+  .xl\:py-0 {
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+
+  .xl\:px-0 {
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  .xl\:py-1 {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+  }
+
+  .xl\:px-1 {
+    padding-left: 0.25rem;
+    padding-right: 0.25rem;
+  }
+
+  .xl\:py-2 {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
+
+  .xl\:px-2 {
+    padding-left: 0.5rem;
+    padding-right: 0.5rem;
+  }
+
+  .xl\:py-3 {
+    padding-top: 0.75rem;
+    padding-bottom: 0.75rem;
+  }
+
+  .xl\:px-3 {
+    padding-left: 0.75rem;
+    padding-right: 0.75rem;
+  }
+
+  .xl\:py-4 {
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+  }
+
+  .xl\:px-4 {
+    padding-left: 1rem;
+    padding-right: 1rem;
+  }
+
+  .xl\:py-5 {
+    padding-top: 1.25rem;
+    padding-bottom: 1.25rem;
+  }
+
+  .xl\:px-5 {
+    padding-left: 1.25rem;
+    padding-right: 1.25rem;
+  }
+
+  .xl\:py-6 {
+    padding-top: 1.5rem;
+    padding-bottom: 1.5rem;
+  }
+
+  .xl\:px-6 {
+    padding-left: 1.5rem;
+    padding-right: 1.5rem;
+  }
+
+  .xl\:py-8 {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
+
+  .xl\:px-8 {
+    padding-left: 2rem;
+    padding-right: 2rem;
+  }
+
+  .xl\:py-10 {
+    padding-top: 2.5rem;
+    padding-bottom: 2.5rem;
+  }
+
+  .xl\:px-10 {
+    padding-left: 2.5rem;
+    padding-right: 2.5rem;
+  }
+
+  .xl\:py-12 {
+    padding-top: 3rem;
+    padding-bottom: 3rem;
+  }
+
+  .xl\:px-12 {
+    padding-left: 3rem;
+    padding-right: 3rem;
+  }
+
+  .xl\:py-16 {
+    padding-top: 4rem;
+    padding-bottom: 4rem;
+  }
+
+  .xl\:px-16 {
+    padding-left: 4rem;
+    padding-right: 4rem;
+  }
+
+  .xl\:py-20 {
+    padding-top: 5rem;
+    padding-bottom: 5rem;
+  }
+
+  .xl\:px-20 {
+    padding-left: 5rem;
+    padding-right: 5rem;
+  }
+
+  .xl\:py-24 {
+    padding-top: 6rem;
+    padding-bottom: 6rem;
+  }
+
+  .xl\:px-24 {
+    padding-left: 6rem;
+    padding-right: 6rem;
+  }
+
+  .xl\:py-32 {
+    padding-top: 8rem;
+    padding-bottom: 8rem;
+  }
+
+  .xl\:px-32 {
+    padding-left: 8rem;
+    padding-right: 8rem;
+  }
+
+  .xl\:py-40 {
+    padding-top: 10rem;
+    padding-bottom: 10rem;
+  }
+
+  .xl\:px-40 {
+    padding-left: 10rem;
+    padding-right: 10rem;
+  }
+
+  .xl\:py-48 {
+    padding-top: 12rem;
+    padding-bottom: 12rem;
+  }
+
+  .xl\:px-48 {
+    padding-left: 12rem;
+    padding-right: 12rem;
+  }
+
+  .xl\:py-56 {
+    padding-top: 14rem;
+    padding-bottom: 14rem;
+  }
+
+  .xl\:px-56 {
+    padding-left: 14rem;
+    padding-right: 14rem;
+  }
+
+  .xl\:py-64 {
+    padding-top: 16rem;
+    padding-bottom: 16rem;
+  }
+
+  .xl\:px-64 {
+    padding-left: 16rem;
+    padding-right: 16rem;
+  }
+
+  .xl\:py-px {
+    padding-top: 1px;
+    padding-bottom: 1px;
+  }
+
+  .xl\:px-px {
+    padding-left: 1px;
+    padding-right: 1px;
+  }
+
+  .xl\:pt-0 {
+    padding-top: 0;
+  }
+
+  .xl\:pr-0 {
+    padding-right: 0;
+  }
+
+  .xl\:pb-0 {
+    padding-bottom: 0;
+  }
+
+  .xl\:pl-0 {
+    padding-left: 0;
+  }
+
+  .xl\:pt-1 {
+    padding-top: 0.25rem;
+  }
+
+  .xl\:pr-1 {
+    padding-right: 0.25rem;
+  }
+
+  .xl\:pb-1 {
+    padding-bottom: 0.25rem;
+  }
+
+  .xl\:pl-1 {
+    padding-left: 0.25rem;
+  }
+
+  .xl\:pt-2 {
+    padding-top: 0.5rem;
+  }
+
+  .xl\:pr-2 {
+    padding-right: 0.5rem;
+  }
+
+  .xl\:pb-2 {
+    padding-bottom: 0.5rem;
+  }
+
+  .xl\:pl-2 {
+    padding-left: 0.5rem;
+  }
+
+  .xl\:pt-3 {
+    padding-top: 0.75rem;
+  }
+
+  .xl\:pr-3 {
+    padding-right: 0.75rem;
+  }
+
+  .xl\:pb-3 {
+    padding-bottom: 0.75rem;
+  }
+
+  .xl\:pl-3 {
+    padding-left: 0.75rem;
+  }
+
+  .xl\:pt-4 {
+    padding-top: 1rem;
+  }
+
+  .xl\:pr-4 {
+    padding-right: 1rem;
+  }
+
+  .xl\:pb-4 {
+    padding-bottom: 1rem;
+  }
+
+  .xl\:pl-4 {
+    padding-left: 1rem;
+  }
+
+  .xl\:pt-5 {
+    padding-top: 1.25rem;
+  }
+
+  .xl\:pr-5 {
+    padding-right: 1.25rem;
+  }
+
+  .xl\:pb-5 {
+    padding-bottom: 1.25rem;
+  }
+
+  .xl\:pl-5 {
+    padding-left: 1.25rem;
+  }
+
+  .xl\:pt-6 {
+    padding-top: 1.5rem;
+  }
+
+  .xl\:pr-6 {
+    padding-right: 1.5rem;
+  }
+
+  .xl\:pb-6 {
+    padding-bottom: 1.5rem;
+  }
+
+  .xl\:pl-6 {
+    padding-left: 1.5rem;
+  }
+
+  .xl\:pt-8 {
+    padding-top: 2rem;
+  }
+
+  .xl\:pr-8 {
+    padding-right: 2rem;
+  }
+
+  .xl\:pb-8 {
+    padding-bottom: 2rem;
+  }
+
+  .xl\:pl-8 {
+    padding-left: 2rem;
+  }
+
+  .xl\:pt-10 {
+    padding-top: 2.5rem;
+  }
+
+  .xl\:pr-10 {
+    padding-right: 2.5rem;
+  }
+
+  .xl\:pb-10 {
+    padding-bottom: 2.5rem;
+  }
+
+  .xl\:pl-10 {
+    padding-left: 2.5rem;
+  }
+
+  .xl\:pt-12 {
+    padding-top: 3rem;
+  }
+
+  .xl\:pr-12 {
+    padding-right: 3rem;
+  }
+
+  .xl\:pb-12 {
+    padding-bottom: 3rem;
+  }
+
+  .xl\:pl-12 {
+    padding-left: 3rem;
+  }
+
+  .xl\:pt-16 {
+    padding-top: 4rem;
+  }
+
+  .xl\:pr-16 {
+    padding-right: 4rem;
+  }
+
+  .xl\:pb-16 {
+    padding-bottom: 4rem;
+  }
+
+  .xl\:pl-16 {
+    padding-left: 4rem;
+  }
+
+  .xl\:pt-20 {
+    padding-top: 5rem;
+  }
+
+  .xl\:pr-20 {
+    padding-right: 5rem;
+  }
+
+  .xl\:pb-20 {
+    padding-bottom: 5rem;
+  }
+
+  .xl\:pl-20 {
+    padding-left: 5rem;
+  }
+
+  .xl\:pt-24 {
+    padding-top: 6rem;
+  }
+
+  .xl\:pr-24 {
+    padding-right: 6rem;
+  }
+
+  .xl\:pb-24 {
+    padding-bottom: 6rem;
+  }
+
+  .xl\:pl-24 {
+    padding-left: 6rem;
+  }
+
+  .xl\:pt-32 {
+    padding-top: 8rem;
+  }
+
+  .xl\:pr-32 {
+    padding-right: 8rem;
+  }
+
+  .xl\:pb-32 {
+    padding-bottom: 8rem;
+  }
+
+  .xl\:pl-32 {
+    padding-left: 8rem;
+  }
+
+  .xl\:pt-40 {
+    padding-top: 10rem;
+  }
+
+  .xl\:pr-40 {
+    padding-right: 10rem;
+  }
+
+  .xl\:pb-40 {
+    padding-bottom: 10rem;
+  }
+
+  .xl\:pl-40 {
+    padding-left: 10rem;
+  }
+
+  .xl\:pt-48 {
+    padding-top: 12rem;
+  }
+
+  .xl\:pr-48 {
+    padding-right: 12rem;
+  }
+
+  .xl\:pb-48 {
+    padding-bottom: 12rem;
+  }
+
+  .xl\:pl-48 {
+    padding-left: 12rem;
+  }
+
+  .xl\:pt-56 {
+    padding-top: 14rem;
+  }
+
+  .xl\:pr-56 {
+    padding-right: 14rem;
+  }
+
+  .xl\:pb-56 {
+    padding-bottom: 14rem;
+  }
+
+  .xl\:pl-56 {
+    padding-left: 14rem;
+  }
+
+  .xl\:pt-64 {
+    padding-top: 16rem;
+  }
+
+  .xl\:pr-64 {
+    padding-right: 16rem;
+  }
+
+  .xl\:pb-64 {
+    padding-bottom: 16rem;
+  }
+
+  .xl\:pl-64 {
+    padding-left: 16rem;
+  }
+
+  .xl\:pt-px {
+    padding-top: 1px;
+  }
+
+  .xl\:pr-px {
+    padding-right: 1px;
+  }
+
+  .xl\:pb-px {
+    padding-bottom: 1px;
+  }
+
+  .xl\:pl-px {
+    padding-left: 1px;
+  }
+
+  .xl\:placeholder-transparent::-moz-placeholder {
+    color: transparent;
+  }
+
+  .xl\:placeholder-transparent:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .xl\:placeholder-transparent::placeholder {
+    color: transparent;
+  }
+
+  .xl\:placeholder-current::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .xl\:placeholder-current:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .xl\:placeholder-current::placeholder {
+    color: currentColor;
+  }
+
+  .xl\:placeholder-black::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-black:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-black::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-white::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-white:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-white::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-transparent:focus::-moz-placeholder {
+    color: transparent;
+  }
+
+  .xl\:focus\:placeholder-transparent:focus:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .xl\:focus\:placeholder-transparent:focus::placeholder {
+    color: transparent;
+  }
+
+  .xl\:focus\:placeholder-current:focus::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .xl\:focus\:placeholder-current:focus:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .xl\:focus\:placeholder-current:focus::placeholder {
+    color: currentColor;
+  }
+
+  .xl\:focus\:placeholder-black:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-black:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-black:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-white:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-white:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-white:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-opacity-0::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:placeholder-opacity-0:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:placeholder-opacity-0::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:placeholder-opacity-25::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:placeholder-opacity-25:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:placeholder-opacity-25::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:placeholder-opacity-50::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:placeholder-opacity-50:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:placeholder-opacity-50::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:placeholder-opacity-75::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:placeholder-opacity-75:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:placeholder-opacity-75::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:placeholder-opacity-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:placeholder-opacity-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:placeholder-opacity-100::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:focus\:placeholder-opacity-0:focus::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:focus\:placeholder-opacity-0:focus::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:focus\:placeholder-opacity-25:focus::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:focus\:placeholder-opacity-25:focus::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:focus\:placeholder-opacity-50:focus::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:focus\:placeholder-opacity-50:focus::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:focus\:placeholder-opacity-75:focus::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:focus\:placeholder-opacity-75:focus::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:focus\:placeholder-opacity-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:focus\:placeholder-opacity-100:focus::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:pointer-events-none {
+    pointer-events: none;
+  }
+
+  .xl\:pointer-events-auto {
+    pointer-events: auto;
+  }
+
+  .xl\:static {
+    position: static;
+  }
+
+  .xl\:fixed {
+    position: fixed;
+  }
+
+  .xl\:absolute {
+    position: absolute;
+  }
+
+  .xl\:relative {
+    position: relative;
+  }
+
+  .xl\:sticky {
+    position: -webkit-sticky;
+    position: sticky;
+  }
+
+  .xl\:inset-0 {
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+  }
+
+  .xl\:inset-auto {
+    top: auto;
+    right: auto;
+    bottom: auto;
+    left: auto;
+  }
+
+  .xl\:inset-y-0 {
+    top: 0;
+    bottom: 0;
+  }
+
+  .xl\:inset-x-0 {
+    right: 0;
+    left: 0;
+  }
+
+  .xl\:inset-y-auto {
+    top: auto;
+    bottom: auto;
+  }
+
+  .xl\:inset-x-auto {
+    right: auto;
+    left: auto;
+  }
+
+  .xl\:top-0 {
+    top: 0;
+  }
+
+  .xl\:right-0 {
+    right: 0;
+  }
+
+  .xl\:bottom-0 {
+    bottom: 0;
+  }
+
+  .xl\:left-0 {
+    left: 0;
+  }
+
+  .xl\:top-auto {
+    top: auto;
+  }
+
+  .xl\:right-auto {
+    right: auto;
+  }
+
+  .xl\:bottom-auto {
+    bottom: auto;
+  }
+
+  .xl\:left-auto {
+    left: auto;
+  }
+
+  .xl\:resize-none {
+    resize: none;
+  }
+
+  .xl\:resize-y {
+    resize: vertical;
+  }
+
+  .xl\:resize-x {
+    resize: horizontal;
+  }
+
+  .xl\:resize {
+    resize: both;
+  }
+
+  .xl\:shadow-xs {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:shadow-sm {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:shadow {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:shadow-md {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:shadow-lg {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:shadow-xl {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .xl\:shadow-2xl {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .xl\:shadow-inner {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:shadow-outline {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .xl\:shadow-none {
+    box-shadow: none;
+  }
+
+  .xl\:hover\:shadow-xs:hover {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:hover\:shadow-sm:hover {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:hover\:shadow:hover {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:hover\:shadow-md:hover {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:hover\:shadow-lg:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:hover\:shadow-xl:hover {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .xl\:hover\:shadow-2xl:hover {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .xl\:hover\:shadow-inner:hover {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:hover\:shadow-outline:hover {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .xl\:hover\:shadow-none:hover {
+    box-shadow: none;
+  }
+
+  .xl\:focus\:shadow-xs:focus {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:focus\:shadow-sm:focus {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:focus\:shadow:focus {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:focus\:shadow-md:focus {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:focus\:shadow-lg:focus {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:focus\:shadow-xl:focus {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .xl\:focus\:shadow-2xl:focus {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .xl\:focus\:shadow-inner:focus {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:focus\:shadow-outline:focus {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .xl\:focus\:shadow-none:focus {
+    box-shadow: none;
+  }
+
+  .xl\:fill-current {
+    fill: currentColor;
+  }
+
+  .xl\:stroke-current {
+    stroke: currentColor;
+  }
+
+  .xl\:stroke-0 {
+    stroke-width: 0;
+  }
+
+  .xl\:stroke-1 {
+    stroke-width: 1;
+  }
+
+  .xl\:stroke-2 {
+    stroke-width: 2;
+  }
+
+  .xl\:table-auto {
+    table-layout: auto;
+  }
+
+  .xl\:table-fixed {
+    table-layout: fixed;
+  }
+
+  .xl\:text-left {
+    text-align: left;
+  }
+
+  .xl\:text-center {
+    text-align: center;
+  }
+
+  .xl\:text-right {
+    text-align: right;
+  }
+
+  .xl\:text-justify {
+    text-align: justify;
+  }
+
+  .xl\:text-transparent {
+    color: transparent;
+  }
+
+  .xl\:text-current {
+    color: currentColor;
+  }
+
+  .xl\:text-black {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .xl\:text-white {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .xl\:text-gray-100 {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .xl\:text-gray-200 {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .xl\:text-gray-300 {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .xl\:text-gray-400 {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .xl\:text-gray-500 {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .xl\:text-gray-600 {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .xl\:text-gray-700 {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .xl\:text-gray-800 {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .xl\:text-gray-900 {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .xl\:text-red-100 {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .xl\:text-red-200 {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .xl\:text-red-300 {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .xl\:text-red-400 {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .xl\:text-red-500 {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .xl\:text-red-600 {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .xl\:text-red-700 {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .xl\:text-red-800 {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .xl\:text-red-900 {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .xl\:text-orange-100 {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .xl\:text-orange-200 {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .xl\:text-orange-300 {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .xl\:text-orange-400 {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .xl\:text-orange-500 {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .xl\:text-orange-600 {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .xl\:text-orange-700 {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .xl\:text-orange-800 {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .xl\:text-orange-900 {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-100 {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-200 {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-300 {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-400 {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-500 {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-600 {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-700 {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-800 {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-900 {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .xl\:text-green-100 {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .xl\:text-green-200 {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .xl\:text-green-300 {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .xl\:text-green-400 {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .xl\:text-green-500 {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .xl\:text-green-600 {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .xl\:text-green-700 {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .xl\:text-green-800 {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .xl\:text-green-900 {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .xl\:text-teal-100 {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .xl\:text-teal-200 {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .xl\:text-teal-300 {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .xl\:text-teal-400 {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .xl\:text-teal-500 {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .xl\:text-teal-600 {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .xl\:text-teal-700 {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .xl\:text-teal-800 {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .xl\:text-teal-900 {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .xl\:text-blue-100 {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .xl\:text-blue-200 {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .xl\:text-blue-300 {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .xl\:text-blue-400 {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .xl\:text-blue-500 {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .xl\:text-blue-600 {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .xl\:text-blue-700 {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .xl\:text-blue-800 {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .xl\:text-blue-900 {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-100 {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-200 {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-300 {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-400 {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-500 {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-600 {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-700 {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-800 {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-900 {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .xl\:text-purple-100 {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .xl\:text-purple-200 {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .xl\:text-purple-300 {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .xl\:text-purple-400 {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .xl\:text-purple-500 {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .xl\:text-purple-600 {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .xl\:text-purple-700 {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .xl\:text-purple-800 {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .xl\:text-purple-900 {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .xl\:text-pink-100 {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .xl\:text-pink-200 {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .xl\:text-pink-300 {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .xl\:text-pink-400 {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .xl\:text-pink-500 {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .xl\:text-pink-600 {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .xl\:text-pink-700 {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .xl\:text-pink-800 {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .xl\:text-pink-900 {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-transparent:hover {
+    color: transparent;
+  }
+
+  .xl\:hover\:text-current:hover {
+    color: currentColor;
+  }
+
+  .xl\:hover\:text-black:hover {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-white:hover {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-100:hover {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-200:hover {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-300:hover {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-400:hover {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-500:hover {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-600:hover {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-700:hover {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-800:hover {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-900:hover {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-100:hover {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-200:hover {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-300:hover {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-400:hover {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-500:hover {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-600:hover {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-700:hover {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-800:hover {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-900:hover {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-100:hover {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-200:hover {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-300:hover {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-400:hover {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-500:hover {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-600:hover {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-700:hover {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-800:hover {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-900:hover {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-100:hover {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-200:hover {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-300:hover {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-400:hover {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-500:hover {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-600:hover {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-700:hover {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-800:hover {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-900:hover {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-100:hover {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-200:hover {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-300:hover {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-400:hover {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-500:hover {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-600:hover {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-700:hover {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-800:hover {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-900:hover {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-100:hover {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-200:hover {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-300:hover {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-400:hover {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-500:hover {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-600:hover {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-700:hover {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-800:hover {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-900:hover {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-100:hover {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-200:hover {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-300:hover {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-400:hover {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-500:hover {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-600:hover {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-700:hover {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-800:hover {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-900:hover {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-100:hover {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-200:hover {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-300:hover {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-400:hover {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-500:hover {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-600:hover {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-700:hover {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-800:hover {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-900:hover {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-100:hover {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-200:hover {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-300:hover {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-400:hover {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-500:hover {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-600:hover {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-700:hover {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-800:hover {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-900:hover {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-100:hover {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-200:hover {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-300:hover {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-400:hover {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-500:hover {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-600:hover {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-700:hover {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-800:hover {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-900:hover {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-transparent:focus {
+    color: transparent;
+  }
+
+  .xl\:focus\:text-current:focus {
+    color: currentColor;
+  }
+
+  .xl\:focus\:text-black:focus {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-white:focus {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-100:focus {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-200:focus {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-300:focus {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-400:focus {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-500:focus {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-600:focus {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-700:focus {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-800:focus {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-900:focus {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-100:focus {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-200:focus {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-300:focus {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-400:focus {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-500:focus {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-600:focus {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-700:focus {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-800:focus {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-900:focus {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-100:focus {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-200:focus {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-300:focus {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-400:focus {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-500:focus {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-600:focus {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-700:focus {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-800:focus {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-900:focus {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-100:focus {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-200:focus {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-300:focus {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-400:focus {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-500:focus {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-600:focus {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-700:focus {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-800:focus {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-900:focus {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-100:focus {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-200:focus {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-300:focus {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-400:focus {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-500:focus {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-600:focus {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-700:focus {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-800:focus {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-900:focus {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-100:focus {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-200:focus {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-300:focus {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-400:focus {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-500:focus {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-600:focus {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-700:focus {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-800:focus {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-900:focus {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-100:focus {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-200:focus {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-300:focus {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-400:focus {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-500:focus {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-600:focus {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-700:focus {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-800:focus {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-900:focus {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-100:focus {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-200:focus {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-300:focus {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-400:focus {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-500:focus {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-600:focus {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-700:focus {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-800:focus {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-900:focus {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-100:focus {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-200:focus {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-300:focus {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-400:focus {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-500:focus {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-600:focus {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-700:focus {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-800:focus {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-900:focus {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-100:focus {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-200:focus {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-300:focus {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-400:focus {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-500:focus {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-600:focus {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-700:focus {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-800:focus {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-900:focus {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .xl\:text-opacity-0 {
+    --text-opacity: 0;
+  }
+
+  .xl\:text-opacity-25 {
+    --text-opacity: 0.25;
+  }
+
+  .xl\:text-opacity-50 {
+    --text-opacity: 0.5;
+  }
+
+  .xl\:text-opacity-75 {
+    --text-opacity: 0.75;
+  }
+
+  .xl\:text-opacity-100 {
+    --text-opacity: 1;
+  }
+
+  .xl\:hover\:text-opacity-0:hover {
+    --text-opacity: 0;
+  }
+
+  .xl\:hover\:text-opacity-25:hover {
+    --text-opacity: 0.25;
+  }
+
+  .xl\:hover\:text-opacity-50:hover {
+    --text-opacity: 0.5;
+  }
+
+  .xl\:hover\:text-opacity-75:hover {
+    --text-opacity: 0.75;
+  }
+
+  .xl\:hover\:text-opacity-100:hover {
+    --text-opacity: 1;
+  }
+
+  .xl\:focus\:text-opacity-0:focus {
+    --text-opacity: 0;
+  }
+
+  .xl\:focus\:text-opacity-25:focus {
+    --text-opacity: 0.25;
+  }
+
+  .xl\:focus\:text-opacity-50:focus {
+    --text-opacity: 0.5;
+  }
+
+  .xl\:focus\:text-opacity-75:focus {
+    --text-opacity: 0.75;
+  }
+
+  .xl\:focus\:text-opacity-100:focus {
+    --text-opacity: 1;
+  }
+
+  .xl\:italic {
+    font-style: italic;
+  }
+
+  .xl\:not-italic {
+    font-style: normal;
+  }
+
+  .xl\:uppercase {
+    text-transform: uppercase;
+  }
+
+  .xl\:lowercase {
+    text-transform: lowercase;
+  }
+
+  .xl\:capitalize {
+    text-transform: capitalize;
+  }
+
+  .xl\:normal-case {
+    text-transform: none;
+  }
+
+  .xl\:underline {
+    text-decoration: underline;
+  }
+
+  .xl\:line-through {
+    text-decoration: line-through;
+  }
+
+  .xl\:no-underline {
+    text-decoration: none;
+  }
+
+  .xl\:hover\:underline:hover {
+    text-decoration: underline;
+  }
+
+  .xl\:hover\:line-through:hover {
+    text-decoration: line-through;
+  }
+
+  .xl\:hover\:no-underline:hover {
+    text-decoration: none;
+  }
+
+  .xl\:focus\:underline:focus {
+    text-decoration: underline;
+  }
+
+  .xl\:focus\:line-through:focus {
+    text-decoration: line-through;
+  }
+
+  .xl\:focus\:no-underline:focus {
+    text-decoration: none;
+  }
+
+  .xl\:antialiased {
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+
+  .xl\:subpixel-antialiased {
+    -webkit-font-smoothing: auto;
+    -moz-osx-font-smoothing: auto;
+  }
+
+  .xl\:ordinal, .xl\:slashed-zero, .xl\:lining-nums, .xl\:oldstyle-nums, .xl\:proportional-nums, .xl\:tabular-nums, .xl\:diagonal-fractions, .xl\:stacked-fractions {
+    --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+    font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+  }
+
+  .xl\:normal-nums {
+    font-variant-numeric: normal;
+  }
+
+  .xl\:ordinal {
+    --font-variant-numeric-ordinal: ordinal;
+  }
+
+  .xl\:slashed-zero {
+    --font-variant-numeric-slashed-zero: slashed-zero;
+  }
+
+  .xl\:lining-nums {
+    --font-variant-numeric-figure: lining-nums;
+  }
+
+  .xl\:oldstyle-nums {
+    --font-variant-numeric-figure: oldstyle-nums;
+  }
+
+  .xl\:proportional-nums {
+    --font-variant-numeric-spacing: proportional-nums;
+  }
+
+  .xl\:tabular-nums {
+    --font-variant-numeric-spacing: tabular-nums;
+  }
+
+  .xl\:diagonal-fractions {
+    --font-variant-numeric-fraction: diagonal-fractions;
+  }
+
+  .xl\:stacked-fractions {
+    --font-variant-numeric-fraction: stacked-fractions;
+  }
+
+  .xl\:tracking-tighter {
+    letter-spacing: -0.05em;
+  }
+
+  .xl\:tracking-tight {
+    letter-spacing: -0.025em;
+  }
+
+  .xl\:tracking-normal {
+    letter-spacing: 0;
+  }
+
+  .xl\:tracking-wide {
+    letter-spacing: 0.025em;
+  }
+
+  .xl\:tracking-wider {
+    letter-spacing: 0.05em;
+  }
+
+  .xl\:tracking-widest {
+    letter-spacing: 0.1em;
+  }
+
+  .xl\:select-none {
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+  }
+
+  .xl\:select-text {
+    -webkit-user-select: text;
+       -moz-user-select: text;
+        -ms-user-select: text;
+            user-select: text;
+  }
+
+  .xl\:select-all {
+    -webkit-user-select: all;
+       -moz-user-select: all;
+        -ms-user-select: all;
+            user-select: all;
+  }
+
+  .xl\:select-auto {
+    -webkit-user-select: auto;
+       -moz-user-select: auto;
+        -ms-user-select: auto;
+            user-select: auto;
+  }
+
+  .xl\:align-baseline {
+    vertical-align: baseline;
+  }
+
+  .xl\:align-top {
+    vertical-align: top;
+  }
+
+  .xl\:align-middle {
+    vertical-align: middle;
+  }
+
+  .xl\:align-bottom {
+    vertical-align: bottom;
+  }
+
+  .xl\:align-text-top {
+    vertical-align: text-top;
+  }
+
+  .xl\:align-text-bottom {
+    vertical-align: text-bottom;
+  }
+
+  .xl\:visible {
+    visibility: visible;
+  }
+
+  .xl\:invisible {
+    visibility: hidden;
+  }
+
+  .xl\:whitespace-normal {
+    white-space: normal;
+  }
+
+  .xl\:whitespace-no-wrap {
+    white-space: nowrap;
+  }
+
+  .xl\:whitespace-pre {
+    white-space: pre;
+  }
+
+  .xl\:whitespace-pre-line {
+    white-space: pre-line;
+  }
+
+  .xl\:whitespace-pre-wrap {
+    white-space: pre-wrap;
+  }
+
+  .xl\:break-normal {
+    overflow-wrap: normal;
+    word-break: normal;
+  }
+
+  .xl\:break-words {
+    overflow-wrap: break-word;
+  }
+
+  .xl\:break-all {
+    word-break: break-all;
+  }
+
+  .xl\:truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .xl\:w-0 {
+    width: 0;
+  }
+
+  .xl\:w-1 {
+    width: 0.25rem;
+  }
+
+  .xl\:w-2 {
+    width: 0.5rem;
+  }
+
+  .xl\:w-3 {
+    width: 0.75rem;
+  }
+
+  .xl\:w-4 {
+    width: 1rem;
+  }
+
+  .xl\:w-5 {
+    width: 1.25rem;
+  }
+
+  .xl\:w-6 {
+    width: 1.5rem;
+  }
+
+  .xl\:w-8 {
+    width: 2rem;
+  }
+
+  .xl\:w-10 {
+    width: 2.5rem;
+  }
+
+  .xl\:w-12 {
+    width: 3rem;
+  }
+
+  .xl\:w-16 {
+    width: 4rem;
+  }
+
+  .xl\:w-20 {
+    width: 5rem;
+  }
+
+  .xl\:w-24 {
+    width: 6rem;
+  }
+
+  .xl\:w-32 {
+    width: 8rem;
+  }
+
+  .xl\:w-40 {
+    width: 10rem;
+  }
+
+  .xl\:w-48 {
+    width: 12rem;
+  }
+
+  .xl\:w-56 {
+    width: 14rem;
+  }
+
+  .xl\:w-64 {
+    width: 16rem;
+  }
+
+  .xl\:w-auto {
+    width: auto;
+  }
+
+  .xl\:w-px {
+    width: 1px;
+  }
+
+  .xl\:w-1\/2 {
+    width: 50%;
+  }
+
+  .xl\:w-1\/3 {
+    width: 33.333333%;
+  }
+
+  .xl\:w-2\/3 {
+    width: 66.666667%;
+  }
+
+  .xl\:w-1\/4 {
+    width: 25%;
+  }
+
+  .xl\:w-2\/4 {
+    width: 50%;
+  }
+
+  .xl\:w-3\/4 {
+    width: 75%;
+  }
+
+  .xl\:w-1\/5 {
+    width: 20%;
+  }
+
+  .xl\:w-2\/5 {
+    width: 40%;
+  }
+
+  .xl\:w-3\/5 {
+    width: 60%;
+  }
+
+  .xl\:w-4\/5 {
+    width: 80%;
+  }
+
+  .xl\:w-1\/6 {
+    width: 16.666667%;
+  }
+
+  .xl\:w-2\/6 {
+    width: 33.333333%;
+  }
+
+  .xl\:w-3\/6 {
+    width: 50%;
+  }
+
+  .xl\:w-4\/6 {
+    width: 66.666667%;
+  }
+
+  .xl\:w-5\/6 {
+    width: 83.333333%;
+  }
+
+  .xl\:w-1\/12 {
+    width: 8.333333%;
+  }
+
+  .xl\:w-2\/12 {
+    width: 16.666667%;
+  }
+
+  .xl\:w-3\/12 {
+    width: 25%;
+  }
+
+  .xl\:w-4\/12 {
+    width: 33.333333%;
+  }
+
+  .xl\:w-5\/12 {
+    width: 41.666667%;
+  }
+
+  .xl\:w-6\/12 {
+    width: 50%;
+  }
+
+  .xl\:w-7\/12 {
+    width: 58.333333%;
+  }
+
+  .xl\:w-8\/12 {
+    width: 66.666667%;
+  }
+
+  .xl\:w-9\/12 {
+    width: 75%;
+  }
+
+  .xl\:w-10\/12 {
+    width: 83.333333%;
+  }
+
+  .xl\:w-11\/12 {
+    width: 91.666667%;
+  }
+
+  .xl\:w-full {
+    width: 100%;
+  }
+
+  .xl\:w-screen {
+    width: 100vw;
+  }
+
+  .xl\:z-0 {
+    z-index: 0;
+  }
+
+  .xl\:z-10 {
+    z-index: 10;
+  }
+
+  .xl\:z-20 {
+    z-index: 20;
+  }
+
+  .xl\:z-30 {
+    z-index: 30;
+  }
+
+  .xl\:z-40 {
+    z-index: 40;
+  }
+
+  .xl\:z-50 {
+    z-index: 50;
+  }
+
+  .xl\:z-auto {
+    z-index: auto;
+  }
+
+  .xl\:gap-0 {
+    grid-gap: 0;
+    gap: 0;
+  }
+
+  .xl\:gap-1 {
+    grid-gap: 0.25rem;
+    gap: 0.25rem;
+  }
+
+  .xl\:gap-2 {
+    grid-gap: 0.5rem;
+    gap: 0.5rem;
+  }
+
+  .xl\:gap-3 {
+    grid-gap: 0.75rem;
+    gap: 0.75rem;
+  }
+
+  .xl\:gap-4 {
+    grid-gap: 1rem;
+    gap: 1rem;
+  }
+
+  .xl\:gap-5 {
+    grid-gap: 1.25rem;
+    gap: 1.25rem;
+  }
+
+  .xl\:gap-6 {
+    grid-gap: 1.5rem;
+    gap: 1.5rem;
+  }
+
+  .xl\:gap-8 {
+    grid-gap: 2rem;
+    gap: 2rem;
+  }
+
+  .xl\:gap-10 {
+    grid-gap: 2.5rem;
+    gap: 2.5rem;
+  }
+
+  .xl\:gap-12 {
+    grid-gap: 3rem;
+    gap: 3rem;
+  }
+
+  .xl\:gap-16 {
+    grid-gap: 4rem;
+    gap: 4rem;
+  }
+
+  .xl\:gap-20 {
+    grid-gap: 5rem;
+    gap: 5rem;
+  }
+
+  .xl\:gap-24 {
+    grid-gap: 6rem;
+    gap: 6rem;
+  }
+
+  .xl\:gap-32 {
+    grid-gap: 8rem;
+    gap: 8rem;
+  }
+
+  .xl\:gap-40 {
+    grid-gap: 10rem;
+    gap: 10rem;
+  }
+
+  .xl\:gap-48 {
+    grid-gap: 12rem;
+    gap: 12rem;
+  }
+
+  .xl\:gap-56 {
+    grid-gap: 14rem;
+    gap: 14rem;
+  }
+
+  .xl\:gap-64 {
+    grid-gap: 16rem;
+    gap: 16rem;
+  }
+
+  .xl\:gap-px {
+    grid-gap: 1px;
+    gap: 1px;
+  }
+
+  .xl\:col-gap-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .xl\:col-gap-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .xl\:col-gap-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .xl\:col-gap-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .xl\:col-gap-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .xl\:col-gap-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .xl\:col-gap-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .xl\:col-gap-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .xl\:col-gap-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .xl\:col-gap-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .xl\:col-gap-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .xl\:col-gap-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .xl\:col-gap-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .xl\:col-gap-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .xl\:col-gap-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .xl\:col-gap-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .xl\:col-gap-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .xl\:col-gap-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .xl\:col-gap-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .xl\:gap-x-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .xl\:gap-x-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .xl\:gap-x-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .xl\:gap-x-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .xl\:gap-x-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .xl\:gap-x-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .xl\:gap-x-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .xl\:gap-x-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .xl\:gap-x-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .xl\:gap-x-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .xl\:gap-x-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .xl\:gap-x-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .xl\:gap-x-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .xl\:gap-x-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .xl\:gap-x-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .xl\:gap-x-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .xl\:gap-x-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .xl\:gap-x-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .xl\:gap-x-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .xl\:row-gap-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .xl\:row-gap-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .xl\:row-gap-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .xl\:row-gap-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .xl\:row-gap-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .xl\:row-gap-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .xl\:row-gap-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .xl\:row-gap-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .xl\:row-gap-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .xl\:row-gap-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .xl\:row-gap-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .xl\:row-gap-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .xl\:row-gap-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .xl\:row-gap-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .xl\:row-gap-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .xl\:row-gap-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .xl\:row-gap-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .xl\:row-gap-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .xl\:row-gap-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .xl\:gap-y-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .xl\:gap-y-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .xl\:gap-y-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .xl\:gap-y-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .xl\:gap-y-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .xl\:gap-y-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .xl\:gap-y-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .xl\:gap-y-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .xl\:gap-y-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .xl\:gap-y-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .xl\:gap-y-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .xl\:gap-y-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .xl\:gap-y-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .xl\:gap-y-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .xl\:gap-y-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .xl\:gap-y-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .xl\:gap-y-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .xl\:gap-y-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .xl\:gap-y-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .xl\:grid-flow-row {
+    grid-auto-flow: row;
+  }
+
+  .xl\:grid-flow-col {
+    grid-auto-flow: column;
+  }
+
+  .xl\:grid-flow-row-dense {
+    grid-auto-flow: row dense;
+  }
+
+  .xl\:grid-flow-col-dense {
+    grid-auto-flow: column dense;
+  }
+
+  .xl\:grid-cols-1 {
+    grid-template-columns: repeat(1, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-2 {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-3 {
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-4 {
+    grid-template-columns: repeat(4, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-5 {
+    grid-template-columns: repeat(5, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-6 {
+    grid-template-columns: repeat(6, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-7 {
+    grid-template-columns: repeat(7, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-8 {
+    grid-template-columns: repeat(8, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-9 {
+    grid-template-columns: repeat(9, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-10 {
+    grid-template-columns: repeat(10, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-11 {
+    grid-template-columns: repeat(11, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-12 {
+    grid-template-columns: repeat(12, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-none {
+    grid-template-columns: none;
+  }
+
+  .xl\:col-auto {
+    grid-column: auto;
+  }
+
+  .xl\:col-span-1 {
+    grid-column: span 1 / span 1;
+  }
+
+  .xl\:col-span-2 {
+    grid-column: span 2 / span 2;
+  }
+
+  .xl\:col-span-3 {
+    grid-column: span 3 / span 3;
+  }
+
+  .xl\:col-span-4 {
+    grid-column: span 4 / span 4;
+  }
+
+  .xl\:col-span-5 {
+    grid-column: span 5 / span 5;
+  }
+
+  .xl\:col-span-6 {
+    grid-column: span 6 / span 6;
+  }
+
+  .xl\:col-span-7 {
+    grid-column: span 7 / span 7;
+  }
+
+  .xl\:col-span-8 {
+    grid-column: span 8 / span 8;
+  }
+
+  .xl\:col-span-9 {
+    grid-column: span 9 / span 9;
+  }
+
+  .xl\:col-span-10 {
+    grid-column: span 10 / span 10;
+  }
+
+  .xl\:col-span-11 {
+    grid-column: span 11 / span 11;
+  }
+
+  .xl\:col-span-12 {
+    grid-column: span 12 / span 12;
+  }
+
+  .xl\:col-start-1 {
+    grid-column-start: 1;
+  }
+
+  .xl\:col-start-2 {
+    grid-column-start: 2;
+  }
+
+  .xl\:col-start-3 {
+    grid-column-start: 3;
+  }
+
+  .xl\:col-start-4 {
+    grid-column-start: 4;
+  }
+
+  .xl\:col-start-5 {
+    grid-column-start: 5;
+  }
+
+  .xl\:col-start-6 {
+    grid-column-start: 6;
+  }
+
+  .xl\:col-start-7 {
+    grid-column-start: 7;
+  }
+
+  .xl\:col-start-8 {
+    grid-column-start: 8;
+  }
+
+  .xl\:col-start-9 {
+    grid-column-start: 9;
+  }
+
+  .xl\:col-start-10 {
+    grid-column-start: 10;
+  }
+
+  .xl\:col-start-11 {
+    grid-column-start: 11;
+  }
+
+  .xl\:col-start-12 {
+    grid-column-start: 12;
+  }
+
+  .xl\:col-start-13 {
+    grid-column-start: 13;
+  }
+
+  .xl\:col-start-auto {
+    grid-column-start: auto;
+  }
+
+  .xl\:col-end-1 {
+    grid-column-end: 1;
+  }
+
+  .xl\:col-end-2 {
+    grid-column-end: 2;
+  }
+
+  .xl\:col-end-3 {
+    grid-column-end: 3;
+  }
+
+  .xl\:col-end-4 {
+    grid-column-end: 4;
+  }
+
+  .xl\:col-end-5 {
+    grid-column-end: 5;
+  }
+
+  .xl\:col-end-6 {
+    grid-column-end: 6;
+  }
+
+  .xl\:col-end-7 {
+    grid-column-end: 7;
+  }
+
+  .xl\:col-end-8 {
+    grid-column-end: 8;
+  }
+
+  .xl\:col-end-9 {
+    grid-column-end: 9;
+  }
+
+  .xl\:col-end-10 {
+    grid-column-end: 10;
+  }
+
+  .xl\:col-end-11 {
+    grid-column-end: 11;
+  }
+
+  .xl\:col-end-12 {
+    grid-column-end: 12;
+  }
+
+  .xl\:col-end-13 {
+    grid-column-end: 13;
+  }
+
+  .xl\:col-end-auto {
+    grid-column-end: auto;
+  }
+
+  .xl\:grid-rows-1 {
+    grid-template-rows: repeat(1, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-2 {
+    grid-template-rows: repeat(2, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-3 {
+    grid-template-rows: repeat(3, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-4 {
+    grid-template-rows: repeat(4, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-5 {
+    grid-template-rows: repeat(5, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-6 {
+    grid-template-rows: repeat(6, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-none {
+    grid-template-rows: none;
+  }
+
+  .xl\:row-auto {
+    grid-row: auto;
+  }
+
+  .xl\:row-span-1 {
+    grid-row: span 1 / span 1;
+  }
+
+  .xl\:row-span-2 {
+    grid-row: span 2 / span 2;
+  }
+
+  .xl\:row-span-3 {
+    grid-row: span 3 / span 3;
+  }
+
+  .xl\:row-span-4 {
+    grid-row: span 4 / span 4;
+  }
+
+  .xl\:row-span-5 {
+    grid-row: span 5 / span 5;
+  }
+
+  .xl\:row-span-6 {
+    grid-row: span 6 / span 6;
+  }
+
+  .xl\:row-start-1 {
+    grid-row-start: 1;
+  }
+
+  .xl\:row-start-2 {
+    grid-row-start: 2;
+  }
+
+  .xl\:row-start-3 {
+    grid-row-start: 3;
+  }
+
+  .xl\:row-start-4 {
+    grid-row-start: 4;
+  }
+
+  .xl\:row-start-5 {
+    grid-row-start: 5;
+  }
+
+  .xl\:row-start-6 {
+    grid-row-start: 6;
+  }
+
+  .xl\:row-start-7 {
+    grid-row-start: 7;
+  }
+
+  .xl\:row-start-auto {
+    grid-row-start: auto;
+  }
+
+  .xl\:row-end-1 {
+    grid-row-end: 1;
+  }
+
+  .xl\:row-end-2 {
+    grid-row-end: 2;
+  }
+
+  .xl\:row-end-3 {
+    grid-row-end: 3;
+  }
+
+  .xl\:row-end-4 {
+    grid-row-end: 4;
+  }
+
+  .xl\:row-end-5 {
+    grid-row-end: 5;
+  }
+
+  .xl\:row-end-6 {
+    grid-row-end: 6;
+  }
+
+  .xl\:row-end-7 {
+    grid-row-end: 7;
+  }
+
+  .xl\:row-end-auto {
+    grid-row-end: auto;
+  }
+
+  .xl\:transform {
+    --transform-translate-x: 0;
+    --transform-translate-y: 0;
+    --transform-rotate: 0;
+    --transform-skew-x: 0;
+    --transform-skew-y: 0;
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+    transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+  }
+
+  .xl\:transform-none {
+    transform: none;
+  }
+
+  .xl\:origin-center {
+    transform-origin: center;
+  }
+
+  .xl\:origin-top {
+    transform-origin: top;
+  }
+
+  .xl\:origin-top-right {
+    transform-origin: top right;
+  }
+
+  .xl\:origin-right {
+    transform-origin: right;
+  }
+
+  .xl\:origin-bottom-right {
+    transform-origin: bottom right;
+  }
+
+  .xl\:origin-bottom {
+    transform-origin: bottom;
+  }
+
+  .xl\:origin-bottom-left {
+    transform-origin: bottom left;
+  }
+
+  .xl\:origin-left {
+    transform-origin: left;
+  }
+
+  .xl\:origin-top-left {
+    transform-origin: top left;
+  }
+
+  .xl\:scale-0 {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .xl\:scale-50 {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .xl\:scale-75 {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .xl\:scale-90 {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .xl\:scale-95 {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .xl\:scale-100 {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .xl\:scale-105 {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:scale-110 {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:scale-125 {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:scale-150 {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:scale-x-0 {
+    --transform-scale-x: 0;
+  }
+
+  .xl\:scale-x-50 {
+    --transform-scale-x: .5;
+  }
+
+  .xl\:scale-x-75 {
+    --transform-scale-x: .75;
+  }
+
+  .xl\:scale-x-90 {
+    --transform-scale-x: .9;
+  }
+
+  .xl\:scale-x-95 {
+    --transform-scale-x: .95;
+  }
+
+  .xl\:scale-x-100 {
+    --transform-scale-x: 1;
+  }
+
+  .xl\:scale-x-105 {
+    --transform-scale-x: 1.05;
+  }
+
+  .xl\:scale-x-110 {
+    --transform-scale-x: 1.1;
+  }
+
+  .xl\:scale-x-125 {
+    --transform-scale-x: 1.25;
+  }
+
+  .xl\:scale-x-150 {
+    --transform-scale-x: 1.5;
+  }
+
+  .xl\:scale-y-0 {
+    --transform-scale-y: 0;
+  }
+
+  .xl\:scale-y-50 {
+    --transform-scale-y: .5;
+  }
+
+  .xl\:scale-y-75 {
+    --transform-scale-y: .75;
+  }
+
+  .xl\:scale-y-90 {
+    --transform-scale-y: .9;
+  }
+
+  .xl\:scale-y-95 {
+    --transform-scale-y: .95;
+  }
+
+  .xl\:scale-y-100 {
+    --transform-scale-y: 1;
+  }
+
+  .xl\:scale-y-105 {
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:scale-y-110 {
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:scale-y-125 {
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:scale-y-150 {
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:hover\:scale-0:hover {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .xl\:hover\:scale-50:hover {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .xl\:hover\:scale-75:hover {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .xl\:hover\:scale-90:hover {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .xl\:hover\:scale-95:hover {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .xl\:hover\:scale-100:hover {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .xl\:hover\:scale-105:hover {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:hover\:scale-110:hover {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:hover\:scale-125:hover {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:hover\:scale-150:hover {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:hover\:scale-x-0:hover {
+    --transform-scale-x: 0;
+  }
+
+  .xl\:hover\:scale-x-50:hover {
+    --transform-scale-x: .5;
+  }
+
+  .xl\:hover\:scale-x-75:hover {
+    --transform-scale-x: .75;
+  }
+
+  .xl\:hover\:scale-x-90:hover {
+    --transform-scale-x: .9;
+  }
+
+  .xl\:hover\:scale-x-95:hover {
+    --transform-scale-x: .95;
+  }
+
+  .xl\:hover\:scale-x-100:hover {
+    --transform-scale-x: 1;
+  }
+
+  .xl\:hover\:scale-x-105:hover {
+    --transform-scale-x: 1.05;
+  }
+
+  .xl\:hover\:scale-x-110:hover {
+    --transform-scale-x: 1.1;
+  }
+
+  .xl\:hover\:scale-x-125:hover {
+    --transform-scale-x: 1.25;
+  }
+
+  .xl\:hover\:scale-x-150:hover {
+    --transform-scale-x: 1.5;
+  }
+
+  .xl\:hover\:scale-y-0:hover {
+    --transform-scale-y: 0;
+  }
+
+  .xl\:hover\:scale-y-50:hover {
+    --transform-scale-y: .5;
+  }
+
+  .xl\:hover\:scale-y-75:hover {
+    --transform-scale-y: .75;
+  }
+
+  .xl\:hover\:scale-y-90:hover {
+    --transform-scale-y: .9;
+  }
+
+  .xl\:hover\:scale-y-95:hover {
+    --transform-scale-y: .95;
+  }
+
+  .xl\:hover\:scale-y-100:hover {
+    --transform-scale-y: 1;
+  }
+
+  .xl\:hover\:scale-y-105:hover {
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:hover\:scale-y-110:hover {
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:hover\:scale-y-125:hover {
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:hover\:scale-y-150:hover {
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:focus\:scale-0:focus {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .xl\:focus\:scale-50:focus {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .xl\:focus\:scale-75:focus {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .xl\:focus\:scale-90:focus {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .xl\:focus\:scale-95:focus {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .xl\:focus\:scale-100:focus {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .xl\:focus\:scale-105:focus {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:focus\:scale-110:focus {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:focus\:scale-125:focus {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:focus\:scale-150:focus {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:focus\:scale-x-0:focus {
+    --transform-scale-x: 0;
+  }
+
+  .xl\:focus\:scale-x-50:focus {
+    --transform-scale-x: .5;
+  }
+
+  .xl\:focus\:scale-x-75:focus {
+    --transform-scale-x: .75;
+  }
+
+  .xl\:focus\:scale-x-90:focus {
+    --transform-scale-x: .9;
+  }
+
+  .xl\:focus\:scale-x-95:focus {
+    --transform-scale-x: .95;
+  }
+
+  .xl\:focus\:scale-x-100:focus {
+    --transform-scale-x: 1;
+  }
+
+  .xl\:focus\:scale-x-105:focus {
+    --transform-scale-x: 1.05;
+  }
+
+  .xl\:focus\:scale-x-110:focus {
+    --transform-scale-x: 1.1;
+  }
+
+  .xl\:focus\:scale-x-125:focus {
+    --transform-scale-x: 1.25;
+  }
+
+  .xl\:focus\:scale-x-150:focus {
+    --transform-scale-x: 1.5;
+  }
+
+  .xl\:focus\:scale-y-0:focus {
+    --transform-scale-y: 0;
+  }
+
+  .xl\:focus\:scale-y-50:focus {
+    --transform-scale-y: .5;
+  }
+
+  .xl\:focus\:scale-y-75:focus {
+    --transform-scale-y: .75;
+  }
+
+  .xl\:focus\:scale-y-90:focus {
+    --transform-scale-y: .9;
+  }
+
+  .xl\:focus\:scale-y-95:focus {
+    --transform-scale-y: .95;
+  }
+
+  .xl\:focus\:scale-y-100:focus {
+    --transform-scale-y: 1;
+  }
+
+  .xl\:focus\:scale-y-105:focus {
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:focus\:scale-y-110:focus {
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:focus\:scale-y-125:focus {
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:focus\:scale-y-150:focus {
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:rotate-0 {
+    --transform-rotate: 0;
+  }
+
+  .xl\:rotate-45 {
+    --transform-rotate: 45deg;
+  }
+
+  .xl\:rotate-90 {
+    --transform-rotate: 90deg;
+  }
+
+  .xl\:rotate-180 {
+    --transform-rotate: 180deg;
+  }
+
+  .xl\:-rotate-180 {
+    --transform-rotate: -180deg;
+  }
+
+  .xl\:-rotate-90 {
+    --transform-rotate: -90deg;
+  }
+
+  .xl\:-rotate-45 {
+    --transform-rotate: -45deg;
+  }
+
+  .xl\:hover\:rotate-0:hover {
+    --transform-rotate: 0;
+  }
+
+  .xl\:hover\:rotate-45:hover {
+    --transform-rotate: 45deg;
+  }
+
+  .xl\:hover\:rotate-90:hover {
+    --transform-rotate: 90deg;
+  }
+
+  .xl\:hover\:rotate-180:hover {
+    --transform-rotate: 180deg;
+  }
+
+  .xl\:hover\:-rotate-180:hover {
+    --transform-rotate: -180deg;
+  }
+
+  .xl\:hover\:-rotate-90:hover {
+    --transform-rotate: -90deg;
+  }
+
+  .xl\:hover\:-rotate-45:hover {
+    --transform-rotate: -45deg;
+  }
+
+  .xl\:focus\:rotate-0:focus {
+    --transform-rotate: 0;
+  }
+
+  .xl\:focus\:rotate-45:focus {
+    --transform-rotate: 45deg;
+  }
+
+  .xl\:focus\:rotate-90:focus {
+    --transform-rotate: 90deg;
+  }
+
+  .xl\:focus\:rotate-180:focus {
+    --transform-rotate: 180deg;
+  }
+
+  .xl\:focus\:-rotate-180:focus {
+    --transform-rotate: -180deg;
+  }
+
+  .xl\:focus\:-rotate-90:focus {
+    --transform-rotate: -90deg;
+  }
+
+  .xl\:focus\:-rotate-45:focus {
+    --transform-rotate: -45deg;
+  }
+
+  .xl\:translate-x-0 {
+    --transform-translate-x: 0;
+  }
+
+  .xl\:translate-x-1 {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .xl\:translate-x-2 {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .xl\:translate-x-3 {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .xl\:translate-x-4 {
+    --transform-translate-x: 1rem;
+  }
+
+  .xl\:translate-x-5 {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .xl\:translate-x-6 {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .xl\:translate-x-8 {
+    --transform-translate-x: 2rem;
+  }
+
+  .xl\:translate-x-10 {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .xl\:translate-x-12 {
+    --transform-translate-x: 3rem;
+  }
+
+  .xl\:translate-x-16 {
+    --transform-translate-x: 4rem;
+  }
+
+  .xl\:translate-x-20 {
+    --transform-translate-x: 5rem;
+  }
+
+  .xl\:translate-x-24 {
+    --transform-translate-x: 6rem;
+  }
+
+  .xl\:translate-x-32 {
+    --transform-translate-x: 8rem;
+  }
+
+  .xl\:translate-x-40 {
+    --transform-translate-x: 10rem;
+  }
+
+  .xl\:translate-x-48 {
+    --transform-translate-x: 12rem;
+  }
+
+  .xl\:translate-x-56 {
+    --transform-translate-x: 14rem;
+  }
+
+  .xl\:translate-x-64 {
+    --transform-translate-x: 16rem;
+  }
+
+  .xl\:translate-x-px {
+    --transform-translate-x: 1px;
+  }
+
+  .xl\:-translate-x-1 {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .xl\:-translate-x-2 {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .xl\:-translate-x-3 {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .xl\:-translate-x-4 {
+    --transform-translate-x: -1rem;
+  }
+
+  .xl\:-translate-x-5 {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .xl\:-translate-x-6 {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .xl\:-translate-x-8 {
+    --transform-translate-x: -2rem;
+  }
+
+  .xl\:-translate-x-10 {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .xl\:-translate-x-12 {
+    --transform-translate-x: -3rem;
+  }
+
+  .xl\:-translate-x-16 {
+    --transform-translate-x: -4rem;
+  }
+
+  .xl\:-translate-x-20 {
+    --transform-translate-x: -5rem;
+  }
+
+  .xl\:-translate-x-24 {
+    --transform-translate-x: -6rem;
+  }
+
+  .xl\:-translate-x-32 {
+    --transform-translate-x: -8rem;
+  }
+
+  .xl\:-translate-x-40 {
+    --transform-translate-x: -10rem;
+  }
+
+  .xl\:-translate-x-48 {
+    --transform-translate-x: -12rem;
+  }
+
+  .xl\:-translate-x-56 {
+    --transform-translate-x: -14rem;
+  }
+
+  .xl\:-translate-x-64 {
+    --transform-translate-x: -16rem;
+  }
+
+  .xl\:-translate-x-px {
+    --transform-translate-x: -1px;
+  }
+
+  .xl\:-translate-x-full {
+    --transform-translate-x: -100%;
+  }
+
+  .xl\:-translate-x-1\/2 {
+    --transform-translate-x: -50%;
+  }
+
+  .xl\:translate-x-1\/2 {
+    --transform-translate-x: 50%;
+  }
+
+  .xl\:translate-x-full {
+    --transform-translate-x: 100%;
+  }
+
+  .xl\:translate-y-0 {
+    --transform-translate-y: 0;
+  }
+
+  .xl\:translate-y-1 {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .xl\:translate-y-2 {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .xl\:translate-y-3 {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .xl\:translate-y-4 {
+    --transform-translate-y: 1rem;
+  }
+
+  .xl\:translate-y-5 {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .xl\:translate-y-6 {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .xl\:translate-y-8 {
+    --transform-translate-y: 2rem;
+  }
+
+  .xl\:translate-y-10 {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .xl\:translate-y-12 {
+    --transform-translate-y: 3rem;
+  }
+
+  .xl\:translate-y-16 {
+    --transform-translate-y: 4rem;
+  }
+
+  .xl\:translate-y-20 {
+    --transform-translate-y: 5rem;
+  }
+
+  .xl\:translate-y-24 {
+    --transform-translate-y: 6rem;
+  }
+
+  .xl\:translate-y-32 {
+    --transform-translate-y: 8rem;
+  }
+
+  .xl\:translate-y-40 {
+    --transform-translate-y: 10rem;
+  }
+
+  .xl\:translate-y-48 {
+    --transform-translate-y: 12rem;
+  }
+
+  .xl\:translate-y-56 {
+    --transform-translate-y: 14rem;
+  }
+
+  .xl\:translate-y-64 {
+    --transform-translate-y: 16rem;
+  }
+
+  .xl\:translate-y-px {
+    --transform-translate-y: 1px;
+  }
+
+  .xl\:-translate-y-1 {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .xl\:-translate-y-2 {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .xl\:-translate-y-3 {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .xl\:-translate-y-4 {
+    --transform-translate-y: -1rem;
+  }
+
+  .xl\:-translate-y-5 {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .xl\:-translate-y-6 {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .xl\:-translate-y-8 {
+    --transform-translate-y: -2rem;
+  }
+
+  .xl\:-translate-y-10 {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .xl\:-translate-y-12 {
+    --transform-translate-y: -3rem;
+  }
+
+  .xl\:-translate-y-16 {
+    --transform-translate-y: -4rem;
+  }
+
+  .xl\:-translate-y-20 {
+    --transform-translate-y: -5rem;
+  }
+
+  .xl\:-translate-y-24 {
+    --transform-translate-y: -6rem;
+  }
+
+  .xl\:-translate-y-32 {
+    --transform-translate-y: -8rem;
+  }
+
+  .xl\:-translate-y-40 {
+    --transform-translate-y: -10rem;
+  }
+
+  .xl\:-translate-y-48 {
+    --transform-translate-y: -12rem;
+  }
+
+  .xl\:-translate-y-56 {
+    --transform-translate-y: -14rem;
+  }
+
+  .xl\:-translate-y-64 {
+    --transform-translate-y: -16rem;
+  }
+
+  .xl\:-translate-y-px {
+    --transform-translate-y: -1px;
+  }
+
+  .xl\:-translate-y-full {
+    --transform-translate-y: -100%;
+  }
+
+  .xl\:-translate-y-1\/2 {
+    --transform-translate-y: -50%;
+  }
+
+  .xl\:translate-y-1\/2 {
+    --transform-translate-y: 50%;
+  }
+
+  .xl\:translate-y-full {
+    --transform-translate-y: 100%;
+  }
+
+  .xl\:hover\:translate-x-0:hover {
+    --transform-translate-x: 0;
+  }
+
+  .xl\:hover\:translate-x-1:hover {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .xl\:hover\:translate-x-2:hover {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .xl\:hover\:translate-x-3:hover {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .xl\:hover\:translate-x-4:hover {
+    --transform-translate-x: 1rem;
+  }
+
+  .xl\:hover\:translate-x-5:hover {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .xl\:hover\:translate-x-6:hover {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .xl\:hover\:translate-x-8:hover {
+    --transform-translate-x: 2rem;
+  }
+
+  .xl\:hover\:translate-x-10:hover {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .xl\:hover\:translate-x-12:hover {
+    --transform-translate-x: 3rem;
+  }
+
+  .xl\:hover\:translate-x-16:hover {
+    --transform-translate-x: 4rem;
+  }
+
+  .xl\:hover\:translate-x-20:hover {
+    --transform-translate-x: 5rem;
+  }
+
+  .xl\:hover\:translate-x-24:hover {
+    --transform-translate-x: 6rem;
+  }
+
+  .xl\:hover\:translate-x-32:hover {
+    --transform-translate-x: 8rem;
+  }
+
+  .xl\:hover\:translate-x-40:hover {
+    --transform-translate-x: 10rem;
+  }
+
+  .xl\:hover\:translate-x-48:hover {
+    --transform-translate-x: 12rem;
+  }
+
+  .xl\:hover\:translate-x-56:hover {
+    --transform-translate-x: 14rem;
+  }
+
+  .xl\:hover\:translate-x-64:hover {
+    --transform-translate-x: 16rem;
+  }
+
+  .xl\:hover\:translate-x-px:hover {
+    --transform-translate-x: 1px;
+  }
+
+  .xl\:hover\:-translate-x-1:hover {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .xl\:hover\:-translate-x-2:hover {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .xl\:hover\:-translate-x-3:hover {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .xl\:hover\:-translate-x-4:hover {
+    --transform-translate-x: -1rem;
+  }
+
+  .xl\:hover\:-translate-x-5:hover {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .xl\:hover\:-translate-x-6:hover {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .xl\:hover\:-translate-x-8:hover {
+    --transform-translate-x: -2rem;
+  }
+
+  .xl\:hover\:-translate-x-10:hover {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .xl\:hover\:-translate-x-12:hover {
+    --transform-translate-x: -3rem;
+  }
+
+  .xl\:hover\:-translate-x-16:hover {
+    --transform-translate-x: -4rem;
+  }
+
+  .xl\:hover\:-translate-x-20:hover {
+    --transform-translate-x: -5rem;
+  }
+
+  .xl\:hover\:-translate-x-24:hover {
+    --transform-translate-x: -6rem;
+  }
+
+  .xl\:hover\:-translate-x-32:hover {
+    --transform-translate-x: -8rem;
+  }
+
+  .xl\:hover\:-translate-x-40:hover {
+    --transform-translate-x: -10rem;
+  }
+
+  .xl\:hover\:-translate-x-48:hover {
+    --transform-translate-x: -12rem;
+  }
+
+  .xl\:hover\:-translate-x-56:hover {
+    --transform-translate-x: -14rem;
+  }
+
+  .xl\:hover\:-translate-x-64:hover {
+    --transform-translate-x: -16rem;
+  }
+
+  .xl\:hover\:-translate-x-px:hover {
+    --transform-translate-x: -1px;
+  }
+
+  .xl\:hover\:-translate-x-full:hover {
+    --transform-translate-x: -100%;
+  }
+
+  .xl\:hover\:-translate-x-1\/2:hover {
+    --transform-translate-x: -50%;
+  }
+
+  .xl\:hover\:translate-x-1\/2:hover {
+    --transform-translate-x: 50%;
+  }
+
+  .xl\:hover\:translate-x-full:hover {
+    --transform-translate-x: 100%;
+  }
+
+  .xl\:hover\:translate-y-0:hover {
+    --transform-translate-y: 0;
+  }
+
+  .xl\:hover\:translate-y-1:hover {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .xl\:hover\:translate-y-2:hover {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .xl\:hover\:translate-y-3:hover {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .xl\:hover\:translate-y-4:hover {
+    --transform-translate-y: 1rem;
+  }
+
+  .xl\:hover\:translate-y-5:hover {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .xl\:hover\:translate-y-6:hover {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .xl\:hover\:translate-y-8:hover {
+    --transform-translate-y: 2rem;
+  }
+
+  .xl\:hover\:translate-y-10:hover {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .xl\:hover\:translate-y-12:hover {
+    --transform-translate-y: 3rem;
+  }
+
+  .xl\:hover\:translate-y-16:hover {
+    --transform-translate-y: 4rem;
+  }
+
+  .xl\:hover\:translate-y-20:hover {
+    --transform-translate-y: 5rem;
+  }
+
+  .xl\:hover\:translate-y-24:hover {
+    --transform-translate-y: 6rem;
+  }
+
+  .xl\:hover\:translate-y-32:hover {
+    --transform-translate-y: 8rem;
+  }
+
+  .xl\:hover\:translate-y-40:hover {
+    --transform-translate-y: 10rem;
+  }
+
+  .xl\:hover\:translate-y-48:hover {
+    --transform-translate-y: 12rem;
+  }
+
+  .xl\:hover\:translate-y-56:hover {
+    --transform-translate-y: 14rem;
+  }
+
+  .xl\:hover\:translate-y-64:hover {
+    --transform-translate-y: 16rem;
+  }
+
+  .xl\:hover\:translate-y-px:hover {
+    --transform-translate-y: 1px;
+  }
+
+  .xl\:hover\:-translate-y-1:hover {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .xl\:hover\:-translate-y-2:hover {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .xl\:hover\:-translate-y-3:hover {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .xl\:hover\:-translate-y-4:hover {
+    --transform-translate-y: -1rem;
+  }
+
+  .xl\:hover\:-translate-y-5:hover {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .xl\:hover\:-translate-y-6:hover {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .xl\:hover\:-translate-y-8:hover {
+    --transform-translate-y: -2rem;
+  }
+
+  .xl\:hover\:-translate-y-10:hover {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .xl\:hover\:-translate-y-12:hover {
+    --transform-translate-y: -3rem;
+  }
+
+  .xl\:hover\:-translate-y-16:hover {
+    --transform-translate-y: -4rem;
+  }
+
+  .xl\:hover\:-translate-y-20:hover {
+    --transform-translate-y: -5rem;
+  }
+
+  .xl\:hover\:-translate-y-24:hover {
+    --transform-translate-y: -6rem;
+  }
+
+  .xl\:hover\:-translate-y-32:hover {
+    --transform-translate-y: -8rem;
+  }
+
+  .xl\:hover\:-translate-y-40:hover {
+    --transform-translate-y: -10rem;
+  }
+
+  .xl\:hover\:-translate-y-48:hover {
+    --transform-translate-y: -12rem;
+  }
+
+  .xl\:hover\:-translate-y-56:hover {
+    --transform-translate-y: -14rem;
+  }
+
+  .xl\:hover\:-translate-y-64:hover {
+    --transform-translate-y: -16rem;
+  }
+
+  .xl\:hover\:-translate-y-px:hover {
+    --transform-translate-y: -1px;
+  }
+
+  .xl\:hover\:-translate-y-full:hover {
+    --transform-translate-y: -100%;
+  }
+
+  .xl\:hover\:-translate-y-1\/2:hover {
+    --transform-translate-y: -50%;
+  }
+
+  .xl\:hover\:translate-y-1\/2:hover {
+    --transform-translate-y: 50%;
+  }
+
+  .xl\:hover\:translate-y-full:hover {
+    --transform-translate-y: 100%;
+  }
+
+  .xl\:focus\:translate-x-0:focus {
+    --transform-translate-x: 0;
+  }
+
+  .xl\:focus\:translate-x-1:focus {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .xl\:focus\:translate-x-2:focus {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .xl\:focus\:translate-x-3:focus {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .xl\:focus\:translate-x-4:focus {
+    --transform-translate-x: 1rem;
+  }
+
+  .xl\:focus\:translate-x-5:focus {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .xl\:focus\:translate-x-6:focus {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .xl\:focus\:translate-x-8:focus {
+    --transform-translate-x: 2rem;
+  }
+
+  .xl\:focus\:translate-x-10:focus {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .xl\:focus\:translate-x-12:focus {
+    --transform-translate-x: 3rem;
+  }
+
+  .xl\:focus\:translate-x-16:focus {
+    --transform-translate-x: 4rem;
+  }
+
+  .xl\:focus\:translate-x-20:focus {
+    --transform-translate-x: 5rem;
+  }
+
+  .xl\:focus\:translate-x-24:focus {
+    --transform-translate-x: 6rem;
+  }
+
+  .xl\:focus\:translate-x-32:focus {
+    --transform-translate-x: 8rem;
+  }
+
+  .xl\:focus\:translate-x-40:focus {
+    --transform-translate-x: 10rem;
+  }
+
+  .xl\:focus\:translate-x-48:focus {
+    --transform-translate-x: 12rem;
+  }
+
+  .xl\:focus\:translate-x-56:focus {
+    --transform-translate-x: 14rem;
+  }
+
+  .xl\:focus\:translate-x-64:focus {
+    --transform-translate-x: 16rem;
+  }
+
+  .xl\:focus\:translate-x-px:focus {
+    --transform-translate-x: 1px;
+  }
+
+  .xl\:focus\:-translate-x-1:focus {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .xl\:focus\:-translate-x-2:focus {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .xl\:focus\:-translate-x-3:focus {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .xl\:focus\:-translate-x-4:focus {
+    --transform-translate-x: -1rem;
+  }
+
+  .xl\:focus\:-translate-x-5:focus {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .xl\:focus\:-translate-x-6:focus {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .xl\:focus\:-translate-x-8:focus {
+    --transform-translate-x: -2rem;
+  }
+
+  .xl\:focus\:-translate-x-10:focus {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .xl\:focus\:-translate-x-12:focus {
+    --transform-translate-x: -3rem;
+  }
+
+  .xl\:focus\:-translate-x-16:focus {
+    --transform-translate-x: -4rem;
+  }
+
+  .xl\:focus\:-translate-x-20:focus {
+    --transform-translate-x: -5rem;
+  }
+
+  .xl\:focus\:-translate-x-24:focus {
+    --transform-translate-x: -6rem;
+  }
+
+  .xl\:focus\:-translate-x-32:focus {
+    --transform-translate-x: -8rem;
+  }
+
+  .xl\:focus\:-translate-x-40:focus {
+    --transform-translate-x: -10rem;
+  }
+
+  .xl\:focus\:-translate-x-48:focus {
+    --transform-translate-x: -12rem;
+  }
+
+  .xl\:focus\:-translate-x-56:focus {
+    --transform-translate-x: -14rem;
+  }
+
+  .xl\:focus\:-translate-x-64:focus {
+    --transform-translate-x: -16rem;
+  }
+
+  .xl\:focus\:-translate-x-px:focus {
+    --transform-translate-x: -1px;
+  }
+
+  .xl\:focus\:-translate-x-full:focus {
+    --transform-translate-x: -100%;
+  }
+
+  .xl\:focus\:-translate-x-1\/2:focus {
+    --transform-translate-x: -50%;
+  }
+
+  .xl\:focus\:translate-x-1\/2:focus {
+    --transform-translate-x: 50%;
+  }
+
+  .xl\:focus\:translate-x-full:focus {
+    --transform-translate-x: 100%;
+  }
+
+  .xl\:focus\:translate-y-0:focus {
+    --transform-translate-y: 0;
+  }
+
+  .xl\:focus\:translate-y-1:focus {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .xl\:focus\:translate-y-2:focus {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .xl\:focus\:translate-y-3:focus {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .xl\:focus\:translate-y-4:focus {
+    --transform-translate-y: 1rem;
+  }
+
+  .xl\:focus\:translate-y-5:focus {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .xl\:focus\:translate-y-6:focus {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .xl\:focus\:translate-y-8:focus {
+    --transform-translate-y: 2rem;
+  }
+
+  .xl\:focus\:translate-y-10:focus {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .xl\:focus\:translate-y-12:focus {
+    --transform-translate-y: 3rem;
+  }
+
+  .xl\:focus\:translate-y-16:focus {
+    --transform-translate-y: 4rem;
+  }
+
+  .xl\:focus\:translate-y-20:focus {
+    --transform-translate-y: 5rem;
+  }
+
+  .xl\:focus\:translate-y-24:focus {
+    --transform-translate-y: 6rem;
+  }
+
+  .xl\:focus\:translate-y-32:focus {
+    --transform-translate-y: 8rem;
+  }
+
+  .xl\:focus\:translate-y-40:focus {
+    --transform-translate-y: 10rem;
+  }
+
+  .xl\:focus\:translate-y-48:focus {
+    --transform-translate-y: 12rem;
+  }
+
+  .xl\:focus\:translate-y-56:focus {
+    --transform-translate-y: 14rem;
+  }
+
+  .xl\:focus\:translate-y-64:focus {
+    --transform-translate-y: 16rem;
+  }
+
+  .xl\:focus\:translate-y-px:focus {
+    --transform-translate-y: 1px;
+  }
+
+  .xl\:focus\:-translate-y-1:focus {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .xl\:focus\:-translate-y-2:focus {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .xl\:focus\:-translate-y-3:focus {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .xl\:focus\:-translate-y-4:focus {
+    --transform-translate-y: -1rem;
+  }
+
+  .xl\:focus\:-translate-y-5:focus {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .xl\:focus\:-translate-y-6:focus {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .xl\:focus\:-translate-y-8:focus {
+    --transform-translate-y: -2rem;
+  }
+
+  .xl\:focus\:-translate-y-10:focus {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .xl\:focus\:-translate-y-12:focus {
+    --transform-translate-y: -3rem;
+  }
+
+  .xl\:focus\:-translate-y-16:focus {
+    --transform-translate-y: -4rem;
+  }
+
+  .xl\:focus\:-translate-y-20:focus {
+    --transform-translate-y: -5rem;
+  }
+
+  .xl\:focus\:-translate-y-24:focus {
+    --transform-translate-y: -6rem;
+  }
+
+  .xl\:focus\:-translate-y-32:focus {
+    --transform-translate-y: -8rem;
+  }
+
+  .xl\:focus\:-translate-y-40:focus {
+    --transform-translate-y: -10rem;
+  }
+
+  .xl\:focus\:-translate-y-48:focus {
+    --transform-translate-y: -12rem;
+  }
+
+  .xl\:focus\:-translate-y-56:focus {
+    --transform-translate-y: -14rem;
+  }
+
+  .xl\:focus\:-translate-y-64:focus {
+    --transform-translate-y: -16rem;
+  }
+
+  .xl\:focus\:-translate-y-px:focus {
+    --transform-translate-y: -1px;
+  }
+
+  .xl\:focus\:-translate-y-full:focus {
+    --transform-translate-y: -100%;
+  }
+
+  .xl\:focus\:-translate-y-1\/2:focus {
+    --transform-translate-y: -50%;
+  }
+
+  .xl\:focus\:translate-y-1\/2:focus {
+    --transform-translate-y: 50%;
+  }
+
+  .xl\:focus\:translate-y-full:focus {
+    --transform-translate-y: 100%;
+  }
+
+  .xl\:skew-x-0 {
+    --transform-skew-x: 0;
+  }
+
+  .xl\:skew-x-3 {
+    --transform-skew-x: 3deg;
+  }
+
+  .xl\:skew-x-6 {
+    --transform-skew-x: 6deg;
+  }
+
+  .xl\:skew-x-12 {
+    --transform-skew-x: 12deg;
+  }
+
+  .xl\:-skew-x-12 {
+    --transform-skew-x: -12deg;
+  }
+
+  .xl\:-skew-x-6 {
+    --transform-skew-x: -6deg;
+  }
+
+  .xl\:-skew-x-3 {
+    --transform-skew-x: -3deg;
+  }
+
+  .xl\:skew-y-0 {
+    --transform-skew-y: 0;
+  }
+
+  .xl\:skew-y-3 {
+    --transform-skew-y: 3deg;
+  }
+
+  .xl\:skew-y-6 {
+    --transform-skew-y: 6deg;
+  }
+
+  .xl\:skew-y-12 {
+    --transform-skew-y: 12deg;
+  }
+
+  .xl\:-skew-y-12 {
+    --transform-skew-y: -12deg;
+  }
+
+  .xl\:-skew-y-6 {
+    --transform-skew-y: -6deg;
+  }
+
+  .xl\:-skew-y-3 {
+    --transform-skew-y: -3deg;
+  }
+
+  .xl\:hover\:skew-x-0:hover {
+    --transform-skew-x: 0;
+  }
+
+  .xl\:hover\:skew-x-3:hover {
+    --transform-skew-x: 3deg;
+  }
+
+  .xl\:hover\:skew-x-6:hover {
+    --transform-skew-x: 6deg;
+  }
+
+  .xl\:hover\:skew-x-12:hover {
+    --transform-skew-x: 12deg;
+  }
+
+  .xl\:hover\:-skew-x-12:hover {
+    --transform-skew-x: -12deg;
+  }
+
+  .xl\:hover\:-skew-x-6:hover {
+    --transform-skew-x: -6deg;
+  }
+
+  .xl\:hover\:-skew-x-3:hover {
+    --transform-skew-x: -3deg;
+  }
+
+  .xl\:hover\:skew-y-0:hover {
+    --transform-skew-y: 0;
+  }
+
+  .xl\:hover\:skew-y-3:hover {
+    --transform-skew-y: 3deg;
+  }
+
+  .xl\:hover\:skew-y-6:hover {
+    --transform-skew-y: 6deg;
+  }
+
+  .xl\:hover\:skew-y-12:hover {
+    --transform-skew-y: 12deg;
+  }
+
+  .xl\:hover\:-skew-y-12:hover {
+    --transform-skew-y: -12deg;
+  }
+
+  .xl\:hover\:-skew-y-6:hover {
+    --transform-skew-y: -6deg;
+  }
+
+  .xl\:hover\:-skew-y-3:hover {
+    --transform-skew-y: -3deg;
+  }
+
+  .xl\:focus\:skew-x-0:focus {
+    --transform-skew-x: 0;
+  }
+
+  .xl\:focus\:skew-x-3:focus {
+    --transform-skew-x: 3deg;
+  }
+
+  .xl\:focus\:skew-x-6:focus {
+    --transform-skew-x: 6deg;
+  }
+
+  .xl\:focus\:skew-x-12:focus {
+    --transform-skew-x: 12deg;
+  }
+
+  .xl\:focus\:-skew-x-12:focus {
+    --transform-skew-x: -12deg;
+  }
+
+  .xl\:focus\:-skew-x-6:focus {
+    --transform-skew-x: -6deg;
+  }
+
+  .xl\:focus\:-skew-x-3:focus {
+    --transform-skew-x: -3deg;
+  }
+
+  .xl\:focus\:skew-y-0:focus {
+    --transform-skew-y: 0;
+  }
+
+  .xl\:focus\:skew-y-3:focus {
+    --transform-skew-y: 3deg;
+  }
+
+  .xl\:focus\:skew-y-6:focus {
+    --transform-skew-y: 6deg;
+  }
+
+  .xl\:focus\:skew-y-12:focus {
+    --transform-skew-y: 12deg;
+  }
+
+  .xl\:focus\:-skew-y-12:focus {
+    --transform-skew-y: -12deg;
+  }
+
+  .xl\:focus\:-skew-y-6:focus {
+    --transform-skew-y: -6deg;
+  }
+
+  .xl\:focus\:-skew-y-3:focus {
+    --transform-skew-y: -3deg;
+  }
+
+  .xl\:transition-none {
+    transition-property: none;
+  }
+
+  .xl\:transition-all {
+    transition-property: all;
+  }
+
+  .xl\:transition {
+    transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+  }
+
+  .xl\:transition-colors {
+    transition-property: background-color, border-color, color, fill, stroke;
+  }
+
+  .xl\:transition-opacity {
+    transition-property: opacity;
+  }
+
+  .xl\:transition-shadow {
+    transition-property: box-shadow;
+  }
+
+  .xl\:transition-transform {
+    transition-property: transform;
+  }
+
+  .xl\:ease-linear {
+    transition-timing-function: linear;
+  }
+
+  .xl\:ease-in {
+    transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+  }
+
+  .xl\:ease-out {
+    transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+  }
+
+  .xl\:ease-in-out {
+    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .xl\:duration-75 {
+    transition-duration: 75ms;
+  }
+
+  .xl\:duration-100 {
+    transition-duration: 100ms;
+  }
+
+  .xl\:duration-150 {
+    transition-duration: 150ms;
+  }
+
+  .xl\:duration-200 {
+    transition-duration: 200ms;
+  }
+
+  .xl\:duration-300 {
+    transition-duration: 300ms;
+  }
+
+  .xl\:duration-500 {
+    transition-duration: 500ms;
+  }
+
+  .xl\:duration-700 {
+    transition-duration: 700ms;
+  }
+
+  .xl\:duration-1000 {
+    transition-duration: 1000ms;
+  }
+
+  .xl\:delay-75 {
+    transition-delay: 75ms;
+  }
+
+  .xl\:delay-100 {
+    transition-delay: 100ms;
+  }
+
+  .xl\:delay-150 {
+    transition-delay: 150ms;
+  }
+
+  .xl\:delay-200 {
+    transition-delay: 200ms;
+  }
+
+  .xl\:delay-300 {
+    transition-delay: 300ms;
+  }
+
+  .xl\:delay-500 {
+    transition-delay: 500ms;
+  }
+
+  .xl\:delay-700 {
+    transition-delay: 700ms;
+  }
+
+  .xl\:delay-1000 {
+    transition-delay: 1000ms;
+  }
+
+  .xl\:animate-none {
+    -webkit-animation: none;
+            animation: none;
+  }
+
+  .xl\:animate-spin {
+    -webkit-animation: spin 1s linear infinite;
+            animation: spin 1s linear infinite;
+  }
+
+  .xl\:animate-ping {
+    -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+            animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+  }
+
+  .xl\:animate-pulse {
+    -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+            animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+  }
+
+  .xl\:animate-bounce {
+    -webkit-animation: bounce 1s infinite;
+            animation: bounce 1s infinite;
+  }
+}
diff --git a/users/wpcarro/website/habit-screens/registry.dat b/users/wpcarro/website/habit-screens/registry.dat
new file mode 100644
index 0000000000..d2671b2cf1
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/registry.dat
Binary files differdiff --git a/users/wpcarro/website/habit-screens/shell.nix b/users/wpcarro/website/habit-screens/shell.nix
new file mode 100644
index 0000000000..afcc0f4d36
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/shell.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs.elmPackages; [
+    elm
+    elm-format
+    elm-live
+  ];
+}
diff --git a/users/wpcarro/website/habit-screens/src/Habits.elm b/users/wpcarro/website/habit-screens/src/Habits.elm
new file mode 100644
index 0000000000..691adc9394
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/src/Habits.elm
@@ -0,0 +1,463 @@
+module Habits exposing (render)
+
+import Browser
+import Date exposing (Date)
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Set exposing (Set)
+import State exposing (HabitType(..))
+import Time exposing (Weekday(..))
+import UI
+import Utils exposing (Strategy(..))
+
+
+morning : List State.Habit
+morning =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.Morning
+            , minutesDuration = duration
+            }
+        )
+        [ ( 1, "Make bed" )
+        , ( 2, "Brush teeth" )
+        , ( 30, "Run (15 minutes)" )
+        , ( 10, "Shower" )
+        , ( 10, "Meditate" )
+        ]
+
+
+evening : List State.Habit
+evening =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.Evening
+            , minutesDuration = duration
+            }
+        )
+        [ ( 30, "Read" )
+        , ( 1, "Record in habit Journal" )
+        ]
+
+
+monday : List ( Int, String )
+monday =
+    [ ( 90, "Bikram Yoga @ 17:00" )
+    ]
+
+
+tuesday : List ( Int, String )
+tuesday =
+    [ ( 90, "Bikram Yoga @ 18:00" )
+    ]
+
+
+wednesday : List ( Int, String )
+wednesday =
+    [ ( 5, "Shave" )
+    , ( 90, "Bikram Yoga @ 17:00" )
+    ]
+
+
+thursday : List ( Int, String )
+thursday =
+    []
+
+
+friday : List ( Int, String )
+friday =
+    [ ( 60, "Bikram Yoga @ 17:00" )
+    , ( 3, "Take-out trash" )
+    , ( 60, "Shop for groceries" )
+    ]
+
+
+saturday : List ( Int, String )
+saturday =
+    [ ( 60, "Warm Yin Yoga @ 15:00" )
+    ]
+
+
+sunday : List ( Int, String )
+sunday =
+    [ ( 1, "Shampoo" )
+    , ( 5, "Shave" )
+    , ( 1, "Trim nails" )
+    , ( 1, "Combine trash cans" )
+    , ( 10, "Mop tile and wood floors" )
+    , ( 10, "Laundry" )
+    , ( 5, "Vacuum bedroom" )
+    , ( 5, "Clean desk" )
+    ]
+
+
+payday : List State.Habit
+payday =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.Payday
+            , minutesDuration = duration
+            }
+        )
+        [ ( 1, "Ensure \"Emergency\" fund has a balance of 1000 GBP" )
+        , ( 1, "Open \"finances_2020\" Google Sheet" )
+        , ( 1, "Settle up with Mimi on TransferWise" )
+        , ( 1, "Adjust GBP:USD exchange rate" )
+        , ( 1, "Adjust \"Stocks (after tax)\" to reflect amount Google sent" )
+        , ( 1, "Add remaining cash to \"Carryover (cash)\"" )
+        , ( 1, "Adjust \"Paycheck\" to reflect amount Google sent" )
+        , ( 5, "In the \"International Xfer\" table, send \"Xfer amount\" from Monzo to USAA" )
+        , ( 10, "Go to an ATM and extract the amount in \"ATM withdrawal\"" )
+        , ( 0, "Await the TransferWise transaction to complete and pay MyFedLoan in USD" )
+        ]
+
+
+firstOfTheMonth : List State.Habit
+firstOfTheMonth =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.FirstOfTheMonth
+            , minutesDuration = duration
+            }
+        )
+        [ ( 10, "Create habit template in journal" )
+        , ( 30, "Assess previous month's performance" )
+        , ( 5, "Register for Bikram Yoga classes" )
+        ]
+
+
+firstOfTheYear : List State.Habit
+firstOfTheYear =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.FirstOfTheYear
+            , minutesDuration = duration
+            }
+        )
+        [ ( 60, "Write a post mortem for the previous year" )
+        ]
+
+
+habitTypes :
+    { includeMorning : Bool
+    , includeEvening : Bool
+    , date : Date
+    }
+    -> List State.HabitType
+habitTypes { includeMorning, includeEvening, date } =
+    let
+        habitTypePredicates : List ( State.HabitType, Date -> Bool )
+        habitTypePredicates =
+            [ ( Morning, \_ -> includeMorning )
+            , ( DayOfWeek, \_ -> True )
+            , ( Payday, \x -> Date.day x == 25 )
+            , ( FirstOfTheMonth, \x -> Date.day x == 1 )
+            , ( FirstOfTheYear, \x -> Date.day x == 1 && Date.monthNumber x == 1 )
+            , ( Evening, \_ -> includeEvening )
+            ]
+    in
+    habitTypePredicates
+        |> List.filter (\( _, predicate ) -> predicate date)
+        |> List.map (\( habitType, _ ) -> habitType)
+
+
+habitsFor : State.HabitType -> Weekday -> List State.Habit
+habitsFor habitType weekday =
+    case habitType of
+        Morning ->
+            morning
+
+        Evening ->
+            evening
+
+        DayOfWeek ->
+            let
+                toHabit : List ( Int, String ) -> List State.Habit
+                toHabit =
+                    List.map
+                        (\( duration, x ) ->
+                            { label = x
+                            , habitType = State.DayOfWeek
+                            , minutesDuration = duration
+                            }
+                        )
+            in
+            case weekday of
+                Mon ->
+                    toHabit monday
+
+                Tue ->
+                    toHabit tuesday
+
+                Wed ->
+                    toHabit wednesday
+
+                Thu ->
+                    toHabit thursday
+
+                Fri ->
+                    toHabit friday
+
+                Sat ->
+                    toHabit saturday
+
+                Sun ->
+                    toHabit sunday
+
+        Payday ->
+            payday
+
+        FirstOfTheMonth ->
+            firstOfTheMonth
+
+        FirstOfTheYear ->
+            firstOfTheYear
+
+
+weekdayLabelFor : Weekday -> State.WeekdayLabel
+weekdayLabelFor weekday =
+    case weekday of
+        Mon ->
+            "Monday"
+
+        Tue ->
+            "Tuesday"
+
+        Wed ->
+            "Wednesday"
+
+        Thu ->
+            "Thursday"
+
+        Fri ->
+            "Friday"
+
+        Sat ->
+            "Saturday"
+
+        Sun ->
+            "Sunday"
+
+
+timeRemaining : State.WeekdayLabel -> State.CompletedHabits -> List State.Habit -> Int
+timeRemaining weekdayLabel completed habits =
+    habits
+        |> List.indexedMap
+            (\i { label, minutesDuration } ->
+                if Set.member ( weekdayLabel, label ) completed then
+                    0
+
+                else
+                    minutesDuration
+            )
+        |> List.sum
+
+
+render : State.Model -> Html State.Msg
+render { today, visibleDayOfWeek, completed, includeMorning, includeEvening } =
+    case ( today, visibleDayOfWeek ) of
+        ( Just todaysDate, Just visibleWeekday ) ->
+            let
+                todaysWeekday : Weekday
+                todaysWeekday =
+                    Date.weekday todaysDate
+
+                habits : List State.Habit
+                habits =
+                    habitTypes
+                        { includeMorning = includeMorning
+                        , includeEvening = includeEvening
+                        , date = todaysDate
+                        }
+                        |> List.map (\habitType -> habitsFor habitType todaysWeekday)
+                        |> List.concat
+            in
+            div
+                [ Utils.class
+                    [ Always "max-w-xl mx-auto py-6 px-6"
+                    , When (todaysWeekday /= visibleWeekday) "pt-20"
+                    ]
+                ]
+                [ header []
+                    [ if todaysWeekday /= visibleWeekday then
+                        div [ class "text-center w-full bg-blue-600 text-white fixed top-0 left-0 px-3 py-4" ]
+                            [ p [ class "py-2 inline pr-5" ]
+                                [ text "As you are not viewing today's habits, the UI is in read-only mode" ]
+                            , UI.button
+                                [ class "bg-blue-200 px-4 py-2 rounded text-blue-600 text-xs font-bold"
+                                , onClick State.ViewToday
+                                ]
+                                [ text "View Today's Habits" ]
+                            ]
+
+                      else
+                        text ""
+                    , div [ class "flex center" ]
+                        [ UI.button
+                            [ class "w-1/4 text-gray-500"
+                            , onClick State.ViewPrevious
+                            ]
+                            [ text "โ€น previous" ]
+                        , h1 [ class "font-bold text-blue-500 text-3xl text-center w-full" ]
+                            [ text (weekdayLabelFor visibleWeekday) ]
+                        , UI.button
+                            [ class "w-1/4 text-gray-500"
+                            , onClick State.ViewNext
+                            ]
+                            [ text "next โ€บ" ]
+                        ]
+                    ]
+                , if todaysWeekday == visibleWeekday then
+                    p [ class "text-center pt-1 pb-4" ]
+                        [ let
+                            t : Int
+                            t =
+                                timeRemaining (weekdayLabelFor todaysWeekday) completed habits
+                          in
+                          if t == 0 then
+                            text "Nothing to do!"
+
+                          else
+                            text
+                                ((habits
+                                    |> timeRemaining (weekdayLabelFor todaysWeekday) completed
+                                    |> String.fromInt
+                                 )
+                                    ++ " minutes remaining"
+                                )
+                        ]
+
+                  else
+                    text ""
+                , if todaysWeekday == visibleWeekday then
+                    div []
+                        [ UI.button
+                            [ onClick
+                                (if Set.size completed == 0 then
+                                    State.DoNothing
+
+                                 else
+                                    State.ClearAll
+                                )
+                            , Utils.class
+                                [ Always "ml-10 px-3"
+                                , If (Set.size completed == 0)
+                                    "text-gray-500 cursor-not-allowed"
+                                    "text-red-500 underline cursor-pointer"
+                                ]
+                            ]
+                            [ let
+                                numCompleted : Int
+                                numCompleted =
+                                    habits
+                                        |> List.indexedMap (\i { label } -> ( i, label ))
+                                        |> List.filter
+                                            (\( i, label ) ->
+                                                Set.member
+                                                    ( weekdayLabelFor todaysWeekday, label )
+                                                    completed
+                                            )
+                                        |> List.length
+                              in
+                              if numCompleted == 0 then
+                                text "Clear"
+
+                              else
+                                text ("Clear (" ++ String.fromInt numCompleted ++ ")")
+                            ]
+                        , UI.button
+                            [ onClick State.ToggleMorning
+                            , Utils.class
+                                [ Always "px-3 underline"
+                                , If includeMorning
+                                    "text-gray-600"
+                                    "text-blue-600"
+                                ]
+                            ]
+                            [ text
+                                (if includeMorning then
+                                    "Hide Morning"
+
+                                 else
+                                    "Show Morning"
+                                )
+                            ]
+                        , UI.button
+                            [ Utils.class
+                                [ Always "px-3 underline"
+                                , If includeEvening
+                                    "text-gray-600"
+                                    "text-blue-600"
+                                ]
+                            , onClick State.ToggleEvening
+                            ]
+                            [ text
+                                (if includeEvening then
+                                    "Hide Evening"
+
+                                 else
+                                    "Show Evening"
+                                )
+                            ]
+                        ]
+
+                  else
+                    text ""
+                , ul [ class "pb-10" ]
+                    (habits
+                        |> List.indexedMap
+                            (\i { label, minutesDuration } ->
+                                let
+                                    isCompleted : Bool
+                                    isCompleted =
+                                        Set.member ( weekdayLabelFor todaysWeekday, label ) completed
+                                in
+                                li [ class "text-xl list-disc ml-6" ]
+                                    [ if todaysWeekday == visibleWeekday then
+                                        UI.button
+                                            [ class "py-5 px-3"
+                                            , onClick
+                                                (State.ToggleHabit
+                                                    (weekdayLabelFor todaysWeekday)
+                                                    label
+                                                )
+                                            ]
+                                            [ span
+                                                [ Utils.class
+                                                    [ Always "text-white pt-1 px-2 rounded"
+                                                    , If isCompleted "bg-gray-400" "bg-blue-500"
+                                                    ]
+                                                ]
+                                                [ text (String.fromInt minutesDuration ++ " mins") ]
+                                            , p
+                                                [ Utils.class
+                                                    [ Always "inline pl-3"
+                                                    , When isCompleted "line-through text-gray-400"
+                                                    ]
+                                                ]
+                                                [ text label ]
+                                            ]
+
+                                      else
+                                        UI.button
+                                            [ class "py-5 px-3 cursor-not-allowed"
+                                            , onClick State.DoNothing
+                                            ]
+                                            [ text label ]
+                                    ]
+                            )
+                    )
+                , footer [ class "bg-white text-sm text-center text-gray-500 fixed bottom-0 left-0 w-full py-4" ]
+                    [ p [] [ text "This app is brought to you by William Carroll." ]
+                    , p [] [ text "Client: Elm; Server: n/a" ]
+                    ]
+                ]
+
+        ( _, _ ) ->
+            p [] [ text "Unable to display habits because we do not know what day of the week it is." ]
diff --git a/users/wpcarro/website/habit-screens/src/Main.elm b/users/wpcarro/website/habit-screens/src/Main.elm
new file mode 100644
index 0000000000..2ddedb9133
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/src/Main.elm
@@ -0,0 +1,29 @@
+module Main exposing (main)
+
+import Browser
+import Habits
+import Html exposing (..)
+import State
+import Time
+
+
+subscriptions : State.Model -> Sub State.Msg
+subscriptions model =
+    -- once per minute
+    Time.every (1000 * 60) (\_ -> State.MaybeAdjustWeekday)
+
+
+view : State.Model -> Html State.Msg
+view model =
+    case model.view of
+        State.Habits ->
+            Habits.render model
+
+
+main =
+    Browser.element
+        { init = \() -> State.init
+        , subscriptions = subscriptions
+        , update = State.update
+        , view = view
+        }
diff --git a/users/wpcarro/website/habit-screens/src/State.elm b/users/wpcarro/website/habit-screens/src/State.elm
new file mode 100644
index 0000000000..ea00a01351
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/src/State.elm
@@ -0,0 +1,195 @@
+module State exposing (..)
+
+import Date exposing (Date)
+import Set exposing (Set)
+import Task
+import Time exposing (Weekday(..))
+
+
+type alias WeekdayLabel =
+    String
+
+
+type alias HabitLabel =
+    String
+
+
+type Msg
+    = DoNothing
+    | SetView View
+    | ReceiveDate Date
+    | ToggleHabit WeekdayLabel HabitLabel
+    | MaybeAdjustWeekday
+    | ViewToday
+    | ViewPrevious
+    | ViewNext
+    | ClearAll
+    | ToggleMorning
+    | ToggleEvening
+
+
+type View
+    = Habits
+
+
+type HabitType
+    = Morning
+    | Evening
+    | DayOfWeek
+    | Payday
+    | FirstOfTheMonth
+    | FirstOfTheYear
+
+
+type alias Habit =
+    { label : HabitLabel
+    , habitType : HabitType
+    , minutesDuration : Int
+    }
+
+
+type alias CompletedHabits =
+    Set ( WeekdayLabel, HabitLabel )
+
+
+type alias Model =
+    { isLoading : Bool
+    , view : View
+    , today : Maybe Date
+    , completed : CompletedHabits
+    , visibleDayOfWeek : Maybe Weekday
+    , includeMorning : Bool
+    , includeEvening : Bool
+    }
+
+
+previousDay : Weekday -> Weekday
+previousDay weekday =
+    case weekday of
+        Mon ->
+            Sun
+
+        Tue ->
+            Mon
+
+        Wed ->
+            Tue
+
+        Thu ->
+            Wed
+
+        Fri ->
+            Thu
+
+        Sat ->
+            Fri
+
+        Sun ->
+            Sat
+
+
+nextDay : Weekday -> Weekday
+nextDay weekday =
+    case weekday of
+        Mon ->
+            Tue
+
+        Tue ->
+            Wed
+
+        Wed ->
+            Thu
+
+        Thu ->
+            Fri
+
+        Fri ->
+            Sat
+
+        Sat ->
+            Sun
+
+        Sun ->
+            Mon
+
+
+{-| The initial state for the application.
+-}
+init : ( Model, Cmd Msg )
+init =
+    ( { isLoading = False
+      , view = Habits
+      , today = Nothing
+      , completed = Set.empty
+      , visibleDayOfWeek = Nothing
+      , includeMorning = True
+      , includeEvening = True
+      }
+    , Date.today |> Task.perform ReceiveDate
+    )
+
+
+{-| Now that we have state, we need a function to change the state.
+-}
+update : Msg -> Model -> ( Model, Cmd Msg )
+update msg ({ today, visibleDayOfWeek, completed } as model) =
+    case msg of
+        DoNothing ->
+            ( model, Cmd.none )
+
+        SetView x ->
+            ( { model
+                | view = x
+                , isLoading = True
+              }
+            , Cmd.none
+            )
+
+        ReceiveDate x ->
+            ( { model
+                | today = Just x
+                , visibleDayOfWeek = Just (Date.weekday x)
+              }
+            , Cmd.none
+            )
+
+        ToggleHabit weekdayLabel habitLabel ->
+            ( { model
+                | completed =
+                    if Set.member ( weekdayLabel, habitLabel ) completed then
+                        Set.remove ( weekdayLabel, habitLabel ) completed
+
+                    else
+                        Set.insert ( weekdayLabel, habitLabel ) completed
+              }
+            , Cmd.none
+            )
+
+        MaybeAdjustWeekday ->
+            ( model, Date.today |> Task.perform ReceiveDate )
+
+        ViewToday ->
+            ( { model | visibleDayOfWeek = today |> Maybe.map Date.weekday }, Cmd.none )
+
+        ViewPrevious ->
+            ( { model
+                | visibleDayOfWeek = visibleDayOfWeek |> Maybe.map previousDay
+              }
+            , Cmd.none
+            )
+
+        ViewNext ->
+            ( { model
+                | visibleDayOfWeek = visibleDayOfWeek |> Maybe.map nextDay
+              }
+            , Cmd.none
+            )
+
+        ClearAll ->
+            ( { model | completed = Set.empty }, Cmd.none )
+
+        ToggleMorning ->
+            ( { model | includeMorning = not model.includeMorning }, Cmd.none )
+
+        ToggleEvening ->
+            ( { model | includeEvening = not model.includeEvening }, Cmd.none )
diff --git a/users/wpcarro/website/habit-screens/src/UI.elm b/users/wpcarro/website/habit-screens/src/UI.elm
new file mode 100644
index 0000000000..5b54269135
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/src/UI.elm
@@ -0,0 +1,9 @@
+module UI exposing (..)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+
+
+button : List (Attribute msg) -> List (Html msg) -> Html msg
+button attrs children =
+    Html.button ([ class "focus:outline-none" ] ++ attrs) children
diff --git a/users/wpcarro/website/habit-screens/src/Utils.elm b/users/wpcarro/website/habit-screens/src/Utils.elm
new file mode 100644
index 0000000000..23b13c224c
--- /dev/null
+++ b/users/wpcarro/website/habit-screens/src/Utils.elm
@@ -0,0 +1,37 @@
+module Utils exposing (..)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Maybe.Extra
+
+
+type Strategy
+    = Always String
+    | When Bool String
+    | If Bool String String
+
+
+class : List Strategy -> Attribute msg
+class classes =
+    classes
+        |> List.map
+            (\strategy ->
+                case strategy of
+                    Always x ->
+                        Just x
+
+                    When True x ->
+                        Just x
+
+                    When False _ ->
+                        Nothing
+
+                    If True x _ ->
+                        Just x
+
+                    If False _ x ->
+                        Just x
+            )
+        |> Maybe.Extra.values
+        |> String.join " "
+        |> Html.Attributes.class
diff --git a/users/wpcarro/website/sandbox/contentful/.envrc b/users/wpcarro/website/sandbox/contentful/.envrc
new file mode 100644
index 0000000000..e9d0356aaa
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/.envrc
@@ -0,0 +1,5 @@
+source_up
+use_nix
+# TODO(wpcarro): Prefer age-nix solution if possible.
+export CONTENTFUL_SPACE_ID="$(jq -j '.contentful | .spaceId' < $WPCARRO/secrets.json)"
+export CONTENTFUL_ACCESS_TOKEN="$(jq -j '.contentful | .accessToken' < $WPCARRO/secrets.json)"
diff --git a/users/wpcarro/website/sandbox/contentful/.gitignore b/users/wpcarro/website/sandbox/contentful/.gitignore
new file mode 100644
index 0000000000..fdf1c6188a
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/.gitignore
@@ -0,0 +1,2 @@
+.cache
+dist
\ No newline at end of file
diff --git a/users/wpcarro/website/sandbox/contentful/README.md b/users/wpcarro/website/sandbox/contentful/README.md
new file mode 100644
index 0000000000..9bd6fc914b
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/README.md
@@ -0,0 +1,18 @@
+# Contentful
+
+I have not used a CMS in a few years. I learned about Contentful from a
+Gatsby.js tutorial, and I wanted to learn more; I created a Contentful account,
+and I'm experimenting with the data here.
+
+## Developing
+
+```shell
+$ nix-shell
+$ yarn run dev
+```
+
+## Building
+
+```shell
+$ nix-build
+```
diff --git a/users/wpcarro/website/sandbox/contentful/default.nix b/users/wpcarro/website/sandbox/contentful/default.nix
new file mode 100644
index 0000000000..ce7e534b23
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/default.nix
@@ -0,0 +1,24 @@
+{ pkgs, ... }:
+
+pkgs.stdenv.mkDerivation {
+  name = "ideal-website";
+  src = builtins.path { path = ./.; name = "contentful"; };
+  buildInputs = with pkgs; [
+    nodejs
+    # Exposes lscpu for parcel.js
+    utillinux
+  ];
+  # parcel.js needs number of CPUs
+  PARCEL_WORKERS = "1";
+  buildPhase = ''
+    export HOME="."
+    npx parcel build index.html
+  '';
+
+  installPhase = ''
+    mv dist $out
+  '';
+
+  # TODO(wpcarro): This doesn't build at all.
+  meta.ci.skip = true;
+}
diff --git a/users/wpcarro/website/sandbox/contentful/package.json b/users/wpcarro/website/sandbox/contentful/package.json
new file mode 100644
index 0000000000..3530bef763
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/package.json
@@ -0,0 +1,26 @@
+{
+  "name": "tailwindcss",
+  "version": "1.0.0",
+  "main": "index.js",
+  "license": "MIT",
+  "scripts": {
+    "dev": "npx parcel src/index.html & npx tsc --watch --noEmit"
+  },
+  "devDependencies": {
+    "@types/node": "^13.9.3",
+    "parcel-bundler": "^1.12.4",
+    "tailwindcss": "^1.2.0",
+    "typescript": "^3.8.3"
+  },
+  "dependencies": {
+    "@reduxjs/toolkit": "^1.2.5",
+    "@types/react-dom": "^16.9.5",
+    "@types/react-redux": "^7.1.7",
+    "@types/react-router-dom": "^5.1.3",
+    "contentful": "^7.14.0",
+    "react": "^16.13.1",
+    "react-dom": "^16.13.1",
+    "react-redux": "^7.2.0",
+    "react-router-dom": "^5.1.2"
+  }
+}
diff --git a/users/wpcarro/website/sandbox/contentful/postcss.config.js b/users/wpcarro/website/sandbox/contentful/postcss.config.js
new file mode 100644
index 0000000000..a23795075b
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/postcss.config.js
@@ -0,0 +1,5 @@
+const tailwindcss = require("tailwindcss");
+
+module.exports = {
+  plugins: [tailwindcss("./tailwind.config.js")],
+};
diff --git a/users/wpcarro/website/sandbox/contentful/shell.nix b/users/wpcarro/website/sandbox/contentful/shell.nix
new file mode 100644
index 0000000000..a3ae929ef4
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/shell.nix
@@ -0,0 +1,8 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    nodejs
+    yarn
+  ];
+}
diff --git a/users/wpcarro/website/sandbox/contentful/src/App.tsx b/users/wpcarro/website/sandbox/contentful/src/App.tsx
new file mode 100644
index 0000000000..288f033218
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/src/App.tsx
@@ -0,0 +1,49 @@
+import React, { useEffect } from "react";
+import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
+import { useDispatch } from "react-redux";
+import { actions, useTypedSelector } from "./store";
+import { Link } from "react-router-dom";
+import { getClient } from "./contentful";
+import type { Book } from "./store";
+
+const App: React.FC = () => {
+  const dispatch = useDispatch();
+  const { isLoading, books } = useTypedSelector((state) => ({
+    isLoading: state.isLoading,
+    books: state.books,
+  }));
+
+  useEffect(() => {
+    async function fetchData() {
+      const entries = await getClient().getEntries();
+      const books = entries.items.map((x) => x.fields) as Book[];
+
+      dispatch(actions.setBooks(books));
+    }
+    fetchData();
+  }, []);
+
+  return (
+    <Router>
+      <Switch>
+        <Route exact path="/">
+          <div className="container mx-auto">
+            <h1 className="py-6 text-2xl">Books</h1>
+            <ul>
+              {books.map((book) => (
+                <li key={book.title} className="py-3">
+                  <p>
+                    <span className="font-bold pr-3">{book.title}</span>
+                    <span className="text-gray-600">{book.author}</span>
+                  </p>
+                </li>
+              ))}
+            </ul>
+          </div>
+        </Route>
+      </Switch>
+    </Router>
+  );
+};
+
+export default App;
diff --git a/users/wpcarro/website/sandbox/contentful/src/contentful.ts b/users/wpcarro/website/sandbox/contentful/src/contentful.ts
new file mode 100644
index 0000000000..02ebc92b68
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/src/contentful.ts
@@ -0,0 +1,27 @@
+import { createClient } from "contentful";
+import type { ContentfulClientApi } from "contentful";
+
+const space = process.env.CONTENTFUL_SPACE_ID;
+const accessToken = process.env.CONTENTFUL_ACCESS_TOKEN;
+
+let client: ContentfulClientApi;
+
+// Idempotent way to get a reference to the Contentful client.
+export const getClient = (): ContentfulClientApi => {
+  if (typeof client !== "undefined") {
+    return client;
+  } else {
+    if (typeof space === "string" && typeof accessToken === "string") {
+      let client = createClient({
+        space,
+        accessToken,
+      });
+
+      return client;
+    } else {
+      throw new Error(
+        "Please set CONTENTFUL_SPACE_ID and CONTENTFUL_ACCESS_TOKEN"
+      );
+    }
+  }
+};
diff --git a/users/wpcarro/website/sandbox/contentful/src/index.css b/users/wpcarro/website/sandbox/contentful/src/index.css
new file mode 100644
index 0000000000..b5c61c9567
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/src/index.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/users/wpcarro/website/sandbox/contentful/src/index.html b/users/wpcarro/website/sandbox/contentful/src/index.html
new file mode 100644
index 0000000000..91752af916
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/src/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="stylesheet" href="./index.css" />
+  </head>
+  <body>
+    <div id="mount"></div>
+    <script src="./index.tsx"></script>
+  </body>
+</html>
diff --git a/users/wpcarro/website/sandbox/contentful/src/index.tsx b/users/wpcarro/website/sandbox/contentful/src/index.tsx
new file mode 100644
index 0000000000..dc28dc4a9c
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/src/index.tsx
@@ -0,0 +1,12 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import App from "./App";
+import { Provider } from "react-redux";
+import store from "./store";
+
+ReactDOM.render(
+  <Provider store={store}>
+    <App />
+  </Provider>,
+  document.getElementById("mount")
+);
diff --git a/users/wpcarro/website/sandbox/contentful/src/store.ts b/users/wpcarro/website/sandbox/contentful/src/store.ts
new file mode 100644
index 0000000000..b02053d302
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/src/store.ts
@@ -0,0 +1,36 @@
+import { createSlice, configureStore, PayloadAction } from "@reduxjs/toolkit";
+import { useSelector, TypedUseSelectorHook } from "react-redux";
+
+export interface Book {
+  title: string;
+  author: string;
+  // TODO(wpcarro): Prefer datetime type here.
+  publicationDate: string;
+}
+
+export interface State {
+  isLoading: boolean;
+  books: Book[];
+}
+
+const initialState: State = {
+  isLoading: true,
+  books: [],
+};
+
+export const { actions, reducer } = createSlice({
+  name: "application",
+  initialState,
+  reducers: {
+    toggleIsLoading: (state) => ({ ...state, isLoading: !state.isLoading }),
+    setBooks: (state, action) => ({ ...state, books: action.payload }),
+  },
+});
+
+/**
+ * Defining and consuming this allows us to avoid annotating State in all of our
+ * selectors.
+ */
+export const useTypedSelector: TypedUseSelectorHook<State> = useSelector;
+
+export default configureStore({ reducer });
diff --git a/users/wpcarro/website/sandbox/contentful/tailwind.config.js b/users/wpcarro/website/sandbox/contentful/tailwind.config.js
new file mode 100644
index 0000000000..3da6fa0dc7
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/tailwind.config.js
@@ -0,0 +1,7 @@
+module.exports = {
+  theme: {
+    extend: {},
+  },
+  variants: {},
+  plugins: [],
+};
diff --git a/users/wpcarro/website/sandbox/contentful/tsconfig.json b/users/wpcarro/website/sandbox/contentful/tsconfig.json
new file mode 100644
index 0000000000..fe07ec1da4
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/tsconfig.json
@@ -0,0 +1,19 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "lib": ["dom", "dom.iterable", "esnext"],
+    "allowJs": true,
+    "skipLibCheck": true,
+    "esModuleInterop": true,
+    "allowSyntheticDefaultImports": true,
+    "strict": true,
+    "forceConsistentCasingInFileNames": true,
+    "module": "esnext",
+    "moduleResolution": "node",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "noEmit": true,
+    "jsx": "react"
+  },
+  "include": ["src/**/*"]
+}
diff --git a/users/wpcarro/website/sandbox/contentful/yarn.lock b/users/wpcarro/website/sandbox/contentful/yarn.lock
new file mode 100644
index 0000000000..45fdea32b7
--- /dev/null
+++ b/users/wpcarro/website/sandbox/contentful/yarn.lock
@@ -0,0 +1,5717 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
+  integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==
+  dependencies:
+    "@babel/highlight" "^7.8.3"
+
+"@babel/compat-data@^7.8.6", "@babel/compat-data@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.0.tgz#04815556fc90b0c174abd2c0c1bb966faa036a6c"
+  integrity sha512-zeFQrr+284Ekvd9e7KAX954LkapWiOmQtsfHirhxqfdlX6MEC32iRE+pqUGlYIBchdevaCwvzxWGSy/YBNI85g==
+  dependencies:
+    browserslist "^4.9.1"
+    invariant "^2.2.4"
+    semver "^5.5.0"
+
+"@babel/core@^7.4.4":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e"
+  integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==
+  dependencies:
+    "@babel/code-frame" "^7.8.3"
+    "@babel/generator" "^7.9.0"
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helpers" "^7.9.0"
+    "@babel/parser" "^7.9.0"
+    "@babel/template" "^7.8.6"
+    "@babel/traverse" "^7.9.0"
+    "@babel/types" "^7.9.0"
+    convert-source-map "^1.7.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.1"
+    json5 "^2.1.2"
+    lodash "^4.17.13"
+    resolve "^1.3.2"
+    semver "^5.4.1"
+    source-map "^0.5.0"
+
+"@babel/generator@^7.4.4", "@babel/generator@^7.9.0":
+  version "7.9.3"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.3.tgz#7c8b2956c6f68b3ab732bd16305916fbba521d94"
+  integrity sha512-RpxM252EYsz9qLUIq6F7YJyK1sv0wWDBFuztfDGWaQKzHjqDHysxSiRUpA/X9jmfqo+WzkAVKFaUily5h+gDCQ==
+  dependencies:
+    "@babel/types" "^7.9.0"
+    jsesc "^2.5.1"
+    lodash "^4.17.13"
+    source-map "^0.5.0"
+
+"@babel/helper-annotate-as-pure@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee"
+  integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503"
+  integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==
+  dependencies:
+    "@babel/helper-explode-assignable-expression" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-builder-react-jsx-experimental@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.9.0.tgz#066d80262ade488f9c1b1823ce5db88a4cedaa43"
+  integrity sha512-3xJEiyuYU4Q/Ar9BsHisgdxZsRlsShMe90URZ0e6przL26CCs8NJbDoxH94kKT17PcxlMhsCAwZd90evCo26VQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-module-imports" "^7.8.3"
+    "@babel/types" "^7.9.0"
+
+"@babel/helper-builder-react-jsx@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.9.0.tgz#16bf391990b57732700a3278d4d9a81231ea8d32"
+  integrity sha512-weiIo4gaoGgnhff54GQ3P5wsUQmnSwpkvU0r6ZHq6TzoSzKy4JxHEgnxNytaKbov2a9z/CVNyzliuCOUPEX3Jw==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/types" "^7.9.0"
+
+"@babel/helper-compilation-targets@^7.8.7":
+  version "7.8.7"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz#dac1eea159c0e4bd46e309b5a1b04a66b53c1dde"
+  integrity sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==
+  dependencies:
+    "@babel/compat-data" "^7.8.6"
+    browserslist "^4.9.1"
+    invariant "^2.2.4"
+    levenary "^1.1.1"
+    semver "^5.5.0"
+
+"@babel/helper-create-regexp-features-plugin@^7.8.3", "@babel/helper-create-regexp-features-plugin@^7.8.8":
+  version "7.8.8"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz#5d84180b588f560b7864efaeea89243e58312087"
+  integrity sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-regex" "^7.8.3"
+    regexpu-core "^4.7.0"
+
+"@babel/helper-define-map@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15"
+  integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==
+  dependencies:
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/types" "^7.8.3"
+    lodash "^4.17.13"
+
+"@babel/helper-explode-assignable-expression@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982"
+  integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==
+  dependencies:
+    "@babel/traverse" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-function-name@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca"
+  integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.8.3"
+    "@babel/template" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-get-function-arity@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5"
+  integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-hoist-variables@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134"
+  integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-member-expression-to-functions@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c"
+  integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-module-imports@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498"
+  integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-module-transforms@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5"
+  integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==
+  dependencies:
+    "@babel/helper-module-imports" "^7.8.3"
+    "@babel/helper-replace-supers" "^7.8.6"
+    "@babel/helper-simple-access" "^7.8.3"
+    "@babel/helper-split-export-declaration" "^7.8.3"
+    "@babel/template" "^7.8.6"
+    "@babel/types" "^7.9.0"
+    lodash "^4.17.13"
+
+"@babel/helper-optimise-call-expression@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9"
+  integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
+  integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==
+
+"@babel/helper-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965"
+  integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==
+  dependencies:
+    lodash "^4.17.13"
+
+"@babel/helper-remap-async-to-generator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86"
+  integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-wrap-function" "^7.8.3"
+    "@babel/template" "^7.8.3"
+    "@babel/traverse" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-replace-supers@^7.8.3", "@babel/helper-replace-supers@^7.8.6":
+  version "7.8.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8"
+  integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.8.3"
+    "@babel/helper-optimise-call-expression" "^7.8.3"
+    "@babel/traverse" "^7.8.6"
+    "@babel/types" "^7.8.6"
+
+"@babel/helper-simple-access@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae"
+  integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==
+  dependencies:
+    "@babel/template" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-split-export-declaration@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9"
+  integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==
+  dependencies:
+    "@babel/types" "^7.8.3"
+
+"@babel/helper-validator-identifier@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.0.tgz#ad53562a7fc29b3b9a91bbf7d10397fd146346ed"
+  integrity sha512-6G8bQKjOh+of4PV/ThDm/rRqlU7+IGoJuofpagU5GlEl29Vv0RGqqt86ZGRV8ZuSOY3o+8yXl5y782SMcG7SHw==
+
+"@babel/helper-wrap-function@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610"
+  integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==
+  dependencies:
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/template" "^7.8.3"
+    "@babel/traverse" "^7.8.3"
+    "@babel/types" "^7.8.3"
+
+"@babel/helpers@^7.9.0":
+  version "7.9.2"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f"
+  integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==
+  dependencies:
+    "@babel/template" "^7.8.3"
+    "@babel/traverse" "^7.9.0"
+    "@babel/types" "^7.9.0"
+
+"@babel/highlight@^7.8.3":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
+  integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.9.0"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
+"@babel/parser@^7.4.4", "@babel/parser@^7.8.6", "@babel/parser@^7.9.0":
+  version "7.9.3"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.3.tgz#043a5fc2ad8b7ea9facddc4e802a1f0f25da7255"
+  integrity sha512-E6SpIDJZ0cZAKoCNk+qSDd0ChfTnpiJN9FfNf3RZ20dzwA2vL2oq5IX1XTVT+4vDmRlta2nGk5HGMMskJAR+4A==
+
+"@babel/plugin-proposal-async-generator-functions@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f"
+  integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-remap-async-to-generator" "^7.8.3"
+    "@babel/plugin-syntax-async-generators" "^7.8.0"
+
+"@babel/plugin-proposal-dynamic-import@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054"
+  integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+
+"@babel/plugin-proposal-json-strings@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b"
+  integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-json-strings" "^7.8.0"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2"
+  integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+
+"@babel/plugin-proposal-numeric-separator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.8.3.tgz#5d6769409699ec9b3b68684cd8116cedff93bad8"
+  integrity sha512-jWioO1s6R/R+wEHizfaScNsAx+xKgwTLNXSh7tTC4Usj3ItsPEhYkEpU4h+lpnBwq7NBVOJXfO6cRFYcX69JUQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-numeric-separator" "^7.8.3"
+
+"@babel/plugin-proposal-object-rest-spread@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.9.0.tgz#a28993699fc13df165995362693962ba6b061d6f"
+  integrity sha512-UgqBv6bjq4fDb8uku9f+wcm1J7YxJ5nT7WO/jBr0cl0PLKb7t1O6RNR1kZbjgx2LQtsDI9hwoQVmn0yhXeQyow==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9"
+  integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+
+"@babel/plugin-proposal-optional-chaining@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.9.0.tgz#31db16b154c39d6b8a645292472b98394c292a58"
+  integrity sha512-NDn5tu3tcv4W30jNhmc2hyD5c56G6cXx4TesJubhxrJeCvuuMpttxr0OnNCqbZGhFjLrg+NIhxxC+BK5F6yS3w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.4.4", "@babel/plugin-proposal-unicode-property-regex@^7.8.3":
+  version "7.8.8"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz#ee3a95e90cdc04fe8cd92ec3279fa017d68a0d1d"
+  integrity sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.8.8"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-async-generators@^7.8.0":
+  version "7.8.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
+  integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-dynamic-import@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
+  integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-flow@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.8.3.tgz#f2c883bd61a6316f2c89380ae5122f923ba4527f"
+  integrity sha512-innAx3bUbA0KSYj2E2MNFSn9hiCeowOFLxlsuhXzw8hMQnzkDomUr9QCD7E9VF60NmnG1sNTuuv6Qf4f8INYsg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-json-strings@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
+  integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-jsx@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz#521b06c83c40480f1e58b4fd33b92eceb1d6ea94"
+  integrity sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+  integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.8.0", "@babel/plugin-syntax-numeric-separator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f"
+  integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-object-rest-spread@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
+  integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
+  integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.0":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+  integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-top-level-await@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391"
+  integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-arrow-functions@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6"
+  integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-async-to-generator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086"
+  integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==
+  dependencies:
+    "@babel/helper-module-imports" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-remap-async-to-generator" "^7.8.3"
+
+"@babel/plugin-transform-block-scoped-functions@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3"
+  integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-block-scoping@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a"
+  integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    lodash "^4.17.13"
+
+"@babel/plugin-transform-classes@^7.9.0":
+  version "7.9.2"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.9.2.tgz#8603fc3cc449e31fdbdbc257f67717536a11af8d"
+  integrity sha512-TC2p3bPzsfvSsqBZo0kJnuelnoK9O3welkUpqSqBQuBF6R5MN2rysopri8kNvtlGIb2jmUO7i15IooAZJjZuMQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-define-map" "^7.8.3"
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/helper-optimise-call-expression" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-replace-supers" "^7.8.6"
+    "@babel/helper-split-export-declaration" "^7.8.3"
+    globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b"
+  integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-destructuring@^7.8.3":
+  version "7.8.8"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz#fadb2bc8e90ccaf5658de6f8d4d22ff6272a2f4b"
+  integrity sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-dotall-regex@^7.4.4", "@babel/plugin-transform-dotall-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e"
+  integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-duplicate-keys@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1"
+  integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-exponentiation-operator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7"
+  integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==
+  dependencies:
+    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-flow-strip-types@^7.4.4":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.9.0.tgz#8a3538aa40434e000b8f44a3c5c9ac7229bd2392"
+  integrity sha512-7Qfg0lKQhEHs93FChxVLAvhBshOPQDtJUTVHr/ZwQNRccCm4O9D79r9tVSoV8iNwjP1YgfD+e/fgHcPkN1qEQg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-flow" "^7.8.3"
+
+"@babel/plugin-transform-for-of@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.9.0.tgz#0f260e27d3e29cd1bb3128da5e76c761aa6c108e"
+  integrity sha512-lTAnWOpMwOXpyDx06N+ywmF3jNbafZEqZ96CGYabxHrxNX8l5ny7dt4bK/rGwAh9utyP2b2Hv7PlZh1AAS54FQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-function-name@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b"
+  integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==
+  dependencies:
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-literals@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1"
+  integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-member-expression-literals@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410"
+  integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-modules-amd@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.9.0.tgz#19755ee721912cf5bb04c07d50280af3484efef4"
+  integrity sha512-vZgDDF003B14O8zJy0XXLnPH4sg+9X5hFBBGN1V+B2rgrB+J2xIypSN6Rk9imB2hSTHQi5OHLrFWsZab1GMk+Q==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-commonjs@^7.4.4", "@babel/plugin-transform-modules-commonjs@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.9.0.tgz#e3e72f4cbc9b4a260e30be0ea59bdf5a39748940"
+  integrity sha512-qzlCrLnKqio4SlgJ6FMMLBe4bySNis8DFn1VkGmOcxG9gqEyPIOzeQrA//u0HAKrWpJlpZbZMPB1n/OPa4+n8g==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-simple-access" "^7.8.3"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-systemjs@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.9.0.tgz#e9fd46a296fc91e009b64e07ddaa86d6f0edeb90"
+  integrity sha512-FsiAv/nao/ud2ZWy4wFacoLOm5uxl0ExSQ7ErvP7jpoihLR6Cq90ilOFyX9UXct3rbtKsAiZ9kFt5XGfPe/5SQ==
+  dependencies:
+    "@babel/helper-hoist-variables" "^7.8.3"
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    babel-plugin-dynamic-import-node "^2.3.0"
+
+"@babel/plugin-transform-modules-umd@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.9.0.tgz#e909acae276fec280f9b821a5f38e1f08b480697"
+  integrity sha512-uTWkXkIVtg/JGRSIABdBoMsoIeoHQHPTL0Y2E7xf5Oj7sLqwVsNXOkNk0VJc7vF0IMBsPeikHxFjGe+qmwPtTQ==
+  dependencies:
+    "@babel/helper-module-transforms" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c"
+  integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.8.3"
+
+"@babel/plugin-transform-new-target@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43"
+  integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-object-super@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725"
+  integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-replace-supers" "^7.8.3"
+
+"@babel/plugin-transform-parameters@^7.8.7":
+  version "7.9.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.9.3.tgz#3028d0cc20ddc733166c6e9c8534559cee09f54a"
+  integrity sha512-fzrQFQhp7mIhOzmOtPiKffvCYQSK10NR8t6BBz2yPbeUHb9OLW8RZGtgDRBn8z2hGcwvKDL3vC7ojPTLNxmqEg==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-property-literals@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263"
+  integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-react-jsx@^7.0.0":
+  version "7.9.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.9.1.tgz#d03af29396a6dc51bfa24eefd8005a9fd381152a"
+  integrity sha512-+xIZ6fPoix7h57CNO/ZeYADchg1tFyX9NDsnmNFFua8e1JNPln156mzS+8AQe1On2X2GLlANHJWHIXbMCqWDkQ==
+  dependencies:
+    "@babel/helper-builder-react-jsx" "^7.9.0"
+    "@babel/helper-builder-react-jsx-experimental" "^7.9.0"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-syntax-jsx" "^7.8.3"
+
+"@babel/plugin-transform-regenerator@^7.8.7":
+  version "7.8.7"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8"
+  integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==
+  dependencies:
+    regenerator-transform "^0.14.2"
+
+"@babel/plugin-transform-reserved-words@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5"
+  integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-shorthand-properties@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8"
+  integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-spread@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8"
+  integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-sticky-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100"
+  integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/helper-regex" "^7.8.3"
+
+"@babel/plugin-transform-template-literals@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80"
+  integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-typeof-symbol@^7.8.4":
+  version "7.8.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz#ede4062315ce0aaf8a657a920858f1a2f35fc412"
+  integrity sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-transform-unicode-regex@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad"
+  integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/preset-env@^7.4.4":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.9.0.tgz#a5fc42480e950ae8f5d9f8f2bbc03f52722df3a8"
+  integrity sha512-712DeRXT6dyKAM/FMbQTV/FvRCms2hPCx+3weRjZ8iQVQWZejWWk1wwG6ViWMyqb/ouBbGOl5b6aCk0+j1NmsQ==
+  dependencies:
+    "@babel/compat-data" "^7.9.0"
+    "@babel/helper-compilation-targets" "^7.8.7"
+    "@babel/helper-module-imports" "^7.8.3"
+    "@babel/helper-plugin-utils" "^7.8.3"
+    "@babel/plugin-proposal-async-generator-functions" "^7.8.3"
+    "@babel/plugin-proposal-dynamic-import" "^7.8.3"
+    "@babel/plugin-proposal-json-strings" "^7.8.3"
+    "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3"
+    "@babel/plugin-proposal-numeric-separator" "^7.8.3"
+    "@babel/plugin-proposal-object-rest-spread" "^7.9.0"
+    "@babel/plugin-proposal-optional-catch-binding" "^7.8.3"
+    "@babel/plugin-proposal-optional-chaining" "^7.9.0"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.8.3"
+    "@babel/plugin-syntax-async-generators" "^7.8.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+    "@babel/plugin-syntax-json-strings" "^7.8.0"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+    "@babel/plugin-syntax-numeric-separator" "^7.8.0"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.0"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.0"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+    "@babel/plugin-syntax-top-level-await" "^7.8.3"
+    "@babel/plugin-transform-arrow-functions" "^7.8.3"
+    "@babel/plugin-transform-async-to-generator" "^7.8.3"
+    "@babel/plugin-transform-block-scoped-functions" "^7.8.3"
+    "@babel/plugin-transform-block-scoping" "^7.8.3"
+    "@babel/plugin-transform-classes" "^7.9.0"
+    "@babel/plugin-transform-computed-properties" "^7.8.3"
+    "@babel/plugin-transform-destructuring" "^7.8.3"
+    "@babel/plugin-transform-dotall-regex" "^7.8.3"
+    "@babel/plugin-transform-duplicate-keys" "^7.8.3"
+    "@babel/plugin-transform-exponentiation-operator" "^7.8.3"
+    "@babel/plugin-transform-for-of" "^7.9.0"
+    "@babel/plugin-transform-function-name" "^7.8.3"
+    "@babel/plugin-transform-literals" "^7.8.3"
+    "@babel/plugin-transform-member-expression-literals" "^7.8.3"
+    "@babel/plugin-transform-modules-amd" "^7.9.0"
+    "@babel/plugin-transform-modules-commonjs" "^7.9.0"
+    "@babel/plugin-transform-modules-systemjs" "^7.9.0"
+    "@babel/plugin-transform-modules-umd" "^7.9.0"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3"
+    "@babel/plugin-transform-new-target" "^7.8.3"
+    "@babel/plugin-transform-object-super" "^7.8.3"
+    "@babel/plugin-transform-parameters" "^7.8.7"
+    "@babel/plugin-transform-property-literals" "^7.8.3"
+    "@babel/plugin-transform-regenerator" "^7.8.7"
+    "@babel/plugin-transform-reserved-words" "^7.8.3"
+    "@babel/plugin-transform-shorthand-properties" "^7.8.3"
+    "@babel/plugin-transform-spread" "^7.8.3"
+    "@babel/plugin-transform-sticky-regex" "^7.8.3"
+    "@babel/plugin-transform-template-literals" "^7.8.3"
+    "@babel/plugin-transform-typeof-symbol" "^7.8.4"
+    "@babel/plugin-transform-unicode-regex" "^7.8.3"
+    "@babel/preset-modules" "^0.1.3"
+    "@babel/types" "^7.9.0"
+    browserslist "^4.9.1"
+    core-js-compat "^3.6.2"
+    invariant "^2.2.2"
+    levenary "^1.1.1"
+    semver "^5.5.0"
+
+"@babel/preset-modules@^0.1.3":
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.3.tgz#13242b53b5ef8c883c3cf7dddd55b36ce80fbc72"
+  integrity sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
+    "@babel/plugin-transform-dotall-regex" "^7.4.4"
+    "@babel/types" "^7.4.4"
+    esutils "^2.0.2"
+
+"@babel/runtime@^7.1.2", "@babel/runtime@^7.4.0", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4":
+  version "7.9.2"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06"
+  integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
+"@babel/template@^7.4.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
+  version "7.8.6"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
+  integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==
+  dependencies:
+    "@babel/code-frame" "^7.8.3"
+    "@babel/parser" "^7.8.6"
+    "@babel/types" "^7.8.6"
+
+"@babel/traverse@^7.4.4", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.0.tgz#d3882c2830e513f4fe4cec9fe76ea1cc78747892"
+  integrity sha512-jAZQj0+kn4WTHO5dUZkZKhbFrqZE7K5LAQ5JysMnmvGij+wOdr+8lWqPeW0BcF4wFwrEXXtdGO7wcV6YPJcf3w==
+  dependencies:
+    "@babel/code-frame" "^7.8.3"
+    "@babel/generator" "^7.9.0"
+    "@babel/helper-function-name" "^7.8.3"
+    "@babel/helper-split-export-declaration" "^7.8.3"
+    "@babel/parser" "^7.9.0"
+    "@babel/types" "^7.9.0"
+    debug "^4.1.0"
+    globals "^11.1.0"
+    lodash "^4.17.13"
+
+"@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0":
+  version "7.9.0"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.0.tgz#00b064c3df83ad32b2dbf5ff07312b15c7f1efb5"
+  integrity sha512-BS9JKfXkzzJl8RluW4JGknzpiUV7ZrvTayM6yfqLTVBEnFtyowVIOu6rqxRd5cVO6yGoWf4T8u8dgK9oB+GCng==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.9.0"
+    lodash "^4.17.13"
+    to-fast-properties "^2.0.0"
+
+"@iarna/toml@^2.2.0":
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.3.tgz#f060bf6eaafae4d56a7dac618980838b0696e2ab"
+  integrity sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==
+
+"@mrmlnc/readdir-enhanced@^2.2.1":
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
+  integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==
+  dependencies:
+    call-me-maybe "^1.0.1"
+    glob-to-regexp "^0.3.0"
+
+"@nodelib/fs.stat@^1.1.2":
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
+  integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
+
+"@parcel/fs@^1.11.0":
+  version "1.11.0"
+  resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-1.11.0.tgz#fb8a2be038c454ad46a50dc0554c1805f13535cd"
+  integrity sha512-86RyEqULbbVoeo8OLcv+LQ1Vq2PKBAvWTU9fCgALxuCTbbs5Ppcvll4Vr+Ko1AnmMzja/k++SzNAwJfeQXVlpA==
+  dependencies:
+    "@parcel/utils" "^1.11.0"
+    mkdirp "^0.5.1"
+    rimraf "^2.6.2"
+
+"@parcel/logger@^1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-1.11.1.tgz#c55b0744bcbe84ebc291155627f0ec406a23e2e6"
+  integrity sha512-9NF3M6UVeP2udOBDILuoEHd8VrF4vQqoWHEafymO1pfSoOMfxrSJZw1MfyAAIUN/IFp9qjcpDCUbDZB+ioVevA==
+  dependencies:
+    "@parcel/workers" "^1.11.0"
+    chalk "^2.1.0"
+    grapheme-breaker "^0.3.2"
+    ora "^2.1.0"
+    strip-ansi "^4.0.0"
+
+"@parcel/utils@^1.11.0":
+  version "1.11.0"
+  resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-1.11.0.tgz#539e08fff8af3b26eca11302be80b522674b51ea"
+  integrity sha512-cA3p4jTlaMeOtAKR/6AadanOPvKeg8VwgnHhOyfi0yClD0TZS/hi9xu12w4EzA/8NtHu0g6o4RDfcNjqN8l1AQ==
+
+"@parcel/watcher@^1.12.1":
+  version "1.12.1"
+  resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-1.12.1.tgz#b98b3df309fcab93451b5583fc38e40826696dad"
+  integrity sha512-od+uCtCxC/KoNQAIE1vWx1YTyKYY+7CTrxBJPRh3cDWw/C0tCtlBMVlrbplscGoEpt6B27KhJDCv82PBxOERNA==
+  dependencies:
+    "@parcel/utils" "^1.11.0"
+    chokidar "^2.1.5"
+
+"@parcel/workers@^1.11.0":
+  version "1.11.0"
+  resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-1.11.0.tgz#7b8dcf992806f4ad2b6cecf629839c41c2336c59"
+  integrity sha512-USSjRAAQYsZFlv43FUPdD+jEGML5/8oLF0rUzPQTtK4q9kvaXr49F5ZplyLz5lox78cLZ0TxN2bIDQ1xhOkulQ==
+  dependencies:
+    "@parcel/utils" "^1.11.0"
+    physical-cpu-count "^2.0.0"
+
+"@reduxjs/toolkit@^1.2.5":
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.2.5.tgz#149aa62da12a18a67a30495cb63fd897003f2272"
+  integrity sha512-/OWoW5mniUXAomw4+3ZhhWodcs1/SRvK2HKyxLXdW6vKgmJhiBiSHe/huHARlKWujEmGaJrkafx548GE494bCQ==
+  dependencies:
+    immer "^4.0.1"
+    redux "^4.0.0"
+    redux-devtools-extension "^2.13.8"
+    redux-immutable-state-invariant "^2.1.0"
+    redux-thunk "^2.3.0"
+    reselect "^4.0.0"
+
+"@types/color-name@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
+  integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
+
+"@types/history@*":
+  version "4.7.5"
+  resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.5.tgz#527d20ef68571a4af02ed74350164e7a67544860"
+  integrity sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw==
+
+"@types/hoist-non-react-statics@^3.3.0":
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
+  integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
+  dependencies:
+    "@types/react" "*"
+    hoist-non-react-statics "^3.3.0"
+
+"@types/node@^13.9.3":
+  version "13.9.3"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.3.tgz#6356df2647de9eac569f9a52eda3480fa9e70b4d"
+  integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==
+
+"@types/prop-types@*":
+  version "15.7.3"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
+  integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
+
+"@types/q@^1.5.1":
+  version "1.5.2"
+  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
+  integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
+
+"@types/react-dom@^16.9.5":
+  version "16.9.5"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7"
+  integrity sha512-BX6RQ8s9D+2/gDhxrj8OW+YD4R+8hj7FEM/OJHGNR0KipE1h1mSsf39YeyC81qafkq+N3rU3h3RFbLSwE5VqUg==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react-redux@^7.1.7":
+  version "7.1.7"
+  resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.7.tgz#12a0c529aba660696947384a059c5c6e08185c7a"
+  integrity sha512-U+WrzeFfI83+evZE2dkZ/oF/1vjIYgqrb5dGgedkqVV8HEfDFujNgWCwHL89TDuWKb47U0nTBT6PLGq4IIogWg==
+  dependencies:
+    "@types/hoist-non-react-statics" "^3.3.0"
+    "@types/react" "*"
+    hoist-non-react-statics "^3.3.0"
+    redux "^4.0.0"
+
+"@types/react-router-dom@^5.1.3":
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.3.tgz#b5d28e7850bd274d944c0fbbe5d57e6b30d71196"
+  integrity sha512-pCq7AkOvjE65jkGS5fQwQhvUp4+4PVD9g39gXLZViP2UqFiFzsEpB3PKf0O6mdbKsewSK8N14/eegisa/0CwnA==
+  dependencies:
+    "@types/history" "*"
+    "@types/react" "*"
+    "@types/react-router" "*"
+
+"@types/react-router@*":
+  version "5.1.4"
+  resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.4.tgz#7d70bd905543cb6bcbdcc6bd98902332054f31a6"
+  integrity sha512-PZtnBuyfL07sqCJvGg3z+0+kt6fobc/xmle08jBiezLS8FrmGeiGkJnuxL/8Zgy9L83ypUhniV5atZn/L8n9MQ==
+  dependencies:
+    "@types/history" "*"
+    "@types/react" "*"
+
+"@types/react@*":
+  version "16.9.25"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.25.tgz#6ae2159b40138c792058a23c3c04fd3db49e929e"
+  integrity sha512-Dlj2V72cfYLPNscIG3/SMUOzhzj7GK3bpSrfefwt2YT9GLynvLCCZjbhyF6VsT0q0+aRACRX03TDJGb7cA0cqg==
+  dependencies:
+    "@types/prop-types" "*"
+    csstype "^2.2.0"
+
+abab@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a"
+  integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==
+
+acorn-globals@^4.3.0:
+  version "4.3.4"
+  resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7"
+  integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==
+  dependencies:
+    acorn "^6.0.1"
+    acorn-walk "^6.0.1"
+
+acorn-node@^1.6.1:
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
+  integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==
+  dependencies:
+    acorn "^7.0.0"
+    acorn-walk "^7.0.0"
+    xtend "^4.0.2"
+
+acorn-walk@^6.0.1:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
+  integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
+
+acorn-walk@^7.0.0:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e"
+  integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==
+
+acorn@^6.0.1, acorn@^6.0.4:
+  version "6.4.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
+  integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
+
+acorn@^7.0.0, acorn@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
+  integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==
+
+ajv@^6.5.5:
+  version "6.12.0"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
+  integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
+alphanum-sort@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
+  integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
+
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
+ansi-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+  integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+ansi-regex@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
+  integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+
+ansi-styles@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
+  integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
+
+ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+ansi-styles@^4.1.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
+  integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
+  dependencies:
+    "@types/color-name" "^1.1.1"
+    color-convert "^2.0.1"
+
+ansi-to-html@^0.6.4:
+  version "0.6.14"
+  resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.14.tgz#65fe6d08bba5dd9db33f44a20aec331e0010dad8"
+  integrity sha512-7ZslfB1+EnFSDO5Ju+ue5Y6It19DRnZXWv8jrGHgIlPna5Mh4jz7BV5jCbQneXNFurQcKoolaaAjHtgSBfOIuA==
+  dependencies:
+    entities "^1.1.2"
+
+anymatch@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
+  integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
+  dependencies:
+    micromatch "^3.1.4"
+    normalize-path "^2.1.1"
+
+argparse@^1.0.7:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+  dependencies:
+    sprintf-js "~1.0.2"
+
+arr-diff@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+  integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+  integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+  integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-equal@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
+  integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
+
+array-unique@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+  integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+asn1.js@^4.0.0:
+  version "4.10.1"
+  resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
+  integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==
+  dependencies:
+    bn.js "^4.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+asn1@~0.2.3:
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
+  integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
+  dependencies:
+    safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+  integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
+
+assert@^1.1.1:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb"
+  integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==
+  dependencies:
+    object-assign "^4.1.1"
+    util "0.10.3"
+
+assign-symbols@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+  integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+async-each@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
+  integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
+
+async-limiter@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
+  integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+
+atob@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+  integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+autoprefixer@^9.4.5:
+  version "9.7.4"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378"
+  integrity sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==
+  dependencies:
+    browserslist "^4.8.3"
+    caniuse-lite "^1.0.30001020"
+    chalk "^2.4.2"
+    normalize-range "^0.1.2"
+    num2fraction "^1.2.2"
+    postcss "^7.0.26"
+    postcss-value-parser "^4.0.2"
+
+aws-sign2@~0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+  integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
+
+aws4@^1.8.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
+  integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
+
+axios@^0.19.1:
+  version "0.19.2"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
+  integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
+  dependencies:
+    follow-redirects "1.5.10"
+
+babel-plugin-dynamic-import-node@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f"
+  integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==
+  dependencies:
+    object.assign "^4.1.0"
+
+babel-runtime@^6.11.6, babel-runtime@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.11.0"
+
+babel-types@^6.15.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
+  integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
+  dependencies:
+    babel-runtime "^6.26.0"
+    esutils "^2.0.2"
+    lodash "^4.17.4"
+    to-fast-properties "^1.0.3"
+
+babylon-walk@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/babylon-walk/-/babylon-walk-1.0.2.tgz#3b15a5ddbb482a78b4ce9c01c8ba181702d9d6ce"
+  integrity sha1-OxWl3btIKni0zpwByLoYFwLZ1s4=
+  dependencies:
+    babel-runtime "^6.11.6"
+    babel-types "^6.15.0"
+    lodash.clone "^4.5.0"
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base64-js@^1.0.2:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
+  integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
+
+base@^0.11.1:
+  version "0.11.2"
+  resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+  integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+  dependencies:
+    cache-base "^1.0.1"
+    class-utils "^0.3.5"
+    component-emitter "^1.2.1"
+    define-property "^1.0.0"
+    isobject "^3.0.1"
+    mixin-deep "^1.2.0"
+    pascalcase "^0.1.1"
+
+bcrypt-pbkdf@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+  integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
+  dependencies:
+    tweetnacl "^0.14.3"
+
+binary-extensions@^1.0.0:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
+  integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
+
+bindings@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
+  integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
+  dependencies:
+    file-uri-to-path "1.0.0"
+
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
+  version "4.11.8"
+  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
+  integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==
+
+boolbase@^1.0.0, boolbase@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+braces@^2.3.1, braces@^2.3.2:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
+  integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+  dependencies:
+    arr-flatten "^1.1.0"
+    array-unique "^0.3.2"
+    extend-shallow "^2.0.1"
+    fill-range "^4.0.0"
+    isobject "^3.0.1"
+    repeat-element "^1.1.2"
+    snapdragon "^0.8.1"
+    snapdragon-node "^2.0.1"
+    split-string "^3.0.2"
+    to-regex "^3.0.1"
+
+brfs@^1.2.0:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/brfs/-/brfs-1.6.1.tgz#b78ce2336d818e25eea04a0947cba6d4fb8849c3"
+  integrity sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==
+  dependencies:
+    quote-stream "^1.0.1"
+    resolve "^1.1.5"
+    static-module "^2.2.0"
+    through2 "^2.0.0"
+
+brorand@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
+  integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
+
+browser-process-hrtime@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
+  integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
+
+browserify-aes@^1.0.0, browserify-aes@^1.0.4:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
+  integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==
+  dependencies:
+    buffer-xor "^1.0.3"
+    cipher-base "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.3"
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+browserify-cipher@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
+  integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
+  dependencies:
+    browserify-aes "^1.0.4"
+    browserify-des "^1.0.0"
+    evp_bytestokey "^1.0.0"
+
+browserify-des@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c"
+  integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==
+  dependencies:
+    cipher-base "^1.0.1"
+    des.js "^1.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+browserify-rsa@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524"
+  integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=
+  dependencies:
+    bn.js "^4.1.0"
+    randombytes "^2.0.1"
+
+browserify-sign@^4.0.0:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
+  integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=
+  dependencies:
+    bn.js "^4.1.1"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.2"
+    elliptic "^6.0.0"
+    inherits "^2.0.1"
+    parse-asn1 "^5.0.0"
+
+browserify-zlib@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
+  integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==
+  dependencies:
+    pako "~1.0.5"
+
+browserslist@^4.0.0, browserslist@^4.1.0, browserslist@^4.8.3, browserslist@^4.9.1:
+  version "4.11.0"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.11.0.tgz#aef4357b10a8abda00f97aac7cd587b2082ba1ad"
+  integrity sha512-WqEC7Yr5wUH5sg6ruR++v2SGOQYpyUdYYd4tZoAq1F7y+QXoLoYGXVbxhtaIqWmAJjtNTRjVD3HuJc1OXTel2A==
+  dependencies:
+    caniuse-lite "^1.0.30001035"
+    electron-to-chromium "^1.3.380"
+    node-releases "^1.1.52"
+    pkg-up "^3.1.0"
+
+buffer-equal@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b"
+  integrity sha1-kbx0sR6kBbyRa8aqkI+q+ltKrEs=
+
+buffer-from@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+  integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
+buffer-xor@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
+  integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
+
+buffer@^4.3.0:
+  version "4.9.2"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
+  integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
+  dependencies:
+    base64-js "^1.0.2"
+    ieee754 "^1.1.4"
+    isarray "^1.0.0"
+
+builtin-status-codes@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
+  integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
+
+bytes@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
+  integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
+
+cache-base@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+  integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+  dependencies:
+    collection-visit "^1.0.0"
+    component-emitter "^1.2.1"
+    get-value "^2.0.6"
+    has-value "^1.0.0"
+    isobject "^3.0.1"
+    set-value "^2.0.0"
+    to-object-path "^0.3.0"
+    union-value "^1.0.0"
+    unset-value "^1.0.0"
+
+call-me-maybe@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
+  integrity sha1-JtII6onje1y95gJQoV8DHBak1ms=
+
+caller-callsite@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134"
+  integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
+  dependencies:
+    callsites "^2.0.0"
+
+caller-path@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4"
+  integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
+  dependencies:
+    caller-callsite "^2.0.0"
+
+callsites@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
+  integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+
+camelcase-css@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
+  integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
+
+camelcase@^5.0.0:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+caniuse-api@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
+  integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==
+  dependencies:
+    browserslist "^4.0.0"
+    caniuse-lite "^1.0.0"
+    lodash.memoize "^4.1.2"
+    lodash.uniq "^4.5.0"
+
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001035:
+  version "1.0.30001036"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001036.tgz#930ea5272010d8bf190d859159d757c0b398caf0"
+  integrity sha512-jU8CIFIj2oR7r4W+5AKcsvWNVIb6Q6OZE3UsrXrZBHFtreT4YgTeOJtTucp+zSedEpTi3L5wASSP0LYIE3if6w==
+
+caseless@~0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+  integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
+
+chalk@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
+  integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
+  dependencies:
+    ansi-styles "^2.2.1"
+    escape-string-regexp "^1.0.2"
+    has-ansi "^2.0.0"
+    strip-ansi "^3.0.0"
+    supports-color "^2.0.0"
+
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
+  integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chokidar@^2.1.5:
+  version "2.1.8"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
+  integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==
+  dependencies:
+    anymatch "^2.0.0"
+    async-each "^1.0.1"
+    braces "^2.3.2"
+    glob-parent "^3.1.0"
+    inherits "^2.0.3"
+    is-binary-path "^1.0.0"
+    is-glob "^4.0.0"
+    normalize-path "^3.0.0"
+    path-is-absolute "^1.0.0"
+    readdirp "^2.2.1"
+    upath "^1.1.1"
+  optionalDependencies:
+    fsevents "^1.2.7"
+
+cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
+  integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+class-utils@^0.3.5:
+  version "0.3.6"
+  resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+  integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+  dependencies:
+    arr-union "^3.1.0"
+    define-property "^0.2.5"
+    isobject "^3.0.0"
+    static-extend "^0.1.1"
+
+cli-cursor@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+  integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
+  dependencies:
+    restore-cursor "^2.0.0"
+
+cli-spinners@^1.1.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a"
+  integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg==
+
+cliui@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
+  integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
+  dependencies:
+    string-width "^3.1.0"
+    strip-ansi "^5.2.0"
+    wrap-ansi "^5.1.0"
+
+clone@^1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+  integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+
+clone@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
+  integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
+
+coa@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
+  integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
+  dependencies:
+    "@types/q" "^1.5.1"
+    chalk "^2.4.1"
+    q "^1.1.2"
+
+collection-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+  integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+  dependencies:
+    map-visit "^1.0.0"
+    object-visit "^1.0.0"
+
+color-convert@^1.9.0, color-convert@^1.9.1:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+color-name@^1.0.0, color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+color-string@^1.5.2:
+  version "1.5.3"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc"
+  integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==
+  dependencies:
+    color-name "^1.0.0"
+    simple-swizzle "^0.2.2"
+
+color@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10"
+  integrity sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==
+  dependencies:
+    color-convert "^1.9.1"
+    color-string "^1.5.2"
+
+combined-stream@^1.0.6, combined-stream@~1.0.6:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
+command-exists@^1.2.6:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.8.tgz#715acefdd1223b9c9b37110a149c6392c2852291"
+  integrity sha512-PM54PkseWbiiD/mMsbvW351/u+dafwTJ0ye2qB60G1aGQP9j3xK2gmMDc+R34L3nDtx4qMCitXT75mkbkGJDLw==
+
+commander@^2.11.0, commander@^2.19.0, commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+component-emitter@^1.2.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
+  integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+concat-stream@~1.6.0:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
+  integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
+  dependencies:
+    buffer-from "^1.0.0"
+    inherits "^2.0.3"
+    readable-stream "^2.2.2"
+    typedarray "^0.0.6"
+
+console-browserify@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
+  integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
+
+constants-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
+  integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
+
+contentful-resolve-response@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/contentful-resolve-response/-/contentful-resolve-response-1.1.4.tgz#9eb656876eecb2cd00444f0adf26bd91a5ec1992"
+  integrity sha512-oFq6n6zjbiwD9/7mBa8YHPwvPM0B0D4uOgg1n/rVzpQPhCrzeIixNj6fbJAbDiJt05rZqxiY3K1Db7pPRhRaZw==
+  dependencies:
+    lodash "^4.17.4"
+
+contentful-sdk-core@^6.4.0:
+  version "6.4.0"
+  resolved "https://registry.yarnpkg.com/contentful-sdk-core/-/contentful-sdk-core-6.4.0.tgz#3b42991ae9084baf1bc5d01c61cb54441f740803"
+  integrity sha512-UvYQ/Wrt5EntlMSBbgqgvKfTBRzf6fIT2p5Wp7bsnA3/KLEiYcYd/2qhUKw4x9nfp+0G8B1s4TpDwxV0oymBiA==
+  dependencies:
+    lodash "^4.17.10"
+    qs "^6.5.2"
+
+contentful@^7.14.0:
+  version "7.14.0"
+  resolved "https://registry.yarnpkg.com/contentful/-/contentful-7.14.0.tgz#3b57287e484b8370adfd654a5196be2c2ffb9afa"
+  integrity sha512-edoiQx0AkmNqnGofmLHGVt84k2S8XuPyw2UOct/Oc3HEW0Z66osMJ4M/XA9GeByCCD5ZC7qotseBRyag/1g0iA==
+  dependencies:
+    axios "^0.19.1"
+    contentful-resolve-response "^1.1.4"
+    contentful-sdk-core "^6.4.0"
+    json-stringify-safe "^5.0.1"
+    lodash "^4.17.11"
+
+convert-source-map@^1.5.1, convert-source-map@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
+  integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
+  dependencies:
+    safe-buffer "~5.1.1"
+
+copy-descriptor@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+  integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+core-js-compat@^3.6.2:
+  version "3.6.4"
+  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17"
+  integrity sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==
+  dependencies:
+    browserslist "^4.8.3"
+    semver "7.0.0"
+
+core-js@^2.4.0, core-js@^2.6.5:
+  version "2.6.11"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
+  integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
+
+core-util-is@1.0.2, core-util-is@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+cosmiconfig@^5.0.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a"
+  integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
+  dependencies:
+    import-fresh "^2.0.0"
+    is-directory "^0.3.1"
+    js-yaml "^3.13.1"
+    parse-json "^4.0.0"
+
+create-ecdh@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff"
+  integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==
+  dependencies:
+    bn.js "^4.1.0"
+    elliptic "^6.0.0"
+
+create-hash@^1.1.0, create-hash@^1.1.2:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
+  integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
+  dependencies:
+    cipher-base "^1.0.1"
+    inherits "^2.0.1"
+    md5.js "^1.3.4"
+    ripemd160 "^2.0.1"
+    sha.js "^2.4.0"
+
+create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
+  integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
+  dependencies:
+    cipher-base "^1.0.3"
+    create-hash "^1.1.0"
+    inherits "^2.0.1"
+    ripemd160 "^2.0.0"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+cross-spawn@^6.0.4:
+  version "6.0.5"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+  integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+  dependencies:
+    nice-try "^1.0.4"
+    path-key "^2.0.1"
+    semver "^5.5.0"
+    shebang-command "^1.2.0"
+    which "^1.2.9"
+
+crypto-browserify@^3.11.0:
+  version "3.12.0"
+  resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
+  integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==
+  dependencies:
+    browserify-cipher "^1.0.0"
+    browserify-sign "^4.0.0"
+    create-ecdh "^4.0.0"
+    create-hash "^1.1.0"
+    create-hmac "^1.1.0"
+    diffie-hellman "^5.0.0"
+    inherits "^2.0.1"
+    pbkdf2 "^3.0.3"
+    public-encrypt "^4.0.0"
+    randombytes "^2.0.0"
+    randomfill "^1.0.3"
+
+css-color-names@0.0.4, css-color-names@^0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
+  integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
+
+css-declaration-sorter@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22"
+  integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==
+  dependencies:
+    postcss "^7.0.1"
+    timsort "^0.3.0"
+
+css-modules-loader-core@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/css-modules-loader-core/-/css-modules-loader-core-1.1.0.tgz#5908668294a1becd261ae0a4ce21b0b551f21d16"
+  integrity sha1-WQhmgpShvs0mGuCkziGwtVHyHRY=
+  dependencies:
+    icss-replace-symbols "1.1.0"
+    postcss "6.0.1"
+    postcss-modules-extract-imports "1.1.0"
+    postcss-modules-local-by-default "1.2.0"
+    postcss-modules-scope "1.1.0"
+    postcss-modules-values "1.3.0"
+
+css-select-base-adapter@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7"
+  integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
+
+css-select@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
+  integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^3.2.1"
+    domutils "^1.7.0"
+    nth-check "^1.0.2"
+
+css-selector-tokenizer@^0.7.0:
+  version "0.7.2"
+  resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.2.tgz#11e5e27c9a48d90284f22d45061c303d7a25ad87"
+  integrity sha512-yj856NGuAymN6r8bn8/Jl46pR+OC3eEvAhfGYDUe7YPtTPAYrSSw4oAniZ9Y8T5B92hjhwTBLUen0/vKPxf6pw==
+  dependencies:
+    cssesc "^3.0.0"
+    fastparse "^1.1.2"
+    regexpu-core "^4.6.0"
+
+css-tree@1.0.0-alpha.37:
+  version "1.0.0-alpha.37"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
+  integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
+  dependencies:
+    mdn-data "2.0.4"
+    source-map "^0.6.1"
+
+css-unit-converter@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996"
+  integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=
+
+css-what@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.2.1.tgz#f4a8f12421064621b456755e34a03a2c22df5da1"
+  integrity sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==
+
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+cssnano-preset-default@^4.0.7:
+  version "4.0.7"
+  resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76"
+  integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==
+  dependencies:
+    css-declaration-sorter "^4.0.1"
+    cssnano-util-raw-cache "^4.0.1"
+    postcss "^7.0.0"
+    postcss-calc "^7.0.1"
+    postcss-colormin "^4.0.3"
+    postcss-convert-values "^4.0.1"
+    postcss-discard-comments "^4.0.2"
+    postcss-discard-duplicates "^4.0.2"
+    postcss-discard-empty "^4.0.1"
+    postcss-discard-overridden "^4.0.1"
+    postcss-merge-longhand "^4.0.11"
+    postcss-merge-rules "^4.0.3"
+    postcss-minify-font-values "^4.0.2"
+    postcss-minify-gradients "^4.0.2"
+    postcss-minify-params "^4.0.2"
+    postcss-minify-selectors "^4.0.2"
+    postcss-normalize-charset "^4.0.1"
+    postcss-normalize-display-values "^4.0.2"
+    postcss-normalize-positions "^4.0.2"
+    postcss-normalize-repeat-style "^4.0.2"
+    postcss-normalize-string "^4.0.2"
+    postcss-normalize-timing-functions "^4.0.2"
+    postcss-normalize-unicode "^4.0.1"
+    postcss-normalize-url "^4.0.1"
+    postcss-normalize-whitespace "^4.0.2"
+    postcss-ordered-values "^4.1.2"
+    postcss-reduce-initial "^4.0.3"
+    postcss-reduce-transforms "^4.0.2"
+    postcss-svgo "^4.0.2"
+    postcss-unique-selectors "^4.0.1"
+
+cssnano-util-get-arguments@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f"
+  integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=
+
+cssnano-util-get-match@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d"
+  integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=
+
+cssnano-util-raw-cache@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282"
+  integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==
+  dependencies:
+    postcss "^7.0.0"
+
+cssnano-util-same-parent@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3"
+  integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==
+
+cssnano@^4.0.0, cssnano@^4.1.10:
+  version "4.1.10"
+  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2"
+  integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==
+  dependencies:
+    cosmiconfig "^5.0.0"
+    cssnano-preset-default "^4.0.7"
+    is-resolvable "^1.0.0"
+    postcss "^7.0.0"
+
+csso@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-4.0.2.tgz#e5f81ab3a56b8eefb7f0092ce7279329f454de3d"
+  integrity sha512-kS7/oeNVXkHWxby5tHVxlhjizRCSv8QdU7hB2FpdAibDU8FjTAolhNjKNTiLzXtUrKT6HwClE81yXwEk1309wg==
+  dependencies:
+    css-tree "1.0.0-alpha.37"
+
+cssom@0.3.x, cssom@^0.3.4:
+  version "0.3.8"
+  resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
+  integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
+
+cssstyle@^1.1.1:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1"
+  integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==
+  dependencies:
+    cssom "0.3.x"
+
+csstype@^2.2.0:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098"
+  integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==
+
+dashdash@^1.12.0:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+  integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
+  dependencies:
+    assert-plus "^1.0.0"
+
+data-urls@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe"
+  integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==
+  dependencies:
+    abab "^2.0.0"
+    whatwg-mimetype "^2.2.0"
+    whatwg-url "^7.0.0"
+
+deasync@^0.1.14:
+  version "0.1.19"
+  resolved "https://registry.yarnpkg.com/deasync/-/deasync-0.1.19.tgz#e7ea89fcc9ad483367e8a48fe78f508ca86286e8"
+  integrity sha512-oh3MRktfnPlLysCPpBpKZZzb4cUC/p0aA3SyRGp15lN30juJBTo/CiD0d4fR+f1kBtUQoJj1NE9RPNWQ7BQ9Mg==
+  dependencies:
+    bindings "^1.5.0"
+    node-addon-api "^1.7.1"
+
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+  dependencies:
+    ms "2.0.0"
+
+debug@=3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+  integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
+  dependencies:
+    ms "2.0.0"
+
+debug@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
+  integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+  dependencies:
+    ms "^2.1.1"
+
+decamelize@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+  integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
+
+decode-uri-component@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+  integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+deep-is@~0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+  integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+
+defaults@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
+  integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
+  dependencies:
+    clone "^1.0.2"
+
+define-properties@^1.1.2, define-properties@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+  integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
+  dependencies:
+    object-keys "^1.0.12"
+
+define-property@^0.2.5:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+  integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+  dependencies:
+    is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+  integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+  dependencies:
+    is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+  integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+  dependencies:
+    is-descriptor "^1.0.2"
+    isobject "^3.0.1"
+
+defined@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
+  integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
+
+depd@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+  integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
+
+des.js@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843"
+  integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==
+  dependencies:
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+
+destroy@~1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
+  integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
+
+detective@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b"
+  integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==
+  dependencies:
+    acorn-node "^1.6.1"
+    defined "^1.0.0"
+    minimist "^1.1.1"
+
+diffie-hellman@^5.0.0:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
+  integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
+  dependencies:
+    bn.js "^4.1.0"
+    miller-rabin "^4.0.0"
+    randombytes "^2.0.0"
+
+dom-serializer@0:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
+  integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
+  dependencies:
+    domelementtype "^2.0.1"
+    entities "^2.0.0"
+
+domain-browser@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
+  integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
+
+domelementtype@1, domelementtype@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
+  integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
+
+domelementtype@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d"
+  integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==
+
+domexception@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
+  integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
+  dependencies:
+    webidl-conversions "^4.0.2"
+
+domhandler@^2.3.0:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
+  integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==
+  dependencies:
+    domelementtype "1"
+
+domutils@^1.5.1, domutils@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
+  integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
+  dependencies:
+    dom-serializer "0"
+    domelementtype "1"
+
+dot-prop@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
+  integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
+  dependencies:
+    is-obj "^2.0.0"
+
+dotenv-expand@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
+  integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
+
+dotenv@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef"
+  integrity sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==
+
+duplexer2@~0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
+  integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=
+  dependencies:
+    readable-stream "^2.0.2"
+
+ecc-jsbn@~0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+  integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
+  dependencies:
+    jsbn "~0.1.0"
+    safer-buffer "^2.1.0"
+
+ee-first@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+  integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
+
+electron-to-chromium@^1.3.380:
+  version "1.3.381"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.381.tgz#952678ff91a5f36175a3832358a6dd2de3bf62b7"
+  integrity sha512-JQBpVUr83l+QOqPQpj2SbOve1bBE4ACpmwcMNqWlZmfib7jccxJ02qFNichDpZ5LS4Zsqc985NIPKegBIZjK8Q==
+
+elliptic@^6.0.0:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762"
+  integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==
+  dependencies:
+    bn.js "^4.4.0"
+    brorand "^1.0.1"
+    hash.js "^1.0.0"
+    hmac-drbg "^1.0.0"
+    inherits "^2.0.1"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.0"
+
+emoji-regex@^7.0.1:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
+  integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+
+encodeurl@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+  integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+
+entities@^1.1.1, entities@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
+  integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
+
+entities@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
+  integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
+
+envinfo@^7.3.1:
+  version "7.5.0"
+  resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.5.0.tgz#91410bb6db262fb4f1409bd506e9ff57e91023f4"
+  integrity sha512-jDgnJaF/Btomk+m3PZDTTCb5XIIIX3zYItnCRfF73zVgvinLoRomuhi75Y4su0PtQxWz4v66XnLLckyvyJTOIQ==
+
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+es-abstract@^1.17.0-next.1, es-abstract@^1.17.2:
+  version "1.17.5"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
+  integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==
+  dependencies:
+    es-to-primitive "^1.2.1"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+    has-symbols "^1.0.1"
+    is-callable "^1.1.5"
+    is-regex "^1.0.5"
+    object-inspect "^1.7.0"
+    object-keys "^1.1.1"
+    object.assign "^4.1.0"
+    string.prototype.trimleft "^2.1.1"
+    string.prototype.trimright "^2.1.1"
+
+es-to-primitive@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+  integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
+  dependencies:
+    is-callable "^1.1.4"
+    is-date-object "^1.0.1"
+    is-symbol "^1.0.2"
+
+escape-html@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
+
+escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+escodegen@^1.11.0, escodegen@^1.11.1:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457"
+  integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==
+  dependencies:
+    esprima "^4.0.1"
+    estraverse "^4.2.0"
+    esutils "^2.0.2"
+    optionator "^0.8.1"
+  optionalDependencies:
+    source-map "~0.6.1"
+
+escodegen@~1.9.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2"
+  integrity sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==
+  dependencies:
+    esprima "^3.1.3"
+    estraverse "^4.2.0"
+    esutils "^2.0.2"
+    optionator "^0.8.1"
+  optionalDependencies:
+    source-map "~0.6.1"
+
+esprima@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
+  integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=
+
+esprima@^4.0.0, esprima@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+  integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+estraverse@^4.2.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+  integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+esutils@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+  integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
+
+events@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59"
+  integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==
+
+evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
+  integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==
+  dependencies:
+    md5.js "^1.3.4"
+    safe-buffer "^5.1.1"
+
+expand-brackets@^2.1.4:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+  integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+  dependencies:
+    debug "^2.3.3"
+    define-property "^0.2.5"
+    extend-shallow "^2.0.1"
+    posix-character-classes "^0.1.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+extend-shallow@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+  integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+  dependencies:
+    is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+  integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+  dependencies:
+    assign-symbols "^1.0.0"
+    is-extendable "^1.0.1"
+
+extend@~3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+  integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+extglob@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+  integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+  dependencies:
+    array-unique "^0.3.2"
+    define-property "^1.0.0"
+    expand-brackets "^2.1.4"
+    extend-shallow "^2.0.1"
+    fragment-cache "^0.2.1"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+extsprintf@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+  integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
+
+extsprintf@^1.2.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
+  integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
+
+falafel@^2.1.0:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.2.4.tgz#b5d86c060c2412a43166243cb1bce44d1abd2819"
+  integrity sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ==
+  dependencies:
+    acorn "^7.1.1"
+    foreach "^2.0.5"
+    isarray "^2.0.1"
+    object-keys "^1.0.6"
+
+fast-deep-equal@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
+  integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
+
+fast-glob@^2.2.2:
+  version "2.2.7"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
+  integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==
+  dependencies:
+    "@mrmlnc/readdir-enhanced" "^2.2.1"
+    "@nodelib/fs.stat" "^1.1.2"
+    glob-parent "^3.1.0"
+    is-glob "^4.0.0"
+    merge2 "^1.2.3"
+    micromatch "^3.1.10"
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@~2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+fastparse@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
+  integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
+
+file-uri-to-path@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+  integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
+
+filesize@^3.6.0:
+  version "3.6.1"
+  resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317"
+  integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==
+
+fill-range@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+  integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+    to-regex-range "^2.1.0"
+
+find-up@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+  integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+  dependencies:
+    locate-path "^3.0.0"
+
+follow-redirects@1.5.10:
+  version "1.5.10"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
+  integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
+  dependencies:
+    debug "=3.1.0"
+
+for-in@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+  integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+foreach@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
+  integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
+
+forever-agent@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+  integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+
+form-data@~2.3.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+  integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.6"
+    mime-types "^2.1.12"
+
+fragment-cache@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+  integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+  dependencies:
+    map-cache "^0.2.2"
+
+fresh@0.5.2:
+  version "0.5.2"
+  resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+  integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
+
+fs-extra@^8.0.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+fsevents@^1.2.7:
+  version "1.2.12"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.12.tgz#db7e0d8ec3b0b45724fd4d83d43554a8f1f0de5c"
+  integrity sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==
+  dependencies:
+    bindings "^1.5.0"
+    nan "^2.12.1"
+
+function-bind@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+gensync@^1.0.0-beta.1:
+  version "1.0.0-beta.1"
+  resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
+  integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==
+
+get-caller-file@^2.0.1:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-port@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
+  integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=
+
+get-value@^2.0.3, get-value@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+  integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+getpass@^0.1.1:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+  integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
+  dependencies:
+    assert-plus "^1.0.0"
+
+glob-parent@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
+  integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=
+  dependencies:
+    is-glob "^3.1.0"
+    path-dirname "^1.0.0"
+
+glob-to-regexp@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
+  integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
+
+glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
+  version "7.1.6"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+  integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+globals@^11.1.0:
+  version "11.12.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
+  integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
+
+grapheme-breaker@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/grapheme-breaker/-/grapheme-breaker-0.3.2.tgz#5b9e6b78c3832452d2ba2bb1cb830f96276410ac"
+  integrity sha1-W55reMODJFLSuiuxy4MPlidkEKw=
+  dependencies:
+    brfs "^1.2.0"
+    unicode-trie "^0.3.1"
+
+gud@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
+  integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
+
+har-schema@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+  integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
+
+har-validator@~5.1.3:
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
+  integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
+  dependencies:
+    ajv "^6.5.5"
+    har-schema "^2.0.0"
+
+has-ansi@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
+  integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+has-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+  integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-symbols@^1.0.0, has-symbols@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
+  integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
+
+has-value@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+  integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+  dependencies:
+    get-value "^2.0.3"
+    has-values "^0.1.4"
+    isobject "^2.0.0"
+
+has-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+  integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+  dependencies:
+    get-value "^2.0.6"
+    has-values "^1.0.0"
+    isobject "^3.0.0"
+
+has-values@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+  integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+  integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+  dependencies:
+    is-number "^3.0.0"
+    kind-of "^4.0.0"
+
+has@^1.0.0, has@^1.0.1, has@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+  dependencies:
+    function-bind "^1.1.1"
+
+hash-base@^3.0.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
+  integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+hash.js@^1.0.0, hash.js@^1.0.3:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
+  integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
+  dependencies:
+    inherits "^2.0.3"
+    minimalistic-assert "^1.0.1"
+
+hex-color-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
+  integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
+
+history@^4.9.0:
+  version "4.10.1"
+  resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
+  integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
+  dependencies:
+    "@babel/runtime" "^7.1.2"
+    loose-envify "^1.2.0"
+    resolve-pathname "^3.0.0"
+    tiny-invariant "^1.0.2"
+    tiny-warning "^1.0.0"
+    value-equal "^1.0.1"
+
+hmac-drbg@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+  integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=
+  dependencies:
+    hash.js "^1.0.3"
+    minimalistic-assert "^1.0.0"
+    minimalistic-crypto-utils "^1.0.1"
+
+hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
+  integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
+  dependencies:
+    react-is "^16.7.0"
+
+hsl-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e"
+  integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=
+
+hsla-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38"
+  integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg=
+
+html-comment-regex@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
+  integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
+
+html-encoding-sniffer@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
+  integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==
+  dependencies:
+    whatwg-encoding "^1.0.1"
+
+html-tags@^1.0.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-1.2.0.tgz#c78de65b5663aa597989dd2b7ab49200d7e4db98"
+  integrity sha1-x43mW1Zjqll5id0rerSSANfk25g=
+
+htmlnano@^0.2.2:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-0.2.5.tgz#134fd9548c7cbe51c8508ce434a3f9488cff1b0b"
+  integrity sha512-X1iPSwXG/iF9bVs+/obt2n6F64uH0ETkA8zp7qFDmLW9/+A6ueHGeb/+qD67T21qUY22owZPMdawljN50ajkqA==
+  dependencies:
+    cssnano "^4.1.10"
+    normalize-html-whitespace "^1.0.0"
+    posthtml "^0.12.0"
+    posthtml-render "^1.1.5"
+    purgecss "^1.4.0"
+    svgo "^1.3.2"
+    terser "^4.3.9"
+    uncss "^0.17.2"
+
+htmlparser2@^3.9.2:
+  version "3.10.1"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
+  integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
+  dependencies:
+    domelementtype "^1.3.1"
+    domhandler "^2.3.0"
+    domutils "^1.5.1"
+    entities "^1.1.1"
+    inherits "^2.0.1"
+    readable-stream "^3.1.1"
+
+http-errors@~1.7.2:
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
+  integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
+  dependencies:
+    depd "~1.1.2"
+    inherits "2.0.4"
+    setprototypeof "1.1.1"
+    statuses ">= 1.5.0 < 2"
+    toidentifier "1.0.0"
+
+http-signature@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+  integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+  dependencies:
+    assert-plus "^1.0.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
+https-browserify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
+  integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
+
+iconv-lite@0.4.24:
+  version "0.4.24"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3"
+
+icss-replace-symbols@1.1.0, icss-replace-symbols@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
+  integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
+
+ieee754@^1.1.4:
+  version "1.1.13"
+  resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
+  integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
+
+immer@^4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/immer/-/immer-4.0.2.tgz#9ff0fcdf88e06f92618a5978ceecb5884e633559"
+  integrity sha512-Q/tm+yKqnKy4RIBmmtISBlhXuSDrB69e9EKTYiIenIKQkXBQir43w+kN/eGiax3wt1J0O1b2fYcNqLSbEcXA7w==
+
+import-fresh@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
+  integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
+  dependencies:
+    caller-path "^2.0.0"
+    resolve-from "^3.0.0"
+
+indexes-of@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
+  integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inherits@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
+  integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=
+
+inherits@2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
+  integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
+
+invariant@^2.1.0, invariant@^2.2.2, invariant@^2.2.4:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+  integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+  dependencies:
+    loose-envify "^1.0.0"
+
+is-absolute-url@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
+  integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
+
+is-absolute-url@^3.0.1:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698"
+  integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==
+
+is-accessor-descriptor@^0.1.6:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+  integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+  integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
+
+is-arrayish@^0.3.1:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03"
+  integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
+
+is-binary-path@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
+  integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=
+  dependencies:
+    binary-extensions "^1.0.0"
+
+is-buffer@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+  integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-callable@^1.1.4, is-callable@^1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
+  integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==
+
+is-color-stop@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345"
+  integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=
+  dependencies:
+    css-color-names "^0.0.4"
+    hex-color-regex "^1.1.0"
+    hsl-regex "^1.0.0"
+    hsla-regex "^1.0.0"
+    rgb-regex "^1.0.1"
+    rgba-regex "^1.0.0"
+
+is-data-descriptor@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+  integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+  integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+  dependencies:
+    kind-of "^6.0.0"
+
+is-date-object@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
+  integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
+
+is-descriptor@^0.1.0:
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+  integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+  dependencies:
+    is-accessor-descriptor "^0.1.6"
+    is-data-descriptor "^0.1.4"
+    kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+  integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+  dependencies:
+    is-accessor-descriptor "^1.0.0"
+    is-data-descriptor "^1.0.0"
+    kind-of "^6.0.2"
+
+is-directory@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
+  integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+  integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+  integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+  dependencies:
+    is-plain-object "^2.0.4"
+
+is-extglob@^2.1.0, is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+  integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-glob@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
+  integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=
+  dependencies:
+    is-extglob "^2.1.0"
+
+is-glob@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
+  integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-html@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-html/-/is-html-1.1.0.tgz#e04f1c18d39485111396f9a0273eab51af218464"
+  integrity sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ=
+  dependencies:
+    html-tags "^1.0.0"
+
+is-number@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+  integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+  dependencies:
+    kind-of "^3.0.2"
+
+is-obj@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
+  integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
+
+is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+  integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+  dependencies:
+    isobject "^3.0.1"
+
+is-regex@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
+  integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==
+  dependencies:
+    has "^1.0.3"
+
+is-resolvable@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
+  integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
+
+is-svg@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75"
+  integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==
+  dependencies:
+    html-comment-regex "^1.1.0"
+
+is-symbol@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
+  integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
+  dependencies:
+    has-symbols "^1.0.1"
+
+is-typedarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+  integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+
+is-url@^1.2.2:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52"
+  integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
+
+is-windows@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+  integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+is-wsl@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+  integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
+
+isarray@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+  integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
+
+isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isarray@^2.0.1:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
+  integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+  integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+  dependencies:
+    isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+  integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+  integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^3.10.0, js-yaml@^3.13.1:
+  version "3.13.1"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
+  integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
+jsbn@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+  integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
+
+jsdom@^14.1.0:
+  version "14.1.0"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-14.1.0.tgz#916463b6094956b0a6c1782c94e380cd30e1981b"
+  integrity sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==
+  dependencies:
+    abab "^2.0.0"
+    acorn "^6.0.4"
+    acorn-globals "^4.3.0"
+    array-equal "^1.0.0"
+    cssom "^0.3.4"
+    cssstyle "^1.1.1"
+    data-urls "^1.1.0"
+    domexception "^1.0.1"
+    escodegen "^1.11.0"
+    html-encoding-sniffer "^1.0.2"
+    nwsapi "^2.1.3"
+    parse5 "5.1.0"
+    pn "^1.1.0"
+    request "^2.88.0"
+    request-promise-native "^1.0.5"
+    saxes "^3.1.9"
+    symbol-tree "^3.2.2"
+    tough-cookie "^2.5.0"
+    w3c-hr-time "^1.0.1"
+    w3c-xmlserializer "^1.1.2"
+    webidl-conversions "^4.0.2"
+    whatwg-encoding "^1.0.5"
+    whatwg-mimetype "^2.3.0"
+    whatwg-url "^7.0.0"
+    ws "^6.1.2"
+    xml-name-validator "^3.0.0"
+
+jsesc@^2.5.1:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+  integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+jsesc@~0.5.0:
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+  integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
+
+json-parse-better-errors@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+  integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema@0.2.3:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+  integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
+
+json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+  integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
+
+json5@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
+  integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
+  dependencies:
+    minimist "^1.2.0"
+
+json5@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e"
+  integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==
+  dependencies:
+    minimist "^1.2.5"
+
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+jsprim@^1.2.2:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
+  integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
+  dependencies:
+    assert-plus "1.0.0"
+    extsprintf "1.3.0"
+    json-schema "0.2.3"
+    verror "1.10.0"
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+  integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+  integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+  dependencies:
+    is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+  integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+  integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+leven@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
+  integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+
+levenary@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.1.tgz#842a9ee98d2075aa7faeedbe32679e9205f46f77"
+  integrity sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==
+  dependencies:
+    leven "^3.1.0"
+
+levn@~0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+  integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
+  dependencies:
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+
+locate-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+  integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+  dependencies:
+    p-locate "^3.0.0"
+    path-exists "^3.0.0"
+
+lodash.clone@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6"
+  integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=
+
+lodash.memoize@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+  integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
+
+lodash.sortby@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
+  integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
+
+lodash.toarray@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
+  integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE=
+
+lodash.uniq@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+  integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
+
+lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.4:
+  version "4.17.15"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+  integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+log-symbols@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
+  integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
+  dependencies:
+    chalk "^2.0.1"
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+magic-string@^0.22.4:
+  version "0.22.5"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e"
+  integrity sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==
+  dependencies:
+    vlq "^0.2.2"
+
+map-cache@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+  integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+  integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+  dependencies:
+    object-visit "^1.0.0"
+
+md5.js@^1.3.4:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
+  integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+    safe-buffer "^5.1.2"
+
+mdn-data@2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
+  integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
+
+merge-source-map@1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.0.4.tgz#a5de46538dae84d4114cc5ea02b4772a6346701f"
+  integrity sha1-pd5GU42uhNQRTMXqArR3KmNGcB8=
+  dependencies:
+    source-map "^0.5.6"
+
+merge2@^1.2.3:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81"
+  integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==
+
+micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4:
+  version "3.1.10"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+  integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+  dependencies:
+    arr-diff "^4.0.0"
+    array-unique "^0.3.2"
+    braces "^2.3.1"
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    extglob "^2.0.4"
+    fragment-cache "^0.2.1"
+    kind-of "^6.0.2"
+    nanomatch "^1.2.9"
+    object.pick "^1.3.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.2"
+
+miller-rabin@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
+  integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
+  dependencies:
+    bn.js "^4.0.0"
+    brorand "^1.0.1"
+
+mime-db@1.43.0:
+  version "1.43.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
+  integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
+
+mime-types@^2.1.12, mime-types@~2.1.19:
+  version "2.1.26"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
+  integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
+  dependencies:
+    mime-db "1.43.0"
+
+mime@1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mimic-fn@^1.0.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
+  integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
+
+mini-create-react-context@^0.3.0:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz#79fc598f283dd623da8e088b05db8cddab250189"
+  integrity sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==
+  dependencies:
+    "@babel/runtime" "^7.4.0"
+    gud "^1.0.0"
+    tiny-warning "^1.0.2"
+
+minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
+  integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
+
+minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+  integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
+
+minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+  integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
+mixin-deep@^1.2.0:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
+  integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
+  dependencies:
+    for-in "^1.0.2"
+    is-extendable "^1.0.1"
+
+mkdirp@^0.5.1, mkdirp@~0.5.1:
+  version "0.5.4"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512"
+  integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==
+  dependencies:
+    minimist "^1.2.5"
+
+ms@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+  integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+  integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
+
+ms@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+nan@^2.12.1:
+  version "2.14.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
+  integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
+
+nanomatch@^1.2.9:
+  version "1.2.13"
+  resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
+  integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+  dependencies:
+    arr-diff "^4.0.0"
+    array-unique "^0.3.2"
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    fragment-cache "^0.2.1"
+    is-windows "^1.0.2"
+    kind-of "^6.0.2"
+    object.pick "^1.3.0"
+    regex-not "^1.0.0"
+    snapdragon "^0.8.1"
+    to-regex "^3.0.1"
+
+nice-try@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+  integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+
+node-addon-api@^1.7.1:
+  version "1.7.1"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.1.tgz#cf813cd69bb8d9100f6bdca6755fc268f54ac492"
+  integrity sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ==
+
+node-emoji@^1.8.1:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da"
+  integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==
+  dependencies:
+    lodash.toarray "^4.4.0"
+
+node-forge@^0.7.1:
+  version "0.7.6"
+  resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
+  integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==
+
+node-libs-browser@^2.0.0:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425"
+  integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==
+  dependencies:
+    assert "^1.1.1"
+    browserify-zlib "^0.2.0"
+    buffer "^4.3.0"
+    console-browserify "^1.1.0"
+    constants-browserify "^1.0.0"
+    crypto-browserify "^3.11.0"
+    domain-browser "^1.1.1"
+    events "^3.0.0"
+    https-browserify "^1.0.0"
+    os-browserify "^0.3.0"
+    path-browserify "0.0.1"
+    process "^0.11.10"
+    punycode "^1.2.4"
+    querystring-es3 "^0.2.0"
+    readable-stream "^2.3.3"
+    stream-browserify "^2.0.1"
+    stream-http "^2.7.2"
+    string_decoder "^1.0.0"
+    timers-browserify "^2.0.4"
+    tty-browserify "0.0.0"
+    url "^0.11.0"
+    util "^0.11.0"
+    vm-browserify "^1.0.1"
+
+node-releases@^1.1.52:
+  version "1.1.52"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9"
+  integrity sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ==
+  dependencies:
+    semver "^6.3.0"
+
+normalize-html-whitespace@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz#5e3c8e192f1b06c3b9eee4b7e7f28854c7601e34"
+  integrity sha512-9ui7CGtOOlehQu0t/OhhlmDyc71mKVlv+4vF+me4iZLPrNtRL2xoquEdfZxasC/bdQi/Hr3iTrpyRKIG+ocabA==
+
+normalize-path@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+  integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
+  dependencies:
+    remove-trailing-separator "^1.0.1"
+
+normalize-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+normalize-range@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+  integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
+
+normalize-url@^3.0.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
+  integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
+
+normalize.css@^8.0.1:
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3"
+  integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==
+
+nth-check@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
+  integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
+  dependencies:
+    boolbase "~1.0.0"
+
+num2fraction@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
+  integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
+
+nwsapi@^2.1.3:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
+  integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
+
+oauth-sign@~0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+  integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+
+object-assign@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-copy@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+  integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+  dependencies:
+    copy-descriptor "^0.1.0"
+    define-property "^0.2.5"
+    kind-of "^3.0.3"
+
+object-inspect@^1.7.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
+  integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
+
+object-inspect@~1.4.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.4.1.tgz#37ffb10e71adaf3748d05f713b4c9452f402cbc4"
+  integrity sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw==
+
+object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+  integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object-visit@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+  integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+  dependencies:
+    isobject "^3.0.0"
+
+object.assign@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+  integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
+  dependencies:
+    define-properties "^1.1.2"
+    function-bind "^1.1.1"
+    has-symbols "^1.0.0"
+    object-keys "^1.0.11"
+
+object.getownpropertydescriptors@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
+  integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.0-next.1"
+
+object.pick@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+  integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+  dependencies:
+    isobject "^3.0.1"
+
+object.values@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
+  integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.0-next.1"
+    function-bind "^1.1.1"
+    has "^1.0.3"
+
+on-finished@~2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+  integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
+  dependencies:
+    ee-first "1.1.1"
+
+once@^1.3.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
+onetime@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+  integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
+  dependencies:
+    mimic-fn "^1.0.0"
+
+opn@^5.1.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc"
+  integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==
+  dependencies:
+    is-wsl "^1.1.0"
+
+optionator@^0.8.1:
+  version "0.8.3"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
+  integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
+  dependencies:
+    deep-is "~0.1.3"
+    fast-levenshtein "~2.0.6"
+    levn "~0.3.0"
+    prelude-ls "~1.1.2"
+    type-check "~0.3.2"
+    word-wrap "~1.2.3"
+
+ora@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/ora/-/ora-2.1.0.tgz#6caf2830eb924941861ec53a173799e008b51e5b"
+  integrity sha512-hNNlAd3gfv/iPmsNxYoAPLvxg7HuPozww7fFonMZvL84tP6Ox5igfk5j/+a9rtJJwqMgKK+JgWsAQik5o0HTLA==
+  dependencies:
+    chalk "^2.3.1"
+    cli-cursor "^2.1.0"
+    cli-spinners "^1.1.0"
+    log-symbols "^2.2.0"
+    strip-ansi "^4.0.0"
+    wcwidth "^1.0.1"
+
+os-browserify@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
+  integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
+
+p-limit@^2.0.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
+  integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
+  dependencies:
+    p-try "^2.0.0"
+
+p-locate@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+  integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+  dependencies:
+    p-limit "^2.0.0"
+
+p-try@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+  integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+pako@^0.2.5:
+  version "0.2.9"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
+  integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=
+
+pako@~1.0.5:
+  version "1.0.11"
+  resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
+  integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
+
+parcel-bundler@^1.12.4:
+  version "1.12.4"
+  resolved "https://registry.yarnpkg.com/parcel-bundler/-/parcel-bundler-1.12.4.tgz#31223f4ab4d00323a109fce28d5e46775409a9ee"
+  integrity sha512-G+iZGGiPEXcRzw0fiRxWYCKxdt/F7l9a0xkiU4XbcVRJCSlBnioWEwJMutOCCpoQmaQtjB4RBHDGIHN85AIhLQ==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    "@babel/core" "^7.4.4"
+    "@babel/generator" "^7.4.4"
+    "@babel/parser" "^7.4.4"
+    "@babel/plugin-transform-flow-strip-types" "^7.4.4"
+    "@babel/plugin-transform-modules-commonjs" "^7.4.4"
+    "@babel/plugin-transform-react-jsx" "^7.0.0"
+    "@babel/preset-env" "^7.4.4"
+    "@babel/runtime" "^7.4.4"
+    "@babel/template" "^7.4.4"
+    "@babel/traverse" "^7.4.4"
+    "@babel/types" "^7.4.4"
+    "@iarna/toml" "^2.2.0"
+    "@parcel/fs" "^1.11.0"
+    "@parcel/logger" "^1.11.1"
+    "@parcel/utils" "^1.11.0"
+    "@parcel/watcher" "^1.12.1"
+    "@parcel/workers" "^1.11.0"
+    ansi-to-html "^0.6.4"
+    babylon-walk "^1.0.2"
+    browserslist "^4.1.0"
+    chalk "^2.1.0"
+    clone "^2.1.1"
+    command-exists "^1.2.6"
+    commander "^2.11.0"
+    core-js "^2.6.5"
+    cross-spawn "^6.0.4"
+    css-modules-loader-core "^1.1.0"
+    cssnano "^4.0.0"
+    deasync "^0.1.14"
+    dotenv "^5.0.0"
+    dotenv-expand "^5.1.0"
+    envinfo "^7.3.1"
+    fast-glob "^2.2.2"
+    filesize "^3.6.0"
+    get-port "^3.2.0"
+    htmlnano "^0.2.2"
+    is-glob "^4.0.0"
+    is-url "^1.2.2"
+    js-yaml "^3.10.0"
+    json5 "^1.0.1"
+    micromatch "^3.0.4"
+    mkdirp "^0.5.1"
+    node-forge "^0.7.1"
+    node-libs-browser "^2.0.0"
+    opn "^5.1.0"
+    postcss "^7.0.11"
+    postcss-value-parser "^3.3.1"
+    posthtml "^0.11.2"
+    posthtml-parser "^0.4.0"
+    posthtml-render "^1.1.3"
+    resolve "^1.4.0"
+    semver "^5.4.1"
+    serialize-to-js "^3.0.0"
+    serve-static "^1.12.4"
+    source-map "0.6.1"
+    terser "^3.7.3"
+    v8-compile-cache "^2.0.0"
+    ws "^5.1.1"
+
+parse-asn1@^5.0.0:
+  version "5.1.5"
+  resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.5.tgz#003271343da58dc94cace494faef3d2147ecea0e"
+  integrity sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==
+  dependencies:
+    asn1.js "^4.0.0"
+    browserify-aes "^1.0.0"
+    create-hash "^1.1.0"
+    evp_bytestokey "^1.0.0"
+    pbkdf2 "^3.0.3"
+    safe-buffer "^5.1.1"
+
+parse-json@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+  integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+  dependencies:
+    error-ex "^1.3.1"
+    json-parse-better-errors "^1.0.1"
+
+parse5@5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2"
+  integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==
+
+parseurl@~1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+  integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+pascalcase@^0.1.1:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+  integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-browserify@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
+  integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
+
+path-dirname@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
+  integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
+
+path-exists@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+  integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-key@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+  integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
+path-parse@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+  integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+path-to-regexp@^1.7.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
+  integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
+  dependencies:
+    isarray "0.0.1"
+
+pbkdf2@^3.0.3:
+  version "3.0.17"
+  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
+  integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==
+  dependencies:
+    create-hash "^1.1.2"
+    create-hmac "^1.1.4"
+    ripemd160 "^2.0.1"
+    safe-buffer "^5.0.1"
+    sha.js "^2.4.8"
+
+performance-now@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+  integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+
+physical-cpu-count@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/physical-cpu-count/-/physical-cpu-count-2.0.0.tgz#18de2f97e4bf7a9551ad7511942b5496f7aba660"
+  integrity sha1-GN4vl+S/epVRrXURlCtUlverpmA=
+
+pkg-up@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
+  integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
+  dependencies:
+    find-up "^3.0.0"
+
+pn@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
+  integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
+
+posix-character-classes@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+  integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+postcss-calc@^7.0.1:
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.2.tgz#504efcd008ca0273120568b0792b16cdcde8aac1"
+  integrity sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==
+  dependencies:
+    postcss "^7.0.27"
+    postcss-selector-parser "^6.0.2"
+    postcss-value-parser "^4.0.2"
+
+postcss-colormin@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381"
+  integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==
+  dependencies:
+    browserslist "^4.0.0"
+    color "^3.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-convert-values@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f"
+  integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==
+  dependencies:
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-discard-comments@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033"
+  integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-discard-duplicates@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb"
+  integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-discard-empty@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765"
+  integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-discard-overridden@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57"
+  integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-functions@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-functions/-/postcss-functions-3.0.0.tgz#0e94d01444700a481de20de4d55fb2640564250e"
+  integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=
+  dependencies:
+    glob "^7.1.2"
+    object-assign "^4.1.1"
+    postcss "^6.0.9"
+    postcss-value-parser "^3.3.0"
+
+postcss-js@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-2.0.3.tgz#a96f0f23ff3d08cec7dc5b11bf11c5f8077cdab9"
+  integrity sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==
+  dependencies:
+    camelcase-css "^2.0.1"
+    postcss "^7.0.18"
+
+postcss-merge-longhand@^4.0.11:
+  version "4.0.11"
+  resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24"
+  integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==
+  dependencies:
+    css-color-names "0.0.4"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    stylehacks "^4.0.0"
+
+postcss-merge-rules@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650"
+  integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==
+  dependencies:
+    browserslist "^4.0.0"
+    caniuse-api "^3.0.0"
+    cssnano-util-same-parent "^4.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
+    vendors "^1.0.0"
+
+postcss-minify-font-values@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6"
+  integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==
+  dependencies:
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-minify-gradients@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471"
+  integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    is-color-stop "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-minify-params@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874"
+  integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    browserslist "^4.0.0"
+    cssnano-util-get-arguments "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    uniqs "^2.0.0"
+
+postcss-minify-selectors@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8"
+  integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
+
+postcss-modules-extract-imports@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb"
+  integrity sha1-thTJcgvmgW6u41+zpfqh26agXds=
+  dependencies:
+    postcss "^6.0.1"
+
+postcss-modules-local-by-default@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
+  integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=
+  dependencies:
+    css-selector-tokenizer "^0.7.0"
+    postcss "^6.0.1"
+
+postcss-modules-scope@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
+  integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A=
+  dependencies:
+    css-selector-tokenizer "^0.7.0"
+    postcss "^6.0.1"
+
+postcss-modules-values@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
+  integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=
+  dependencies:
+    icss-replace-symbols "^1.1.0"
+    postcss "^6.0.1"
+
+postcss-nested@^4.1.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-4.2.1.tgz#4bc2e5b35e3b1e481ff81e23b700da7f82a8b248"
+  integrity sha512-AMayXX8tS0HCp4O4lolp4ygj9wBn32DJWXvG6gCv+ZvJrEa00GUxJcJEEzMh87BIe6FrWdYkpR2cuyqHKrxmXw==
+  dependencies:
+    postcss "^7.0.21"
+    postcss-selector-parser "^6.0.2"
+
+postcss-normalize-charset@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4"
+  integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==
+  dependencies:
+    postcss "^7.0.0"
+
+postcss-normalize-display-values@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a"
+  integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-positions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f"
+  integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-repeat-style@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c"
+  integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-string@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c"
+  integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==
+  dependencies:
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-timing-functions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9"
+  integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-unicode@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb"
+  integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==
+  dependencies:
+    browserslist "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-url@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1"
+  integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==
+  dependencies:
+    is-absolute-url "^2.0.0"
+    normalize-url "^3.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-normalize-whitespace@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82"
+  integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==
+  dependencies:
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-ordered-values@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee"
+  integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==
+  dependencies:
+    cssnano-util-get-arguments "^4.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-reduce-initial@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df"
+  integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==
+  dependencies:
+    browserslist "^4.0.0"
+    caniuse-api "^3.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+
+postcss-reduce-transforms@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29"
+  integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==
+  dependencies:
+    cssnano-util-get-match "^4.0.0"
+    has "^1.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+
+postcss-selector-parser@6.0.2, postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
+  integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
+  dependencies:
+    cssesc "^3.0.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-selector-parser@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270"
+  integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==
+  dependencies:
+    dot-prop "^5.2.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-svgo@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258"
+  integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==
+  dependencies:
+    is-svg "^3.0.0"
+    postcss "^7.0.0"
+    postcss-value-parser "^3.0.0"
+    svgo "^1.0.0"
+
+postcss-unique-selectors@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac"
+  integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==
+  dependencies:
+    alphanum-sort "^1.0.0"
+    postcss "^7.0.0"
+    uniqs "^2.0.0"
+
+postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
+  integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
+
+postcss-value-parser@^4.0.2:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d"
+  integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==
+
+postcss@6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2"
+  integrity sha1-AA29H47vIXqjaLmiEsX8QLKo8/I=
+  dependencies:
+    chalk "^1.1.3"
+    source-map "^0.5.6"
+    supports-color "^3.2.3"
+
+postcss@^6.0.1, postcss@^6.0.9:
+  version "6.0.23"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
+  integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
+  dependencies:
+    chalk "^2.4.1"
+    source-map "^0.6.1"
+    supports-color "^5.4.0"
+
+postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.11, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.18, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.27:
+  version "7.0.27"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
+  integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==
+  dependencies:
+    chalk "^2.4.2"
+    source-map "^0.6.1"
+    supports-color "^6.1.0"
+
+posthtml-parser@^0.4.0, posthtml-parser@^0.4.1:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.4.2.tgz#a132bbdf0cd4bc199d34f322f5c1599385d7c6c1"
+  integrity sha512-BUIorsYJTvS9UhXxPTzupIztOMVNPa/HtAm9KHni9z6qEfiJ1bpOBL5DfUOL9XAc3XkLIEzBzpph+Zbm4AdRAg==
+  dependencies:
+    htmlparser2 "^3.9.2"
+
+posthtml-render@^1.1.3, posthtml-render@^1.1.5:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-1.2.0.tgz#3df0c800a8bbb95af583a94748520469477addf4"
+  integrity sha512-dQB+hoAKDtnI94RZm/wxBUH9My8OJcXd0uhWmGh2c7tVtQ85A+OS3yCN3LNbFtPz3bViwBJXAeoi+CBGMXM0DA==
+
+posthtml@^0.11.2:
+  version "0.11.6"
+  resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.11.6.tgz#e349d51af7929d0683b9d8c3abd8166beecc90a8"
+  integrity sha512-C2hrAPzmRdpuL3iH0TDdQ6XCc9M7Dcc3zEW5BLerY65G4tWWszwv6nG/ksi6ul5i2mx22ubdljgktXCtNkydkw==
+  dependencies:
+    posthtml-parser "^0.4.1"
+    posthtml-render "^1.1.5"
+
+posthtml@^0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.12.0.tgz#6e2a2fcd774eaed1a419a95c5cc3a92b676a40a6"
+  integrity sha512-aNUEP/SfKUXAt+ghG51LC5MmafChBZeslVe/SSdfKIgLGUVRE68mrMF4V8XbH07ZifM91tCSuxY3eHIFLlecQw==
+  dependencies:
+    posthtml-parser "^0.4.1"
+    posthtml-render "^1.1.5"
+
+prelude-ls@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+  integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
+
+pretty-hrtime@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
+  integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
+
+private@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+  integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
+
+process-nextick-args@~2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+  integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
+
+prop-types@^15.6.2, prop-types@^15.7.2:
+  version "15.7.2"
+  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
+  integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
+  dependencies:
+    loose-envify "^1.4.0"
+    object-assign "^4.1.1"
+    react-is "^16.8.1"
+
+psl@^1.1.28:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c"
+  integrity sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==
+
+public-encrypt@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
+  integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==
+  dependencies:
+    bn.js "^4.1.0"
+    browserify-rsa "^4.0.0"
+    create-hash "^1.1.0"
+    parse-asn1 "^5.0.0"
+    randombytes "^2.0.1"
+    safe-buffer "^5.1.2"
+
+punycode@1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
+  integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
+
+punycode@^1.2.4:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+  integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
+
+punycode@^2.1.0, punycode@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+purgecss@^1.4.0:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-1.4.2.tgz#67ab50cb4f5c163fcefde56002467c974e577f41"
+  integrity sha512-hkOreFTgiyMHMmC2BxzdIw5DuC6kxAbP/gGOGd3MEsF3+5m69rIvUEPaxrnoUtfODTFKe9hcXjGwC6jcjoyhOw==
+  dependencies:
+    glob "^7.1.3"
+    postcss "^7.0.14"
+    postcss-selector-parser "^6.0.0"
+    yargs "^14.0.0"
+
+q@^1.1.2:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
+  integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
+
+qs@^6.5.2:
+  version "6.9.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.2.tgz#a27b695006544a04bf0e6c6a7e8120778926d5bd"
+  integrity sha512-2eQ6zajpK7HwqrY1rRtGw5IZvjgtELXzJECaEDuzDFo2jjnIXpJSimzd4qflWZq6bLLi+Zgfj5eDrAzl/lptyg==
+
+qs@~6.5.2:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+  integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+
+querystring-es3@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
+  integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
+
+querystring@0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
+  integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
+
+quote-stream@^1.0.1, quote-stream@~1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2"
+  integrity sha1-hJY/jJwmuULhU/7rU6rnRlK34LI=
+  dependencies:
+    buffer-equal "0.0.1"
+    minimist "^1.1.3"
+    through2 "^2.0.0"
+
+randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+  integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+  dependencies:
+    safe-buffer "^5.1.0"
+
+randomfill@^1.0.3:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
+  integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==
+  dependencies:
+    randombytes "^2.0.5"
+    safe-buffer "^5.1.0"
+
+range-parser@~1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+  integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+react-dom@^16.13.1:
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
+  integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.2"
+    scheduler "^0.19.1"
+
+react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.9.0:
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+react-redux@^7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d"
+  integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==
+  dependencies:
+    "@babel/runtime" "^7.5.5"
+    hoist-non-react-statics "^3.3.0"
+    loose-envify "^1.4.0"
+    prop-types "^15.7.2"
+    react-is "^16.9.0"
+
+react-router-dom@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.1.2.tgz#06701b834352f44d37fbb6311f870f84c76b9c18"
+  integrity sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==
+  dependencies:
+    "@babel/runtime" "^7.1.2"
+    history "^4.9.0"
+    loose-envify "^1.3.1"
+    prop-types "^15.6.2"
+    react-router "5.1.2"
+    tiny-invariant "^1.0.2"
+    tiny-warning "^1.0.0"
+
+react-router@5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.1.2.tgz#6ea51d789cb36a6be1ba5f7c0d48dd9e817d3418"
+  integrity sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==
+  dependencies:
+    "@babel/runtime" "^7.1.2"
+    history "^4.9.0"
+    hoist-non-react-statics "^3.1.0"
+    loose-envify "^1.3.1"
+    mini-create-react-context "^0.3.0"
+    path-to-regexp "^1.7.0"
+    prop-types "^15.6.2"
+    react-is "^16.6.0"
+    tiny-invariant "^1.0.2"
+    tiny-warning "^1.0.0"
+
+react@^16.13.1:
+  version "16.13.1"
+  resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
+  integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.2"
+
+readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3, readable-stream@~2.3.6:
+  version "2.3.7"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
+  integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~2.0.0"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.1.1"
+    util-deprecate "~1.0.1"
+
+readable-stream@^3.1.1:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+  integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
+readdirp@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
+  integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==
+  dependencies:
+    graceful-fs "^4.1.11"
+    micromatch "^3.1.10"
+    readable-stream "^2.0.2"
+
+reduce-css-calc@^2.1.6:
+  version "2.1.7"
+  resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz#1ace2e02c286d78abcd01fd92bfe8097ab0602c2"
+  integrity sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA==
+  dependencies:
+    css-unit-converter "^1.1.1"
+    postcss-value-parser "^3.3.0"
+
+redux-devtools-extension@^2.13.8:
+  version "2.13.8"
+  resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1"
+  integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==
+
+redux-immutable-state-invariant@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/redux-immutable-state-invariant/-/redux-immutable-state-invariant-2.1.0.tgz#308fd3cc7415a0e7f11f51ec997b6379c7055ce1"
+  integrity sha512-3czbDKs35FwiBRsx/3KabUk5zSOoTXC+cgVofGkpBNv3jQcqIe5JrHcF5AmVt7B/4hyJ8MijBIpCJ8cife6yJg==
+  dependencies:
+    invariant "^2.1.0"
+    json-stringify-safe "^5.0.1"
+
+redux-thunk@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
+  integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
+
+redux@^4.0.0:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
+  integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
+  dependencies:
+    loose-envify "^1.4.0"
+    symbol-observable "^1.2.0"
+
+regenerate-unicode-properties@^8.2.0:
+  version "8.2.0"
+  resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
+  integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==
+  dependencies:
+    regenerate "^1.4.0"
+
+regenerate@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
+  integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
+
+regenerator-runtime@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+  integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
+regenerator-runtime@^0.13.4:
+  version "0.13.5"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
+  integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
+
+regenerator-transform@^0.14.2:
+  version "0.14.4"
+  resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7"
+  integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==
+  dependencies:
+    "@babel/runtime" "^7.8.4"
+    private "^0.1.8"
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+  integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+  dependencies:
+    extend-shallow "^3.0.2"
+    safe-regex "^1.1.0"
+
+regexpu-core@^4.6.0, regexpu-core@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.0.tgz#fcbf458c50431b0bb7b45d6967b8192d91f3d938"
+  integrity sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==
+  dependencies:
+    regenerate "^1.4.0"
+    regenerate-unicode-properties "^8.2.0"
+    regjsgen "^0.5.1"
+    regjsparser "^0.6.4"
+    unicode-match-property-ecmascript "^1.0.4"
+    unicode-match-property-value-ecmascript "^1.2.0"
+
+regjsgen@^0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c"
+  integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==
+
+regjsparser@^0.6.4:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.4.tgz#a769f8684308401a66e9b529d2436ff4d0666272"
+  integrity sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==
+  dependencies:
+    jsesc "~0.5.0"
+
+remove-trailing-separator@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
+  integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
+
+repeat-element@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+  integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+  integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+request-promise-core@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9"
+  integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==
+  dependencies:
+    lodash "^4.17.15"
+
+request-promise-native@^1.0.5:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36"
+  integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==
+  dependencies:
+    request-promise-core "1.1.3"
+    stealthy-require "^1.1.1"
+    tough-cookie "^2.3.3"
+
+request@^2.88.0:
+  version "2.88.2"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
+  integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
+  dependencies:
+    aws-sign2 "~0.7.0"
+    aws4 "^1.8.0"
+    caseless "~0.12.0"
+    combined-stream "~1.0.6"
+    extend "~3.0.2"
+    forever-agent "~0.6.1"
+    form-data "~2.3.2"
+    har-validator "~5.1.3"
+    http-signature "~1.2.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.19"
+    oauth-sign "~0.9.0"
+    performance-now "^2.1.0"
+    qs "~6.5.2"
+    safe-buffer "^5.1.2"
+    tough-cookie "~2.5.0"
+    tunnel-agent "^0.6.0"
+    uuid "^3.3.2"
+
+require-directory@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+  integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+
+require-main-filename@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+  integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
+reselect@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
+  integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==
+
+resolve-from@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+  integrity sha1-six699nWiBvItuZTM17rywoYh0g=
+
+resolve-pathname@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
+  integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
+
+resolve-url@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+  integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+resolve@^1.1.5, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.4.0:
+  version "1.15.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
+  integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
+  dependencies:
+    path-parse "^1.0.6"
+
+restore-cursor@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
+  integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
+  dependencies:
+    onetime "^2.0.0"
+    signal-exit "^3.0.2"
+
+ret@~0.1.10:
+  version "0.1.15"
+  resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+  integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rgb-regex@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
+  integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE=
+
+rgba-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
+  integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
+
+rimraf@^2.6.2:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+  integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+  dependencies:
+    glob "^7.1.3"
+
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
+  integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
+  dependencies:
+    hash-base "^3.0.0"
+    inherits "^2.0.1"
+
+safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
+  integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
+
+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-regex@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+  integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+  dependencies:
+    ret "~0.1.10"
+
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sax@~1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+  integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+saxes@^3.1.9:
+  version "3.1.11"
+  resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b"
+  integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==
+  dependencies:
+    xmlchars "^2.1.1"
+
+scheduler@^0.19.1:
+  version "0.19.1"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
+  integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
+  dependencies:
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+
+semver@7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
+  integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
+
+semver@^5.4.1, semver@^5.5.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+semver@^6.3.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+send@0.17.1:
+  version "0.17.1"
+  resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
+  integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
+  dependencies:
+    debug "2.6.9"
+    depd "~1.1.2"
+    destroy "~1.0.4"
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    etag "~1.8.1"
+    fresh "0.5.2"
+    http-errors "~1.7.2"
+    mime "1.6.0"
+    ms "2.1.1"
+    on-finished "~2.3.0"
+    range-parser "~1.2.1"
+    statuses "~1.5.0"
+
+serialize-to-js@^3.0.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/serialize-to-js/-/serialize-to-js-3.1.1.tgz#b3e77d0568ee4a60bfe66287f991e104d3a1a4ac"
+  integrity sha512-F+NGU0UHMBO4Q965tjw7rvieNVjlH6Lqi2emq/Lc9LUURYJbiCzmpi4Cy1OOjjVPtxu0c+NE85LU6968Wko5ZA==
+
+serve-static@^1.12.4:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
+  integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
+  dependencies:
+    encodeurl "~1.0.2"
+    escape-html "~1.0.3"
+    parseurl "~1.3.3"
+    send "0.17.1"
+
+set-blocking@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+  integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+set-value@^2.0.0, set-value@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
+  integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==
+  dependencies:
+    extend-shallow "^2.0.1"
+    is-extendable "^0.1.1"
+    is-plain-object "^2.0.3"
+    split-string "^3.0.1"
+
+setimmediate@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+  integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
+
+setprototypeof@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
+  integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
+
+sha.js@^2.4.0, sha.js@^2.4.8:
+  version "2.4.11"
+  resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
+  integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
+  dependencies:
+    inherits "^2.0.1"
+    safe-buffer "^5.0.1"
+
+shallow-copy@~0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170"
+  integrity sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=
+
+shebang-command@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+  integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+  dependencies:
+    shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+  integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
+signal-exit@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
+  integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+
+simple-swizzle@^0.2.2:
+  version "0.2.2"
+  resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a"
+  integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=
+  dependencies:
+    is-arrayish "^0.3.1"
+
+snapdragon-node@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+  integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+  dependencies:
+    define-property "^1.0.0"
+    isobject "^3.0.0"
+    snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+  integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+  dependencies:
+    kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+  integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+  dependencies:
+    base "^0.11.1"
+    debug "^2.2.0"
+    define-property "^0.2.5"
+    extend-shallow "^2.0.1"
+    map-cache "^0.2.2"
+    source-map "^0.5.6"
+    source-map-resolve "^0.5.0"
+    use "^3.1.0"
+
+source-map-resolve@^0.5.0:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
+  integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==
+  dependencies:
+    atob "^2.1.2"
+    decode-uri-component "^0.2.0"
+    resolve-url "^0.2.1"
+    source-map-url "^0.4.0"
+    urix "^0.1.0"
+
+source-map-support@~0.5.10, source-map-support@~0.5.12:
+  version "0.5.16"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042"
+  integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map-url@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+  integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+source-map@^0.5.0, source-map@^0.5.6:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+  integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+split-string@^3.0.1, split-string@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+  integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+  dependencies:
+    extend-shallow "^3.0.0"
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+  integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+
+sshpk@^1.7.0:
+  version "1.16.1"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
+  integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
+  dependencies:
+    asn1 "~0.2.3"
+    assert-plus "^1.0.0"
+    bcrypt-pbkdf "^1.0.0"
+    dashdash "^1.12.0"
+    ecc-jsbn "~0.1.1"
+    getpass "^0.1.1"
+    jsbn "~0.1.0"
+    safer-buffer "^2.0.2"
+    tweetnacl "~0.14.0"
+
+stable@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+static-eval@^2.0.0:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.5.tgz#f0782e66999c4b3651cda99d9ce59c507d188f71"
+  integrity sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA==
+  dependencies:
+    escodegen "^1.11.1"
+
+static-extend@^0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+  integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+  dependencies:
+    define-property "^0.2.5"
+    object-copy "^0.1.0"
+
+static-module@^2.2.0:
+  version "2.2.5"
+  resolved "https://registry.yarnpkg.com/static-module/-/static-module-2.2.5.tgz#bd40abceae33da6b7afb84a0e4329ff8852bfbbf"
+  integrity sha512-D8vv82E/Kpmz3TXHKG8PPsCPg+RAX6cbCOyvjM6x04qZtQ47EtJFVwRsdov3n5d6/6ynrOY9XB4JkaZwB2xoRQ==
+  dependencies:
+    concat-stream "~1.6.0"
+    convert-source-map "^1.5.1"
+    duplexer2 "~0.1.4"
+    escodegen "~1.9.0"
+    falafel "^2.1.0"
+    has "^1.0.1"
+    magic-string "^0.22.4"
+    merge-source-map "1.0.4"
+    object-inspect "~1.4.0"
+    quote-stream "~1.0.2"
+    readable-stream "~2.3.3"
+    shallow-copy "~0.0.1"
+    static-eval "^2.0.0"
+    through2 "~2.0.3"
+
+"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+  integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+
+stealthy-require@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
+  integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
+
+stream-browserify@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b"
+  integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==
+  dependencies:
+    inherits "~2.0.1"
+    readable-stream "^2.0.2"
+
+stream-http@^2.7.2:
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc"
+  integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==
+  dependencies:
+    builtin-status-codes "^3.0.0"
+    inherits "^2.0.1"
+    readable-stream "^2.3.6"
+    to-arraybuffer "^1.0.0"
+    xtend "^4.0.0"
+
+string-width@^3.0.0, string-width@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
+  integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
+  dependencies:
+    emoji-regex "^7.0.1"
+    is-fullwidth-code-point "^2.0.0"
+    strip-ansi "^5.1.0"
+
+string.prototype.trimleft@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"
+  integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==
+  dependencies:
+    define-properties "^1.1.3"
+    function-bind "^1.1.1"
+
+string.prototype.trimright@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9"
+  integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==
+  dependencies:
+    define-properties "^1.1.3"
+    function-bind "^1.1.1"
+
+string_decoder@^1.0.0, string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
+strip-ansi@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+  integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+  dependencies:
+    ansi-regex "^2.0.0"
+
+strip-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+  integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+  dependencies:
+    ansi-regex "^3.0.0"
+
+strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
+  integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
+  dependencies:
+    ansi-regex "^4.1.0"
+
+stylehacks@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5"
+  integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==
+  dependencies:
+    browserslist "^4.0.0"
+    postcss "^7.0.0"
+    postcss-selector-parser "^3.0.0"
+
+supports-color@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
+  integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
+
+supports-color@^3.2.3:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+  integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
+  dependencies:
+    has-flag "^1.0.0"
+
+supports-color@^5.3.0, supports-color@^5.4.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
+  integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
+  integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
+  dependencies:
+    has-flag "^4.0.0"
+
+svgo@^1.0.0, svgo@^1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
+  integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
+  dependencies:
+    chalk "^2.4.1"
+    coa "^2.0.2"
+    css-select "^2.0.0"
+    css-select-base-adapter "^0.1.1"
+    css-tree "1.0.0-alpha.37"
+    csso "^4.0.2"
+    js-yaml "^3.13.1"
+    mkdirp "~0.5.1"
+    object.values "^1.1.0"
+    sax "~1.2.4"
+    stable "^0.1.8"
+    unquote "~1.1.1"
+    util.promisify "~1.0.0"
+
+symbol-observable@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+  integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
+symbol-tree@^3.2.2:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
+  integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+
+tailwindcss@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.2.0.tgz#5df317cebac4f3131f275d258a39da1ba3a0f291"
+  integrity sha512-CKvY0ytB3ze5qvynG7qv4XSpQtFNGPbu9pUn8qFdkqgD8Yo/vGss8mhzbqls44YCXTl4G62p3qVZBj45qrd6FQ==
+  dependencies:
+    autoprefixer "^9.4.5"
+    bytes "^3.0.0"
+    chalk "^3.0.0"
+    detective "^5.2.0"
+    fs-extra "^8.0.0"
+    lodash "^4.17.15"
+    node-emoji "^1.8.1"
+    normalize.css "^8.0.1"
+    postcss "^7.0.11"
+    postcss-functions "^3.0.0"
+    postcss-js "^2.0.0"
+    postcss-nested "^4.1.1"
+    postcss-selector-parser "^6.0.0"
+    pretty-hrtime "^1.0.3"
+    reduce-css-calc "^2.1.6"
+    resolve "^1.14.2"
+
+terser@^3.7.3:
+  version "3.17.0"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2"
+  integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ==
+  dependencies:
+    commander "^2.19.0"
+    source-map "~0.6.1"
+    source-map-support "~0.5.10"
+
+terser@^4.3.9:
+  version "4.6.7"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.7.tgz#478d7f9394ec1907f0e488c5f6a6a9a2bad55e72"
+  integrity sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==
+  dependencies:
+    commander "^2.20.0"
+    source-map "~0.6.1"
+    source-map-support "~0.5.12"
+
+through2@^2.0.0, through2@~2.0.3:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
+  integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
+  dependencies:
+    readable-stream "~2.3.6"
+    xtend "~4.0.1"
+
+timers-browserify@^2.0.4:
+  version "2.0.11"
+  resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f"
+  integrity sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==
+  dependencies:
+    setimmediate "^1.0.4"
+
+timsort@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
+  integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
+
+tiny-inflate@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4"
+  integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==
+
+tiny-invariant@^1.0.2:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
+  integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
+
+tiny-warning@^1.0.0, tiny-warning@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
+  integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
+
+to-arraybuffer@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
+  integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
+
+to-fast-properties@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+  integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
+
+to-fast-properties@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+  integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+to-object-path@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+  integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+  dependencies:
+    kind-of "^3.0.2"
+
+to-regex-range@^2.1.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+  integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+  dependencies:
+    is-number "^3.0.0"
+    repeat-string "^1.6.1"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+  integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+  dependencies:
+    define-property "^2.0.2"
+    extend-shallow "^3.0.2"
+    regex-not "^1.0.2"
+    safe-regex "^1.1.0"
+
+toidentifier@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
+  integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
+
+tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
+  integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
+  dependencies:
+    psl "^1.1.28"
+    punycode "^2.1.1"
+
+tr46@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
+  integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
+  dependencies:
+    punycode "^2.1.0"
+
+tty-browserify@0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
+  integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
+
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
+  dependencies:
+    safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+  integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
+
+type-check@~0.3.2:
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+  integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
+  dependencies:
+    prelude-ls "~1.1.2"
+
+typedarray@^0.0.6:
+  version "0.0.6"
+  resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
+  integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+
+typescript@^3.8.3:
+  version "3.8.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
+  integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==
+
+uncss@^0.17.2:
+  version "0.17.3"
+  resolved "https://registry.yarnpkg.com/uncss/-/uncss-0.17.3.tgz#50fc1eb4ed573ffff763458d801cd86e4d69ea11"
+  integrity sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog==
+  dependencies:
+    commander "^2.20.0"
+    glob "^7.1.4"
+    is-absolute-url "^3.0.1"
+    is-html "^1.1.0"
+    jsdom "^14.1.0"
+    lodash "^4.17.15"
+    postcss "^7.0.17"
+    postcss-selector-parser "6.0.2"
+    request "^2.88.0"
+
+unicode-canonical-property-names-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
+  integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==
+
+unicode-match-property-ecmascript@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c"
+  integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==
+  dependencies:
+    unicode-canonical-property-names-ecmascript "^1.0.4"
+    unicode-property-aliases-ecmascript "^1.0.4"
+
+unicode-match-property-value-ecmascript@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531"
+  integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==
+
+unicode-property-aliases-ecmascript@^1.0.4:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4"
+  integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==
+
+unicode-trie@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/unicode-trie/-/unicode-trie-0.3.1.tgz#d671dddd89101a08bac37b6a5161010602052085"
+  integrity sha1-1nHd3YkQGgi6w3tqUWEBBgIFIIU=
+  dependencies:
+    pako "^0.2.5"
+    tiny-inflate "^1.0.0"
+
+union-value@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
+  integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==
+  dependencies:
+    arr-union "^3.1.0"
+    get-value "^2.0.6"
+    is-extendable "^0.1.1"
+    set-value "^2.0.1"
+
+uniq@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+  integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
+
+uniqs@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
+  integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
+
+universalify@^0.1.0:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+  integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+unquote@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
+  integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
+
+unset-value@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+  integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+  dependencies:
+    has-value "^0.3.1"
+    isobject "^3.0.0"
+
+upath@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
+  integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
+
+uri-js@^4.2.2:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
+  integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
+  dependencies:
+    punycode "^2.1.0"
+
+urix@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+  integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+url@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
+  integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=
+  dependencies:
+    punycode "1.3.2"
+    querystring "0.2.0"
+
+use@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+  integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+util-deprecate@^1.0.1, util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+util.promisify@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
+  integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.17.2"
+    has-symbols "^1.0.1"
+    object.getownpropertydescriptors "^2.1.0"
+
+util@0.10.3:
+  version "0.10.3"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
+  integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk=
+  dependencies:
+    inherits "2.0.1"
+
+util@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
+  integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==
+  dependencies:
+    inherits "2.0.3"
+
+uuid@^3.3.2:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+  integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+
+v8-compile-cache@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e"
+  integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==
+
+value-equal@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
+  integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
+
+vendors@^1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e"
+  integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==
+
+verror@1.10.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+  integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
+  dependencies:
+    assert-plus "^1.0.0"
+    core-util-is "1.0.2"
+    extsprintf "^1.2.0"
+
+vlq@^0.2.2:
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26"
+  integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==
+
+vm-browserify@^1.0.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
+  integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
+
+w3c-hr-time@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
+  integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
+  dependencies:
+    browser-process-hrtime "^1.0.0"
+
+w3c-xmlserializer@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794"
+  integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==
+  dependencies:
+    domexception "^1.0.1"
+    webidl-conversions "^4.0.2"
+    xml-name-validator "^3.0.0"
+
+wcwidth@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+  integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
+  dependencies:
+    defaults "^1.0.3"
+
+webidl-conversions@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
+  integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
+
+whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
+  integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
+  dependencies:
+    iconv-lite "0.4.24"
+
+whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
+  integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
+
+whatwg-url@^7.0.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
+  integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
+  dependencies:
+    lodash.sortby "^4.7.0"
+    tr46 "^1.0.1"
+    webidl-conversions "^4.0.2"
+
+which-module@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+  integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+
+which@^1.2.9:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+  integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+  dependencies:
+    isexe "^2.0.0"
+
+word-wrap@~1.2.3:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+  integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wrap-ansi@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
+  integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
+  dependencies:
+    ansi-styles "^3.2.0"
+    string-width "^3.0.0"
+    strip-ansi "^5.0.0"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+ws@^5.1.1:
+  version "5.2.2"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
+  integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==
+  dependencies:
+    async-limiter "~1.0.0"
+
+ws@^6.1.2:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
+  integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
+  dependencies:
+    async-limiter "~1.0.0"
+
+xml-name-validator@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
+  integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
+
+xmlchars@^2.1.1:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
+  integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
+
+xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+  integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
+
+y18n@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+  integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+
+yargs-parser@^15.0.1:
+  version "15.0.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3"
+  integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
+yargs@^14.0.0:
+  version "14.2.3"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414"
+  integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==
+  dependencies:
+    cliui "^5.0.0"
+    decamelize "^1.2.0"
+    find-up "^3.0.0"
+    get-caller-file "^2.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^2.0.0"
+    set-blocking "^2.0.0"
+    string-width "^3.0.0"
+    which-module "^2.0.0"
+    y18n "^4.0.0"
+    yargs-parser "^15.0.1"
diff --git a/users/wpcarro/website/sandbox/covid-uk/default.nix.ignore b/users/wpcarro/website/sandbox/covid-uk/default.nix.ignore
new file mode 100644
index 0000000000..309b1fa64b
--- /dev/null
+++ b/users/wpcarro/website/sandbox/covid-uk/default.nix.ignore
@@ -0,0 +1,16 @@
+{ pkgs, ... }:
+
+pkgs.stdenv.mkDerivation {
+  name = "covid-uk";
+  buildInputs = [];
+  src = builtins.path { path = ./.; name = "covid-uk"; };
+  # TODO(wpcarro): Need to run `yarn install` somehow.
+  # TODO(wpcarro): Need to run `npx tailwindcss build styles.css -o output.css`.
+  buildPhase = ''
+    mkdir -p $out
+    mkdir -p $out/node_modules/chart.js/dist
+    cp $src/node_modules/chart.js/dist/Chart.bundle.min.js $out/node_modules/chart.js/dist
+    cp $src/index.html $src/output.css $out
+  '';
+  dontInstall = true;
+}
diff --git a/users/wpcarro/website/sandbox/covid-uk/index.html b/users/wpcarro/website/sandbox/covid-uk/index.html
new file mode 100644
index 0000000000..15769f7490
--- /dev/null
+++ b/users/wpcarro/website/sandbox/covid-uk/index.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <title>COVID-19 UK</title>
+  <link rel="stylesheet"  href="output.css">
+</head>
+<body class="container mx-auto py-10">
+  <div>
+    <h1 class="text-center">COVID-19 in the UK</h1>
+    <p>
+      Up until recently, I used a couple of resources (i.e.
+      <a href="https://multimedia.scmp.com/infographics/news/china/article/3047038/wuhan-virus/index.html">one</a>, 
+      <a href="https://www.worldometers.info/coronavirus/">two</a>) for tracking
+      an updated number of confirmed covid-19 cases.
+    </p>
+    <p>
+      Given the high speed at which the virus is spreading, I was having a
+      difficult time intuiting the shape of this growth. For example if today
+      the total number of confirmed cases for covid-19 in the UK was 500, I
+      could not remember if yesterday it was 450, 400, or 200.
+    </p>
+    <p>
+      Thankfully someone is <a
+      href="https://github.com/pomber/covid19">publishing this data</a> as a
+      timeseries database. I am currently living in London, so I decided to
+      chart the <u>daily number of confirmed covid-19 cases in the UK</u> to
+      better understand what is happening.
+    </p>
+  </div>
+  <canvas id="myChart" class="py-12"></canvas>
+  <script src="./node_modules/chart.js/dist/Chart.bundle.min.js"></script>
+  <script>
+   var timeseries =
+     fetch('https://pomber.github.io/covid19/timeseries.json')
+       .then(res => res.json())
+       .then(createChart);
+
+   function createChart(data) {
+     var uk = data["United Kingdom"];
+     var data = uk.map(x => x["confirmed"]);
+     var labels = uk.map(x => x["date"]);
+
+     var ctx = document.getElementById('myChart').getContext('2d');
+     var myChart = new Chart(ctx, {
+       type: 'line',
+       data: {
+         labels: labels,
+         datasets: [{
+           label: 'Number of confirmed COVID-19 cases in the U.K.',
+           data: data,
+           backgroundColor: 'rgba(255, 0, 100, 0.2)',
+           borderWidth: 3
+         }]
+       },
+       options: {
+         scales: {
+           yAxes: [{
+             ticks: {
+               beginAtZero: true
+             }
+           }]
+         }
+       }
+     });
+   }
+  </script>
+  <div>
+    <h2 class="text-center">Back of the envelope predictions</h2>
+    <p>
+      From what I have read, a population where 60% of its constituents have
+      been infected with covid-19 and have recovered is said to have "herd
+      immunity". Once a population has herd immunity, the rate at which the
+      virus spreads decreases.
+    </p>
+    <p>
+      Roughly 60M people live in the UK; 60% of 60M is around 40M. Before a
+      population reaches "herd immunity", the total number of <em>true
+      covid-19 cases</em> <u>doubles every five days</u>. Therefore in <u>fifty
+      days</u> you might expect the number of true cases to be <u>1000x
+      larger</u> than what it is today.
+    </p>
+    <p>
+      So if you think the total number of <em>true covid-19 cases</em>
+      <u>today</u> is 40,000 then you might expect the rate of growth to slow
+      down in a little less than two months.
+    </p>
+    <p>
+      Thank you for reading.
+    </p>
+  </div>
+  <footer class="pt-5 mb-8 lg:flex">
+    <a class="block py-2 lg:w-1/4 text-center hover:underline" href="https://learn.wpcarro.dev">Learn</a>
+    <a class="block py-2 lg:w-1/4 text-center hover:underline" href="https://blog.wpcarro.dev">Blog</a>
+    <a class="block py-2 lg:w-1/4 text-center hover:underline" href="https://twitter.com/wpcarro">Twitter</a>
+    <a class="block py-2 lg:w-1/4 text-center hover:underline" href="https://github.com/wpcarro">Github</a>
+  </footer>
+</body>
+</html>
diff --git a/users/wpcarro/website/sandbox/covid-uk/package.json b/users/wpcarro/website/sandbox/covid-uk/package.json
new file mode 100644
index 0000000000..939506c8cc
--- /dev/null
+++ b/users/wpcarro/website/sandbox/covid-uk/package.json
@@ -0,0 +1,16 @@
+{
+  "name": "covid-uk",
+  "version": "1.0.0",
+  "description": "",
+  "main": "index.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [],
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "chart.js": "^2.9.3",
+    "tailwindcss": "^1.2.0"
+  }
+}
diff --git a/users/wpcarro/website/sandbox/covid-uk/shell.nix b/users/wpcarro/website/sandbox/covid-uk/shell.nix
new file mode 100644
index 0000000000..f918c4033e
--- /dev/null
+++ b/users/wpcarro/website/sandbox/covid-uk/shell.nix
@@ -0,0 +1,8 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs; [
+    yarn
+    nodejs
+  ];
+}
diff --git a/users/wpcarro/website/sandbox/covid-uk/styles.css b/users/wpcarro/website/sandbox/covid-uk/styles.css
new file mode 100644
index 0000000000..1f48906d8f
--- /dev/null
+++ b/users/wpcarro/website/sandbox/covid-uk/styles.css
@@ -0,0 +1,28 @@
+@tailwind base;
+
+body {
+  @apply font-mono;
+}
+
+h1 {
+  @apply text-3xl mb-5 font-bold;
+}
+
+h2 {
+  @apply text-2xl mb-5 font-bold;
+}
+
+h3 {
+  @apply text-xl mb-5 font-bold;
+}
+
+a {
+  @apply text-blue-600 underline;
+}
+
+p {
+  @apply mt-2 mb-5;
+}
+
+@tailwind components;
+@tailwind utilities;
diff --git a/users/wpcarro/website/sandbox/covid-uk/tailwind.config.js b/users/wpcarro/website/sandbox/covid-uk/tailwind.config.js
new file mode 100644
index 0000000000..af829e20f9
--- /dev/null
+++ b/users/wpcarro/website/sandbox/covid-uk/tailwind.config.js
@@ -0,0 +1,7 @@
+module.exports = {
+  theme: {
+    extend: {},
+  },
+  variants: {},
+  plugins: [],
+}
diff --git a/users/wpcarro/website/sandbox/covid-uk/yarn.lock b/users/wpcarro/website/sandbox/covid-uk/yarn.lock
new file mode 100644
index 0000000000..3e66831c9e
--- /dev/null
+++ b/users/wpcarro/website/sandbox/covid-uk/yarn.lock
@@ -0,0 +1,542 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@types/color-name@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
+  integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
+
+acorn-node@^1.6.1:
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
+  integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==
+  dependencies:
+    acorn "^7.0.0"
+    acorn-walk "^7.0.0"
+    xtend "^4.0.2"
+
+acorn-walk@^7.0.0:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e"
+  integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==
+
+acorn@^7.0.0:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
+  integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==
+
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+ansi-styles@^4.1.0:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
+  integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
+  dependencies:
+    "@types/color-name" "^1.1.1"
+    color-convert "^2.0.1"
+
+autoprefixer@^9.4.5:
+  version "9.7.4"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378"
+  integrity sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==
+  dependencies:
+    browserslist "^4.8.3"
+    caniuse-lite "^1.0.30001020"
+    chalk "^2.4.2"
+    normalize-range "^0.1.2"
+    num2fraction "^1.2.2"
+    postcss "^7.0.26"
+    postcss-value-parser "^4.0.2"
+
+balanced-match@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+browserslist@^4.8.3:
+  version "4.10.0"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.10.0.tgz#f179737913eaf0d2b98e4926ac1ca6a15cbcc6a9"
+  integrity sha512-TpfK0TDgv71dzuTsEAlQiHeWQ/tiPqgNZVdv046fvNtBZrjbv2O3TsWCDU0AWGJJKCF/KsjNdLzR9hXOsh/CfA==
+  dependencies:
+    caniuse-lite "^1.0.30001035"
+    electron-to-chromium "^1.3.378"
+    node-releases "^1.1.52"
+    pkg-up "^3.1.0"
+
+bytes@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
+  integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
+
+camelcase-css@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
+  integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
+
+caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001035:
+  version "1.0.30001035"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz#2bb53b8aa4716b2ed08e088d4dc816a5fe089a1e"
+  integrity sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ==
+
+chalk@^2.4.1, chalk@^2.4.2:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
+  integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chart.js@^2.9.3:
+  version "2.9.3"
+  resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.3.tgz#ae3884114dafd381bc600f5b35a189138aac1ef7"
+  integrity sha512-+2jlOobSk52c1VU6fzkh3UwqHMdSlgH1xFv9FKMqHiNCpXsGPQa/+81AFa+i3jZ253Mq9aAycPwDjnn1XbRNNw==
+  dependencies:
+    chartjs-color "^2.1.0"
+    moment "^2.10.2"
+
+chartjs-color-string@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71"
+  integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
+  dependencies:
+    color-name "^1.0.0"
+
+chartjs-color@^2.1.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0"
+  integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==
+  dependencies:
+    chartjs-color-string "^0.6.0"
+    color-convert "^1.9.3"
+
+color-convert@^1.9.0, color-convert@^1.9.3:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+color-name@^1.0.0, color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+css-unit-converter@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996"
+  integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=
+
+cssesc@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
+  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+
+defined@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
+  integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=
+
+detective@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b"
+  integrity sha512-6SsIx+nUUbuK0EthKjv0zrdnajCCXVYGmbYYiYjFVpzcjwEs/JMDZ8tPRG29J/HhN56t3GJp2cGSWDRjjot8Pg==
+  dependencies:
+    acorn-node "^1.6.1"
+    defined "^1.0.0"
+    minimist "^1.1.1"
+
+electron-to-chromium@^1.3.378:
+  version "1.3.379"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.379.tgz#81dc5e82a3e72bbb830d93e15bc35eda2bbc910e"
+  integrity sha512-NK9DBBYEBb5f9D7zXI0hiE941gq3wkBeQmXs1ingigA/jnTg5mhwY2Z5egwA+ZI8OLGKCx0h1Cl8/xeuIBuLlg==
+
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+find-up@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+  integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+  dependencies:
+    locate-path "^3.0.0"
+
+fs-extra@^8.0.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+  integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+  dependencies:
+    graceful-fs "^4.2.0"
+    jsonfile "^4.0.0"
+    universalify "^0.1.0"
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+glob@^7.1.2:
+  version "7.1.6"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+  integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.0.4"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
+  integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+indexes-of@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
+  integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+jsonfile@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+  integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
+  optionalDependencies:
+    graceful-fs "^4.1.6"
+
+locate-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+  integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+  dependencies:
+    p-locate "^3.0.0"
+    path-exists "^3.0.0"
+
+lodash.toarray@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
+  integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE=
+
+lodash@^4.17.15:
+  version "4.17.15"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+  integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+minimatch@^3.0.4:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist@^1.1.1:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+  integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
+moment@^2.10.2:
+  version "2.24.0"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
+  integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==
+
+node-emoji@^1.8.1:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da"
+  integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==
+  dependencies:
+    lodash.toarray "^4.4.0"
+
+node-releases@^1.1.52:
+  version "1.1.52"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.52.tgz#bcffee3e0a758e92e44ecfaecd0a47554b0bcba9"
+  integrity sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ==
+  dependencies:
+    semver "^6.3.0"
+
+normalize-range@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+  integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
+
+normalize.css@^8.0.1:
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3"
+  integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==
+
+num2fraction@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
+  integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
+
+object-assign@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+once@^1.3.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+  dependencies:
+    wrappy "1"
+
+p-limit@^2.0.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e"
+  integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==
+  dependencies:
+    p-try "^2.0.0"
+
+p-locate@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+  integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+  dependencies:
+    p-limit "^2.0.0"
+
+p-try@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+  integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+path-exists@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+  integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-parse@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+  integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+pkg-up@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
+  integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
+  dependencies:
+    find-up "^3.0.0"
+
+postcss-functions@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-functions/-/postcss-functions-3.0.0.tgz#0e94d01444700a481de20de4d55fb2640564250e"
+  integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4=
+  dependencies:
+    glob "^7.1.2"
+    object-assign "^4.1.1"
+    postcss "^6.0.9"
+    postcss-value-parser "^3.3.0"
+
+postcss-js@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-2.0.3.tgz#a96f0f23ff3d08cec7dc5b11bf11c5f8077cdab9"
+  integrity sha512-zS59pAk3deu6dVHyrGqmC3oDXBdNdajk4k1RyxeVXCrcEDBUBHoIhE4QTsmhxgzXxsaqFDAkUZfmMa5f/N/79w==
+  dependencies:
+    camelcase-css "^2.0.1"
+    postcss "^7.0.18"
+
+postcss-nested@^4.1.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-4.2.1.tgz#4bc2e5b35e3b1e481ff81e23b700da7f82a8b248"
+  integrity sha512-AMayXX8tS0HCp4O4lolp4ygj9wBn32DJWXvG6gCv+ZvJrEa00GUxJcJEEzMh87BIe6FrWdYkpR2cuyqHKrxmXw==
+  dependencies:
+    postcss "^7.0.21"
+    postcss-selector-parser "^6.0.2"
+
+postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c"
+  integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==
+  dependencies:
+    cssesc "^3.0.0"
+    indexes-of "^1.0.1"
+    uniq "^1.0.1"
+
+postcss-value-parser@^3.3.0:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
+  integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
+
+postcss-value-parser@^4.0.2:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d"
+  integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==
+
+postcss@^6.0.9:
+  version "6.0.23"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
+  integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
+  dependencies:
+    chalk "^2.4.1"
+    source-map "^0.6.1"
+    supports-color "^5.4.0"
+
+postcss@^7.0.11, postcss@^7.0.18, postcss@^7.0.21, postcss@^7.0.26:
+  version "7.0.27"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
+  integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==
+  dependencies:
+    chalk "^2.4.2"
+    source-map "^0.6.1"
+    supports-color "^6.1.0"
+
+pretty-hrtime@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
+  integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
+
+reduce-css-calc@^2.1.6:
+  version "2.1.7"
+  resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.7.tgz#1ace2e02c286d78abcd01fd92bfe8097ab0602c2"
+  integrity sha512-fDnlZ+AybAS3C7Q9xDq5y8A2z+lT63zLbynew/lur/IR24OQF5x98tfNwf79mzEdfywZ0a2wpM860FhFfMxZlA==
+  dependencies:
+    css-unit-converter "^1.1.1"
+    postcss-value-parser "^3.3.0"
+
+resolve@^1.14.2:
+  version "1.15.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
+  integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
+  dependencies:
+    path-parse "^1.0.6"
+
+semver@^6.3.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+supports-color@^5.3.0, supports-color@^5.4.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
+  integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
+  integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
+  dependencies:
+    has-flag "^4.0.0"
+
+tailwindcss@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-1.2.0.tgz#5df317cebac4f3131f275d258a39da1ba3a0f291"
+  integrity sha512-CKvY0ytB3ze5qvynG7qv4XSpQtFNGPbu9pUn8qFdkqgD8Yo/vGss8mhzbqls44YCXTl4G62p3qVZBj45qrd6FQ==
+  dependencies:
+    autoprefixer "^9.4.5"
+    bytes "^3.0.0"
+    chalk "^3.0.0"
+    detective "^5.2.0"
+    fs-extra "^8.0.0"
+    lodash "^4.17.15"
+    node-emoji "^1.8.1"
+    normalize.css "^8.0.1"
+    postcss "^7.0.11"
+    postcss-functions "^3.0.0"
+    postcss-js "^2.0.0"
+    postcss-nested "^4.1.1"
+    postcss-selector-parser "^6.0.0"
+    pretty-hrtime "^1.0.3"
+    reduce-css-calc "^2.1.6"
+    resolve "^1.14.2"
+
+uniq@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+  integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
+
+universalify@^0.1.0:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+  integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+xtend@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+  integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
diff --git a/users/wpcarro/website/sandbox/default.nix.ignore b/users/wpcarro/website/sandbox/default.nix.ignore
new file mode 100644
index 0000000000..d7b4940a55
--- /dev/null
+++ b/users/wpcarro/website/sandbox/default.nix.ignore
@@ -0,0 +1,13 @@
+{ pkgs, ... }:
+
+pkgs.stdenv.mkDerivation {
+  name = "covid-uk";
+  buildInputs = [];
+  src = builtins.path { path = ./.; name = "sandbox"; };
+  buildPhase = ''
+    mkdir -p $out
+    cp $src/index.html $out
+    cp -r ${depot.users.wpcarro.website.sandbox.covid-uk} $out/covid-uk
+  '';
+  dontInstall = true;
+}
diff --git a/users/wpcarro/website/sandbox/github-issues-service/README.md b/users/wpcarro/website/sandbox/github-issues-service/README.md
new file mode 100644
index 0000000000..2af8600143
--- /dev/null
+++ b/users/wpcarro/website/sandbox/github-issues-service/README.md
@@ -0,0 +1,28 @@
+# Github Issues Service (GIS)
+
+> 'Cause I got issues. But you got 'em too...
+> - [Issues by Julia Michaels][issues]
+
+You have a website and your users want to request features or report bugs. How
+do they do this?
+
+Our robot, GIS, can help you. GIS adds a widget to your website that allows
+users to easily request features and report bugs.
+
+## Getting Started
+
+If Github is hosting your website's source code, you're ready to start using
+GIS. GIS works with public and private repositories.
+
+Let's adopt Github's notion of "issues" to group feature requests and bug
+reports together. When users click the GIS widget to create an issue, GIS
+displays a modal form that the user completes. When the user submits the form,
+GIS creates an issue on your Github repository. Now your team can use all of
+Github's rich issue-tracking tools to manage your issues.
+
+## Installation
+
+To add GIS to your website, register your Github repository with us and we'll
+give you a snippet to add to your website's HTML. It's that simple.
+
+[issues]: https://www.youtube.com/watch?v=9Ke4480MicU
diff --git a/users/wpcarro/website/sandbox/index.html b/users/wpcarro/website/sandbox/index.html
new file mode 100644
index 0000000000..ecd5475af2
--- /dev/null
+++ b/users/wpcarro/website/sandbox/index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>sandbox.wpcarro.dev</title>
+  </head>
+  <body>
+    <h1>Projects</h1>
+    <ul>
+      <li>
+        <a href="/covid-uk">COVID-19 in the UK</a>
+      </li>
+    </ul>
+  </body>
+</html>
diff --git a/users/wpcarro/website/sandbox/learnpianochords/.envrc b/users/wpcarro/website/sandbox/learnpianochords/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/website/sandbox/learnpianochords/.gitignore b/users/wpcarro/website/sandbox/learnpianochords/.gitignore
new file mode 100644
index 0000000000..0c1c258f65
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/.gitignore
@@ -0,0 +1,2 @@
+/elm-stuff
+/Main.min.js
diff --git a/users/wpcarro/website/sandbox/learnpianochords/README.md b/users/wpcarro/website/sandbox/learnpianochords/README.md
new file mode 100644
index 0000000000..2527f4b963
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/README.md
@@ -0,0 +1,57 @@
+# Learn Piano Chords (LPC)
+
+Are you a musician looking for a more effective way to improve your craft? Maybe
+you're a music teacher looking to create useful exercises to give your students.
+
+Studying music theory can be a fruitful undertaking, but it can often overwhelm
+or bore students. I think that if practicing is enjoyable, students will
+practice more. Practice doesn't make perfect; *perfect* practice makes perfect.
+Learn Piano Chords is a web app that lowers the barrier to practicing and
+internalizing music theory.
+
+## How does it work?
+
+1. Grab a cell phone or a laptop and your instrument.
+2. Open a web browser and visit the Learn Piano Chords app (URL and app
+   forthcoming).
+6. Set the tempo (i.e. pace) at which you would like to practice.
+4. Set the target duration of your session.
+5. Select the key(s) and chord(s) you would like to practice.
+7. LPC will display chords at various rhythmic intervals during your practice
+   session. It is your job to play these chords in time before the next chord
+   appears.
+
+## Highlights
+
+Here are some useful features of LPC:
+- Tempo: Set the rate at which LPC displays chords.
+- Predefined practice sessions: LPC offers users a few practice sessions to get
+  users started. The goal, however, is to teach users to create their own
+  bespoke practice sessions. LPC aims to foster a community of practitioners who
+  curate and share their practice sessions.
+- Whitelist / blacklist: Construct the set of chords you would like to
+  practice. Let's say you only want to practice triads in the keys of F, C, and
+  G. Would you also like to avoid diminished chords? Or maybe you *only* want to
+  practice major-7th chords for *all* keys. LPC supports all of these scenarios
+  and many others. You can save these chord configurations to reuse them at any
+  time. You can also share chord configurations with other LPC users if you find
+  the practice useful.
+- Inversions: Every chord has inversions. For instance, every triad (i.e. chord
+  composed of three notes) has three inversions: root, second, and third
+  positions. LPC acknowledges all of the positions in which chords may appear
+  and helps you study all, some, or none of these inversions.
+- Harmony: LPC understands basic harmony and can sort the chords you would like
+  to train in various harmonious permutations.
+- Chaos-mode: Feeling confident? Throw the classical notions of harmony to the
+  wayside and use LPC in "chaos-mode" where LPC samples randomly from the Circle
+  of Fifths.
+
+## Developing
+
+If you're interested in contributing, the following will create an environment
+in which you can develop:
+
+```shell
+$ nix-shell
+$ elm-live -- src/Main.elm --output=elm.js
+```
diff --git a/users/wpcarro/website/sandbox/learnpianochords/default.nix b/users/wpcarro/website/sandbox/learnpianochords/default.nix
new file mode 100644
index 0000000000..7cfdf7c451
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/default.nix
@@ -0,0 +1,63 @@
+{ pkgs, ... }:
+
+with pkgs;
+
+let
+  mkDerivation =
+    { srcs ? ./elm-srcs.nix
+    , src
+    , name
+    , srcdir ? "./src"
+    , targets ? [ ]
+    , registryDat ? ./registry.dat
+    , outputJavaScript ? false
+    }:
+    stdenv.mkDerivation {
+      inherit name src;
+
+      buildInputs = [ elmPackages.elm ]
+        ++ lib.optional outputJavaScript nodePackages.uglify-js;
+
+      buildPhase = elmPackages.fetchElmDeps {
+        elmPackages = import srcs;
+        elmVersion = "0.19.1";
+        inherit registryDat;
+      };
+
+      installPhase =
+        let
+          elmfile = module: "${srcdir}/${builtins.replaceStrings ["."] ["/"] module}.elm";
+          extension = if outputJavaScript then "js" else "html";
+        in
+        ''
+          mkdir -p $out/share/doc
+          ${lib.concatStrings (map (module: ''
+            echo "compiling ${elmfile module}"
+            elm make ${elmfile module} --output $out/${module}.${extension} --docs $out/share/doc/${module}.json
+            ${lib.optionalString outputJavaScript ''
+              echo "minifying ${elmfile module}"
+              uglifyjs $out/${module}.${extension} --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters,keep_fargs=false,unsafe_comps,unsafe' \
+                  | uglifyjs --mangle --output $out/${module}.min.${extension}
+            ''}
+          '') targets)}
+        '';
+    };
+  mainDotElm = mkDerivation {
+    name = "elm-app-0.1.0";
+    srcs = ./elm-srcs.nix;
+    src = builtins.path { path = ./.; name = "learnpianochords"; };
+    targets = [ "Main" ];
+    srcdir = "./src";
+    outputJavaScript = true;
+  };
+in
+stdenv.mkDerivation {
+  name = "learn-piano-chords";
+  buildInputs = [ ];
+  src = builtins.path { path = ./.; name = "learnpianochords"; };
+  buildPhase = ''
+    mkdir -p $out
+    cp index.html output.css ${mainDotElm}/Main.min.js $out
+  '';
+  dontInstall = true;
+}
diff --git a/users/wpcarro/website/sandbox/learnpianochords/elm-srcs.nix b/users/wpcarro/website/sandbox/learnpianochords/elm-srcs.nix
new file mode 100644
index 0000000000..c62262e683
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/elm-srcs.nix
@@ -0,0 +1,67 @@
+{
+
+  "elm-community/maybe-extra" = {
+    sha256 = "0qslmgswa625d218djd3p62pnqcrz38f5p558mbjl6kc1ss0kzv3";
+    version = "5.2.0";
+  };
+
+  "elm/html" = {
+    sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
+    version = "1.0.0";
+  };
+
+  "elm-community/random-extra" = {
+    sha256 = "1dg2nz77w2cvp16xazbdsxkkw0xc9ycqpkd032faqdyky6gmz9g6";
+    version = "3.1.0";
+  };
+
+  "elm/svg" = {
+    sha256 = "1cwcj73p61q45wqwgqvrvz3aypjyy3fw732xyxdyj6s256hwkn0k";
+    version = "1.0.1";
+  };
+
+  "elm/browser" = {
+    sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
+    version = "1.0.2";
+  };
+
+  "elm/core" = {
+    sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
+    version = "1.0.5";
+  };
+
+  "elm-community/list-extra" = {
+    sha256 = "1ayv3148drynqnxdfwpjxal8vwzgsjqanjg7yxp6lhdcbkxgd3vd";
+    version = "8.2.3";
+  };
+
+  "elm/random" = {
+    sha256 = "138n2455wdjwa657w6sjq18wx2r0k60ibpc4frhbqr50sncxrfdl";
+    version = "1.0.0";
+  };
+
+  "elm/time" = {
+    sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
+    version = "1.0.0";
+  };
+
+  "elm/json" = {
+    sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
+    version = "1.1.3";
+  };
+
+  "owanturist/elm-union-find" = {
+    sha256 = "13gm7msnp0gr1lqia5m7m4lhy3m6kvjg37d304whb3psn88wqhj5";
+    version = "1.0.0";
+  };
+
+  "elm/url" = {
+    sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
+    version = "1.0.0";
+  };
+
+  "elm/virtual-dom" = {
+    sha256 = "0q1v5gi4g336bzz1lgwpn5b1639lrn63d8y6k6pimcyismp2i1yg";
+    version = "1.0.2";
+  };
+}
diff --git a/users/wpcarro/website/sandbox/learnpianochords/elm.json b/users/wpcarro/website/sandbox/learnpianochords/elm.json
new file mode 100644
index 0000000000..a95f80408e
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/elm.json
@@ -0,0 +1,30 @@
+{
+    "type": "application",
+    "source-directories": [
+        "src"
+    ],
+    "elm-version": "0.19.1",
+    "dependencies": {
+        "direct": {
+            "elm/browser": "1.0.2",
+            "elm/core": "1.0.5",
+            "elm/html": "1.0.0",
+            "elm/random": "1.0.0",
+            "elm/svg": "1.0.1",
+            "elm/time": "1.0.0",
+            "elm-community/list-extra": "8.2.3",
+            "elm-community/maybe-extra": "5.2.0",
+            "elm-community/random-extra": "3.1.0"
+        },
+        "indirect": {
+            "elm/json": "1.1.3",
+            "elm/url": "1.0.0",
+            "elm/virtual-dom": "1.0.2",
+            "owanturist/elm-union-find": "1.0.0"
+        }
+    },
+    "test-dependencies": {
+        "direct": {},
+        "indirect": {}
+    }
+}
diff --git a/users/wpcarro/website/sandbox/learnpianochords/ideas.org b/users/wpcarro/website/sandbox/learnpianochords/ideas.org
new file mode 100644
index 0000000000..4c2372280e
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/ideas.org
@@ -0,0 +1,3 @@
+* Support a frequency table of all of the chords
+* Support using spaced-repetition to help populate the frequency table of chords
+* If doing a frequency table, support left and right hands
diff --git a/users/wpcarro/website/sandbox/learnpianochords/index.css b/users/wpcarro/website/sandbox/learnpianochords/index.css
new file mode 100644
index 0000000000..b5c61c9567
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/index.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/users/wpcarro/website/sandbox/learnpianochords/index.html b/users/wpcarro/website/sandbox/learnpianochords/index.html
new file mode 100644
index 0000000000..5687c29eb7
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>Learn Piano Chords</title>
+    <link rel="stylesheet" href="./output.css" />
+    <script src="./Main.min.js"></script>
+  </head>
+  <body class="font-serif">
+    <div id="mount"></div>
+    <script>
+     Elm.Main.init({node: document.getElementById("mount")});
+    </script>
+  </body>
+</html>
diff --git a/users/wpcarro/website/sandbox/learnpianochords/output.css b/users/wpcarro/website/sandbox/learnpianochords/output.css
new file mode 100644
index 0000000000..b522419aa3
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/output.css
@@ -0,0 +1,103571 @@
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+   ========================================================================== */
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+
+html {
+  line-height: 1.15; /* 1 */
+  -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/* Sections
+   ========================================================================== */
+
+/**
+ * Remove the margin in all browsers.
+ */
+
+body {
+  margin: 0;
+}
+
+/**
+ * Render the `main` element consistently in IE.
+ */
+
+main {
+  display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+/* Grouping content
+   ========================================================================== */
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+  box-sizing: content-box; /* 1 */
+  height: 0; /* 1 */
+  overflow: visible; /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+   ========================================================================== */
+
+/**
+ * Remove the gray background on active links in IE 10.
+ */
+
+a {
+  background-color: transparent;
+}
+
+/**
+ * 1. Remove the bottom border in Chrome 57-
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+  border-bottom: none; /* 1 */
+  text-decoration: underline; /* 2 */
+  -webkit-text-decoration: underline dotted;
+          text-decoration: underline dotted; /* 2 */
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+  font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+  font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+sup {
+  top: -0.5em;
+}
+
+/* Embedded content
+   ========================================================================== */
+
+/**
+ * Remove the border on images inside links in IE 10.
+ */
+
+img {
+  border-style: none;
+}
+
+/* Forms
+   ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers.
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+  font-family: inherit; /* 1 */
+  font-size: 100%; /* 1 */
+  line-height: 1.15; /* 1 */
+  margin: 0; /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input { /* 1 */
+  overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select { /* 1 */
+  text-transform: none;
+}
+
+/**
+ * Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+  -webkit-appearance: button;
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+  border-style: none;
+  padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+  outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+
+fieldset {
+  padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ *    `fieldset` elements in all browsers.
+ */
+
+legend {
+  box-sizing: border-box; /* 1 */
+  color: inherit; /* 2 */
+  display: table; /* 1 */
+  max-width: 100%; /* 1 */
+  padding: 0; /* 3 */
+  white-space: normal; /* 1 */
+}
+
+/**
+ * Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+  vertical-align: baseline;
+}
+
+/**
+ * Remove the default vertical scrollbar in IE 10+.
+ */
+
+textarea {
+  overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10.
+ * 2. Remove the padding in IE 10.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+  box-sizing: border-box; /* 1 */
+  padding: 0; /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+  -webkit-appearance: textfield; /* 1 */
+  outline-offset: -2px; /* 2 */
+}
+
+/**
+ * Remove the inner padding in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+  -webkit-appearance: button; /* 1 */
+  font: inherit; /* 2 */
+}
+
+/* Interactive
+   ========================================================================== */
+
+/*
+ * Add the correct display in Edge, IE 10+, and Firefox.
+ */
+
+details {
+  display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+  display: list-item;
+}
+
+/* Misc
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 10+.
+ */
+
+template {
+  display: none;
+}
+
+/**
+ * Add the correct display in IE 10.
+ */
+
+[hidden] {
+  display: none;
+}
+
+/**
+ * Manually forked from SUIT CSS Base: https://github.com/suitcss/base
+ * A thin layer on top of normalize.css that provides a starting point more
+ * suitable for web applications.
+ */
+
+/**
+ * Removes the default spacing and border for appropriate elements.
+ */
+
+blockquote,
+dl,
+dd,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+hr,
+figure,
+p,
+pre {
+  margin: 0;
+}
+
+button {
+  background-color: transparent;
+  background-image: none;
+}
+
+/**
+ * Work around a Firefox/IE bug where the transparent `button` background
+ * results in a loss of the default `button` focus styles.
+ */
+
+button:focus {
+  outline: 1px dotted;
+  outline: 5px auto -webkit-focus-ring-color;
+}
+
+fieldset {
+  margin: 0;
+  padding: 0;
+}
+
+ol,
+ul {
+  list-style: none;
+  margin: 0;
+  padding: 0;
+}
+
+/**
+ * Tailwind custom reset styles
+ */
+
+/**
+ * 1. Use the user's configured `sans` font-family (with Tailwind's default
+ *    sans-serif font stack as a fallback) as a sane default.
+ * 2. Use Tailwind's default "normal" line-height so the user isn't forced
+ *    to override it to ensure consistency even when using the default theme.
+ */
+
+html {
+  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 1 */
+  line-height: 1.5; /* 2 */
+}
+
+/**
+ * 1. Prevent padding and border from affecting element width.
+ *
+ *    We used to set this in the html element and inherit from
+ *    the parent element for everything else. This caused issues
+ *    in shadow-dom-enhanced elements like <details> where the content
+ *    is wrapped by a div with box-sizing set to `content-box`.
+ *
+ *    https://github.com/mozdevs/cssremedy/issues/4
+ *
+ *
+ * 2. Allow adding a border to an element by just adding a border-width.
+ *
+ *    By default, the way the browser specifies that an element should have no
+ *    border is by setting it's border-style to `none` in the user-agent
+ *    stylesheet.
+ *
+ *    In order to easily add borders to elements by just setting the `border-width`
+ *    property, we change the default border-style for all elements to `solid`, and
+ *    use border-width to hide them instead. This way our `border` utilities only
+ *    need to set the `border-width` property instead of the entire `border`
+ *    shorthand, making our border utilities much more straightforward to compose.
+ *
+ *    https://github.com/tailwindcss/tailwindcss/pull/116
+ */
+
+*,
+::before,
+::after {
+  box-sizing: border-box; /* 1 */
+  border-width: 0; /* 2 */
+  border-style: solid; /* 2 */
+  border-color: #e2e8f0; /* 2 */
+}
+
+/*
+ * Ensure horizontal rules are visible by default
+ */
+
+hr {
+  border-top-width: 1px;
+}
+
+/**
+ * Undo the `border-style: none` reset that Normalize applies to images so that
+ * our `border-{width}` utilities have the expected effect.
+ *
+ * The Normalize reset is unnecessary for us since we default the border-width
+ * to 0 on all elements.
+ *
+ * https://github.com/tailwindcss/tailwindcss/issues/362
+ */
+
+img {
+  border-style: solid;
+}
+
+textarea {
+  resize: vertical;
+}
+
+input::-moz-placeholder, textarea::-moz-placeholder {
+  color: #a0aec0;
+}
+
+input:-ms-input-placeholder, textarea:-ms-input-placeholder {
+  color: #a0aec0;
+}
+
+input::placeholder,
+textarea::placeholder {
+  color: #a0aec0;
+}
+
+button,
+[role="button"] {
+  cursor: pointer;
+}
+
+table {
+  border-collapse: collapse;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  font-size: inherit;
+  font-weight: inherit;
+}
+
+/**
+ * Reset links to optimize for opt-in styling instead of
+ * opt-out.
+ */
+
+a {
+  color: inherit;
+  text-decoration: inherit;
+}
+
+/**
+ * Reset form element properties that are easy to forget to
+ * style explicitly so you don't inadvertently introduce
+ * styles that deviate from your design system. These styles
+ * supplement a partial reset that is already applied by
+ * normalize.css.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+  padding: 0;
+  line-height: inherit;
+  color: inherit;
+}
+
+/**
+ * Use the configured 'mono' font family for elements that
+ * are expected to be rendered with a monospace font, falling
+ * back to the system monospace stack if there is no configured
+ * 'mono' font family.
+ */
+
+pre,
+code,
+kbd,
+samp {
+  font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+/**
+ * Make replaced elements `display: block` by default as that's
+ * the behavior you want almost all of the time. Inspired by
+ * CSS Remedy, with `svg` added as well.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/14
+ */
+
+img,
+svg,
+video,
+canvas,
+audio,
+iframe,
+embed,
+object {
+  display: block;
+  vertical-align: middle;
+}
+
+/**
+ * Constrain images and videos to the parent width and preserve
+ * their instrinsic aspect ratio.
+ *
+ * https://github.com/mozdevs/cssremedy/issues/14
+ */
+
+img,
+video {
+  max-width: 100%;
+  height: auto;
+}
+
+.container {
+  width: 100%;
+}
+
+@media (min-width: 640px) {
+  .container {
+    max-width: 640px;
+  }
+}
+
+@media (min-width: 768px) {
+  .container {
+    max-width: 768px;
+  }
+}
+
+@media (min-width: 1024px) {
+  .container {
+    max-width: 1024px;
+  }
+}
+
+@media (min-width: 1280px) {
+  .container {
+    max-width: 1280px;
+  }
+}
+
+.space-y-0 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(0px * var(--space-y-reverse));
+}
+
+.space-x-0 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(0px * var(--space-x-reverse));
+  margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-1 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(0.25rem * var(--space-y-reverse));
+}
+
+.space-x-1 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(0.25rem * var(--space-x-reverse));
+  margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-2 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(0.5rem * var(--space-y-reverse));
+}
+
+.space-x-2 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(0.5rem * var(--space-x-reverse));
+  margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-3 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(0.75rem * var(--space-y-reverse));
+}
+
+.space-x-3 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(0.75rem * var(--space-x-reverse));
+  margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-4 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(1rem * var(--space-y-reverse));
+}
+
+.space-x-4 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(1rem * var(--space-x-reverse));
+  margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-5 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(1.25rem * var(--space-y-reverse));
+}
+
+.space-x-5 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(1.25rem * var(--space-x-reverse));
+  margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-6 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(1.5rem * var(--space-y-reverse));
+}
+
+.space-x-6 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(1.5rem * var(--space-x-reverse));
+  margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-8 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(2rem * var(--space-y-reverse));
+}
+
+.space-x-8 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(2rem * var(--space-x-reverse));
+  margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-10 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(2.5rem * var(--space-y-reverse));
+}
+
+.space-x-10 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(2.5rem * var(--space-x-reverse));
+  margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-12 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(3rem * var(--space-y-reverse));
+}
+
+.space-x-12 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(3rem * var(--space-x-reverse));
+  margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-16 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(4rem * var(--space-y-reverse));
+}
+
+.space-x-16 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(4rem * var(--space-x-reverse));
+  margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-20 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(5rem * var(--space-y-reverse));
+}
+
+.space-x-20 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(5rem * var(--space-x-reverse));
+  margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-24 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(6rem * var(--space-y-reverse));
+}
+
+.space-x-24 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(6rem * var(--space-x-reverse));
+  margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-32 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(8rem * var(--space-y-reverse));
+}
+
+.space-x-32 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(8rem * var(--space-x-reverse));
+  margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-40 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(10rem * var(--space-y-reverse));
+}
+
+.space-x-40 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(10rem * var(--space-x-reverse));
+  margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-48 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(12rem * var(--space-y-reverse));
+}
+
+.space-x-48 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(12rem * var(--space-x-reverse));
+  margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-56 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(14rem * var(--space-y-reverse));
+}
+
+.space-x-56 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(14rem * var(--space-x-reverse));
+  margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-64 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(16rem * var(--space-y-reverse));
+}
+
+.space-x-64 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(16rem * var(--space-x-reverse));
+  margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-px > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(1px * var(--space-y-reverse));
+}
+
+.space-x-px > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(1px * var(--space-x-reverse));
+  margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-1 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+}
+
+.-space-x-1 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-0.25rem * var(--space-x-reverse));
+  margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-2 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+}
+
+.-space-x-2 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-0.5rem * var(--space-x-reverse));
+  margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-3 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+}
+
+.-space-x-3 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-0.75rem * var(--space-x-reverse));
+  margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-4 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-1rem * var(--space-y-reverse));
+}
+
+.-space-x-4 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-1rem * var(--space-x-reverse));
+  margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-5 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+}
+
+.-space-x-5 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-1.25rem * var(--space-x-reverse));
+  margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-6 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+}
+
+.-space-x-6 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-1.5rem * var(--space-x-reverse));
+  margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-8 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-2rem * var(--space-y-reverse));
+}
+
+.-space-x-8 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-2rem * var(--space-x-reverse));
+  margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-10 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+}
+
+.-space-x-10 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-2.5rem * var(--space-x-reverse));
+  margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-12 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-3rem * var(--space-y-reverse));
+}
+
+.-space-x-12 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-3rem * var(--space-x-reverse));
+  margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-16 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-4rem * var(--space-y-reverse));
+}
+
+.-space-x-16 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-4rem * var(--space-x-reverse));
+  margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-20 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-5rem * var(--space-y-reverse));
+}
+
+.-space-x-20 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-5rem * var(--space-x-reverse));
+  margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-24 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-6rem * var(--space-y-reverse));
+}
+
+.-space-x-24 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-6rem * var(--space-x-reverse));
+  margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-32 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-8rem * var(--space-y-reverse));
+}
+
+.-space-x-32 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-8rem * var(--space-x-reverse));
+  margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-40 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-10rem * var(--space-y-reverse));
+}
+
+.-space-x-40 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-10rem * var(--space-x-reverse));
+  margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-48 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-12rem * var(--space-y-reverse));
+}
+
+.-space-x-48 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-12rem * var(--space-x-reverse));
+  margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-56 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-14rem * var(--space-y-reverse));
+}
+
+.-space-x-56 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-14rem * var(--space-x-reverse));
+  margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-64 > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-16rem * var(--space-y-reverse));
+}
+
+.-space-x-64 > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-16rem * var(--space-x-reverse));
+  margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+}
+
+.-space-y-px > :not(template) ~ :not(template) {
+  --space-y-reverse: 0;
+  margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+  margin-bottom: calc(-1px * var(--space-y-reverse));
+}
+
+.-space-x-px > :not(template) ~ :not(template) {
+  --space-x-reverse: 0;
+  margin-right: calc(-1px * var(--space-x-reverse));
+  margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+}
+
+.space-y-reverse > :not(template) ~ :not(template) {
+  --space-y-reverse: 1;
+}
+
+.space-x-reverse > :not(template) ~ :not(template) {
+  --space-x-reverse: 1;
+}
+
+.divide-y-0 > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(0px * var(--divide-y-reverse));
+}
+
+.divide-x-0 > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(0px * var(--divide-x-reverse));
+  border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y-2 > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(2px * var(--divide-y-reverse));
+}
+
+.divide-x-2 > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(2px * var(--divide-x-reverse));
+  border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y-4 > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(4px * var(--divide-y-reverse));
+}
+
+.divide-x-4 > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(4px * var(--divide-x-reverse));
+  border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y-8 > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(8px * var(--divide-y-reverse));
+}
+
+.divide-x-8 > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(8px * var(--divide-x-reverse));
+  border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y > :not(template) ~ :not(template) {
+  --divide-y-reverse: 0;
+  border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+  border-bottom-width: calc(1px * var(--divide-y-reverse));
+}
+
+.divide-x > :not(template) ~ :not(template) {
+  --divide-x-reverse: 0;
+  border-right-width: calc(1px * var(--divide-x-reverse));
+  border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+}
+
+.divide-y-reverse > :not(template) ~ :not(template) {
+  --divide-y-reverse: 1;
+}
+
+.divide-x-reverse > :not(template) ~ :not(template) {
+  --divide-x-reverse: 1;
+}
+
+.divide-transparent > :not(template) ~ :not(template) {
+  border-color: transparent;
+}
+
+.divide-current > :not(template) ~ :not(template) {
+  border-color: currentColor;
+}
+
+.divide-black > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #000;
+  border-color: rgba(0, 0, 0, var(--divide-opacity));
+}
+
+.divide-white > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fff;
+  border-color: rgba(255, 255, 255, var(--divide-opacity));
+}
+
+.divide-gray-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f7fafc;
+  border-color: rgba(247, 250, 252, var(--divide-opacity));
+}
+
+.divide-gray-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #edf2f7;
+  border-color: rgba(237, 242, 247, var(--divide-opacity));
+}
+
+.divide-gray-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #e2e8f0;
+  border-color: rgba(226, 232, 240, var(--divide-opacity));
+}
+
+.divide-gray-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #cbd5e0;
+  border-color: rgba(203, 213, 224, var(--divide-opacity));
+}
+
+.divide-gray-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #a0aec0;
+  border-color: rgba(160, 174, 192, var(--divide-opacity));
+}
+
+.divide-gray-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #718096;
+  border-color: rgba(113, 128, 150, var(--divide-opacity));
+}
+
+.divide-gray-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #4a5568;
+  border-color: rgba(74, 85, 104, var(--divide-opacity));
+}
+
+.divide-gray-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2d3748;
+  border-color: rgba(45, 55, 72, var(--divide-opacity));
+}
+
+.divide-gray-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #1a202c;
+  border-color: rgba(26, 32, 44, var(--divide-opacity));
+}
+
+.divide-red-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fff5f5;
+  border-color: rgba(255, 245, 245, var(--divide-opacity));
+}
+
+.divide-red-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fed7d7;
+  border-color: rgba(254, 215, 215, var(--divide-opacity));
+}
+
+.divide-red-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #feb2b2;
+  border-color: rgba(254, 178, 178, var(--divide-opacity));
+}
+
+.divide-red-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fc8181;
+  border-color: rgba(252, 129, 129, var(--divide-opacity));
+}
+
+.divide-red-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f56565;
+  border-color: rgba(245, 101, 101, var(--divide-opacity));
+}
+
+.divide-red-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #e53e3e;
+  border-color: rgba(229, 62, 62, var(--divide-opacity));
+}
+
+.divide-red-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #c53030;
+  border-color: rgba(197, 48, 48, var(--divide-opacity));
+}
+
+.divide-red-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #9b2c2c;
+  border-color: rgba(155, 44, 44, var(--divide-opacity));
+}
+
+.divide-red-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #742a2a;
+  border-color: rgba(116, 42, 42, var(--divide-opacity));
+}
+
+.divide-orange-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fffaf0;
+  border-color: rgba(255, 250, 240, var(--divide-opacity));
+}
+
+.divide-orange-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #feebc8;
+  border-color: rgba(254, 235, 200, var(--divide-opacity));
+}
+
+.divide-orange-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fbd38d;
+  border-color: rgba(251, 211, 141, var(--divide-opacity));
+}
+
+.divide-orange-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f6ad55;
+  border-color: rgba(246, 173, 85, var(--divide-opacity));
+}
+
+.divide-orange-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ed8936;
+  border-color: rgba(237, 137, 54, var(--divide-opacity));
+}
+
+.divide-orange-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #dd6b20;
+  border-color: rgba(221, 107, 32, var(--divide-opacity));
+}
+
+.divide-orange-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #c05621;
+  border-color: rgba(192, 86, 33, var(--divide-opacity));
+}
+
+.divide-orange-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #9c4221;
+  border-color: rgba(156, 66, 33, var(--divide-opacity));
+}
+
+.divide-orange-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #7b341e;
+  border-color: rgba(123, 52, 30, var(--divide-opacity));
+}
+
+.divide-yellow-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fffff0;
+  border-color: rgba(255, 255, 240, var(--divide-opacity));
+}
+
+.divide-yellow-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fefcbf;
+  border-color: rgba(254, 252, 191, var(--divide-opacity));
+}
+
+.divide-yellow-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #faf089;
+  border-color: rgba(250, 240, 137, var(--divide-opacity));
+}
+
+.divide-yellow-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f6e05e;
+  border-color: rgba(246, 224, 94, var(--divide-opacity));
+}
+
+.divide-yellow-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ecc94b;
+  border-color: rgba(236, 201, 75, var(--divide-opacity));
+}
+
+.divide-yellow-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #d69e2e;
+  border-color: rgba(214, 158, 46, var(--divide-opacity));
+}
+
+.divide-yellow-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #b7791f;
+  border-color: rgba(183, 121, 31, var(--divide-opacity));
+}
+
+.divide-yellow-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #975a16;
+  border-color: rgba(151, 90, 22, var(--divide-opacity));
+}
+
+.divide-yellow-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #744210;
+  border-color: rgba(116, 66, 16, var(--divide-opacity));
+}
+
+.divide-green-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f0fff4;
+  border-color: rgba(240, 255, 244, var(--divide-opacity));
+}
+
+.divide-green-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #c6f6d5;
+  border-color: rgba(198, 246, 213, var(--divide-opacity));
+}
+
+.divide-green-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #9ae6b4;
+  border-color: rgba(154, 230, 180, var(--divide-opacity));
+}
+
+.divide-green-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #68d391;
+  border-color: rgba(104, 211, 145, var(--divide-opacity));
+}
+
+.divide-green-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #48bb78;
+  border-color: rgba(72, 187, 120, var(--divide-opacity));
+}
+
+.divide-green-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #38a169;
+  border-color: rgba(56, 161, 105, var(--divide-opacity));
+}
+
+.divide-green-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2f855a;
+  border-color: rgba(47, 133, 90, var(--divide-opacity));
+}
+
+.divide-green-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #276749;
+  border-color: rgba(39, 103, 73, var(--divide-opacity));
+}
+
+.divide-green-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #22543d;
+  border-color: rgba(34, 84, 61, var(--divide-opacity));
+}
+
+.divide-teal-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #e6fffa;
+  border-color: rgba(230, 255, 250, var(--divide-opacity));
+}
+
+.divide-teal-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #b2f5ea;
+  border-color: rgba(178, 245, 234, var(--divide-opacity));
+}
+
+.divide-teal-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #81e6d9;
+  border-color: rgba(129, 230, 217, var(--divide-opacity));
+}
+
+.divide-teal-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #4fd1c5;
+  border-color: rgba(79, 209, 197, var(--divide-opacity));
+}
+
+.divide-teal-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #38b2ac;
+  border-color: rgba(56, 178, 172, var(--divide-opacity));
+}
+
+.divide-teal-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #319795;
+  border-color: rgba(49, 151, 149, var(--divide-opacity));
+}
+
+.divide-teal-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2c7a7b;
+  border-color: rgba(44, 122, 123, var(--divide-opacity));
+}
+
+.divide-teal-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #285e61;
+  border-color: rgba(40, 94, 97, var(--divide-opacity));
+}
+
+.divide-teal-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #234e52;
+  border-color: rgba(35, 78, 82, var(--divide-opacity));
+}
+
+.divide-blue-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ebf8ff;
+  border-color: rgba(235, 248, 255, var(--divide-opacity));
+}
+
+.divide-blue-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #bee3f8;
+  border-color: rgba(190, 227, 248, var(--divide-opacity));
+}
+
+.divide-blue-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #90cdf4;
+  border-color: rgba(144, 205, 244, var(--divide-opacity));
+}
+
+.divide-blue-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #63b3ed;
+  border-color: rgba(99, 179, 237, var(--divide-opacity));
+}
+
+.divide-blue-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #4299e1;
+  border-color: rgba(66, 153, 225, var(--divide-opacity));
+}
+
+.divide-blue-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #3182ce;
+  border-color: rgba(49, 130, 206, var(--divide-opacity));
+}
+
+.divide-blue-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2b6cb0;
+  border-color: rgba(43, 108, 176, var(--divide-opacity));
+}
+
+.divide-blue-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2c5282;
+  border-color: rgba(44, 82, 130, var(--divide-opacity));
+}
+
+.divide-blue-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #2a4365;
+  border-color: rgba(42, 67, 101, var(--divide-opacity));
+}
+
+.divide-indigo-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ebf4ff;
+  border-color: rgba(235, 244, 255, var(--divide-opacity));
+}
+
+.divide-indigo-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #c3dafe;
+  border-color: rgba(195, 218, 254, var(--divide-opacity));
+}
+
+.divide-indigo-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #a3bffa;
+  border-color: rgba(163, 191, 250, var(--divide-opacity));
+}
+
+.divide-indigo-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #7f9cf5;
+  border-color: rgba(127, 156, 245, var(--divide-opacity));
+}
+
+.divide-indigo-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #667eea;
+  border-color: rgba(102, 126, 234, var(--divide-opacity));
+}
+
+.divide-indigo-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #5a67d8;
+  border-color: rgba(90, 103, 216, var(--divide-opacity));
+}
+
+.divide-indigo-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #4c51bf;
+  border-color: rgba(76, 81, 191, var(--divide-opacity));
+}
+
+.divide-indigo-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #434190;
+  border-color: rgba(67, 65, 144, var(--divide-opacity));
+}
+
+.divide-indigo-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #3c366b;
+  border-color: rgba(60, 54, 107, var(--divide-opacity));
+}
+
+.divide-purple-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #faf5ff;
+  border-color: rgba(250, 245, 255, var(--divide-opacity));
+}
+
+.divide-purple-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #e9d8fd;
+  border-color: rgba(233, 216, 253, var(--divide-opacity));
+}
+
+.divide-purple-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #d6bcfa;
+  border-color: rgba(214, 188, 250, var(--divide-opacity));
+}
+
+.divide-purple-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #b794f4;
+  border-color: rgba(183, 148, 244, var(--divide-opacity));
+}
+
+.divide-purple-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #9f7aea;
+  border-color: rgba(159, 122, 234, var(--divide-opacity));
+}
+
+.divide-purple-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #805ad5;
+  border-color: rgba(128, 90, 213, var(--divide-opacity));
+}
+
+.divide-purple-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #6b46c1;
+  border-color: rgba(107, 70, 193, var(--divide-opacity));
+}
+
+.divide-purple-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #553c9a;
+  border-color: rgba(85, 60, 154, var(--divide-opacity));
+}
+
+.divide-purple-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #44337a;
+  border-color: rgba(68, 51, 122, var(--divide-opacity));
+}
+
+.divide-pink-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fff5f7;
+  border-color: rgba(255, 245, 247, var(--divide-opacity));
+}
+
+.divide-pink-200 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fed7e2;
+  border-color: rgba(254, 215, 226, var(--divide-opacity));
+}
+
+.divide-pink-300 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #fbb6ce;
+  border-color: rgba(251, 182, 206, var(--divide-opacity));
+}
+
+.divide-pink-400 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #f687b3;
+  border-color: rgba(246, 135, 179, var(--divide-opacity));
+}
+
+.divide-pink-500 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #ed64a6;
+  border-color: rgba(237, 100, 166, var(--divide-opacity));
+}
+
+.divide-pink-600 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #d53f8c;
+  border-color: rgba(213, 63, 140, var(--divide-opacity));
+}
+
+.divide-pink-700 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #b83280;
+  border-color: rgba(184, 50, 128, var(--divide-opacity));
+}
+
+.divide-pink-800 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #97266d;
+  border-color: rgba(151, 38, 109, var(--divide-opacity));
+}
+
+.divide-pink-900 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+  border-color: #702459;
+  border-color: rgba(112, 36, 89, var(--divide-opacity));
+}
+
+.divide-solid > :not(template) ~ :not(template) {
+  border-style: solid;
+}
+
+.divide-dashed > :not(template) ~ :not(template) {
+  border-style: dashed;
+}
+
+.divide-dotted > :not(template) ~ :not(template) {
+  border-style: dotted;
+}
+
+.divide-double > :not(template) ~ :not(template) {
+  border-style: double;
+}
+
+.divide-none > :not(template) ~ :not(template) {
+  border-style: none;
+}
+
+.divide-opacity-0 > :not(template) ~ :not(template) {
+  --divide-opacity: 0;
+}
+
+.divide-opacity-25 > :not(template) ~ :not(template) {
+  --divide-opacity: 0.25;
+}
+
+.divide-opacity-50 > :not(template) ~ :not(template) {
+  --divide-opacity: 0.5;
+}
+
+.divide-opacity-75 > :not(template) ~ :not(template) {
+  --divide-opacity: 0.75;
+}
+
+.divide-opacity-100 > :not(template) ~ :not(template) {
+  --divide-opacity: 1;
+}
+
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border-width: 0;
+}
+
+.not-sr-only {
+  position: static;
+  width: auto;
+  height: auto;
+  padding: 0;
+  margin: 0;
+  overflow: visible;
+  clip: auto;
+  white-space: normal;
+}
+
+.focus\:sr-only:focus {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border-width: 0;
+}
+
+.focus\:not-sr-only:focus {
+  position: static;
+  width: auto;
+  height: auto;
+  padding: 0;
+  margin: 0;
+  overflow: visible;
+  clip: auto;
+  white-space: normal;
+}
+
+.appearance-none {
+  -webkit-appearance: none;
+     -moz-appearance: none;
+          appearance: none;
+}
+
+.bg-fixed {
+  background-attachment: fixed;
+}
+
+.bg-local {
+  background-attachment: local;
+}
+
+.bg-scroll {
+  background-attachment: scroll;
+}
+
+.bg-clip-border {
+  background-clip: border-box;
+}
+
+.bg-clip-padding {
+  background-clip: padding-box;
+}
+
+.bg-clip-content {
+  background-clip: content-box;
+}
+
+.bg-clip-text {
+  -webkit-background-clip: text;
+          background-clip: text;
+}
+
+.bg-transparent {
+  background-color: transparent;
+}
+
+.bg-current {
+  background-color: currentColor;
+}
+
+.bg-black {
+  --bg-opacity: 1;
+  background-color: #000;
+  background-color: rgba(0, 0, 0, var(--bg-opacity));
+}
+
+.bg-white {
+  --bg-opacity: 1;
+  background-color: #fff;
+  background-color: rgba(255, 255, 255, var(--bg-opacity));
+}
+
+.bg-gray-100 {
+  --bg-opacity: 1;
+  background-color: #f7fafc;
+  background-color: rgba(247, 250, 252, var(--bg-opacity));
+}
+
+.bg-gray-200 {
+  --bg-opacity: 1;
+  background-color: #edf2f7;
+  background-color: rgba(237, 242, 247, var(--bg-opacity));
+}
+
+.bg-gray-300 {
+  --bg-opacity: 1;
+  background-color: #e2e8f0;
+  background-color: rgba(226, 232, 240, var(--bg-opacity));
+}
+
+.bg-gray-400 {
+  --bg-opacity: 1;
+  background-color: #cbd5e0;
+  background-color: rgba(203, 213, 224, var(--bg-opacity));
+}
+
+.bg-gray-500 {
+  --bg-opacity: 1;
+  background-color: #a0aec0;
+  background-color: rgba(160, 174, 192, var(--bg-opacity));
+}
+
+.bg-gray-600 {
+  --bg-opacity: 1;
+  background-color: #718096;
+  background-color: rgba(113, 128, 150, var(--bg-opacity));
+}
+
+.bg-gray-700 {
+  --bg-opacity: 1;
+  background-color: #4a5568;
+  background-color: rgba(74, 85, 104, var(--bg-opacity));
+}
+
+.bg-gray-800 {
+  --bg-opacity: 1;
+  background-color: #2d3748;
+  background-color: rgba(45, 55, 72, var(--bg-opacity));
+}
+
+.bg-gray-900 {
+  --bg-opacity: 1;
+  background-color: #1a202c;
+  background-color: rgba(26, 32, 44, var(--bg-opacity));
+}
+
+.bg-red-100 {
+  --bg-opacity: 1;
+  background-color: #fff5f5;
+  background-color: rgba(255, 245, 245, var(--bg-opacity));
+}
+
+.bg-red-200 {
+  --bg-opacity: 1;
+  background-color: #fed7d7;
+  background-color: rgba(254, 215, 215, var(--bg-opacity));
+}
+
+.bg-red-300 {
+  --bg-opacity: 1;
+  background-color: #feb2b2;
+  background-color: rgba(254, 178, 178, var(--bg-opacity));
+}
+
+.bg-red-400 {
+  --bg-opacity: 1;
+  background-color: #fc8181;
+  background-color: rgba(252, 129, 129, var(--bg-opacity));
+}
+
+.bg-red-500 {
+  --bg-opacity: 1;
+  background-color: #f56565;
+  background-color: rgba(245, 101, 101, var(--bg-opacity));
+}
+
+.bg-red-600 {
+  --bg-opacity: 1;
+  background-color: #e53e3e;
+  background-color: rgba(229, 62, 62, var(--bg-opacity));
+}
+
+.bg-red-700 {
+  --bg-opacity: 1;
+  background-color: #c53030;
+  background-color: rgba(197, 48, 48, var(--bg-opacity));
+}
+
+.bg-red-800 {
+  --bg-opacity: 1;
+  background-color: #9b2c2c;
+  background-color: rgba(155, 44, 44, var(--bg-opacity));
+}
+
+.bg-red-900 {
+  --bg-opacity: 1;
+  background-color: #742a2a;
+  background-color: rgba(116, 42, 42, var(--bg-opacity));
+}
+
+.bg-orange-100 {
+  --bg-opacity: 1;
+  background-color: #fffaf0;
+  background-color: rgba(255, 250, 240, var(--bg-opacity));
+}
+
+.bg-orange-200 {
+  --bg-opacity: 1;
+  background-color: #feebc8;
+  background-color: rgba(254, 235, 200, var(--bg-opacity));
+}
+
+.bg-orange-300 {
+  --bg-opacity: 1;
+  background-color: #fbd38d;
+  background-color: rgba(251, 211, 141, var(--bg-opacity));
+}
+
+.bg-orange-400 {
+  --bg-opacity: 1;
+  background-color: #f6ad55;
+  background-color: rgba(246, 173, 85, var(--bg-opacity));
+}
+
+.bg-orange-500 {
+  --bg-opacity: 1;
+  background-color: #ed8936;
+  background-color: rgba(237, 137, 54, var(--bg-opacity));
+}
+
+.bg-orange-600 {
+  --bg-opacity: 1;
+  background-color: #dd6b20;
+  background-color: rgba(221, 107, 32, var(--bg-opacity));
+}
+
+.bg-orange-700 {
+  --bg-opacity: 1;
+  background-color: #c05621;
+  background-color: rgba(192, 86, 33, var(--bg-opacity));
+}
+
+.bg-orange-800 {
+  --bg-opacity: 1;
+  background-color: #9c4221;
+  background-color: rgba(156, 66, 33, var(--bg-opacity));
+}
+
+.bg-orange-900 {
+  --bg-opacity: 1;
+  background-color: #7b341e;
+  background-color: rgba(123, 52, 30, var(--bg-opacity));
+}
+
+.bg-yellow-100 {
+  --bg-opacity: 1;
+  background-color: #fffff0;
+  background-color: rgba(255, 255, 240, var(--bg-opacity));
+}
+
+.bg-yellow-200 {
+  --bg-opacity: 1;
+  background-color: #fefcbf;
+  background-color: rgba(254, 252, 191, var(--bg-opacity));
+}
+
+.bg-yellow-300 {
+  --bg-opacity: 1;
+  background-color: #faf089;
+  background-color: rgba(250, 240, 137, var(--bg-opacity));
+}
+
+.bg-yellow-400 {
+  --bg-opacity: 1;
+  background-color: #f6e05e;
+  background-color: rgba(246, 224, 94, var(--bg-opacity));
+}
+
+.bg-yellow-500 {
+  --bg-opacity: 1;
+  background-color: #ecc94b;
+  background-color: rgba(236, 201, 75, var(--bg-opacity));
+}
+
+.bg-yellow-600 {
+  --bg-opacity: 1;
+  background-color: #d69e2e;
+  background-color: rgba(214, 158, 46, var(--bg-opacity));
+}
+
+.bg-yellow-700 {
+  --bg-opacity: 1;
+  background-color: #b7791f;
+  background-color: rgba(183, 121, 31, var(--bg-opacity));
+}
+
+.bg-yellow-800 {
+  --bg-opacity: 1;
+  background-color: #975a16;
+  background-color: rgba(151, 90, 22, var(--bg-opacity));
+}
+
+.bg-yellow-900 {
+  --bg-opacity: 1;
+  background-color: #744210;
+  background-color: rgba(116, 66, 16, var(--bg-opacity));
+}
+
+.bg-green-100 {
+  --bg-opacity: 1;
+  background-color: #f0fff4;
+  background-color: rgba(240, 255, 244, var(--bg-opacity));
+}
+
+.bg-green-200 {
+  --bg-opacity: 1;
+  background-color: #c6f6d5;
+  background-color: rgba(198, 246, 213, var(--bg-opacity));
+}
+
+.bg-green-300 {
+  --bg-opacity: 1;
+  background-color: #9ae6b4;
+  background-color: rgba(154, 230, 180, var(--bg-opacity));
+}
+
+.bg-green-400 {
+  --bg-opacity: 1;
+  background-color: #68d391;
+  background-color: rgba(104, 211, 145, var(--bg-opacity));
+}
+
+.bg-green-500 {
+  --bg-opacity: 1;
+  background-color: #48bb78;
+  background-color: rgba(72, 187, 120, var(--bg-opacity));
+}
+
+.bg-green-600 {
+  --bg-opacity: 1;
+  background-color: #38a169;
+  background-color: rgba(56, 161, 105, var(--bg-opacity));
+}
+
+.bg-green-700 {
+  --bg-opacity: 1;
+  background-color: #2f855a;
+  background-color: rgba(47, 133, 90, var(--bg-opacity));
+}
+
+.bg-green-800 {
+  --bg-opacity: 1;
+  background-color: #276749;
+  background-color: rgba(39, 103, 73, var(--bg-opacity));
+}
+
+.bg-green-900 {
+  --bg-opacity: 1;
+  background-color: #22543d;
+  background-color: rgba(34, 84, 61, var(--bg-opacity));
+}
+
+.bg-teal-100 {
+  --bg-opacity: 1;
+  background-color: #e6fffa;
+  background-color: rgba(230, 255, 250, var(--bg-opacity));
+}
+
+.bg-teal-200 {
+  --bg-opacity: 1;
+  background-color: #b2f5ea;
+  background-color: rgba(178, 245, 234, var(--bg-opacity));
+}
+
+.bg-teal-300 {
+  --bg-opacity: 1;
+  background-color: #81e6d9;
+  background-color: rgba(129, 230, 217, var(--bg-opacity));
+}
+
+.bg-teal-400 {
+  --bg-opacity: 1;
+  background-color: #4fd1c5;
+  background-color: rgba(79, 209, 197, var(--bg-opacity));
+}
+
+.bg-teal-500 {
+  --bg-opacity: 1;
+  background-color: #38b2ac;
+  background-color: rgba(56, 178, 172, var(--bg-opacity));
+}
+
+.bg-teal-600 {
+  --bg-opacity: 1;
+  background-color: #319795;
+  background-color: rgba(49, 151, 149, var(--bg-opacity));
+}
+
+.bg-teal-700 {
+  --bg-opacity: 1;
+  background-color: #2c7a7b;
+  background-color: rgba(44, 122, 123, var(--bg-opacity));
+}
+
+.bg-teal-800 {
+  --bg-opacity: 1;
+  background-color: #285e61;
+  background-color: rgba(40, 94, 97, var(--bg-opacity));
+}
+
+.bg-teal-900 {
+  --bg-opacity: 1;
+  background-color: #234e52;
+  background-color: rgba(35, 78, 82, var(--bg-opacity));
+}
+
+.bg-blue-100 {
+  --bg-opacity: 1;
+  background-color: #ebf8ff;
+  background-color: rgba(235, 248, 255, var(--bg-opacity));
+}
+
+.bg-blue-200 {
+  --bg-opacity: 1;
+  background-color: #bee3f8;
+  background-color: rgba(190, 227, 248, var(--bg-opacity));
+}
+
+.bg-blue-300 {
+  --bg-opacity: 1;
+  background-color: #90cdf4;
+  background-color: rgba(144, 205, 244, var(--bg-opacity));
+}
+
+.bg-blue-400 {
+  --bg-opacity: 1;
+  background-color: #63b3ed;
+  background-color: rgba(99, 179, 237, var(--bg-opacity));
+}
+
+.bg-blue-500 {
+  --bg-opacity: 1;
+  background-color: #4299e1;
+  background-color: rgba(66, 153, 225, var(--bg-opacity));
+}
+
+.bg-blue-600 {
+  --bg-opacity: 1;
+  background-color: #3182ce;
+  background-color: rgba(49, 130, 206, var(--bg-opacity));
+}
+
+.bg-blue-700 {
+  --bg-opacity: 1;
+  background-color: #2b6cb0;
+  background-color: rgba(43, 108, 176, var(--bg-opacity));
+}
+
+.bg-blue-800 {
+  --bg-opacity: 1;
+  background-color: #2c5282;
+  background-color: rgba(44, 82, 130, var(--bg-opacity));
+}
+
+.bg-blue-900 {
+  --bg-opacity: 1;
+  background-color: #2a4365;
+  background-color: rgba(42, 67, 101, var(--bg-opacity));
+}
+
+.bg-indigo-100 {
+  --bg-opacity: 1;
+  background-color: #ebf4ff;
+  background-color: rgba(235, 244, 255, var(--bg-opacity));
+}
+
+.bg-indigo-200 {
+  --bg-opacity: 1;
+  background-color: #c3dafe;
+  background-color: rgba(195, 218, 254, var(--bg-opacity));
+}
+
+.bg-indigo-300 {
+  --bg-opacity: 1;
+  background-color: #a3bffa;
+  background-color: rgba(163, 191, 250, var(--bg-opacity));
+}
+
+.bg-indigo-400 {
+  --bg-opacity: 1;
+  background-color: #7f9cf5;
+  background-color: rgba(127, 156, 245, var(--bg-opacity));
+}
+
+.bg-indigo-500 {
+  --bg-opacity: 1;
+  background-color: #667eea;
+  background-color: rgba(102, 126, 234, var(--bg-opacity));
+}
+
+.bg-indigo-600 {
+  --bg-opacity: 1;
+  background-color: #5a67d8;
+  background-color: rgba(90, 103, 216, var(--bg-opacity));
+}
+
+.bg-indigo-700 {
+  --bg-opacity: 1;
+  background-color: #4c51bf;
+  background-color: rgba(76, 81, 191, var(--bg-opacity));
+}
+
+.bg-indigo-800 {
+  --bg-opacity: 1;
+  background-color: #434190;
+  background-color: rgba(67, 65, 144, var(--bg-opacity));
+}
+
+.bg-indigo-900 {
+  --bg-opacity: 1;
+  background-color: #3c366b;
+  background-color: rgba(60, 54, 107, var(--bg-opacity));
+}
+
+.bg-purple-100 {
+  --bg-opacity: 1;
+  background-color: #faf5ff;
+  background-color: rgba(250, 245, 255, var(--bg-opacity));
+}
+
+.bg-purple-200 {
+  --bg-opacity: 1;
+  background-color: #e9d8fd;
+  background-color: rgba(233, 216, 253, var(--bg-opacity));
+}
+
+.bg-purple-300 {
+  --bg-opacity: 1;
+  background-color: #d6bcfa;
+  background-color: rgba(214, 188, 250, var(--bg-opacity));
+}
+
+.bg-purple-400 {
+  --bg-opacity: 1;
+  background-color: #b794f4;
+  background-color: rgba(183, 148, 244, var(--bg-opacity));
+}
+
+.bg-purple-500 {
+  --bg-opacity: 1;
+  background-color: #9f7aea;
+  background-color: rgba(159, 122, 234, var(--bg-opacity));
+}
+
+.bg-purple-600 {
+  --bg-opacity: 1;
+  background-color: #805ad5;
+  background-color: rgba(128, 90, 213, var(--bg-opacity));
+}
+
+.bg-purple-700 {
+  --bg-opacity: 1;
+  background-color: #6b46c1;
+  background-color: rgba(107, 70, 193, var(--bg-opacity));
+}
+
+.bg-purple-800 {
+  --bg-opacity: 1;
+  background-color: #553c9a;
+  background-color: rgba(85, 60, 154, var(--bg-opacity));
+}
+
+.bg-purple-900 {
+  --bg-opacity: 1;
+  background-color: #44337a;
+  background-color: rgba(68, 51, 122, var(--bg-opacity));
+}
+
+.bg-pink-100 {
+  --bg-opacity: 1;
+  background-color: #fff5f7;
+  background-color: rgba(255, 245, 247, var(--bg-opacity));
+}
+
+.bg-pink-200 {
+  --bg-opacity: 1;
+  background-color: #fed7e2;
+  background-color: rgba(254, 215, 226, var(--bg-opacity));
+}
+
+.bg-pink-300 {
+  --bg-opacity: 1;
+  background-color: #fbb6ce;
+  background-color: rgba(251, 182, 206, var(--bg-opacity));
+}
+
+.bg-pink-400 {
+  --bg-opacity: 1;
+  background-color: #f687b3;
+  background-color: rgba(246, 135, 179, var(--bg-opacity));
+}
+
+.bg-pink-500 {
+  --bg-opacity: 1;
+  background-color: #ed64a6;
+  background-color: rgba(237, 100, 166, var(--bg-opacity));
+}
+
+.bg-pink-600 {
+  --bg-opacity: 1;
+  background-color: #d53f8c;
+  background-color: rgba(213, 63, 140, var(--bg-opacity));
+}
+
+.bg-pink-700 {
+  --bg-opacity: 1;
+  background-color: #b83280;
+  background-color: rgba(184, 50, 128, var(--bg-opacity));
+}
+
+.bg-pink-800 {
+  --bg-opacity: 1;
+  background-color: #97266d;
+  background-color: rgba(151, 38, 109, var(--bg-opacity));
+}
+
+.bg-pink-900 {
+  --bg-opacity: 1;
+  background-color: #702459;
+  background-color: rgba(112, 36, 89, var(--bg-opacity));
+}
+
+.hover\:bg-transparent:hover {
+  background-color: transparent;
+}
+
+.hover\:bg-current:hover {
+  background-color: currentColor;
+}
+
+.hover\:bg-black:hover {
+  --bg-opacity: 1;
+  background-color: #000;
+  background-color: rgba(0, 0, 0, var(--bg-opacity));
+}
+
+.hover\:bg-white:hover {
+  --bg-opacity: 1;
+  background-color: #fff;
+  background-color: rgba(255, 255, 255, var(--bg-opacity));
+}
+
+.hover\:bg-gray-100:hover {
+  --bg-opacity: 1;
+  background-color: #f7fafc;
+  background-color: rgba(247, 250, 252, var(--bg-opacity));
+}
+
+.hover\:bg-gray-200:hover {
+  --bg-opacity: 1;
+  background-color: #edf2f7;
+  background-color: rgba(237, 242, 247, var(--bg-opacity));
+}
+
+.hover\:bg-gray-300:hover {
+  --bg-opacity: 1;
+  background-color: #e2e8f0;
+  background-color: rgba(226, 232, 240, var(--bg-opacity));
+}
+
+.hover\:bg-gray-400:hover {
+  --bg-opacity: 1;
+  background-color: #cbd5e0;
+  background-color: rgba(203, 213, 224, var(--bg-opacity));
+}
+
+.hover\:bg-gray-500:hover {
+  --bg-opacity: 1;
+  background-color: #a0aec0;
+  background-color: rgba(160, 174, 192, var(--bg-opacity));
+}
+
+.hover\:bg-gray-600:hover {
+  --bg-opacity: 1;
+  background-color: #718096;
+  background-color: rgba(113, 128, 150, var(--bg-opacity));
+}
+
+.hover\:bg-gray-700:hover {
+  --bg-opacity: 1;
+  background-color: #4a5568;
+  background-color: rgba(74, 85, 104, var(--bg-opacity));
+}
+
+.hover\:bg-gray-800:hover {
+  --bg-opacity: 1;
+  background-color: #2d3748;
+  background-color: rgba(45, 55, 72, var(--bg-opacity));
+}
+
+.hover\:bg-gray-900:hover {
+  --bg-opacity: 1;
+  background-color: #1a202c;
+  background-color: rgba(26, 32, 44, var(--bg-opacity));
+}
+
+.hover\:bg-red-100:hover {
+  --bg-opacity: 1;
+  background-color: #fff5f5;
+  background-color: rgba(255, 245, 245, var(--bg-opacity));
+}
+
+.hover\:bg-red-200:hover {
+  --bg-opacity: 1;
+  background-color: #fed7d7;
+  background-color: rgba(254, 215, 215, var(--bg-opacity));
+}
+
+.hover\:bg-red-300:hover {
+  --bg-opacity: 1;
+  background-color: #feb2b2;
+  background-color: rgba(254, 178, 178, var(--bg-opacity));
+}
+
+.hover\:bg-red-400:hover {
+  --bg-opacity: 1;
+  background-color: #fc8181;
+  background-color: rgba(252, 129, 129, var(--bg-opacity));
+}
+
+.hover\:bg-red-500:hover {
+  --bg-opacity: 1;
+  background-color: #f56565;
+  background-color: rgba(245, 101, 101, var(--bg-opacity));
+}
+
+.hover\:bg-red-600:hover {
+  --bg-opacity: 1;
+  background-color: #e53e3e;
+  background-color: rgba(229, 62, 62, var(--bg-opacity));
+}
+
+.hover\:bg-red-700:hover {
+  --bg-opacity: 1;
+  background-color: #c53030;
+  background-color: rgba(197, 48, 48, var(--bg-opacity));
+}
+
+.hover\:bg-red-800:hover {
+  --bg-opacity: 1;
+  background-color: #9b2c2c;
+  background-color: rgba(155, 44, 44, var(--bg-opacity));
+}
+
+.hover\:bg-red-900:hover {
+  --bg-opacity: 1;
+  background-color: #742a2a;
+  background-color: rgba(116, 42, 42, var(--bg-opacity));
+}
+
+.hover\:bg-orange-100:hover {
+  --bg-opacity: 1;
+  background-color: #fffaf0;
+  background-color: rgba(255, 250, 240, var(--bg-opacity));
+}
+
+.hover\:bg-orange-200:hover {
+  --bg-opacity: 1;
+  background-color: #feebc8;
+  background-color: rgba(254, 235, 200, var(--bg-opacity));
+}
+
+.hover\:bg-orange-300:hover {
+  --bg-opacity: 1;
+  background-color: #fbd38d;
+  background-color: rgba(251, 211, 141, var(--bg-opacity));
+}
+
+.hover\:bg-orange-400:hover {
+  --bg-opacity: 1;
+  background-color: #f6ad55;
+  background-color: rgba(246, 173, 85, var(--bg-opacity));
+}
+
+.hover\:bg-orange-500:hover {
+  --bg-opacity: 1;
+  background-color: #ed8936;
+  background-color: rgba(237, 137, 54, var(--bg-opacity));
+}
+
+.hover\:bg-orange-600:hover {
+  --bg-opacity: 1;
+  background-color: #dd6b20;
+  background-color: rgba(221, 107, 32, var(--bg-opacity));
+}
+
+.hover\:bg-orange-700:hover {
+  --bg-opacity: 1;
+  background-color: #c05621;
+  background-color: rgba(192, 86, 33, var(--bg-opacity));
+}
+
+.hover\:bg-orange-800:hover {
+  --bg-opacity: 1;
+  background-color: #9c4221;
+  background-color: rgba(156, 66, 33, var(--bg-opacity));
+}
+
+.hover\:bg-orange-900:hover {
+  --bg-opacity: 1;
+  background-color: #7b341e;
+  background-color: rgba(123, 52, 30, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-100:hover {
+  --bg-opacity: 1;
+  background-color: #fffff0;
+  background-color: rgba(255, 255, 240, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-200:hover {
+  --bg-opacity: 1;
+  background-color: #fefcbf;
+  background-color: rgba(254, 252, 191, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-300:hover {
+  --bg-opacity: 1;
+  background-color: #faf089;
+  background-color: rgba(250, 240, 137, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-400:hover {
+  --bg-opacity: 1;
+  background-color: #f6e05e;
+  background-color: rgba(246, 224, 94, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-500:hover {
+  --bg-opacity: 1;
+  background-color: #ecc94b;
+  background-color: rgba(236, 201, 75, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-600:hover {
+  --bg-opacity: 1;
+  background-color: #d69e2e;
+  background-color: rgba(214, 158, 46, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-700:hover {
+  --bg-opacity: 1;
+  background-color: #b7791f;
+  background-color: rgba(183, 121, 31, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-800:hover {
+  --bg-opacity: 1;
+  background-color: #975a16;
+  background-color: rgba(151, 90, 22, var(--bg-opacity));
+}
+
+.hover\:bg-yellow-900:hover {
+  --bg-opacity: 1;
+  background-color: #744210;
+  background-color: rgba(116, 66, 16, var(--bg-opacity));
+}
+
+.hover\:bg-green-100:hover {
+  --bg-opacity: 1;
+  background-color: #f0fff4;
+  background-color: rgba(240, 255, 244, var(--bg-opacity));
+}
+
+.hover\:bg-green-200:hover {
+  --bg-opacity: 1;
+  background-color: #c6f6d5;
+  background-color: rgba(198, 246, 213, var(--bg-opacity));
+}
+
+.hover\:bg-green-300:hover {
+  --bg-opacity: 1;
+  background-color: #9ae6b4;
+  background-color: rgba(154, 230, 180, var(--bg-opacity));
+}
+
+.hover\:bg-green-400:hover {
+  --bg-opacity: 1;
+  background-color: #68d391;
+  background-color: rgba(104, 211, 145, var(--bg-opacity));
+}
+
+.hover\:bg-green-500:hover {
+  --bg-opacity: 1;
+  background-color: #48bb78;
+  background-color: rgba(72, 187, 120, var(--bg-opacity));
+}
+
+.hover\:bg-green-600:hover {
+  --bg-opacity: 1;
+  background-color: #38a169;
+  background-color: rgba(56, 161, 105, var(--bg-opacity));
+}
+
+.hover\:bg-green-700:hover {
+  --bg-opacity: 1;
+  background-color: #2f855a;
+  background-color: rgba(47, 133, 90, var(--bg-opacity));
+}
+
+.hover\:bg-green-800:hover {
+  --bg-opacity: 1;
+  background-color: #276749;
+  background-color: rgba(39, 103, 73, var(--bg-opacity));
+}
+
+.hover\:bg-green-900:hover {
+  --bg-opacity: 1;
+  background-color: #22543d;
+  background-color: rgba(34, 84, 61, var(--bg-opacity));
+}
+
+.hover\:bg-teal-100:hover {
+  --bg-opacity: 1;
+  background-color: #e6fffa;
+  background-color: rgba(230, 255, 250, var(--bg-opacity));
+}
+
+.hover\:bg-teal-200:hover {
+  --bg-opacity: 1;
+  background-color: #b2f5ea;
+  background-color: rgba(178, 245, 234, var(--bg-opacity));
+}
+
+.hover\:bg-teal-300:hover {
+  --bg-opacity: 1;
+  background-color: #81e6d9;
+  background-color: rgba(129, 230, 217, var(--bg-opacity));
+}
+
+.hover\:bg-teal-400:hover {
+  --bg-opacity: 1;
+  background-color: #4fd1c5;
+  background-color: rgba(79, 209, 197, var(--bg-opacity));
+}
+
+.hover\:bg-teal-500:hover {
+  --bg-opacity: 1;
+  background-color: #38b2ac;
+  background-color: rgba(56, 178, 172, var(--bg-opacity));
+}
+
+.hover\:bg-teal-600:hover {
+  --bg-opacity: 1;
+  background-color: #319795;
+  background-color: rgba(49, 151, 149, var(--bg-opacity));
+}
+
+.hover\:bg-teal-700:hover {
+  --bg-opacity: 1;
+  background-color: #2c7a7b;
+  background-color: rgba(44, 122, 123, var(--bg-opacity));
+}
+
+.hover\:bg-teal-800:hover {
+  --bg-opacity: 1;
+  background-color: #285e61;
+  background-color: rgba(40, 94, 97, var(--bg-opacity));
+}
+
+.hover\:bg-teal-900:hover {
+  --bg-opacity: 1;
+  background-color: #234e52;
+  background-color: rgba(35, 78, 82, var(--bg-opacity));
+}
+
+.hover\:bg-blue-100:hover {
+  --bg-opacity: 1;
+  background-color: #ebf8ff;
+  background-color: rgba(235, 248, 255, var(--bg-opacity));
+}
+
+.hover\:bg-blue-200:hover {
+  --bg-opacity: 1;
+  background-color: #bee3f8;
+  background-color: rgba(190, 227, 248, var(--bg-opacity));
+}
+
+.hover\:bg-blue-300:hover {
+  --bg-opacity: 1;
+  background-color: #90cdf4;
+  background-color: rgba(144, 205, 244, var(--bg-opacity));
+}
+
+.hover\:bg-blue-400:hover {
+  --bg-opacity: 1;
+  background-color: #63b3ed;
+  background-color: rgba(99, 179, 237, var(--bg-opacity));
+}
+
+.hover\:bg-blue-500:hover {
+  --bg-opacity: 1;
+  background-color: #4299e1;
+  background-color: rgba(66, 153, 225, var(--bg-opacity));
+}
+
+.hover\:bg-blue-600:hover {
+  --bg-opacity: 1;
+  background-color: #3182ce;
+  background-color: rgba(49, 130, 206, var(--bg-opacity));
+}
+
+.hover\:bg-blue-700:hover {
+  --bg-opacity: 1;
+  background-color: #2b6cb0;
+  background-color: rgba(43, 108, 176, var(--bg-opacity));
+}
+
+.hover\:bg-blue-800:hover {
+  --bg-opacity: 1;
+  background-color: #2c5282;
+  background-color: rgba(44, 82, 130, var(--bg-opacity));
+}
+
+.hover\:bg-blue-900:hover {
+  --bg-opacity: 1;
+  background-color: #2a4365;
+  background-color: rgba(42, 67, 101, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-100:hover {
+  --bg-opacity: 1;
+  background-color: #ebf4ff;
+  background-color: rgba(235, 244, 255, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-200:hover {
+  --bg-opacity: 1;
+  background-color: #c3dafe;
+  background-color: rgba(195, 218, 254, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-300:hover {
+  --bg-opacity: 1;
+  background-color: #a3bffa;
+  background-color: rgba(163, 191, 250, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-400:hover {
+  --bg-opacity: 1;
+  background-color: #7f9cf5;
+  background-color: rgba(127, 156, 245, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-500:hover {
+  --bg-opacity: 1;
+  background-color: #667eea;
+  background-color: rgba(102, 126, 234, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-600:hover {
+  --bg-opacity: 1;
+  background-color: #5a67d8;
+  background-color: rgba(90, 103, 216, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-700:hover {
+  --bg-opacity: 1;
+  background-color: #4c51bf;
+  background-color: rgba(76, 81, 191, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-800:hover {
+  --bg-opacity: 1;
+  background-color: #434190;
+  background-color: rgba(67, 65, 144, var(--bg-opacity));
+}
+
+.hover\:bg-indigo-900:hover {
+  --bg-opacity: 1;
+  background-color: #3c366b;
+  background-color: rgba(60, 54, 107, var(--bg-opacity));
+}
+
+.hover\:bg-purple-100:hover {
+  --bg-opacity: 1;
+  background-color: #faf5ff;
+  background-color: rgba(250, 245, 255, var(--bg-opacity));
+}
+
+.hover\:bg-purple-200:hover {
+  --bg-opacity: 1;
+  background-color: #e9d8fd;
+  background-color: rgba(233, 216, 253, var(--bg-opacity));
+}
+
+.hover\:bg-purple-300:hover {
+  --bg-opacity: 1;
+  background-color: #d6bcfa;
+  background-color: rgba(214, 188, 250, var(--bg-opacity));
+}
+
+.hover\:bg-purple-400:hover {
+  --bg-opacity: 1;
+  background-color: #b794f4;
+  background-color: rgba(183, 148, 244, var(--bg-opacity));
+}
+
+.hover\:bg-purple-500:hover {
+  --bg-opacity: 1;
+  background-color: #9f7aea;
+  background-color: rgba(159, 122, 234, var(--bg-opacity));
+}
+
+.hover\:bg-purple-600:hover {
+  --bg-opacity: 1;
+  background-color: #805ad5;
+  background-color: rgba(128, 90, 213, var(--bg-opacity));
+}
+
+.hover\:bg-purple-700:hover {
+  --bg-opacity: 1;
+  background-color: #6b46c1;
+  background-color: rgba(107, 70, 193, var(--bg-opacity));
+}
+
+.hover\:bg-purple-800:hover {
+  --bg-opacity: 1;
+  background-color: #553c9a;
+  background-color: rgba(85, 60, 154, var(--bg-opacity));
+}
+
+.hover\:bg-purple-900:hover {
+  --bg-opacity: 1;
+  background-color: #44337a;
+  background-color: rgba(68, 51, 122, var(--bg-opacity));
+}
+
+.hover\:bg-pink-100:hover {
+  --bg-opacity: 1;
+  background-color: #fff5f7;
+  background-color: rgba(255, 245, 247, var(--bg-opacity));
+}
+
+.hover\:bg-pink-200:hover {
+  --bg-opacity: 1;
+  background-color: #fed7e2;
+  background-color: rgba(254, 215, 226, var(--bg-opacity));
+}
+
+.hover\:bg-pink-300:hover {
+  --bg-opacity: 1;
+  background-color: #fbb6ce;
+  background-color: rgba(251, 182, 206, var(--bg-opacity));
+}
+
+.hover\:bg-pink-400:hover {
+  --bg-opacity: 1;
+  background-color: #f687b3;
+  background-color: rgba(246, 135, 179, var(--bg-opacity));
+}
+
+.hover\:bg-pink-500:hover {
+  --bg-opacity: 1;
+  background-color: #ed64a6;
+  background-color: rgba(237, 100, 166, var(--bg-opacity));
+}
+
+.hover\:bg-pink-600:hover {
+  --bg-opacity: 1;
+  background-color: #d53f8c;
+  background-color: rgba(213, 63, 140, var(--bg-opacity));
+}
+
+.hover\:bg-pink-700:hover {
+  --bg-opacity: 1;
+  background-color: #b83280;
+  background-color: rgba(184, 50, 128, var(--bg-opacity));
+}
+
+.hover\:bg-pink-800:hover {
+  --bg-opacity: 1;
+  background-color: #97266d;
+  background-color: rgba(151, 38, 109, var(--bg-opacity));
+}
+
+.hover\:bg-pink-900:hover {
+  --bg-opacity: 1;
+  background-color: #702459;
+  background-color: rgba(112, 36, 89, var(--bg-opacity));
+}
+
+.focus\:bg-transparent:focus {
+  background-color: transparent;
+}
+
+.focus\:bg-current:focus {
+  background-color: currentColor;
+}
+
+.focus\:bg-black:focus {
+  --bg-opacity: 1;
+  background-color: #000;
+  background-color: rgba(0, 0, 0, var(--bg-opacity));
+}
+
+.focus\:bg-white:focus {
+  --bg-opacity: 1;
+  background-color: #fff;
+  background-color: rgba(255, 255, 255, var(--bg-opacity));
+}
+
+.focus\:bg-gray-100:focus {
+  --bg-opacity: 1;
+  background-color: #f7fafc;
+  background-color: rgba(247, 250, 252, var(--bg-opacity));
+}
+
+.focus\:bg-gray-200:focus {
+  --bg-opacity: 1;
+  background-color: #edf2f7;
+  background-color: rgba(237, 242, 247, var(--bg-opacity));
+}
+
+.focus\:bg-gray-300:focus {
+  --bg-opacity: 1;
+  background-color: #e2e8f0;
+  background-color: rgba(226, 232, 240, var(--bg-opacity));
+}
+
+.focus\:bg-gray-400:focus {
+  --bg-opacity: 1;
+  background-color: #cbd5e0;
+  background-color: rgba(203, 213, 224, var(--bg-opacity));
+}
+
+.focus\:bg-gray-500:focus {
+  --bg-opacity: 1;
+  background-color: #a0aec0;
+  background-color: rgba(160, 174, 192, var(--bg-opacity));
+}
+
+.focus\:bg-gray-600:focus {
+  --bg-opacity: 1;
+  background-color: #718096;
+  background-color: rgba(113, 128, 150, var(--bg-opacity));
+}
+
+.focus\:bg-gray-700:focus {
+  --bg-opacity: 1;
+  background-color: #4a5568;
+  background-color: rgba(74, 85, 104, var(--bg-opacity));
+}
+
+.focus\:bg-gray-800:focus {
+  --bg-opacity: 1;
+  background-color: #2d3748;
+  background-color: rgba(45, 55, 72, var(--bg-opacity));
+}
+
+.focus\:bg-gray-900:focus {
+  --bg-opacity: 1;
+  background-color: #1a202c;
+  background-color: rgba(26, 32, 44, var(--bg-opacity));
+}
+
+.focus\:bg-red-100:focus {
+  --bg-opacity: 1;
+  background-color: #fff5f5;
+  background-color: rgba(255, 245, 245, var(--bg-opacity));
+}
+
+.focus\:bg-red-200:focus {
+  --bg-opacity: 1;
+  background-color: #fed7d7;
+  background-color: rgba(254, 215, 215, var(--bg-opacity));
+}
+
+.focus\:bg-red-300:focus {
+  --bg-opacity: 1;
+  background-color: #feb2b2;
+  background-color: rgba(254, 178, 178, var(--bg-opacity));
+}
+
+.focus\:bg-red-400:focus {
+  --bg-opacity: 1;
+  background-color: #fc8181;
+  background-color: rgba(252, 129, 129, var(--bg-opacity));
+}
+
+.focus\:bg-red-500:focus {
+  --bg-opacity: 1;
+  background-color: #f56565;
+  background-color: rgba(245, 101, 101, var(--bg-opacity));
+}
+
+.focus\:bg-red-600:focus {
+  --bg-opacity: 1;
+  background-color: #e53e3e;
+  background-color: rgba(229, 62, 62, var(--bg-opacity));
+}
+
+.focus\:bg-red-700:focus {
+  --bg-opacity: 1;
+  background-color: #c53030;
+  background-color: rgba(197, 48, 48, var(--bg-opacity));
+}
+
+.focus\:bg-red-800:focus {
+  --bg-opacity: 1;
+  background-color: #9b2c2c;
+  background-color: rgba(155, 44, 44, var(--bg-opacity));
+}
+
+.focus\:bg-red-900:focus {
+  --bg-opacity: 1;
+  background-color: #742a2a;
+  background-color: rgba(116, 42, 42, var(--bg-opacity));
+}
+
+.focus\:bg-orange-100:focus {
+  --bg-opacity: 1;
+  background-color: #fffaf0;
+  background-color: rgba(255, 250, 240, var(--bg-opacity));
+}
+
+.focus\:bg-orange-200:focus {
+  --bg-opacity: 1;
+  background-color: #feebc8;
+  background-color: rgba(254, 235, 200, var(--bg-opacity));
+}
+
+.focus\:bg-orange-300:focus {
+  --bg-opacity: 1;
+  background-color: #fbd38d;
+  background-color: rgba(251, 211, 141, var(--bg-opacity));
+}
+
+.focus\:bg-orange-400:focus {
+  --bg-opacity: 1;
+  background-color: #f6ad55;
+  background-color: rgba(246, 173, 85, var(--bg-opacity));
+}
+
+.focus\:bg-orange-500:focus {
+  --bg-opacity: 1;
+  background-color: #ed8936;
+  background-color: rgba(237, 137, 54, var(--bg-opacity));
+}
+
+.focus\:bg-orange-600:focus {
+  --bg-opacity: 1;
+  background-color: #dd6b20;
+  background-color: rgba(221, 107, 32, var(--bg-opacity));
+}
+
+.focus\:bg-orange-700:focus {
+  --bg-opacity: 1;
+  background-color: #c05621;
+  background-color: rgba(192, 86, 33, var(--bg-opacity));
+}
+
+.focus\:bg-orange-800:focus {
+  --bg-opacity: 1;
+  background-color: #9c4221;
+  background-color: rgba(156, 66, 33, var(--bg-opacity));
+}
+
+.focus\:bg-orange-900:focus {
+  --bg-opacity: 1;
+  background-color: #7b341e;
+  background-color: rgba(123, 52, 30, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-100:focus {
+  --bg-opacity: 1;
+  background-color: #fffff0;
+  background-color: rgba(255, 255, 240, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-200:focus {
+  --bg-opacity: 1;
+  background-color: #fefcbf;
+  background-color: rgba(254, 252, 191, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-300:focus {
+  --bg-opacity: 1;
+  background-color: #faf089;
+  background-color: rgba(250, 240, 137, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-400:focus {
+  --bg-opacity: 1;
+  background-color: #f6e05e;
+  background-color: rgba(246, 224, 94, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-500:focus {
+  --bg-opacity: 1;
+  background-color: #ecc94b;
+  background-color: rgba(236, 201, 75, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-600:focus {
+  --bg-opacity: 1;
+  background-color: #d69e2e;
+  background-color: rgba(214, 158, 46, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-700:focus {
+  --bg-opacity: 1;
+  background-color: #b7791f;
+  background-color: rgba(183, 121, 31, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-800:focus {
+  --bg-opacity: 1;
+  background-color: #975a16;
+  background-color: rgba(151, 90, 22, var(--bg-opacity));
+}
+
+.focus\:bg-yellow-900:focus {
+  --bg-opacity: 1;
+  background-color: #744210;
+  background-color: rgba(116, 66, 16, var(--bg-opacity));
+}
+
+.focus\:bg-green-100:focus {
+  --bg-opacity: 1;
+  background-color: #f0fff4;
+  background-color: rgba(240, 255, 244, var(--bg-opacity));
+}
+
+.focus\:bg-green-200:focus {
+  --bg-opacity: 1;
+  background-color: #c6f6d5;
+  background-color: rgba(198, 246, 213, var(--bg-opacity));
+}
+
+.focus\:bg-green-300:focus {
+  --bg-opacity: 1;
+  background-color: #9ae6b4;
+  background-color: rgba(154, 230, 180, var(--bg-opacity));
+}
+
+.focus\:bg-green-400:focus {
+  --bg-opacity: 1;
+  background-color: #68d391;
+  background-color: rgba(104, 211, 145, var(--bg-opacity));
+}
+
+.focus\:bg-green-500:focus {
+  --bg-opacity: 1;
+  background-color: #48bb78;
+  background-color: rgba(72, 187, 120, var(--bg-opacity));
+}
+
+.focus\:bg-green-600:focus {
+  --bg-opacity: 1;
+  background-color: #38a169;
+  background-color: rgba(56, 161, 105, var(--bg-opacity));
+}
+
+.focus\:bg-green-700:focus {
+  --bg-opacity: 1;
+  background-color: #2f855a;
+  background-color: rgba(47, 133, 90, var(--bg-opacity));
+}
+
+.focus\:bg-green-800:focus {
+  --bg-opacity: 1;
+  background-color: #276749;
+  background-color: rgba(39, 103, 73, var(--bg-opacity));
+}
+
+.focus\:bg-green-900:focus {
+  --bg-opacity: 1;
+  background-color: #22543d;
+  background-color: rgba(34, 84, 61, var(--bg-opacity));
+}
+
+.focus\:bg-teal-100:focus {
+  --bg-opacity: 1;
+  background-color: #e6fffa;
+  background-color: rgba(230, 255, 250, var(--bg-opacity));
+}
+
+.focus\:bg-teal-200:focus {
+  --bg-opacity: 1;
+  background-color: #b2f5ea;
+  background-color: rgba(178, 245, 234, var(--bg-opacity));
+}
+
+.focus\:bg-teal-300:focus {
+  --bg-opacity: 1;
+  background-color: #81e6d9;
+  background-color: rgba(129, 230, 217, var(--bg-opacity));
+}
+
+.focus\:bg-teal-400:focus {
+  --bg-opacity: 1;
+  background-color: #4fd1c5;
+  background-color: rgba(79, 209, 197, var(--bg-opacity));
+}
+
+.focus\:bg-teal-500:focus {
+  --bg-opacity: 1;
+  background-color: #38b2ac;
+  background-color: rgba(56, 178, 172, var(--bg-opacity));
+}
+
+.focus\:bg-teal-600:focus {
+  --bg-opacity: 1;
+  background-color: #319795;
+  background-color: rgba(49, 151, 149, var(--bg-opacity));
+}
+
+.focus\:bg-teal-700:focus {
+  --bg-opacity: 1;
+  background-color: #2c7a7b;
+  background-color: rgba(44, 122, 123, var(--bg-opacity));
+}
+
+.focus\:bg-teal-800:focus {
+  --bg-opacity: 1;
+  background-color: #285e61;
+  background-color: rgba(40, 94, 97, var(--bg-opacity));
+}
+
+.focus\:bg-teal-900:focus {
+  --bg-opacity: 1;
+  background-color: #234e52;
+  background-color: rgba(35, 78, 82, var(--bg-opacity));
+}
+
+.focus\:bg-blue-100:focus {
+  --bg-opacity: 1;
+  background-color: #ebf8ff;
+  background-color: rgba(235, 248, 255, var(--bg-opacity));
+}
+
+.focus\:bg-blue-200:focus {
+  --bg-opacity: 1;
+  background-color: #bee3f8;
+  background-color: rgba(190, 227, 248, var(--bg-opacity));
+}
+
+.focus\:bg-blue-300:focus {
+  --bg-opacity: 1;
+  background-color: #90cdf4;
+  background-color: rgba(144, 205, 244, var(--bg-opacity));
+}
+
+.focus\:bg-blue-400:focus {
+  --bg-opacity: 1;
+  background-color: #63b3ed;
+  background-color: rgba(99, 179, 237, var(--bg-opacity));
+}
+
+.focus\:bg-blue-500:focus {
+  --bg-opacity: 1;
+  background-color: #4299e1;
+  background-color: rgba(66, 153, 225, var(--bg-opacity));
+}
+
+.focus\:bg-blue-600:focus {
+  --bg-opacity: 1;
+  background-color: #3182ce;
+  background-color: rgba(49, 130, 206, var(--bg-opacity));
+}
+
+.focus\:bg-blue-700:focus {
+  --bg-opacity: 1;
+  background-color: #2b6cb0;
+  background-color: rgba(43, 108, 176, var(--bg-opacity));
+}
+
+.focus\:bg-blue-800:focus {
+  --bg-opacity: 1;
+  background-color: #2c5282;
+  background-color: rgba(44, 82, 130, var(--bg-opacity));
+}
+
+.focus\:bg-blue-900:focus {
+  --bg-opacity: 1;
+  background-color: #2a4365;
+  background-color: rgba(42, 67, 101, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-100:focus {
+  --bg-opacity: 1;
+  background-color: #ebf4ff;
+  background-color: rgba(235, 244, 255, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-200:focus {
+  --bg-opacity: 1;
+  background-color: #c3dafe;
+  background-color: rgba(195, 218, 254, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-300:focus {
+  --bg-opacity: 1;
+  background-color: #a3bffa;
+  background-color: rgba(163, 191, 250, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-400:focus {
+  --bg-opacity: 1;
+  background-color: #7f9cf5;
+  background-color: rgba(127, 156, 245, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-500:focus {
+  --bg-opacity: 1;
+  background-color: #667eea;
+  background-color: rgba(102, 126, 234, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-600:focus {
+  --bg-opacity: 1;
+  background-color: #5a67d8;
+  background-color: rgba(90, 103, 216, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-700:focus {
+  --bg-opacity: 1;
+  background-color: #4c51bf;
+  background-color: rgba(76, 81, 191, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-800:focus {
+  --bg-opacity: 1;
+  background-color: #434190;
+  background-color: rgba(67, 65, 144, var(--bg-opacity));
+}
+
+.focus\:bg-indigo-900:focus {
+  --bg-opacity: 1;
+  background-color: #3c366b;
+  background-color: rgba(60, 54, 107, var(--bg-opacity));
+}
+
+.focus\:bg-purple-100:focus {
+  --bg-opacity: 1;
+  background-color: #faf5ff;
+  background-color: rgba(250, 245, 255, var(--bg-opacity));
+}
+
+.focus\:bg-purple-200:focus {
+  --bg-opacity: 1;
+  background-color: #e9d8fd;
+  background-color: rgba(233, 216, 253, var(--bg-opacity));
+}
+
+.focus\:bg-purple-300:focus {
+  --bg-opacity: 1;
+  background-color: #d6bcfa;
+  background-color: rgba(214, 188, 250, var(--bg-opacity));
+}
+
+.focus\:bg-purple-400:focus {
+  --bg-opacity: 1;
+  background-color: #b794f4;
+  background-color: rgba(183, 148, 244, var(--bg-opacity));
+}
+
+.focus\:bg-purple-500:focus {
+  --bg-opacity: 1;
+  background-color: #9f7aea;
+  background-color: rgba(159, 122, 234, var(--bg-opacity));
+}
+
+.focus\:bg-purple-600:focus {
+  --bg-opacity: 1;
+  background-color: #805ad5;
+  background-color: rgba(128, 90, 213, var(--bg-opacity));
+}
+
+.focus\:bg-purple-700:focus {
+  --bg-opacity: 1;
+  background-color: #6b46c1;
+  background-color: rgba(107, 70, 193, var(--bg-opacity));
+}
+
+.focus\:bg-purple-800:focus {
+  --bg-opacity: 1;
+  background-color: #553c9a;
+  background-color: rgba(85, 60, 154, var(--bg-opacity));
+}
+
+.focus\:bg-purple-900:focus {
+  --bg-opacity: 1;
+  background-color: #44337a;
+  background-color: rgba(68, 51, 122, var(--bg-opacity));
+}
+
+.focus\:bg-pink-100:focus {
+  --bg-opacity: 1;
+  background-color: #fff5f7;
+  background-color: rgba(255, 245, 247, var(--bg-opacity));
+}
+
+.focus\:bg-pink-200:focus {
+  --bg-opacity: 1;
+  background-color: #fed7e2;
+  background-color: rgba(254, 215, 226, var(--bg-opacity));
+}
+
+.focus\:bg-pink-300:focus {
+  --bg-opacity: 1;
+  background-color: #fbb6ce;
+  background-color: rgba(251, 182, 206, var(--bg-opacity));
+}
+
+.focus\:bg-pink-400:focus {
+  --bg-opacity: 1;
+  background-color: #f687b3;
+  background-color: rgba(246, 135, 179, var(--bg-opacity));
+}
+
+.focus\:bg-pink-500:focus {
+  --bg-opacity: 1;
+  background-color: #ed64a6;
+  background-color: rgba(237, 100, 166, var(--bg-opacity));
+}
+
+.focus\:bg-pink-600:focus {
+  --bg-opacity: 1;
+  background-color: #d53f8c;
+  background-color: rgba(213, 63, 140, var(--bg-opacity));
+}
+
+.focus\:bg-pink-700:focus {
+  --bg-opacity: 1;
+  background-color: #b83280;
+  background-color: rgba(184, 50, 128, var(--bg-opacity));
+}
+
+.focus\:bg-pink-800:focus {
+  --bg-opacity: 1;
+  background-color: #97266d;
+  background-color: rgba(151, 38, 109, var(--bg-opacity));
+}
+
+.focus\:bg-pink-900:focus {
+  --bg-opacity: 1;
+  background-color: #702459;
+  background-color: rgba(112, 36, 89, var(--bg-opacity));
+}
+
+.bg-none {
+  background-image: none;
+}
+
+.bg-gradient-to-t {
+  background-image: linear-gradient(to top, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-tr {
+  background-image: linear-gradient(to top right, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-r {
+  background-image: linear-gradient(to right, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-br {
+  background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-b {
+  background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-bl {
+  background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-l {
+  background-image: linear-gradient(to left, var(--gradient-color-stops));
+}
+
+.bg-gradient-to-tl {
+  background-image: linear-gradient(to top left, var(--gradient-color-stops));
+}
+
+.from-transparent {
+  --gradient-from-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.from-current {
+  --gradient-from-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.from-black {
+  --gradient-from-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.from-white {
+  --gradient-from-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.from-gray-100 {
+  --gradient-from-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.from-gray-200 {
+  --gradient-from-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.from-gray-300 {
+  --gradient-from-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.from-gray-400 {
+  --gradient-from-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.from-gray-500 {
+  --gradient-from-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.from-gray-600 {
+  --gradient-from-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.from-gray-700 {
+  --gradient-from-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.from-gray-800 {
+  --gradient-from-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.from-gray-900 {
+  --gradient-from-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.from-red-100 {
+  --gradient-from-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.from-red-200 {
+  --gradient-from-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.from-red-300 {
+  --gradient-from-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.from-red-400 {
+  --gradient-from-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.from-red-500 {
+  --gradient-from-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.from-red-600 {
+  --gradient-from-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.from-red-700 {
+  --gradient-from-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.from-red-800 {
+  --gradient-from-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.from-red-900 {
+  --gradient-from-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.from-orange-100 {
+  --gradient-from-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.from-orange-200 {
+  --gradient-from-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.from-orange-300 {
+  --gradient-from-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.from-orange-400 {
+  --gradient-from-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.from-orange-500 {
+  --gradient-from-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.from-orange-600 {
+  --gradient-from-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.from-orange-700 {
+  --gradient-from-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.from-orange-800 {
+  --gradient-from-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.from-orange-900 {
+  --gradient-from-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.from-yellow-100 {
+  --gradient-from-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.from-yellow-200 {
+  --gradient-from-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.from-yellow-300 {
+  --gradient-from-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.from-yellow-400 {
+  --gradient-from-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.from-yellow-500 {
+  --gradient-from-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.from-yellow-600 {
+  --gradient-from-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.from-yellow-700 {
+  --gradient-from-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.from-yellow-800 {
+  --gradient-from-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.from-yellow-900 {
+  --gradient-from-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.from-green-100 {
+  --gradient-from-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.from-green-200 {
+  --gradient-from-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.from-green-300 {
+  --gradient-from-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.from-green-400 {
+  --gradient-from-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.from-green-500 {
+  --gradient-from-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.from-green-600 {
+  --gradient-from-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.from-green-700 {
+  --gradient-from-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.from-green-800 {
+  --gradient-from-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.from-green-900 {
+  --gradient-from-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.from-teal-100 {
+  --gradient-from-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.from-teal-200 {
+  --gradient-from-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.from-teal-300 {
+  --gradient-from-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.from-teal-400 {
+  --gradient-from-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.from-teal-500 {
+  --gradient-from-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.from-teal-600 {
+  --gradient-from-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.from-teal-700 {
+  --gradient-from-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.from-teal-800 {
+  --gradient-from-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.from-teal-900 {
+  --gradient-from-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.from-blue-100 {
+  --gradient-from-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.from-blue-200 {
+  --gradient-from-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.from-blue-300 {
+  --gradient-from-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.from-blue-400 {
+  --gradient-from-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.from-blue-500 {
+  --gradient-from-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.from-blue-600 {
+  --gradient-from-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.from-blue-700 {
+  --gradient-from-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.from-blue-800 {
+  --gradient-from-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.from-blue-900 {
+  --gradient-from-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.from-indigo-100 {
+  --gradient-from-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.from-indigo-200 {
+  --gradient-from-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.from-indigo-300 {
+  --gradient-from-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.from-indigo-400 {
+  --gradient-from-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.from-indigo-500 {
+  --gradient-from-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.from-indigo-600 {
+  --gradient-from-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.from-indigo-700 {
+  --gradient-from-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.from-indigo-800 {
+  --gradient-from-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.from-indigo-900 {
+  --gradient-from-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.from-purple-100 {
+  --gradient-from-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.from-purple-200 {
+  --gradient-from-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.from-purple-300 {
+  --gradient-from-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.from-purple-400 {
+  --gradient-from-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.from-purple-500 {
+  --gradient-from-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.from-purple-600 {
+  --gradient-from-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.from-purple-700 {
+  --gradient-from-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.from-purple-800 {
+  --gradient-from-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.from-purple-900 {
+  --gradient-from-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.from-pink-100 {
+  --gradient-from-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.from-pink-200 {
+  --gradient-from-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.from-pink-300 {
+  --gradient-from-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.from-pink-400 {
+  --gradient-from-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.from-pink-500 {
+  --gradient-from-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.from-pink-600 {
+  --gradient-from-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.from-pink-700 {
+  --gradient-from-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.from-pink-800 {
+  --gradient-from-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.from-pink-900 {
+  --gradient-from-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.via-transparent {
+  --gradient-via-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.via-current {
+  --gradient-via-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.via-black {
+  --gradient-via-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.via-white {
+  --gradient-via-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.via-gray-100 {
+  --gradient-via-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.via-gray-200 {
+  --gradient-via-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.via-gray-300 {
+  --gradient-via-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.via-gray-400 {
+  --gradient-via-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.via-gray-500 {
+  --gradient-via-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.via-gray-600 {
+  --gradient-via-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.via-gray-700 {
+  --gradient-via-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.via-gray-800 {
+  --gradient-via-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.via-gray-900 {
+  --gradient-via-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.via-red-100 {
+  --gradient-via-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.via-red-200 {
+  --gradient-via-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.via-red-300 {
+  --gradient-via-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.via-red-400 {
+  --gradient-via-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.via-red-500 {
+  --gradient-via-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.via-red-600 {
+  --gradient-via-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.via-red-700 {
+  --gradient-via-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.via-red-800 {
+  --gradient-via-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.via-red-900 {
+  --gradient-via-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.via-orange-100 {
+  --gradient-via-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.via-orange-200 {
+  --gradient-via-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.via-orange-300 {
+  --gradient-via-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.via-orange-400 {
+  --gradient-via-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.via-orange-500 {
+  --gradient-via-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.via-orange-600 {
+  --gradient-via-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.via-orange-700 {
+  --gradient-via-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.via-orange-800 {
+  --gradient-via-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.via-orange-900 {
+  --gradient-via-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.via-yellow-100 {
+  --gradient-via-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.via-yellow-200 {
+  --gradient-via-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.via-yellow-300 {
+  --gradient-via-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.via-yellow-400 {
+  --gradient-via-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.via-yellow-500 {
+  --gradient-via-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.via-yellow-600 {
+  --gradient-via-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.via-yellow-700 {
+  --gradient-via-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.via-yellow-800 {
+  --gradient-via-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.via-yellow-900 {
+  --gradient-via-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.via-green-100 {
+  --gradient-via-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.via-green-200 {
+  --gradient-via-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.via-green-300 {
+  --gradient-via-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.via-green-400 {
+  --gradient-via-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.via-green-500 {
+  --gradient-via-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.via-green-600 {
+  --gradient-via-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.via-green-700 {
+  --gradient-via-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.via-green-800 {
+  --gradient-via-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.via-green-900 {
+  --gradient-via-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.via-teal-100 {
+  --gradient-via-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.via-teal-200 {
+  --gradient-via-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.via-teal-300 {
+  --gradient-via-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.via-teal-400 {
+  --gradient-via-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.via-teal-500 {
+  --gradient-via-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.via-teal-600 {
+  --gradient-via-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.via-teal-700 {
+  --gradient-via-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.via-teal-800 {
+  --gradient-via-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.via-teal-900 {
+  --gradient-via-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.via-blue-100 {
+  --gradient-via-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.via-blue-200 {
+  --gradient-via-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.via-blue-300 {
+  --gradient-via-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.via-blue-400 {
+  --gradient-via-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.via-blue-500 {
+  --gradient-via-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.via-blue-600 {
+  --gradient-via-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.via-blue-700 {
+  --gradient-via-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.via-blue-800 {
+  --gradient-via-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.via-blue-900 {
+  --gradient-via-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.via-indigo-100 {
+  --gradient-via-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.via-indigo-200 {
+  --gradient-via-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.via-indigo-300 {
+  --gradient-via-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.via-indigo-400 {
+  --gradient-via-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.via-indigo-500 {
+  --gradient-via-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.via-indigo-600 {
+  --gradient-via-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.via-indigo-700 {
+  --gradient-via-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.via-indigo-800 {
+  --gradient-via-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.via-indigo-900 {
+  --gradient-via-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.via-purple-100 {
+  --gradient-via-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.via-purple-200 {
+  --gradient-via-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.via-purple-300 {
+  --gradient-via-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.via-purple-400 {
+  --gradient-via-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.via-purple-500 {
+  --gradient-via-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.via-purple-600 {
+  --gradient-via-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.via-purple-700 {
+  --gradient-via-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.via-purple-800 {
+  --gradient-via-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.via-purple-900 {
+  --gradient-via-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.via-pink-100 {
+  --gradient-via-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.via-pink-200 {
+  --gradient-via-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.via-pink-300 {
+  --gradient-via-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.via-pink-400 {
+  --gradient-via-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.via-pink-500 {
+  --gradient-via-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.via-pink-600 {
+  --gradient-via-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.via-pink-700 {
+  --gradient-via-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.via-pink-800 {
+  --gradient-via-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.via-pink-900 {
+  --gradient-via-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.to-transparent {
+  --gradient-to-color: transparent;
+}
+
+.to-current {
+  --gradient-to-color: currentColor;
+}
+
+.to-black {
+  --gradient-to-color: #000;
+}
+
+.to-white {
+  --gradient-to-color: #fff;
+}
+
+.to-gray-100 {
+  --gradient-to-color: #f7fafc;
+}
+
+.to-gray-200 {
+  --gradient-to-color: #edf2f7;
+}
+
+.to-gray-300 {
+  --gradient-to-color: #e2e8f0;
+}
+
+.to-gray-400 {
+  --gradient-to-color: #cbd5e0;
+}
+
+.to-gray-500 {
+  --gradient-to-color: #a0aec0;
+}
+
+.to-gray-600 {
+  --gradient-to-color: #718096;
+}
+
+.to-gray-700 {
+  --gradient-to-color: #4a5568;
+}
+
+.to-gray-800 {
+  --gradient-to-color: #2d3748;
+}
+
+.to-gray-900 {
+  --gradient-to-color: #1a202c;
+}
+
+.to-red-100 {
+  --gradient-to-color: #fff5f5;
+}
+
+.to-red-200 {
+  --gradient-to-color: #fed7d7;
+}
+
+.to-red-300 {
+  --gradient-to-color: #feb2b2;
+}
+
+.to-red-400 {
+  --gradient-to-color: #fc8181;
+}
+
+.to-red-500 {
+  --gradient-to-color: #f56565;
+}
+
+.to-red-600 {
+  --gradient-to-color: #e53e3e;
+}
+
+.to-red-700 {
+  --gradient-to-color: #c53030;
+}
+
+.to-red-800 {
+  --gradient-to-color: #9b2c2c;
+}
+
+.to-red-900 {
+  --gradient-to-color: #742a2a;
+}
+
+.to-orange-100 {
+  --gradient-to-color: #fffaf0;
+}
+
+.to-orange-200 {
+  --gradient-to-color: #feebc8;
+}
+
+.to-orange-300 {
+  --gradient-to-color: #fbd38d;
+}
+
+.to-orange-400 {
+  --gradient-to-color: #f6ad55;
+}
+
+.to-orange-500 {
+  --gradient-to-color: #ed8936;
+}
+
+.to-orange-600 {
+  --gradient-to-color: #dd6b20;
+}
+
+.to-orange-700 {
+  --gradient-to-color: #c05621;
+}
+
+.to-orange-800 {
+  --gradient-to-color: #9c4221;
+}
+
+.to-orange-900 {
+  --gradient-to-color: #7b341e;
+}
+
+.to-yellow-100 {
+  --gradient-to-color: #fffff0;
+}
+
+.to-yellow-200 {
+  --gradient-to-color: #fefcbf;
+}
+
+.to-yellow-300 {
+  --gradient-to-color: #faf089;
+}
+
+.to-yellow-400 {
+  --gradient-to-color: #f6e05e;
+}
+
+.to-yellow-500 {
+  --gradient-to-color: #ecc94b;
+}
+
+.to-yellow-600 {
+  --gradient-to-color: #d69e2e;
+}
+
+.to-yellow-700 {
+  --gradient-to-color: #b7791f;
+}
+
+.to-yellow-800 {
+  --gradient-to-color: #975a16;
+}
+
+.to-yellow-900 {
+  --gradient-to-color: #744210;
+}
+
+.to-green-100 {
+  --gradient-to-color: #f0fff4;
+}
+
+.to-green-200 {
+  --gradient-to-color: #c6f6d5;
+}
+
+.to-green-300 {
+  --gradient-to-color: #9ae6b4;
+}
+
+.to-green-400 {
+  --gradient-to-color: #68d391;
+}
+
+.to-green-500 {
+  --gradient-to-color: #48bb78;
+}
+
+.to-green-600 {
+  --gradient-to-color: #38a169;
+}
+
+.to-green-700 {
+  --gradient-to-color: #2f855a;
+}
+
+.to-green-800 {
+  --gradient-to-color: #276749;
+}
+
+.to-green-900 {
+  --gradient-to-color: #22543d;
+}
+
+.to-teal-100 {
+  --gradient-to-color: #e6fffa;
+}
+
+.to-teal-200 {
+  --gradient-to-color: #b2f5ea;
+}
+
+.to-teal-300 {
+  --gradient-to-color: #81e6d9;
+}
+
+.to-teal-400 {
+  --gradient-to-color: #4fd1c5;
+}
+
+.to-teal-500 {
+  --gradient-to-color: #38b2ac;
+}
+
+.to-teal-600 {
+  --gradient-to-color: #319795;
+}
+
+.to-teal-700 {
+  --gradient-to-color: #2c7a7b;
+}
+
+.to-teal-800 {
+  --gradient-to-color: #285e61;
+}
+
+.to-teal-900 {
+  --gradient-to-color: #234e52;
+}
+
+.to-blue-100 {
+  --gradient-to-color: #ebf8ff;
+}
+
+.to-blue-200 {
+  --gradient-to-color: #bee3f8;
+}
+
+.to-blue-300 {
+  --gradient-to-color: #90cdf4;
+}
+
+.to-blue-400 {
+  --gradient-to-color: #63b3ed;
+}
+
+.to-blue-500 {
+  --gradient-to-color: #4299e1;
+}
+
+.to-blue-600 {
+  --gradient-to-color: #3182ce;
+}
+
+.to-blue-700 {
+  --gradient-to-color: #2b6cb0;
+}
+
+.to-blue-800 {
+  --gradient-to-color: #2c5282;
+}
+
+.to-blue-900 {
+  --gradient-to-color: #2a4365;
+}
+
+.to-indigo-100 {
+  --gradient-to-color: #ebf4ff;
+}
+
+.to-indigo-200 {
+  --gradient-to-color: #c3dafe;
+}
+
+.to-indigo-300 {
+  --gradient-to-color: #a3bffa;
+}
+
+.to-indigo-400 {
+  --gradient-to-color: #7f9cf5;
+}
+
+.to-indigo-500 {
+  --gradient-to-color: #667eea;
+}
+
+.to-indigo-600 {
+  --gradient-to-color: #5a67d8;
+}
+
+.to-indigo-700 {
+  --gradient-to-color: #4c51bf;
+}
+
+.to-indigo-800 {
+  --gradient-to-color: #434190;
+}
+
+.to-indigo-900 {
+  --gradient-to-color: #3c366b;
+}
+
+.to-purple-100 {
+  --gradient-to-color: #faf5ff;
+}
+
+.to-purple-200 {
+  --gradient-to-color: #e9d8fd;
+}
+
+.to-purple-300 {
+  --gradient-to-color: #d6bcfa;
+}
+
+.to-purple-400 {
+  --gradient-to-color: #b794f4;
+}
+
+.to-purple-500 {
+  --gradient-to-color: #9f7aea;
+}
+
+.to-purple-600 {
+  --gradient-to-color: #805ad5;
+}
+
+.to-purple-700 {
+  --gradient-to-color: #6b46c1;
+}
+
+.to-purple-800 {
+  --gradient-to-color: #553c9a;
+}
+
+.to-purple-900 {
+  --gradient-to-color: #44337a;
+}
+
+.to-pink-100 {
+  --gradient-to-color: #fff5f7;
+}
+
+.to-pink-200 {
+  --gradient-to-color: #fed7e2;
+}
+
+.to-pink-300 {
+  --gradient-to-color: #fbb6ce;
+}
+
+.to-pink-400 {
+  --gradient-to-color: #f687b3;
+}
+
+.to-pink-500 {
+  --gradient-to-color: #ed64a6;
+}
+
+.to-pink-600 {
+  --gradient-to-color: #d53f8c;
+}
+
+.to-pink-700 {
+  --gradient-to-color: #b83280;
+}
+
+.to-pink-800 {
+  --gradient-to-color: #97266d;
+}
+
+.to-pink-900 {
+  --gradient-to-color: #702459;
+}
+
+.hover\:from-transparent:hover {
+  --gradient-from-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.hover\:from-current:hover {
+  --gradient-from-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.hover\:from-black:hover {
+  --gradient-from-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.hover\:from-white:hover {
+  --gradient-from-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.hover\:from-gray-100:hover {
+  --gradient-from-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.hover\:from-gray-200:hover {
+  --gradient-from-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.hover\:from-gray-300:hover {
+  --gradient-from-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.hover\:from-gray-400:hover {
+  --gradient-from-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.hover\:from-gray-500:hover {
+  --gradient-from-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.hover\:from-gray-600:hover {
+  --gradient-from-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.hover\:from-gray-700:hover {
+  --gradient-from-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.hover\:from-gray-800:hover {
+  --gradient-from-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.hover\:from-gray-900:hover {
+  --gradient-from-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.hover\:from-red-100:hover {
+  --gradient-from-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.hover\:from-red-200:hover {
+  --gradient-from-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.hover\:from-red-300:hover {
+  --gradient-from-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.hover\:from-red-400:hover {
+  --gradient-from-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.hover\:from-red-500:hover {
+  --gradient-from-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.hover\:from-red-600:hover {
+  --gradient-from-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.hover\:from-red-700:hover {
+  --gradient-from-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.hover\:from-red-800:hover {
+  --gradient-from-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.hover\:from-red-900:hover {
+  --gradient-from-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.hover\:from-orange-100:hover {
+  --gradient-from-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.hover\:from-orange-200:hover {
+  --gradient-from-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.hover\:from-orange-300:hover {
+  --gradient-from-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.hover\:from-orange-400:hover {
+  --gradient-from-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.hover\:from-orange-500:hover {
+  --gradient-from-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.hover\:from-orange-600:hover {
+  --gradient-from-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.hover\:from-orange-700:hover {
+  --gradient-from-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.hover\:from-orange-800:hover {
+  --gradient-from-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.hover\:from-orange-900:hover {
+  --gradient-from-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.hover\:from-yellow-100:hover {
+  --gradient-from-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.hover\:from-yellow-200:hover {
+  --gradient-from-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.hover\:from-yellow-300:hover {
+  --gradient-from-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.hover\:from-yellow-400:hover {
+  --gradient-from-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.hover\:from-yellow-500:hover {
+  --gradient-from-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.hover\:from-yellow-600:hover {
+  --gradient-from-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.hover\:from-yellow-700:hover {
+  --gradient-from-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.hover\:from-yellow-800:hover {
+  --gradient-from-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.hover\:from-yellow-900:hover {
+  --gradient-from-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.hover\:from-green-100:hover {
+  --gradient-from-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.hover\:from-green-200:hover {
+  --gradient-from-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.hover\:from-green-300:hover {
+  --gradient-from-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.hover\:from-green-400:hover {
+  --gradient-from-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.hover\:from-green-500:hover {
+  --gradient-from-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.hover\:from-green-600:hover {
+  --gradient-from-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.hover\:from-green-700:hover {
+  --gradient-from-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.hover\:from-green-800:hover {
+  --gradient-from-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.hover\:from-green-900:hover {
+  --gradient-from-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.hover\:from-teal-100:hover {
+  --gradient-from-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.hover\:from-teal-200:hover {
+  --gradient-from-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.hover\:from-teal-300:hover {
+  --gradient-from-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.hover\:from-teal-400:hover {
+  --gradient-from-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.hover\:from-teal-500:hover {
+  --gradient-from-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.hover\:from-teal-600:hover {
+  --gradient-from-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.hover\:from-teal-700:hover {
+  --gradient-from-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.hover\:from-teal-800:hover {
+  --gradient-from-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.hover\:from-teal-900:hover {
+  --gradient-from-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.hover\:from-blue-100:hover {
+  --gradient-from-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.hover\:from-blue-200:hover {
+  --gradient-from-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.hover\:from-blue-300:hover {
+  --gradient-from-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.hover\:from-blue-400:hover {
+  --gradient-from-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.hover\:from-blue-500:hover {
+  --gradient-from-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.hover\:from-blue-600:hover {
+  --gradient-from-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.hover\:from-blue-700:hover {
+  --gradient-from-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.hover\:from-blue-800:hover {
+  --gradient-from-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.hover\:from-blue-900:hover {
+  --gradient-from-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.hover\:from-indigo-100:hover {
+  --gradient-from-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.hover\:from-indigo-200:hover {
+  --gradient-from-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.hover\:from-indigo-300:hover {
+  --gradient-from-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.hover\:from-indigo-400:hover {
+  --gradient-from-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.hover\:from-indigo-500:hover {
+  --gradient-from-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.hover\:from-indigo-600:hover {
+  --gradient-from-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.hover\:from-indigo-700:hover {
+  --gradient-from-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.hover\:from-indigo-800:hover {
+  --gradient-from-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.hover\:from-indigo-900:hover {
+  --gradient-from-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.hover\:from-purple-100:hover {
+  --gradient-from-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.hover\:from-purple-200:hover {
+  --gradient-from-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.hover\:from-purple-300:hover {
+  --gradient-from-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.hover\:from-purple-400:hover {
+  --gradient-from-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.hover\:from-purple-500:hover {
+  --gradient-from-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.hover\:from-purple-600:hover {
+  --gradient-from-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.hover\:from-purple-700:hover {
+  --gradient-from-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.hover\:from-purple-800:hover {
+  --gradient-from-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.hover\:from-purple-900:hover {
+  --gradient-from-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.hover\:from-pink-100:hover {
+  --gradient-from-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.hover\:from-pink-200:hover {
+  --gradient-from-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.hover\:from-pink-300:hover {
+  --gradient-from-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.hover\:from-pink-400:hover {
+  --gradient-from-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.hover\:from-pink-500:hover {
+  --gradient-from-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.hover\:from-pink-600:hover {
+  --gradient-from-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.hover\:from-pink-700:hover {
+  --gradient-from-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.hover\:from-pink-800:hover {
+  --gradient-from-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.hover\:from-pink-900:hover {
+  --gradient-from-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.hover\:via-transparent:hover {
+  --gradient-via-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.hover\:via-current:hover {
+  --gradient-via-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.hover\:via-black:hover {
+  --gradient-via-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.hover\:via-white:hover {
+  --gradient-via-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.hover\:via-gray-100:hover {
+  --gradient-via-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.hover\:via-gray-200:hover {
+  --gradient-via-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.hover\:via-gray-300:hover {
+  --gradient-via-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.hover\:via-gray-400:hover {
+  --gradient-via-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.hover\:via-gray-500:hover {
+  --gradient-via-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.hover\:via-gray-600:hover {
+  --gradient-via-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.hover\:via-gray-700:hover {
+  --gradient-via-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.hover\:via-gray-800:hover {
+  --gradient-via-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.hover\:via-gray-900:hover {
+  --gradient-via-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.hover\:via-red-100:hover {
+  --gradient-via-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.hover\:via-red-200:hover {
+  --gradient-via-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.hover\:via-red-300:hover {
+  --gradient-via-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.hover\:via-red-400:hover {
+  --gradient-via-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.hover\:via-red-500:hover {
+  --gradient-via-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.hover\:via-red-600:hover {
+  --gradient-via-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.hover\:via-red-700:hover {
+  --gradient-via-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.hover\:via-red-800:hover {
+  --gradient-via-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.hover\:via-red-900:hover {
+  --gradient-via-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.hover\:via-orange-100:hover {
+  --gradient-via-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.hover\:via-orange-200:hover {
+  --gradient-via-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.hover\:via-orange-300:hover {
+  --gradient-via-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.hover\:via-orange-400:hover {
+  --gradient-via-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.hover\:via-orange-500:hover {
+  --gradient-via-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.hover\:via-orange-600:hover {
+  --gradient-via-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.hover\:via-orange-700:hover {
+  --gradient-via-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.hover\:via-orange-800:hover {
+  --gradient-via-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.hover\:via-orange-900:hover {
+  --gradient-via-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.hover\:via-yellow-100:hover {
+  --gradient-via-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.hover\:via-yellow-200:hover {
+  --gradient-via-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.hover\:via-yellow-300:hover {
+  --gradient-via-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.hover\:via-yellow-400:hover {
+  --gradient-via-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.hover\:via-yellow-500:hover {
+  --gradient-via-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.hover\:via-yellow-600:hover {
+  --gradient-via-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.hover\:via-yellow-700:hover {
+  --gradient-via-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.hover\:via-yellow-800:hover {
+  --gradient-via-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.hover\:via-yellow-900:hover {
+  --gradient-via-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.hover\:via-green-100:hover {
+  --gradient-via-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.hover\:via-green-200:hover {
+  --gradient-via-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.hover\:via-green-300:hover {
+  --gradient-via-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.hover\:via-green-400:hover {
+  --gradient-via-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.hover\:via-green-500:hover {
+  --gradient-via-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.hover\:via-green-600:hover {
+  --gradient-via-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.hover\:via-green-700:hover {
+  --gradient-via-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.hover\:via-green-800:hover {
+  --gradient-via-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.hover\:via-green-900:hover {
+  --gradient-via-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.hover\:via-teal-100:hover {
+  --gradient-via-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.hover\:via-teal-200:hover {
+  --gradient-via-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.hover\:via-teal-300:hover {
+  --gradient-via-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.hover\:via-teal-400:hover {
+  --gradient-via-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.hover\:via-teal-500:hover {
+  --gradient-via-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.hover\:via-teal-600:hover {
+  --gradient-via-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.hover\:via-teal-700:hover {
+  --gradient-via-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.hover\:via-teal-800:hover {
+  --gradient-via-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.hover\:via-teal-900:hover {
+  --gradient-via-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.hover\:via-blue-100:hover {
+  --gradient-via-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.hover\:via-blue-200:hover {
+  --gradient-via-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.hover\:via-blue-300:hover {
+  --gradient-via-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.hover\:via-blue-400:hover {
+  --gradient-via-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.hover\:via-blue-500:hover {
+  --gradient-via-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.hover\:via-blue-600:hover {
+  --gradient-via-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.hover\:via-blue-700:hover {
+  --gradient-via-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.hover\:via-blue-800:hover {
+  --gradient-via-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.hover\:via-blue-900:hover {
+  --gradient-via-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.hover\:via-indigo-100:hover {
+  --gradient-via-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.hover\:via-indigo-200:hover {
+  --gradient-via-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.hover\:via-indigo-300:hover {
+  --gradient-via-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.hover\:via-indigo-400:hover {
+  --gradient-via-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.hover\:via-indigo-500:hover {
+  --gradient-via-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.hover\:via-indigo-600:hover {
+  --gradient-via-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.hover\:via-indigo-700:hover {
+  --gradient-via-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.hover\:via-indigo-800:hover {
+  --gradient-via-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.hover\:via-indigo-900:hover {
+  --gradient-via-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.hover\:via-purple-100:hover {
+  --gradient-via-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.hover\:via-purple-200:hover {
+  --gradient-via-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.hover\:via-purple-300:hover {
+  --gradient-via-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.hover\:via-purple-400:hover {
+  --gradient-via-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.hover\:via-purple-500:hover {
+  --gradient-via-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.hover\:via-purple-600:hover {
+  --gradient-via-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.hover\:via-purple-700:hover {
+  --gradient-via-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.hover\:via-purple-800:hover {
+  --gradient-via-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.hover\:via-purple-900:hover {
+  --gradient-via-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.hover\:via-pink-100:hover {
+  --gradient-via-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.hover\:via-pink-200:hover {
+  --gradient-via-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.hover\:via-pink-300:hover {
+  --gradient-via-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.hover\:via-pink-400:hover {
+  --gradient-via-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.hover\:via-pink-500:hover {
+  --gradient-via-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.hover\:via-pink-600:hover {
+  --gradient-via-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.hover\:via-pink-700:hover {
+  --gradient-via-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.hover\:via-pink-800:hover {
+  --gradient-via-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.hover\:via-pink-900:hover {
+  --gradient-via-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.hover\:to-transparent:hover {
+  --gradient-to-color: transparent;
+}
+
+.hover\:to-current:hover {
+  --gradient-to-color: currentColor;
+}
+
+.hover\:to-black:hover {
+  --gradient-to-color: #000;
+}
+
+.hover\:to-white:hover {
+  --gradient-to-color: #fff;
+}
+
+.hover\:to-gray-100:hover {
+  --gradient-to-color: #f7fafc;
+}
+
+.hover\:to-gray-200:hover {
+  --gradient-to-color: #edf2f7;
+}
+
+.hover\:to-gray-300:hover {
+  --gradient-to-color: #e2e8f0;
+}
+
+.hover\:to-gray-400:hover {
+  --gradient-to-color: #cbd5e0;
+}
+
+.hover\:to-gray-500:hover {
+  --gradient-to-color: #a0aec0;
+}
+
+.hover\:to-gray-600:hover {
+  --gradient-to-color: #718096;
+}
+
+.hover\:to-gray-700:hover {
+  --gradient-to-color: #4a5568;
+}
+
+.hover\:to-gray-800:hover {
+  --gradient-to-color: #2d3748;
+}
+
+.hover\:to-gray-900:hover {
+  --gradient-to-color: #1a202c;
+}
+
+.hover\:to-red-100:hover {
+  --gradient-to-color: #fff5f5;
+}
+
+.hover\:to-red-200:hover {
+  --gradient-to-color: #fed7d7;
+}
+
+.hover\:to-red-300:hover {
+  --gradient-to-color: #feb2b2;
+}
+
+.hover\:to-red-400:hover {
+  --gradient-to-color: #fc8181;
+}
+
+.hover\:to-red-500:hover {
+  --gradient-to-color: #f56565;
+}
+
+.hover\:to-red-600:hover {
+  --gradient-to-color: #e53e3e;
+}
+
+.hover\:to-red-700:hover {
+  --gradient-to-color: #c53030;
+}
+
+.hover\:to-red-800:hover {
+  --gradient-to-color: #9b2c2c;
+}
+
+.hover\:to-red-900:hover {
+  --gradient-to-color: #742a2a;
+}
+
+.hover\:to-orange-100:hover {
+  --gradient-to-color: #fffaf0;
+}
+
+.hover\:to-orange-200:hover {
+  --gradient-to-color: #feebc8;
+}
+
+.hover\:to-orange-300:hover {
+  --gradient-to-color: #fbd38d;
+}
+
+.hover\:to-orange-400:hover {
+  --gradient-to-color: #f6ad55;
+}
+
+.hover\:to-orange-500:hover {
+  --gradient-to-color: #ed8936;
+}
+
+.hover\:to-orange-600:hover {
+  --gradient-to-color: #dd6b20;
+}
+
+.hover\:to-orange-700:hover {
+  --gradient-to-color: #c05621;
+}
+
+.hover\:to-orange-800:hover {
+  --gradient-to-color: #9c4221;
+}
+
+.hover\:to-orange-900:hover {
+  --gradient-to-color: #7b341e;
+}
+
+.hover\:to-yellow-100:hover {
+  --gradient-to-color: #fffff0;
+}
+
+.hover\:to-yellow-200:hover {
+  --gradient-to-color: #fefcbf;
+}
+
+.hover\:to-yellow-300:hover {
+  --gradient-to-color: #faf089;
+}
+
+.hover\:to-yellow-400:hover {
+  --gradient-to-color: #f6e05e;
+}
+
+.hover\:to-yellow-500:hover {
+  --gradient-to-color: #ecc94b;
+}
+
+.hover\:to-yellow-600:hover {
+  --gradient-to-color: #d69e2e;
+}
+
+.hover\:to-yellow-700:hover {
+  --gradient-to-color: #b7791f;
+}
+
+.hover\:to-yellow-800:hover {
+  --gradient-to-color: #975a16;
+}
+
+.hover\:to-yellow-900:hover {
+  --gradient-to-color: #744210;
+}
+
+.hover\:to-green-100:hover {
+  --gradient-to-color: #f0fff4;
+}
+
+.hover\:to-green-200:hover {
+  --gradient-to-color: #c6f6d5;
+}
+
+.hover\:to-green-300:hover {
+  --gradient-to-color: #9ae6b4;
+}
+
+.hover\:to-green-400:hover {
+  --gradient-to-color: #68d391;
+}
+
+.hover\:to-green-500:hover {
+  --gradient-to-color: #48bb78;
+}
+
+.hover\:to-green-600:hover {
+  --gradient-to-color: #38a169;
+}
+
+.hover\:to-green-700:hover {
+  --gradient-to-color: #2f855a;
+}
+
+.hover\:to-green-800:hover {
+  --gradient-to-color: #276749;
+}
+
+.hover\:to-green-900:hover {
+  --gradient-to-color: #22543d;
+}
+
+.hover\:to-teal-100:hover {
+  --gradient-to-color: #e6fffa;
+}
+
+.hover\:to-teal-200:hover {
+  --gradient-to-color: #b2f5ea;
+}
+
+.hover\:to-teal-300:hover {
+  --gradient-to-color: #81e6d9;
+}
+
+.hover\:to-teal-400:hover {
+  --gradient-to-color: #4fd1c5;
+}
+
+.hover\:to-teal-500:hover {
+  --gradient-to-color: #38b2ac;
+}
+
+.hover\:to-teal-600:hover {
+  --gradient-to-color: #319795;
+}
+
+.hover\:to-teal-700:hover {
+  --gradient-to-color: #2c7a7b;
+}
+
+.hover\:to-teal-800:hover {
+  --gradient-to-color: #285e61;
+}
+
+.hover\:to-teal-900:hover {
+  --gradient-to-color: #234e52;
+}
+
+.hover\:to-blue-100:hover {
+  --gradient-to-color: #ebf8ff;
+}
+
+.hover\:to-blue-200:hover {
+  --gradient-to-color: #bee3f8;
+}
+
+.hover\:to-blue-300:hover {
+  --gradient-to-color: #90cdf4;
+}
+
+.hover\:to-blue-400:hover {
+  --gradient-to-color: #63b3ed;
+}
+
+.hover\:to-blue-500:hover {
+  --gradient-to-color: #4299e1;
+}
+
+.hover\:to-blue-600:hover {
+  --gradient-to-color: #3182ce;
+}
+
+.hover\:to-blue-700:hover {
+  --gradient-to-color: #2b6cb0;
+}
+
+.hover\:to-blue-800:hover {
+  --gradient-to-color: #2c5282;
+}
+
+.hover\:to-blue-900:hover {
+  --gradient-to-color: #2a4365;
+}
+
+.hover\:to-indigo-100:hover {
+  --gradient-to-color: #ebf4ff;
+}
+
+.hover\:to-indigo-200:hover {
+  --gradient-to-color: #c3dafe;
+}
+
+.hover\:to-indigo-300:hover {
+  --gradient-to-color: #a3bffa;
+}
+
+.hover\:to-indigo-400:hover {
+  --gradient-to-color: #7f9cf5;
+}
+
+.hover\:to-indigo-500:hover {
+  --gradient-to-color: #667eea;
+}
+
+.hover\:to-indigo-600:hover {
+  --gradient-to-color: #5a67d8;
+}
+
+.hover\:to-indigo-700:hover {
+  --gradient-to-color: #4c51bf;
+}
+
+.hover\:to-indigo-800:hover {
+  --gradient-to-color: #434190;
+}
+
+.hover\:to-indigo-900:hover {
+  --gradient-to-color: #3c366b;
+}
+
+.hover\:to-purple-100:hover {
+  --gradient-to-color: #faf5ff;
+}
+
+.hover\:to-purple-200:hover {
+  --gradient-to-color: #e9d8fd;
+}
+
+.hover\:to-purple-300:hover {
+  --gradient-to-color: #d6bcfa;
+}
+
+.hover\:to-purple-400:hover {
+  --gradient-to-color: #b794f4;
+}
+
+.hover\:to-purple-500:hover {
+  --gradient-to-color: #9f7aea;
+}
+
+.hover\:to-purple-600:hover {
+  --gradient-to-color: #805ad5;
+}
+
+.hover\:to-purple-700:hover {
+  --gradient-to-color: #6b46c1;
+}
+
+.hover\:to-purple-800:hover {
+  --gradient-to-color: #553c9a;
+}
+
+.hover\:to-purple-900:hover {
+  --gradient-to-color: #44337a;
+}
+
+.hover\:to-pink-100:hover {
+  --gradient-to-color: #fff5f7;
+}
+
+.hover\:to-pink-200:hover {
+  --gradient-to-color: #fed7e2;
+}
+
+.hover\:to-pink-300:hover {
+  --gradient-to-color: #fbb6ce;
+}
+
+.hover\:to-pink-400:hover {
+  --gradient-to-color: #f687b3;
+}
+
+.hover\:to-pink-500:hover {
+  --gradient-to-color: #ed64a6;
+}
+
+.hover\:to-pink-600:hover {
+  --gradient-to-color: #d53f8c;
+}
+
+.hover\:to-pink-700:hover {
+  --gradient-to-color: #b83280;
+}
+
+.hover\:to-pink-800:hover {
+  --gradient-to-color: #97266d;
+}
+
+.hover\:to-pink-900:hover {
+  --gradient-to-color: #702459;
+}
+
+.focus\:from-transparent:focus {
+  --gradient-from-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.focus\:from-current:focus {
+  --gradient-from-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.focus\:from-black:focus {
+  --gradient-from-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.focus\:from-white:focus {
+  --gradient-from-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.focus\:from-gray-100:focus {
+  --gradient-from-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.focus\:from-gray-200:focus {
+  --gradient-from-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.focus\:from-gray-300:focus {
+  --gradient-from-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.focus\:from-gray-400:focus {
+  --gradient-from-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.focus\:from-gray-500:focus {
+  --gradient-from-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.focus\:from-gray-600:focus {
+  --gradient-from-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.focus\:from-gray-700:focus {
+  --gradient-from-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.focus\:from-gray-800:focus {
+  --gradient-from-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.focus\:from-gray-900:focus {
+  --gradient-from-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.focus\:from-red-100:focus {
+  --gradient-from-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.focus\:from-red-200:focus {
+  --gradient-from-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.focus\:from-red-300:focus {
+  --gradient-from-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.focus\:from-red-400:focus {
+  --gradient-from-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.focus\:from-red-500:focus {
+  --gradient-from-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.focus\:from-red-600:focus {
+  --gradient-from-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.focus\:from-red-700:focus {
+  --gradient-from-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.focus\:from-red-800:focus {
+  --gradient-from-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.focus\:from-red-900:focus {
+  --gradient-from-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.focus\:from-orange-100:focus {
+  --gradient-from-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.focus\:from-orange-200:focus {
+  --gradient-from-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.focus\:from-orange-300:focus {
+  --gradient-from-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.focus\:from-orange-400:focus {
+  --gradient-from-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.focus\:from-orange-500:focus {
+  --gradient-from-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.focus\:from-orange-600:focus {
+  --gradient-from-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.focus\:from-orange-700:focus {
+  --gradient-from-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.focus\:from-orange-800:focus {
+  --gradient-from-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.focus\:from-orange-900:focus {
+  --gradient-from-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.focus\:from-yellow-100:focus {
+  --gradient-from-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.focus\:from-yellow-200:focus {
+  --gradient-from-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.focus\:from-yellow-300:focus {
+  --gradient-from-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.focus\:from-yellow-400:focus {
+  --gradient-from-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.focus\:from-yellow-500:focus {
+  --gradient-from-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.focus\:from-yellow-600:focus {
+  --gradient-from-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.focus\:from-yellow-700:focus {
+  --gradient-from-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.focus\:from-yellow-800:focus {
+  --gradient-from-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.focus\:from-yellow-900:focus {
+  --gradient-from-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.focus\:from-green-100:focus {
+  --gradient-from-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.focus\:from-green-200:focus {
+  --gradient-from-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.focus\:from-green-300:focus {
+  --gradient-from-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.focus\:from-green-400:focus {
+  --gradient-from-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.focus\:from-green-500:focus {
+  --gradient-from-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.focus\:from-green-600:focus {
+  --gradient-from-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.focus\:from-green-700:focus {
+  --gradient-from-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.focus\:from-green-800:focus {
+  --gradient-from-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.focus\:from-green-900:focus {
+  --gradient-from-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.focus\:from-teal-100:focus {
+  --gradient-from-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.focus\:from-teal-200:focus {
+  --gradient-from-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.focus\:from-teal-300:focus {
+  --gradient-from-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.focus\:from-teal-400:focus {
+  --gradient-from-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.focus\:from-teal-500:focus {
+  --gradient-from-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.focus\:from-teal-600:focus {
+  --gradient-from-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.focus\:from-teal-700:focus {
+  --gradient-from-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.focus\:from-teal-800:focus {
+  --gradient-from-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.focus\:from-teal-900:focus {
+  --gradient-from-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.focus\:from-blue-100:focus {
+  --gradient-from-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.focus\:from-blue-200:focus {
+  --gradient-from-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.focus\:from-blue-300:focus {
+  --gradient-from-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.focus\:from-blue-400:focus {
+  --gradient-from-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.focus\:from-blue-500:focus {
+  --gradient-from-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.focus\:from-blue-600:focus {
+  --gradient-from-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.focus\:from-blue-700:focus {
+  --gradient-from-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.focus\:from-blue-800:focus {
+  --gradient-from-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.focus\:from-blue-900:focus {
+  --gradient-from-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.focus\:from-indigo-100:focus {
+  --gradient-from-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.focus\:from-indigo-200:focus {
+  --gradient-from-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.focus\:from-indigo-300:focus {
+  --gradient-from-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.focus\:from-indigo-400:focus {
+  --gradient-from-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.focus\:from-indigo-500:focus {
+  --gradient-from-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.focus\:from-indigo-600:focus {
+  --gradient-from-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.focus\:from-indigo-700:focus {
+  --gradient-from-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.focus\:from-indigo-800:focus {
+  --gradient-from-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.focus\:from-indigo-900:focus {
+  --gradient-from-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.focus\:from-purple-100:focus {
+  --gradient-from-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.focus\:from-purple-200:focus {
+  --gradient-from-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.focus\:from-purple-300:focus {
+  --gradient-from-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.focus\:from-purple-400:focus {
+  --gradient-from-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.focus\:from-purple-500:focus {
+  --gradient-from-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.focus\:from-purple-600:focus {
+  --gradient-from-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.focus\:from-purple-700:focus {
+  --gradient-from-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.focus\:from-purple-800:focus {
+  --gradient-from-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.focus\:from-purple-900:focus {
+  --gradient-from-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.focus\:from-pink-100:focus {
+  --gradient-from-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.focus\:from-pink-200:focus {
+  --gradient-from-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.focus\:from-pink-300:focus {
+  --gradient-from-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.focus\:from-pink-400:focus {
+  --gradient-from-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.focus\:from-pink-500:focus {
+  --gradient-from-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.focus\:from-pink-600:focus {
+  --gradient-from-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.focus\:from-pink-700:focus {
+  --gradient-from-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.focus\:from-pink-800:focus {
+  --gradient-from-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.focus\:from-pink-900:focus {
+  --gradient-from-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.focus\:via-transparent:focus {
+  --gradient-via-color: transparent;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.focus\:via-current:focus {
+  --gradient-via-color: currentColor;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.focus\:via-black:focus {
+  --gradient-via-color: #000;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+}
+
+.focus\:via-white:focus {
+  --gradient-via-color: #fff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+}
+
+.focus\:via-gray-100:focus {
+  --gradient-via-color: #f7fafc;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+}
+
+.focus\:via-gray-200:focus {
+  --gradient-via-color: #edf2f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+}
+
+.focus\:via-gray-300:focus {
+  --gradient-via-color: #e2e8f0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+}
+
+.focus\:via-gray-400:focus {
+  --gradient-via-color: #cbd5e0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+}
+
+.focus\:via-gray-500:focus {
+  --gradient-via-color: #a0aec0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+}
+
+.focus\:via-gray-600:focus {
+  --gradient-via-color: #718096;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+}
+
+.focus\:via-gray-700:focus {
+  --gradient-via-color: #4a5568;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+}
+
+.focus\:via-gray-800:focus {
+  --gradient-via-color: #2d3748;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+}
+
+.focus\:via-gray-900:focus {
+  --gradient-via-color: #1a202c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+}
+
+.focus\:via-red-100:focus {
+  --gradient-via-color: #fff5f5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+}
+
+.focus\:via-red-200:focus {
+  --gradient-via-color: #fed7d7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+}
+
+.focus\:via-red-300:focus {
+  --gradient-via-color: #feb2b2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+}
+
+.focus\:via-red-400:focus {
+  --gradient-via-color: #fc8181;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+}
+
+.focus\:via-red-500:focus {
+  --gradient-via-color: #f56565;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+}
+
+.focus\:via-red-600:focus {
+  --gradient-via-color: #e53e3e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+}
+
+.focus\:via-red-700:focus {
+  --gradient-via-color: #c53030;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+}
+
+.focus\:via-red-800:focus {
+  --gradient-via-color: #9b2c2c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+}
+
+.focus\:via-red-900:focus {
+  --gradient-via-color: #742a2a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+}
+
+.focus\:via-orange-100:focus {
+  --gradient-via-color: #fffaf0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+}
+
+.focus\:via-orange-200:focus {
+  --gradient-via-color: #feebc8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+}
+
+.focus\:via-orange-300:focus {
+  --gradient-via-color: #fbd38d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+}
+
+.focus\:via-orange-400:focus {
+  --gradient-via-color: #f6ad55;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+}
+
+.focus\:via-orange-500:focus {
+  --gradient-via-color: #ed8936;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+}
+
+.focus\:via-orange-600:focus {
+  --gradient-via-color: #dd6b20;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+}
+
+.focus\:via-orange-700:focus {
+  --gradient-via-color: #c05621;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+}
+
+.focus\:via-orange-800:focus {
+  --gradient-via-color: #9c4221;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+}
+
+.focus\:via-orange-900:focus {
+  --gradient-via-color: #7b341e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+}
+
+.focus\:via-yellow-100:focus {
+  --gradient-via-color: #fffff0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+}
+
+.focus\:via-yellow-200:focus {
+  --gradient-via-color: #fefcbf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+}
+
+.focus\:via-yellow-300:focus {
+  --gradient-via-color: #faf089;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+}
+
+.focus\:via-yellow-400:focus {
+  --gradient-via-color: #f6e05e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+}
+
+.focus\:via-yellow-500:focus {
+  --gradient-via-color: #ecc94b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+}
+
+.focus\:via-yellow-600:focus {
+  --gradient-via-color: #d69e2e;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+}
+
+.focus\:via-yellow-700:focus {
+  --gradient-via-color: #b7791f;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+}
+
+.focus\:via-yellow-800:focus {
+  --gradient-via-color: #975a16;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+}
+
+.focus\:via-yellow-900:focus {
+  --gradient-via-color: #744210;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+}
+
+.focus\:via-green-100:focus {
+  --gradient-via-color: #f0fff4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+}
+
+.focus\:via-green-200:focus {
+  --gradient-via-color: #c6f6d5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+}
+
+.focus\:via-green-300:focus {
+  --gradient-via-color: #9ae6b4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+}
+
+.focus\:via-green-400:focus {
+  --gradient-via-color: #68d391;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+}
+
+.focus\:via-green-500:focus {
+  --gradient-via-color: #48bb78;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+}
+
+.focus\:via-green-600:focus {
+  --gradient-via-color: #38a169;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+}
+
+.focus\:via-green-700:focus {
+  --gradient-via-color: #2f855a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+}
+
+.focus\:via-green-800:focus {
+  --gradient-via-color: #276749;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+}
+
+.focus\:via-green-900:focus {
+  --gradient-via-color: #22543d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+}
+
+.focus\:via-teal-100:focus {
+  --gradient-via-color: #e6fffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+}
+
+.focus\:via-teal-200:focus {
+  --gradient-via-color: #b2f5ea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+}
+
+.focus\:via-teal-300:focus {
+  --gradient-via-color: #81e6d9;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+}
+
+.focus\:via-teal-400:focus {
+  --gradient-via-color: #4fd1c5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+}
+
+.focus\:via-teal-500:focus {
+  --gradient-via-color: #38b2ac;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+}
+
+.focus\:via-teal-600:focus {
+  --gradient-via-color: #319795;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+}
+
+.focus\:via-teal-700:focus {
+  --gradient-via-color: #2c7a7b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+}
+
+.focus\:via-teal-800:focus {
+  --gradient-via-color: #285e61;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+}
+
+.focus\:via-teal-900:focus {
+  --gradient-via-color: #234e52;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+}
+
+.focus\:via-blue-100:focus {
+  --gradient-via-color: #ebf8ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+}
+
+.focus\:via-blue-200:focus {
+  --gradient-via-color: #bee3f8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+}
+
+.focus\:via-blue-300:focus {
+  --gradient-via-color: #90cdf4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+}
+
+.focus\:via-blue-400:focus {
+  --gradient-via-color: #63b3ed;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+}
+
+.focus\:via-blue-500:focus {
+  --gradient-via-color: #4299e1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+}
+
+.focus\:via-blue-600:focus {
+  --gradient-via-color: #3182ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+}
+
+.focus\:via-blue-700:focus {
+  --gradient-via-color: #2b6cb0;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+}
+
+.focus\:via-blue-800:focus {
+  --gradient-via-color: #2c5282;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+}
+
+.focus\:via-blue-900:focus {
+  --gradient-via-color: #2a4365;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+}
+
+.focus\:via-indigo-100:focus {
+  --gradient-via-color: #ebf4ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+}
+
+.focus\:via-indigo-200:focus {
+  --gradient-via-color: #c3dafe;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+}
+
+.focus\:via-indigo-300:focus {
+  --gradient-via-color: #a3bffa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+}
+
+.focus\:via-indigo-400:focus {
+  --gradient-via-color: #7f9cf5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+}
+
+.focus\:via-indigo-500:focus {
+  --gradient-via-color: #667eea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+}
+
+.focus\:via-indigo-600:focus {
+  --gradient-via-color: #5a67d8;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+}
+
+.focus\:via-indigo-700:focus {
+  --gradient-via-color: #4c51bf;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+}
+
+.focus\:via-indigo-800:focus {
+  --gradient-via-color: #434190;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+}
+
+.focus\:via-indigo-900:focus {
+  --gradient-via-color: #3c366b;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+}
+
+.focus\:via-purple-100:focus {
+  --gradient-via-color: #faf5ff;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+}
+
+.focus\:via-purple-200:focus {
+  --gradient-via-color: #e9d8fd;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+}
+
+.focus\:via-purple-300:focus {
+  --gradient-via-color: #d6bcfa;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+}
+
+.focus\:via-purple-400:focus {
+  --gradient-via-color: #b794f4;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+}
+
+.focus\:via-purple-500:focus {
+  --gradient-via-color: #9f7aea;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+}
+
+.focus\:via-purple-600:focus {
+  --gradient-via-color: #805ad5;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+}
+
+.focus\:via-purple-700:focus {
+  --gradient-via-color: #6b46c1;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+}
+
+.focus\:via-purple-800:focus {
+  --gradient-via-color: #553c9a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+}
+
+.focus\:via-purple-900:focus {
+  --gradient-via-color: #44337a;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+}
+
+.focus\:via-pink-100:focus {
+  --gradient-via-color: #fff5f7;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+}
+
+.focus\:via-pink-200:focus {
+  --gradient-via-color: #fed7e2;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+}
+
+.focus\:via-pink-300:focus {
+  --gradient-via-color: #fbb6ce;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+}
+
+.focus\:via-pink-400:focus {
+  --gradient-via-color: #f687b3;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+}
+
+.focus\:via-pink-500:focus {
+  --gradient-via-color: #ed64a6;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+}
+
+.focus\:via-pink-600:focus {
+  --gradient-via-color: #d53f8c;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+}
+
+.focus\:via-pink-700:focus {
+  --gradient-via-color: #b83280;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+}
+
+.focus\:via-pink-800:focus {
+  --gradient-via-color: #97266d;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+}
+
+.focus\:via-pink-900:focus {
+  --gradient-via-color: #702459;
+  --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+}
+
+.focus\:to-transparent:focus {
+  --gradient-to-color: transparent;
+}
+
+.focus\:to-current:focus {
+  --gradient-to-color: currentColor;
+}
+
+.focus\:to-black:focus {
+  --gradient-to-color: #000;
+}
+
+.focus\:to-white:focus {
+  --gradient-to-color: #fff;
+}
+
+.focus\:to-gray-100:focus {
+  --gradient-to-color: #f7fafc;
+}
+
+.focus\:to-gray-200:focus {
+  --gradient-to-color: #edf2f7;
+}
+
+.focus\:to-gray-300:focus {
+  --gradient-to-color: #e2e8f0;
+}
+
+.focus\:to-gray-400:focus {
+  --gradient-to-color: #cbd5e0;
+}
+
+.focus\:to-gray-500:focus {
+  --gradient-to-color: #a0aec0;
+}
+
+.focus\:to-gray-600:focus {
+  --gradient-to-color: #718096;
+}
+
+.focus\:to-gray-700:focus {
+  --gradient-to-color: #4a5568;
+}
+
+.focus\:to-gray-800:focus {
+  --gradient-to-color: #2d3748;
+}
+
+.focus\:to-gray-900:focus {
+  --gradient-to-color: #1a202c;
+}
+
+.focus\:to-red-100:focus {
+  --gradient-to-color: #fff5f5;
+}
+
+.focus\:to-red-200:focus {
+  --gradient-to-color: #fed7d7;
+}
+
+.focus\:to-red-300:focus {
+  --gradient-to-color: #feb2b2;
+}
+
+.focus\:to-red-400:focus {
+  --gradient-to-color: #fc8181;
+}
+
+.focus\:to-red-500:focus {
+  --gradient-to-color: #f56565;
+}
+
+.focus\:to-red-600:focus {
+  --gradient-to-color: #e53e3e;
+}
+
+.focus\:to-red-700:focus {
+  --gradient-to-color: #c53030;
+}
+
+.focus\:to-red-800:focus {
+  --gradient-to-color: #9b2c2c;
+}
+
+.focus\:to-red-900:focus {
+  --gradient-to-color: #742a2a;
+}
+
+.focus\:to-orange-100:focus {
+  --gradient-to-color: #fffaf0;
+}
+
+.focus\:to-orange-200:focus {
+  --gradient-to-color: #feebc8;
+}
+
+.focus\:to-orange-300:focus {
+  --gradient-to-color: #fbd38d;
+}
+
+.focus\:to-orange-400:focus {
+  --gradient-to-color: #f6ad55;
+}
+
+.focus\:to-orange-500:focus {
+  --gradient-to-color: #ed8936;
+}
+
+.focus\:to-orange-600:focus {
+  --gradient-to-color: #dd6b20;
+}
+
+.focus\:to-orange-700:focus {
+  --gradient-to-color: #c05621;
+}
+
+.focus\:to-orange-800:focus {
+  --gradient-to-color: #9c4221;
+}
+
+.focus\:to-orange-900:focus {
+  --gradient-to-color: #7b341e;
+}
+
+.focus\:to-yellow-100:focus {
+  --gradient-to-color: #fffff0;
+}
+
+.focus\:to-yellow-200:focus {
+  --gradient-to-color: #fefcbf;
+}
+
+.focus\:to-yellow-300:focus {
+  --gradient-to-color: #faf089;
+}
+
+.focus\:to-yellow-400:focus {
+  --gradient-to-color: #f6e05e;
+}
+
+.focus\:to-yellow-500:focus {
+  --gradient-to-color: #ecc94b;
+}
+
+.focus\:to-yellow-600:focus {
+  --gradient-to-color: #d69e2e;
+}
+
+.focus\:to-yellow-700:focus {
+  --gradient-to-color: #b7791f;
+}
+
+.focus\:to-yellow-800:focus {
+  --gradient-to-color: #975a16;
+}
+
+.focus\:to-yellow-900:focus {
+  --gradient-to-color: #744210;
+}
+
+.focus\:to-green-100:focus {
+  --gradient-to-color: #f0fff4;
+}
+
+.focus\:to-green-200:focus {
+  --gradient-to-color: #c6f6d5;
+}
+
+.focus\:to-green-300:focus {
+  --gradient-to-color: #9ae6b4;
+}
+
+.focus\:to-green-400:focus {
+  --gradient-to-color: #68d391;
+}
+
+.focus\:to-green-500:focus {
+  --gradient-to-color: #48bb78;
+}
+
+.focus\:to-green-600:focus {
+  --gradient-to-color: #38a169;
+}
+
+.focus\:to-green-700:focus {
+  --gradient-to-color: #2f855a;
+}
+
+.focus\:to-green-800:focus {
+  --gradient-to-color: #276749;
+}
+
+.focus\:to-green-900:focus {
+  --gradient-to-color: #22543d;
+}
+
+.focus\:to-teal-100:focus {
+  --gradient-to-color: #e6fffa;
+}
+
+.focus\:to-teal-200:focus {
+  --gradient-to-color: #b2f5ea;
+}
+
+.focus\:to-teal-300:focus {
+  --gradient-to-color: #81e6d9;
+}
+
+.focus\:to-teal-400:focus {
+  --gradient-to-color: #4fd1c5;
+}
+
+.focus\:to-teal-500:focus {
+  --gradient-to-color: #38b2ac;
+}
+
+.focus\:to-teal-600:focus {
+  --gradient-to-color: #319795;
+}
+
+.focus\:to-teal-700:focus {
+  --gradient-to-color: #2c7a7b;
+}
+
+.focus\:to-teal-800:focus {
+  --gradient-to-color: #285e61;
+}
+
+.focus\:to-teal-900:focus {
+  --gradient-to-color: #234e52;
+}
+
+.focus\:to-blue-100:focus {
+  --gradient-to-color: #ebf8ff;
+}
+
+.focus\:to-blue-200:focus {
+  --gradient-to-color: #bee3f8;
+}
+
+.focus\:to-blue-300:focus {
+  --gradient-to-color: #90cdf4;
+}
+
+.focus\:to-blue-400:focus {
+  --gradient-to-color: #63b3ed;
+}
+
+.focus\:to-blue-500:focus {
+  --gradient-to-color: #4299e1;
+}
+
+.focus\:to-blue-600:focus {
+  --gradient-to-color: #3182ce;
+}
+
+.focus\:to-blue-700:focus {
+  --gradient-to-color: #2b6cb0;
+}
+
+.focus\:to-blue-800:focus {
+  --gradient-to-color: #2c5282;
+}
+
+.focus\:to-blue-900:focus {
+  --gradient-to-color: #2a4365;
+}
+
+.focus\:to-indigo-100:focus {
+  --gradient-to-color: #ebf4ff;
+}
+
+.focus\:to-indigo-200:focus {
+  --gradient-to-color: #c3dafe;
+}
+
+.focus\:to-indigo-300:focus {
+  --gradient-to-color: #a3bffa;
+}
+
+.focus\:to-indigo-400:focus {
+  --gradient-to-color: #7f9cf5;
+}
+
+.focus\:to-indigo-500:focus {
+  --gradient-to-color: #667eea;
+}
+
+.focus\:to-indigo-600:focus {
+  --gradient-to-color: #5a67d8;
+}
+
+.focus\:to-indigo-700:focus {
+  --gradient-to-color: #4c51bf;
+}
+
+.focus\:to-indigo-800:focus {
+  --gradient-to-color: #434190;
+}
+
+.focus\:to-indigo-900:focus {
+  --gradient-to-color: #3c366b;
+}
+
+.focus\:to-purple-100:focus {
+  --gradient-to-color: #faf5ff;
+}
+
+.focus\:to-purple-200:focus {
+  --gradient-to-color: #e9d8fd;
+}
+
+.focus\:to-purple-300:focus {
+  --gradient-to-color: #d6bcfa;
+}
+
+.focus\:to-purple-400:focus {
+  --gradient-to-color: #b794f4;
+}
+
+.focus\:to-purple-500:focus {
+  --gradient-to-color: #9f7aea;
+}
+
+.focus\:to-purple-600:focus {
+  --gradient-to-color: #805ad5;
+}
+
+.focus\:to-purple-700:focus {
+  --gradient-to-color: #6b46c1;
+}
+
+.focus\:to-purple-800:focus {
+  --gradient-to-color: #553c9a;
+}
+
+.focus\:to-purple-900:focus {
+  --gradient-to-color: #44337a;
+}
+
+.focus\:to-pink-100:focus {
+  --gradient-to-color: #fff5f7;
+}
+
+.focus\:to-pink-200:focus {
+  --gradient-to-color: #fed7e2;
+}
+
+.focus\:to-pink-300:focus {
+  --gradient-to-color: #fbb6ce;
+}
+
+.focus\:to-pink-400:focus {
+  --gradient-to-color: #f687b3;
+}
+
+.focus\:to-pink-500:focus {
+  --gradient-to-color: #ed64a6;
+}
+
+.focus\:to-pink-600:focus {
+  --gradient-to-color: #d53f8c;
+}
+
+.focus\:to-pink-700:focus {
+  --gradient-to-color: #b83280;
+}
+
+.focus\:to-pink-800:focus {
+  --gradient-to-color: #97266d;
+}
+
+.focus\:to-pink-900:focus {
+  --gradient-to-color: #702459;
+}
+
+.bg-opacity-0 {
+  --bg-opacity: 0;
+}
+
+.bg-opacity-25 {
+  --bg-opacity: 0.25;
+}
+
+.bg-opacity-50 {
+  --bg-opacity: 0.5;
+}
+
+.bg-opacity-75 {
+  --bg-opacity: 0.75;
+}
+
+.bg-opacity-100 {
+  --bg-opacity: 1;
+}
+
+.hover\:bg-opacity-0:hover {
+  --bg-opacity: 0;
+}
+
+.hover\:bg-opacity-25:hover {
+  --bg-opacity: 0.25;
+}
+
+.hover\:bg-opacity-50:hover {
+  --bg-opacity: 0.5;
+}
+
+.hover\:bg-opacity-75:hover {
+  --bg-opacity: 0.75;
+}
+
+.hover\:bg-opacity-100:hover {
+  --bg-opacity: 1;
+}
+
+.focus\:bg-opacity-0:focus {
+  --bg-opacity: 0;
+}
+
+.focus\:bg-opacity-25:focus {
+  --bg-opacity: 0.25;
+}
+
+.focus\:bg-opacity-50:focus {
+  --bg-opacity: 0.5;
+}
+
+.focus\:bg-opacity-75:focus {
+  --bg-opacity: 0.75;
+}
+
+.focus\:bg-opacity-100:focus {
+  --bg-opacity: 1;
+}
+
+.bg-bottom {
+  background-position: bottom;
+}
+
+.bg-center {
+  background-position: center;
+}
+
+.bg-left {
+  background-position: left;
+}
+
+.bg-left-bottom {
+  background-position: left bottom;
+}
+
+.bg-left-top {
+  background-position: left top;
+}
+
+.bg-right {
+  background-position: right;
+}
+
+.bg-right-bottom {
+  background-position: right bottom;
+}
+
+.bg-right-top {
+  background-position: right top;
+}
+
+.bg-top {
+  background-position: top;
+}
+
+.bg-repeat {
+  background-repeat: repeat;
+}
+
+.bg-no-repeat {
+  background-repeat: no-repeat;
+}
+
+.bg-repeat-x {
+  background-repeat: repeat-x;
+}
+
+.bg-repeat-y {
+  background-repeat: repeat-y;
+}
+
+.bg-repeat-round {
+  background-repeat: round;
+}
+
+.bg-repeat-space {
+  background-repeat: space;
+}
+
+.bg-auto {
+  background-size: auto;
+}
+
+.bg-cover {
+  background-size: cover;
+}
+
+.bg-contain {
+  background-size: contain;
+}
+
+.border-collapse {
+  border-collapse: collapse;
+}
+
+.border-separate {
+  border-collapse: separate;
+}
+
+.border-transparent {
+  border-color: transparent;
+}
+
+.border-current {
+  border-color: currentColor;
+}
+
+.border-black {
+  --border-opacity: 1;
+  border-color: #000;
+  border-color: rgba(0, 0, 0, var(--border-opacity));
+}
+
+.border-white {
+  --border-opacity: 1;
+  border-color: #fff;
+  border-color: rgba(255, 255, 255, var(--border-opacity));
+}
+
+.border-gray-100 {
+  --border-opacity: 1;
+  border-color: #f7fafc;
+  border-color: rgba(247, 250, 252, var(--border-opacity));
+}
+
+.border-gray-200 {
+  --border-opacity: 1;
+  border-color: #edf2f7;
+  border-color: rgba(237, 242, 247, var(--border-opacity));
+}
+
+.border-gray-300 {
+  --border-opacity: 1;
+  border-color: #e2e8f0;
+  border-color: rgba(226, 232, 240, var(--border-opacity));
+}
+
+.border-gray-400 {
+  --border-opacity: 1;
+  border-color: #cbd5e0;
+  border-color: rgba(203, 213, 224, var(--border-opacity));
+}
+
+.border-gray-500 {
+  --border-opacity: 1;
+  border-color: #a0aec0;
+  border-color: rgba(160, 174, 192, var(--border-opacity));
+}
+
+.border-gray-600 {
+  --border-opacity: 1;
+  border-color: #718096;
+  border-color: rgba(113, 128, 150, var(--border-opacity));
+}
+
+.border-gray-700 {
+  --border-opacity: 1;
+  border-color: #4a5568;
+  border-color: rgba(74, 85, 104, var(--border-opacity));
+}
+
+.border-gray-800 {
+  --border-opacity: 1;
+  border-color: #2d3748;
+  border-color: rgba(45, 55, 72, var(--border-opacity));
+}
+
+.border-gray-900 {
+  --border-opacity: 1;
+  border-color: #1a202c;
+  border-color: rgba(26, 32, 44, var(--border-opacity));
+}
+
+.border-red-100 {
+  --border-opacity: 1;
+  border-color: #fff5f5;
+  border-color: rgba(255, 245, 245, var(--border-opacity));
+}
+
+.border-red-200 {
+  --border-opacity: 1;
+  border-color: #fed7d7;
+  border-color: rgba(254, 215, 215, var(--border-opacity));
+}
+
+.border-red-300 {
+  --border-opacity: 1;
+  border-color: #feb2b2;
+  border-color: rgba(254, 178, 178, var(--border-opacity));
+}
+
+.border-red-400 {
+  --border-opacity: 1;
+  border-color: #fc8181;
+  border-color: rgba(252, 129, 129, var(--border-opacity));
+}
+
+.border-red-500 {
+  --border-opacity: 1;
+  border-color: #f56565;
+  border-color: rgba(245, 101, 101, var(--border-opacity));
+}
+
+.border-red-600 {
+  --border-opacity: 1;
+  border-color: #e53e3e;
+  border-color: rgba(229, 62, 62, var(--border-opacity));
+}
+
+.border-red-700 {
+  --border-opacity: 1;
+  border-color: #c53030;
+  border-color: rgba(197, 48, 48, var(--border-opacity));
+}
+
+.border-red-800 {
+  --border-opacity: 1;
+  border-color: #9b2c2c;
+  border-color: rgba(155, 44, 44, var(--border-opacity));
+}
+
+.border-red-900 {
+  --border-opacity: 1;
+  border-color: #742a2a;
+  border-color: rgba(116, 42, 42, var(--border-opacity));
+}
+
+.border-orange-100 {
+  --border-opacity: 1;
+  border-color: #fffaf0;
+  border-color: rgba(255, 250, 240, var(--border-opacity));
+}
+
+.border-orange-200 {
+  --border-opacity: 1;
+  border-color: #feebc8;
+  border-color: rgba(254, 235, 200, var(--border-opacity));
+}
+
+.border-orange-300 {
+  --border-opacity: 1;
+  border-color: #fbd38d;
+  border-color: rgba(251, 211, 141, var(--border-opacity));
+}
+
+.border-orange-400 {
+  --border-opacity: 1;
+  border-color: #f6ad55;
+  border-color: rgba(246, 173, 85, var(--border-opacity));
+}
+
+.border-orange-500 {
+  --border-opacity: 1;
+  border-color: #ed8936;
+  border-color: rgba(237, 137, 54, var(--border-opacity));
+}
+
+.border-orange-600 {
+  --border-opacity: 1;
+  border-color: #dd6b20;
+  border-color: rgba(221, 107, 32, var(--border-opacity));
+}
+
+.border-orange-700 {
+  --border-opacity: 1;
+  border-color: #c05621;
+  border-color: rgba(192, 86, 33, var(--border-opacity));
+}
+
+.border-orange-800 {
+  --border-opacity: 1;
+  border-color: #9c4221;
+  border-color: rgba(156, 66, 33, var(--border-opacity));
+}
+
+.border-orange-900 {
+  --border-opacity: 1;
+  border-color: #7b341e;
+  border-color: rgba(123, 52, 30, var(--border-opacity));
+}
+
+.border-yellow-100 {
+  --border-opacity: 1;
+  border-color: #fffff0;
+  border-color: rgba(255, 255, 240, var(--border-opacity));
+}
+
+.border-yellow-200 {
+  --border-opacity: 1;
+  border-color: #fefcbf;
+  border-color: rgba(254, 252, 191, var(--border-opacity));
+}
+
+.border-yellow-300 {
+  --border-opacity: 1;
+  border-color: #faf089;
+  border-color: rgba(250, 240, 137, var(--border-opacity));
+}
+
+.border-yellow-400 {
+  --border-opacity: 1;
+  border-color: #f6e05e;
+  border-color: rgba(246, 224, 94, var(--border-opacity));
+}
+
+.border-yellow-500 {
+  --border-opacity: 1;
+  border-color: #ecc94b;
+  border-color: rgba(236, 201, 75, var(--border-opacity));
+}
+
+.border-yellow-600 {
+  --border-opacity: 1;
+  border-color: #d69e2e;
+  border-color: rgba(214, 158, 46, var(--border-opacity));
+}
+
+.border-yellow-700 {
+  --border-opacity: 1;
+  border-color: #b7791f;
+  border-color: rgba(183, 121, 31, var(--border-opacity));
+}
+
+.border-yellow-800 {
+  --border-opacity: 1;
+  border-color: #975a16;
+  border-color: rgba(151, 90, 22, var(--border-opacity));
+}
+
+.border-yellow-900 {
+  --border-opacity: 1;
+  border-color: #744210;
+  border-color: rgba(116, 66, 16, var(--border-opacity));
+}
+
+.border-green-100 {
+  --border-opacity: 1;
+  border-color: #f0fff4;
+  border-color: rgba(240, 255, 244, var(--border-opacity));
+}
+
+.border-green-200 {
+  --border-opacity: 1;
+  border-color: #c6f6d5;
+  border-color: rgba(198, 246, 213, var(--border-opacity));
+}
+
+.border-green-300 {
+  --border-opacity: 1;
+  border-color: #9ae6b4;
+  border-color: rgba(154, 230, 180, var(--border-opacity));
+}
+
+.border-green-400 {
+  --border-opacity: 1;
+  border-color: #68d391;
+  border-color: rgba(104, 211, 145, var(--border-opacity));
+}
+
+.border-green-500 {
+  --border-opacity: 1;
+  border-color: #48bb78;
+  border-color: rgba(72, 187, 120, var(--border-opacity));
+}
+
+.border-green-600 {
+  --border-opacity: 1;
+  border-color: #38a169;
+  border-color: rgba(56, 161, 105, var(--border-opacity));
+}
+
+.border-green-700 {
+  --border-opacity: 1;
+  border-color: #2f855a;
+  border-color: rgba(47, 133, 90, var(--border-opacity));
+}
+
+.border-green-800 {
+  --border-opacity: 1;
+  border-color: #276749;
+  border-color: rgba(39, 103, 73, var(--border-opacity));
+}
+
+.border-green-900 {
+  --border-opacity: 1;
+  border-color: #22543d;
+  border-color: rgba(34, 84, 61, var(--border-opacity));
+}
+
+.border-teal-100 {
+  --border-opacity: 1;
+  border-color: #e6fffa;
+  border-color: rgba(230, 255, 250, var(--border-opacity));
+}
+
+.border-teal-200 {
+  --border-opacity: 1;
+  border-color: #b2f5ea;
+  border-color: rgba(178, 245, 234, var(--border-opacity));
+}
+
+.border-teal-300 {
+  --border-opacity: 1;
+  border-color: #81e6d9;
+  border-color: rgba(129, 230, 217, var(--border-opacity));
+}
+
+.border-teal-400 {
+  --border-opacity: 1;
+  border-color: #4fd1c5;
+  border-color: rgba(79, 209, 197, var(--border-opacity));
+}
+
+.border-teal-500 {
+  --border-opacity: 1;
+  border-color: #38b2ac;
+  border-color: rgba(56, 178, 172, var(--border-opacity));
+}
+
+.border-teal-600 {
+  --border-opacity: 1;
+  border-color: #319795;
+  border-color: rgba(49, 151, 149, var(--border-opacity));
+}
+
+.border-teal-700 {
+  --border-opacity: 1;
+  border-color: #2c7a7b;
+  border-color: rgba(44, 122, 123, var(--border-opacity));
+}
+
+.border-teal-800 {
+  --border-opacity: 1;
+  border-color: #285e61;
+  border-color: rgba(40, 94, 97, var(--border-opacity));
+}
+
+.border-teal-900 {
+  --border-opacity: 1;
+  border-color: #234e52;
+  border-color: rgba(35, 78, 82, var(--border-opacity));
+}
+
+.border-blue-100 {
+  --border-opacity: 1;
+  border-color: #ebf8ff;
+  border-color: rgba(235, 248, 255, var(--border-opacity));
+}
+
+.border-blue-200 {
+  --border-opacity: 1;
+  border-color: #bee3f8;
+  border-color: rgba(190, 227, 248, var(--border-opacity));
+}
+
+.border-blue-300 {
+  --border-opacity: 1;
+  border-color: #90cdf4;
+  border-color: rgba(144, 205, 244, var(--border-opacity));
+}
+
+.border-blue-400 {
+  --border-opacity: 1;
+  border-color: #63b3ed;
+  border-color: rgba(99, 179, 237, var(--border-opacity));
+}
+
+.border-blue-500 {
+  --border-opacity: 1;
+  border-color: #4299e1;
+  border-color: rgba(66, 153, 225, var(--border-opacity));
+}
+
+.border-blue-600 {
+  --border-opacity: 1;
+  border-color: #3182ce;
+  border-color: rgba(49, 130, 206, var(--border-opacity));
+}
+
+.border-blue-700 {
+  --border-opacity: 1;
+  border-color: #2b6cb0;
+  border-color: rgba(43, 108, 176, var(--border-opacity));
+}
+
+.border-blue-800 {
+  --border-opacity: 1;
+  border-color: #2c5282;
+  border-color: rgba(44, 82, 130, var(--border-opacity));
+}
+
+.border-blue-900 {
+  --border-opacity: 1;
+  border-color: #2a4365;
+  border-color: rgba(42, 67, 101, var(--border-opacity));
+}
+
+.border-indigo-100 {
+  --border-opacity: 1;
+  border-color: #ebf4ff;
+  border-color: rgba(235, 244, 255, var(--border-opacity));
+}
+
+.border-indigo-200 {
+  --border-opacity: 1;
+  border-color: #c3dafe;
+  border-color: rgba(195, 218, 254, var(--border-opacity));
+}
+
+.border-indigo-300 {
+  --border-opacity: 1;
+  border-color: #a3bffa;
+  border-color: rgba(163, 191, 250, var(--border-opacity));
+}
+
+.border-indigo-400 {
+  --border-opacity: 1;
+  border-color: #7f9cf5;
+  border-color: rgba(127, 156, 245, var(--border-opacity));
+}
+
+.border-indigo-500 {
+  --border-opacity: 1;
+  border-color: #667eea;
+  border-color: rgba(102, 126, 234, var(--border-opacity));
+}
+
+.border-indigo-600 {
+  --border-opacity: 1;
+  border-color: #5a67d8;
+  border-color: rgba(90, 103, 216, var(--border-opacity));
+}
+
+.border-indigo-700 {
+  --border-opacity: 1;
+  border-color: #4c51bf;
+  border-color: rgba(76, 81, 191, var(--border-opacity));
+}
+
+.border-indigo-800 {
+  --border-opacity: 1;
+  border-color: #434190;
+  border-color: rgba(67, 65, 144, var(--border-opacity));
+}
+
+.border-indigo-900 {
+  --border-opacity: 1;
+  border-color: #3c366b;
+  border-color: rgba(60, 54, 107, var(--border-opacity));
+}
+
+.border-purple-100 {
+  --border-opacity: 1;
+  border-color: #faf5ff;
+  border-color: rgba(250, 245, 255, var(--border-opacity));
+}
+
+.border-purple-200 {
+  --border-opacity: 1;
+  border-color: #e9d8fd;
+  border-color: rgba(233, 216, 253, var(--border-opacity));
+}
+
+.border-purple-300 {
+  --border-opacity: 1;
+  border-color: #d6bcfa;
+  border-color: rgba(214, 188, 250, var(--border-opacity));
+}
+
+.border-purple-400 {
+  --border-opacity: 1;
+  border-color: #b794f4;
+  border-color: rgba(183, 148, 244, var(--border-opacity));
+}
+
+.border-purple-500 {
+  --border-opacity: 1;
+  border-color: #9f7aea;
+  border-color: rgba(159, 122, 234, var(--border-opacity));
+}
+
+.border-purple-600 {
+  --border-opacity: 1;
+  border-color: #805ad5;
+  border-color: rgba(128, 90, 213, var(--border-opacity));
+}
+
+.border-purple-700 {
+  --border-opacity: 1;
+  border-color: #6b46c1;
+  border-color: rgba(107, 70, 193, var(--border-opacity));
+}
+
+.border-purple-800 {
+  --border-opacity: 1;
+  border-color: #553c9a;
+  border-color: rgba(85, 60, 154, var(--border-opacity));
+}
+
+.border-purple-900 {
+  --border-opacity: 1;
+  border-color: #44337a;
+  border-color: rgba(68, 51, 122, var(--border-opacity));
+}
+
+.border-pink-100 {
+  --border-opacity: 1;
+  border-color: #fff5f7;
+  border-color: rgba(255, 245, 247, var(--border-opacity));
+}
+
+.border-pink-200 {
+  --border-opacity: 1;
+  border-color: #fed7e2;
+  border-color: rgba(254, 215, 226, var(--border-opacity));
+}
+
+.border-pink-300 {
+  --border-opacity: 1;
+  border-color: #fbb6ce;
+  border-color: rgba(251, 182, 206, var(--border-opacity));
+}
+
+.border-pink-400 {
+  --border-opacity: 1;
+  border-color: #f687b3;
+  border-color: rgba(246, 135, 179, var(--border-opacity));
+}
+
+.border-pink-500 {
+  --border-opacity: 1;
+  border-color: #ed64a6;
+  border-color: rgba(237, 100, 166, var(--border-opacity));
+}
+
+.border-pink-600 {
+  --border-opacity: 1;
+  border-color: #d53f8c;
+  border-color: rgba(213, 63, 140, var(--border-opacity));
+}
+
+.border-pink-700 {
+  --border-opacity: 1;
+  border-color: #b83280;
+  border-color: rgba(184, 50, 128, var(--border-opacity));
+}
+
+.border-pink-800 {
+  --border-opacity: 1;
+  border-color: #97266d;
+  border-color: rgba(151, 38, 109, var(--border-opacity));
+}
+
+.border-pink-900 {
+  --border-opacity: 1;
+  border-color: #702459;
+  border-color: rgba(112, 36, 89, var(--border-opacity));
+}
+
+.hover\:border-transparent:hover {
+  border-color: transparent;
+}
+
+.hover\:border-current:hover {
+  border-color: currentColor;
+}
+
+.hover\:border-black:hover {
+  --border-opacity: 1;
+  border-color: #000;
+  border-color: rgba(0, 0, 0, var(--border-opacity));
+}
+
+.hover\:border-white:hover {
+  --border-opacity: 1;
+  border-color: #fff;
+  border-color: rgba(255, 255, 255, var(--border-opacity));
+}
+
+.hover\:border-gray-100:hover {
+  --border-opacity: 1;
+  border-color: #f7fafc;
+  border-color: rgba(247, 250, 252, var(--border-opacity));
+}
+
+.hover\:border-gray-200:hover {
+  --border-opacity: 1;
+  border-color: #edf2f7;
+  border-color: rgba(237, 242, 247, var(--border-opacity));
+}
+
+.hover\:border-gray-300:hover {
+  --border-opacity: 1;
+  border-color: #e2e8f0;
+  border-color: rgba(226, 232, 240, var(--border-opacity));
+}
+
+.hover\:border-gray-400:hover {
+  --border-opacity: 1;
+  border-color: #cbd5e0;
+  border-color: rgba(203, 213, 224, var(--border-opacity));
+}
+
+.hover\:border-gray-500:hover {
+  --border-opacity: 1;
+  border-color: #a0aec0;
+  border-color: rgba(160, 174, 192, var(--border-opacity));
+}
+
+.hover\:border-gray-600:hover {
+  --border-opacity: 1;
+  border-color: #718096;
+  border-color: rgba(113, 128, 150, var(--border-opacity));
+}
+
+.hover\:border-gray-700:hover {
+  --border-opacity: 1;
+  border-color: #4a5568;
+  border-color: rgba(74, 85, 104, var(--border-opacity));
+}
+
+.hover\:border-gray-800:hover {
+  --border-opacity: 1;
+  border-color: #2d3748;
+  border-color: rgba(45, 55, 72, var(--border-opacity));
+}
+
+.hover\:border-gray-900:hover {
+  --border-opacity: 1;
+  border-color: #1a202c;
+  border-color: rgba(26, 32, 44, var(--border-opacity));
+}
+
+.hover\:border-red-100:hover {
+  --border-opacity: 1;
+  border-color: #fff5f5;
+  border-color: rgba(255, 245, 245, var(--border-opacity));
+}
+
+.hover\:border-red-200:hover {
+  --border-opacity: 1;
+  border-color: #fed7d7;
+  border-color: rgba(254, 215, 215, var(--border-opacity));
+}
+
+.hover\:border-red-300:hover {
+  --border-opacity: 1;
+  border-color: #feb2b2;
+  border-color: rgba(254, 178, 178, var(--border-opacity));
+}
+
+.hover\:border-red-400:hover {
+  --border-opacity: 1;
+  border-color: #fc8181;
+  border-color: rgba(252, 129, 129, var(--border-opacity));
+}
+
+.hover\:border-red-500:hover {
+  --border-opacity: 1;
+  border-color: #f56565;
+  border-color: rgba(245, 101, 101, var(--border-opacity));
+}
+
+.hover\:border-red-600:hover {
+  --border-opacity: 1;
+  border-color: #e53e3e;
+  border-color: rgba(229, 62, 62, var(--border-opacity));
+}
+
+.hover\:border-red-700:hover {
+  --border-opacity: 1;
+  border-color: #c53030;
+  border-color: rgba(197, 48, 48, var(--border-opacity));
+}
+
+.hover\:border-red-800:hover {
+  --border-opacity: 1;
+  border-color: #9b2c2c;
+  border-color: rgba(155, 44, 44, var(--border-opacity));
+}
+
+.hover\:border-red-900:hover {
+  --border-opacity: 1;
+  border-color: #742a2a;
+  border-color: rgba(116, 42, 42, var(--border-opacity));
+}
+
+.hover\:border-orange-100:hover {
+  --border-opacity: 1;
+  border-color: #fffaf0;
+  border-color: rgba(255, 250, 240, var(--border-opacity));
+}
+
+.hover\:border-orange-200:hover {
+  --border-opacity: 1;
+  border-color: #feebc8;
+  border-color: rgba(254, 235, 200, var(--border-opacity));
+}
+
+.hover\:border-orange-300:hover {
+  --border-opacity: 1;
+  border-color: #fbd38d;
+  border-color: rgba(251, 211, 141, var(--border-opacity));
+}
+
+.hover\:border-orange-400:hover {
+  --border-opacity: 1;
+  border-color: #f6ad55;
+  border-color: rgba(246, 173, 85, var(--border-opacity));
+}
+
+.hover\:border-orange-500:hover {
+  --border-opacity: 1;
+  border-color: #ed8936;
+  border-color: rgba(237, 137, 54, var(--border-opacity));
+}
+
+.hover\:border-orange-600:hover {
+  --border-opacity: 1;
+  border-color: #dd6b20;
+  border-color: rgba(221, 107, 32, var(--border-opacity));
+}
+
+.hover\:border-orange-700:hover {
+  --border-opacity: 1;
+  border-color: #c05621;
+  border-color: rgba(192, 86, 33, var(--border-opacity));
+}
+
+.hover\:border-orange-800:hover {
+  --border-opacity: 1;
+  border-color: #9c4221;
+  border-color: rgba(156, 66, 33, var(--border-opacity));
+}
+
+.hover\:border-orange-900:hover {
+  --border-opacity: 1;
+  border-color: #7b341e;
+  border-color: rgba(123, 52, 30, var(--border-opacity));
+}
+
+.hover\:border-yellow-100:hover {
+  --border-opacity: 1;
+  border-color: #fffff0;
+  border-color: rgba(255, 255, 240, var(--border-opacity));
+}
+
+.hover\:border-yellow-200:hover {
+  --border-opacity: 1;
+  border-color: #fefcbf;
+  border-color: rgba(254, 252, 191, var(--border-opacity));
+}
+
+.hover\:border-yellow-300:hover {
+  --border-opacity: 1;
+  border-color: #faf089;
+  border-color: rgba(250, 240, 137, var(--border-opacity));
+}
+
+.hover\:border-yellow-400:hover {
+  --border-opacity: 1;
+  border-color: #f6e05e;
+  border-color: rgba(246, 224, 94, var(--border-opacity));
+}
+
+.hover\:border-yellow-500:hover {
+  --border-opacity: 1;
+  border-color: #ecc94b;
+  border-color: rgba(236, 201, 75, var(--border-opacity));
+}
+
+.hover\:border-yellow-600:hover {
+  --border-opacity: 1;
+  border-color: #d69e2e;
+  border-color: rgba(214, 158, 46, var(--border-opacity));
+}
+
+.hover\:border-yellow-700:hover {
+  --border-opacity: 1;
+  border-color: #b7791f;
+  border-color: rgba(183, 121, 31, var(--border-opacity));
+}
+
+.hover\:border-yellow-800:hover {
+  --border-opacity: 1;
+  border-color: #975a16;
+  border-color: rgba(151, 90, 22, var(--border-opacity));
+}
+
+.hover\:border-yellow-900:hover {
+  --border-opacity: 1;
+  border-color: #744210;
+  border-color: rgba(116, 66, 16, var(--border-opacity));
+}
+
+.hover\:border-green-100:hover {
+  --border-opacity: 1;
+  border-color: #f0fff4;
+  border-color: rgba(240, 255, 244, var(--border-opacity));
+}
+
+.hover\:border-green-200:hover {
+  --border-opacity: 1;
+  border-color: #c6f6d5;
+  border-color: rgba(198, 246, 213, var(--border-opacity));
+}
+
+.hover\:border-green-300:hover {
+  --border-opacity: 1;
+  border-color: #9ae6b4;
+  border-color: rgba(154, 230, 180, var(--border-opacity));
+}
+
+.hover\:border-green-400:hover {
+  --border-opacity: 1;
+  border-color: #68d391;
+  border-color: rgba(104, 211, 145, var(--border-opacity));
+}
+
+.hover\:border-green-500:hover {
+  --border-opacity: 1;
+  border-color: #48bb78;
+  border-color: rgba(72, 187, 120, var(--border-opacity));
+}
+
+.hover\:border-green-600:hover {
+  --border-opacity: 1;
+  border-color: #38a169;
+  border-color: rgba(56, 161, 105, var(--border-opacity));
+}
+
+.hover\:border-green-700:hover {
+  --border-opacity: 1;
+  border-color: #2f855a;
+  border-color: rgba(47, 133, 90, var(--border-opacity));
+}
+
+.hover\:border-green-800:hover {
+  --border-opacity: 1;
+  border-color: #276749;
+  border-color: rgba(39, 103, 73, var(--border-opacity));
+}
+
+.hover\:border-green-900:hover {
+  --border-opacity: 1;
+  border-color: #22543d;
+  border-color: rgba(34, 84, 61, var(--border-opacity));
+}
+
+.hover\:border-teal-100:hover {
+  --border-opacity: 1;
+  border-color: #e6fffa;
+  border-color: rgba(230, 255, 250, var(--border-opacity));
+}
+
+.hover\:border-teal-200:hover {
+  --border-opacity: 1;
+  border-color: #b2f5ea;
+  border-color: rgba(178, 245, 234, var(--border-opacity));
+}
+
+.hover\:border-teal-300:hover {
+  --border-opacity: 1;
+  border-color: #81e6d9;
+  border-color: rgba(129, 230, 217, var(--border-opacity));
+}
+
+.hover\:border-teal-400:hover {
+  --border-opacity: 1;
+  border-color: #4fd1c5;
+  border-color: rgba(79, 209, 197, var(--border-opacity));
+}
+
+.hover\:border-teal-500:hover {
+  --border-opacity: 1;
+  border-color: #38b2ac;
+  border-color: rgba(56, 178, 172, var(--border-opacity));
+}
+
+.hover\:border-teal-600:hover {
+  --border-opacity: 1;
+  border-color: #319795;
+  border-color: rgba(49, 151, 149, var(--border-opacity));
+}
+
+.hover\:border-teal-700:hover {
+  --border-opacity: 1;
+  border-color: #2c7a7b;
+  border-color: rgba(44, 122, 123, var(--border-opacity));
+}
+
+.hover\:border-teal-800:hover {
+  --border-opacity: 1;
+  border-color: #285e61;
+  border-color: rgba(40, 94, 97, var(--border-opacity));
+}
+
+.hover\:border-teal-900:hover {
+  --border-opacity: 1;
+  border-color: #234e52;
+  border-color: rgba(35, 78, 82, var(--border-opacity));
+}
+
+.hover\:border-blue-100:hover {
+  --border-opacity: 1;
+  border-color: #ebf8ff;
+  border-color: rgba(235, 248, 255, var(--border-opacity));
+}
+
+.hover\:border-blue-200:hover {
+  --border-opacity: 1;
+  border-color: #bee3f8;
+  border-color: rgba(190, 227, 248, var(--border-opacity));
+}
+
+.hover\:border-blue-300:hover {
+  --border-opacity: 1;
+  border-color: #90cdf4;
+  border-color: rgba(144, 205, 244, var(--border-opacity));
+}
+
+.hover\:border-blue-400:hover {
+  --border-opacity: 1;
+  border-color: #63b3ed;
+  border-color: rgba(99, 179, 237, var(--border-opacity));
+}
+
+.hover\:border-blue-500:hover {
+  --border-opacity: 1;
+  border-color: #4299e1;
+  border-color: rgba(66, 153, 225, var(--border-opacity));
+}
+
+.hover\:border-blue-600:hover {
+  --border-opacity: 1;
+  border-color: #3182ce;
+  border-color: rgba(49, 130, 206, var(--border-opacity));
+}
+
+.hover\:border-blue-700:hover {
+  --border-opacity: 1;
+  border-color: #2b6cb0;
+  border-color: rgba(43, 108, 176, var(--border-opacity));
+}
+
+.hover\:border-blue-800:hover {
+  --border-opacity: 1;
+  border-color: #2c5282;
+  border-color: rgba(44, 82, 130, var(--border-opacity));
+}
+
+.hover\:border-blue-900:hover {
+  --border-opacity: 1;
+  border-color: #2a4365;
+  border-color: rgba(42, 67, 101, var(--border-opacity));
+}
+
+.hover\:border-indigo-100:hover {
+  --border-opacity: 1;
+  border-color: #ebf4ff;
+  border-color: rgba(235, 244, 255, var(--border-opacity));
+}
+
+.hover\:border-indigo-200:hover {
+  --border-opacity: 1;
+  border-color: #c3dafe;
+  border-color: rgba(195, 218, 254, var(--border-opacity));
+}
+
+.hover\:border-indigo-300:hover {
+  --border-opacity: 1;
+  border-color: #a3bffa;
+  border-color: rgba(163, 191, 250, var(--border-opacity));
+}
+
+.hover\:border-indigo-400:hover {
+  --border-opacity: 1;
+  border-color: #7f9cf5;
+  border-color: rgba(127, 156, 245, var(--border-opacity));
+}
+
+.hover\:border-indigo-500:hover {
+  --border-opacity: 1;
+  border-color: #667eea;
+  border-color: rgba(102, 126, 234, var(--border-opacity));
+}
+
+.hover\:border-indigo-600:hover {
+  --border-opacity: 1;
+  border-color: #5a67d8;
+  border-color: rgba(90, 103, 216, var(--border-opacity));
+}
+
+.hover\:border-indigo-700:hover {
+  --border-opacity: 1;
+  border-color: #4c51bf;
+  border-color: rgba(76, 81, 191, var(--border-opacity));
+}
+
+.hover\:border-indigo-800:hover {
+  --border-opacity: 1;
+  border-color: #434190;
+  border-color: rgba(67, 65, 144, var(--border-opacity));
+}
+
+.hover\:border-indigo-900:hover {
+  --border-opacity: 1;
+  border-color: #3c366b;
+  border-color: rgba(60, 54, 107, var(--border-opacity));
+}
+
+.hover\:border-purple-100:hover {
+  --border-opacity: 1;
+  border-color: #faf5ff;
+  border-color: rgba(250, 245, 255, var(--border-opacity));
+}
+
+.hover\:border-purple-200:hover {
+  --border-opacity: 1;
+  border-color: #e9d8fd;
+  border-color: rgba(233, 216, 253, var(--border-opacity));
+}
+
+.hover\:border-purple-300:hover {
+  --border-opacity: 1;
+  border-color: #d6bcfa;
+  border-color: rgba(214, 188, 250, var(--border-opacity));
+}
+
+.hover\:border-purple-400:hover {
+  --border-opacity: 1;
+  border-color: #b794f4;
+  border-color: rgba(183, 148, 244, var(--border-opacity));
+}
+
+.hover\:border-purple-500:hover {
+  --border-opacity: 1;
+  border-color: #9f7aea;
+  border-color: rgba(159, 122, 234, var(--border-opacity));
+}
+
+.hover\:border-purple-600:hover {
+  --border-opacity: 1;
+  border-color: #805ad5;
+  border-color: rgba(128, 90, 213, var(--border-opacity));
+}
+
+.hover\:border-purple-700:hover {
+  --border-opacity: 1;
+  border-color: #6b46c1;
+  border-color: rgba(107, 70, 193, var(--border-opacity));
+}
+
+.hover\:border-purple-800:hover {
+  --border-opacity: 1;
+  border-color: #553c9a;
+  border-color: rgba(85, 60, 154, var(--border-opacity));
+}
+
+.hover\:border-purple-900:hover {
+  --border-opacity: 1;
+  border-color: #44337a;
+  border-color: rgba(68, 51, 122, var(--border-opacity));
+}
+
+.hover\:border-pink-100:hover {
+  --border-opacity: 1;
+  border-color: #fff5f7;
+  border-color: rgba(255, 245, 247, var(--border-opacity));
+}
+
+.hover\:border-pink-200:hover {
+  --border-opacity: 1;
+  border-color: #fed7e2;
+  border-color: rgba(254, 215, 226, var(--border-opacity));
+}
+
+.hover\:border-pink-300:hover {
+  --border-opacity: 1;
+  border-color: #fbb6ce;
+  border-color: rgba(251, 182, 206, var(--border-opacity));
+}
+
+.hover\:border-pink-400:hover {
+  --border-opacity: 1;
+  border-color: #f687b3;
+  border-color: rgba(246, 135, 179, var(--border-opacity));
+}
+
+.hover\:border-pink-500:hover {
+  --border-opacity: 1;
+  border-color: #ed64a6;
+  border-color: rgba(237, 100, 166, var(--border-opacity));
+}
+
+.hover\:border-pink-600:hover {
+  --border-opacity: 1;
+  border-color: #d53f8c;
+  border-color: rgba(213, 63, 140, var(--border-opacity));
+}
+
+.hover\:border-pink-700:hover {
+  --border-opacity: 1;
+  border-color: #b83280;
+  border-color: rgba(184, 50, 128, var(--border-opacity));
+}
+
+.hover\:border-pink-800:hover {
+  --border-opacity: 1;
+  border-color: #97266d;
+  border-color: rgba(151, 38, 109, var(--border-opacity));
+}
+
+.hover\:border-pink-900:hover {
+  --border-opacity: 1;
+  border-color: #702459;
+  border-color: rgba(112, 36, 89, var(--border-opacity));
+}
+
+.focus\:border-transparent:focus {
+  border-color: transparent;
+}
+
+.focus\:border-current:focus {
+  border-color: currentColor;
+}
+
+.focus\:border-black:focus {
+  --border-opacity: 1;
+  border-color: #000;
+  border-color: rgba(0, 0, 0, var(--border-opacity));
+}
+
+.focus\:border-white:focus {
+  --border-opacity: 1;
+  border-color: #fff;
+  border-color: rgba(255, 255, 255, var(--border-opacity));
+}
+
+.focus\:border-gray-100:focus {
+  --border-opacity: 1;
+  border-color: #f7fafc;
+  border-color: rgba(247, 250, 252, var(--border-opacity));
+}
+
+.focus\:border-gray-200:focus {
+  --border-opacity: 1;
+  border-color: #edf2f7;
+  border-color: rgba(237, 242, 247, var(--border-opacity));
+}
+
+.focus\:border-gray-300:focus {
+  --border-opacity: 1;
+  border-color: #e2e8f0;
+  border-color: rgba(226, 232, 240, var(--border-opacity));
+}
+
+.focus\:border-gray-400:focus {
+  --border-opacity: 1;
+  border-color: #cbd5e0;
+  border-color: rgba(203, 213, 224, var(--border-opacity));
+}
+
+.focus\:border-gray-500:focus {
+  --border-opacity: 1;
+  border-color: #a0aec0;
+  border-color: rgba(160, 174, 192, var(--border-opacity));
+}
+
+.focus\:border-gray-600:focus {
+  --border-opacity: 1;
+  border-color: #718096;
+  border-color: rgba(113, 128, 150, var(--border-opacity));
+}
+
+.focus\:border-gray-700:focus {
+  --border-opacity: 1;
+  border-color: #4a5568;
+  border-color: rgba(74, 85, 104, var(--border-opacity));
+}
+
+.focus\:border-gray-800:focus {
+  --border-opacity: 1;
+  border-color: #2d3748;
+  border-color: rgba(45, 55, 72, var(--border-opacity));
+}
+
+.focus\:border-gray-900:focus {
+  --border-opacity: 1;
+  border-color: #1a202c;
+  border-color: rgba(26, 32, 44, var(--border-opacity));
+}
+
+.focus\:border-red-100:focus {
+  --border-opacity: 1;
+  border-color: #fff5f5;
+  border-color: rgba(255, 245, 245, var(--border-opacity));
+}
+
+.focus\:border-red-200:focus {
+  --border-opacity: 1;
+  border-color: #fed7d7;
+  border-color: rgba(254, 215, 215, var(--border-opacity));
+}
+
+.focus\:border-red-300:focus {
+  --border-opacity: 1;
+  border-color: #feb2b2;
+  border-color: rgba(254, 178, 178, var(--border-opacity));
+}
+
+.focus\:border-red-400:focus {
+  --border-opacity: 1;
+  border-color: #fc8181;
+  border-color: rgba(252, 129, 129, var(--border-opacity));
+}
+
+.focus\:border-red-500:focus {
+  --border-opacity: 1;
+  border-color: #f56565;
+  border-color: rgba(245, 101, 101, var(--border-opacity));
+}
+
+.focus\:border-red-600:focus {
+  --border-opacity: 1;
+  border-color: #e53e3e;
+  border-color: rgba(229, 62, 62, var(--border-opacity));
+}
+
+.focus\:border-red-700:focus {
+  --border-opacity: 1;
+  border-color: #c53030;
+  border-color: rgba(197, 48, 48, var(--border-opacity));
+}
+
+.focus\:border-red-800:focus {
+  --border-opacity: 1;
+  border-color: #9b2c2c;
+  border-color: rgba(155, 44, 44, var(--border-opacity));
+}
+
+.focus\:border-red-900:focus {
+  --border-opacity: 1;
+  border-color: #742a2a;
+  border-color: rgba(116, 42, 42, var(--border-opacity));
+}
+
+.focus\:border-orange-100:focus {
+  --border-opacity: 1;
+  border-color: #fffaf0;
+  border-color: rgba(255, 250, 240, var(--border-opacity));
+}
+
+.focus\:border-orange-200:focus {
+  --border-opacity: 1;
+  border-color: #feebc8;
+  border-color: rgba(254, 235, 200, var(--border-opacity));
+}
+
+.focus\:border-orange-300:focus {
+  --border-opacity: 1;
+  border-color: #fbd38d;
+  border-color: rgba(251, 211, 141, var(--border-opacity));
+}
+
+.focus\:border-orange-400:focus {
+  --border-opacity: 1;
+  border-color: #f6ad55;
+  border-color: rgba(246, 173, 85, var(--border-opacity));
+}
+
+.focus\:border-orange-500:focus {
+  --border-opacity: 1;
+  border-color: #ed8936;
+  border-color: rgba(237, 137, 54, var(--border-opacity));
+}
+
+.focus\:border-orange-600:focus {
+  --border-opacity: 1;
+  border-color: #dd6b20;
+  border-color: rgba(221, 107, 32, var(--border-opacity));
+}
+
+.focus\:border-orange-700:focus {
+  --border-opacity: 1;
+  border-color: #c05621;
+  border-color: rgba(192, 86, 33, var(--border-opacity));
+}
+
+.focus\:border-orange-800:focus {
+  --border-opacity: 1;
+  border-color: #9c4221;
+  border-color: rgba(156, 66, 33, var(--border-opacity));
+}
+
+.focus\:border-orange-900:focus {
+  --border-opacity: 1;
+  border-color: #7b341e;
+  border-color: rgba(123, 52, 30, var(--border-opacity));
+}
+
+.focus\:border-yellow-100:focus {
+  --border-opacity: 1;
+  border-color: #fffff0;
+  border-color: rgba(255, 255, 240, var(--border-opacity));
+}
+
+.focus\:border-yellow-200:focus {
+  --border-opacity: 1;
+  border-color: #fefcbf;
+  border-color: rgba(254, 252, 191, var(--border-opacity));
+}
+
+.focus\:border-yellow-300:focus {
+  --border-opacity: 1;
+  border-color: #faf089;
+  border-color: rgba(250, 240, 137, var(--border-opacity));
+}
+
+.focus\:border-yellow-400:focus {
+  --border-opacity: 1;
+  border-color: #f6e05e;
+  border-color: rgba(246, 224, 94, var(--border-opacity));
+}
+
+.focus\:border-yellow-500:focus {
+  --border-opacity: 1;
+  border-color: #ecc94b;
+  border-color: rgba(236, 201, 75, var(--border-opacity));
+}
+
+.focus\:border-yellow-600:focus {
+  --border-opacity: 1;
+  border-color: #d69e2e;
+  border-color: rgba(214, 158, 46, var(--border-opacity));
+}
+
+.focus\:border-yellow-700:focus {
+  --border-opacity: 1;
+  border-color: #b7791f;
+  border-color: rgba(183, 121, 31, var(--border-opacity));
+}
+
+.focus\:border-yellow-800:focus {
+  --border-opacity: 1;
+  border-color: #975a16;
+  border-color: rgba(151, 90, 22, var(--border-opacity));
+}
+
+.focus\:border-yellow-900:focus {
+  --border-opacity: 1;
+  border-color: #744210;
+  border-color: rgba(116, 66, 16, var(--border-opacity));
+}
+
+.focus\:border-green-100:focus {
+  --border-opacity: 1;
+  border-color: #f0fff4;
+  border-color: rgba(240, 255, 244, var(--border-opacity));
+}
+
+.focus\:border-green-200:focus {
+  --border-opacity: 1;
+  border-color: #c6f6d5;
+  border-color: rgba(198, 246, 213, var(--border-opacity));
+}
+
+.focus\:border-green-300:focus {
+  --border-opacity: 1;
+  border-color: #9ae6b4;
+  border-color: rgba(154, 230, 180, var(--border-opacity));
+}
+
+.focus\:border-green-400:focus {
+  --border-opacity: 1;
+  border-color: #68d391;
+  border-color: rgba(104, 211, 145, var(--border-opacity));
+}
+
+.focus\:border-green-500:focus {
+  --border-opacity: 1;
+  border-color: #48bb78;
+  border-color: rgba(72, 187, 120, var(--border-opacity));
+}
+
+.focus\:border-green-600:focus {
+  --border-opacity: 1;
+  border-color: #38a169;
+  border-color: rgba(56, 161, 105, var(--border-opacity));
+}
+
+.focus\:border-green-700:focus {
+  --border-opacity: 1;
+  border-color: #2f855a;
+  border-color: rgba(47, 133, 90, var(--border-opacity));
+}
+
+.focus\:border-green-800:focus {
+  --border-opacity: 1;
+  border-color: #276749;
+  border-color: rgba(39, 103, 73, var(--border-opacity));
+}
+
+.focus\:border-green-900:focus {
+  --border-opacity: 1;
+  border-color: #22543d;
+  border-color: rgba(34, 84, 61, var(--border-opacity));
+}
+
+.focus\:border-teal-100:focus {
+  --border-opacity: 1;
+  border-color: #e6fffa;
+  border-color: rgba(230, 255, 250, var(--border-opacity));
+}
+
+.focus\:border-teal-200:focus {
+  --border-opacity: 1;
+  border-color: #b2f5ea;
+  border-color: rgba(178, 245, 234, var(--border-opacity));
+}
+
+.focus\:border-teal-300:focus {
+  --border-opacity: 1;
+  border-color: #81e6d9;
+  border-color: rgba(129, 230, 217, var(--border-opacity));
+}
+
+.focus\:border-teal-400:focus {
+  --border-opacity: 1;
+  border-color: #4fd1c5;
+  border-color: rgba(79, 209, 197, var(--border-opacity));
+}
+
+.focus\:border-teal-500:focus {
+  --border-opacity: 1;
+  border-color: #38b2ac;
+  border-color: rgba(56, 178, 172, var(--border-opacity));
+}
+
+.focus\:border-teal-600:focus {
+  --border-opacity: 1;
+  border-color: #319795;
+  border-color: rgba(49, 151, 149, var(--border-opacity));
+}
+
+.focus\:border-teal-700:focus {
+  --border-opacity: 1;
+  border-color: #2c7a7b;
+  border-color: rgba(44, 122, 123, var(--border-opacity));
+}
+
+.focus\:border-teal-800:focus {
+  --border-opacity: 1;
+  border-color: #285e61;
+  border-color: rgba(40, 94, 97, var(--border-opacity));
+}
+
+.focus\:border-teal-900:focus {
+  --border-opacity: 1;
+  border-color: #234e52;
+  border-color: rgba(35, 78, 82, var(--border-opacity));
+}
+
+.focus\:border-blue-100:focus {
+  --border-opacity: 1;
+  border-color: #ebf8ff;
+  border-color: rgba(235, 248, 255, var(--border-opacity));
+}
+
+.focus\:border-blue-200:focus {
+  --border-opacity: 1;
+  border-color: #bee3f8;
+  border-color: rgba(190, 227, 248, var(--border-opacity));
+}
+
+.focus\:border-blue-300:focus {
+  --border-opacity: 1;
+  border-color: #90cdf4;
+  border-color: rgba(144, 205, 244, var(--border-opacity));
+}
+
+.focus\:border-blue-400:focus {
+  --border-opacity: 1;
+  border-color: #63b3ed;
+  border-color: rgba(99, 179, 237, var(--border-opacity));
+}
+
+.focus\:border-blue-500:focus {
+  --border-opacity: 1;
+  border-color: #4299e1;
+  border-color: rgba(66, 153, 225, var(--border-opacity));
+}
+
+.focus\:border-blue-600:focus {
+  --border-opacity: 1;
+  border-color: #3182ce;
+  border-color: rgba(49, 130, 206, var(--border-opacity));
+}
+
+.focus\:border-blue-700:focus {
+  --border-opacity: 1;
+  border-color: #2b6cb0;
+  border-color: rgba(43, 108, 176, var(--border-opacity));
+}
+
+.focus\:border-blue-800:focus {
+  --border-opacity: 1;
+  border-color: #2c5282;
+  border-color: rgba(44, 82, 130, var(--border-opacity));
+}
+
+.focus\:border-blue-900:focus {
+  --border-opacity: 1;
+  border-color: #2a4365;
+  border-color: rgba(42, 67, 101, var(--border-opacity));
+}
+
+.focus\:border-indigo-100:focus {
+  --border-opacity: 1;
+  border-color: #ebf4ff;
+  border-color: rgba(235, 244, 255, var(--border-opacity));
+}
+
+.focus\:border-indigo-200:focus {
+  --border-opacity: 1;
+  border-color: #c3dafe;
+  border-color: rgba(195, 218, 254, var(--border-opacity));
+}
+
+.focus\:border-indigo-300:focus {
+  --border-opacity: 1;
+  border-color: #a3bffa;
+  border-color: rgba(163, 191, 250, var(--border-opacity));
+}
+
+.focus\:border-indigo-400:focus {
+  --border-opacity: 1;
+  border-color: #7f9cf5;
+  border-color: rgba(127, 156, 245, var(--border-opacity));
+}
+
+.focus\:border-indigo-500:focus {
+  --border-opacity: 1;
+  border-color: #667eea;
+  border-color: rgba(102, 126, 234, var(--border-opacity));
+}
+
+.focus\:border-indigo-600:focus {
+  --border-opacity: 1;
+  border-color: #5a67d8;
+  border-color: rgba(90, 103, 216, var(--border-opacity));
+}
+
+.focus\:border-indigo-700:focus {
+  --border-opacity: 1;
+  border-color: #4c51bf;
+  border-color: rgba(76, 81, 191, var(--border-opacity));
+}
+
+.focus\:border-indigo-800:focus {
+  --border-opacity: 1;
+  border-color: #434190;
+  border-color: rgba(67, 65, 144, var(--border-opacity));
+}
+
+.focus\:border-indigo-900:focus {
+  --border-opacity: 1;
+  border-color: #3c366b;
+  border-color: rgba(60, 54, 107, var(--border-opacity));
+}
+
+.focus\:border-purple-100:focus {
+  --border-opacity: 1;
+  border-color: #faf5ff;
+  border-color: rgba(250, 245, 255, var(--border-opacity));
+}
+
+.focus\:border-purple-200:focus {
+  --border-opacity: 1;
+  border-color: #e9d8fd;
+  border-color: rgba(233, 216, 253, var(--border-opacity));
+}
+
+.focus\:border-purple-300:focus {
+  --border-opacity: 1;
+  border-color: #d6bcfa;
+  border-color: rgba(214, 188, 250, var(--border-opacity));
+}
+
+.focus\:border-purple-400:focus {
+  --border-opacity: 1;
+  border-color: #b794f4;
+  border-color: rgba(183, 148, 244, var(--border-opacity));
+}
+
+.focus\:border-purple-500:focus {
+  --border-opacity: 1;
+  border-color: #9f7aea;
+  border-color: rgba(159, 122, 234, var(--border-opacity));
+}
+
+.focus\:border-purple-600:focus {
+  --border-opacity: 1;
+  border-color: #805ad5;
+  border-color: rgba(128, 90, 213, var(--border-opacity));
+}
+
+.focus\:border-purple-700:focus {
+  --border-opacity: 1;
+  border-color: #6b46c1;
+  border-color: rgba(107, 70, 193, var(--border-opacity));
+}
+
+.focus\:border-purple-800:focus {
+  --border-opacity: 1;
+  border-color: #553c9a;
+  border-color: rgba(85, 60, 154, var(--border-opacity));
+}
+
+.focus\:border-purple-900:focus {
+  --border-opacity: 1;
+  border-color: #44337a;
+  border-color: rgba(68, 51, 122, var(--border-opacity));
+}
+
+.focus\:border-pink-100:focus {
+  --border-opacity: 1;
+  border-color: #fff5f7;
+  border-color: rgba(255, 245, 247, var(--border-opacity));
+}
+
+.focus\:border-pink-200:focus {
+  --border-opacity: 1;
+  border-color: #fed7e2;
+  border-color: rgba(254, 215, 226, var(--border-opacity));
+}
+
+.focus\:border-pink-300:focus {
+  --border-opacity: 1;
+  border-color: #fbb6ce;
+  border-color: rgba(251, 182, 206, var(--border-opacity));
+}
+
+.focus\:border-pink-400:focus {
+  --border-opacity: 1;
+  border-color: #f687b3;
+  border-color: rgba(246, 135, 179, var(--border-opacity));
+}
+
+.focus\:border-pink-500:focus {
+  --border-opacity: 1;
+  border-color: #ed64a6;
+  border-color: rgba(237, 100, 166, var(--border-opacity));
+}
+
+.focus\:border-pink-600:focus {
+  --border-opacity: 1;
+  border-color: #d53f8c;
+  border-color: rgba(213, 63, 140, var(--border-opacity));
+}
+
+.focus\:border-pink-700:focus {
+  --border-opacity: 1;
+  border-color: #b83280;
+  border-color: rgba(184, 50, 128, var(--border-opacity));
+}
+
+.focus\:border-pink-800:focus {
+  --border-opacity: 1;
+  border-color: #97266d;
+  border-color: rgba(151, 38, 109, var(--border-opacity));
+}
+
+.focus\:border-pink-900:focus {
+  --border-opacity: 1;
+  border-color: #702459;
+  border-color: rgba(112, 36, 89, var(--border-opacity));
+}
+
+.border-opacity-0 {
+  --border-opacity: 0;
+}
+
+.border-opacity-25 {
+  --border-opacity: 0.25;
+}
+
+.border-opacity-50 {
+  --border-opacity: 0.5;
+}
+
+.border-opacity-75 {
+  --border-opacity: 0.75;
+}
+
+.border-opacity-100 {
+  --border-opacity: 1;
+}
+
+.hover\:border-opacity-0:hover {
+  --border-opacity: 0;
+}
+
+.hover\:border-opacity-25:hover {
+  --border-opacity: 0.25;
+}
+
+.hover\:border-opacity-50:hover {
+  --border-opacity: 0.5;
+}
+
+.hover\:border-opacity-75:hover {
+  --border-opacity: 0.75;
+}
+
+.hover\:border-opacity-100:hover {
+  --border-opacity: 1;
+}
+
+.focus\:border-opacity-0:focus {
+  --border-opacity: 0;
+}
+
+.focus\:border-opacity-25:focus {
+  --border-opacity: 0.25;
+}
+
+.focus\:border-opacity-50:focus {
+  --border-opacity: 0.5;
+}
+
+.focus\:border-opacity-75:focus {
+  --border-opacity: 0.75;
+}
+
+.focus\:border-opacity-100:focus {
+  --border-opacity: 1;
+}
+
+.rounded-none {
+  border-radius: 0;
+}
+
+.rounded-sm {
+  border-radius: 0.125rem;
+}
+
+.rounded {
+  border-radius: 0.25rem;
+}
+
+.rounded-md {
+  border-radius: 0.375rem;
+}
+
+.rounded-lg {
+  border-radius: 0.5rem;
+}
+
+.rounded-full {
+  border-radius: 9999px;
+}
+
+.rounded-t-none {
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+
+.rounded-r-none {
+  border-top-right-radius: 0;
+  border-bottom-right-radius: 0;
+}
+
+.rounded-b-none {
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+.rounded-l-none {
+  border-top-left-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+.rounded-t-sm {
+  border-top-left-radius: 0.125rem;
+  border-top-right-radius: 0.125rem;
+}
+
+.rounded-r-sm {
+  border-top-right-radius: 0.125rem;
+  border-bottom-right-radius: 0.125rem;
+}
+
+.rounded-b-sm {
+  border-bottom-right-radius: 0.125rem;
+  border-bottom-left-radius: 0.125rem;
+}
+
+.rounded-l-sm {
+  border-top-left-radius: 0.125rem;
+  border-bottom-left-radius: 0.125rem;
+}
+
+.rounded-t {
+  border-top-left-radius: 0.25rem;
+  border-top-right-radius: 0.25rem;
+}
+
+.rounded-r {
+  border-top-right-radius: 0.25rem;
+  border-bottom-right-radius: 0.25rem;
+}
+
+.rounded-b {
+  border-bottom-right-radius: 0.25rem;
+  border-bottom-left-radius: 0.25rem;
+}
+
+.rounded-l {
+  border-top-left-radius: 0.25rem;
+  border-bottom-left-radius: 0.25rem;
+}
+
+.rounded-t-md {
+  border-top-left-radius: 0.375rem;
+  border-top-right-radius: 0.375rem;
+}
+
+.rounded-r-md {
+  border-top-right-radius: 0.375rem;
+  border-bottom-right-radius: 0.375rem;
+}
+
+.rounded-b-md {
+  border-bottom-right-radius: 0.375rem;
+  border-bottom-left-radius: 0.375rem;
+}
+
+.rounded-l-md {
+  border-top-left-radius: 0.375rem;
+  border-bottom-left-radius: 0.375rem;
+}
+
+.rounded-t-lg {
+  border-top-left-radius: 0.5rem;
+  border-top-right-radius: 0.5rem;
+}
+
+.rounded-r-lg {
+  border-top-right-radius: 0.5rem;
+  border-bottom-right-radius: 0.5rem;
+}
+
+.rounded-b-lg {
+  border-bottom-right-radius: 0.5rem;
+  border-bottom-left-radius: 0.5rem;
+}
+
+.rounded-l-lg {
+  border-top-left-radius: 0.5rem;
+  border-bottom-left-radius: 0.5rem;
+}
+
+.rounded-t-full {
+  border-top-left-radius: 9999px;
+  border-top-right-radius: 9999px;
+}
+
+.rounded-r-full {
+  border-top-right-radius: 9999px;
+  border-bottom-right-radius: 9999px;
+}
+
+.rounded-b-full {
+  border-bottom-right-radius: 9999px;
+  border-bottom-left-radius: 9999px;
+}
+
+.rounded-l-full {
+  border-top-left-radius: 9999px;
+  border-bottom-left-radius: 9999px;
+}
+
+.rounded-tl-none {
+  border-top-left-radius: 0;
+}
+
+.rounded-tr-none {
+  border-top-right-radius: 0;
+}
+
+.rounded-br-none {
+  border-bottom-right-radius: 0;
+}
+
+.rounded-bl-none {
+  border-bottom-left-radius: 0;
+}
+
+.rounded-tl-sm {
+  border-top-left-radius: 0.125rem;
+}
+
+.rounded-tr-sm {
+  border-top-right-radius: 0.125rem;
+}
+
+.rounded-br-sm {
+  border-bottom-right-radius: 0.125rem;
+}
+
+.rounded-bl-sm {
+  border-bottom-left-radius: 0.125rem;
+}
+
+.rounded-tl {
+  border-top-left-radius: 0.25rem;
+}
+
+.rounded-tr {
+  border-top-right-radius: 0.25rem;
+}
+
+.rounded-br {
+  border-bottom-right-radius: 0.25rem;
+}
+
+.rounded-bl {
+  border-bottom-left-radius: 0.25rem;
+}
+
+.rounded-tl-md {
+  border-top-left-radius: 0.375rem;
+}
+
+.rounded-tr-md {
+  border-top-right-radius: 0.375rem;
+}
+
+.rounded-br-md {
+  border-bottom-right-radius: 0.375rem;
+}
+
+.rounded-bl-md {
+  border-bottom-left-radius: 0.375rem;
+}
+
+.rounded-tl-lg {
+  border-top-left-radius: 0.5rem;
+}
+
+.rounded-tr-lg {
+  border-top-right-radius: 0.5rem;
+}
+
+.rounded-br-lg {
+  border-bottom-right-radius: 0.5rem;
+}
+
+.rounded-bl-lg {
+  border-bottom-left-radius: 0.5rem;
+}
+
+.rounded-tl-full {
+  border-top-left-radius: 9999px;
+}
+
+.rounded-tr-full {
+  border-top-right-radius: 9999px;
+}
+
+.rounded-br-full {
+  border-bottom-right-radius: 9999px;
+}
+
+.rounded-bl-full {
+  border-bottom-left-radius: 9999px;
+}
+
+.border-solid {
+  border-style: solid;
+}
+
+.border-dashed {
+  border-style: dashed;
+}
+
+.border-dotted {
+  border-style: dotted;
+}
+
+.border-double {
+  border-style: double;
+}
+
+.border-none {
+  border-style: none;
+}
+
+.border-0 {
+  border-width: 0;
+}
+
+.border-2 {
+  border-width: 2px;
+}
+
+.border-4 {
+  border-width: 4px;
+}
+
+.border-8 {
+  border-width: 8px;
+}
+
+.border {
+  border-width: 1px;
+}
+
+.border-t-0 {
+  border-top-width: 0;
+}
+
+.border-r-0 {
+  border-right-width: 0;
+}
+
+.border-b-0 {
+  border-bottom-width: 0;
+}
+
+.border-l-0 {
+  border-left-width: 0;
+}
+
+.border-t-2 {
+  border-top-width: 2px;
+}
+
+.border-r-2 {
+  border-right-width: 2px;
+}
+
+.border-b-2 {
+  border-bottom-width: 2px;
+}
+
+.border-l-2 {
+  border-left-width: 2px;
+}
+
+.border-t-4 {
+  border-top-width: 4px;
+}
+
+.border-r-4 {
+  border-right-width: 4px;
+}
+
+.border-b-4 {
+  border-bottom-width: 4px;
+}
+
+.border-l-4 {
+  border-left-width: 4px;
+}
+
+.border-t-8 {
+  border-top-width: 8px;
+}
+
+.border-r-8 {
+  border-right-width: 8px;
+}
+
+.border-b-8 {
+  border-bottom-width: 8px;
+}
+
+.border-l-8 {
+  border-left-width: 8px;
+}
+
+.border-t {
+  border-top-width: 1px;
+}
+
+.border-r {
+  border-right-width: 1px;
+}
+
+.border-b {
+  border-bottom-width: 1px;
+}
+
+.border-l {
+  border-left-width: 1px;
+}
+
+.box-border {
+  box-sizing: border-box;
+}
+
+.box-content {
+  box-sizing: content-box;
+}
+
+.cursor-auto {
+  cursor: auto;
+}
+
+.cursor-default {
+  cursor: default;
+}
+
+.cursor-pointer {
+  cursor: pointer;
+}
+
+.cursor-wait {
+  cursor: wait;
+}
+
+.cursor-text {
+  cursor: text;
+}
+
+.cursor-move {
+  cursor: move;
+}
+
+.cursor-not-allowed {
+  cursor: not-allowed;
+}
+
+.block {
+  display: block;
+}
+
+.inline-block {
+  display: inline-block;
+}
+
+.inline {
+  display: inline;
+}
+
+.flex {
+  display: flex;
+}
+
+.inline-flex {
+  display: inline-flex;
+}
+
+.table {
+  display: table;
+}
+
+.table-caption {
+  display: table-caption;
+}
+
+.table-cell {
+  display: table-cell;
+}
+
+.table-column {
+  display: table-column;
+}
+
+.table-column-group {
+  display: table-column-group;
+}
+
+.table-footer-group {
+  display: table-footer-group;
+}
+
+.table-header-group {
+  display: table-header-group;
+}
+
+.table-row-group {
+  display: table-row-group;
+}
+
+.table-row {
+  display: table-row;
+}
+
+.flow-root {
+  display: flow-root;
+}
+
+.grid {
+  display: grid;
+}
+
+.inline-grid {
+  display: inline-grid;
+}
+
+.contents {
+  display: contents;
+}
+
+.hidden {
+  display: none;
+}
+
+.flex-row {
+  flex-direction: row;
+}
+
+.flex-row-reverse {
+  flex-direction: row-reverse;
+}
+
+.flex-col {
+  flex-direction: column;
+}
+
+.flex-col-reverse {
+  flex-direction: column-reverse;
+}
+
+.flex-wrap {
+  flex-wrap: wrap;
+}
+
+.flex-wrap-reverse {
+  flex-wrap: wrap-reverse;
+}
+
+.flex-no-wrap {
+  flex-wrap: nowrap;
+}
+
+.place-items-auto {
+  place-items: auto;
+}
+
+.place-items-start {
+  place-items: start;
+}
+
+.place-items-end {
+  place-items: end;
+}
+
+.place-items-center {
+  place-items: center;
+}
+
+.place-items-stretch {
+  place-items: stretch;
+}
+
+.place-content-center {
+  place-content: center;
+}
+
+.place-content-start {
+  place-content: start;
+}
+
+.place-content-end {
+  place-content: end;
+}
+
+.place-content-between {
+  place-content: space-between;
+}
+
+.place-content-around {
+  place-content: space-around;
+}
+
+.place-content-evenly {
+  place-content: space-evenly;
+}
+
+.place-content-stretch {
+  place-content: stretch;
+}
+
+.place-self-auto {
+  place-self: auto;
+}
+
+.place-self-start {
+  place-self: start;
+}
+
+.place-self-end {
+  place-self: end;
+}
+
+.place-self-center {
+  place-self: center;
+}
+
+.place-self-stretch {
+  place-self: stretch;
+}
+
+.items-start {
+  align-items: flex-start;
+}
+
+.items-end {
+  align-items: flex-end;
+}
+
+.items-center {
+  align-items: center;
+}
+
+.items-baseline {
+  align-items: baseline;
+}
+
+.items-stretch {
+  align-items: stretch;
+}
+
+.content-center {
+  align-content: center;
+}
+
+.content-start {
+  align-content: flex-start;
+}
+
+.content-end {
+  align-content: flex-end;
+}
+
+.content-between {
+  align-content: space-between;
+}
+
+.content-around {
+  align-content: space-around;
+}
+
+.content-evenly {
+  align-content: space-evenly;
+}
+
+.self-auto {
+  align-self: auto;
+}
+
+.self-start {
+  align-self: flex-start;
+}
+
+.self-end {
+  align-self: flex-end;
+}
+
+.self-center {
+  align-self: center;
+}
+
+.self-stretch {
+  align-self: stretch;
+}
+
+.justify-items-auto {
+  justify-items: auto;
+}
+
+.justify-items-start {
+  justify-items: start;
+}
+
+.justify-items-end {
+  justify-items: end;
+}
+
+.justify-items-center {
+  justify-items: center;
+}
+
+.justify-items-stretch {
+  justify-items: stretch;
+}
+
+.justify-start {
+  justify-content: flex-start;
+}
+
+.justify-end {
+  justify-content: flex-end;
+}
+
+.justify-center {
+  justify-content: center;
+}
+
+.justify-between {
+  justify-content: space-between;
+}
+
+.justify-around {
+  justify-content: space-around;
+}
+
+.justify-evenly {
+  justify-content: space-evenly;
+}
+
+.justify-self-auto {
+  justify-self: auto;
+}
+
+.justify-self-start {
+  justify-self: start;
+}
+
+.justify-self-end {
+  justify-self: end;
+}
+
+.justify-self-center {
+  justify-self: center;
+}
+
+.justify-self-stretch {
+  justify-self: stretch;
+}
+
+.flex-1 {
+  flex: 1 1 0%;
+}
+
+.flex-auto {
+  flex: 1 1 auto;
+}
+
+.flex-initial {
+  flex: 0 1 auto;
+}
+
+.flex-none {
+  flex: none;
+}
+
+.flex-grow-0 {
+  flex-grow: 0;
+}
+
+.flex-grow {
+  flex-grow: 1;
+}
+
+.flex-shrink-0 {
+  flex-shrink: 0;
+}
+
+.flex-shrink {
+  flex-shrink: 1;
+}
+
+.order-1 {
+  order: 1;
+}
+
+.order-2 {
+  order: 2;
+}
+
+.order-3 {
+  order: 3;
+}
+
+.order-4 {
+  order: 4;
+}
+
+.order-5 {
+  order: 5;
+}
+
+.order-6 {
+  order: 6;
+}
+
+.order-7 {
+  order: 7;
+}
+
+.order-8 {
+  order: 8;
+}
+
+.order-9 {
+  order: 9;
+}
+
+.order-10 {
+  order: 10;
+}
+
+.order-11 {
+  order: 11;
+}
+
+.order-12 {
+  order: 12;
+}
+
+.order-first {
+  order: -9999;
+}
+
+.order-last {
+  order: 9999;
+}
+
+.order-none {
+  order: 0;
+}
+
+.float-right {
+  float: right;
+}
+
+.float-left {
+  float: left;
+}
+
+.float-none {
+  float: none;
+}
+
+.clearfix:after {
+  content: "";
+  display: table;
+  clear: both;
+}
+
+.clear-left {
+  clear: left;
+}
+
+.clear-right {
+  clear: right;
+}
+
+.clear-both {
+  clear: both;
+}
+
+.clear-none {
+  clear: none;
+}
+
+.font-sans {
+  font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+}
+
+.font-serif {
+  font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+}
+
+.font-mono {
+  font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+.font-hairline {
+  font-weight: 100;
+}
+
+.font-thin {
+  font-weight: 200;
+}
+
+.font-light {
+  font-weight: 300;
+}
+
+.font-normal {
+  font-weight: 400;
+}
+
+.font-medium {
+  font-weight: 500;
+}
+
+.font-semibold {
+  font-weight: 600;
+}
+
+.font-bold {
+  font-weight: 700;
+}
+
+.font-extrabold {
+  font-weight: 800;
+}
+
+.font-black {
+  font-weight: 900;
+}
+
+.hover\:font-hairline:hover {
+  font-weight: 100;
+}
+
+.hover\:font-thin:hover {
+  font-weight: 200;
+}
+
+.hover\:font-light:hover {
+  font-weight: 300;
+}
+
+.hover\:font-normal:hover {
+  font-weight: 400;
+}
+
+.hover\:font-medium:hover {
+  font-weight: 500;
+}
+
+.hover\:font-semibold:hover {
+  font-weight: 600;
+}
+
+.hover\:font-bold:hover {
+  font-weight: 700;
+}
+
+.hover\:font-extrabold:hover {
+  font-weight: 800;
+}
+
+.hover\:font-black:hover {
+  font-weight: 900;
+}
+
+.focus\:font-hairline:focus {
+  font-weight: 100;
+}
+
+.focus\:font-thin:focus {
+  font-weight: 200;
+}
+
+.focus\:font-light:focus {
+  font-weight: 300;
+}
+
+.focus\:font-normal:focus {
+  font-weight: 400;
+}
+
+.focus\:font-medium:focus {
+  font-weight: 500;
+}
+
+.focus\:font-semibold:focus {
+  font-weight: 600;
+}
+
+.focus\:font-bold:focus {
+  font-weight: 700;
+}
+
+.focus\:font-extrabold:focus {
+  font-weight: 800;
+}
+
+.focus\:font-black:focus {
+  font-weight: 900;
+}
+
+.h-0 {
+  height: 0;
+}
+
+.h-1 {
+  height: 0.25rem;
+}
+
+.h-2 {
+  height: 0.5rem;
+}
+
+.h-3 {
+  height: 0.75rem;
+}
+
+.h-4 {
+  height: 1rem;
+}
+
+.h-5 {
+  height: 1.25rem;
+}
+
+.h-6 {
+  height: 1.5rem;
+}
+
+.h-8 {
+  height: 2rem;
+}
+
+.h-10 {
+  height: 2.5rem;
+}
+
+.h-12 {
+  height: 3rem;
+}
+
+.h-16 {
+  height: 4rem;
+}
+
+.h-20 {
+  height: 5rem;
+}
+
+.h-24 {
+  height: 6rem;
+}
+
+.h-32 {
+  height: 8rem;
+}
+
+.h-40 {
+  height: 10rem;
+}
+
+.h-48 {
+  height: 12rem;
+}
+
+.h-56 {
+  height: 14rem;
+}
+
+.h-64 {
+  height: 16rem;
+}
+
+.h-auto {
+  height: auto;
+}
+
+.h-px {
+  height: 1px;
+}
+
+.h-full {
+  height: 100%;
+}
+
+.h-screen {
+  height: 100vh;
+}
+
+.text-xs {
+  font-size: 0.75rem;
+}
+
+.text-sm {
+  font-size: 0.875rem;
+}
+
+.text-base {
+  font-size: 1rem;
+}
+
+.text-lg {
+  font-size: 1.125rem;
+}
+
+.text-xl {
+  font-size: 1.25rem;
+}
+
+.text-2xl {
+  font-size: 1.5rem;
+}
+
+.text-3xl {
+  font-size: 1.875rem;
+}
+
+.text-4xl {
+  font-size: 2.25rem;
+}
+
+.text-5xl {
+  font-size: 3rem;
+}
+
+.text-6xl {
+  font-size: 4rem;
+}
+
+.leading-3 {
+  line-height: .75rem;
+}
+
+.leading-4 {
+  line-height: 1rem;
+}
+
+.leading-5 {
+  line-height: 1.25rem;
+}
+
+.leading-6 {
+  line-height: 1.5rem;
+}
+
+.leading-7 {
+  line-height: 1.75rem;
+}
+
+.leading-8 {
+  line-height: 2rem;
+}
+
+.leading-9 {
+  line-height: 2.25rem;
+}
+
+.leading-10 {
+  line-height: 2.5rem;
+}
+
+.leading-none {
+  line-height: 1;
+}
+
+.leading-tight {
+  line-height: 1.25;
+}
+
+.leading-snug {
+  line-height: 1.375;
+}
+
+.leading-normal {
+  line-height: 1.5;
+}
+
+.leading-relaxed {
+  line-height: 1.625;
+}
+
+.leading-loose {
+  line-height: 2;
+}
+
+.list-inside {
+  list-style-position: inside;
+}
+
+.list-outside {
+  list-style-position: outside;
+}
+
+.list-none {
+  list-style-type: none;
+}
+
+.list-disc {
+  list-style-type: disc;
+}
+
+.list-decimal {
+  list-style-type: decimal;
+}
+
+.m-0 {
+  margin: 0;
+}
+
+.m-1 {
+  margin: 0.25rem;
+}
+
+.m-2 {
+  margin: 0.5rem;
+}
+
+.m-3 {
+  margin: 0.75rem;
+}
+
+.m-4 {
+  margin: 1rem;
+}
+
+.m-5 {
+  margin: 1.25rem;
+}
+
+.m-6 {
+  margin: 1.5rem;
+}
+
+.m-8 {
+  margin: 2rem;
+}
+
+.m-10 {
+  margin: 2.5rem;
+}
+
+.m-12 {
+  margin: 3rem;
+}
+
+.m-16 {
+  margin: 4rem;
+}
+
+.m-20 {
+  margin: 5rem;
+}
+
+.m-24 {
+  margin: 6rem;
+}
+
+.m-32 {
+  margin: 8rem;
+}
+
+.m-40 {
+  margin: 10rem;
+}
+
+.m-48 {
+  margin: 12rem;
+}
+
+.m-56 {
+  margin: 14rem;
+}
+
+.m-64 {
+  margin: 16rem;
+}
+
+.m-auto {
+  margin: auto;
+}
+
+.m-px {
+  margin: 1px;
+}
+
+.-m-1 {
+  margin: -0.25rem;
+}
+
+.-m-2 {
+  margin: -0.5rem;
+}
+
+.-m-3 {
+  margin: -0.75rem;
+}
+
+.-m-4 {
+  margin: -1rem;
+}
+
+.-m-5 {
+  margin: -1.25rem;
+}
+
+.-m-6 {
+  margin: -1.5rem;
+}
+
+.-m-8 {
+  margin: -2rem;
+}
+
+.-m-10 {
+  margin: -2.5rem;
+}
+
+.-m-12 {
+  margin: -3rem;
+}
+
+.-m-16 {
+  margin: -4rem;
+}
+
+.-m-20 {
+  margin: -5rem;
+}
+
+.-m-24 {
+  margin: -6rem;
+}
+
+.-m-32 {
+  margin: -8rem;
+}
+
+.-m-40 {
+  margin: -10rem;
+}
+
+.-m-48 {
+  margin: -12rem;
+}
+
+.-m-56 {
+  margin: -14rem;
+}
+
+.-m-64 {
+  margin: -16rem;
+}
+
+.-m-px {
+  margin: -1px;
+}
+
+.my-0 {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+.mx-0 {
+  margin-left: 0;
+  margin-right: 0;
+}
+
+.my-1 {
+  margin-top: 0.25rem;
+  margin-bottom: 0.25rem;
+}
+
+.mx-1 {
+  margin-left: 0.25rem;
+  margin-right: 0.25rem;
+}
+
+.my-2 {
+  margin-top: 0.5rem;
+  margin-bottom: 0.5rem;
+}
+
+.mx-2 {
+  margin-left: 0.5rem;
+  margin-right: 0.5rem;
+}
+
+.my-3 {
+  margin-top: 0.75rem;
+  margin-bottom: 0.75rem;
+}
+
+.mx-3 {
+  margin-left: 0.75rem;
+  margin-right: 0.75rem;
+}
+
+.my-4 {
+  margin-top: 1rem;
+  margin-bottom: 1rem;
+}
+
+.mx-4 {
+  margin-left: 1rem;
+  margin-right: 1rem;
+}
+
+.my-5 {
+  margin-top: 1.25rem;
+  margin-bottom: 1.25rem;
+}
+
+.mx-5 {
+  margin-left: 1.25rem;
+  margin-right: 1.25rem;
+}
+
+.my-6 {
+  margin-top: 1.5rem;
+  margin-bottom: 1.5rem;
+}
+
+.mx-6 {
+  margin-left: 1.5rem;
+  margin-right: 1.5rem;
+}
+
+.my-8 {
+  margin-top: 2rem;
+  margin-bottom: 2rem;
+}
+
+.mx-8 {
+  margin-left: 2rem;
+  margin-right: 2rem;
+}
+
+.my-10 {
+  margin-top: 2.5rem;
+  margin-bottom: 2.5rem;
+}
+
+.mx-10 {
+  margin-left: 2.5rem;
+  margin-right: 2.5rem;
+}
+
+.my-12 {
+  margin-top: 3rem;
+  margin-bottom: 3rem;
+}
+
+.mx-12 {
+  margin-left: 3rem;
+  margin-right: 3rem;
+}
+
+.my-16 {
+  margin-top: 4rem;
+  margin-bottom: 4rem;
+}
+
+.mx-16 {
+  margin-left: 4rem;
+  margin-right: 4rem;
+}
+
+.my-20 {
+  margin-top: 5rem;
+  margin-bottom: 5rem;
+}
+
+.mx-20 {
+  margin-left: 5rem;
+  margin-right: 5rem;
+}
+
+.my-24 {
+  margin-top: 6rem;
+  margin-bottom: 6rem;
+}
+
+.mx-24 {
+  margin-left: 6rem;
+  margin-right: 6rem;
+}
+
+.my-32 {
+  margin-top: 8rem;
+  margin-bottom: 8rem;
+}
+
+.mx-32 {
+  margin-left: 8rem;
+  margin-right: 8rem;
+}
+
+.my-40 {
+  margin-top: 10rem;
+  margin-bottom: 10rem;
+}
+
+.mx-40 {
+  margin-left: 10rem;
+  margin-right: 10rem;
+}
+
+.my-48 {
+  margin-top: 12rem;
+  margin-bottom: 12rem;
+}
+
+.mx-48 {
+  margin-left: 12rem;
+  margin-right: 12rem;
+}
+
+.my-56 {
+  margin-top: 14rem;
+  margin-bottom: 14rem;
+}
+
+.mx-56 {
+  margin-left: 14rem;
+  margin-right: 14rem;
+}
+
+.my-64 {
+  margin-top: 16rem;
+  margin-bottom: 16rem;
+}
+
+.mx-64 {
+  margin-left: 16rem;
+  margin-right: 16rem;
+}
+
+.my-auto {
+  margin-top: auto;
+  margin-bottom: auto;
+}
+
+.mx-auto {
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.my-px {
+  margin-top: 1px;
+  margin-bottom: 1px;
+}
+
+.mx-px {
+  margin-left: 1px;
+  margin-right: 1px;
+}
+
+.-my-1 {
+  margin-top: -0.25rem;
+  margin-bottom: -0.25rem;
+}
+
+.-mx-1 {
+  margin-left: -0.25rem;
+  margin-right: -0.25rem;
+}
+
+.-my-2 {
+  margin-top: -0.5rem;
+  margin-bottom: -0.5rem;
+}
+
+.-mx-2 {
+  margin-left: -0.5rem;
+  margin-right: -0.5rem;
+}
+
+.-my-3 {
+  margin-top: -0.75rem;
+  margin-bottom: -0.75rem;
+}
+
+.-mx-3 {
+  margin-left: -0.75rem;
+  margin-right: -0.75rem;
+}
+
+.-my-4 {
+  margin-top: -1rem;
+  margin-bottom: -1rem;
+}
+
+.-mx-4 {
+  margin-left: -1rem;
+  margin-right: -1rem;
+}
+
+.-my-5 {
+  margin-top: -1.25rem;
+  margin-bottom: -1.25rem;
+}
+
+.-mx-5 {
+  margin-left: -1.25rem;
+  margin-right: -1.25rem;
+}
+
+.-my-6 {
+  margin-top: -1.5rem;
+  margin-bottom: -1.5rem;
+}
+
+.-mx-6 {
+  margin-left: -1.5rem;
+  margin-right: -1.5rem;
+}
+
+.-my-8 {
+  margin-top: -2rem;
+  margin-bottom: -2rem;
+}
+
+.-mx-8 {
+  margin-left: -2rem;
+  margin-right: -2rem;
+}
+
+.-my-10 {
+  margin-top: -2.5rem;
+  margin-bottom: -2.5rem;
+}
+
+.-mx-10 {
+  margin-left: -2.5rem;
+  margin-right: -2.5rem;
+}
+
+.-my-12 {
+  margin-top: -3rem;
+  margin-bottom: -3rem;
+}
+
+.-mx-12 {
+  margin-left: -3rem;
+  margin-right: -3rem;
+}
+
+.-my-16 {
+  margin-top: -4rem;
+  margin-bottom: -4rem;
+}
+
+.-mx-16 {
+  margin-left: -4rem;
+  margin-right: -4rem;
+}
+
+.-my-20 {
+  margin-top: -5rem;
+  margin-bottom: -5rem;
+}
+
+.-mx-20 {
+  margin-left: -5rem;
+  margin-right: -5rem;
+}
+
+.-my-24 {
+  margin-top: -6rem;
+  margin-bottom: -6rem;
+}
+
+.-mx-24 {
+  margin-left: -6rem;
+  margin-right: -6rem;
+}
+
+.-my-32 {
+  margin-top: -8rem;
+  margin-bottom: -8rem;
+}
+
+.-mx-32 {
+  margin-left: -8rem;
+  margin-right: -8rem;
+}
+
+.-my-40 {
+  margin-top: -10rem;
+  margin-bottom: -10rem;
+}
+
+.-mx-40 {
+  margin-left: -10rem;
+  margin-right: -10rem;
+}
+
+.-my-48 {
+  margin-top: -12rem;
+  margin-bottom: -12rem;
+}
+
+.-mx-48 {
+  margin-left: -12rem;
+  margin-right: -12rem;
+}
+
+.-my-56 {
+  margin-top: -14rem;
+  margin-bottom: -14rem;
+}
+
+.-mx-56 {
+  margin-left: -14rem;
+  margin-right: -14rem;
+}
+
+.-my-64 {
+  margin-top: -16rem;
+  margin-bottom: -16rem;
+}
+
+.-mx-64 {
+  margin-left: -16rem;
+  margin-right: -16rem;
+}
+
+.-my-px {
+  margin-top: -1px;
+  margin-bottom: -1px;
+}
+
+.-mx-px {
+  margin-left: -1px;
+  margin-right: -1px;
+}
+
+.mt-0 {
+  margin-top: 0;
+}
+
+.mr-0 {
+  margin-right: 0;
+}
+
+.mb-0 {
+  margin-bottom: 0;
+}
+
+.ml-0 {
+  margin-left: 0;
+}
+
+.mt-1 {
+  margin-top: 0.25rem;
+}
+
+.mr-1 {
+  margin-right: 0.25rem;
+}
+
+.mb-1 {
+  margin-bottom: 0.25rem;
+}
+
+.ml-1 {
+  margin-left: 0.25rem;
+}
+
+.mt-2 {
+  margin-top: 0.5rem;
+}
+
+.mr-2 {
+  margin-right: 0.5rem;
+}
+
+.mb-2 {
+  margin-bottom: 0.5rem;
+}
+
+.ml-2 {
+  margin-left: 0.5rem;
+}
+
+.mt-3 {
+  margin-top: 0.75rem;
+}
+
+.mr-3 {
+  margin-right: 0.75rem;
+}
+
+.mb-3 {
+  margin-bottom: 0.75rem;
+}
+
+.ml-3 {
+  margin-left: 0.75rem;
+}
+
+.mt-4 {
+  margin-top: 1rem;
+}
+
+.mr-4 {
+  margin-right: 1rem;
+}
+
+.mb-4 {
+  margin-bottom: 1rem;
+}
+
+.ml-4 {
+  margin-left: 1rem;
+}
+
+.mt-5 {
+  margin-top: 1.25rem;
+}
+
+.mr-5 {
+  margin-right: 1.25rem;
+}
+
+.mb-5 {
+  margin-bottom: 1.25rem;
+}
+
+.ml-5 {
+  margin-left: 1.25rem;
+}
+
+.mt-6 {
+  margin-top: 1.5rem;
+}
+
+.mr-6 {
+  margin-right: 1.5rem;
+}
+
+.mb-6 {
+  margin-bottom: 1.5rem;
+}
+
+.ml-6 {
+  margin-left: 1.5rem;
+}
+
+.mt-8 {
+  margin-top: 2rem;
+}
+
+.mr-8 {
+  margin-right: 2rem;
+}
+
+.mb-8 {
+  margin-bottom: 2rem;
+}
+
+.ml-8 {
+  margin-left: 2rem;
+}
+
+.mt-10 {
+  margin-top: 2.5rem;
+}
+
+.mr-10 {
+  margin-right: 2.5rem;
+}
+
+.mb-10 {
+  margin-bottom: 2.5rem;
+}
+
+.ml-10 {
+  margin-left: 2.5rem;
+}
+
+.mt-12 {
+  margin-top: 3rem;
+}
+
+.mr-12 {
+  margin-right: 3rem;
+}
+
+.mb-12 {
+  margin-bottom: 3rem;
+}
+
+.ml-12 {
+  margin-left: 3rem;
+}
+
+.mt-16 {
+  margin-top: 4rem;
+}
+
+.mr-16 {
+  margin-right: 4rem;
+}
+
+.mb-16 {
+  margin-bottom: 4rem;
+}
+
+.ml-16 {
+  margin-left: 4rem;
+}
+
+.mt-20 {
+  margin-top: 5rem;
+}
+
+.mr-20 {
+  margin-right: 5rem;
+}
+
+.mb-20 {
+  margin-bottom: 5rem;
+}
+
+.ml-20 {
+  margin-left: 5rem;
+}
+
+.mt-24 {
+  margin-top: 6rem;
+}
+
+.mr-24 {
+  margin-right: 6rem;
+}
+
+.mb-24 {
+  margin-bottom: 6rem;
+}
+
+.ml-24 {
+  margin-left: 6rem;
+}
+
+.mt-32 {
+  margin-top: 8rem;
+}
+
+.mr-32 {
+  margin-right: 8rem;
+}
+
+.mb-32 {
+  margin-bottom: 8rem;
+}
+
+.ml-32 {
+  margin-left: 8rem;
+}
+
+.mt-40 {
+  margin-top: 10rem;
+}
+
+.mr-40 {
+  margin-right: 10rem;
+}
+
+.mb-40 {
+  margin-bottom: 10rem;
+}
+
+.ml-40 {
+  margin-left: 10rem;
+}
+
+.mt-48 {
+  margin-top: 12rem;
+}
+
+.mr-48 {
+  margin-right: 12rem;
+}
+
+.mb-48 {
+  margin-bottom: 12rem;
+}
+
+.ml-48 {
+  margin-left: 12rem;
+}
+
+.mt-56 {
+  margin-top: 14rem;
+}
+
+.mr-56 {
+  margin-right: 14rem;
+}
+
+.mb-56 {
+  margin-bottom: 14rem;
+}
+
+.ml-56 {
+  margin-left: 14rem;
+}
+
+.mt-64 {
+  margin-top: 16rem;
+}
+
+.mr-64 {
+  margin-right: 16rem;
+}
+
+.mb-64 {
+  margin-bottom: 16rem;
+}
+
+.ml-64 {
+  margin-left: 16rem;
+}
+
+.mt-auto {
+  margin-top: auto;
+}
+
+.mr-auto {
+  margin-right: auto;
+}
+
+.mb-auto {
+  margin-bottom: auto;
+}
+
+.ml-auto {
+  margin-left: auto;
+}
+
+.mt-px {
+  margin-top: 1px;
+}
+
+.mr-px {
+  margin-right: 1px;
+}
+
+.mb-px {
+  margin-bottom: 1px;
+}
+
+.ml-px {
+  margin-left: 1px;
+}
+
+.-mt-1 {
+  margin-top: -0.25rem;
+}
+
+.-mr-1 {
+  margin-right: -0.25rem;
+}
+
+.-mb-1 {
+  margin-bottom: -0.25rem;
+}
+
+.-ml-1 {
+  margin-left: -0.25rem;
+}
+
+.-mt-2 {
+  margin-top: -0.5rem;
+}
+
+.-mr-2 {
+  margin-right: -0.5rem;
+}
+
+.-mb-2 {
+  margin-bottom: -0.5rem;
+}
+
+.-ml-2 {
+  margin-left: -0.5rem;
+}
+
+.-mt-3 {
+  margin-top: -0.75rem;
+}
+
+.-mr-3 {
+  margin-right: -0.75rem;
+}
+
+.-mb-3 {
+  margin-bottom: -0.75rem;
+}
+
+.-ml-3 {
+  margin-left: -0.75rem;
+}
+
+.-mt-4 {
+  margin-top: -1rem;
+}
+
+.-mr-4 {
+  margin-right: -1rem;
+}
+
+.-mb-4 {
+  margin-bottom: -1rem;
+}
+
+.-ml-4 {
+  margin-left: -1rem;
+}
+
+.-mt-5 {
+  margin-top: -1.25rem;
+}
+
+.-mr-5 {
+  margin-right: -1.25rem;
+}
+
+.-mb-5 {
+  margin-bottom: -1.25rem;
+}
+
+.-ml-5 {
+  margin-left: -1.25rem;
+}
+
+.-mt-6 {
+  margin-top: -1.5rem;
+}
+
+.-mr-6 {
+  margin-right: -1.5rem;
+}
+
+.-mb-6 {
+  margin-bottom: -1.5rem;
+}
+
+.-ml-6 {
+  margin-left: -1.5rem;
+}
+
+.-mt-8 {
+  margin-top: -2rem;
+}
+
+.-mr-8 {
+  margin-right: -2rem;
+}
+
+.-mb-8 {
+  margin-bottom: -2rem;
+}
+
+.-ml-8 {
+  margin-left: -2rem;
+}
+
+.-mt-10 {
+  margin-top: -2.5rem;
+}
+
+.-mr-10 {
+  margin-right: -2.5rem;
+}
+
+.-mb-10 {
+  margin-bottom: -2.5rem;
+}
+
+.-ml-10 {
+  margin-left: -2.5rem;
+}
+
+.-mt-12 {
+  margin-top: -3rem;
+}
+
+.-mr-12 {
+  margin-right: -3rem;
+}
+
+.-mb-12 {
+  margin-bottom: -3rem;
+}
+
+.-ml-12 {
+  margin-left: -3rem;
+}
+
+.-mt-16 {
+  margin-top: -4rem;
+}
+
+.-mr-16 {
+  margin-right: -4rem;
+}
+
+.-mb-16 {
+  margin-bottom: -4rem;
+}
+
+.-ml-16 {
+  margin-left: -4rem;
+}
+
+.-mt-20 {
+  margin-top: -5rem;
+}
+
+.-mr-20 {
+  margin-right: -5rem;
+}
+
+.-mb-20 {
+  margin-bottom: -5rem;
+}
+
+.-ml-20 {
+  margin-left: -5rem;
+}
+
+.-mt-24 {
+  margin-top: -6rem;
+}
+
+.-mr-24 {
+  margin-right: -6rem;
+}
+
+.-mb-24 {
+  margin-bottom: -6rem;
+}
+
+.-ml-24 {
+  margin-left: -6rem;
+}
+
+.-mt-32 {
+  margin-top: -8rem;
+}
+
+.-mr-32 {
+  margin-right: -8rem;
+}
+
+.-mb-32 {
+  margin-bottom: -8rem;
+}
+
+.-ml-32 {
+  margin-left: -8rem;
+}
+
+.-mt-40 {
+  margin-top: -10rem;
+}
+
+.-mr-40 {
+  margin-right: -10rem;
+}
+
+.-mb-40 {
+  margin-bottom: -10rem;
+}
+
+.-ml-40 {
+  margin-left: -10rem;
+}
+
+.-mt-48 {
+  margin-top: -12rem;
+}
+
+.-mr-48 {
+  margin-right: -12rem;
+}
+
+.-mb-48 {
+  margin-bottom: -12rem;
+}
+
+.-ml-48 {
+  margin-left: -12rem;
+}
+
+.-mt-56 {
+  margin-top: -14rem;
+}
+
+.-mr-56 {
+  margin-right: -14rem;
+}
+
+.-mb-56 {
+  margin-bottom: -14rem;
+}
+
+.-ml-56 {
+  margin-left: -14rem;
+}
+
+.-mt-64 {
+  margin-top: -16rem;
+}
+
+.-mr-64 {
+  margin-right: -16rem;
+}
+
+.-mb-64 {
+  margin-bottom: -16rem;
+}
+
+.-ml-64 {
+  margin-left: -16rem;
+}
+
+.-mt-px {
+  margin-top: -1px;
+}
+
+.-mr-px {
+  margin-right: -1px;
+}
+
+.-mb-px {
+  margin-bottom: -1px;
+}
+
+.-ml-px {
+  margin-left: -1px;
+}
+
+.max-h-full {
+  max-height: 100%;
+}
+
+.max-h-screen {
+  max-height: 100vh;
+}
+
+.max-w-none {
+  max-width: none;
+}
+
+.max-w-xs {
+  max-width: 20rem;
+}
+
+.max-w-sm {
+  max-width: 24rem;
+}
+
+.max-w-md {
+  max-width: 28rem;
+}
+
+.max-w-lg {
+  max-width: 32rem;
+}
+
+.max-w-xl {
+  max-width: 36rem;
+}
+
+.max-w-2xl {
+  max-width: 42rem;
+}
+
+.max-w-3xl {
+  max-width: 48rem;
+}
+
+.max-w-4xl {
+  max-width: 56rem;
+}
+
+.max-w-5xl {
+  max-width: 64rem;
+}
+
+.max-w-6xl {
+  max-width: 72rem;
+}
+
+.max-w-full {
+  max-width: 100%;
+}
+
+.max-w-screen-sm {
+  max-width: 640px;
+}
+
+.max-w-screen-md {
+  max-width: 768px;
+}
+
+.max-w-screen-lg {
+  max-width: 1024px;
+}
+
+.max-w-screen-xl {
+  max-width: 1280px;
+}
+
+.min-h-0 {
+  min-height: 0;
+}
+
+.min-h-full {
+  min-height: 100%;
+}
+
+.min-h-screen {
+  min-height: 100vh;
+}
+
+.min-w-0 {
+  min-width: 0;
+}
+
+.min-w-full {
+  min-width: 100%;
+}
+
+.object-contain {
+  -o-object-fit: contain;
+     object-fit: contain;
+}
+
+.object-cover {
+  -o-object-fit: cover;
+     object-fit: cover;
+}
+
+.object-fill {
+  -o-object-fit: fill;
+     object-fit: fill;
+}
+
+.object-none {
+  -o-object-fit: none;
+     object-fit: none;
+}
+
+.object-scale-down {
+  -o-object-fit: scale-down;
+     object-fit: scale-down;
+}
+
+.object-bottom {
+  -o-object-position: bottom;
+     object-position: bottom;
+}
+
+.object-center {
+  -o-object-position: center;
+     object-position: center;
+}
+
+.object-left {
+  -o-object-position: left;
+     object-position: left;
+}
+
+.object-left-bottom {
+  -o-object-position: left bottom;
+     object-position: left bottom;
+}
+
+.object-left-top {
+  -o-object-position: left top;
+     object-position: left top;
+}
+
+.object-right {
+  -o-object-position: right;
+     object-position: right;
+}
+
+.object-right-bottom {
+  -o-object-position: right bottom;
+     object-position: right bottom;
+}
+
+.object-right-top {
+  -o-object-position: right top;
+     object-position: right top;
+}
+
+.object-top {
+  -o-object-position: top;
+     object-position: top;
+}
+
+.opacity-0 {
+  opacity: 0;
+}
+
+.opacity-25 {
+  opacity: 0.25;
+}
+
+.opacity-50 {
+  opacity: 0.5;
+}
+
+.opacity-75 {
+  opacity: 0.75;
+}
+
+.opacity-100 {
+  opacity: 1;
+}
+
+.hover\:opacity-0:hover {
+  opacity: 0;
+}
+
+.hover\:opacity-25:hover {
+  opacity: 0.25;
+}
+
+.hover\:opacity-50:hover {
+  opacity: 0.5;
+}
+
+.hover\:opacity-75:hover {
+  opacity: 0.75;
+}
+
+.hover\:opacity-100:hover {
+  opacity: 1;
+}
+
+.focus\:opacity-0:focus {
+  opacity: 0;
+}
+
+.focus\:opacity-25:focus {
+  opacity: 0.25;
+}
+
+.focus\:opacity-50:focus {
+  opacity: 0.5;
+}
+
+.focus\:opacity-75:focus {
+  opacity: 0.75;
+}
+
+.focus\:opacity-100:focus {
+  opacity: 1;
+}
+
+.outline-none {
+  outline: 0;
+}
+
+.focus\:outline-none:focus {
+  outline: 0;
+}
+
+.overflow-auto {
+  overflow: auto;
+}
+
+.overflow-hidden {
+  overflow: hidden;
+}
+
+.overflow-visible {
+  overflow: visible;
+}
+
+.overflow-scroll {
+  overflow: scroll;
+}
+
+.overflow-x-auto {
+  overflow-x: auto;
+}
+
+.overflow-y-auto {
+  overflow-y: auto;
+}
+
+.overflow-x-hidden {
+  overflow-x: hidden;
+}
+
+.overflow-y-hidden {
+  overflow-y: hidden;
+}
+
+.overflow-x-visible {
+  overflow-x: visible;
+}
+
+.overflow-y-visible {
+  overflow-y: visible;
+}
+
+.overflow-x-scroll {
+  overflow-x: scroll;
+}
+
+.overflow-y-scroll {
+  overflow-y: scroll;
+}
+
+.scrolling-touch {
+  -webkit-overflow-scrolling: touch;
+}
+
+.scrolling-auto {
+  -webkit-overflow-scrolling: auto;
+}
+
+.overscroll-auto {
+  -ms-scroll-chaining: chained;
+      overscroll-behavior: auto;
+}
+
+.overscroll-contain {
+  -ms-scroll-chaining: none;
+      overscroll-behavior: contain;
+}
+
+.overscroll-none {
+  -ms-scroll-chaining: none;
+      overscroll-behavior: none;
+}
+
+.overscroll-y-auto {
+  overscroll-behavior-y: auto;
+}
+
+.overscroll-y-contain {
+  overscroll-behavior-y: contain;
+}
+
+.overscroll-y-none {
+  overscroll-behavior-y: none;
+}
+
+.overscroll-x-auto {
+  overscroll-behavior-x: auto;
+}
+
+.overscroll-x-contain {
+  overscroll-behavior-x: contain;
+}
+
+.overscroll-x-none {
+  overscroll-behavior-x: none;
+}
+
+.p-0 {
+  padding: 0;
+}
+
+.p-1 {
+  padding: 0.25rem;
+}
+
+.p-2 {
+  padding: 0.5rem;
+}
+
+.p-3 {
+  padding: 0.75rem;
+}
+
+.p-4 {
+  padding: 1rem;
+}
+
+.p-5 {
+  padding: 1.25rem;
+}
+
+.p-6 {
+  padding: 1.5rem;
+}
+
+.p-8 {
+  padding: 2rem;
+}
+
+.p-10 {
+  padding: 2.5rem;
+}
+
+.p-12 {
+  padding: 3rem;
+}
+
+.p-16 {
+  padding: 4rem;
+}
+
+.p-20 {
+  padding: 5rem;
+}
+
+.p-24 {
+  padding: 6rem;
+}
+
+.p-32 {
+  padding: 8rem;
+}
+
+.p-40 {
+  padding: 10rem;
+}
+
+.p-48 {
+  padding: 12rem;
+}
+
+.p-56 {
+  padding: 14rem;
+}
+
+.p-64 {
+  padding: 16rem;
+}
+
+.p-px {
+  padding: 1px;
+}
+
+.py-0 {
+  padding-top: 0;
+  padding-bottom: 0;
+}
+
+.px-0 {
+  padding-left: 0;
+  padding-right: 0;
+}
+
+.py-1 {
+  padding-top: 0.25rem;
+  padding-bottom: 0.25rem;
+}
+
+.px-1 {
+  padding-left: 0.25rem;
+  padding-right: 0.25rem;
+}
+
+.py-2 {
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+}
+
+.px-2 {
+  padding-left: 0.5rem;
+  padding-right: 0.5rem;
+}
+
+.py-3 {
+  padding-top: 0.75rem;
+  padding-bottom: 0.75rem;
+}
+
+.px-3 {
+  padding-left: 0.75rem;
+  padding-right: 0.75rem;
+}
+
+.py-4 {
+  padding-top: 1rem;
+  padding-bottom: 1rem;
+}
+
+.px-4 {
+  padding-left: 1rem;
+  padding-right: 1rem;
+}
+
+.py-5 {
+  padding-top: 1.25rem;
+  padding-bottom: 1.25rem;
+}
+
+.px-5 {
+  padding-left: 1.25rem;
+  padding-right: 1.25rem;
+}
+
+.py-6 {
+  padding-top: 1.5rem;
+  padding-bottom: 1.5rem;
+}
+
+.px-6 {
+  padding-left: 1.5rem;
+  padding-right: 1.5rem;
+}
+
+.py-8 {
+  padding-top: 2rem;
+  padding-bottom: 2rem;
+}
+
+.px-8 {
+  padding-left: 2rem;
+  padding-right: 2rem;
+}
+
+.py-10 {
+  padding-top: 2.5rem;
+  padding-bottom: 2.5rem;
+}
+
+.px-10 {
+  padding-left: 2.5rem;
+  padding-right: 2.5rem;
+}
+
+.py-12 {
+  padding-top: 3rem;
+  padding-bottom: 3rem;
+}
+
+.px-12 {
+  padding-left: 3rem;
+  padding-right: 3rem;
+}
+
+.py-16 {
+  padding-top: 4rem;
+  padding-bottom: 4rem;
+}
+
+.px-16 {
+  padding-left: 4rem;
+  padding-right: 4rem;
+}
+
+.py-20 {
+  padding-top: 5rem;
+  padding-bottom: 5rem;
+}
+
+.px-20 {
+  padding-left: 5rem;
+  padding-right: 5rem;
+}
+
+.py-24 {
+  padding-top: 6rem;
+  padding-bottom: 6rem;
+}
+
+.px-24 {
+  padding-left: 6rem;
+  padding-right: 6rem;
+}
+
+.py-32 {
+  padding-top: 8rem;
+  padding-bottom: 8rem;
+}
+
+.px-32 {
+  padding-left: 8rem;
+  padding-right: 8rem;
+}
+
+.py-40 {
+  padding-top: 10rem;
+  padding-bottom: 10rem;
+}
+
+.px-40 {
+  padding-left: 10rem;
+  padding-right: 10rem;
+}
+
+.py-48 {
+  padding-top: 12rem;
+  padding-bottom: 12rem;
+}
+
+.px-48 {
+  padding-left: 12rem;
+  padding-right: 12rem;
+}
+
+.py-56 {
+  padding-top: 14rem;
+  padding-bottom: 14rem;
+}
+
+.px-56 {
+  padding-left: 14rem;
+  padding-right: 14rem;
+}
+
+.py-64 {
+  padding-top: 16rem;
+  padding-bottom: 16rem;
+}
+
+.px-64 {
+  padding-left: 16rem;
+  padding-right: 16rem;
+}
+
+.py-px {
+  padding-top: 1px;
+  padding-bottom: 1px;
+}
+
+.px-px {
+  padding-left: 1px;
+  padding-right: 1px;
+}
+
+.pt-0 {
+  padding-top: 0;
+}
+
+.pr-0 {
+  padding-right: 0;
+}
+
+.pb-0 {
+  padding-bottom: 0;
+}
+
+.pl-0 {
+  padding-left: 0;
+}
+
+.pt-1 {
+  padding-top: 0.25rem;
+}
+
+.pr-1 {
+  padding-right: 0.25rem;
+}
+
+.pb-1 {
+  padding-bottom: 0.25rem;
+}
+
+.pl-1 {
+  padding-left: 0.25rem;
+}
+
+.pt-2 {
+  padding-top: 0.5rem;
+}
+
+.pr-2 {
+  padding-right: 0.5rem;
+}
+
+.pb-2 {
+  padding-bottom: 0.5rem;
+}
+
+.pl-2 {
+  padding-left: 0.5rem;
+}
+
+.pt-3 {
+  padding-top: 0.75rem;
+}
+
+.pr-3 {
+  padding-right: 0.75rem;
+}
+
+.pb-3 {
+  padding-bottom: 0.75rem;
+}
+
+.pl-3 {
+  padding-left: 0.75rem;
+}
+
+.pt-4 {
+  padding-top: 1rem;
+}
+
+.pr-4 {
+  padding-right: 1rem;
+}
+
+.pb-4 {
+  padding-bottom: 1rem;
+}
+
+.pl-4 {
+  padding-left: 1rem;
+}
+
+.pt-5 {
+  padding-top: 1.25rem;
+}
+
+.pr-5 {
+  padding-right: 1.25rem;
+}
+
+.pb-5 {
+  padding-bottom: 1.25rem;
+}
+
+.pl-5 {
+  padding-left: 1.25rem;
+}
+
+.pt-6 {
+  padding-top: 1.5rem;
+}
+
+.pr-6 {
+  padding-right: 1.5rem;
+}
+
+.pb-6 {
+  padding-bottom: 1.5rem;
+}
+
+.pl-6 {
+  padding-left: 1.5rem;
+}
+
+.pt-8 {
+  padding-top: 2rem;
+}
+
+.pr-8 {
+  padding-right: 2rem;
+}
+
+.pb-8 {
+  padding-bottom: 2rem;
+}
+
+.pl-8 {
+  padding-left: 2rem;
+}
+
+.pt-10 {
+  padding-top: 2.5rem;
+}
+
+.pr-10 {
+  padding-right: 2.5rem;
+}
+
+.pb-10 {
+  padding-bottom: 2.5rem;
+}
+
+.pl-10 {
+  padding-left: 2.5rem;
+}
+
+.pt-12 {
+  padding-top: 3rem;
+}
+
+.pr-12 {
+  padding-right: 3rem;
+}
+
+.pb-12 {
+  padding-bottom: 3rem;
+}
+
+.pl-12 {
+  padding-left: 3rem;
+}
+
+.pt-16 {
+  padding-top: 4rem;
+}
+
+.pr-16 {
+  padding-right: 4rem;
+}
+
+.pb-16 {
+  padding-bottom: 4rem;
+}
+
+.pl-16 {
+  padding-left: 4rem;
+}
+
+.pt-20 {
+  padding-top: 5rem;
+}
+
+.pr-20 {
+  padding-right: 5rem;
+}
+
+.pb-20 {
+  padding-bottom: 5rem;
+}
+
+.pl-20 {
+  padding-left: 5rem;
+}
+
+.pt-24 {
+  padding-top: 6rem;
+}
+
+.pr-24 {
+  padding-right: 6rem;
+}
+
+.pb-24 {
+  padding-bottom: 6rem;
+}
+
+.pl-24 {
+  padding-left: 6rem;
+}
+
+.pt-32 {
+  padding-top: 8rem;
+}
+
+.pr-32 {
+  padding-right: 8rem;
+}
+
+.pb-32 {
+  padding-bottom: 8rem;
+}
+
+.pl-32 {
+  padding-left: 8rem;
+}
+
+.pt-40 {
+  padding-top: 10rem;
+}
+
+.pr-40 {
+  padding-right: 10rem;
+}
+
+.pb-40 {
+  padding-bottom: 10rem;
+}
+
+.pl-40 {
+  padding-left: 10rem;
+}
+
+.pt-48 {
+  padding-top: 12rem;
+}
+
+.pr-48 {
+  padding-right: 12rem;
+}
+
+.pb-48 {
+  padding-bottom: 12rem;
+}
+
+.pl-48 {
+  padding-left: 12rem;
+}
+
+.pt-56 {
+  padding-top: 14rem;
+}
+
+.pr-56 {
+  padding-right: 14rem;
+}
+
+.pb-56 {
+  padding-bottom: 14rem;
+}
+
+.pl-56 {
+  padding-left: 14rem;
+}
+
+.pt-64 {
+  padding-top: 16rem;
+}
+
+.pr-64 {
+  padding-right: 16rem;
+}
+
+.pb-64 {
+  padding-bottom: 16rem;
+}
+
+.pl-64 {
+  padding-left: 16rem;
+}
+
+.pt-px {
+  padding-top: 1px;
+}
+
+.pr-px {
+  padding-right: 1px;
+}
+
+.pb-px {
+  padding-bottom: 1px;
+}
+
+.pl-px {
+  padding-left: 1px;
+}
+
+.placeholder-transparent::-moz-placeholder {
+  color: transparent;
+}
+
+.placeholder-transparent:-ms-input-placeholder {
+  color: transparent;
+}
+
+.placeholder-transparent::placeholder {
+  color: transparent;
+}
+
+.placeholder-current::-moz-placeholder {
+  color: currentColor;
+}
+
+.placeholder-current:-ms-input-placeholder {
+  color: currentColor;
+}
+
+.placeholder-current::placeholder {
+  color: currentColor;
+}
+
+.placeholder-black::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.placeholder-black:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.placeholder-black::placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.placeholder-white::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.placeholder-white:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.placeholder-white::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.placeholder-gray-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.placeholder-gray-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.placeholder-gray-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.placeholder-gray-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.placeholder-gray-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.placeholder-gray-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.placeholder-gray-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.placeholder-gray-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.placeholder-gray-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.placeholder-gray-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.placeholder-gray-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.placeholder-gray-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.placeholder-gray-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.placeholder-gray-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.placeholder-gray-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.placeholder-gray-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.placeholder-gray-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.placeholder-gray-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.placeholder-gray-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.placeholder-gray-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.placeholder-gray-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.placeholder-gray-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.placeholder-gray-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.placeholder-gray-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.placeholder-gray-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.placeholder-gray-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.placeholder-gray-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.placeholder-red-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.placeholder-red-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.placeholder-red-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.placeholder-red-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.placeholder-red-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.placeholder-red-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.placeholder-red-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.placeholder-red-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.placeholder-red-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.placeholder-red-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.placeholder-red-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.placeholder-red-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.placeholder-red-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.placeholder-red-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.placeholder-red-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.placeholder-red-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.placeholder-red-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.placeholder-red-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.placeholder-red-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.placeholder-red-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.placeholder-red-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.placeholder-red-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.placeholder-red-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.placeholder-red-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.placeholder-red-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.placeholder-red-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.placeholder-red-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.placeholder-orange-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.placeholder-orange-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.placeholder-orange-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.placeholder-orange-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.placeholder-orange-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.placeholder-orange-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.placeholder-orange-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.placeholder-orange-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.placeholder-orange-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.placeholder-orange-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.placeholder-orange-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.placeholder-orange-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.placeholder-orange-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.placeholder-orange-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.placeholder-orange-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.placeholder-orange-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.placeholder-orange-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.placeholder-orange-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.placeholder-orange-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.placeholder-orange-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.placeholder-orange-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.placeholder-orange-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.placeholder-yellow-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.placeholder-green-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.placeholder-green-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.placeholder-green-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.placeholder-green-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.placeholder-green-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.placeholder-green-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.placeholder-green-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.placeholder-green-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.placeholder-green-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.placeholder-green-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.placeholder-green-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.placeholder-green-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.placeholder-green-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.placeholder-green-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.placeholder-green-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.placeholder-green-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.placeholder-green-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.placeholder-green-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.placeholder-green-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.placeholder-green-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.placeholder-green-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.placeholder-green-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.placeholder-green-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.placeholder-green-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.placeholder-green-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.placeholder-green-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.placeholder-green-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.placeholder-teal-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.placeholder-teal-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.placeholder-teal-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.placeholder-teal-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.placeholder-teal-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.placeholder-teal-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.placeholder-teal-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.placeholder-teal-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.placeholder-teal-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.placeholder-teal-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.placeholder-teal-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.placeholder-teal-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.placeholder-teal-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.placeholder-teal-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.placeholder-teal-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.placeholder-teal-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.placeholder-teal-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.placeholder-teal-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.placeholder-teal-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.placeholder-teal-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.placeholder-teal-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.placeholder-teal-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.placeholder-teal-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.placeholder-teal-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.placeholder-teal-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.placeholder-teal-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.placeholder-teal-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.placeholder-blue-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.placeholder-blue-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.placeholder-blue-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.placeholder-blue-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.placeholder-blue-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.placeholder-blue-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.placeholder-blue-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.placeholder-blue-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.placeholder-blue-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.placeholder-blue-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.placeholder-blue-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.placeholder-blue-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.placeholder-blue-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.placeholder-blue-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.placeholder-blue-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.placeholder-blue-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.placeholder-blue-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.placeholder-blue-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.placeholder-blue-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.placeholder-blue-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.placeholder-blue-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.placeholder-blue-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.placeholder-blue-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.placeholder-blue-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.placeholder-blue-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.placeholder-blue-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.placeholder-blue-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.placeholder-indigo-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.placeholder-purple-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.placeholder-purple-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.placeholder-purple-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.placeholder-purple-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.placeholder-purple-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.placeholder-purple-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.placeholder-purple-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.placeholder-purple-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.placeholder-purple-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.placeholder-purple-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.placeholder-purple-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.placeholder-purple-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.placeholder-purple-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.placeholder-purple-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.placeholder-purple-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.placeholder-purple-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.placeholder-purple-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.placeholder-purple-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.placeholder-purple-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.placeholder-purple-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.placeholder-purple-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.placeholder-purple-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.placeholder-purple-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.placeholder-purple-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.placeholder-purple-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.placeholder-purple-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.placeholder-purple-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.placeholder-pink-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.placeholder-pink-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.placeholder-pink-100::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.placeholder-pink-200::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.placeholder-pink-200:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.placeholder-pink-200::placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.placeholder-pink-300::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.placeholder-pink-300:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.placeholder-pink-300::placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.placeholder-pink-400::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.placeholder-pink-400:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.placeholder-pink-400::placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.placeholder-pink-500::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.placeholder-pink-500:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.placeholder-pink-500::placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.placeholder-pink-600::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.placeholder-pink-600:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.placeholder-pink-600::placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.placeholder-pink-700::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.placeholder-pink-700:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.placeholder-pink-700::placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.placeholder-pink-800::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.placeholder-pink-800:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.placeholder-pink-800::placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.placeholder-pink-900::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.placeholder-pink-900:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.placeholder-pink-900::placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-transparent:focus::-moz-placeholder {
+  color: transparent;
+}
+
+.focus\:placeholder-transparent:focus:-ms-input-placeholder {
+  color: transparent;
+}
+
+.focus\:placeholder-transparent:focus::placeholder {
+  color: transparent;
+}
+
+.focus\:placeholder-current:focus::-moz-placeholder {
+  color: currentColor;
+}
+
+.focus\:placeholder-current:focus:-ms-input-placeholder {
+  color: currentColor;
+}
+
+.focus\:placeholder-current:focus::placeholder {
+  color: currentColor;
+}
+
+.focus\:placeholder-black:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-black:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-black:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-white:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-white:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-white:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-gray-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-red-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-orange-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-yellow-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-green-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-teal-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-blue-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-indigo-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-purple-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-100:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-200:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-200:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-300:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-300:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-400:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-400:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-500:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-500:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-600:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-600:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-700:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-700:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-800:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-800:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-900:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.focus\:placeholder-pink-900:focus::placeholder {
+  --placeholder-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--placeholder-opacity));
+}
+
+.placeholder-opacity-0::-moz-placeholder {
+  --placeholder-opacity: 0;
+}
+
+.placeholder-opacity-0:-ms-input-placeholder {
+  --placeholder-opacity: 0;
+}
+
+.placeholder-opacity-0::placeholder {
+  --placeholder-opacity: 0;
+}
+
+.placeholder-opacity-25::-moz-placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.placeholder-opacity-25:-ms-input-placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.placeholder-opacity-25::placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.placeholder-opacity-50::-moz-placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.placeholder-opacity-50:-ms-input-placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.placeholder-opacity-50::placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.placeholder-opacity-75::-moz-placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.placeholder-opacity-75:-ms-input-placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.placeholder-opacity-75::placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.placeholder-opacity-100::-moz-placeholder {
+  --placeholder-opacity: 1;
+}
+
+.placeholder-opacity-100:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+}
+
+.placeholder-opacity-100::placeholder {
+  --placeholder-opacity: 1;
+}
+
+.focus\:placeholder-opacity-0:focus::-moz-placeholder {
+  --placeholder-opacity: 0;
+}
+
+.focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+  --placeholder-opacity: 0;
+}
+
+.focus\:placeholder-opacity-0:focus::placeholder {
+  --placeholder-opacity: 0;
+}
+
+.focus\:placeholder-opacity-25:focus::-moz-placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.focus\:placeholder-opacity-25:focus::placeholder {
+  --placeholder-opacity: 0.25;
+}
+
+.focus\:placeholder-opacity-50:focus::-moz-placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.focus\:placeholder-opacity-50:focus::placeholder {
+  --placeholder-opacity: 0.5;
+}
+
+.focus\:placeholder-opacity-75:focus::-moz-placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.focus\:placeholder-opacity-75:focus::placeholder {
+  --placeholder-opacity: 0.75;
+}
+
+.focus\:placeholder-opacity-100:focus::-moz-placeholder {
+  --placeholder-opacity: 1;
+}
+
+.focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+  --placeholder-opacity: 1;
+}
+
+.focus\:placeholder-opacity-100:focus::placeholder {
+  --placeholder-opacity: 1;
+}
+
+.pointer-events-none {
+  pointer-events: none;
+}
+
+.pointer-events-auto {
+  pointer-events: auto;
+}
+
+.static {
+  position: static;
+}
+
+.fixed {
+  position: fixed;
+}
+
+.absolute {
+  position: absolute;
+}
+
+.relative {
+  position: relative;
+}
+
+.sticky {
+  position: -webkit-sticky;
+  position: sticky;
+}
+
+.inset-0 {
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+}
+
+.inset-auto {
+  top: auto;
+  right: auto;
+  bottom: auto;
+  left: auto;
+}
+
+.inset-y-0 {
+  top: 0;
+  bottom: 0;
+}
+
+.inset-x-0 {
+  right: 0;
+  left: 0;
+}
+
+.inset-y-auto {
+  top: auto;
+  bottom: auto;
+}
+
+.inset-x-auto {
+  right: auto;
+  left: auto;
+}
+
+.top-0 {
+  top: 0;
+}
+
+.right-0 {
+  right: 0;
+}
+
+.bottom-0 {
+  bottom: 0;
+}
+
+.left-0 {
+  left: 0;
+}
+
+.top-auto {
+  top: auto;
+}
+
+.right-auto {
+  right: auto;
+}
+
+.bottom-auto {
+  bottom: auto;
+}
+
+.left-auto {
+  left: auto;
+}
+
+.resize-none {
+  resize: none;
+}
+
+.resize-y {
+  resize: vertical;
+}
+
+.resize-x {
+  resize: horizontal;
+}
+
+.resize {
+  resize: both;
+}
+
+.shadow-xs {
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+}
+
+.shadow-sm {
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+}
+
+.shadow {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+}
+
+.shadow-md {
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+}
+
+.shadow-lg {
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+}
+
+.shadow-xl {
+  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+.shadow-2xl {
+  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+}
+
+.shadow-inner {
+  box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+}
+
+.shadow-outline {
+  box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+}
+
+.shadow-none {
+  box-shadow: none;
+}
+
+.hover\:shadow-xs:hover {
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+}
+
+.hover\:shadow-sm:hover {
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+}
+
+.hover\:shadow:hover {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+}
+
+.hover\:shadow-md:hover {
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+}
+
+.hover\:shadow-lg:hover {
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+}
+
+.hover\:shadow-xl:hover {
+  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+.hover\:shadow-2xl:hover {
+  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+}
+
+.hover\:shadow-inner:hover {
+  box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+}
+
+.hover\:shadow-outline:hover {
+  box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+}
+
+.hover\:shadow-none:hover {
+  box-shadow: none;
+}
+
+.focus\:shadow-xs:focus {
+  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+}
+
+.focus\:shadow-sm:focus {
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+}
+
+.focus\:shadow:focus {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+}
+
+.focus\:shadow-md:focus {
+  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+}
+
+.focus\:shadow-lg:focus {
+  box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+}
+
+.focus\:shadow-xl:focus {
+  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+}
+
+.focus\:shadow-2xl:focus {
+  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+}
+
+.focus\:shadow-inner:focus {
+  box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+}
+
+.focus\:shadow-outline:focus {
+  box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+}
+
+.focus\:shadow-none:focus {
+  box-shadow: none;
+}
+
+.fill-current {
+  fill: currentColor;
+}
+
+.stroke-current {
+  stroke: currentColor;
+}
+
+.stroke-0 {
+  stroke-width: 0;
+}
+
+.stroke-1 {
+  stroke-width: 1;
+}
+
+.stroke-2 {
+  stroke-width: 2;
+}
+
+.table-auto {
+  table-layout: auto;
+}
+
+.table-fixed {
+  table-layout: fixed;
+}
+
+.text-left {
+  text-align: left;
+}
+
+.text-center {
+  text-align: center;
+}
+
+.text-right {
+  text-align: right;
+}
+
+.text-justify {
+  text-align: justify;
+}
+
+.text-transparent {
+  color: transparent;
+}
+
+.text-current {
+  color: currentColor;
+}
+
+.text-black {
+  --text-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--text-opacity));
+}
+
+.text-white {
+  --text-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--text-opacity));
+}
+
+.text-gray-100 {
+  --text-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--text-opacity));
+}
+
+.text-gray-200 {
+  --text-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--text-opacity));
+}
+
+.text-gray-300 {
+  --text-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--text-opacity));
+}
+
+.text-gray-400 {
+  --text-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--text-opacity));
+}
+
+.text-gray-500 {
+  --text-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--text-opacity));
+}
+
+.text-gray-600 {
+  --text-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--text-opacity));
+}
+
+.text-gray-700 {
+  --text-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--text-opacity));
+}
+
+.text-gray-800 {
+  --text-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--text-opacity));
+}
+
+.text-gray-900 {
+  --text-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--text-opacity));
+}
+
+.text-red-100 {
+  --text-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--text-opacity));
+}
+
+.text-red-200 {
+  --text-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--text-opacity));
+}
+
+.text-red-300 {
+  --text-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--text-opacity));
+}
+
+.text-red-400 {
+  --text-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--text-opacity));
+}
+
+.text-red-500 {
+  --text-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--text-opacity));
+}
+
+.text-red-600 {
+  --text-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--text-opacity));
+}
+
+.text-red-700 {
+  --text-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--text-opacity));
+}
+
+.text-red-800 {
+  --text-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--text-opacity));
+}
+
+.text-red-900 {
+  --text-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--text-opacity));
+}
+
+.text-orange-100 {
+  --text-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--text-opacity));
+}
+
+.text-orange-200 {
+  --text-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--text-opacity));
+}
+
+.text-orange-300 {
+  --text-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--text-opacity));
+}
+
+.text-orange-400 {
+  --text-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--text-opacity));
+}
+
+.text-orange-500 {
+  --text-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--text-opacity));
+}
+
+.text-orange-600 {
+  --text-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--text-opacity));
+}
+
+.text-orange-700 {
+  --text-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--text-opacity));
+}
+
+.text-orange-800 {
+  --text-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--text-opacity));
+}
+
+.text-orange-900 {
+  --text-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--text-opacity));
+}
+
+.text-yellow-100 {
+  --text-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--text-opacity));
+}
+
+.text-yellow-200 {
+  --text-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--text-opacity));
+}
+
+.text-yellow-300 {
+  --text-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--text-opacity));
+}
+
+.text-yellow-400 {
+  --text-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--text-opacity));
+}
+
+.text-yellow-500 {
+  --text-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--text-opacity));
+}
+
+.text-yellow-600 {
+  --text-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--text-opacity));
+}
+
+.text-yellow-700 {
+  --text-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--text-opacity));
+}
+
+.text-yellow-800 {
+  --text-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--text-opacity));
+}
+
+.text-yellow-900 {
+  --text-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--text-opacity));
+}
+
+.text-green-100 {
+  --text-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--text-opacity));
+}
+
+.text-green-200 {
+  --text-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--text-opacity));
+}
+
+.text-green-300 {
+  --text-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--text-opacity));
+}
+
+.text-green-400 {
+  --text-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--text-opacity));
+}
+
+.text-green-500 {
+  --text-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--text-opacity));
+}
+
+.text-green-600 {
+  --text-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--text-opacity));
+}
+
+.text-green-700 {
+  --text-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--text-opacity));
+}
+
+.text-green-800 {
+  --text-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--text-opacity));
+}
+
+.text-green-900 {
+  --text-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--text-opacity));
+}
+
+.text-teal-100 {
+  --text-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--text-opacity));
+}
+
+.text-teal-200 {
+  --text-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--text-opacity));
+}
+
+.text-teal-300 {
+  --text-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--text-opacity));
+}
+
+.text-teal-400 {
+  --text-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--text-opacity));
+}
+
+.text-teal-500 {
+  --text-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--text-opacity));
+}
+
+.text-teal-600 {
+  --text-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--text-opacity));
+}
+
+.text-teal-700 {
+  --text-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--text-opacity));
+}
+
+.text-teal-800 {
+  --text-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--text-opacity));
+}
+
+.text-teal-900 {
+  --text-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--text-opacity));
+}
+
+.text-blue-100 {
+  --text-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--text-opacity));
+}
+
+.text-blue-200 {
+  --text-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--text-opacity));
+}
+
+.text-blue-300 {
+  --text-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--text-opacity));
+}
+
+.text-blue-400 {
+  --text-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--text-opacity));
+}
+
+.text-blue-500 {
+  --text-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--text-opacity));
+}
+
+.text-blue-600 {
+  --text-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--text-opacity));
+}
+
+.text-blue-700 {
+  --text-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--text-opacity));
+}
+
+.text-blue-800 {
+  --text-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--text-opacity));
+}
+
+.text-blue-900 {
+  --text-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--text-opacity));
+}
+
+.text-indigo-100 {
+  --text-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--text-opacity));
+}
+
+.text-indigo-200 {
+  --text-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--text-opacity));
+}
+
+.text-indigo-300 {
+  --text-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--text-opacity));
+}
+
+.text-indigo-400 {
+  --text-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--text-opacity));
+}
+
+.text-indigo-500 {
+  --text-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--text-opacity));
+}
+
+.text-indigo-600 {
+  --text-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--text-opacity));
+}
+
+.text-indigo-700 {
+  --text-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--text-opacity));
+}
+
+.text-indigo-800 {
+  --text-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--text-opacity));
+}
+
+.text-indigo-900 {
+  --text-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--text-opacity));
+}
+
+.text-purple-100 {
+  --text-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--text-opacity));
+}
+
+.text-purple-200 {
+  --text-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--text-opacity));
+}
+
+.text-purple-300 {
+  --text-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--text-opacity));
+}
+
+.text-purple-400 {
+  --text-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--text-opacity));
+}
+
+.text-purple-500 {
+  --text-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--text-opacity));
+}
+
+.text-purple-600 {
+  --text-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--text-opacity));
+}
+
+.text-purple-700 {
+  --text-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--text-opacity));
+}
+
+.text-purple-800 {
+  --text-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--text-opacity));
+}
+
+.text-purple-900 {
+  --text-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--text-opacity));
+}
+
+.text-pink-100 {
+  --text-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--text-opacity));
+}
+
+.text-pink-200 {
+  --text-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--text-opacity));
+}
+
+.text-pink-300 {
+  --text-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--text-opacity));
+}
+
+.text-pink-400 {
+  --text-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--text-opacity));
+}
+
+.text-pink-500 {
+  --text-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--text-opacity));
+}
+
+.text-pink-600 {
+  --text-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--text-opacity));
+}
+
+.text-pink-700 {
+  --text-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--text-opacity));
+}
+
+.text-pink-800 {
+  --text-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--text-opacity));
+}
+
+.text-pink-900 {
+  --text-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--text-opacity));
+}
+
+.hover\:text-transparent:hover {
+  color: transparent;
+}
+
+.hover\:text-current:hover {
+  color: currentColor;
+}
+
+.hover\:text-black:hover {
+  --text-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--text-opacity));
+}
+
+.hover\:text-white:hover {
+  --text-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--text-opacity));
+}
+
+.hover\:text-gray-100:hover {
+  --text-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--text-opacity));
+}
+
+.hover\:text-gray-200:hover {
+  --text-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--text-opacity));
+}
+
+.hover\:text-gray-300:hover {
+  --text-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--text-opacity));
+}
+
+.hover\:text-gray-400:hover {
+  --text-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--text-opacity));
+}
+
+.hover\:text-gray-500:hover {
+  --text-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--text-opacity));
+}
+
+.hover\:text-gray-600:hover {
+  --text-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--text-opacity));
+}
+
+.hover\:text-gray-700:hover {
+  --text-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--text-opacity));
+}
+
+.hover\:text-gray-800:hover {
+  --text-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--text-opacity));
+}
+
+.hover\:text-gray-900:hover {
+  --text-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--text-opacity));
+}
+
+.hover\:text-red-100:hover {
+  --text-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--text-opacity));
+}
+
+.hover\:text-red-200:hover {
+  --text-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--text-opacity));
+}
+
+.hover\:text-red-300:hover {
+  --text-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--text-opacity));
+}
+
+.hover\:text-red-400:hover {
+  --text-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--text-opacity));
+}
+
+.hover\:text-red-500:hover {
+  --text-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--text-opacity));
+}
+
+.hover\:text-red-600:hover {
+  --text-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--text-opacity));
+}
+
+.hover\:text-red-700:hover {
+  --text-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--text-opacity));
+}
+
+.hover\:text-red-800:hover {
+  --text-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--text-opacity));
+}
+
+.hover\:text-red-900:hover {
+  --text-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--text-opacity));
+}
+
+.hover\:text-orange-100:hover {
+  --text-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--text-opacity));
+}
+
+.hover\:text-orange-200:hover {
+  --text-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--text-opacity));
+}
+
+.hover\:text-orange-300:hover {
+  --text-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--text-opacity));
+}
+
+.hover\:text-orange-400:hover {
+  --text-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--text-opacity));
+}
+
+.hover\:text-orange-500:hover {
+  --text-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--text-opacity));
+}
+
+.hover\:text-orange-600:hover {
+  --text-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--text-opacity));
+}
+
+.hover\:text-orange-700:hover {
+  --text-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--text-opacity));
+}
+
+.hover\:text-orange-800:hover {
+  --text-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--text-opacity));
+}
+
+.hover\:text-orange-900:hover {
+  --text-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--text-opacity));
+}
+
+.hover\:text-yellow-100:hover {
+  --text-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--text-opacity));
+}
+
+.hover\:text-yellow-200:hover {
+  --text-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--text-opacity));
+}
+
+.hover\:text-yellow-300:hover {
+  --text-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--text-opacity));
+}
+
+.hover\:text-yellow-400:hover {
+  --text-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--text-opacity));
+}
+
+.hover\:text-yellow-500:hover {
+  --text-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--text-opacity));
+}
+
+.hover\:text-yellow-600:hover {
+  --text-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--text-opacity));
+}
+
+.hover\:text-yellow-700:hover {
+  --text-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--text-opacity));
+}
+
+.hover\:text-yellow-800:hover {
+  --text-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--text-opacity));
+}
+
+.hover\:text-yellow-900:hover {
+  --text-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--text-opacity));
+}
+
+.hover\:text-green-100:hover {
+  --text-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--text-opacity));
+}
+
+.hover\:text-green-200:hover {
+  --text-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--text-opacity));
+}
+
+.hover\:text-green-300:hover {
+  --text-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--text-opacity));
+}
+
+.hover\:text-green-400:hover {
+  --text-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--text-opacity));
+}
+
+.hover\:text-green-500:hover {
+  --text-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--text-opacity));
+}
+
+.hover\:text-green-600:hover {
+  --text-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--text-opacity));
+}
+
+.hover\:text-green-700:hover {
+  --text-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--text-opacity));
+}
+
+.hover\:text-green-800:hover {
+  --text-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--text-opacity));
+}
+
+.hover\:text-green-900:hover {
+  --text-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--text-opacity));
+}
+
+.hover\:text-teal-100:hover {
+  --text-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--text-opacity));
+}
+
+.hover\:text-teal-200:hover {
+  --text-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--text-opacity));
+}
+
+.hover\:text-teal-300:hover {
+  --text-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--text-opacity));
+}
+
+.hover\:text-teal-400:hover {
+  --text-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--text-opacity));
+}
+
+.hover\:text-teal-500:hover {
+  --text-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--text-opacity));
+}
+
+.hover\:text-teal-600:hover {
+  --text-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--text-opacity));
+}
+
+.hover\:text-teal-700:hover {
+  --text-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--text-opacity));
+}
+
+.hover\:text-teal-800:hover {
+  --text-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--text-opacity));
+}
+
+.hover\:text-teal-900:hover {
+  --text-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--text-opacity));
+}
+
+.hover\:text-blue-100:hover {
+  --text-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--text-opacity));
+}
+
+.hover\:text-blue-200:hover {
+  --text-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--text-opacity));
+}
+
+.hover\:text-blue-300:hover {
+  --text-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--text-opacity));
+}
+
+.hover\:text-blue-400:hover {
+  --text-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--text-opacity));
+}
+
+.hover\:text-blue-500:hover {
+  --text-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--text-opacity));
+}
+
+.hover\:text-blue-600:hover {
+  --text-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--text-opacity));
+}
+
+.hover\:text-blue-700:hover {
+  --text-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--text-opacity));
+}
+
+.hover\:text-blue-800:hover {
+  --text-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--text-opacity));
+}
+
+.hover\:text-blue-900:hover {
+  --text-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--text-opacity));
+}
+
+.hover\:text-indigo-100:hover {
+  --text-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--text-opacity));
+}
+
+.hover\:text-indigo-200:hover {
+  --text-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--text-opacity));
+}
+
+.hover\:text-indigo-300:hover {
+  --text-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--text-opacity));
+}
+
+.hover\:text-indigo-400:hover {
+  --text-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--text-opacity));
+}
+
+.hover\:text-indigo-500:hover {
+  --text-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--text-opacity));
+}
+
+.hover\:text-indigo-600:hover {
+  --text-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--text-opacity));
+}
+
+.hover\:text-indigo-700:hover {
+  --text-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--text-opacity));
+}
+
+.hover\:text-indigo-800:hover {
+  --text-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--text-opacity));
+}
+
+.hover\:text-indigo-900:hover {
+  --text-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--text-opacity));
+}
+
+.hover\:text-purple-100:hover {
+  --text-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--text-opacity));
+}
+
+.hover\:text-purple-200:hover {
+  --text-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--text-opacity));
+}
+
+.hover\:text-purple-300:hover {
+  --text-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--text-opacity));
+}
+
+.hover\:text-purple-400:hover {
+  --text-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--text-opacity));
+}
+
+.hover\:text-purple-500:hover {
+  --text-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--text-opacity));
+}
+
+.hover\:text-purple-600:hover {
+  --text-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--text-opacity));
+}
+
+.hover\:text-purple-700:hover {
+  --text-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--text-opacity));
+}
+
+.hover\:text-purple-800:hover {
+  --text-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--text-opacity));
+}
+
+.hover\:text-purple-900:hover {
+  --text-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--text-opacity));
+}
+
+.hover\:text-pink-100:hover {
+  --text-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--text-opacity));
+}
+
+.hover\:text-pink-200:hover {
+  --text-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--text-opacity));
+}
+
+.hover\:text-pink-300:hover {
+  --text-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--text-opacity));
+}
+
+.hover\:text-pink-400:hover {
+  --text-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--text-opacity));
+}
+
+.hover\:text-pink-500:hover {
+  --text-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--text-opacity));
+}
+
+.hover\:text-pink-600:hover {
+  --text-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--text-opacity));
+}
+
+.hover\:text-pink-700:hover {
+  --text-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--text-opacity));
+}
+
+.hover\:text-pink-800:hover {
+  --text-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--text-opacity));
+}
+
+.hover\:text-pink-900:hover {
+  --text-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--text-opacity));
+}
+
+.focus\:text-transparent:focus {
+  color: transparent;
+}
+
+.focus\:text-current:focus {
+  color: currentColor;
+}
+
+.focus\:text-black:focus {
+  --text-opacity: 1;
+  color: #000;
+  color: rgba(0, 0, 0, var(--text-opacity));
+}
+
+.focus\:text-white:focus {
+  --text-opacity: 1;
+  color: #fff;
+  color: rgba(255, 255, 255, var(--text-opacity));
+}
+
+.focus\:text-gray-100:focus {
+  --text-opacity: 1;
+  color: #f7fafc;
+  color: rgba(247, 250, 252, var(--text-opacity));
+}
+
+.focus\:text-gray-200:focus {
+  --text-opacity: 1;
+  color: #edf2f7;
+  color: rgba(237, 242, 247, var(--text-opacity));
+}
+
+.focus\:text-gray-300:focus {
+  --text-opacity: 1;
+  color: #e2e8f0;
+  color: rgba(226, 232, 240, var(--text-opacity));
+}
+
+.focus\:text-gray-400:focus {
+  --text-opacity: 1;
+  color: #cbd5e0;
+  color: rgba(203, 213, 224, var(--text-opacity));
+}
+
+.focus\:text-gray-500:focus {
+  --text-opacity: 1;
+  color: #a0aec0;
+  color: rgba(160, 174, 192, var(--text-opacity));
+}
+
+.focus\:text-gray-600:focus {
+  --text-opacity: 1;
+  color: #718096;
+  color: rgba(113, 128, 150, var(--text-opacity));
+}
+
+.focus\:text-gray-700:focus {
+  --text-opacity: 1;
+  color: #4a5568;
+  color: rgba(74, 85, 104, var(--text-opacity));
+}
+
+.focus\:text-gray-800:focus {
+  --text-opacity: 1;
+  color: #2d3748;
+  color: rgba(45, 55, 72, var(--text-opacity));
+}
+
+.focus\:text-gray-900:focus {
+  --text-opacity: 1;
+  color: #1a202c;
+  color: rgba(26, 32, 44, var(--text-opacity));
+}
+
+.focus\:text-red-100:focus {
+  --text-opacity: 1;
+  color: #fff5f5;
+  color: rgba(255, 245, 245, var(--text-opacity));
+}
+
+.focus\:text-red-200:focus {
+  --text-opacity: 1;
+  color: #fed7d7;
+  color: rgba(254, 215, 215, var(--text-opacity));
+}
+
+.focus\:text-red-300:focus {
+  --text-opacity: 1;
+  color: #feb2b2;
+  color: rgba(254, 178, 178, var(--text-opacity));
+}
+
+.focus\:text-red-400:focus {
+  --text-opacity: 1;
+  color: #fc8181;
+  color: rgba(252, 129, 129, var(--text-opacity));
+}
+
+.focus\:text-red-500:focus {
+  --text-opacity: 1;
+  color: #f56565;
+  color: rgba(245, 101, 101, var(--text-opacity));
+}
+
+.focus\:text-red-600:focus {
+  --text-opacity: 1;
+  color: #e53e3e;
+  color: rgba(229, 62, 62, var(--text-opacity));
+}
+
+.focus\:text-red-700:focus {
+  --text-opacity: 1;
+  color: #c53030;
+  color: rgba(197, 48, 48, var(--text-opacity));
+}
+
+.focus\:text-red-800:focus {
+  --text-opacity: 1;
+  color: #9b2c2c;
+  color: rgba(155, 44, 44, var(--text-opacity));
+}
+
+.focus\:text-red-900:focus {
+  --text-opacity: 1;
+  color: #742a2a;
+  color: rgba(116, 42, 42, var(--text-opacity));
+}
+
+.focus\:text-orange-100:focus {
+  --text-opacity: 1;
+  color: #fffaf0;
+  color: rgba(255, 250, 240, var(--text-opacity));
+}
+
+.focus\:text-orange-200:focus {
+  --text-opacity: 1;
+  color: #feebc8;
+  color: rgba(254, 235, 200, var(--text-opacity));
+}
+
+.focus\:text-orange-300:focus {
+  --text-opacity: 1;
+  color: #fbd38d;
+  color: rgba(251, 211, 141, var(--text-opacity));
+}
+
+.focus\:text-orange-400:focus {
+  --text-opacity: 1;
+  color: #f6ad55;
+  color: rgba(246, 173, 85, var(--text-opacity));
+}
+
+.focus\:text-orange-500:focus {
+  --text-opacity: 1;
+  color: #ed8936;
+  color: rgba(237, 137, 54, var(--text-opacity));
+}
+
+.focus\:text-orange-600:focus {
+  --text-opacity: 1;
+  color: #dd6b20;
+  color: rgba(221, 107, 32, var(--text-opacity));
+}
+
+.focus\:text-orange-700:focus {
+  --text-opacity: 1;
+  color: #c05621;
+  color: rgba(192, 86, 33, var(--text-opacity));
+}
+
+.focus\:text-orange-800:focus {
+  --text-opacity: 1;
+  color: #9c4221;
+  color: rgba(156, 66, 33, var(--text-opacity));
+}
+
+.focus\:text-orange-900:focus {
+  --text-opacity: 1;
+  color: #7b341e;
+  color: rgba(123, 52, 30, var(--text-opacity));
+}
+
+.focus\:text-yellow-100:focus {
+  --text-opacity: 1;
+  color: #fffff0;
+  color: rgba(255, 255, 240, var(--text-opacity));
+}
+
+.focus\:text-yellow-200:focus {
+  --text-opacity: 1;
+  color: #fefcbf;
+  color: rgba(254, 252, 191, var(--text-opacity));
+}
+
+.focus\:text-yellow-300:focus {
+  --text-opacity: 1;
+  color: #faf089;
+  color: rgba(250, 240, 137, var(--text-opacity));
+}
+
+.focus\:text-yellow-400:focus {
+  --text-opacity: 1;
+  color: #f6e05e;
+  color: rgba(246, 224, 94, var(--text-opacity));
+}
+
+.focus\:text-yellow-500:focus {
+  --text-opacity: 1;
+  color: #ecc94b;
+  color: rgba(236, 201, 75, var(--text-opacity));
+}
+
+.focus\:text-yellow-600:focus {
+  --text-opacity: 1;
+  color: #d69e2e;
+  color: rgba(214, 158, 46, var(--text-opacity));
+}
+
+.focus\:text-yellow-700:focus {
+  --text-opacity: 1;
+  color: #b7791f;
+  color: rgba(183, 121, 31, var(--text-opacity));
+}
+
+.focus\:text-yellow-800:focus {
+  --text-opacity: 1;
+  color: #975a16;
+  color: rgba(151, 90, 22, var(--text-opacity));
+}
+
+.focus\:text-yellow-900:focus {
+  --text-opacity: 1;
+  color: #744210;
+  color: rgba(116, 66, 16, var(--text-opacity));
+}
+
+.focus\:text-green-100:focus {
+  --text-opacity: 1;
+  color: #f0fff4;
+  color: rgba(240, 255, 244, var(--text-opacity));
+}
+
+.focus\:text-green-200:focus {
+  --text-opacity: 1;
+  color: #c6f6d5;
+  color: rgba(198, 246, 213, var(--text-opacity));
+}
+
+.focus\:text-green-300:focus {
+  --text-opacity: 1;
+  color: #9ae6b4;
+  color: rgba(154, 230, 180, var(--text-opacity));
+}
+
+.focus\:text-green-400:focus {
+  --text-opacity: 1;
+  color: #68d391;
+  color: rgba(104, 211, 145, var(--text-opacity));
+}
+
+.focus\:text-green-500:focus {
+  --text-opacity: 1;
+  color: #48bb78;
+  color: rgba(72, 187, 120, var(--text-opacity));
+}
+
+.focus\:text-green-600:focus {
+  --text-opacity: 1;
+  color: #38a169;
+  color: rgba(56, 161, 105, var(--text-opacity));
+}
+
+.focus\:text-green-700:focus {
+  --text-opacity: 1;
+  color: #2f855a;
+  color: rgba(47, 133, 90, var(--text-opacity));
+}
+
+.focus\:text-green-800:focus {
+  --text-opacity: 1;
+  color: #276749;
+  color: rgba(39, 103, 73, var(--text-opacity));
+}
+
+.focus\:text-green-900:focus {
+  --text-opacity: 1;
+  color: #22543d;
+  color: rgba(34, 84, 61, var(--text-opacity));
+}
+
+.focus\:text-teal-100:focus {
+  --text-opacity: 1;
+  color: #e6fffa;
+  color: rgba(230, 255, 250, var(--text-opacity));
+}
+
+.focus\:text-teal-200:focus {
+  --text-opacity: 1;
+  color: #b2f5ea;
+  color: rgba(178, 245, 234, var(--text-opacity));
+}
+
+.focus\:text-teal-300:focus {
+  --text-opacity: 1;
+  color: #81e6d9;
+  color: rgba(129, 230, 217, var(--text-opacity));
+}
+
+.focus\:text-teal-400:focus {
+  --text-opacity: 1;
+  color: #4fd1c5;
+  color: rgba(79, 209, 197, var(--text-opacity));
+}
+
+.focus\:text-teal-500:focus {
+  --text-opacity: 1;
+  color: #38b2ac;
+  color: rgba(56, 178, 172, var(--text-opacity));
+}
+
+.focus\:text-teal-600:focus {
+  --text-opacity: 1;
+  color: #319795;
+  color: rgba(49, 151, 149, var(--text-opacity));
+}
+
+.focus\:text-teal-700:focus {
+  --text-opacity: 1;
+  color: #2c7a7b;
+  color: rgba(44, 122, 123, var(--text-opacity));
+}
+
+.focus\:text-teal-800:focus {
+  --text-opacity: 1;
+  color: #285e61;
+  color: rgba(40, 94, 97, var(--text-opacity));
+}
+
+.focus\:text-teal-900:focus {
+  --text-opacity: 1;
+  color: #234e52;
+  color: rgba(35, 78, 82, var(--text-opacity));
+}
+
+.focus\:text-blue-100:focus {
+  --text-opacity: 1;
+  color: #ebf8ff;
+  color: rgba(235, 248, 255, var(--text-opacity));
+}
+
+.focus\:text-blue-200:focus {
+  --text-opacity: 1;
+  color: #bee3f8;
+  color: rgba(190, 227, 248, var(--text-opacity));
+}
+
+.focus\:text-blue-300:focus {
+  --text-opacity: 1;
+  color: #90cdf4;
+  color: rgba(144, 205, 244, var(--text-opacity));
+}
+
+.focus\:text-blue-400:focus {
+  --text-opacity: 1;
+  color: #63b3ed;
+  color: rgba(99, 179, 237, var(--text-opacity));
+}
+
+.focus\:text-blue-500:focus {
+  --text-opacity: 1;
+  color: #4299e1;
+  color: rgba(66, 153, 225, var(--text-opacity));
+}
+
+.focus\:text-blue-600:focus {
+  --text-opacity: 1;
+  color: #3182ce;
+  color: rgba(49, 130, 206, var(--text-opacity));
+}
+
+.focus\:text-blue-700:focus {
+  --text-opacity: 1;
+  color: #2b6cb0;
+  color: rgba(43, 108, 176, var(--text-opacity));
+}
+
+.focus\:text-blue-800:focus {
+  --text-opacity: 1;
+  color: #2c5282;
+  color: rgba(44, 82, 130, var(--text-opacity));
+}
+
+.focus\:text-blue-900:focus {
+  --text-opacity: 1;
+  color: #2a4365;
+  color: rgba(42, 67, 101, var(--text-opacity));
+}
+
+.focus\:text-indigo-100:focus {
+  --text-opacity: 1;
+  color: #ebf4ff;
+  color: rgba(235, 244, 255, var(--text-opacity));
+}
+
+.focus\:text-indigo-200:focus {
+  --text-opacity: 1;
+  color: #c3dafe;
+  color: rgba(195, 218, 254, var(--text-opacity));
+}
+
+.focus\:text-indigo-300:focus {
+  --text-opacity: 1;
+  color: #a3bffa;
+  color: rgba(163, 191, 250, var(--text-opacity));
+}
+
+.focus\:text-indigo-400:focus {
+  --text-opacity: 1;
+  color: #7f9cf5;
+  color: rgba(127, 156, 245, var(--text-opacity));
+}
+
+.focus\:text-indigo-500:focus {
+  --text-opacity: 1;
+  color: #667eea;
+  color: rgba(102, 126, 234, var(--text-opacity));
+}
+
+.focus\:text-indigo-600:focus {
+  --text-opacity: 1;
+  color: #5a67d8;
+  color: rgba(90, 103, 216, var(--text-opacity));
+}
+
+.focus\:text-indigo-700:focus {
+  --text-opacity: 1;
+  color: #4c51bf;
+  color: rgba(76, 81, 191, var(--text-opacity));
+}
+
+.focus\:text-indigo-800:focus {
+  --text-opacity: 1;
+  color: #434190;
+  color: rgba(67, 65, 144, var(--text-opacity));
+}
+
+.focus\:text-indigo-900:focus {
+  --text-opacity: 1;
+  color: #3c366b;
+  color: rgba(60, 54, 107, var(--text-opacity));
+}
+
+.focus\:text-purple-100:focus {
+  --text-opacity: 1;
+  color: #faf5ff;
+  color: rgba(250, 245, 255, var(--text-opacity));
+}
+
+.focus\:text-purple-200:focus {
+  --text-opacity: 1;
+  color: #e9d8fd;
+  color: rgba(233, 216, 253, var(--text-opacity));
+}
+
+.focus\:text-purple-300:focus {
+  --text-opacity: 1;
+  color: #d6bcfa;
+  color: rgba(214, 188, 250, var(--text-opacity));
+}
+
+.focus\:text-purple-400:focus {
+  --text-opacity: 1;
+  color: #b794f4;
+  color: rgba(183, 148, 244, var(--text-opacity));
+}
+
+.focus\:text-purple-500:focus {
+  --text-opacity: 1;
+  color: #9f7aea;
+  color: rgba(159, 122, 234, var(--text-opacity));
+}
+
+.focus\:text-purple-600:focus {
+  --text-opacity: 1;
+  color: #805ad5;
+  color: rgba(128, 90, 213, var(--text-opacity));
+}
+
+.focus\:text-purple-700:focus {
+  --text-opacity: 1;
+  color: #6b46c1;
+  color: rgba(107, 70, 193, var(--text-opacity));
+}
+
+.focus\:text-purple-800:focus {
+  --text-opacity: 1;
+  color: #553c9a;
+  color: rgba(85, 60, 154, var(--text-opacity));
+}
+
+.focus\:text-purple-900:focus {
+  --text-opacity: 1;
+  color: #44337a;
+  color: rgba(68, 51, 122, var(--text-opacity));
+}
+
+.focus\:text-pink-100:focus {
+  --text-opacity: 1;
+  color: #fff5f7;
+  color: rgba(255, 245, 247, var(--text-opacity));
+}
+
+.focus\:text-pink-200:focus {
+  --text-opacity: 1;
+  color: #fed7e2;
+  color: rgba(254, 215, 226, var(--text-opacity));
+}
+
+.focus\:text-pink-300:focus {
+  --text-opacity: 1;
+  color: #fbb6ce;
+  color: rgba(251, 182, 206, var(--text-opacity));
+}
+
+.focus\:text-pink-400:focus {
+  --text-opacity: 1;
+  color: #f687b3;
+  color: rgba(246, 135, 179, var(--text-opacity));
+}
+
+.focus\:text-pink-500:focus {
+  --text-opacity: 1;
+  color: #ed64a6;
+  color: rgba(237, 100, 166, var(--text-opacity));
+}
+
+.focus\:text-pink-600:focus {
+  --text-opacity: 1;
+  color: #d53f8c;
+  color: rgba(213, 63, 140, var(--text-opacity));
+}
+
+.focus\:text-pink-700:focus {
+  --text-opacity: 1;
+  color: #b83280;
+  color: rgba(184, 50, 128, var(--text-opacity));
+}
+
+.focus\:text-pink-800:focus {
+  --text-opacity: 1;
+  color: #97266d;
+  color: rgba(151, 38, 109, var(--text-opacity));
+}
+
+.focus\:text-pink-900:focus {
+  --text-opacity: 1;
+  color: #702459;
+  color: rgba(112, 36, 89, var(--text-opacity));
+}
+
+.text-opacity-0 {
+  --text-opacity: 0;
+}
+
+.text-opacity-25 {
+  --text-opacity: 0.25;
+}
+
+.text-opacity-50 {
+  --text-opacity: 0.5;
+}
+
+.text-opacity-75 {
+  --text-opacity: 0.75;
+}
+
+.text-opacity-100 {
+  --text-opacity: 1;
+}
+
+.hover\:text-opacity-0:hover {
+  --text-opacity: 0;
+}
+
+.hover\:text-opacity-25:hover {
+  --text-opacity: 0.25;
+}
+
+.hover\:text-opacity-50:hover {
+  --text-opacity: 0.5;
+}
+
+.hover\:text-opacity-75:hover {
+  --text-opacity: 0.75;
+}
+
+.hover\:text-opacity-100:hover {
+  --text-opacity: 1;
+}
+
+.focus\:text-opacity-0:focus {
+  --text-opacity: 0;
+}
+
+.focus\:text-opacity-25:focus {
+  --text-opacity: 0.25;
+}
+
+.focus\:text-opacity-50:focus {
+  --text-opacity: 0.5;
+}
+
+.focus\:text-opacity-75:focus {
+  --text-opacity: 0.75;
+}
+
+.focus\:text-opacity-100:focus {
+  --text-opacity: 1;
+}
+
+.italic {
+  font-style: italic;
+}
+
+.not-italic {
+  font-style: normal;
+}
+
+.uppercase {
+  text-transform: uppercase;
+}
+
+.lowercase {
+  text-transform: lowercase;
+}
+
+.capitalize {
+  text-transform: capitalize;
+}
+
+.normal-case {
+  text-transform: none;
+}
+
+.underline {
+  text-decoration: underline;
+}
+
+.line-through {
+  text-decoration: line-through;
+}
+
+.no-underline {
+  text-decoration: none;
+}
+
+.hover\:underline:hover {
+  text-decoration: underline;
+}
+
+.hover\:line-through:hover {
+  text-decoration: line-through;
+}
+
+.hover\:no-underline:hover {
+  text-decoration: none;
+}
+
+.focus\:underline:focus {
+  text-decoration: underline;
+}
+
+.focus\:line-through:focus {
+  text-decoration: line-through;
+}
+
+.focus\:no-underline:focus {
+  text-decoration: none;
+}
+
+.antialiased {
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.subpixel-antialiased {
+  -webkit-font-smoothing: auto;
+  -moz-osx-font-smoothing: auto;
+}
+
+.ordinal, .slashed-zero, .lining-nums, .oldstyle-nums, .proportional-nums, .tabular-nums, .diagonal-fractions, .stacked-fractions {
+  --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+  --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+  --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+  --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+  --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+  font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+}
+
+.normal-nums {
+  font-variant-numeric: normal;
+}
+
+.ordinal {
+  --font-variant-numeric-ordinal: ordinal;
+}
+
+.slashed-zero {
+  --font-variant-numeric-slashed-zero: slashed-zero;
+}
+
+.lining-nums {
+  --font-variant-numeric-figure: lining-nums;
+}
+
+.oldstyle-nums {
+  --font-variant-numeric-figure: oldstyle-nums;
+}
+
+.proportional-nums {
+  --font-variant-numeric-spacing: proportional-nums;
+}
+
+.tabular-nums {
+  --font-variant-numeric-spacing: tabular-nums;
+}
+
+.diagonal-fractions {
+  --font-variant-numeric-fraction: diagonal-fractions;
+}
+
+.stacked-fractions {
+  --font-variant-numeric-fraction: stacked-fractions;
+}
+
+.tracking-tighter {
+  letter-spacing: -0.05em;
+}
+
+.tracking-tight {
+  letter-spacing: -0.025em;
+}
+
+.tracking-normal {
+  letter-spacing: 0;
+}
+
+.tracking-wide {
+  letter-spacing: 0.025em;
+}
+
+.tracking-wider {
+  letter-spacing: 0.05em;
+}
+
+.tracking-widest {
+  letter-spacing: 0.1em;
+}
+
+.select-none {
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+
+.select-text {
+  -webkit-user-select: text;
+     -moz-user-select: text;
+      -ms-user-select: text;
+          user-select: text;
+}
+
+.select-all {
+  -webkit-user-select: all;
+     -moz-user-select: all;
+      -ms-user-select: all;
+          user-select: all;
+}
+
+.select-auto {
+  -webkit-user-select: auto;
+     -moz-user-select: auto;
+      -ms-user-select: auto;
+          user-select: auto;
+}
+
+.align-baseline {
+  vertical-align: baseline;
+}
+
+.align-top {
+  vertical-align: top;
+}
+
+.align-middle {
+  vertical-align: middle;
+}
+
+.align-bottom {
+  vertical-align: bottom;
+}
+
+.align-text-top {
+  vertical-align: text-top;
+}
+
+.align-text-bottom {
+  vertical-align: text-bottom;
+}
+
+.visible {
+  visibility: visible;
+}
+
+.invisible {
+  visibility: hidden;
+}
+
+.whitespace-normal {
+  white-space: normal;
+}
+
+.whitespace-no-wrap {
+  white-space: nowrap;
+}
+
+.whitespace-pre {
+  white-space: pre;
+}
+
+.whitespace-pre-line {
+  white-space: pre-line;
+}
+
+.whitespace-pre-wrap {
+  white-space: pre-wrap;
+}
+
+.break-normal {
+  overflow-wrap: normal;
+  word-break: normal;
+}
+
+.break-words {
+  overflow-wrap: break-word;
+}
+
+.break-all {
+  word-break: break-all;
+}
+
+.truncate {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.w-0 {
+  width: 0;
+}
+
+.w-1 {
+  width: 0.25rem;
+}
+
+.w-2 {
+  width: 0.5rem;
+}
+
+.w-3 {
+  width: 0.75rem;
+}
+
+.w-4 {
+  width: 1rem;
+}
+
+.w-5 {
+  width: 1.25rem;
+}
+
+.w-6 {
+  width: 1.5rem;
+}
+
+.w-8 {
+  width: 2rem;
+}
+
+.w-10 {
+  width: 2.5rem;
+}
+
+.w-12 {
+  width: 3rem;
+}
+
+.w-16 {
+  width: 4rem;
+}
+
+.w-20 {
+  width: 5rem;
+}
+
+.w-24 {
+  width: 6rem;
+}
+
+.w-32 {
+  width: 8rem;
+}
+
+.w-40 {
+  width: 10rem;
+}
+
+.w-48 {
+  width: 12rem;
+}
+
+.w-56 {
+  width: 14rem;
+}
+
+.w-64 {
+  width: 16rem;
+}
+
+.w-auto {
+  width: auto;
+}
+
+.w-px {
+  width: 1px;
+}
+
+.w-1\/2 {
+  width: 50%;
+}
+
+.w-1\/3 {
+  width: 33.333333%;
+}
+
+.w-2\/3 {
+  width: 66.666667%;
+}
+
+.w-1\/4 {
+  width: 25%;
+}
+
+.w-2\/4 {
+  width: 50%;
+}
+
+.w-3\/4 {
+  width: 75%;
+}
+
+.w-1\/5 {
+  width: 20%;
+}
+
+.w-2\/5 {
+  width: 40%;
+}
+
+.w-3\/5 {
+  width: 60%;
+}
+
+.w-4\/5 {
+  width: 80%;
+}
+
+.w-1\/6 {
+  width: 16.666667%;
+}
+
+.w-2\/6 {
+  width: 33.333333%;
+}
+
+.w-3\/6 {
+  width: 50%;
+}
+
+.w-4\/6 {
+  width: 66.666667%;
+}
+
+.w-5\/6 {
+  width: 83.333333%;
+}
+
+.w-1\/12 {
+  width: 8.333333%;
+}
+
+.w-2\/12 {
+  width: 16.666667%;
+}
+
+.w-3\/12 {
+  width: 25%;
+}
+
+.w-4\/12 {
+  width: 33.333333%;
+}
+
+.w-5\/12 {
+  width: 41.666667%;
+}
+
+.w-6\/12 {
+  width: 50%;
+}
+
+.w-7\/12 {
+  width: 58.333333%;
+}
+
+.w-8\/12 {
+  width: 66.666667%;
+}
+
+.w-9\/12 {
+  width: 75%;
+}
+
+.w-10\/12 {
+  width: 83.333333%;
+}
+
+.w-11\/12 {
+  width: 91.666667%;
+}
+
+.w-full {
+  width: 100%;
+}
+
+.w-screen {
+  width: 100vw;
+}
+
+.z-0 {
+  z-index: 0;
+}
+
+.z-10 {
+  z-index: 10;
+}
+
+.z-20 {
+  z-index: 20;
+}
+
+.z-30 {
+  z-index: 30;
+}
+
+.z-40 {
+  z-index: 40;
+}
+
+.z-50 {
+  z-index: 50;
+}
+
+.z-auto {
+  z-index: auto;
+}
+
+.gap-0 {
+  grid-gap: 0;
+  gap: 0;
+}
+
+.gap-1 {
+  grid-gap: 0.25rem;
+  gap: 0.25rem;
+}
+
+.gap-2 {
+  grid-gap: 0.5rem;
+  gap: 0.5rem;
+}
+
+.gap-3 {
+  grid-gap: 0.75rem;
+  gap: 0.75rem;
+}
+
+.gap-4 {
+  grid-gap: 1rem;
+  gap: 1rem;
+}
+
+.gap-5 {
+  grid-gap: 1.25rem;
+  gap: 1.25rem;
+}
+
+.gap-6 {
+  grid-gap: 1.5rem;
+  gap: 1.5rem;
+}
+
+.gap-8 {
+  grid-gap: 2rem;
+  gap: 2rem;
+}
+
+.gap-10 {
+  grid-gap: 2.5rem;
+  gap: 2.5rem;
+}
+
+.gap-12 {
+  grid-gap: 3rem;
+  gap: 3rem;
+}
+
+.gap-16 {
+  grid-gap: 4rem;
+  gap: 4rem;
+}
+
+.gap-20 {
+  grid-gap: 5rem;
+  gap: 5rem;
+}
+
+.gap-24 {
+  grid-gap: 6rem;
+  gap: 6rem;
+}
+
+.gap-32 {
+  grid-gap: 8rem;
+  gap: 8rem;
+}
+
+.gap-40 {
+  grid-gap: 10rem;
+  gap: 10rem;
+}
+
+.gap-48 {
+  grid-gap: 12rem;
+  gap: 12rem;
+}
+
+.gap-56 {
+  grid-gap: 14rem;
+  gap: 14rem;
+}
+
+.gap-64 {
+  grid-gap: 16rem;
+  gap: 16rem;
+}
+
+.gap-px {
+  grid-gap: 1px;
+  gap: 1px;
+}
+
+.col-gap-0 {
+  grid-column-gap: 0;
+  -moz-column-gap: 0;
+       column-gap: 0;
+}
+
+.col-gap-1 {
+  grid-column-gap: 0.25rem;
+  -moz-column-gap: 0.25rem;
+       column-gap: 0.25rem;
+}
+
+.col-gap-2 {
+  grid-column-gap: 0.5rem;
+  -moz-column-gap: 0.5rem;
+       column-gap: 0.5rem;
+}
+
+.col-gap-3 {
+  grid-column-gap: 0.75rem;
+  -moz-column-gap: 0.75rem;
+       column-gap: 0.75rem;
+}
+
+.col-gap-4 {
+  grid-column-gap: 1rem;
+  -moz-column-gap: 1rem;
+       column-gap: 1rem;
+}
+
+.col-gap-5 {
+  grid-column-gap: 1.25rem;
+  -moz-column-gap: 1.25rem;
+       column-gap: 1.25rem;
+}
+
+.col-gap-6 {
+  grid-column-gap: 1.5rem;
+  -moz-column-gap: 1.5rem;
+       column-gap: 1.5rem;
+}
+
+.col-gap-8 {
+  grid-column-gap: 2rem;
+  -moz-column-gap: 2rem;
+       column-gap: 2rem;
+}
+
+.col-gap-10 {
+  grid-column-gap: 2.5rem;
+  -moz-column-gap: 2.5rem;
+       column-gap: 2.5rem;
+}
+
+.col-gap-12 {
+  grid-column-gap: 3rem;
+  -moz-column-gap: 3rem;
+       column-gap: 3rem;
+}
+
+.col-gap-16 {
+  grid-column-gap: 4rem;
+  -moz-column-gap: 4rem;
+       column-gap: 4rem;
+}
+
+.col-gap-20 {
+  grid-column-gap: 5rem;
+  -moz-column-gap: 5rem;
+       column-gap: 5rem;
+}
+
+.col-gap-24 {
+  grid-column-gap: 6rem;
+  -moz-column-gap: 6rem;
+       column-gap: 6rem;
+}
+
+.col-gap-32 {
+  grid-column-gap: 8rem;
+  -moz-column-gap: 8rem;
+       column-gap: 8rem;
+}
+
+.col-gap-40 {
+  grid-column-gap: 10rem;
+  -moz-column-gap: 10rem;
+       column-gap: 10rem;
+}
+
+.col-gap-48 {
+  grid-column-gap: 12rem;
+  -moz-column-gap: 12rem;
+       column-gap: 12rem;
+}
+
+.col-gap-56 {
+  grid-column-gap: 14rem;
+  -moz-column-gap: 14rem;
+       column-gap: 14rem;
+}
+
+.col-gap-64 {
+  grid-column-gap: 16rem;
+  -moz-column-gap: 16rem;
+       column-gap: 16rem;
+}
+
+.col-gap-px {
+  grid-column-gap: 1px;
+  -moz-column-gap: 1px;
+       column-gap: 1px;
+}
+
+.gap-x-0 {
+  grid-column-gap: 0;
+  -moz-column-gap: 0;
+       column-gap: 0;
+}
+
+.gap-x-1 {
+  grid-column-gap: 0.25rem;
+  -moz-column-gap: 0.25rem;
+       column-gap: 0.25rem;
+}
+
+.gap-x-2 {
+  grid-column-gap: 0.5rem;
+  -moz-column-gap: 0.5rem;
+       column-gap: 0.5rem;
+}
+
+.gap-x-3 {
+  grid-column-gap: 0.75rem;
+  -moz-column-gap: 0.75rem;
+       column-gap: 0.75rem;
+}
+
+.gap-x-4 {
+  grid-column-gap: 1rem;
+  -moz-column-gap: 1rem;
+       column-gap: 1rem;
+}
+
+.gap-x-5 {
+  grid-column-gap: 1.25rem;
+  -moz-column-gap: 1.25rem;
+       column-gap: 1.25rem;
+}
+
+.gap-x-6 {
+  grid-column-gap: 1.5rem;
+  -moz-column-gap: 1.5rem;
+       column-gap: 1.5rem;
+}
+
+.gap-x-8 {
+  grid-column-gap: 2rem;
+  -moz-column-gap: 2rem;
+       column-gap: 2rem;
+}
+
+.gap-x-10 {
+  grid-column-gap: 2.5rem;
+  -moz-column-gap: 2.5rem;
+       column-gap: 2.5rem;
+}
+
+.gap-x-12 {
+  grid-column-gap: 3rem;
+  -moz-column-gap: 3rem;
+       column-gap: 3rem;
+}
+
+.gap-x-16 {
+  grid-column-gap: 4rem;
+  -moz-column-gap: 4rem;
+       column-gap: 4rem;
+}
+
+.gap-x-20 {
+  grid-column-gap: 5rem;
+  -moz-column-gap: 5rem;
+       column-gap: 5rem;
+}
+
+.gap-x-24 {
+  grid-column-gap: 6rem;
+  -moz-column-gap: 6rem;
+       column-gap: 6rem;
+}
+
+.gap-x-32 {
+  grid-column-gap: 8rem;
+  -moz-column-gap: 8rem;
+       column-gap: 8rem;
+}
+
+.gap-x-40 {
+  grid-column-gap: 10rem;
+  -moz-column-gap: 10rem;
+       column-gap: 10rem;
+}
+
+.gap-x-48 {
+  grid-column-gap: 12rem;
+  -moz-column-gap: 12rem;
+       column-gap: 12rem;
+}
+
+.gap-x-56 {
+  grid-column-gap: 14rem;
+  -moz-column-gap: 14rem;
+       column-gap: 14rem;
+}
+
+.gap-x-64 {
+  grid-column-gap: 16rem;
+  -moz-column-gap: 16rem;
+       column-gap: 16rem;
+}
+
+.gap-x-px {
+  grid-column-gap: 1px;
+  -moz-column-gap: 1px;
+       column-gap: 1px;
+}
+
+.row-gap-0 {
+  grid-row-gap: 0;
+  row-gap: 0;
+}
+
+.row-gap-1 {
+  grid-row-gap: 0.25rem;
+  row-gap: 0.25rem;
+}
+
+.row-gap-2 {
+  grid-row-gap: 0.5rem;
+  row-gap: 0.5rem;
+}
+
+.row-gap-3 {
+  grid-row-gap: 0.75rem;
+  row-gap: 0.75rem;
+}
+
+.row-gap-4 {
+  grid-row-gap: 1rem;
+  row-gap: 1rem;
+}
+
+.row-gap-5 {
+  grid-row-gap: 1.25rem;
+  row-gap: 1.25rem;
+}
+
+.row-gap-6 {
+  grid-row-gap: 1.5rem;
+  row-gap: 1.5rem;
+}
+
+.row-gap-8 {
+  grid-row-gap: 2rem;
+  row-gap: 2rem;
+}
+
+.row-gap-10 {
+  grid-row-gap: 2.5rem;
+  row-gap: 2.5rem;
+}
+
+.row-gap-12 {
+  grid-row-gap: 3rem;
+  row-gap: 3rem;
+}
+
+.row-gap-16 {
+  grid-row-gap: 4rem;
+  row-gap: 4rem;
+}
+
+.row-gap-20 {
+  grid-row-gap: 5rem;
+  row-gap: 5rem;
+}
+
+.row-gap-24 {
+  grid-row-gap: 6rem;
+  row-gap: 6rem;
+}
+
+.row-gap-32 {
+  grid-row-gap: 8rem;
+  row-gap: 8rem;
+}
+
+.row-gap-40 {
+  grid-row-gap: 10rem;
+  row-gap: 10rem;
+}
+
+.row-gap-48 {
+  grid-row-gap: 12rem;
+  row-gap: 12rem;
+}
+
+.row-gap-56 {
+  grid-row-gap: 14rem;
+  row-gap: 14rem;
+}
+
+.row-gap-64 {
+  grid-row-gap: 16rem;
+  row-gap: 16rem;
+}
+
+.row-gap-px {
+  grid-row-gap: 1px;
+  row-gap: 1px;
+}
+
+.gap-y-0 {
+  grid-row-gap: 0;
+  row-gap: 0;
+}
+
+.gap-y-1 {
+  grid-row-gap: 0.25rem;
+  row-gap: 0.25rem;
+}
+
+.gap-y-2 {
+  grid-row-gap: 0.5rem;
+  row-gap: 0.5rem;
+}
+
+.gap-y-3 {
+  grid-row-gap: 0.75rem;
+  row-gap: 0.75rem;
+}
+
+.gap-y-4 {
+  grid-row-gap: 1rem;
+  row-gap: 1rem;
+}
+
+.gap-y-5 {
+  grid-row-gap: 1.25rem;
+  row-gap: 1.25rem;
+}
+
+.gap-y-6 {
+  grid-row-gap: 1.5rem;
+  row-gap: 1.5rem;
+}
+
+.gap-y-8 {
+  grid-row-gap: 2rem;
+  row-gap: 2rem;
+}
+
+.gap-y-10 {
+  grid-row-gap: 2.5rem;
+  row-gap: 2.5rem;
+}
+
+.gap-y-12 {
+  grid-row-gap: 3rem;
+  row-gap: 3rem;
+}
+
+.gap-y-16 {
+  grid-row-gap: 4rem;
+  row-gap: 4rem;
+}
+
+.gap-y-20 {
+  grid-row-gap: 5rem;
+  row-gap: 5rem;
+}
+
+.gap-y-24 {
+  grid-row-gap: 6rem;
+  row-gap: 6rem;
+}
+
+.gap-y-32 {
+  grid-row-gap: 8rem;
+  row-gap: 8rem;
+}
+
+.gap-y-40 {
+  grid-row-gap: 10rem;
+  row-gap: 10rem;
+}
+
+.gap-y-48 {
+  grid-row-gap: 12rem;
+  row-gap: 12rem;
+}
+
+.gap-y-56 {
+  grid-row-gap: 14rem;
+  row-gap: 14rem;
+}
+
+.gap-y-64 {
+  grid-row-gap: 16rem;
+  row-gap: 16rem;
+}
+
+.gap-y-px {
+  grid-row-gap: 1px;
+  row-gap: 1px;
+}
+
+.grid-flow-row {
+  grid-auto-flow: row;
+}
+
+.grid-flow-col {
+  grid-auto-flow: column;
+}
+
+.grid-flow-row-dense {
+  grid-auto-flow: row dense;
+}
+
+.grid-flow-col-dense {
+  grid-auto-flow: column dense;
+}
+
+.grid-cols-1 {
+  grid-template-columns: repeat(1, minmax(0, 1fr));
+}
+
+.grid-cols-2 {
+  grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+.grid-cols-3 {
+  grid-template-columns: repeat(3, minmax(0, 1fr));
+}
+
+.grid-cols-4 {
+  grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+
+.grid-cols-5 {
+  grid-template-columns: repeat(5, minmax(0, 1fr));
+}
+
+.grid-cols-6 {
+  grid-template-columns: repeat(6, minmax(0, 1fr));
+}
+
+.grid-cols-7 {
+  grid-template-columns: repeat(7, minmax(0, 1fr));
+}
+
+.grid-cols-8 {
+  grid-template-columns: repeat(8, minmax(0, 1fr));
+}
+
+.grid-cols-9 {
+  grid-template-columns: repeat(9, minmax(0, 1fr));
+}
+
+.grid-cols-10 {
+  grid-template-columns: repeat(10, minmax(0, 1fr));
+}
+
+.grid-cols-11 {
+  grid-template-columns: repeat(11, minmax(0, 1fr));
+}
+
+.grid-cols-12 {
+  grid-template-columns: repeat(12, minmax(0, 1fr));
+}
+
+.grid-cols-none {
+  grid-template-columns: none;
+}
+
+.col-auto {
+  grid-column: auto;
+}
+
+.col-span-1 {
+  grid-column: span 1 / span 1;
+}
+
+.col-span-2 {
+  grid-column: span 2 / span 2;
+}
+
+.col-span-3 {
+  grid-column: span 3 / span 3;
+}
+
+.col-span-4 {
+  grid-column: span 4 / span 4;
+}
+
+.col-span-5 {
+  grid-column: span 5 / span 5;
+}
+
+.col-span-6 {
+  grid-column: span 6 / span 6;
+}
+
+.col-span-7 {
+  grid-column: span 7 / span 7;
+}
+
+.col-span-8 {
+  grid-column: span 8 / span 8;
+}
+
+.col-span-9 {
+  grid-column: span 9 / span 9;
+}
+
+.col-span-10 {
+  grid-column: span 10 / span 10;
+}
+
+.col-span-11 {
+  grid-column: span 11 / span 11;
+}
+
+.col-span-12 {
+  grid-column: span 12 / span 12;
+}
+
+.col-start-1 {
+  grid-column-start: 1;
+}
+
+.col-start-2 {
+  grid-column-start: 2;
+}
+
+.col-start-3 {
+  grid-column-start: 3;
+}
+
+.col-start-4 {
+  grid-column-start: 4;
+}
+
+.col-start-5 {
+  grid-column-start: 5;
+}
+
+.col-start-6 {
+  grid-column-start: 6;
+}
+
+.col-start-7 {
+  grid-column-start: 7;
+}
+
+.col-start-8 {
+  grid-column-start: 8;
+}
+
+.col-start-9 {
+  grid-column-start: 9;
+}
+
+.col-start-10 {
+  grid-column-start: 10;
+}
+
+.col-start-11 {
+  grid-column-start: 11;
+}
+
+.col-start-12 {
+  grid-column-start: 12;
+}
+
+.col-start-13 {
+  grid-column-start: 13;
+}
+
+.col-start-auto {
+  grid-column-start: auto;
+}
+
+.col-end-1 {
+  grid-column-end: 1;
+}
+
+.col-end-2 {
+  grid-column-end: 2;
+}
+
+.col-end-3 {
+  grid-column-end: 3;
+}
+
+.col-end-4 {
+  grid-column-end: 4;
+}
+
+.col-end-5 {
+  grid-column-end: 5;
+}
+
+.col-end-6 {
+  grid-column-end: 6;
+}
+
+.col-end-7 {
+  grid-column-end: 7;
+}
+
+.col-end-8 {
+  grid-column-end: 8;
+}
+
+.col-end-9 {
+  grid-column-end: 9;
+}
+
+.col-end-10 {
+  grid-column-end: 10;
+}
+
+.col-end-11 {
+  grid-column-end: 11;
+}
+
+.col-end-12 {
+  grid-column-end: 12;
+}
+
+.col-end-13 {
+  grid-column-end: 13;
+}
+
+.col-end-auto {
+  grid-column-end: auto;
+}
+
+.grid-rows-1 {
+  grid-template-rows: repeat(1, minmax(0, 1fr));
+}
+
+.grid-rows-2 {
+  grid-template-rows: repeat(2, minmax(0, 1fr));
+}
+
+.grid-rows-3 {
+  grid-template-rows: repeat(3, minmax(0, 1fr));
+}
+
+.grid-rows-4 {
+  grid-template-rows: repeat(4, minmax(0, 1fr));
+}
+
+.grid-rows-5 {
+  grid-template-rows: repeat(5, minmax(0, 1fr));
+}
+
+.grid-rows-6 {
+  grid-template-rows: repeat(6, minmax(0, 1fr));
+}
+
+.grid-rows-none {
+  grid-template-rows: none;
+}
+
+.row-auto {
+  grid-row: auto;
+}
+
+.row-span-1 {
+  grid-row: span 1 / span 1;
+}
+
+.row-span-2 {
+  grid-row: span 2 / span 2;
+}
+
+.row-span-3 {
+  grid-row: span 3 / span 3;
+}
+
+.row-span-4 {
+  grid-row: span 4 / span 4;
+}
+
+.row-span-5 {
+  grid-row: span 5 / span 5;
+}
+
+.row-span-6 {
+  grid-row: span 6 / span 6;
+}
+
+.row-start-1 {
+  grid-row-start: 1;
+}
+
+.row-start-2 {
+  grid-row-start: 2;
+}
+
+.row-start-3 {
+  grid-row-start: 3;
+}
+
+.row-start-4 {
+  grid-row-start: 4;
+}
+
+.row-start-5 {
+  grid-row-start: 5;
+}
+
+.row-start-6 {
+  grid-row-start: 6;
+}
+
+.row-start-7 {
+  grid-row-start: 7;
+}
+
+.row-start-auto {
+  grid-row-start: auto;
+}
+
+.row-end-1 {
+  grid-row-end: 1;
+}
+
+.row-end-2 {
+  grid-row-end: 2;
+}
+
+.row-end-3 {
+  grid-row-end: 3;
+}
+
+.row-end-4 {
+  grid-row-end: 4;
+}
+
+.row-end-5 {
+  grid-row-end: 5;
+}
+
+.row-end-6 {
+  grid-row-end: 6;
+}
+
+.row-end-7 {
+  grid-row-end: 7;
+}
+
+.row-end-auto {
+  grid-row-end: auto;
+}
+
+.transform {
+  --transform-translate-x: 0;
+  --transform-translate-y: 0;
+  --transform-rotate: 0;
+  --transform-skew-x: 0;
+  --transform-skew-y: 0;
+  --transform-scale-x: 1;
+  --transform-scale-y: 1;
+  transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+}
+
+.transform-none {
+  transform: none;
+}
+
+.origin-center {
+  transform-origin: center;
+}
+
+.origin-top {
+  transform-origin: top;
+}
+
+.origin-top-right {
+  transform-origin: top right;
+}
+
+.origin-right {
+  transform-origin: right;
+}
+
+.origin-bottom-right {
+  transform-origin: bottom right;
+}
+
+.origin-bottom {
+  transform-origin: bottom;
+}
+
+.origin-bottom-left {
+  transform-origin: bottom left;
+}
+
+.origin-left {
+  transform-origin: left;
+}
+
+.origin-top-left {
+  transform-origin: top left;
+}
+
+.scale-0 {
+  --transform-scale-x: 0;
+  --transform-scale-y: 0;
+}
+
+.scale-50 {
+  --transform-scale-x: .5;
+  --transform-scale-y: .5;
+}
+
+.scale-75 {
+  --transform-scale-x: .75;
+  --transform-scale-y: .75;
+}
+
+.scale-90 {
+  --transform-scale-x: .9;
+  --transform-scale-y: .9;
+}
+
+.scale-95 {
+  --transform-scale-x: .95;
+  --transform-scale-y: .95;
+}
+
+.scale-100 {
+  --transform-scale-x: 1;
+  --transform-scale-y: 1;
+}
+
+.scale-105 {
+  --transform-scale-x: 1.05;
+  --transform-scale-y: 1.05;
+}
+
+.scale-110 {
+  --transform-scale-x: 1.1;
+  --transform-scale-y: 1.1;
+}
+
+.scale-125 {
+  --transform-scale-x: 1.25;
+  --transform-scale-y: 1.25;
+}
+
+.scale-150 {
+  --transform-scale-x: 1.5;
+  --transform-scale-y: 1.5;
+}
+
+.scale-x-0 {
+  --transform-scale-x: 0;
+}
+
+.scale-x-50 {
+  --transform-scale-x: .5;
+}
+
+.scale-x-75 {
+  --transform-scale-x: .75;
+}
+
+.scale-x-90 {
+  --transform-scale-x: .9;
+}
+
+.scale-x-95 {
+  --transform-scale-x: .95;
+}
+
+.scale-x-100 {
+  --transform-scale-x: 1;
+}
+
+.scale-x-105 {
+  --transform-scale-x: 1.05;
+}
+
+.scale-x-110 {
+  --transform-scale-x: 1.1;
+}
+
+.scale-x-125 {
+  --transform-scale-x: 1.25;
+}
+
+.scale-x-150 {
+  --transform-scale-x: 1.5;
+}
+
+.scale-y-0 {
+  --transform-scale-y: 0;
+}
+
+.scale-y-50 {
+  --transform-scale-y: .5;
+}
+
+.scale-y-75 {
+  --transform-scale-y: .75;
+}
+
+.scale-y-90 {
+  --transform-scale-y: .9;
+}
+
+.scale-y-95 {
+  --transform-scale-y: .95;
+}
+
+.scale-y-100 {
+  --transform-scale-y: 1;
+}
+
+.scale-y-105 {
+  --transform-scale-y: 1.05;
+}
+
+.scale-y-110 {
+  --transform-scale-y: 1.1;
+}
+
+.scale-y-125 {
+  --transform-scale-y: 1.25;
+}
+
+.scale-y-150 {
+  --transform-scale-y: 1.5;
+}
+
+.hover\:scale-0:hover {
+  --transform-scale-x: 0;
+  --transform-scale-y: 0;
+}
+
+.hover\:scale-50:hover {
+  --transform-scale-x: .5;
+  --transform-scale-y: .5;
+}
+
+.hover\:scale-75:hover {
+  --transform-scale-x: .75;
+  --transform-scale-y: .75;
+}
+
+.hover\:scale-90:hover {
+  --transform-scale-x: .9;
+  --transform-scale-y: .9;
+}
+
+.hover\:scale-95:hover {
+  --transform-scale-x: .95;
+  --transform-scale-y: .95;
+}
+
+.hover\:scale-100:hover {
+  --transform-scale-x: 1;
+  --transform-scale-y: 1;
+}
+
+.hover\:scale-105:hover {
+  --transform-scale-x: 1.05;
+  --transform-scale-y: 1.05;
+}
+
+.hover\:scale-110:hover {
+  --transform-scale-x: 1.1;
+  --transform-scale-y: 1.1;
+}
+
+.hover\:scale-125:hover {
+  --transform-scale-x: 1.25;
+  --transform-scale-y: 1.25;
+}
+
+.hover\:scale-150:hover {
+  --transform-scale-x: 1.5;
+  --transform-scale-y: 1.5;
+}
+
+.hover\:scale-x-0:hover {
+  --transform-scale-x: 0;
+}
+
+.hover\:scale-x-50:hover {
+  --transform-scale-x: .5;
+}
+
+.hover\:scale-x-75:hover {
+  --transform-scale-x: .75;
+}
+
+.hover\:scale-x-90:hover {
+  --transform-scale-x: .9;
+}
+
+.hover\:scale-x-95:hover {
+  --transform-scale-x: .95;
+}
+
+.hover\:scale-x-100:hover {
+  --transform-scale-x: 1;
+}
+
+.hover\:scale-x-105:hover {
+  --transform-scale-x: 1.05;
+}
+
+.hover\:scale-x-110:hover {
+  --transform-scale-x: 1.1;
+}
+
+.hover\:scale-x-125:hover {
+  --transform-scale-x: 1.25;
+}
+
+.hover\:scale-x-150:hover {
+  --transform-scale-x: 1.5;
+}
+
+.hover\:scale-y-0:hover {
+  --transform-scale-y: 0;
+}
+
+.hover\:scale-y-50:hover {
+  --transform-scale-y: .5;
+}
+
+.hover\:scale-y-75:hover {
+  --transform-scale-y: .75;
+}
+
+.hover\:scale-y-90:hover {
+  --transform-scale-y: .9;
+}
+
+.hover\:scale-y-95:hover {
+  --transform-scale-y: .95;
+}
+
+.hover\:scale-y-100:hover {
+  --transform-scale-y: 1;
+}
+
+.hover\:scale-y-105:hover {
+  --transform-scale-y: 1.05;
+}
+
+.hover\:scale-y-110:hover {
+  --transform-scale-y: 1.1;
+}
+
+.hover\:scale-y-125:hover {
+  --transform-scale-y: 1.25;
+}
+
+.hover\:scale-y-150:hover {
+  --transform-scale-y: 1.5;
+}
+
+.focus\:scale-0:focus {
+  --transform-scale-x: 0;
+  --transform-scale-y: 0;
+}
+
+.focus\:scale-50:focus {
+  --transform-scale-x: .5;
+  --transform-scale-y: .5;
+}
+
+.focus\:scale-75:focus {
+  --transform-scale-x: .75;
+  --transform-scale-y: .75;
+}
+
+.focus\:scale-90:focus {
+  --transform-scale-x: .9;
+  --transform-scale-y: .9;
+}
+
+.focus\:scale-95:focus {
+  --transform-scale-x: .95;
+  --transform-scale-y: .95;
+}
+
+.focus\:scale-100:focus {
+  --transform-scale-x: 1;
+  --transform-scale-y: 1;
+}
+
+.focus\:scale-105:focus {
+  --transform-scale-x: 1.05;
+  --transform-scale-y: 1.05;
+}
+
+.focus\:scale-110:focus {
+  --transform-scale-x: 1.1;
+  --transform-scale-y: 1.1;
+}
+
+.focus\:scale-125:focus {
+  --transform-scale-x: 1.25;
+  --transform-scale-y: 1.25;
+}
+
+.focus\:scale-150:focus {
+  --transform-scale-x: 1.5;
+  --transform-scale-y: 1.5;
+}
+
+.focus\:scale-x-0:focus {
+  --transform-scale-x: 0;
+}
+
+.focus\:scale-x-50:focus {
+  --transform-scale-x: .5;
+}
+
+.focus\:scale-x-75:focus {
+  --transform-scale-x: .75;
+}
+
+.focus\:scale-x-90:focus {
+  --transform-scale-x: .9;
+}
+
+.focus\:scale-x-95:focus {
+  --transform-scale-x: .95;
+}
+
+.focus\:scale-x-100:focus {
+  --transform-scale-x: 1;
+}
+
+.focus\:scale-x-105:focus {
+  --transform-scale-x: 1.05;
+}
+
+.focus\:scale-x-110:focus {
+  --transform-scale-x: 1.1;
+}
+
+.focus\:scale-x-125:focus {
+  --transform-scale-x: 1.25;
+}
+
+.focus\:scale-x-150:focus {
+  --transform-scale-x: 1.5;
+}
+
+.focus\:scale-y-0:focus {
+  --transform-scale-y: 0;
+}
+
+.focus\:scale-y-50:focus {
+  --transform-scale-y: .5;
+}
+
+.focus\:scale-y-75:focus {
+  --transform-scale-y: .75;
+}
+
+.focus\:scale-y-90:focus {
+  --transform-scale-y: .9;
+}
+
+.focus\:scale-y-95:focus {
+  --transform-scale-y: .95;
+}
+
+.focus\:scale-y-100:focus {
+  --transform-scale-y: 1;
+}
+
+.focus\:scale-y-105:focus {
+  --transform-scale-y: 1.05;
+}
+
+.focus\:scale-y-110:focus {
+  --transform-scale-y: 1.1;
+}
+
+.focus\:scale-y-125:focus {
+  --transform-scale-y: 1.25;
+}
+
+.focus\:scale-y-150:focus {
+  --transform-scale-y: 1.5;
+}
+
+.rotate-0 {
+  --transform-rotate: 0;
+}
+
+.rotate-45 {
+  --transform-rotate: 45deg;
+}
+
+.rotate-90 {
+  --transform-rotate: 90deg;
+}
+
+.rotate-180 {
+  --transform-rotate: 180deg;
+}
+
+.-rotate-180 {
+  --transform-rotate: -180deg;
+}
+
+.-rotate-90 {
+  --transform-rotate: -90deg;
+}
+
+.-rotate-45 {
+  --transform-rotate: -45deg;
+}
+
+.hover\:rotate-0:hover {
+  --transform-rotate: 0;
+}
+
+.hover\:rotate-45:hover {
+  --transform-rotate: 45deg;
+}
+
+.hover\:rotate-90:hover {
+  --transform-rotate: 90deg;
+}
+
+.hover\:rotate-180:hover {
+  --transform-rotate: 180deg;
+}
+
+.hover\:-rotate-180:hover {
+  --transform-rotate: -180deg;
+}
+
+.hover\:-rotate-90:hover {
+  --transform-rotate: -90deg;
+}
+
+.hover\:-rotate-45:hover {
+  --transform-rotate: -45deg;
+}
+
+.focus\:rotate-0:focus {
+  --transform-rotate: 0;
+}
+
+.focus\:rotate-45:focus {
+  --transform-rotate: 45deg;
+}
+
+.focus\:rotate-90:focus {
+  --transform-rotate: 90deg;
+}
+
+.focus\:rotate-180:focus {
+  --transform-rotate: 180deg;
+}
+
+.focus\:-rotate-180:focus {
+  --transform-rotate: -180deg;
+}
+
+.focus\:-rotate-90:focus {
+  --transform-rotate: -90deg;
+}
+
+.focus\:-rotate-45:focus {
+  --transform-rotate: -45deg;
+}
+
+.translate-x-0 {
+  --transform-translate-x: 0;
+}
+
+.translate-x-1 {
+  --transform-translate-x: 0.25rem;
+}
+
+.translate-x-2 {
+  --transform-translate-x: 0.5rem;
+}
+
+.translate-x-3 {
+  --transform-translate-x: 0.75rem;
+}
+
+.translate-x-4 {
+  --transform-translate-x: 1rem;
+}
+
+.translate-x-5 {
+  --transform-translate-x: 1.25rem;
+}
+
+.translate-x-6 {
+  --transform-translate-x: 1.5rem;
+}
+
+.translate-x-8 {
+  --transform-translate-x: 2rem;
+}
+
+.translate-x-10 {
+  --transform-translate-x: 2.5rem;
+}
+
+.translate-x-12 {
+  --transform-translate-x: 3rem;
+}
+
+.translate-x-16 {
+  --transform-translate-x: 4rem;
+}
+
+.translate-x-20 {
+  --transform-translate-x: 5rem;
+}
+
+.translate-x-24 {
+  --transform-translate-x: 6rem;
+}
+
+.translate-x-32 {
+  --transform-translate-x: 8rem;
+}
+
+.translate-x-40 {
+  --transform-translate-x: 10rem;
+}
+
+.translate-x-48 {
+  --transform-translate-x: 12rem;
+}
+
+.translate-x-56 {
+  --transform-translate-x: 14rem;
+}
+
+.translate-x-64 {
+  --transform-translate-x: 16rem;
+}
+
+.translate-x-px {
+  --transform-translate-x: 1px;
+}
+
+.-translate-x-1 {
+  --transform-translate-x: -0.25rem;
+}
+
+.-translate-x-2 {
+  --transform-translate-x: -0.5rem;
+}
+
+.-translate-x-3 {
+  --transform-translate-x: -0.75rem;
+}
+
+.-translate-x-4 {
+  --transform-translate-x: -1rem;
+}
+
+.-translate-x-5 {
+  --transform-translate-x: -1.25rem;
+}
+
+.-translate-x-6 {
+  --transform-translate-x: -1.5rem;
+}
+
+.-translate-x-8 {
+  --transform-translate-x: -2rem;
+}
+
+.-translate-x-10 {
+  --transform-translate-x: -2.5rem;
+}
+
+.-translate-x-12 {
+  --transform-translate-x: -3rem;
+}
+
+.-translate-x-16 {
+  --transform-translate-x: -4rem;
+}
+
+.-translate-x-20 {
+  --transform-translate-x: -5rem;
+}
+
+.-translate-x-24 {
+  --transform-translate-x: -6rem;
+}
+
+.-translate-x-32 {
+  --transform-translate-x: -8rem;
+}
+
+.-translate-x-40 {
+  --transform-translate-x: -10rem;
+}
+
+.-translate-x-48 {
+  --transform-translate-x: -12rem;
+}
+
+.-translate-x-56 {
+  --transform-translate-x: -14rem;
+}
+
+.-translate-x-64 {
+  --transform-translate-x: -16rem;
+}
+
+.-translate-x-px {
+  --transform-translate-x: -1px;
+}
+
+.-translate-x-full {
+  --transform-translate-x: -100%;
+}
+
+.-translate-x-1\/2 {
+  --transform-translate-x: -50%;
+}
+
+.translate-x-1\/2 {
+  --transform-translate-x: 50%;
+}
+
+.translate-x-full {
+  --transform-translate-x: 100%;
+}
+
+.translate-y-0 {
+  --transform-translate-y: 0;
+}
+
+.translate-y-1 {
+  --transform-translate-y: 0.25rem;
+}
+
+.translate-y-2 {
+  --transform-translate-y: 0.5rem;
+}
+
+.translate-y-3 {
+  --transform-translate-y: 0.75rem;
+}
+
+.translate-y-4 {
+  --transform-translate-y: 1rem;
+}
+
+.translate-y-5 {
+  --transform-translate-y: 1.25rem;
+}
+
+.translate-y-6 {
+  --transform-translate-y: 1.5rem;
+}
+
+.translate-y-8 {
+  --transform-translate-y: 2rem;
+}
+
+.translate-y-10 {
+  --transform-translate-y: 2.5rem;
+}
+
+.translate-y-12 {
+  --transform-translate-y: 3rem;
+}
+
+.translate-y-16 {
+  --transform-translate-y: 4rem;
+}
+
+.translate-y-20 {
+  --transform-translate-y: 5rem;
+}
+
+.translate-y-24 {
+  --transform-translate-y: 6rem;
+}
+
+.translate-y-32 {
+  --transform-translate-y: 8rem;
+}
+
+.translate-y-40 {
+  --transform-translate-y: 10rem;
+}
+
+.translate-y-48 {
+  --transform-translate-y: 12rem;
+}
+
+.translate-y-56 {
+  --transform-translate-y: 14rem;
+}
+
+.translate-y-64 {
+  --transform-translate-y: 16rem;
+}
+
+.translate-y-px {
+  --transform-translate-y: 1px;
+}
+
+.-translate-y-1 {
+  --transform-translate-y: -0.25rem;
+}
+
+.-translate-y-2 {
+  --transform-translate-y: -0.5rem;
+}
+
+.-translate-y-3 {
+  --transform-translate-y: -0.75rem;
+}
+
+.-translate-y-4 {
+  --transform-translate-y: -1rem;
+}
+
+.-translate-y-5 {
+  --transform-translate-y: -1.25rem;
+}
+
+.-translate-y-6 {
+  --transform-translate-y: -1.5rem;
+}
+
+.-translate-y-8 {
+  --transform-translate-y: -2rem;
+}
+
+.-translate-y-10 {
+  --transform-translate-y: -2.5rem;
+}
+
+.-translate-y-12 {
+  --transform-translate-y: -3rem;
+}
+
+.-translate-y-16 {
+  --transform-translate-y: -4rem;
+}
+
+.-translate-y-20 {
+  --transform-translate-y: -5rem;
+}
+
+.-translate-y-24 {
+  --transform-translate-y: -6rem;
+}
+
+.-translate-y-32 {
+  --transform-translate-y: -8rem;
+}
+
+.-translate-y-40 {
+  --transform-translate-y: -10rem;
+}
+
+.-translate-y-48 {
+  --transform-translate-y: -12rem;
+}
+
+.-translate-y-56 {
+  --transform-translate-y: -14rem;
+}
+
+.-translate-y-64 {
+  --transform-translate-y: -16rem;
+}
+
+.-translate-y-px {
+  --transform-translate-y: -1px;
+}
+
+.-translate-y-full {
+  --transform-translate-y: -100%;
+}
+
+.-translate-y-1\/2 {
+  --transform-translate-y: -50%;
+}
+
+.translate-y-1\/2 {
+  --transform-translate-y: 50%;
+}
+
+.translate-y-full {
+  --transform-translate-y: 100%;
+}
+
+.hover\:translate-x-0:hover {
+  --transform-translate-x: 0;
+}
+
+.hover\:translate-x-1:hover {
+  --transform-translate-x: 0.25rem;
+}
+
+.hover\:translate-x-2:hover {
+  --transform-translate-x: 0.5rem;
+}
+
+.hover\:translate-x-3:hover {
+  --transform-translate-x: 0.75rem;
+}
+
+.hover\:translate-x-4:hover {
+  --transform-translate-x: 1rem;
+}
+
+.hover\:translate-x-5:hover {
+  --transform-translate-x: 1.25rem;
+}
+
+.hover\:translate-x-6:hover {
+  --transform-translate-x: 1.5rem;
+}
+
+.hover\:translate-x-8:hover {
+  --transform-translate-x: 2rem;
+}
+
+.hover\:translate-x-10:hover {
+  --transform-translate-x: 2.5rem;
+}
+
+.hover\:translate-x-12:hover {
+  --transform-translate-x: 3rem;
+}
+
+.hover\:translate-x-16:hover {
+  --transform-translate-x: 4rem;
+}
+
+.hover\:translate-x-20:hover {
+  --transform-translate-x: 5rem;
+}
+
+.hover\:translate-x-24:hover {
+  --transform-translate-x: 6rem;
+}
+
+.hover\:translate-x-32:hover {
+  --transform-translate-x: 8rem;
+}
+
+.hover\:translate-x-40:hover {
+  --transform-translate-x: 10rem;
+}
+
+.hover\:translate-x-48:hover {
+  --transform-translate-x: 12rem;
+}
+
+.hover\:translate-x-56:hover {
+  --transform-translate-x: 14rem;
+}
+
+.hover\:translate-x-64:hover {
+  --transform-translate-x: 16rem;
+}
+
+.hover\:translate-x-px:hover {
+  --transform-translate-x: 1px;
+}
+
+.hover\:-translate-x-1:hover {
+  --transform-translate-x: -0.25rem;
+}
+
+.hover\:-translate-x-2:hover {
+  --transform-translate-x: -0.5rem;
+}
+
+.hover\:-translate-x-3:hover {
+  --transform-translate-x: -0.75rem;
+}
+
+.hover\:-translate-x-4:hover {
+  --transform-translate-x: -1rem;
+}
+
+.hover\:-translate-x-5:hover {
+  --transform-translate-x: -1.25rem;
+}
+
+.hover\:-translate-x-6:hover {
+  --transform-translate-x: -1.5rem;
+}
+
+.hover\:-translate-x-8:hover {
+  --transform-translate-x: -2rem;
+}
+
+.hover\:-translate-x-10:hover {
+  --transform-translate-x: -2.5rem;
+}
+
+.hover\:-translate-x-12:hover {
+  --transform-translate-x: -3rem;
+}
+
+.hover\:-translate-x-16:hover {
+  --transform-translate-x: -4rem;
+}
+
+.hover\:-translate-x-20:hover {
+  --transform-translate-x: -5rem;
+}
+
+.hover\:-translate-x-24:hover {
+  --transform-translate-x: -6rem;
+}
+
+.hover\:-translate-x-32:hover {
+  --transform-translate-x: -8rem;
+}
+
+.hover\:-translate-x-40:hover {
+  --transform-translate-x: -10rem;
+}
+
+.hover\:-translate-x-48:hover {
+  --transform-translate-x: -12rem;
+}
+
+.hover\:-translate-x-56:hover {
+  --transform-translate-x: -14rem;
+}
+
+.hover\:-translate-x-64:hover {
+  --transform-translate-x: -16rem;
+}
+
+.hover\:-translate-x-px:hover {
+  --transform-translate-x: -1px;
+}
+
+.hover\:-translate-x-full:hover {
+  --transform-translate-x: -100%;
+}
+
+.hover\:-translate-x-1\/2:hover {
+  --transform-translate-x: -50%;
+}
+
+.hover\:translate-x-1\/2:hover {
+  --transform-translate-x: 50%;
+}
+
+.hover\:translate-x-full:hover {
+  --transform-translate-x: 100%;
+}
+
+.hover\:translate-y-0:hover {
+  --transform-translate-y: 0;
+}
+
+.hover\:translate-y-1:hover {
+  --transform-translate-y: 0.25rem;
+}
+
+.hover\:translate-y-2:hover {
+  --transform-translate-y: 0.5rem;
+}
+
+.hover\:translate-y-3:hover {
+  --transform-translate-y: 0.75rem;
+}
+
+.hover\:translate-y-4:hover {
+  --transform-translate-y: 1rem;
+}
+
+.hover\:translate-y-5:hover {
+  --transform-translate-y: 1.25rem;
+}
+
+.hover\:translate-y-6:hover {
+  --transform-translate-y: 1.5rem;
+}
+
+.hover\:translate-y-8:hover {
+  --transform-translate-y: 2rem;
+}
+
+.hover\:translate-y-10:hover {
+  --transform-translate-y: 2.5rem;
+}
+
+.hover\:translate-y-12:hover {
+  --transform-translate-y: 3rem;
+}
+
+.hover\:translate-y-16:hover {
+  --transform-translate-y: 4rem;
+}
+
+.hover\:translate-y-20:hover {
+  --transform-translate-y: 5rem;
+}
+
+.hover\:translate-y-24:hover {
+  --transform-translate-y: 6rem;
+}
+
+.hover\:translate-y-32:hover {
+  --transform-translate-y: 8rem;
+}
+
+.hover\:translate-y-40:hover {
+  --transform-translate-y: 10rem;
+}
+
+.hover\:translate-y-48:hover {
+  --transform-translate-y: 12rem;
+}
+
+.hover\:translate-y-56:hover {
+  --transform-translate-y: 14rem;
+}
+
+.hover\:translate-y-64:hover {
+  --transform-translate-y: 16rem;
+}
+
+.hover\:translate-y-px:hover {
+  --transform-translate-y: 1px;
+}
+
+.hover\:-translate-y-1:hover {
+  --transform-translate-y: -0.25rem;
+}
+
+.hover\:-translate-y-2:hover {
+  --transform-translate-y: -0.5rem;
+}
+
+.hover\:-translate-y-3:hover {
+  --transform-translate-y: -0.75rem;
+}
+
+.hover\:-translate-y-4:hover {
+  --transform-translate-y: -1rem;
+}
+
+.hover\:-translate-y-5:hover {
+  --transform-translate-y: -1.25rem;
+}
+
+.hover\:-translate-y-6:hover {
+  --transform-translate-y: -1.5rem;
+}
+
+.hover\:-translate-y-8:hover {
+  --transform-translate-y: -2rem;
+}
+
+.hover\:-translate-y-10:hover {
+  --transform-translate-y: -2.5rem;
+}
+
+.hover\:-translate-y-12:hover {
+  --transform-translate-y: -3rem;
+}
+
+.hover\:-translate-y-16:hover {
+  --transform-translate-y: -4rem;
+}
+
+.hover\:-translate-y-20:hover {
+  --transform-translate-y: -5rem;
+}
+
+.hover\:-translate-y-24:hover {
+  --transform-translate-y: -6rem;
+}
+
+.hover\:-translate-y-32:hover {
+  --transform-translate-y: -8rem;
+}
+
+.hover\:-translate-y-40:hover {
+  --transform-translate-y: -10rem;
+}
+
+.hover\:-translate-y-48:hover {
+  --transform-translate-y: -12rem;
+}
+
+.hover\:-translate-y-56:hover {
+  --transform-translate-y: -14rem;
+}
+
+.hover\:-translate-y-64:hover {
+  --transform-translate-y: -16rem;
+}
+
+.hover\:-translate-y-px:hover {
+  --transform-translate-y: -1px;
+}
+
+.hover\:-translate-y-full:hover {
+  --transform-translate-y: -100%;
+}
+
+.hover\:-translate-y-1\/2:hover {
+  --transform-translate-y: -50%;
+}
+
+.hover\:translate-y-1\/2:hover {
+  --transform-translate-y: 50%;
+}
+
+.hover\:translate-y-full:hover {
+  --transform-translate-y: 100%;
+}
+
+.focus\:translate-x-0:focus {
+  --transform-translate-x: 0;
+}
+
+.focus\:translate-x-1:focus {
+  --transform-translate-x: 0.25rem;
+}
+
+.focus\:translate-x-2:focus {
+  --transform-translate-x: 0.5rem;
+}
+
+.focus\:translate-x-3:focus {
+  --transform-translate-x: 0.75rem;
+}
+
+.focus\:translate-x-4:focus {
+  --transform-translate-x: 1rem;
+}
+
+.focus\:translate-x-5:focus {
+  --transform-translate-x: 1.25rem;
+}
+
+.focus\:translate-x-6:focus {
+  --transform-translate-x: 1.5rem;
+}
+
+.focus\:translate-x-8:focus {
+  --transform-translate-x: 2rem;
+}
+
+.focus\:translate-x-10:focus {
+  --transform-translate-x: 2.5rem;
+}
+
+.focus\:translate-x-12:focus {
+  --transform-translate-x: 3rem;
+}
+
+.focus\:translate-x-16:focus {
+  --transform-translate-x: 4rem;
+}
+
+.focus\:translate-x-20:focus {
+  --transform-translate-x: 5rem;
+}
+
+.focus\:translate-x-24:focus {
+  --transform-translate-x: 6rem;
+}
+
+.focus\:translate-x-32:focus {
+  --transform-translate-x: 8rem;
+}
+
+.focus\:translate-x-40:focus {
+  --transform-translate-x: 10rem;
+}
+
+.focus\:translate-x-48:focus {
+  --transform-translate-x: 12rem;
+}
+
+.focus\:translate-x-56:focus {
+  --transform-translate-x: 14rem;
+}
+
+.focus\:translate-x-64:focus {
+  --transform-translate-x: 16rem;
+}
+
+.focus\:translate-x-px:focus {
+  --transform-translate-x: 1px;
+}
+
+.focus\:-translate-x-1:focus {
+  --transform-translate-x: -0.25rem;
+}
+
+.focus\:-translate-x-2:focus {
+  --transform-translate-x: -0.5rem;
+}
+
+.focus\:-translate-x-3:focus {
+  --transform-translate-x: -0.75rem;
+}
+
+.focus\:-translate-x-4:focus {
+  --transform-translate-x: -1rem;
+}
+
+.focus\:-translate-x-5:focus {
+  --transform-translate-x: -1.25rem;
+}
+
+.focus\:-translate-x-6:focus {
+  --transform-translate-x: -1.5rem;
+}
+
+.focus\:-translate-x-8:focus {
+  --transform-translate-x: -2rem;
+}
+
+.focus\:-translate-x-10:focus {
+  --transform-translate-x: -2.5rem;
+}
+
+.focus\:-translate-x-12:focus {
+  --transform-translate-x: -3rem;
+}
+
+.focus\:-translate-x-16:focus {
+  --transform-translate-x: -4rem;
+}
+
+.focus\:-translate-x-20:focus {
+  --transform-translate-x: -5rem;
+}
+
+.focus\:-translate-x-24:focus {
+  --transform-translate-x: -6rem;
+}
+
+.focus\:-translate-x-32:focus {
+  --transform-translate-x: -8rem;
+}
+
+.focus\:-translate-x-40:focus {
+  --transform-translate-x: -10rem;
+}
+
+.focus\:-translate-x-48:focus {
+  --transform-translate-x: -12rem;
+}
+
+.focus\:-translate-x-56:focus {
+  --transform-translate-x: -14rem;
+}
+
+.focus\:-translate-x-64:focus {
+  --transform-translate-x: -16rem;
+}
+
+.focus\:-translate-x-px:focus {
+  --transform-translate-x: -1px;
+}
+
+.focus\:-translate-x-full:focus {
+  --transform-translate-x: -100%;
+}
+
+.focus\:-translate-x-1\/2:focus {
+  --transform-translate-x: -50%;
+}
+
+.focus\:translate-x-1\/2:focus {
+  --transform-translate-x: 50%;
+}
+
+.focus\:translate-x-full:focus {
+  --transform-translate-x: 100%;
+}
+
+.focus\:translate-y-0:focus {
+  --transform-translate-y: 0;
+}
+
+.focus\:translate-y-1:focus {
+  --transform-translate-y: 0.25rem;
+}
+
+.focus\:translate-y-2:focus {
+  --transform-translate-y: 0.5rem;
+}
+
+.focus\:translate-y-3:focus {
+  --transform-translate-y: 0.75rem;
+}
+
+.focus\:translate-y-4:focus {
+  --transform-translate-y: 1rem;
+}
+
+.focus\:translate-y-5:focus {
+  --transform-translate-y: 1.25rem;
+}
+
+.focus\:translate-y-6:focus {
+  --transform-translate-y: 1.5rem;
+}
+
+.focus\:translate-y-8:focus {
+  --transform-translate-y: 2rem;
+}
+
+.focus\:translate-y-10:focus {
+  --transform-translate-y: 2.5rem;
+}
+
+.focus\:translate-y-12:focus {
+  --transform-translate-y: 3rem;
+}
+
+.focus\:translate-y-16:focus {
+  --transform-translate-y: 4rem;
+}
+
+.focus\:translate-y-20:focus {
+  --transform-translate-y: 5rem;
+}
+
+.focus\:translate-y-24:focus {
+  --transform-translate-y: 6rem;
+}
+
+.focus\:translate-y-32:focus {
+  --transform-translate-y: 8rem;
+}
+
+.focus\:translate-y-40:focus {
+  --transform-translate-y: 10rem;
+}
+
+.focus\:translate-y-48:focus {
+  --transform-translate-y: 12rem;
+}
+
+.focus\:translate-y-56:focus {
+  --transform-translate-y: 14rem;
+}
+
+.focus\:translate-y-64:focus {
+  --transform-translate-y: 16rem;
+}
+
+.focus\:translate-y-px:focus {
+  --transform-translate-y: 1px;
+}
+
+.focus\:-translate-y-1:focus {
+  --transform-translate-y: -0.25rem;
+}
+
+.focus\:-translate-y-2:focus {
+  --transform-translate-y: -0.5rem;
+}
+
+.focus\:-translate-y-3:focus {
+  --transform-translate-y: -0.75rem;
+}
+
+.focus\:-translate-y-4:focus {
+  --transform-translate-y: -1rem;
+}
+
+.focus\:-translate-y-5:focus {
+  --transform-translate-y: -1.25rem;
+}
+
+.focus\:-translate-y-6:focus {
+  --transform-translate-y: -1.5rem;
+}
+
+.focus\:-translate-y-8:focus {
+  --transform-translate-y: -2rem;
+}
+
+.focus\:-translate-y-10:focus {
+  --transform-translate-y: -2.5rem;
+}
+
+.focus\:-translate-y-12:focus {
+  --transform-translate-y: -3rem;
+}
+
+.focus\:-translate-y-16:focus {
+  --transform-translate-y: -4rem;
+}
+
+.focus\:-translate-y-20:focus {
+  --transform-translate-y: -5rem;
+}
+
+.focus\:-translate-y-24:focus {
+  --transform-translate-y: -6rem;
+}
+
+.focus\:-translate-y-32:focus {
+  --transform-translate-y: -8rem;
+}
+
+.focus\:-translate-y-40:focus {
+  --transform-translate-y: -10rem;
+}
+
+.focus\:-translate-y-48:focus {
+  --transform-translate-y: -12rem;
+}
+
+.focus\:-translate-y-56:focus {
+  --transform-translate-y: -14rem;
+}
+
+.focus\:-translate-y-64:focus {
+  --transform-translate-y: -16rem;
+}
+
+.focus\:-translate-y-px:focus {
+  --transform-translate-y: -1px;
+}
+
+.focus\:-translate-y-full:focus {
+  --transform-translate-y: -100%;
+}
+
+.focus\:-translate-y-1\/2:focus {
+  --transform-translate-y: -50%;
+}
+
+.focus\:translate-y-1\/2:focus {
+  --transform-translate-y: 50%;
+}
+
+.focus\:translate-y-full:focus {
+  --transform-translate-y: 100%;
+}
+
+.skew-x-0 {
+  --transform-skew-x: 0;
+}
+
+.skew-x-3 {
+  --transform-skew-x: 3deg;
+}
+
+.skew-x-6 {
+  --transform-skew-x: 6deg;
+}
+
+.skew-x-12 {
+  --transform-skew-x: 12deg;
+}
+
+.-skew-x-12 {
+  --transform-skew-x: -12deg;
+}
+
+.-skew-x-6 {
+  --transform-skew-x: -6deg;
+}
+
+.-skew-x-3 {
+  --transform-skew-x: -3deg;
+}
+
+.skew-y-0 {
+  --transform-skew-y: 0;
+}
+
+.skew-y-3 {
+  --transform-skew-y: 3deg;
+}
+
+.skew-y-6 {
+  --transform-skew-y: 6deg;
+}
+
+.skew-y-12 {
+  --transform-skew-y: 12deg;
+}
+
+.-skew-y-12 {
+  --transform-skew-y: -12deg;
+}
+
+.-skew-y-6 {
+  --transform-skew-y: -6deg;
+}
+
+.-skew-y-3 {
+  --transform-skew-y: -3deg;
+}
+
+.hover\:skew-x-0:hover {
+  --transform-skew-x: 0;
+}
+
+.hover\:skew-x-3:hover {
+  --transform-skew-x: 3deg;
+}
+
+.hover\:skew-x-6:hover {
+  --transform-skew-x: 6deg;
+}
+
+.hover\:skew-x-12:hover {
+  --transform-skew-x: 12deg;
+}
+
+.hover\:-skew-x-12:hover {
+  --transform-skew-x: -12deg;
+}
+
+.hover\:-skew-x-6:hover {
+  --transform-skew-x: -6deg;
+}
+
+.hover\:-skew-x-3:hover {
+  --transform-skew-x: -3deg;
+}
+
+.hover\:skew-y-0:hover {
+  --transform-skew-y: 0;
+}
+
+.hover\:skew-y-3:hover {
+  --transform-skew-y: 3deg;
+}
+
+.hover\:skew-y-6:hover {
+  --transform-skew-y: 6deg;
+}
+
+.hover\:skew-y-12:hover {
+  --transform-skew-y: 12deg;
+}
+
+.hover\:-skew-y-12:hover {
+  --transform-skew-y: -12deg;
+}
+
+.hover\:-skew-y-6:hover {
+  --transform-skew-y: -6deg;
+}
+
+.hover\:-skew-y-3:hover {
+  --transform-skew-y: -3deg;
+}
+
+.focus\:skew-x-0:focus {
+  --transform-skew-x: 0;
+}
+
+.focus\:skew-x-3:focus {
+  --transform-skew-x: 3deg;
+}
+
+.focus\:skew-x-6:focus {
+  --transform-skew-x: 6deg;
+}
+
+.focus\:skew-x-12:focus {
+  --transform-skew-x: 12deg;
+}
+
+.focus\:-skew-x-12:focus {
+  --transform-skew-x: -12deg;
+}
+
+.focus\:-skew-x-6:focus {
+  --transform-skew-x: -6deg;
+}
+
+.focus\:-skew-x-3:focus {
+  --transform-skew-x: -3deg;
+}
+
+.focus\:skew-y-0:focus {
+  --transform-skew-y: 0;
+}
+
+.focus\:skew-y-3:focus {
+  --transform-skew-y: 3deg;
+}
+
+.focus\:skew-y-6:focus {
+  --transform-skew-y: 6deg;
+}
+
+.focus\:skew-y-12:focus {
+  --transform-skew-y: 12deg;
+}
+
+.focus\:-skew-y-12:focus {
+  --transform-skew-y: -12deg;
+}
+
+.focus\:-skew-y-6:focus {
+  --transform-skew-y: -6deg;
+}
+
+.focus\:-skew-y-3:focus {
+  --transform-skew-y: -3deg;
+}
+
+.transition-none {
+  transition-property: none;
+}
+
+.transition-all {
+  transition-property: all;
+}
+
+.transition {
+  transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+}
+
+.transition-colors {
+  transition-property: background-color, border-color, color, fill, stroke;
+}
+
+.transition-opacity {
+  transition-property: opacity;
+}
+
+.transition-shadow {
+  transition-property: box-shadow;
+}
+
+.transition-transform {
+  transition-property: transform;
+}
+
+.ease-linear {
+  transition-timing-function: linear;
+}
+
+.ease-in {
+  transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+}
+
+.ease-out {
+  transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+}
+
+.ease-in-out {
+  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.duration-75 {
+  transition-duration: 75ms;
+}
+
+.duration-100 {
+  transition-duration: 100ms;
+}
+
+.duration-150 {
+  transition-duration: 150ms;
+}
+
+.duration-200 {
+  transition-duration: 200ms;
+}
+
+.duration-300 {
+  transition-duration: 300ms;
+}
+
+.duration-500 {
+  transition-duration: 500ms;
+}
+
+.duration-700 {
+  transition-duration: 700ms;
+}
+
+.duration-1000 {
+  transition-duration: 1000ms;
+}
+
+.delay-75 {
+  transition-delay: 75ms;
+}
+
+.delay-100 {
+  transition-delay: 100ms;
+}
+
+.delay-150 {
+  transition-delay: 150ms;
+}
+
+.delay-200 {
+  transition-delay: 200ms;
+}
+
+.delay-300 {
+  transition-delay: 300ms;
+}
+
+.delay-500 {
+  transition-delay: 500ms;
+}
+
+.delay-700 {
+  transition-delay: 700ms;
+}
+
+.delay-1000 {
+  transition-delay: 1000ms;
+}
+
+@-webkit-keyframes spin {
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes spin {
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+@-webkit-keyframes ping {
+  75%, 100% {
+    transform: scale(2);
+    opacity: 0;
+  }
+}
+
+@keyframes ping {
+  75%, 100% {
+    transform: scale(2);
+    opacity: 0;
+  }
+}
+
+@-webkit-keyframes pulse {
+  50% {
+    opacity: .5;
+  }
+}
+
+@keyframes pulse {
+  50% {
+    opacity: .5;
+  }
+}
+
+@-webkit-keyframes bounce {
+  0%, 100% {
+    transform: translateY(-25%);
+    -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);
+            animation-timing-function: cubic-bezier(0.8,0,1,1);
+  }
+
+  50% {
+    transform: none;
+    -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);
+            animation-timing-function: cubic-bezier(0,0,0.2,1);
+  }
+}
+
+@keyframes bounce {
+  0%, 100% {
+    transform: translateY(-25%);
+    -webkit-animation-timing-function: cubic-bezier(0.8,0,1,1);
+            animation-timing-function: cubic-bezier(0.8,0,1,1);
+  }
+
+  50% {
+    transform: none;
+    -webkit-animation-timing-function: cubic-bezier(0,0,0.2,1);
+            animation-timing-function: cubic-bezier(0,0,0.2,1);
+  }
+}
+
+.animate-none {
+  -webkit-animation: none;
+          animation: none;
+}
+
+.animate-spin {
+  -webkit-animation: spin 1s linear infinite;
+          animation: spin 1s linear infinite;
+}
+
+.animate-ping {
+  -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+          animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+}
+
+.animate-pulse {
+  -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+          animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+}
+
+.animate-bounce {
+  -webkit-animation: bounce 1s infinite;
+          animation: bounce 1s infinite;
+}
+
+@media (min-width: 640px) {
+  .sm\:container {
+    width: 100%;
+  }
+
+  @media (min-width: 640px) {
+    .sm\:container {
+      max-width: 640px;
+    }
+  }
+
+  @media (min-width: 768px) {
+    .sm\:container {
+      max-width: 768px;
+    }
+  }
+
+  @media (min-width: 1024px) {
+    .sm\:container {
+      max-width: 1024px;
+    }
+  }
+
+  @media (min-width: 1280px) {
+    .sm\:container {
+      max-width: 1280px;
+    }
+  }
+
+  .sm\:space-y-0 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0px * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-0 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0px * var(--space-x-reverse));
+    margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.25rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.25rem * var(--space-x-reverse));
+    margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.5rem * var(--space-x-reverse));
+    margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.75rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.75rem * var(--space-x-reverse));
+    margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1rem * var(--space-x-reverse));
+    margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.25rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.25rem * var(--space-x-reverse));
+    margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.5rem * var(--space-x-reverse));
+    margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2rem * var(--space-x-reverse));
+    margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2.5rem * var(--space-x-reverse));
+    margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(3rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(3rem * var(--space-x-reverse));
+    margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(4rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(4rem * var(--space-x-reverse));
+    margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(5rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(5rem * var(--space-x-reverse));
+    margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(6rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(6rem * var(--space-x-reverse));
+    margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(8rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(8rem * var(--space-x-reverse));
+    margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(10rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(10rem * var(--space-x-reverse));
+    margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(12rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(12rem * var(--space-x-reverse));
+    margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(14rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(14rem * var(--space-x-reverse));
+    margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(16rem * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(16rem * var(--space-x-reverse));
+    margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1px * var(--space-y-reverse));
+  }
+
+  .sm\:space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1px * var(--space-x-reverse));
+    margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.25rem * var(--space-x-reverse));
+    margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.5rem * var(--space-x-reverse));
+    margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.75rem * var(--space-x-reverse));
+    margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1rem * var(--space-x-reverse));
+    margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.25rem * var(--space-x-reverse));
+    margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.5rem * var(--space-x-reverse));
+    margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2rem * var(--space-x-reverse));
+    margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2.5rem * var(--space-x-reverse));
+    margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-3rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-3rem * var(--space-x-reverse));
+    margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-4rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-4rem * var(--space-x-reverse));
+    margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-5rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-5rem * var(--space-x-reverse));
+    margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-6rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-6rem * var(--space-x-reverse));
+    margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-8rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-8rem * var(--space-x-reverse));
+    margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-10rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-10rem * var(--space-x-reverse));
+    margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-12rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-12rem * var(--space-x-reverse));
+    margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-14rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-14rem * var(--space-x-reverse));
+    margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-16rem * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-16rem * var(--space-x-reverse));
+    margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:-space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1px * var(--space-y-reverse));
+  }
+
+  .sm\:-space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1px * var(--space-x-reverse));
+    margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .sm\:space-y-reverse > :not(template) ~ :not(template) {
+    --space-y-reverse: 1;
+  }
+
+  .sm\:space-x-reverse > :not(template) ~ :not(template) {
+    --space-x-reverse: 1;
+  }
+
+  .sm\:divide-y-0 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(0px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x-0 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(0px * var(--divide-x-reverse));
+    border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y-2 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(2px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x-2 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(2px * var(--divide-x-reverse));
+    border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y-4 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(4px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x-4 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(4px * var(--divide-x-reverse));
+    border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y-8 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(8px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x-8 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(8px * var(--divide-x-reverse));
+    border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(1px * var(--divide-y-reverse));
+  }
+
+  .sm\:divide-x > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(1px * var(--divide-x-reverse));
+    border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .sm\:divide-y-reverse > :not(template) ~ :not(template) {
+    --divide-y-reverse: 1;
+  }
+
+  .sm\:divide-x-reverse > :not(template) ~ :not(template) {
+    --divide-x-reverse: 1;
+  }
+
+  .sm\:divide-transparent > :not(template) ~ :not(template) {
+    border-color: transparent;
+  }
+
+  .sm\:divide-current > :not(template) ~ :not(template) {
+    border-color: currentColor;
+  }
+
+  .sm\:divide-black > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--divide-opacity));
+  }
+
+  .sm\:divide-white > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--divide-opacity));
+  }
+
+  .sm\:divide-gray-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--divide-opacity));
+  }
+
+  .sm\:divide-red-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--divide-opacity));
+  }
+
+  .sm\:divide-orange-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--divide-opacity));
+  }
+
+  .sm\:divide-yellow-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--divide-opacity));
+  }
+
+  .sm\:divide-green-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--divide-opacity));
+  }
+
+  .sm\:divide-teal-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--divide-opacity));
+  }
+
+  .sm\:divide-blue-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--divide-opacity));
+  }
+
+  .sm\:divide-indigo-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--divide-opacity));
+  }
+
+  .sm\:divide-purple-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--divide-opacity));
+  }
+
+  .sm\:divide-pink-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--divide-opacity));
+  }
+
+  .sm\:divide-solid > :not(template) ~ :not(template) {
+    border-style: solid;
+  }
+
+  .sm\:divide-dashed > :not(template) ~ :not(template) {
+    border-style: dashed;
+  }
+
+  .sm\:divide-dotted > :not(template) ~ :not(template) {
+    border-style: dotted;
+  }
+
+  .sm\:divide-double > :not(template) ~ :not(template) {
+    border-style: double;
+  }
+
+  .sm\:divide-none > :not(template) ~ :not(template) {
+    border-style: none;
+  }
+
+  .sm\:divide-opacity-0 > :not(template) ~ :not(template) {
+    --divide-opacity: 0;
+  }
+
+  .sm\:divide-opacity-25 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.25;
+  }
+
+  .sm\:divide-opacity-50 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.5;
+  }
+
+  .sm\:divide-opacity-75 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.75;
+  }
+
+  .sm\:divide-opacity-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+  }
+
+  .sm\:sr-only {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .sm\:not-sr-only {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .sm\:focus\:sr-only:focus {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .sm\:focus\:not-sr-only:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .sm\:appearance-none {
+    -webkit-appearance: none;
+       -moz-appearance: none;
+            appearance: none;
+  }
+
+  .sm\:bg-fixed {
+    background-attachment: fixed;
+  }
+
+  .sm\:bg-local {
+    background-attachment: local;
+  }
+
+  .sm\:bg-scroll {
+    background-attachment: scroll;
+  }
+
+  .sm\:bg-clip-border {
+    background-clip: border-box;
+  }
+
+  .sm\:bg-clip-padding {
+    background-clip: padding-box;
+  }
+
+  .sm\:bg-clip-content {
+    background-clip: content-box;
+  }
+
+  .sm\:bg-clip-text {
+    -webkit-background-clip: text;
+            background-clip: text;
+  }
+
+  .sm\:bg-transparent {
+    background-color: transparent;
+  }
+
+  .sm\:bg-current {
+    background-color: currentColor;
+  }
+
+  .sm\:bg-black {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .sm\:bg-white {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-100 {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-200 {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-300 {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-400 {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-500 {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-600 {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-700 {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-800 {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .sm\:bg-gray-900 {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-200 {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-300 {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-400 {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-500 {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-600 {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-700 {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-800 {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .sm\:bg-red-900 {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-100 {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-200 {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-300 {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-400 {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-500 {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-600 {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-700 {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-800 {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .sm\:bg-orange-900 {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-100 {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-200 {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-300 {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-400 {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-500 {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-600 {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-700 {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-800 {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .sm\:bg-yellow-900 {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-100 {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-200 {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-300 {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-400 {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-500 {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-600 {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-700 {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-800 {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .sm\:bg-green-900 {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-100 {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-200 {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-300 {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-400 {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-500 {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-600 {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-700 {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-800 {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .sm\:bg-teal-900 {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-100 {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-200 {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-300 {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-400 {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-500 {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-600 {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-700 {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-800 {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .sm\:bg-blue-900 {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-100 {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-200 {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-300 {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-400 {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-500 {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-600 {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-700 {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-800 {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .sm\:bg-indigo-900 {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-100 {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-200 {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-300 {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-400 {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-500 {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-600 {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-700 {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-800 {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .sm\:bg-purple-900 {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-200 {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-300 {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-400 {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-500 {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-600 {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-700 {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-800 {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .sm\:bg-pink-900 {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-transparent:hover {
+    background-color: transparent;
+  }
+
+  .sm\:hover\:bg-current:hover {
+    background-color: currentColor;
+  }
+
+  .sm\:hover\:bg-black:hover {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-white:hover {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-100:hover {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-200:hover {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-300:hover {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-400:hover {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-500:hover {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-600:hover {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-700:hover {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-800:hover {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-gray-900:hover {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-300:hover {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-400:hover {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-500:hover {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-600:hover {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-700:hover {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-800:hover {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-red-900:hover {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-200:hover {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-600:hover {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-700:hover {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-800:hover {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-orange-900:hover {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-200:hover {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-300:hover {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-500:hover {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-600:hover {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-700:hover {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-800:hover {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-yellow-900:hover {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-100:hover {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-200:hover {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-300:hover {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-400:hover {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-500:hover {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-600:hover {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-700:hover {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-800:hover {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-green-900:hover {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-100:hover {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-200:hover {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-300:hover {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-400:hover {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-500:hover {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-600:hover {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-700:hover {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-800:hover {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-teal-900:hover {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-200:hover {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-300:hover {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-400:hover {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-500:hover {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-600:hover {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-700:hover {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-800:hover {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-blue-900:hover {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-200:hover {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-300:hover {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-400:hover {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-500:hover {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-600:hover {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-700:hover {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-800:hover {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-indigo-900:hover {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-100:hover {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-200:hover {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-300:hover {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-400:hover {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-500:hover {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-600:hover {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-700:hover {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-800:hover {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-purple-900:hover {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-400:hover {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-600:hover {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-700:hover {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-800:hover {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .sm\:hover\:bg-pink-900:hover {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-transparent:focus {
+    background-color: transparent;
+  }
+
+  .sm\:focus\:bg-current:focus {
+    background-color: currentColor;
+  }
+
+  .sm\:focus\:bg-black:focus {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-white:focus {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-100:focus {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-200:focus {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-300:focus {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-400:focus {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-500:focus {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-600:focus {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-700:focus {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-800:focus {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-gray-900:focus {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-300:focus {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-400:focus {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-500:focus {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-600:focus {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-700:focus {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-800:focus {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-red-900:focus {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-200:focus {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-600:focus {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-700:focus {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-800:focus {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-orange-900:focus {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-200:focus {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-300:focus {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-500:focus {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-600:focus {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-700:focus {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-800:focus {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-yellow-900:focus {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-100:focus {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-200:focus {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-300:focus {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-400:focus {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-500:focus {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-600:focus {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-700:focus {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-800:focus {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-green-900:focus {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-100:focus {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-200:focus {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-300:focus {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-400:focus {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-500:focus {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-600:focus {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-700:focus {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-800:focus {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-teal-900:focus {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-200:focus {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-300:focus {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-400:focus {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-500:focus {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-600:focus {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-700:focus {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-800:focus {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-blue-900:focus {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-200:focus {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-300:focus {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-400:focus {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-500:focus {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-600:focus {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-700:focus {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-800:focus {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-indigo-900:focus {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-100:focus {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-200:focus {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-300:focus {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-400:focus {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-500:focus {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-600:focus {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-700:focus {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-800:focus {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-purple-900:focus {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-400:focus {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-600:focus {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-700:focus {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-800:focus {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .sm\:focus\:bg-pink-900:focus {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .sm\:bg-none {
+    background-image: none;
+  }
+
+  .sm\:bg-gradient-to-t {
+    background-image: linear-gradient(to top, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-tr {
+    background-image: linear-gradient(to top right, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-r {
+    background-image: linear-gradient(to right, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-br {
+    background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-b {
+    background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-bl {
+    background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-l {
+    background-image: linear-gradient(to left, var(--gradient-color-stops));
+  }
+
+  .sm\:bg-gradient-to-tl {
+    background-image: linear-gradient(to top left, var(--gradient-color-stops));
+  }
+
+  .sm\:from-transparent {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:from-current {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:from-black {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:from-white {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:from-gray-100 {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:from-gray-200 {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:from-gray-300 {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:from-gray-400 {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:from-gray-500 {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:from-gray-600 {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:from-gray-700 {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:from-gray-800 {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:from-gray-900 {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:from-red-100 {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:from-red-200 {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:from-red-300 {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:from-red-400 {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:from-red-500 {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:from-red-600 {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:from-red-700 {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:from-red-800 {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:from-red-900 {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:from-orange-100 {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:from-orange-200 {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:from-orange-300 {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:from-orange-400 {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:from-orange-500 {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:from-orange-600 {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:from-orange-700 {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:from-orange-800 {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:from-orange-900 {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:from-yellow-100 {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:from-yellow-200 {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:from-yellow-300 {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:from-yellow-400 {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:from-yellow-500 {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:from-yellow-600 {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:from-yellow-700 {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:from-yellow-800 {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:from-yellow-900 {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:from-green-100 {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:from-green-200 {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:from-green-300 {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:from-green-400 {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:from-green-500 {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:from-green-600 {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:from-green-700 {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:from-green-800 {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:from-green-900 {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:from-teal-100 {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:from-teal-200 {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:from-teal-300 {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:from-teal-400 {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:from-teal-500 {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:from-teal-600 {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:from-teal-700 {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:from-teal-800 {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:from-teal-900 {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:from-blue-100 {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:from-blue-200 {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:from-blue-300 {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:from-blue-400 {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:from-blue-500 {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:from-blue-600 {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:from-blue-700 {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:from-blue-800 {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:from-blue-900 {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:from-indigo-100 {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:from-indigo-200 {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:from-indigo-300 {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:from-indigo-400 {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:from-indigo-500 {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:from-indigo-600 {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:from-indigo-700 {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:from-indigo-800 {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:from-indigo-900 {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:from-purple-100 {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:from-purple-200 {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:from-purple-300 {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:from-purple-400 {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:from-purple-500 {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:from-purple-600 {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:from-purple-700 {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:from-purple-800 {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:from-purple-900 {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:from-pink-100 {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:from-pink-200 {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:from-pink-300 {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:from-pink-400 {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:from-pink-500 {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:from-pink-600 {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:from-pink-700 {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:from-pink-800 {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:from-pink-900 {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:via-transparent {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:via-current {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:via-black {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:via-white {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:via-gray-100 {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:via-gray-200 {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:via-gray-300 {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:via-gray-400 {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:via-gray-500 {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:via-gray-600 {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:via-gray-700 {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:via-gray-800 {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:via-gray-900 {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:via-red-100 {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:via-red-200 {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:via-red-300 {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:via-red-400 {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:via-red-500 {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:via-red-600 {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:via-red-700 {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:via-red-800 {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:via-red-900 {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:via-orange-100 {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:via-orange-200 {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:via-orange-300 {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:via-orange-400 {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:via-orange-500 {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:via-orange-600 {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:via-orange-700 {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:via-orange-800 {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:via-orange-900 {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:via-yellow-100 {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:via-yellow-200 {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:via-yellow-300 {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:via-yellow-400 {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:via-yellow-500 {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:via-yellow-600 {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:via-yellow-700 {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:via-yellow-800 {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:via-yellow-900 {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:via-green-100 {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:via-green-200 {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:via-green-300 {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:via-green-400 {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:via-green-500 {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:via-green-600 {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:via-green-700 {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:via-green-800 {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:via-green-900 {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:via-teal-100 {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:via-teal-200 {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:via-teal-300 {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:via-teal-400 {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:via-teal-500 {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:via-teal-600 {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:via-teal-700 {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:via-teal-800 {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:via-teal-900 {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:via-blue-100 {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:via-blue-200 {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:via-blue-300 {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:via-blue-400 {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:via-blue-500 {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:via-blue-600 {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:via-blue-700 {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:via-blue-800 {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:via-blue-900 {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:via-indigo-100 {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:via-indigo-200 {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:via-indigo-300 {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:via-indigo-400 {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:via-indigo-500 {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:via-indigo-600 {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:via-indigo-700 {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:via-indigo-800 {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:via-indigo-900 {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:via-purple-100 {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:via-purple-200 {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:via-purple-300 {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:via-purple-400 {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:via-purple-500 {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:via-purple-600 {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:via-purple-700 {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:via-purple-800 {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:via-purple-900 {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:via-pink-100 {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:via-pink-200 {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:via-pink-300 {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:via-pink-400 {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:via-pink-500 {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:via-pink-600 {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:via-pink-700 {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:via-pink-800 {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:via-pink-900 {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:to-transparent {
+    --gradient-to-color: transparent;
+  }
+
+  .sm\:to-current {
+    --gradient-to-color: currentColor;
+  }
+
+  .sm\:to-black {
+    --gradient-to-color: #000;
+  }
+
+  .sm\:to-white {
+    --gradient-to-color: #fff;
+  }
+
+  .sm\:to-gray-100 {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .sm\:to-gray-200 {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .sm\:to-gray-300 {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .sm\:to-gray-400 {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .sm\:to-gray-500 {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .sm\:to-gray-600 {
+    --gradient-to-color: #718096;
+  }
+
+  .sm\:to-gray-700 {
+    --gradient-to-color: #4a5568;
+  }
+
+  .sm\:to-gray-800 {
+    --gradient-to-color: #2d3748;
+  }
+
+  .sm\:to-gray-900 {
+    --gradient-to-color: #1a202c;
+  }
+
+  .sm\:to-red-100 {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .sm\:to-red-200 {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .sm\:to-red-300 {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .sm\:to-red-400 {
+    --gradient-to-color: #fc8181;
+  }
+
+  .sm\:to-red-500 {
+    --gradient-to-color: #f56565;
+  }
+
+  .sm\:to-red-600 {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .sm\:to-red-700 {
+    --gradient-to-color: #c53030;
+  }
+
+  .sm\:to-red-800 {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .sm\:to-red-900 {
+    --gradient-to-color: #742a2a;
+  }
+
+  .sm\:to-orange-100 {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .sm\:to-orange-200 {
+    --gradient-to-color: #feebc8;
+  }
+
+  .sm\:to-orange-300 {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .sm\:to-orange-400 {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .sm\:to-orange-500 {
+    --gradient-to-color: #ed8936;
+  }
+
+  .sm\:to-orange-600 {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .sm\:to-orange-700 {
+    --gradient-to-color: #c05621;
+  }
+
+  .sm\:to-orange-800 {
+    --gradient-to-color: #9c4221;
+  }
+
+  .sm\:to-orange-900 {
+    --gradient-to-color: #7b341e;
+  }
+
+  .sm\:to-yellow-100 {
+    --gradient-to-color: #fffff0;
+  }
+
+  .sm\:to-yellow-200 {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .sm\:to-yellow-300 {
+    --gradient-to-color: #faf089;
+  }
+
+  .sm\:to-yellow-400 {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .sm\:to-yellow-500 {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .sm\:to-yellow-600 {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .sm\:to-yellow-700 {
+    --gradient-to-color: #b7791f;
+  }
+
+  .sm\:to-yellow-800 {
+    --gradient-to-color: #975a16;
+  }
+
+  .sm\:to-yellow-900 {
+    --gradient-to-color: #744210;
+  }
+
+  .sm\:to-green-100 {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .sm\:to-green-200 {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .sm\:to-green-300 {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .sm\:to-green-400 {
+    --gradient-to-color: #68d391;
+  }
+
+  .sm\:to-green-500 {
+    --gradient-to-color: #48bb78;
+  }
+
+  .sm\:to-green-600 {
+    --gradient-to-color: #38a169;
+  }
+
+  .sm\:to-green-700 {
+    --gradient-to-color: #2f855a;
+  }
+
+  .sm\:to-green-800 {
+    --gradient-to-color: #276749;
+  }
+
+  .sm\:to-green-900 {
+    --gradient-to-color: #22543d;
+  }
+
+  .sm\:to-teal-100 {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .sm\:to-teal-200 {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .sm\:to-teal-300 {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .sm\:to-teal-400 {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .sm\:to-teal-500 {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .sm\:to-teal-600 {
+    --gradient-to-color: #319795;
+  }
+
+  .sm\:to-teal-700 {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .sm\:to-teal-800 {
+    --gradient-to-color: #285e61;
+  }
+
+  .sm\:to-teal-900 {
+    --gradient-to-color: #234e52;
+  }
+
+  .sm\:to-blue-100 {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .sm\:to-blue-200 {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .sm\:to-blue-300 {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .sm\:to-blue-400 {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .sm\:to-blue-500 {
+    --gradient-to-color: #4299e1;
+  }
+
+  .sm\:to-blue-600 {
+    --gradient-to-color: #3182ce;
+  }
+
+  .sm\:to-blue-700 {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .sm\:to-blue-800 {
+    --gradient-to-color: #2c5282;
+  }
+
+  .sm\:to-blue-900 {
+    --gradient-to-color: #2a4365;
+  }
+
+  .sm\:to-indigo-100 {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .sm\:to-indigo-200 {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .sm\:to-indigo-300 {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .sm\:to-indigo-400 {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .sm\:to-indigo-500 {
+    --gradient-to-color: #667eea;
+  }
+
+  .sm\:to-indigo-600 {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .sm\:to-indigo-700 {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .sm\:to-indigo-800 {
+    --gradient-to-color: #434190;
+  }
+
+  .sm\:to-indigo-900 {
+    --gradient-to-color: #3c366b;
+  }
+
+  .sm\:to-purple-100 {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .sm\:to-purple-200 {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .sm\:to-purple-300 {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .sm\:to-purple-400 {
+    --gradient-to-color: #b794f4;
+  }
+
+  .sm\:to-purple-500 {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .sm\:to-purple-600 {
+    --gradient-to-color: #805ad5;
+  }
+
+  .sm\:to-purple-700 {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .sm\:to-purple-800 {
+    --gradient-to-color: #553c9a;
+  }
+
+  .sm\:to-purple-900 {
+    --gradient-to-color: #44337a;
+  }
+
+  .sm\:to-pink-100 {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .sm\:to-pink-200 {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .sm\:to-pink-300 {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .sm\:to-pink-400 {
+    --gradient-to-color: #f687b3;
+  }
+
+  .sm\:to-pink-500 {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .sm\:to-pink-600 {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .sm\:to-pink-700 {
+    --gradient-to-color: #b83280;
+  }
+
+  .sm\:to-pink-800 {
+    --gradient-to-color: #97266d;
+  }
+
+  .sm\:to-pink-900 {
+    --gradient-to-color: #702459;
+  }
+
+  .sm\:hover\:from-transparent:hover {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:hover\:from-current:hover {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:hover\:from-black:hover {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:hover\:from-white:hover {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:hover\:from-gray-100:hover {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:hover\:from-gray-200:hover {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:hover\:from-gray-300:hover {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:hover\:from-gray-400:hover {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:hover\:from-gray-500:hover {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:hover\:from-gray-600:hover {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:hover\:from-gray-700:hover {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:hover\:from-gray-800:hover {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:hover\:from-gray-900:hover {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:hover\:from-red-100:hover {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:hover\:from-red-200:hover {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:hover\:from-red-300:hover {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:hover\:from-red-400:hover {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:hover\:from-red-500:hover {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:hover\:from-red-600:hover {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:hover\:from-red-700:hover {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:hover\:from-red-800:hover {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:hover\:from-red-900:hover {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:hover\:from-orange-100:hover {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:hover\:from-orange-200:hover {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:hover\:from-orange-300:hover {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:hover\:from-orange-400:hover {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:hover\:from-orange-500:hover {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:hover\:from-orange-600:hover {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:hover\:from-orange-700:hover {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:hover\:from-orange-800:hover {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:hover\:from-orange-900:hover {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:hover\:from-yellow-100:hover {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:hover\:from-yellow-200:hover {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:hover\:from-yellow-300:hover {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:hover\:from-yellow-400:hover {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:hover\:from-yellow-500:hover {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:hover\:from-yellow-600:hover {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:hover\:from-yellow-700:hover {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:hover\:from-yellow-800:hover {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:hover\:from-yellow-900:hover {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:hover\:from-green-100:hover {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:hover\:from-green-200:hover {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:hover\:from-green-300:hover {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:hover\:from-green-400:hover {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:hover\:from-green-500:hover {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:hover\:from-green-600:hover {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:hover\:from-green-700:hover {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:hover\:from-green-800:hover {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:hover\:from-green-900:hover {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:hover\:from-teal-100:hover {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:hover\:from-teal-200:hover {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:hover\:from-teal-300:hover {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:hover\:from-teal-400:hover {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:hover\:from-teal-500:hover {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:hover\:from-teal-600:hover {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:hover\:from-teal-700:hover {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:hover\:from-teal-800:hover {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:hover\:from-teal-900:hover {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:hover\:from-blue-100:hover {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:hover\:from-blue-200:hover {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:hover\:from-blue-300:hover {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:hover\:from-blue-400:hover {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:hover\:from-blue-500:hover {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:hover\:from-blue-600:hover {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:hover\:from-blue-700:hover {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:hover\:from-blue-800:hover {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:hover\:from-blue-900:hover {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:hover\:from-indigo-100:hover {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:hover\:from-indigo-200:hover {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:hover\:from-indigo-300:hover {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:hover\:from-indigo-400:hover {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:hover\:from-indigo-500:hover {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:hover\:from-indigo-600:hover {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:hover\:from-indigo-700:hover {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:hover\:from-indigo-800:hover {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:hover\:from-indigo-900:hover {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:hover\:from-purple-100:hover {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:hover\:from-purple-200:hover {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:hover\:from-purple-300:hover {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:hover\:from-purple-400:hover {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:hover\:from-purple-500:hover {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:hover\:from-purple-600:hover {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:hover\:from-purple-700:hover {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:hover\:from-purple-800:hover {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:hover\:from-purple-900:hover {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:hover\:from-pink-100:hover {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:hover\:from-pink-200:hover {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:hover\:from-pink-300:hover {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:hover\:from-pink-400:hover {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:hover\:from-pink-500:hover {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:hover\:from-pink-600:hover {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:hover\:from-pink-700:hover {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:hover\:from-pink-800:hover {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:hover\:from-pink-900:hover {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:hover\:via-transparent:hover {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:hover\:via-current:hover {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:hover\:via-black:hover {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:hover\:via-white:hover {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:hover\:via-gray-100:hover {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:hover\:via-gray-200:hover {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:hover\:via-gray-300:hover {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:hover\:via-gray-400:hover {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:hover\:via-gray-500:hover {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:hover\:via-gray-600:hover {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:hover\:via-gray-700:hover {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:hover\:via-gray-800:hover {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:hover\:via-gray-900:hover {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:hover\:via-red-100:hover {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:hover\:via-red-200:hover {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:hover\:via-red-300:hover {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:hover\:via-red-400:hover {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:hover\:via-red-500:hover {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:hover\:via-red-600:hover {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:hover\:via-red-700:hover {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:hover\:via-red-800:hover {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:hover\:via-red-900:hover {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:hover\:via-orange-100:hover {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:hover\:via-orange-200:hover {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:hover\:via-orange-300:hover {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:hover\:via-orange-400:hover {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:hover\:via-orange-500:hover {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:hover\:via-orange-600:hover {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:hover\:via-orange-700:hover {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:hover\:via-orange-800:hover {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:hover\:via-orange-900:hover {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:hover\:via-yellow-100:hover {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:hover\:via-yellow-200:hover {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:hover\:via-yellow-300:hover {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:hover\:via-yellow-400:hover {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:hover\:via-yellow-500:hover {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:hover\:via-yellow-600:hover {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:hover\:via-yellow-700:hover {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:hover\:via-yellow-800:hover {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:hover\:via-yellow-900:hover {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:hover\:via-green-100:hover {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:hover\:via-green-200:hover {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:hover\:via-green-300:hover {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:hover\:via-green-400:hover {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:hover\:via-green-500:hover {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:hover\:via-green-600:hover {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:hover\:via-green-700:hover {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:hover\:via-green-800:hover {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:hover\:via-green-900:hover {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:hover\:via-teal-100:hover {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:hover\:via-teal-200:hover {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:hover\:via-teal-300:hover {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:hover\:via-teal-400:hover {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:hover\:via-teal-500:hover {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:hover\:via-teal-600:hover {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:hover\:via-teal-700:hover {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:hover\:via-teal-800:hover {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:hover\:via-teal-900:hover {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:hover\:via-blue-100:hover {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:hover\:via-blue-200:hover {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:hover\:via-blue-300:hover {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:hover\:via-blue-400:hover {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:hover\:via-blue-500:hover {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:hover\:via-blue-600:hover {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:hover\:via-blue-700:hover {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:hover\:via-blue-800:hover {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:hover\:via-blue-900:hover {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:hover\:via-indigo-100:hover {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:hover\:via-indigo-200:hover {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:hover\:via-indigo-300:hover {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:hover\:via-indigo-400:hover {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:hover\:via-indigo-500:hover {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:hover\:via-indigo-600:hover {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:hover\:via-indigo-700:hover {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:hover\:via-indigo-800:hover {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:hover\:via-indigo-900:hover {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:hover\:via-purple-100:hover {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:hover\:via-purple-200:hover {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:hover\:via-purple-300:hover {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:hover\:via-purple-400:hover {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:hover\:via-purple-500:hover {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:hover\:via-purple-600:hover {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:hover\:via-purple-700:hover {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:hover\:via-purple-800:hover {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:hover\:via-purple-900:hover {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:hover\:via-pink-100:hover {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:hover\:via-pink-200:hover {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:hover\:via-pink-300:hover {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:hover\:via-pink-400:hover {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:hover\:via-pink-500:hover {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:hover\:via-pink-600:hover {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:hover\:via-pink-700:hover {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:hover\:via-pink-800:hover {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:hover\:via-pink-900:hover {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:hover\:to-transparent:hover {
+    --gradient-to-color: transparent;
+  }
+
+  .sm\:hover\:to-current:hover {
+    --gradient-to-color: currentColor;
+  }
+
+  .sm\:hover\:to-black:hover {
+    --gradient-to-color: #000;
+  }
+
+  .sm\:hover\:to-white:hover {
+    --gradient-to-color: #fff;
+  }
+
+  .sm\:hover\:to-gray-100:hover {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .sm\:hover\:to-gray-200:hover {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .sm\:hover\:to-gray-300:hover {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .sm\:hover\:to-gray-400:hover {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .sm\:hover\:to-gray-500:hover {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .sm\:hover\:to-gray-600:hover {
+    --gradient-to-color: #718096;
+  }
+
+  .sm\:hover\:to-gray-700:hover {
+    --gradient-to-color: #4a5568;
+  }
+
+  .sm\:hover\:to-gray-800:hover {
+    --gradient-to-color: #2d3748;
+  }
+
+  .sm\:hover\:to-gray-900:hover {
+    --gradient-to-color: #1a202c;
+  }
+
+  .sm\:hover\:to-red-100:hover {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .sm\:hover\:to-red-200:hover {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .sm\:hover\:to-red-300:hover {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .sm\:hover\:to-red-400:hover {
+    --gradient-to-color: #fc8181;
+  }
+
+  .sm\:hover\:to-red-500:hover {
+    --gradient-to-color: #f56565;
+  }
+
+  .sm\:hover\:to-red-600:hover {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .sm\:hover\:to-red-700:hover {
+    --gradient-to-color: #c53030;
+  }
+
+  .sm\:hover\:to-red-800:hover {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .sm\:hover\:to-red-900:hover {
+    --gradient-to-color: #742a2a;
+  }
+
+  .sm\:hover\:to-orange-100:hover {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .sm\:hover\:to-orange-200:hover {
+    --gradient-to-color: #feebc8;
+  }
+
+  .sm\:hover\:to-orange-300:hover {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .sm\:hover\:to-orange-400:hover {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .sm\:hover\:to-orange-500:hover {
+    --gradient-to-color: #ed8936;
+  }
+
+  .sm\:hover\:to-orange-600:hover {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .sm\:hover\:to-orange-700:hover {
+    --gradient-to-color: #c05621;
+  }
+
+  .sm\:hover\:to-orange-800:hover {
+    --gradient-to-color: #9c4221;
+  }
+
+  .sm\:hover\:to-orange-900:hover {
+    --gradient-to-color: #7b341e;
+  }
+
+  .sm\:hover\:to-yellow-100:hover {
+    --gradient-to-color: #fffff0;
+  }
+
+  .sm\:hover\:to-yellow-200:hover {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .sm\:hover\:to-yellow-300:hover {
+    --gradient-to-color: #faf089;
+  }
+
+  .sm\:hover\:to-yellow-400:hover {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .sm\:hover\:to-yellow-500:hover {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .sm\:hover\:to-yellow-600:hover {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .sm\:hover\:to-yellow-700:hover {
+    --gradient-to-color: #b7791f;
+  }
+
+  .sm\:hover\:to-yellow-800:hover {
+    --gradient-to-color: #975a16;
+  }
+
+  .sm\:hover\:to-yellow-900:hover {
+    --gradient-to-color: #744210;
+  }
+
+  .sm\:hover\:to-green-100:hover {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .sm\:hover\:to-green-200:hover {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .sm\:hover\:to-green-300:hover {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .sm\:hover\:to-green-400:hover {
+    --gradient-to-color: #68d391;
+  }
+
+  .sm\:hover\:to-green-500:hover {
+    --gradient-to-color: #48bb78;
+  }
+
+  .sm\:hover\:to-green-600:hover {
+    --gradient-to-color: #38a169;
+  }
+
+  .sm\:hover\:to-green-700:hover {
+    --gradient-to-color: #2f855a;
+  }
+
+  .sm\:hover\:to-green-800:hover {
+    --gradient-to-color: #276749;
+  }
+
+  .sm\:hover\:to-green-900:hover {
+    --gradient-to-color: #22543d;
+  }
+
+  .sm\:hover\:to-teal-100:hover {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .sm\:hover\:to-teal-200:hover {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .sm\:hover\:to-teal-300:hover {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .sm\:hover\:to-teal-400:hover {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .sm\:hover\:to-teal-500:hover {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .sm\:hover\:to-teal-600:hover {
+    --gradient-to-color: #319795;
+  }
+
+  .sm\:hover\:to-teal-700:hover {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .sm\:hover\:to-teal-800:hover {
+    --gradient-to-color: #285e61;
+  }
+
+  .sm\:hover\:to-teal-900:hover {
+    --gradient-to-color: #234e52;
+  }
+
+  .sm\:hover\:to-blue-100:hover {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .sm\:hover\:to-blue-200:hover {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .sm\:hover\:to-blue-300:hover {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .sm\:hover\:to-blue-400:hover {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .sm\:hover\:to-blue-500:hover {
+    --gradient-to-color: #4299e1;
+  }
+
+  .sm\:hover\:to-blue-600:hover {
+    --gradient-to-color: #3182ce;
+  }
+
+  .sm\:hover\:to-blue-700:hover {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .sm\:hover\:to-blue-800:hover {
+    --gradient-to-color: #2c5282;
+  }
+
+  .sm\:hover\:to-blue-900:hover {
+    --gradient-to-color: #2a4365;
+  }
+
+  .sm\:hover\:to-indigo-100:hover {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .sm\:hover\:to-indigo-200:hover {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .sm\:hover\:to-indigo-300:hover {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .sm\:hover\:to-indigo-400:hover {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .sm\:hover\:to-indigo-500:hover {
+    --gradient-to-color: #667eea;
+  }
+
+  .sm\:hover\:to-indigo-600:hover {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .sm\:hover\:to-indigo-700:hover {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .sm\:hover\:to-indigo-800:hover {
+    --gradient-to-color: #434190;
+  }
+
+  .sm\:hover\:to-indigo-900:hover {
+    --gradient-to-color: #3c366b;
+  }
+
+  .sm\:hover\:to-purple-100:hover {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .sm\:hover\:to-purple-200:hover {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .sm\:hover\:to-purple-300:hover {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .sm\:hover\:to-purple-400:hover {
+    --gradient-to-color: #b794f4;
+  }
+
+  .sm\:hover\:to-purple-500:hover {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .sm\:hover\:to-purple-600:hover {
+    --gradient-to-color: #805ad5;
+  }
+
+  .sm\:hover\:to-purple-700:hover {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .sm\:hover\:to-purple-800:hover {
+    --gradient-to-color: #553c9a;
+  }
+
+  .sm\:hover\:to-purple-900:hover {
+    --gradient-to-color: #44337a;
+  }
+
+  .sm\:hover\:to-pink-100:hover {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .sm\:hover\:to-pink-200:hover {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .sm\:hover\:to-pink-300:hover {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .sm\:hover\:to-pink-400:hover {
+    --gradient-to-color: #f687b3;
+  }
+
+  .sm\:hover\:to-pink-500:hover {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .sm\:hover\:to-pink-600:hover {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .sm\:hover\:to-pink-700:hover {
+    --gradient-to-color: #b83280;
+  }
+
+  .sm\:hover\:to-pink-800:hover {
+    --gradient-to-color: #97266d;
+  }
+
+  .sm\:hover\:to-pink-900:hover {
+    --gradient-to-color: #702459;
+  }
+
+  .sm\:focus\:from-transparent:focus {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:focus\:from-current:focus {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:focus\:from-black:focus {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:focus\:from-white:focus {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:focus\:from-gray-100:focus {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:focus\:from-gray-200:focus {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:focus\:from-gray-300:focus {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:focus\:from-gray-400:focus {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:focus\:from-gray-500:focus {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:focus\:from-gray-600:focus {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:focus\:from-gray-700:focus {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:focus\:from-gray-800:focus {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:focus\:from-gray-900:focus {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:focus\:from-red-100:focus {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:focus\:from-red-200:focus {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:focus\:from-red-300:focus {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:focus\:from-red-400:focus {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:focus\:from-red-500:focus {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:focus\:from-red-600:focus {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:focus\:from-red-700:focus {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:focus\:from-red-800:focus {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:focus\:from-red-900:focus {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:focus\:from-orange-100:focus {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:focus\:from-orange-200:focus {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:focus\:from-orange-300:focus {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:focus\:from-orange-400:focus {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:focus\:from-orange-500:focus {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:focus\:from-orange-600:focus {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:focus\:from-orange-700:focus {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:focus\:from-orange-800:focus {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:focus\:from-orange-900:focus {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:focus\:from-yellow-100:focus {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:focus\:from-yellow-200:focus {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:focus\:from-yellow-300:focus {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:focus\:from-yellow-400:focus {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:focus\:from-yellow-500:focus {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:focus\:from-yellow-600:focus {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:focus\:from-yellow-700:focus {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:focus\:from-yellow-800:focus {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:focus\:from-yellow-900:focus {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:focus\:from-green-100:focus {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:focus\:from-green-200:focus {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:focus\:from-green-300:focus {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:focus\:from-green-400:focus {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:focus\:from-green-500:focus {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:focus\:from-green-600:focus {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:focus\:from-green-700:focus {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:focus\:from-green-800:focus {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:focus\:from-green-900:focus {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:focus\:from-teal-100:focus {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:focus\:from-teal-200:focus {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:focus\:from-teal-300:focus {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:focus\:from-teal-400:focus {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:focus\:from-teal-500:focus {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:focus\:from-teal-600:focus {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:focus\:from-teal-700:focus {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:focus\:from-teal-800:focus {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:focus\:from-teal-900:focus {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:focus\:from-blue-100:focus {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:focus\:from-blue-200:focus {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:focus\:from-blue-300:focus {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:focus\:from-blue-400:focus {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:focus\:from-blue-500:focus {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:focus\:from-blue-600:focus {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:focus\:from-blue-700:focus {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:focus\:from-blue-800:focus {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:focus\:from-blue-900:focus {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:focus\:from-indigo-100:focus {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:focus\:from-indigo-200:focus {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:focus\:from-indigo-300:focus {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:focus\:from-indigo-400:focus {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:focus\:from-indigo-500:focus {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:focus\:from-indigo-600:focus {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:focus\:from-indigo-700:focus {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:focus\:from-indigo-800:focus {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:focus\:from-indigo-900:focus {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:focus\:from-purple-100:focus {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:focus\:from-purple-200:focus {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:focus\:from-purple-300:focus {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:focus\:from-purple-400:focus {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:focus\:from-purple-500:focus {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:focus\:from-purple-600:focus {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:focus\:from-purple-700:focus {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:focus\:from-purple-800:focus {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:focus\:from-purple-900:focus {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:focus\:from-pink-100:focus {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:focus\:from-pink-200:focus {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:focus\:from-pink-300:focus {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:focus\:from-pink-400:focus {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:focus\:from-pink-500:focus {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:focus\:from-pink-600:focus {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:focus\:from-pink-700:focus {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:focus\:from-pink-800:focus {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:focus\:from-pink-900:focus {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:focus\:via-transparent:focus {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:focus\:via-current:focus {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:focus\:via-black:focus {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .sm\:focus\:via-white:focus {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .sm\:focus\:via-gray-100:focus {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .sm\:focus\:via-gray-200:focus {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .sm\:focus\:via-gray-300:focus {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .sm\:focus\:via-gray-400:focus {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .sm\:focus\:via-gray-500:focus {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .sm\:focus\:via-gray-600:focus {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .sm\:focus\:via-gray-700:focus {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .sm\:focus\:via-gray-800:focus {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .sm\:focus\:via-gray-900:focus {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .sm\:focus\:via-red-100:focus {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .sm\:focus\:via-red-200:focus {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .sm\:focus\:via-red-300:focus {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .sm\:focus\:via-red-400:focus {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .sm\:focus\:via-red-500:focus {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .sm\:focus\:via-red-600:focus {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .sm\:focus\:via-red-700:focus {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .sm\:focus\:via-red-800:focus {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .sm\:focus\:via-red-900:focus {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .sm\:focus\:via-orange-100:focus {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .sm\:focus\:via-orange-200:focus {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .sm\:focus\:via-orange-300:focus {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .sm\:focus\:via-orange-400:focus {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .sm\:focus\:via-orange-500:focus {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .sm\:focus\:via-orange-600:focus {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .sm\:focus\:via-orange-700:focus {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .sm\:focus\:via-orange-800:focus {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .sm\:focus\:via-orange-900:focus {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .sm\:focus\:via-yellow-100:focus {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .sm\:focus\:via-yellow-200:focus {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .sm\:focus\:via-yellow-300:focus {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .sm\:focus\:via-yellow-400:focus {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .sm\:focus\:via-yellow-500:focus {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .sm\:focus\:via-yellow-600:focus {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .sm\:focus\:via-yellow-700:focus {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .sm\:focus\:via-yellow-800:focus {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .sm\:focus\:via-yellow-900:focus {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .sm\:focus\:via-green-100:focus {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .sm\:focus\:via-green-200:focus {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .sm\:focus\:via-green-300:focus {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .sm\:focus\:via-green-400:focus {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .sm\:focus\:via-green-500:focus {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .sm\:focus\:via-green-600:focus {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .sm\:focus\:via-green-700:focus {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .sm\:focus\:via-green-800:focus {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .sm\:focus\:via-green-900:focus {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .sm\:focus\:via-teal-100:focus {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .sm\:focus\:via-teal-200:focus {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .sm\:focus\:via-teal-300:focus {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .sm\:focus\:via-teal-400:focus {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .sm\:focus\:via-teal-500:focus {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .sm\:focus\:via-teal-600:focus {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .sm\:focus\:via-teal-700:focus {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .sm\:focus\:via-teal-800:focus {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .sm\:focus\:via-teal-900:focus {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .sm\:focus\:via-blue-100:focus {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .sm\:focus\:via-blue-200:focus {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .sm\:focus\:via-blue-300:focus {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .sm\:focus\:via-blue-400:focus {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .sm\:focus\:via-blue-500:focus {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .sm\:focus\:via-blue-600:focus {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .sm\:focus\:via-blue-700:focus {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .sm\:focus\:via-blue-800:focus {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .sm\:focus\:via-blue-900:focus {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .sm\:focus\:via-indigo-100:focus {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .sm\:focus\:via-indigo-200:focus {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .sm\:focus\:via-indigo-300:focus {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .sm\:focus\:via-indigo-400:focus {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .sm\:focus\:via-indigo-500:focus {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .sm\:focus\:via-indigo-600:focus {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .sm\:focus\:via-indigo-700:focus {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .sm\:focus\:via-indigo-800:focus {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .sm\:focus\:via-indigo-900:focus {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .sm\:focus\:via-purple-100:focus {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .sm\:focus\:via-purple-200:focus {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .sm\:focus\:via-purple-300:focus {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .sm\:focus\:via-purple-400:focus {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .sm\:focus\:via-purple-500:focus {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .sm\:focus\:via-purple-600:focus {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .sm\:focus\:via-purple-700:focus {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .sm\:focus\:via-purple-800:focus {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .sm\:focus\:via-purple-900:focus {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .sm\:focus\:via-pink-100:focus {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .sm\:focus\:via-pink-200:focus {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .sm\:focus\:via-pink-300:focus {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .sm\:focus\:via-pink-400:focus {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .sm\:focus\:via-pink-500:focus {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .sm\:focus\:via-pink-600:focus {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .sm\:focus\:via-pink-700:focus {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .sm\:focus\:via-pink-800:focus {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .sm\:focus\:via-pink-900:focus {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .sm\:focus\:to-transparent:focus {
+    --gradient-to-color: transparent;
+  }
+
+  .sm\:focus\:to-current:focus {
+    --gradient-to-color: currentColor;
+  }
+
+  .sm\:focus\:to-black:focus {
+    --gradient-to-color: #000;
+  }
+
+  .sm\:focus\:to-white:focus {
+    --gradient-to-color: #fff;
+  }
+
+  .sm\:focus\:to-gray-100:focus {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .sm\:focus\:to-gray-200:focus {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .sm\:focus\:to-gray-300:focus {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .sm\:focus\:to-gray-400:focus {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .sm\:focus\:to-gray-500:focus {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .sm\:focus\:to-gray-600:focus {
+    --gradient-to-color: #718096;
+  }
+
+  .sm\:focus\:to-gray-700:focus {
+    --gradient-to-color: #4a5568;
+  }
+
+  .sm\:focus\:to-gray-800:focus {
+    --gradient-to-color: #2d3748;
+  }
+
+  .sm\:focus\:to-gray-900:focus {
+    --gradient-to-color: #1a202c;
+  }
+
+  .sm\:focus\:to-red-100:focus {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .sm\:focus\:to-red-200:focus {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .sm\:focus\:to-red-300:focus {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .sm\:focus\:to-red-400:focus {
+    --gradient-to-color: #fc8181;
+  }
+
+  .sm\:focus\:to-red-500:focus {
+    --gradient-to-color: #f56565;
+  }
+
+  .sm\:focus\:to-red-600:focus {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .sm\:focus\:to-red-700:focus {
+    --gradient-to-color: #c53030;
+  }
+
+  .sm\:focus\:to-red-800:focus {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .sm\:focus\:to-red-900:focus {
+    --gradient-to-color: #742a2a;
+  }
+
+  .sm\:focus\:to-orange-100:focus {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .sm\:focus\:to-orange-200:focus {
+    --gradient-to-color: #feebc8;
+  }
+
+  .sm\:focus\:to-orange-300:focus {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .sm\:focus\:to-orange-400:focus {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .sm\:focus\:to-orange-500:focus {
+    --gradient-to-color: #ed8936;
+  }
+
+  .sm\:focus\:to-orange-600:focus {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .sm\:focus\:to-orange-700:focus {
+    --gradient-to-color: #c05621;
+  }
+
+  .sm\:focus\:to-orange-800:focus {
+    --gradient-to-color: #9c4221;
+  }
+
+  .sm\:focus\:to-orange-900:focus {
+    --gradient-to-color: #7b341e;
+  }
+
+  .sm\:focus\:to-yellow-100:focus {
+    --gradient-to-color: #fffff0;
+  }
+
+  .sm\:focus\:to-yellow-200:focus {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .sm\:focus\:to-yellow-300:focus {
+    --gradient-to-color: #faf089;
+  }
+
+  .sm\:focus\:to-yellow-400:focus {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .sm\:focus\:to-yellow-500:focus {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .sm\:focus\:to-yellow-600:focus {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .sm\:focus\:to-yellow-700:focus {
+    --gradient-to-color: #b7791f;
+  }
+
+  .sm\:focus\:to-yellow-800:focus {
+    --gradient-to-color: #975a16;
+  }
+
+  .sm\:focus\:to-yellow-900:focus {
+    --gradient-to-color: #744210;
+  }
+
+  .sm\:focus\:to-green-100:focus {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .sm\:focus\:to-green-200:focus {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .sm\:focus\:to-green-300:focus {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .sm\:focus\:to-green-400:focus {
+    --gradient-to-color: #68d391;
+  }
+
+  .sm\:focus\:to-green-500:focus {
+    --gradient-to-color: #48bb78;
+  }
+
+  .sm\:focus\:to-green-600:focus {
+    --gradient-to-color: #38a169;
+  }
+
+  .sm\:focus\:to-green-700:focus {
+    --gradient-to-color: #2f855a;
+  }
+
+  .sm\:focus\:to-green-800:focus {
+    --gradient-to-color: #276749;
+  }
+
+  .sm\:focus\:to-green-900:focus {
+    --gradient-to-color: #22543d;
+  }
+
+  .sm\:focus\:to-teal-100:focus {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .sm\:focus\:to-teal-200:focus {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .sm\:focus\:to-teal-300:focus {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .sm\:focus\:to-teal-400:focus {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .sm\:focus\:to-teal-500:focus {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .sm\:focus\:to-teal-600:focus {
+    --gradient-to-color: #319795;
+  }
+
+  .sm\:focus\:to-teal-700:focus {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .sm\:focus\:to-teal-800:focus {
+    --gradient-to-color: #285e61;
+  }
+
+  .sm\:focus\:to-teal-900:focus {
+    --gradient-to-color: #234e52;
+  }
+
+  .sm\:focus\:to-blue-100:focus {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .sm\:focus\:to-blue-200:focus {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .sm\:focus\:to-blue-300:focus {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .sm\:focus\:to-blue-400:focus {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .sm\:focus\:to-blue-500:focus {
+    --gradient-to-color: #4299e1;
+  }
+
+  .sm\:focus\:to-blue-600:focus {
+    --gradient-to-color: #3182ce;
+  }
+
+  .sm\:focus\:to-blue-700:focus {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .sm\:focus\:to-blue-800:focus {
+    --gradient-to-color: #2c5282;
+  }
+
+  .sm\:focus\:to-blue-900:focus {
+    --gradient-to-color: #2a4365;
+  }
+
+  .sm\:focus\:to-indigo-100:focus {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .sm\:focus\:to-indigo-200:focus {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .sm\:focus\:to-indigo-300:focus {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .sm\:focus\:to-indigo-400:focus {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .sm\:focus\:to-indigo-500:focus {
+    --gradient-to-color: #667eea;
+  }
+
+  .sm\:focus\:to-indigo-600:focus {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .sm\:focus\:to-indigo-700:focus {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .sm\:focus\:to-indigo-800:focus {
+    --gradient-to-color: #434190;
+  }
+
+  .sm\:focus\:to-indigo-900:focus {
+    --gradient-to-color: #3c366b;
+  }
+
+  .sm\:focus\:to-purple-100:focus {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .sm\:focus\:to-purple-200:focus {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .sm\:focus\:to-purple-300:focus {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .sm\:focus\:to-purple-400:focus {
+    --gradient-to-color: #b794f4;
+  }
+
+  .sm\:focus\:to-purple-500:focus {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .sm\:focus\:to-purple-600:focus {
+    --gradient-to-color: #805ad5;
+  }
+
+  .sm\:focus\:to-purple-700:focus {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .sm\:focus\:to-purple-800:focus {
+    --gradient-to-color: #553c9a;
+  }
+
+  .sm\:focus\:to-purple-900:focus {
+    --gradient-to-color: #44337a;
+  }
+
+  .sm\:focus\:to-pink-100:focus {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .sm\:focus\:to-pink-200:focus {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .sm\:focus\:to-pink-300:focus {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .sm\:focus\:to-pink-400:focus {
+    --gradient-to-color: #f687b3;
+  }
+
+  .sm\:focus\:to-pink-500:focus {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .sm\:focus\:to-pink-600:focus {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .sm\:focus\:to-pink-700:focus {
+    --gradient-to-color: #b83280;
+  }
+
+  .sm\:focus\:to-pink-800:focus {
+    --gradient-to-color: #97266d;
+  }
+
+  .sm\:focus\:to-pink-900:focus {
+    --gradient-to-color: #702459;
+  }
+
+  .sm\:bg-opacity-0 {
+    --bg-opacity: 0;
+  }
+
+  .sm\:bg-opacity-25 {
+    --bg-opacity: 0.25;
+  }
+
+  .sm\:bg-opacity-50 {
+    --bg-opacity: 0.5;
+  }
+
+  .sm\:bg-opacity-75 {
+    --bg-opacity: 0.75;
+  }
+
+  .sm\:bg-opacity-100 {
+    --bg-opacity: 1;
+  }
+
+  .sm\:hover\:bg-opacity-0:hover {
+    --bg-opacity: 0;
+  }
+
+  .sm\:hover\:bg-opacity-25:hover {
+    --bg-opacity: 0.25;
+  }
+
+  .sm\:hover\:bg-opacity-50:hover {
+    --bg-opacity: 0.5;
+  }
+
+  .sm\:hover\:bg-opacity-75:hover {
+    --bg-opacity: 0.75;
+  }
+
+  .sm\:hover\:bg-opacity-100:hover {
+    --bg-opacity: 1;
+  }
+
+  .sm\:focus\:bg-opacity-0:focus {
+    --bg-opacity: 0;
+  }
+
+  .sm\:focus\:bg-opacity-25:focus {
+    --bg-opacity: 0.25;
+  }
+
+  .sm\:focus\:bg-opacity-50:focus {
+    --bg-opacity: 0.5;
+  }
+
+  .sm\:focus\:bg-opacity-75:focus {
+    --bg-opacity: 0.75;
+  }
+
+  .sm\:focus\:bg-opacity-100:focus {
+    --bg-opacity: 1;
+  }
+
+  .sm\:bg-bottom {
+    background-position: bottom;
+  }
+
+  .sm\:bg-center {
+    background-position: center;
+  }
+
+  .sm\:bg-left {
+    background-position: left;
+  }
+
+  .sm\:bg-left-bottom {
+    background-position: left bottom;
+  }
+
+  .sm\:bg-left-top {
+    background-position: left top;
+  }
+
+  .sm\:bg-right {
+    background-position: right;
+  }
+
+  .sm\:bg-right-bottom {
+    background-position: right bottom;
+  }
+
+  .sm\:bg-right-top {
+    background-position: right top;
+  }
+
+  .sm\:bg-top {
+    background-position: top;
+  }
+
+  .sm\:bg-repeat {
+    background-repeat: repeat;
+  }
+
+  .sm\:bg-no-repeat {
+    background-repeat: no-repeat;
+  }
+
+  .sm\:bg-repeat-x {
+    background-repeat: repeat-x;
+  }
+
+  .sm\:bg-repeat-y {
+    background-repeat: repeat-y;
+  }
+
+  .sm\:bg-repeat-round {
+    background-repeat: round;
+  }
+
+  .sm\:bg-repeat-space {
+    background-repeat: space;
+  }
+
+  .sm\:bg-auto {
+    background-size: auto;
+  }
+
+  .sm\:bg-cover {
+    background-size: cover;
+  }
+
+  .sm\:bg-contain {
+    background-size: contain;
+  }
+
+  .sm\:border-collapse {
+    border-collapse: collapse;
+  }
+
+  .sm\:border-separate {
+    border-collapse: separate;
+  }
+
+  .sm\:border-transparent {
+    border-color: transparent;
+  }
+
+  .sm\:border-current {
+    border-color: currentColor;
+  }
+
+  .sm\:border-black {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .sm\:border-white {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .sm\:border-gray-100 {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .sm\:border-gray-200 {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .sm\:border-gray-300 {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .sm\:border-gray-400 {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .sm\:border-gray-500 {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .sm\:border-gray-600 {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .sm\:border-gray-700 {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .sm\:border-gray-800 {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .sm\:border-gray-900 {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .sm\:border-red-100 {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .sm\:border-red-200 {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .sm\:border-red-300 {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .sm\:border-red-400 {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .sm\:border-red-500 {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .sm\:border-red-600 {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .sm\:border-red-700 {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .sm\:border-red-800 {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .sm\:border-red-900 {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .sm\:border-orange-100 {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .sm\:border-orange-200 {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .sm\:border-orange-300 {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .sm\:border-orange-400 {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .sm\:border-orange-500 {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .sm\:border-orange-600 {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .sm\:border-orange-700 {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .sm\:border-orange-800 {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .sm\:border-orange-900 {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-100 {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-200 {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-300 {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-400 {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-500 {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-600 {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-700 {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-800 {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .sm\:border-yellow-900 {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .sm\:border-green-100 {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .sm\:border-green-200 {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .sm\:border-green-300 {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .sm\:border-green-400 {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .sm\:border-green-500 {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .sm\:border-green-600 {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .sm\:border-green-700 {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .sm\:border-green-800 {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .sm\:border-green-900 {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .sm\:border-teal-100 {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .sm\:border-teal-200 {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .sm\:border-teal-300 {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .sm\:border-teal-400 {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .sm\:border-teal-500 {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .sm\:border-teal-600 {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .sm\:border-teal-700 {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .sm\:border-teal-800 {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .sm\:border-teal-900 {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .sm\:border-blue-100 {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .sm\:border-blue-200 {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .sm\:border-blue-300 {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .sm\:border-blue-400 {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .sm\:border-blue-500 {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .sm\:border-blue-600 {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .sm\:border-blue-700 {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .sm\:border-blue-800 {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .sm\:border-blue-900 {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-100 {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-200 {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-300 {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-400 {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-500 {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-600 {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-700 {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-800 {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .sm\:border-indigo-900 {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .sm\:border-purple-100 {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .sm\:border-purple-200 {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .sm\:border-purple-300 {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .sm\:border-purple-400 {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .sm\:border-purple-500 {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .sm\:border-purple-600 {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .sm\:border-purple-700 {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .sm\:border-purple-800 {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .sm\:border-purple-900 {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .sm\:border-pink-100 {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .sm\:border-pink-200 {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .sm\:border-pink-300 {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .sm\:border-pink-400 {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .sm\:border-pink-500 {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .sm\:border-pink-600 {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .sm\:border-pink-700 {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .sm\:border-pink-800 {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .sm\:border-pink-900 {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-transparent:hover {
+    border-color: transparent;
+  }
+
+  .sm\:hover\:border-current:hover {
+    border-color: currentColor;
+  }
+
+  .sm\:hover\:border-black:hover {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-white:hover {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-100:hover {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-200:hover {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-300:hover {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-400:hover {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-500:hover {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-600:hover {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-700:hover {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-800:hover {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-gray-900:hover {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-300:hover {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-400:hover {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-500:hover {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-600:hover {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-700:hover {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-800:hover {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-red-900:hover {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-100:hover {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-200:hover {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-300:hover {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-400:hover {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-500:hover {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-600:hover {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-700:hover {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-800:hover {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-orange-900:hover {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-100:hover {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-200:hover {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-300:hover {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-400:hover {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-500:hover {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-600:hover {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-700:hover {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-800:hover {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-yellow-900:hover {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-100:hover {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-200:hover {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-300:hover {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-400:hover {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-500:hover {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-600:hover {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-700:hover {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-800:hover {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-green-900:hover {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-100:hover {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-200:hover {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-300:hover {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-400:hover {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-500:hover {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-600:hover {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-700:hover {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-800:hover {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-teal-900:hover {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-200:hover {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-300:hover {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-400:hover {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-500:hover {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-600:hover {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-700:hover {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-800:hover {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-blue-900:hover {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-200:hover {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-300:hover {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-400:hover {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-500:hover {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-600:hover {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-700:hover {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-800:hover {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-indigo-900:hover {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-100:hover {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-200:hover {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-300:hover {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-400:hover {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-500:hover {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-600:hover {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-700:hover {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-800:hover {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-purple-900:hover {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-300:hover {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-400:hover {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-500:hover {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-600:hover {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-700:hover {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-800:hover {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .sm\:hover\:border-pink-900:hover {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-transparent:focus {
+    border-color: transparent;
+  }
+
+  .sm\:focus\:border-current:focus {
+    border-color: currentColor;
+  }
+
+  .sm\:focus\:border-black:focus {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-white:focus {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-100:focus {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-200:focus {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-300:focus {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-400:focus {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-500:focus {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-600:focus {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-700:focus {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-800:focus {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-gray-900:focus {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-300:focus {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-400:focus {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-500:focus {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-600:focus {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-700:focus {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-800:focus {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-red-900:focus {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-100:focus {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-200:focus {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-300:focus {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-400:focus {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-500:focus {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-600:focus {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-700:focus {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-800:focus {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-orange-900:focus {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-100:focus {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-200:focus {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-300:focus {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-400:focus {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-500:focus {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-600:focus {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-700:focus {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-800:focus {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-yellow-900:focus {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-100:focus {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-200:focus {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-300:focus {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-400:focus {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-500:focus {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-600:focus {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-700:focus {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-800:focus {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-green-900:focus {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-100:focus {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-200:focus {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-300:focus {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-400:focus {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-500:focus {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-600:focus {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-700:focus {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-800:focus {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-teal-900:focus {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-200:focus {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-300:focus {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-400:focus {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-500:focus {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-600:focus {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-700:focus {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-800:focus {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-blue-900:focus {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-200:focus {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-300:focus {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-400:focus {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-500:focus {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-600:focus {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-700:focus {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-800:focus {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-indigo-900:focus {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-100:focus {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-200:focus {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-300:focus {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-400:focus {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-500:focus {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-600:focus {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-700:focus {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-800:focus {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-purple-900:focus {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-300:focus {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-400:focus {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-500:focus {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-600:focus {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-700:focus {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-800:focus {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .sm\:focus\:border-pink-900:focus {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .sm\:border-opacity-0 {
+    --border-opacity: 0;
+  }
+
+  .sm\:border-opacity-25 {
+    --border-opacity: 0.25;
+  }
+
+  .sm\:border-opacity-50 {
+    --border-opacity: 0.5;
+  }
+
+  .sm\:border-opacity-75 {
+    --border-opacity: 0.75;
+  }
+
+  .sm\:border-opacity-100 {
+    --border-opacity: 1;
+  }
+
+  .sm\:hover\:border-opacity-0:hover {
+    --border-opacity: 0;
+  }
+
+  .sm\:hover\:border-opacity-25:hover {
+    --border-opacity: 0.25;
+  }
+
+  .sm\:hover\:border-opacity-50:hover {
+    --border-opacity: 0.5;
+  }
+
+  .sm\:hover\:border-opacity-75:hover {
+    --border-opacity: 0.75;
+  }
+
+  .sm\:hover\:border-opacity-100:hover {
+    --border-opacity: 1;
+  }
+
+  .sm\:focus\:border-opacity-0:focus {
+    --border-opacity: 0;
+  }
+
+  .sm\:focus\:border-opacity-25:focus {
+    --border-opacity: 0.25;
+  }
+
+  .sm\:focus\:border-opacity-50:focus {
+    --border-opacity: 0.5;
+  }
+
+  .sm\:focus\:border-opacity-75:focus {
+    --border-opacity: 0.75;
+  }
+
+  .sm\:focus\:border-opacity-100:focus {
+    --border-opacity: 1;
+  }
+
+  .sm\:rounded-none {
+    border-radius: 0;
+  }
+
+  .sm\:rounded-sm {
+    border-radius: 0.125rem;
+  }
+
+  .sm\:rounded {
+    border-radius: 0.25rem;
+  }
+
+  .sm\:rounded-md {
+    border-radius: 0.375rem;
+  }
+
+  .sm\:rounded-lg {
+    border-radius: 0.5rem;
+  }
+
+  .sm\:rounded-full {
+    border-radius: 9999px;
+  }
+
+  .sm\:rounded-t-none {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+  }
+
+  .sm\:rounded-r-none {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+  }
+
+  .sm\:rounded-b-none {
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .sm\:rounded-l-none {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .sm\:rounded-t-sm {
+    border-top-left-radius: 0.125rem;
+    border-top-right-radius: 0.125rem;
+  }
+
+  .sm\:rounded-r-sm {
+    border-top-right-radius: 0.125rem;
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .sm\:rounded-b-sm {
+    border-bottom-right-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .sm\:rounded-l-sm {
+    border-top-left-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .sm\:rounded-t {
+    border-top-left-radius: 0.25rem;
+    border-top-right-radius: 0.25rem;
+  }
+
+  .sm\:rounded-r {
+    border-top-right-radius: 0.25rem;
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .sm\:rounded-b {
+    border-bottom-right-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .sm\:rounded-l {
+    border-top-left-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .sm\:rounded-t-md {
+    border-top-left-radius: 0.375rem;
+    border-top-right-radius: 0.375rem;
+  }
+
+  .sm\:rounded-r-md {
+    border-top-right-radius: 0.375rem;
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .sm\:rounded-b-md {
+    border-bottom-right-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .sm\:rounded-l-md {
+    border-top-left-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .sm\:rounded-t-lg {
+    border-top-left-radius: 0.5rem;
+    border-top-right-radius: 0.5rem;
+  }
+
+  .sm\:rounded-r-lg {
+    border-top-right-radius: 0.5rem;
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .sm\:rounded-b-lg {
+    border-bottom-right-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .sm\:rounded-l-lg {
+    border-top-left-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .sm\:rounded-t-full {
+    border-top-left-radius: 9999px;
+    border-top-right-radius: 9999px;
+  }
+
+  .sm\:rounded-r-full {
+    border-top-right-radius: 9999px;
+    border-bottom-right-radius: 9999px;
+  }
+
+  .sm\:rounded-b-full {
+    border-bottom-right-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .sm\:rounded-l-full {
+    border-top-left-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .sm\:rounded-tl-none {
+    border-top-left-radius: 0;
+  }
+
+  .sm\:rounded-tr-none {
+    border-top-right-radius: 0;
+  }
+
+  .sm\:rounded-br-none {
+    border-bottom-right-radius: 0;
+  }
+
+  .sm\:rounded-bl-none {
+    border-bottom-left-radius: 0;
+  }
+
+  .sm\:rounded-tl-sm {
+    border-top-left-radius: 0.125rem;
+  }
+
+  .sm\:rounded-tr-sm {
+    border-top-right-radius: 0.125rem;
+  }
+
+  .sm\:rounded-br-sm {
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .sm\:rounded-bl-sm {
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .sm\:rounded-tl {
+    border-top-left-radius: 0.25rem;
+  }
+
+  .sm\:rounded-tr {
+    border-top-right-radius: 0.25rem;
+  }
+
+  .sm\:rounded-br {
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .sm\:rounded-bl {
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .sm\:rounded-tl-md {
+    border-top-left-radius: 0.375rem;
+  }
+
+  .sm\:rounded-tr-md {
+    border-top-right-radius: 0.375rem;
+  }
+
+  .sm\:rounded-br-md {
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .sm\:rounded-bl-md {
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .sm\:rounded-tl-lg {
+    border-top-left-radius: 0.5rem;
+  }
+
+  .sm\:rounded-tr-lg {
+    border-top-right-radius: 0.5rem;
+  }
+
+  .sm\:rounded-br-lg {
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .sm\:rounded-bl-lg {
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .sm\:rounded-tl-full {
+    border-top-left-radius: 9999px;
+  }
+
+  .sm\:rounded-tr-full {
+    border-top-right-radius: 9999px;
+  }
+
+  .sm\:rounded-br-full {
+    border-bottom-right-radius: 9999px;
+  }
+
+  .sm\:rounded-bl-full {
+    border-bottom-left-radius: 9999px;
+  }
+
+  .sm\:border-solid {
+    border-style: solid;
+  }
+
+  .sm\:border-dashed {
+    border-style: dashed;
+  }
+
+  .sm\:border-dotted {
+    border-style: dotted;
+  }
+
+  .sm\:border-double {
+    border-style: double;
+  }
+
+  .sm\:border-none {
+    border-style: none;
+  }
+
+  .sm\:border-0 {
+    border-width: 0;
+  }
+
+  .sm\:border-2 {
+    border-width: 2px;
+  }
+
+  .sm\:border-4 {
+    border-width: 4px;
+  }
+
+  .sm\:border-8 {
+    border-width: 8px;
+  }
+
+  .sm\:border {
+    border-width: 1px;
+  }
+
+  .sm\:border-t-0 {
+    border-top-width: 0;
+  }
+
+  .sm\:border-r-0 {
+    border-right-width: 0;
+  }
+
+  .sm\:border-b-0 {
+    border-bottom-width: 0;
+  }
+
+  .sm\:border-l-0 {
+    border-left-width: 0;
+  }
+
+  .sm\:border-t-2 {
+    border-top-width: 2px;
+  }
+
+  .sm\:border-r-2 {
+    border-right-width: 2px;
+  }
+
+  .sm\:border-b-2 {
+    border-bottom-width: 2px;
+  }
+
+  .sm\:border-l-2 {
+    border-left-width: 2px;
+  }
+
+  .sm\:border-t-4 {
+    border-top-width: 4px;
+  }
+
+  .sm\:border-r-4 {
+    border-right-width: 4px;
+  }
+
+  .sm\:border-b-4 {
+    border-bottom-width: 4px;
+  }
+
+  .sm\:border-l-4 {
+    border-left-width: 4px;
+  }
+
+  .sm\:border-t-8 {
+    border-top-width: 8px;
+  }
+
+  .sm\:border-r-8 {
+    border-right-width: 8px;
+  }
+
+  .sm\:border-b-8 {
+    border-bottom-width: 8px;
+  }
+
+  .sm\:border-l-8 {
+    border-left-width: 8px;
+  }
+
+  .sm\:border-t {
+    border-top-width: 1px;
+  }
+
+  .sm\:border-r {
+    border-right-width: 1px;
+  }
+
+  .sm\:border-b {
+    border-bottom-width: 1px;
+  }
+
+  .sm\:border-l {
+    border-left-width: 1px;
+  }
+
+  .sm\:box-border {
+    box-sizing: border-box;
+  }
+
+  .sm\:box-content {
+    box-sizing: content-box;
+  }
+
+  .sm\:cursor-auto {
+    cursor: auto;
+  }
+
+  .sm\:cursor-default {
+    cursor: default;
+  }
+
+  .sm\:cursor-pointer {
+    cursor: pointer;
+  }
+
+  .sm\:cursor-wait {
+    cursor: wait;
+  }
+
+  .sm\:cursor-text {
+    cursor: text;
+  }
+
+  .sm\:cursor-move {
+    cursor: move;
+  }
+
+  .sm\:cursor-not-allowed {
+    cursor: not-allowed;
+  }
+
+  .sm\:block {
+    display: block;
+  }
+
+  .sm\:inline-block {
+    display: inline-block;
+  }
+
+  .sm\:inline {
+    display: inline;
+  }
+
+  .sm\:flex {
+    display: flex;
+  }
+
+  .sm\:inline-flex {
+    display: inline-flex;
+  }
+
+  .sm\:table {
+    display: table;
+  }
+
+  .sm\:table-caption {
+    display: table-caption;
+  }
+
+  .sm\:table-cell {
+    display: table-cell;
+  }
+
+  .sm\:table-column {
+    display: table-column;
+  }
+
+  .sm\:table-column-group {
+    display: table-column-group;
+  }
+
+  .sm\:table-footer-group {
+    display: table-footer-group;
+  }
+
+  .sm\:table-header-group {
+    display: table-header-group;
+  }
+
+  .sm\:table-row-group {
+    display: table-row-group;
+  }
+
+  .sm\:table-row {
+    display: table-row;
+  }
+
+  .sm\:flow-root {
+    display: flow-root;
+  }
+
+  .sm\:grid {
+    display: grid;
+  }
+
+  .sm\:inline-grid {
+    display: inline-grid;
+  }
+
+  .sm\:contents {
+    display: contents;
+  }
+
+  .sm\:hidden {
+    display: none;
+  }
+
+  .sm\:flex-row {
+    flex-direction: row;
+  }
+
+  .sm\:flex-row-reverse {
+    flex-direction: row-reverse;
+  }
+
+  .sm\:flex-col {
+    flex-direction: column;
+  }
+
+  .sm\:flex-col-reverse {
+    flex-direction: column-reverse;
+  }
+
+  .sm\:flex-wrap {
+    flex-wrap: wrap;
+  }
+
+  .sm\:flex-wrap-reverse {
+    flex-wrap: wrap-reverse;
+  }
+
+  .sm\:flex-no-wrap {
+    flex-wrap: nowrap;
+  }
+
+  .sm\:place-items-auto {
+    place-items: auto;
+  }
+
+  .sm\:place-items-start {
+    place-items: start;
+  }
+
+  .sm\:place-items-end {
+    place-items: end;
+  }
+
+  .sm\:place-items-center {
+    place-items: center;
+  }
+
+  .sm\:place-items-stretch {
+    place-items: stretch;
+  }
+
+  .sm\:place-content-center {
+    place-content: center;
+  }
+
+  .sm\:place-content-start {
+    place-content: start;
+  }
+
+  .sm\:place-content-end {
+    place-content: end;
+  }
+
+  .sm\:place-content-between {
+    place-content: space-between;
+  }
+
+  .sm\:place-content-around {
+    place-content: space-around;
+  }
+
+  .sm\:place-content-evenly {
+    place-content: space-evenly;
+  }
+
+  .sm\:place-content-stretch {
+    place-content: stretch;
+  }
+
+  .sm\:place-self-auto {
+    place-self: auto;
+  }
+
+  .sm\:place-self-start {
+    place-self: start;
+  }
+
+  .sm\:place-self-end {
+    place-self: end;
+  }
+
+  .sm\:place-self-center {
+    place-self: center;
+  }
+
+  .sm\:place-self-stretch {
+    place-self: stretch;
+  }
+
+  .sm\:items-start {
+    align-items: flex-start;
+  }
+
+  .sm\:items-end {
+    align-items: flex-end;
+  }
+
+  .sm\:items-center {
+    align-items: center;
+  }
+
+  .sm\:items-baseline {
+    align-items: baseline;
+  }
+
+  .sm\:items-stretch {
+    align-items: stretch;
+  }
+
+  .sm\:content-center {
+    align-content: center;
+  }
+
+  .sm\:content-start {
+    align-content: flex-start;
+  }
+
+  .sm\:content-end {
+    align-content: flex-end;
+  }
+
+  .sm\:content-between {
+    align-content: space-between;
+  }
+
+  .sm\:content-around {
+    align-content: space-around;
+  }
+
+  .sm\:content-evenly {
+    align-content: space-evenly;
+  }
+
+  .sm\:self-auto {
+    align-self: auto;
+  }
+
+  .sm\:self-start {
+    align-self: flex-start;
+  }
+
+  .sm\:self-end {
+    align-self: flex-end;
+  }
+
+  .sm\:self-center {
+    align-self: center;
+  }
+
+  .sm\:self-stretch {
+    align-self: stretch;
+  }
+
+  .sm\:justify-items-auto {
+    justify-items: auto;
+  }
+
+  .sm\:justify-items-start {
+    justify-items: start;
+  }
+
+  .sm\:justify-items-end {
+    justify-items: end;
+  }
+
+  .sm\:justify-items-center {
+    justify-items: center;
+  }
+
+  .sm\:justify-items-stretch {
+    justify-items: stretch;
+  }
+
+  .sm\:justify-start {
+    justify-content: flex-start;
+  }
+
+  .sm\:justify-end {
+    justify-content: flex-end;
+  }
+
+  .sm\:justify-center {
+    justify-content: center;
+  }
+
+  .sm\:justify-between {
+    justify-content: space-between;
+  }
+
+  .sm\:justify-around {
+    justify-content: space-around;
+  }
+
+  .sm\:justify-evenly {
+    justify-content: space-evenly;
+  }
+
+  .sm\:justify-self-auto {
+    justify-self: auto;
+  }
+
+  .sm\:justify-self-start {
+    justify-self: start;
+  }
+
+  .sm\:justify-self-end {
+    justify-self: end;
+  }
+
+  .sm\:justify-self-center {
+    justify-self: center;
+  }
+
+  .sm\:justify-self-stretch {
+    justify-self: stretch;
+  }
+
+  .sm\:flex-1 {
+    flex: 1 1 0%;
+  }
+
+  .sm\:flex-auto {
+    flex: 1 1 auto;
+  }
+
+  .sm\:flex-initial {
+    flex: 0 1 auto;
+  }
+
+  .sm\:flex-none {
+    flex: none;
+  }
+
+  .sm\:flex-grow-0 {
+    flex-grow: 0;
+  }
+
+  .sm\:flex-grow {
+    flex-grow: 1;
+  }
+
+  .sm\:flex-shrink-0 {
+    flex-shrink: 0;
+  }
+
+  .sm\:flex-shrink {
+    flex-shrink: 1;
+  }
+
+  .sm\:order-1 {
+    order: 1;
+  }
+
+  .sm\:order-2 {
+    order: 2;
+  }
+
+  .sm\:order-3 {
+    order: 3;
+  }
+
+  .sm\:order-4 {
+    order: 4;
+  }
+
+  .sm\:order-5 {
+    order: 5;
+  }
+
+  .sm\:order-6 {
+    order: 6;
+  }
+
+  .sm\:order-7 {
+    order: 7;
+  }
+
+  .sm\:order-8 {
+    order: 8;
+  }
+
+  .sm\:order-9 {
+    order: 9;
+  }
+
+  .sm\:order-10 {
+    order: 10;
+  }
+
+  .sm\:order-11 {
+    order: 11;
+  }
+
+  .sm\:order-12 {
+    order: 12;
+  }
+
+  .sm\:order-first {
+    order: -9999;
+  }
+
+  .sm\:order-last {
+    order: 9999;
+  }
+
+  .sm\:order-none {
+    order: 0;
+  }
+
+  .sm\:float-right {
+    float: right;
+  }
+
+  .sm\:float-left {
+    float: left;
+  }
+
+  .sm\:float-none {
+    float: none;
+  }
+
+  .sm\:clearfix:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+
+  .sm\:clear-left {
+    clear: left;
+  }
+
+  .sm\:clear-right {
+    clear: right;
+  }
+
+  .sm\:clear-both {
+    clear: both;
+  }
+
+  .sm\:clear-none {
+    clear: none;
+  }
+
+  .sm\:font-sans {
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  }
+
+  .sm\:font-serif {
+    font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+  }
+
+  .sm\:font-mono {
+    font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+  }
+
+  .sm\:font-hairline {
+    font-weight: 100;
+  }
+
+  .sm\:font-thin {
+    font-weight: 200;
+  }
+
+  .sm\:font-light {
+    font-weight: 300;
+  }
+
+  .sm\:font-normal {
+    font-weight: 400;
+  }
+
+  .sm\:font-medium {
+    font-weight: 500;
+  }
+
+  .sm\:font-semibold {
+    font-weight: 600;
+  }
+
+  .sm\:font-bold {
+    font-weight: 700;
+  }
+
+  .sm\:font-extrabold {
+    font-weight: 800;
+  }
+
+  .sm\:font-black {
+    font-weight: 900;
+  }
+
+  .sm\:hover\:font-hairline:hover {
+    font-weight: 100;
+  }
+
+  .sm\:hover\:font-thin:hover {
+    font-weight: 200;
+  }
+
+  .sm\:hover\:font-light:hover {
+    font-weight: 300;
+  }
+
+  .sm\:hover\:font-normal:hover {
+    font-weight: 400;
+  }
+
+  .sm\:hover\:font-medium:hover {
+    font-weight: 500;
+  }
+
+  .sm\:hover\:font-semibold:hover {
+    font-weight: 600;
+  }
+
+  .sm\:hover\:font-bold:hover {
+    font-weight: 700;
+  }
+
+  .sm\:hover\:font-extrabold:hover {
+    font-weight: 800;
+  }
+
+  .sm\:hover\:font-black:hover {
+    font-weight: 900;
+  }
+
+  .sm\:focus\:font-hairline:focus {
+    font-weight: 100;
+  }
+
+  .sm\:focus\:font-thin:focus {
+    font-weight: 200;
+  }
+
+  .sm\:focus\:font-light:focus {
+    font-weight: 300;
+  }
+
+  .sm\:focus\:font-normal:focus {
+    font-weight: 400;
+  }
+
+  .sm\:focus\:font-medium:focus {
+    font-weight: 500;
+  }
+
+  .sm\:focus\:font-semibold:focus {
+    font-weight: 600;
+  }
+
+  .sm\:focus\:font-bold:focus {
+    font-weight: 700;
+  }
+
+  .sm\:focus\:font-extrabold:focus {
+    font-weight: 800;
+  }
+
+  .sm\:focus\:font-black:focus {
+    font-weight: 900;
+  }
+
+  .sm\:h-0 {
+    height: 0;
+  }
+
+  .sm\:h-1 {
+    height: 0.25rem;
+  }
+
+  .sm\:h-2 {
+    height: 0.5rem;
+  }
+
+  .sm\:h-3 {
+    height: 0.75rem;
+  }
+
+  .sm\:h-4 {
+    height: 1rem;
+  }
+
+  .sm\:h-5 {
+    height: 1.25rem;
+  }
+
+  .sm\:h-6 {
+    height: 1.5rem;
+  }
+
+  .sm\:h-8 {
+    height: 2rem;
+  }
+
+  .sm\:h-10 {
+    height: 2.5rem;
+  }
+
+  .sm\:h-12 {
+    height: 3rem;
+  }
+
+  .sm\:h-16 {
+    height: 4rem;
+  }
+
+  .sm\:h-20 {
+    height: 5rem;
+  }
+
+  .sm\:h-24 {
+    height: 6rem;
+  }
+
+  .sm\:h-32 {
+    height: 8rem;
+  }
+
+  .sm\:h-40 {
+    height: 10rem;
+  }
+
+  .sm\:h-48 {
+    height: 12rem;
+  }
+
+  .sm\:h-56 {
+    height: 14rem;
+  }
+
+  .sm\:h-64 {
+    height: 16rem;
+  }
+
+  .sm\:h-auto {
+    height: auto;
+  }
+
+  .sm\:h-px {
+    height: 1px;
+  }
+
+  .sm\:h-full {
+    height: 100%;
+  }
+
+  .sm\:h-screen {
+    height: 100vh;
+  }
+
+  .sm\:text-xs {
+    font-size: 0.75rem;
+  }
+
+  .sm\:text-sm {
+    font-size: 0.875rem;
+  }
+
+  .sm\:text-base {
+    font-size: 1rem;
+  }
+
+  .sm\:text-lg {
+    font-size: 1.125rem;
+  }
+
+  .sm\:text-xl {
+    font-size: 1.25rem;
+  }
+
+  .sm\:text-2xl {
+    font-size: 1.5rem;
+  }
+
+  .sm\:text-3xl {
+    font-size: 1.875rem;
+  }
+
+  .sm\:text-4xl {
+    font-size: 2.25rem;
+  }
+
+  .sm\:text-5xl {
+    font-size: 3rem;
+  }
+
+  .sm\:text-6xl {
+    font-size: 4rem;
+  }
+
+  .sm\:leading-3 {
+    line-height: .75rem;
+  }
+
+  .sm\:leading-4 {
+    line-height: 1rem;
+  }
+
+  .sm\:leading-5 {
+    line-height: 1.25rem;
+  }
+
+  .sm\:leading-6 {
+    line-height: 1.5rem;
+  }
+
+  .sm\:leading-7 {
+    line-height: 1.75rem;
+  }
+
+  .sm\:leading-8 {
+    line-height: 2rem;
+  }
+
+  .sm\:leading-9 {
+    line-height: 2.25rem;
+  }
+
+  .sm\:leading-10 {
+    line-height: 2.5rem;
+  }
+
+  .sm\:leading-none {
+    line-height: 1;
+  }
+
+  .sm\:leading-tight {
+    line-height: 1.25;
+  }
+
+  .sm\:leading-snug {
+    line-height: 1.375;
+  }
+
+  .sm\:leading-normal {
+    line-height: 1.5;
+  }
+
+  .sm\:leading-relaxed {
+    line-height: 1.625;
+  }
+
+  .sm\:leading-loose {
+    line-height: 2;
+  }
+
+  .sm\:list-inside {
+    list-style-position: inside;
+  }
+
+  .sm\:list-outside {
+    list-style-position: outside;
+  }
+
+  .sm\:list-none {
+    list-style-type: none;
+  }
+
+  .sm\:list-disc {
+    list-style-type: disc;
+  }
+
+  .sm\:list-decimal {
+    list-style-type: decimal;
+  }
+
+  .sm\:m-0 {
+    margin: 0;
+  }
+
+  .sm\:m-1 {
+    margin: 0.25rem;
+  }
+
+  .sm\:m-2 {
+    margin: 0.5rem;
+  }
+
+  .sm\:m-3 {
+    margin: 0.75rem;
+  }
+
+  .sm\:m-4 {
+    margin: 1rem;
+  }
+
+  .sm\:m-5 {
+    margin: 1.25rem;
+  }
+
+  .sm\:m-6 {
+    margin: 1.5rem;
+  }
+
+  .sm\:m-8 {
+    margin: 2rem;
+  }
+
+  .sm\:m-10 {
+    margin: 2.5rem;
+  }
+
+  .sm\:m-12 {
+    margin: 3rem;
+  }
+
+  .sm\:m-16 {
+    margin: 4rem;
+  }
+
+  .sm\:m-20 {
+    margin: 5rem;
+  }
+
+  .sm\:m-24 {
+    margin: 6rem;
+  }
+
+  .sm\:m-32 {
+    margin: 8rem;
+  }
+
+  .sm\:m-40 {
+    margin: 10rem;
+  }
+
+  .sm\:m-48 {
+    margin: 12rem;
+  }
+
+  .sm\:m-56 {
+    margin: 14rem;
+  }
+
+  .sm\:m-64 {
+    margin: 16rem;
+  }
+
+  .sm\:m-auto {
+    margin: auto;
+  }
+
+  .sm\:m-px {
+    margin: 1px;
+  }
+
+  .sm\:-m-1 {
+    margin: -0.25rem;
+  }
+
+  .sm\:-m-2 {
+    margin: -0.5rem;
+  }
+
+  .sm\:-m-3 {
+    margin: -0.75rem;
+  }
+
+  .sm\:-m-4 {
+    margin: -1rem;
+  }
+
+  .sm\:-m-5 {
+    margin: -1.25rem;
+  }
+
+  .sm\:-m-6 {
+    margin: -1.5rem;
+  }
+
+  .sm\:-m-8 {
+    margin: -2rem;
+  }
+
+  .sm\:-m-10 {
+    margin: -2.5rem;
+  }
+
+  .sm\:-m-12 {
+    margin: -3rem;
+  }
+
+  .sm\:-m-16 {
+    margin: -4rem;
+  }
+
+  .sm\:-m-20 {
+    margin: -5rem;
+  }
+
+  .sm\:-m-24 {
+    margin: -6rem;
+  }
+
+  .sm\:-m-32 {
+    margin: -8rem;
+  }
+
+  .sm\:-m-40 {
+    margin: -10rem;
+  }
+
+  .sm\:-m-48 {
+    margin: -12rem;
+  }
+
+  .sm\:-m-56 {
+    margin: -14rem;
+  }
+
+  .sm\:-m-64 {
+    margin: -16rem;
+  }
+
+  .sm\:-m-px {
+    margin: -1px;
+  }
+
+  .sm\:my-0 {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .sm\:mx-0 {
+    margin-left: 0;
+    margin-right: 0;
+  }
+
+  .sm\:my-1 {
+    margin-top: 0.25rem;
+    margin-bottom: 0.25rem;
+  }
+
+  .sm\:mx-1 {
+    margin-left: 0.25rem;
+    margin-right: 0.25rem;
+  }
+
+  .sm\:my-2 {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+  }
+
+  .sm\:mx-2 {
+    margin-left: 0.5rem;
+    margin-right: 0.5rem;
+  }
+
+  .sm\:my-3 {
+    margin-top: 0.75rem;
+    margin-bottom: 0.75rem;
+  }
+
+  .sm\:mx-3 {
+    margin-left: 0.75rem;
+    margin-right: 0.75rem;
+  }
+
+  .sm\:my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+  }
+
+  .sm\:mx-4 {
+    margin-left: 1rem;
+    margin-right: 1rem;
+  }
+
+  .sm\:my-5 {
+    margin-top: 1.25rem;
+    margin-bottom: 1.25rem;
+  }
+
+  .sm\:mx-5 {
+    margin-left: 1.25rem;
+    margin-right: 1.25rem;
+  }
+
+  .sm\:my-6 {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+
+  .sm\:mx-6 {
+    margin-left: 1.5rem;
+    margin-right: 1.5rem;
+  }
+
+  .sm\:my-8 {
+    margin-top: 2rem;
+    margin-bottom: 2rem;
+  }
+
+  .sm\:mx-8 {
+    margin-left: 2rem;
+    margin-right: 2rem;
+  }
+
+  .sm\:my-10 {
+    margin-top: 2.5rem;
+    margin-bottom: 2.5rem;
+  }
+
+  .sm\:mx-10 {
+    margin-left: 2.5rem;
+    margin-right: 2.5rem;
+  }
+
+  .sm\:my-12 {
+    margin-top: 3rem;
+    margin-bottom: 3rem;
+  }
+
+  .sm\:mx-12 {
+    margin-left: 3rem;
+    margin-right: 3rem;
+  }
+
+  .sm\:my-16 {
+    margin-top: 4rem;
+    margin-bottom: 4rem;
+  }
+
+  .sm\:mx-16 {
+    margin-left: 4rem;
+    margin-right: 4rem;
+  }
+
+  .sm\:my-20 {
+    margin-top: 5rem;
+    margin-bottom: 5rem;
+  }
+
+  .sm\:mx-20 {
+    margin-left: 5rem;
+    margin-right: 5rem;
+  }
+
+  .sm\:my-24 {
+    margin-top: 6rem;
+    margin-bottom: 6rem;
+  }
+
+  .sm\:mx-24 {
+    margin-left: 6rem;
+    margin-right: 6rem;
+  }
+
+  .sm\:my-32 {
+    margin-top: 8rem;
+    margin-bottom: 8rem;
+  }
+
+  .sm\:mx-32 {
+    margin-left: 8rem;
+    margin-right: 8rem;
+  }
+
+  .sm\:my-40 {
+    margin-top: 10rem;
+    margin-bottom: 10rem;
+  }
+
+  .sm\:mx-40 {
+    margin-left: 10rem;
+    margin-right: 10rem;
+  }
+
+  .sm\:my-48 {
+    margin-top: 12rem;
+    margin-bottom: 12rem;
+  }
+
+  .sm\:mx-48 {
+    margin-left: 12rem;
+    margin-right: 12rem;
+  }
+
+  .sm\:my-56 {
+    margin-top: 14rem;
+    margin-bottom: 14rem;
+  }
+
+  .sm\:mx-56 {
+    margin-left: 14rem;
+    margin-right: 14rem;
+  }
+
+  .sm\:my-64 {
+    margin-top: 16rem;
+    margin-bottom: 16rem;
+  }
+
+  .sm\:mx-64 {
+    margin-left: 16rem;
+    margin-right: 16rem;
+  }
+
+  .sm\:my-auto {
+    margin-top: auto;
+    margin-bottom: auto;
+  }
+
+  .sm\:mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .sm\:my-px {
+    margin-top: 1px;
+    margin-bottom: 1px;
+  }
+
+  .sm\:mx-px {
+    margin-left: 1px;
+    margin-right: 1px;
+  }
+
+  .sm\:-my-1 {
+    margin-top: -0.25rem;
+    margin-bottom: -0.25rem;
+  }
+
+  .sm\:-mx-1 {
+    margin-left: -0.25rem;
+    margin-right: -0.25rem;
+  }
+
+  .sm\:-my-2 {
+    margin-top: -0.5rem;
+    margin-bottom: -0.5rem;
+  }
+
+  .sm\:-mx-2 {
+    margin-left: -0.5rem;
+    margin-right: -0.5rem;
+  }
+
+  .sm\:-my-3 {
+    margin-top: -0.75rem;
+    margin-bottom: -0.75rem;
+  }
+
+  .sm\:-mx-3 {
+    margin-left: -0.75rem;
+    margin-right: -0.75rem;
+  }
+
+  .sm\:-my-4 {
+    margin-top: -1rem;
+    margin-bottom: -1rem;
+  }
+
+  .sm\:-mx-4 {
+    margin-left: -1rem;
+    margin-right: -1rem;
+  }
+
+  .sm\:-my-5 {
+    margin-top: -1.25rem;
+    margin-bottom: -1.25rem;
+  }
+
+  .sm\:-mx-5 {
+    margin-left: -1.25rem;
+    margin-right: -1.25rem;
+  }
+
+  .sm\:-my-6 {
+    margin-top: -1.5rem;
+    margin-bottom: -1.5rem;
+  }
+
+  .sm\:-mx-6 {
+    margin-left: -1.5rem;
+    margin-right: -1.5rem;
+  }
+
+  .sm\:-my-8 {
+    margin-top: -2rem;
+    margin-bottom: -2rem;
+  }
+
+  .sm\:-mx-8 {
+    margin-left: -2rem;
+    margin-right: -2rem;
+  }
+
+  .sm\:-my-10 {
+    margin-top: -2.5rem;
+    margin-bottom: -2.5rem;
+  }
+
+  .sm\:-mx-10 {
+    margin-left: -2.5rem;
+    margin-right: -2.5rem;
+  }
+
+  .sm\:-my-12 {
+    margin-top: -3rem;
+    margin-bottom: -3rem;
+  }
+
+  .sm\:-mx-12 {
+    margin-left: -3rem;
+    margin-right: -3rem;
+  }
+
+  .sm\:-my-16 {
+    margin-top: -4rem;
+    margin-bottom: -4rem;
+  }
+
+  .sm\:-mx-16 {
+    margin-left: -4rem;
+    margin-right: -4rem;
+  }
+
+  .sm\:-my-20 {
+    margin-top: -5rem;
+    margin-bottom: -5rem;
+  }
+
+  .sm\:-mx-20 {
+    margin-left: -5rem;
+    margin-right: -5rem;
+  }
+
+  .sm\:-my-24 {
+    margin-top: -6rem;
+    margin-bottom: -6rem;
+  }
+
+  .sm\:-mx-24 {
+    margin-left: -6rem;
+    margin-right: -6rem;
+  }
+
+  .sm\:-my-32 {
+    margin-top: -8rem;
+    margin-bottom: -8rem;
+  }
+
+  .sm\:-mx-32 {
+    margin-left: -8rem;
+    margin-right: -8rem;
+  }
+
+  .sm\:-my-40 {
+    margin-top: -10rem;
+    margin-bottom: -10rem;
+  }
+
+  .sm\:-mx-40 {
+    margin-left: -10rem;
+    margin-right: -10rem;
+  }
+
+  .sm\:-my-48 {
+    margin-top: -12rem;
+    margin-bottom: -12rem;
+  }
+
+  .sm\:-mx-48 {
+    margin-left: -12rem;
+    margin-right: -12rem;
+  }
+
+  .sm\:-my-56 {
+    margin-top: -14rem;
+    margin-bottom: -14rem;
+  }
+
+  .sm\:-mx-56 {
+    margin-left: -14rem;
+    margin-right: -14rem;
+  }
+
+  .sm\:-my-64 {
+    margin-top: -16rem;
+    margin-bottom: -16rem;
+  }
+
+  .sm\:-mx-64 {
+    margin-left: -16rem;
+    margin-right: -16rem;
+  }
+
+  .sm\:-my-px {
+    margin-top: -1px;
+    margin-bottom: -1px;
+  }
+
+  .sm\:-mx-px {
+    margin-left: -1px;
+    margin-right: -1px;
+  }
+
+  .sm\:mt-0 {
+    margin-top: 0;
+  }
+
+  .sm\:mr-0 {
+    margin-right: 0;
+  }
+
+  .sm\:mb-0 {
+    margin-bottom: 0;
+  }
+
+  .sm\:ml-0 {
+    margin-left: 0;
+  }
+
+  .sm\:mt-1 {
+    margin-top: 0.25rem;
+  }
+
+  .sm\:mr-1 {
+    margin-right: 0.25rem;
+  }
+
+  .sm\:mb-1 {
+    margin-bottom: 0.25rem;
+  }
+
+  .sm\:ml-1 {
+    margin-left: 0.25rem;
+  }
+
+  .sm\:mt-2 {
+    margin-top: 0.5rem;
+  }
+
+  .sm\:mr-2 {
+    margin-right: 0.5rem;
+  }
+
+  .sm\:mb-2 {
+    margin-bottom: 0.5rem;
+  }
+
+  .sm\:ml-2 {
+    margin-left: 0.5rem;
+  }
+
+  .sm\:mt-3 {
+    margin-top: 0.75rem;
+  }
+
+  .sm\:mr-3 {
+    margin-right: 0.75rem;
+  }
+
+  .sm\:mb-3 {
+    margin-bottom: 0.75rem;
+  }
+
+  .sm\:ml-3 {
+    margin-left: 0.75rem;
+  }
+
+  .sm\:mt-4 {
+    margin-top: 1rem;
+  }
+
+  .sm\:mr-4 {
+    margin-right: 1rem;
+  }
+
+  .sm\:mb-4 {
+    margin-bottom: 1rem;
+  }
+
+  .sm\:ml-4 {
+    margin-left: 1rem;
+  }
+
+  .sm\:mt-5 {
+    margin-top: 1.25rem;
+  }
+
+  .sm\:mr-5 {
+    margin-right: 1.25rem;
+  }
+
+  .sm\:mb-5 {
+    margin-bottom: 1.25rem;
+  }
+
+  .sm\:ml-5 {
+    margin-left: 1.25rem;
+  }
+
+  .sm\:mt-6 {
+    margin-top: 1.5rem;
+  }
+
+  .sm\:mr-6 {
+    margin-right: 1.5rem;
+  }
+
+  .sm\:mb-6 {
+    margin-bottom: 1.5rem;
+  }
+
+  .sm\:ml-6 {
+    margin-left: 1.5rem;
+  }
+
+  .sm\:mt-8 {
+    margin-top: 2rem;
+  }
+
+  .sm\:mr-8 {
+    margin-right: 2rem;
+  }
+
+  .sm\:mb-8 {
+    margin-bottom: 2rem;
+  }
+
+  .sm\:ml-8 {
+    margin-left: 2rem;
+  }
+
+  .sm\:mt-10 {
+    margin-top: 2.5rem;
+  }
+
+  .sm\:mr-10 {
+    margin-right: 2.5rem;
+  }
+
+  .sm\:mb-10 {
+    margin-bottom: 2.5rem;
+  }
+
+  .sm\:ml-10 {
+    margin-left: 2.5rem;
+  }
+
+  .sm\:mt-12 {
+    margin-top: 3rem;
+  }
+
+  .sm\:mr-12 {
+    margin-right: 3rem;
+  }
+
+  .sm\:mb-12 {
+    margin-bottom: 3rem;
+  }
+
+  .sm\:ml-12 {
+    margin-left: 3rem;
+  }
+
+  .sm\:mt-16 {
+    margin-top: 4rem;
+  }
+
+  .sm\:mr-16 {
+    margin-right: 4rem;
+  }
+
+  .sm\:mb-16 {
+    margin-bottom: 4rem;
+  }
+
+  .sm\:ml-16 {
+    margin-left: 4rem;
+  }
+
+  .sm\:mt-20 {
+    margin-top: 5rem;
+  }
+
+  .sm\:mr-20 {
+    margin-right: 5rem;
+  }
+
+  .sm\:mb-20 {
+    margin-bottom: 5rem;
+  }
+
+  .sm\:ml-20 {
+    margin-left: 5rem;
+  }
+
+  .sm\:mt-24 {
+    margin-top: 6rem;
+  }
+
+  .sm\:mr-24 {
+    margin-right: 6rem;
+  }
+
+  .sm\:mb-24 {
+    margin-bottom: 6rem;
+  }
+
+  .sm\:ml-24 {
+    margin-left: 6rem;
+  }
+
+  .sm\:mt-32 {
+    margin-top: 8rem;
+  }
+
+  .sm\:mr-32 {
+    margin-right: 8rem;
+  }
+
+  .sm\:mb-32 {
+    margin-bottom: 8rem;
+  }
+
+  .sm\:ml-32 {
+    margin-left: 8rem;
+  }
+
+  .sm\:mt-40 {
+    margin-top: 10rem;
+  }
+
+  .sm\:mr-40 {
+    margin-right: 10rem;
+  }
+
+  .sm\:mb-40 {
+    margin-bottom: 10rem;
+  }
+
+  .sm\:ml-40 {
+    margin-left: 10rem;
+  }
+
+  .sm\:mt-48 {
+    margin-top: 12rem;
+  }
+
+  .sm\:mr-48 {
+    margin-right: 12rem;
+  }
+
+  .sm\:mb-48 {
+    margin-bottom: 12rem;
+  }
+
+  .sm\:ml-48 {
+    margin-left: 12rem;
+  }
+
+  .sm\:mt-56 {
+    margin-top: 14rem;
+  }
+
+  .sm\:mr-56 {
+    margin-right: 14rem;
+  }
+
+  .sm\:mb-56 {
+    margin-bottom: 14rem;
+  }
+
+  .sm\:ml-56 {
+    margin-left: 14rem;
+  }
+
+  .sm\:mt-64 {
+    margin-top: 16rem;
+  }
+
+  .sm\:mr-64 {
+    margin-right: 16rem;
+  }
+
+  .sm\:mb-64 {
+    margin-bottom: 16rem;
+  }
+
+  .sm\:ml-64 {
+    margin-left: 16rem;
+  }
+
+  .sm\:mt-auto {
+    margin-top: auto;
+  }
+
+  .sm\:mr-auto {
+    margin-right: auto;
+  }
+
+  .sm\:mb-auto {
+    margin-bottom: auto;
+  }
+
+  .sm\:ml-auto {
+    margin-left: auto;
+  }
+
+  .sm\:mt-px {
+    margin-top: 1px;
+  }
+
+  .sm\:mr-px {
+    margin-right: 1px;
+  }
+
+  .sm\:mb-px {
+    margin-bottom: 1px;
+  }
+
+  .sm\:ml-px {
+    margin-left: 1px;
+  }
+
+  .sm\:-mt-1 {
+    margin-top: -0.25rem;
+  }
+
+  .sm\:-mr-1 {
+    margin-right: -0.25rem;
+  }
+
+  .sm\:-mb-1 {
+    margin-bottom: -0.25rem;
+  }
+
+  .sm\:-ml-1 {
+    margin-left: -0.25rem;
+  }
+
+  .sm\:-mt-2 {
+    margin-top: -0.5rem;
+  }
+
+  .sm\:-mr-2 {
+    margin-right: -0.5rem;
+  }
+
+  .sm\:-mb-2 {
+    margin-bottom: -0.5rem;
+  }
+
+  .sm\:-ml-2 {
+    margin-left: -0.5rem;
+  }
+
+  .sm\:-mt-3 {
+    margin-top: -0.75rem;
+  }
+
+  .sm\:-mr-3 {
+    margin-right: -0.75rem;
+  }
+
+  .sm\:-mb-3 {
+    margin-bottom: -0.75rem;
+  }
+
+  .sm\:-ml-3 {
+    margin-left: -0.75rem;
+  }
+
+  .sm\:-mt-4 {
+    margin-top: -1rem;
+  }
+
+  .sm\:-mr-4 {
+    margin-right: -1rem;
+  }
+
+  .sm\:-mb-4 {
+    margin-bottom: -1rem;
+  }
+
+  .sm\:-ml-4 {
+    margin-left: -1rem;
+  }
+
+  .sm\:-mt-5 {
+    margin-top: -1.25rem;
+  }
+
+  .sm\:-mr-5 {
+    margin-right: -1.25rem;
+  }
+
+  .sm\:-mb-5 {
+    margin-bottom: -1.25rem;
+  }
+
+  .sm\:-ml-5 {
+    margin-left: -1.25rem;
+  }
+
+  .sm\:-mt-6 {
+    margin-top: -1.5rem;
+  }
+
+  .sm\:-mr-6 {
+    margin-right: -1.5rem;
+  }
+
+  .sm\:-mb-6 {
+    margin-bottom: -1.5rem;
+  }
+
+  .sm\:-ml-6 {
+    margin-left: -1.5rem;
+  }
+
+  .sm\:-mt-8 {
+    margin-top: -2rem;
+  }
+
+  .sm\:-mr-8 {
+    margin-right: -2rem;
+  }
+
+  .sm\:-mb-8 {
+    margin-bottom: -2rem;
+  }
+
+  .sm\:-ml-8 {
+    margin-left: -2rem;
+  }
+
+  .sm\:-mt-10 {
+    margin-top: -2.5rem;
+  }
+
+  .sm\:-mr-10 {
+    margin-right: -2.5rem;
+  }
+
+  .sm\:-mb-10 {
+    margin-bottom: -2.5rem;
+  }
+
+  .sm\:-ml-10 {
+    margin-left: -2.5rem;
+  }
+
+  .sm\:-mt-12 {
+    margin-top: -3rem;
+  }
+
+  .sm\:-mr-12 {
+    margin-right: -3rem;
+  }
+
+  .sm\:-mb-12 {
+    margin-bottom: -3rem;
+  }
+
+  .sm\:-ml-12 {
+    margin-left: -3rem;
+  }
+
+  .sm\:-mt-16 {
+    margin-top: -4rem;
+  }
+
+  .sm\:-mr-16 {
+    margin-right: -4rem;
+  }
+
+  .sm\:-mb-16 {
+    margin-bottom: -4rem;
+  }
+
+  .sm\:-ml-16 {
+    margin-left: -4rem;
+  }
+
+  .sm\:-mt-20 {
+    margin-top: -5rem;
+  }
+
+  .sm\:-mr-20 {
+    margin-right: -5rem;
+  }
+
+  .sm\:-mb-20 {
+    margin-bottom: -5rem;
+  }
+
+  .sm\:-ml-20 {
+    margin-left: -5rem;
+  }
+
+  .sm\:-mt-24 {
+    margin-top: -6rem;
+  }
+
+  .sm\:-mr-24 {
+    margin-right: -6rem;
+  }
+
+  .sm\:-mb-24 {
+    margin-bottom: -6rem;
+  }
+
+  .sm\:-ml-24 {
+    margin-left: -6rem;
+  }
+
+  .sm\:-mt-32 {
+    margin-top: -8rem;
+  }
+
+  .sm\:-mr-32 {
+    margin-right: -8rem;
+  }
+
+  .sm\:-mb-32 {
+    margin-bottom: -8rem;
+  }
+
+  .sm\:-ml-32 {
+    margin-left: -8rem;
+  }
+
+  .sm\:-mt-40 {
+    margin-top: -10rem;
+  }
+
+  .sm\:-mr-40 {
+    margin-right: -10rem;
+  }
+
+  .sm\:-mb-40 {
+    margin-bottom: -10rem;
+  }
+
+  .sm\:-ml-40 {
+    margin-left: -10rem;
+  }
+
+  .sm\:-mt-48 {
+    margin-top: -12rem;
+  }
+
+  .sm\:-mr-48 {
+    margin-right: -12rem;
+  }
+
+  .sm\:-mb-48 {
+    margin-bottom: -12rem;
+  }
+
+  .sm\:-ml-48 {
+    margin-left: -12rem;
+  }
+
+  .sm\:-mt-56 {
+    margin-top: -14rem;
+  }
+
+  .sm\:-mr-56 {
+    margin-right: -14rem;
+  }
+
+  .sm\:-mb-56 {
+    margin-bottom: -14rem;
+  }
+
+  .sm\:-ml-56 {
+    margin-left: -14rem;
+  }
+
+  .sm\:-mt-64 {
+    margin-top: -16rem;
+  }
+
+  .sm\:-mr-64 {
+    margin-right: -16rem;
+  }
+
+  .sm\:-mb-64 {
+    margin-bottom: -16rem;
+  }
+
+  .sm\:-ml-64 {
+    margin-left: -16rem;
+  }
+
+  .sm\:-mt-px {
+    margin-top: -1px;
+  }
+
+  .sm\:-mr-px {
+    margin-right: -1px;
+  }
+
+  .sm\:-mb-px {
+    margin-bottom: -1px;
+  }
+
+  .sm\:-ml-px {
+    margin-left: -1px;
+  }
+
+  .sm\:max-h-full {
+    max-height: 100%;
+  }
+
+  .sm\:max-h-screen {
+    max-height: 100vh;
+  }
+
+  .sm\:max-w-none {
+    max-width: none;
+  }
+
+  .sm\:max-w-xs {
+    max-width: 20rem;
+  }
+
+  .sm\:max-w-sm {
+    max-width: 24rem;
+  }
+
+  .sm\:max-w-md {
+    max-width: 28rem;
+  }
+
+  .sm\:max-w-lg {
+    max-width: 32rem;
+  }
+
+  .sm\:max-w-xl {
+    max-width: 36rem;
+  }
+
+  .sm\:max-w-2xl {
+    max-width: 42rem;
+  }
+
+  .sm\:max-w-3xl {
+    max-width: 48rem;
+  }
+
+  .sm\:max-w-4xl {
+    max-width: 56rem;
+  }
+
+  .sm\:max-w-5xl {
+    max-width: 64rem;
+  }
+
+  .sm\:max-w-6xl {
+    max-width: 72rem;
+  }
+
+  .sm\:max-w-full {
+    max-width: 100%;
+  }
+
+  .sm\:max-w-screen-sm {
+    max-width: 640px;
+  }
+
+  .sm\:max-w-screen-md {
+    max-width: 768px;
+  }
+
+  .sm\:max-w-screen-lg {
+    max-width: 1024px;
+  }
+
+  .sm\:max-w-screen-xl {
+    max-width: 1280px;
+  }
+
+  .sm\:min-h-0 {
+    min-height: 0;
+  }
+
+  .sm\:min-h-full {
+    min-height: 100%;
+  }
+
+  .sm\:min-h-screen {
+    min-height: 100vh;
+  }
+
+  .sm\:min-w-0 {
+    min-width: 0;
+  }
+
+  .sm\:min-w-full {
+    min-width: 100%;
+  }
+
+  .sm\:object-contain {
+    -o-object-fit: contain;
+       object-fit: contain;
+  }
+
+  .sm\:object-cover {
+    -o-object-fit: cover;
+       object-fit: cover;
+  }
+
+  .sm\:object-fill {
+    -o-object-fit: fill;
+       object-fit: fill;
+  }
+
+  .sm\:object-none {
+    -o-object-fit: none;
+       object-fit: none;
+  }
+
+  .sm\:object-scale-down {
+    -o-object-fit: scale-down;
+       object-fit: scale-down;
+  }
+
+  .sm\:object-bottom {
+    -o-object-position: bottom;
+       object-position: bottom;
+  }
+
+  .sm\:object-center {
+    -o-object-position: center;
+       object-position: center;
+  }
+
+  .sm\:object-left {
+    -o-object-position: left;
+       object-position: left;
+  }
+
+  .sm\:object-left-bottom {
+    -o-object-position: left bottom;
+       object-position: left bottom;
+  }
+
+  .sm\:object-left-top {
+    -o-object-position: left top;
+       object-position: left top;
+  }
+
+  .sm\:object-right {
+    -o-object-position: right;
+       object-position: right;
+  }
+
+  .sm\:object-right-bottom {
+    -o-object-position: right bottom;
+       object-position: right bottom;
+  }
+
+  .sm\:object-right-top {
+    -o-object-position: right top;
+       object-position: right top;
+  }
+
+  .sm\:object-top {
+    -o-object-position: top;
+       object-position: top;
+  }
+
+  .sm\:opacity-0 {
+    opacity: 0;
+  }
+
+  .sm\:opacity-25 {
+    opacity: 0.25;
+  }
+
+  .sm\:opacity-50 {
+    opacity: 0.5;
+  }
+
+  .sm\:opacity-75 {
+    opacity: 0.75;
+  }
+
+  .sm\:opacity-100 {
+    opacity: 1;
+  }
+
+  .sm\:hover\:opacity-0:hover {
+    opacity: 0;
+  }
+
+  .sm\:hover\:opacity-25:hover {
+    opacity: 0.25;
+  }
+
+  .sm\:hover\:opacity-50:hover {
+    opacity: 0.5;
+  }
+
+  .sm\:hover\:opacity-75:hover {
+    opacity: 0.75;
+  }
+
+  .sm\:hover\:opacity-100:hover {
+    opacity: 1;
+  }
+
+  .sm\:focus\:opacity-0:focus {
+    opacity: 0;
+  }
+
+  .sm\:focus\:opacity-25:focus {
+    opacity: 0.25;
+  }
+
+  .sm\:focus\:opacity-50:focus {
+    opacity: 0.5;
+  }
+
+  .sm\:focus\:opacity-75:focus {
+    opacity: 0.75;
+  }
+
+  .sm\:focus\:opacity-100:focus {
+    opacity: 1;
+  }
+
+  .sm\:outline-none {
+    outline: 0;
+  }
+
+  .sm\:focus\:outline-none:focus {
+    outline: 0;
+  }
+
+  .sm\:overflow-auto {
+    overflow: auto;
+  }
+
+  .sm\:overflow-hidden {
+    overflow: hidden;
+  }
+
+  .sm\:overflow-visible {
+    overflow: visible;
+  }
+
+  .sm\:overflow-scroll {
+    overflow: scroll;
+  }
+
+  .sm\:overflow-x-auto {
+    overflow-x: auto;
+  }
+
+  .sm\:overflow-y-auto {
+    overflow-y: auto;
+  }
+
+  .sm\:overflow-x-hidden {
+    overflow-x: hidden;
+  }
+
+  .sm\:overflow-y-hidden {
+    overflow-y: hidden;
+  }
+
+  .sm\:overflow-x-visible {
+    overflow-x: visible;
+  }
+
+  .sm\:overflow-y-visible {
+    overflow-y: visible;
+  }
+
+  .sm\:overflow-x-scroll {
+    overflow-x: scroll;
+  }
+
+  .sm\:overflow-y-scroll {
+    overflow-y: scroll;
+  }
+
+  .sm\:scrolling-touch {
+    -webkit-overflow-scrolling: touch;
+  }
+
+  .sm\:scrolling-auto {
+    -webkit-overflow-scrolling: auto;
+  }
+
+  .sm\:overscroll-auto {
+    -ms-scroll-chaining: chained;
+        overscroll-behavior: auto;
+  }
+
+  .sm\:overscroll-contain {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: contain;
+  }
+
+  .sm\:overscroll-none {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: none;
+  }
+
+  .sm\:overscroll-y-auto {
+    overscroll-behavior-y: auto;
+  }
+
+  .sm\:overscroll-y-contain {
+    overscroll-behavior-y: contain;
+  }
+
+  .sm\:overscroll-y-none {
+    overscroll-behavior-y: none;
+  }
+
+  .sm\:overscroll-x-auto {
+    overscroll-behavior-x: auto;
+  }
+
+  .sm\:overscroll-x-contain {
+    overscroll-behavior-x: contain;
+  }
+
+  .sm\:overscroll-x-none {
+    overscroll-behavior-x: none;
+  }
+
+  .sm\:p-0 {
+    padding: 0;
+  }
+
+  .sm\:p-1 {
+    padding: 0.25rem;
+  }
+
+  .sm\:p-2 {
+    padding: 0.5rem;
+  }
+
+  .sm\:p-3 {
+    padding: 0.75rem;
+  }
+
+  .sm\:p-4 {
+    padding: 1rem;
+  }
+
+  .sm\:p-5 {
+    padding: 1.25rem;
+  }
+
+  .sm\:p-6 {
+    padding: 1.5rem;
+  }
+
+  .sm\:p-8 {
+    padding: 2rem;
+  }
+
+  .sm\:p-10 {
+    padding: 2.5rem;
+  }
+
+  .sm\:p-12 {
+    padding: 3rem;
+  }
+
+  .sm\:p-16 {
+    padding: 4rem;
+  }
+
+  .sm\:p-20 {
+    padding: 5rem;
+  }
+
+  .sm\:p-24 {
+    padding: 6rem;
+  }
+
+  .sm\:p-32 {
+    padding: 8rem;
+  }
+
+  .sm\:p-40 {
+    padding: 10rem;
+  }
+
+  .sm\:p-48 {
+    padding: 12rem;
+  }
+
+  .sm\:p-56 {
+    padding: 14rem;
+  }
+
+  .sm\:p-64 {
+    padding: 16rem;
+  }
+
+  .sm\:p-px {
+    padding: 1px;
+  }
+
+  .sm\:py-0 {
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+
+  .sm\:px-0 {
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  .sm\:py-1 {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+  }
+
+  .sm\:px-1 {
+    padding-left: 0.25rem;
+    padding-right: 0.25rem;
+  }
+
+  .sm\:py-2 {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
+
+  .sm\:px-2 {
+    padding-left: 0.5rem;
+    padding-right: 0.5rem;
+  }
+
+  .sm\:py-3 {
+    padding-top: 0.75rem;
+    padding-bottom: 0.75rem;
+  }
+
+  .sm\:px-3 {
+    padding-left: 0.75rem;
+    padding-right: 0.75rem;
+  }
+
+  .sm\:py-4 {
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+  }
+
+  .sm\:px-4 {
+    padding-left: 1rem;
+    padding-right: 1rem;
+  }
+
+  .sm\:py-5 {
+    padding-top: 1.25rem;
+    padding-bottom: 1.25rem;
+  }
+
+  .sm\:px-5 {
+    padding-left: 1.25rem;
+    padding-right: 1.25rem;
+  }
+
+  .sm\:py-6 {
+    padding-top: 1.5rem;
+    padding-bottom: 1.5rem;
+  }
+
+  .sm\:px-6 {
+    padding-left: 1.5rem;
+    padding-right: 1.5rem;
+  }
+
+  .sm\:py-8 {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
+
+  .sm\:px-8 {
+    padding-left: 2rem;
+    padding-right: 2rem;
+  }
+
+  .sm\:py-10 {
+    padding-top: 2.5rem;
+    padding-bottom: 2.5rem;
+  }
+
+  .sm\:px-10 {
+    padding-left: 2.5rem;
+    padding-right: 2.5rem;
+  }
+
+  .sm\:py-12 {
+    padding-top: 3rem;
+    padding-bottom: 3rem;
+  }
+
+  .sm\:px-12 {
+    padding-left: 3rem;
+    padding-right: 3rem;
+  }
+
+  .sm\:py-16 {
+    padding-top: 4rem;
+    padding-bottom: 4rem;
+  }
+
+  .sm\:px-16 {
+    padding-left: 4rem;
+    padding-right: 4rem;
+  }
+
+  .sm\:py-20 {
+    padding-top: 5rem;
+    padding-bottom: 5rem;
+  }
+
+  .sm\:px-20 {
+    padding-left: 5rem;
+    padding-right: 5rem;
+  }
+
+  .sm\:py-24 {
+    padding-top: 6rem;
+    padding-bottom: 6rem;
+  }
+
+  .sm\:px-24 {
+    padding-left: 6rem;
+    padding-right: 6rem;
+  }
+
+  .sm\:py-32 {
+    padding-top: 8rem;
+    padding-bottom: 8rem;
+  }
+
+  .sm\:px-32 {
+    padding-left: 8rem;
+    padding-right: 8rem;
+  }
+
+  .sm\:py-40 {
+    padding-top: 10rem;
+    padding-bottom: 10rem;
+  }
+
+  .sm\:px-40 {
+    padding-left: 10rem;
+    padding-right: 10rem;
+  }
+
+  .sm\:py-48 {
+    padding-top: 12rem;
+    padding-bottom: 12rem;
+  }
+
+  .sm\:px-48 {
+    padding-left: 12rem;
+    padding-right: 12rem;
+  }
+
+  .sm\:py-56 {
+    padding-top: 14rem;
+    padding-bottom: 14rem;
+  }
+
+  .sm\:px-56 {
+    padding-left: 14rem;
+    padding-right: 14rem;
+  }
+
+  .sm\:py-64 {
+    padding-top: 16rem;
+    padding-bottom: 16rem;
+  }
+
+  .sm\:px-64 {
+    padding-left: 16rem;
+    padding-right: 16rem;
+  }
+
+  .sm\:py-px {
+    padding-top: 1px;
+    padding-bottom: 1px;
+  }
+
+  .sm\:px-px {
+    padding-left: 1px;
+    padding-right: 1px;
+  }
+
+  .sm\:pt-0 {
+    padding-top: 0;
+  }
+
+  .sm\:pr-0 {
+    padding-right: 0;
+  }
+
+  .sm\:pb-0 {
+    padding-bottom: 0;
+  }
+
+  .sm\:pl-0 {
+    padding-left: 0;
+  }
+
+  .sm\:pt-1 {
+    padding-top: 0.25rem;
+  }
+
+  .sm\:pr-1 {
+    padding-right: 0.25rem;
+  }
+
+  .sm\:pb-1 {
+    padding-bottom: 0.25rem;
+  }
+
+  .sm\:pl-1 {
+    padding-left: 0.25rem;
+  }
+
+  .sm\:pt-2 {
+    padding-top: 0.5rem;
+  }
+
+  .sm\:pr-2 {
+    padding-right: 0.5rem;
+  }
+
+  .sm\:pb-2 {
+    padding-bottom: 0.5rem;
+  }
+
+  .sm\:pl-2 {
+    padding-left: 0.5rem;
+  }
+
+  .sm\:pt-3 {
+    padding-top: 0.75rem;
+  }
+
+  .sm\:pr-3 {
+    padding-right: 0.75rem;
+  }
+
+  .sm\:pb-3 {
+    padding-bottom: 0.75rem;
+  }
+
+  .sm\:pl-3 {
+    padding-left: 0.75rem;
+  }
+
+  .sm\:pt-4 {
+    padding-top: 1rem;
+  }
+
+  .sm\:pr-4 {
+    padding-right: 1rem;
+  }
+
+  .sm\:pb-4 {
+    padding-bottom: 1rem;
+  }
+
+  .sm\:pl-4 {
+    padding-left: 1rem;
+  }
+
+  .sm\:pt-5 {
+    padding-top: 1.25rem;
+  }
+
+  .sm\:pr-5 {
+    padding-right: 1.25rem;
+  }
+
+  .sm\:pb-5 {
+    padding-bottom: 1.25rem;
+  }
+
+  .sm\:pl-5 {
+    padding-left: 1.25rem;
+  }
+
+  .sm\:pt-6 {
+    padding-top: 1.5rem;
+  }
+
+  .sm\:pr-6 {
+    padding-right: 1.5rem;
+  }
+
+  .sm\:pb-6 {
+    padding-bottom: 1.5rem;
+  }
+
+  .sm\:pl-6 {
+    padding-left: 1.5rem;
+  }
+
+  .sm\:pt-8 {
+    padding-top: 2rem;
+  }
+
+  .sm\:pr-8 {
+    padding-right: 2rem;
+  }
+
+  .sm\:pb-8 {
+    padding-bottom: 2rem;
+  }
+
+  .sm\:pl-8 {
+    padding-left: 2rem;
+  }
+
+  .sm\:pt-10 {
+    padding-top: 2.5rem;
+  }
+
+  .sm\:pr-10 {
+    padding-right: 2.5rem;
+  }
+
+  .sm\:pb-10 {
+    padding-bottom: 2.5rem;
+  }
+
+  .sm\:pl-10 {
+    padding-left: 2.5rem;
+  }
+
+  .sm\:pt-12 {
+    padding-top: 3rem;
+  }
+
+  .sm\:pr-12 {
+    padding-right: 3rem;
+  }
+
+  .sm\:pb-12 {
+    padding-bottom: 3rem;
+  }
+
+  .sm\:pl-12 {
+    padding-left: 3rem;
+  }
+
+  .sm\:pt-16 {
+    padding-top: 4rem;
+  }
+
+  .sm\:pr-16 {
+    padding-right: 4rem;
+  }
+
+  .sm\:pb-16 {
+    padding-bottom: 4rem;
+  }
+
+  .sm\:pl-16 {
+    padding-left: 4rem;
+  }
+
+  .sm\:pt-20 {
+    padding-top: 5rem;
+  }
+
+  .sm\:pr-20 {
+    padding-right: 5rem;
+  }
+
+  .sm\:pb-20 {
+    padding-bottom: 5rem;
+  }
+
+  .sm\:pl-20 {
+    padding-left: 5rem;
+  }
+
+  .sm\:pt-24 {
+    padding-top: 6rem;
+  }
+
+  .sm\:pr-24 {
+    padding-right: 6rem;
+  }
+
+  .sm\:pb-24 {
+    padding-bottom: 6rem;
+  }
+
+  .sm\:pl-24 {
+    padding-left: 6rem;
+  }
+
+  .sm\:pt-32 {
+    padding-top: 8rem;
+  }
+
+  .sm\:pr-32 {
+    padding-right: 8rem;
+  }
+
+  .sm\:pb-32 {
+    padding-bottom: 8rem;
+  }
+
+  .sm\:pl-32 {
+    padding-left: 8rem;
+  }
+
+  .sm\:pt-40 {
+    padding-top: 10rem;
+  }
+
+  .sm\:pr-40 {
+    padding-right: 10rem;
+  }
+
+  .sm\:pb-40 {
+    padding-bottom: 10rem;
+  }
+
+  .sm\:pl-40 {
+    padding-left: 10rem;
+  }
+
+  .sm\:pt-48 {
+    padding-top: 12rem;
+  }
+
+  .sm\:pr-48 {
+    padding-right: 12rem;
+  }
+
+  .sm\:pb-48 {
+    padding-bottom: 12rem;
+  }
+
+  .sm\:pl-48 {
+    padding-left: 12rem;
+  }
+
+  .sm\:pt-56 {
+    padding-top: 14rem;
+  }
+
+  .sm\:pr-56 {
+    padding-right: 14rem;
+  }
+
+  .sm\:pb-56 {
+    padding-bottom: 14rem;
+  }
+
+  .sm\:pl-56 {
+    padding-left: 14rem;
+  }
+
+  .sm\:pt-64 {
+    padding-top: 16rem;
+  }
+
+  .sm\:pr-64 {
+    padding-right: 16rem;
+  }
+
+  .sm\:pb-64 {
+    padding-bottom: 16rem;
+  }
+
+  .sm\:pl-64 {
+    padding-left: 16rem;
+  }
+
+  .sm\:pt-px {
+    padding-top: 1px;
+  }
+
+  .sm\:pr-px {
+    padding-right: 1px;
+  }
+
+  .sm\:pb-px {
+    padding-bottom: 1px;
+  }
+
+  .sm\:pl-px {
+    padding-left: 1px;
+  }
+
+  .sm\:placeholder-transparent::-moz-placeholder {
+    color: transparent;
+  }
+
+  .sm\:placeholder-transparent:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .sm\:placeholder-transparent::placeholder {
+    color: transparent;
+  }
+
+  .sm\:placeholder-current::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .sm\:placeholder-current:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .sm\:placeholder-current::placeholder {
+    color: currentColor;
+  }
+
+  .sm\:placeholder-black::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-black:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-black::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-white::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-white:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-white::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-gray-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-red-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-orange-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-yellow-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-green-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-teal-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-blue-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-indigo-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-purple-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-pink-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-transparent:focus::-moz-placeholder {
+    color: transparent;
+  }
+
+  .sm\:focus\:placeholder-transparent:focus:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .sm\:focus\:placeholder-transparent:focus::placeholder {
+    color: transparent;
+  }
+
+  .sm\:focus\:placeholder-current:focus::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .sm\:focus\:placeholder-current:focus:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .sm\:focus\:placeholder-current:focus::placeholder {
+    color: currentColor;
+  }
+
+  .sm\:focus\:placeholder-black:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-black:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-black:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-white:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-white:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-white:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-gray-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-red-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-orange-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-yellow-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-green-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-teal-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-blue-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-indigo-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-purple-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:focus\:placeholder-pink-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .sm\:placeholder-opacity-0::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:placeholder-opacity-0:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:placeholder-opacity-0::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:placeholder-opacity-25::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:placeholder-opacity-25:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:placeholder-opacity-25::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:placeholder-opacity-50::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:placeholder-opacity-50:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:placeholder-opacity-50::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:placeholder-opacity-75::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:placeholder-opacity-75:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:placeholder-opacity-75::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:placeholder-opacity-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:placeholder-opacity-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:placeholder-opacity-100::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:focus\:placeholder-opacity-0:focus::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:focus\:placeholder-opacity-0:focus::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .sm\:focus\:placeholder-opacity-25:focus::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:focus\:placeholder-opacity-25:focus::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .sm\:focus\:placeholder-opacity-50:focus::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:focus\:placeholder-opacity-50:focus::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .sm\:focus\:placeholder-opacity-75:focus::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:focus\:placeholder-opacity-75:focus::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .sm\:focus\:placeholder-opacity-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:focus\:placeholder-opacity-100:focus::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .sm\:pointer-events-none {
+    pointer-events: none;
+  }
+
+  .sm\:pointer-events-auto {
+    pointer-events: auto;
+  }
+
+  .sm\:static {
+    position: static;
+  }
+
+  .sm\:fixed {
+    position: fixed;
+  }
+
+  .sm\:absolute {
+    position: absolute;
+  }
+
+  .sm\:relative {
+    position: relative;
+  }
+
+  .sm\:sticky {
+    position: -webkit-sticky;
+    position: sticky;
+  }
+
+  .sm\:inset-0 {
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+  }
+
+  .sm\:inset-auto {
+    top: auto;
+    right: auto;
+    bottom: auto;
+    left: auto;
+  }
+
+  .sm\:inset-y-0 {
+    top: 0;
+    bottom: 0;
+  }
+
+  .sm\:inset-x-0 {
+    right: 0;
+    left: 0;
+  }
+
+  .sm\:inset-y-auto {
+    top: auto;
+    bottom: auto;
+  }
+
+  .sm\:inset-x-auto {
+    right: auto;
+    left: auto;
+  }
+
+  .sm\:top-0 {
+    top: 0;
+  }
+
+  .sm\:right-0 {
+    right: 0;
+  }
+
+  .sm\:bottom-0 {
+    bottom: 0;
+  }
+
+  .sm\:left-0 {
+    left: 0;
+  }
+
+  .sm\:top-auto {
+    top: auto;
+  }
+
+  .sm\:right-auto {
+    right: auto;
+  }
+
+  .sm\:bottom-auto {
+    bottom: auto;
+  }
+
+  .sm\:left-auto {
+    left: auto;
+  }
+
+  .sm\:resize-none {
+    resize: none;
+  }
+
+  .sm\:resize-y {
+    resize: vertical;
+  }
+
+  .sm\:resize-x {
+    resize: horizontal;
+  }
+
+  .sm\:resize {
+    resize: both;
+  }
+
+  .sm\:shadow-xs {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:shadow-sm {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:shadow {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:shadow-md {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:shadow-lg {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:shadow-xl {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .sm\:shadow-2xl {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .sm\:shadow-inner {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:shadow-outline {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .sm\:shadow-none {
+    box-shadow: none;
+  }
+
+  .sm\:hover\:shadow-xs:hover {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:hover\:shadow-sm:hover {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:hover\:shadow:hover {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:hover\:shadow-md:hover {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:hover\:shadow-lg:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:hover\:shadow-xl:hover {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .sm\:hover\:shadow-2xl:hover {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .sm\:hover\:shadow-inner:hover {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:hover\:shadow-outline:hover {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .sm\:hover\:shadow-none:hover {
+    box-shadow: none;
+  }
+
+  .sm\:focus\:shadow-xs:focus {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:focus\:shadow-sm:focus {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:focus\:shadow:focus {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:focus\:shadow-md:focus {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:focus\:shadow-lg:focus {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .sm\:focus\:shadow-xl:focus {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .sm\:focus\:shadow-2xl:focus {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .sm\:focus\:shadow-inner:focus {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .sm\:focus\:shadow-outline:focus {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .sm\:focus\:shadow-none:focus {
+    box-shadow: none;
+  }
+
+  .sm\:fill-current {
+    fill: currentColor;
+  }
+
+  .sm\:stroke-current {
+    stroke: currentColor;
+  }
+
+  .sm\:stroke-0 {
+    stroke-width: 0;
+  }
+
+  .sm\:stroke-1 {
+    stroke-width: 1;
+  }
+
+  .sm\:stroke-2 {
+    stroke-width: 2;
+  }
+
+  .sm\:table-auto {
+    table-layout: auto;
+  }
+
+  .sm\:table-fixed {
+    table-layout: fixed;
+  }
+
+  .sm\:text-left {
+    text-align: left;
+  }
+
+  .sm\:text-center {
+    text-align: center;
+  }
+
+  .sm\:text-right {
+    text-align: right;
+  }
+
+  .sm\:text-justify {
+    text-align: justify;
+  }
+
+  .sm\:text-transparent {
+    color: transparent;
+  }
+
+  .sm\:text-current {
+    color: currentColor;
+  }
+
+  .sm\:text-black {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .sm\:text-white {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .sm\:text-gray-100 {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .sm\:text-gray-200 {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .sm\:text-gray-300 {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .sm\:text-gray-400 {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .sm\:text-gray-500 {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .sm\:text-gray-600 {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .sm\:text-gray-700 {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .sm\:text-gray-800 {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .sm\:text-gray-900 {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .sm\:text-red-100 {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .sm\:text-red-200 {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .sm\:text-red-300 {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .sm\:text-red-400 {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .sm\:text-red-500 {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .sm\:text-red-600 {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .sm\:text-red-700 {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .sm\:text-red-800 {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .sm\:text-red-900 {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .sm\:text-orange-100 {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .sm\:text-orange-200 {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .sm\:text-orange-300 {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .sm\:text-orange-400 {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .sm\:text-orange-500 {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .sm\:text-orange-600 {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .sm\:text-orange-700 {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .sm\:text-orange-800 {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .sm\:text-orange-900 {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-100 {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-200 {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-300 {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-400 {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-500 {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-600 {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-700 {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-800 {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .sm\:text-yellow-900 {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .sm\:text-green-100 {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .sm\:text-green-200 {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .sm\:text-green-300 {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .sm\:text-green-400 {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .sm\:text-green-500 {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .sm\:text-green-600 {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .sm\:text-green-700 {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .sm\:text-green-800 {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .sm\:text-green-900 {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .sm\:text-teal-100 {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .sm\:text-teal-200 {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .sm\:text-teal-300 {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .sm\:text-teal-400 {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .sm\:text-teal-500 {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .sm\:text-teal-600 {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .sm\:text-teal-700 {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .sm\:text-teal-800 {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .sm\:text-teal-900 {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .sm\:text-blue-100 {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .sm\:text-blue-200 {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .sm\:text-blue-300 {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .sm\:text-blue-400 {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .sm\:text-blue-500 {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .sm\:text-blue-600 {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .sm\:text-blue-700 {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .sm\:text-blue-800 {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .sm\:text-blue-900 {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-100 {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-200 {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-300 {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-400 {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-500 {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-600 {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-700 {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-800 {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .sm\:text-indigo-900 {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .sm\:text-purple-100 {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .sm\:text-purple-200 {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .sm\:text-purple-300 {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .sm\:text-purple-400 {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .sm\:text-purple-500 {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .sm\:text-purple-600 {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .sm\:text-purple-700 {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .sm\:text-purple-800 {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .sm\:text-purple-900 {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .sm\:text-pink-100 {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .sm\:text-pink-200 {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .sm\:text-pink-300 {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .sm\:text-pink-400 {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .sm\:text-pink-500 {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .sm\:text-pink-600 {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .sm\:text-pink-700 {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .sm\:text-pink-800 {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .sm\:text-pink-900 {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-transparent:hover {
+    color: transparent;
+  }
+
+  .sm\:hover\:text-current:hover {
+    color: currentColor;
+  }
+
+  .sm\:hover\:text-black:hover {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-white:hover {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-100:hover {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-200:hover {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-300:hover {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-400:hover {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-500:hover {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-600:hover {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-700:hover {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-800:hover {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-gray-900:hover {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-100:hover {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-200:hover {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-300:hover {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-400:hover {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-500:hover {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-600:hover {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-700:hover {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-800:hover {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-red-900:hover {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-100:hover {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-200:hover {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-300:hover {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-400:hover {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-500:hover {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-600:hover {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-700:hover {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-800:hover {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-orange-900:hover {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-100:hover {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-200:hover {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-300:hover {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-400:hover {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-500:hover {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-600:hover {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-700:hover {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-800:hover {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-yellow-900:hover {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-100:hover {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-200:hover {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-300:hover {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-400:hover {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-500:hover {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-600:hover {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-700:hover {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-800:hover {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-green-900:hover {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-100:hover {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-200:hover {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-300:hover {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-400:hover {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-500:hover {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-600:hover {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-700:hover {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-800:hover {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-teal-900:hover {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-100:hover {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-200:hover {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-300:hover {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-400:hover {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-500:hover {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-600:hover {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-700:hover {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-800:hover {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-blue-900:hover {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-100:hover {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-200:hover {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-300:hover {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-400:hover {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-500:hover {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-600:hover {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-700:hover {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-800:hover {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-indigo-900:hover {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-100:hover {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-200:hover {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-300:hover {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-400:hover {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-500:hover {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-600:hover {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-700:hover {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-800:hover {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-purple-900:hover {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-100:hover {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-200:hover {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-300:hover {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-400:hover {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-500:hover {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-600:hover {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-700:hover {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-800:hover {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .sm\:hover\:text-pink-900:hover {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-transparent:focus {
+    color: transparent;
+  }
+
+  .sm\:focus\:text-current:focus {
+    color: currentColor;
+  }
+
+  .sm\:focus\:text-black:focus {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-white:focus {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-100:focus {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-200:focus {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-300:focus {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-400:focus {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-500:focus {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-600:focus {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-700:focus {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-800:focus {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-gray-900:focus {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-100:focus {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-200:focus {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-300:focus {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-400:focus {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-500:focus {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-600:focus {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-700:focus {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-800:focus {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-red-900:focus {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-100:focus {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-200:focus {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-300:focus {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-400:focus {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-500:focus {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-600:focus {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-700:focus {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-800:focus {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-orange-900:focus {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-100:focus {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-200:focus {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-300:focus {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-400:focus {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-500:focus {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-600:focus {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-700:focus {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-800:focus {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-yellow-900:focus {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-100:focus {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-200:focus {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-300:focus {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-400:focus {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-500:focus {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-600:focus {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-700:focus {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-800:focus {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-green-900:focus {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-100:focus {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-200:focus {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-300:focus {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-400:focus {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-500:focus {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-600:focus {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-700:focus {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-800:focus {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-teal-900:focus {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-100:focus {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-200:focus {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-300:focus {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-400:focus {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-500:focus {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-600:focus {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-700:focus {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-800:focus {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-blue-900:focus {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-100:focus {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-200:focus {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-300:focus {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-400:focus {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-500:focus {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-600:focus {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-700:focus {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-800:focus {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-indigo-900:focus {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-100:focus {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-200:focus {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-300:focus {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-400:focus {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-500:focus {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-600:focus {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-700:focus {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-800:focus {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-purple-900:focus {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-100:focus {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-200:focus {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-300:focus {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-400:focus {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-500:focus {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-600:focus {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-700:focus {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-800:focus {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .sm\:focus\:text-pink-900:focus {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .sm\:text-opacity-0 {
+    --text-opacity: 0;
+  }
+
+  .sm\:text-opacity-25 {
+    --text-opacity: 0.25;
+  }
+
+  .sm\:text-opacity-50 {
+    --text-opacity: 0.5;
+  }
+
+  .sm\:text-opacity-75 {
+    --text-opacity: 0.75;
+  }
+
+  .sm\:text-opacity-100 {
+    --text-opacity: 1;
+  }
+
+  .sm\:hover\:text-opacity-0:hover {
+    --text-opacity: 0;
+  }
+
+  .sm\:hover\:text-opacity-25:hover {
+    --text-opacity: 0.25;
+  }
+
+  .sm\:hover\:text-opacity-50:hover {
+    --text-opacity: 0.5;
+  }
+
+  .sm\:hover\:text-opacity-75:hover {
+    --text-opacity: 0.75;
+  }
+
+  .sm\:hover\:text-opacity-100:hover {
+    --text-opacity: 1;
+  }
+
+  .sm\:focus\:text-opacity-0:focus {
+    --text-opacity: 0;
+  }
+
+  .sm\:focus\:text-opacity-25:focus {
+    --text-opacity: 0.25;
+  }
+
+  .sm\:focus\:text-opacity-50:focus {
+    --text-opacity: 0.5;
+  }
+
+  .sm\:focus\:text-opacity-75:focus {
+    --text-opacity: 0.75;
+  }
+
+  .sm\:focus\:text-opacity-100:focus {
+    --text-opacity: 1;
+  }
+
+  .sm\:italic {
+    font-style: italic;
+  }
+
+  .sm\:not-italic {
+    font-style: normal;
+  }
+
+  .sm\:uppercase {
+    text-transform: uppercase;
+  }
+
+  .sm\:lowercase {
+    text-transform: lowercase;
+  }
+
+  .sm\:capitalize {
+    text-transform: capitalize;
+  }
+
+  .sm\:normal-case {
+    text-transform: none;
+  }
+
+  .sm\:underline {
+    text-decoration: underline;
+  }
+
+  .sm\:line-through {
+    text-decoration: line-through;
+  }
+
+  .sm\:no-underline {
+    text-decoration: none;
+  }
+
+  .sm\:hover\:underline:hover {
+    text-decoration: underline;
+  }
+
+  .sm\:hover\:line-through:hover {
+    text-decoration: line-through;
+  }
+
+  .sm\:hover\:no-underline:hover {
+    text-decoration: none;
+  }
+
+  .sm\:focus\:underline:focus {
+    text-decoration: underline;
+  }
+
+  .sm\:focus\:line-through:focus {
+    text-decoration: line-through;
+  }
+
+  .sm\:focus\:no-underline:focus {
+    text-decoration: none;
+  }
+
+  .sm\:antialiased {
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+
+  .sm\:subpixel-antialiased {
+    -webkit-font-smoothing: auto;
+    -moz-osx-font-smoothing: auto;
+  }
+
+  .sm\:ordinal, .sm\:slashed-zero, .sm\:lining-nums, .sm\:oldstyle-nums, .sm\:proportional-nums, .sm\:tabular-nums, .sm\:diagonal-fractions, .sm\:stacked-fractions {
+    --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+    font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+  }
+
+  .sm\:normal-nums {
+    font-variant-numeric: normal;
+  }
+
+  .sm\:ordinal {
+    --font-variant-numeric-ordinal: ordinal;
+  }
+
+  .sm\:slashed-zero {
+    --font-variant-numeric-slashed-zero: slashed-zero;
+  }
+
+  .sm\:lining-nums {
+    --font-variant-numeric-figure: lining-nums;
+  }
+
+  .sm\:oldstyle-nums {
+    --font-variant-numeric-figure: oldstyle-nums;
+  }
+
+  .sm\:proportional-nums {
+    --font-variant-numeric-spacing: proportional-nums;
+  }
+
+  .sm\:tabular-nums {
+    --font-variant-numeric-spacing: tabular-nums;
+  }
+
+  .sm\:diagonal-fractions {
+    --font-variant-numeric-fraction: diagonal-fractions;
+  }
+
+  .sm\:stacked-fractions {
+    --font-variant-numeric-fraction: stacked-fractions;
+  }
+
+  .sm\:tracking-tighter {
+    letter-spacing: -0.05em;
+  }
+
+  .sm\:tracking-tight {
+    letter-spacing: -0.025em;
+  }
+
+  .sm\:tracking-normal {
+    letter-spacing: 0;
+  }
+
+  .sm\:tracking-wide {
+    letter-spacing: 0.025em;
+  }
+
+  .sm\:tracking-wider {
+    letter-spacing: 0.05em;
+  }
+
+  .sm\:tracking-widest {
+    letter-spacing: 0.1em;
+  }
+
+  .sm\:select-none {
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+  }
+
+  .sm\:select-text {
+    -webkit-user-select: text;
+       -moz-user-select: text;
+        -ms-user-select: text;
+            user-select: text;
+  }
+
+  .sm\:select-all {
+    -webkit-user-select: all;
+       -moz-user-select: all;
+        -ms-user-select: all;
+            user-select: all;
+  }
+
+  .sm\:select-auto {
+    -webkit-user-select: auto;
+       -moz-user-select: auto;
+        -ms-user-select: auto;
+            user-select: auto;
+  }
+
+  .sm\:align-baseline {
+    vertical-align: baseline;
+  }
+
+  .sm\:align-top {
+    vertical-align: top;
+  }
+
+  .sm\:align-middle {
+    vertical-align: middle;
+  }
+
+  .sm\:align-bottom {
+    vertical-align: bottom;
+  }
+
+  .sm\:align-text-top {
+    vertical-align: text-top;
+  }
+
+  .sm\:align-text-bottom {
+    vertical-align: text-bottom;
+  }
+
+  .sm\:visible {
+    visibility: visible;
+  }
+
+  .sm\:invisible {
+    visibility: hidden;
+  }
+
+  .sm\:whitespace-normal {
+    white-space: normal;
+  }
+
+  .sm\:whitespace-no-wrap {
+    white-space: nowrap;
+  }
+
+  .sm\:whitespace-pre {
+    white-space: pre;
+  }
+
+  .sm\:whitespace-pre-line {
+    white-space: pre-line;
+  }
+
+  .sm\:whitespace-pre-wrap {
+    white-space: pre-wrap;
+  }
+
+  .sm\:break-normal {
+    overflow-wrap: normal;
+    word-break: normal;
+  }
+
+  .sm\:break-words {
+    overflow-wrap: break-word;
+  }
+
+  .sm\:break-all {
+    word-break: break-all;
+  }
+
+  .sm\:truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .sm\:w-0 {
+    width: 0;
+  }
+
+  .sm\:w-1 {
+    width: 0.25rem;
+  }
+
+  .sm\:w-2 {
+    width: 0.5rem;
+  }
+
+  .sm\:w-3 {
+    width: 0.75rem;
+  }
+
+  .sm\:w-4 {
+    width: 1rem;
+  }
+
+  .sm\:w-5 {
+    width: 1.25rem;
+  }
+
+  .sm\:w-6 {
+    width: 1.5rem;
+  }
+
+  .sm\:w-8 {
+    width: 2rem;
+  }
+
+  .sm\:w-10 {
+    width: 2.5rem;
+  }
+
+  .sm\:w-12 {
+    width: 3rem;
+  }
+
+  .sm\:w-16 {
+    width: 4rem;
+  }
+
+  .sm\:w-20 {
+    width: 5rem;
+  }
+
+  .sm\:w-24 {
+    width: 6rem;
+  }
+
+  .sm\:w-32 {
+    width: 8rem;
+  }
+
+  .sm\:w-40 {
+    width: 10rem;
+  }
+
+  .sm\:w-48 {
+    width: 12rem;
+  }
+
+  .sm\:w-56 {
+    width: 14rem;
+  }
+
+  .sm\:w-64 {
+    width: 16rem;
+  }
+
+  .sm\:w-auto {
+    width: auto;
+  }
+
+  .sm\:w-px {
+    width: 1px;
+  }
+
+  .sm\:w-1\/2 {
+    width: 50%;
+  }
+
+  .sm\:w-1\/3 {
+    width: 33.333333%;
+  }
+
+  .sm\:w-2\/3 {
+    width: 66.666667%;
+  }
+
+  .sm\:w-1\/4 {
+    width: 25%;
+  }
+
+  .sm\:w-2\/4 {
+    width: 50%;
+  }
+
+  .sm\:w-3\/4 {
+    width: 75%;
+  }
+
+  .sm\:w-1\/5 {
+    width: 20%;
+  }
+
+  .sm\:w-2\/5 {
+    width: 40%;
+  }
+
+  .sm\:w-3\/5 {
+    width: 60%;
+  }
+
+  .sm\:w-4\/5 {
+    width: 80%;
+  }
+
+  .sm\:w-1\/6 {
+    width: 16.666667%;
+  }
+
+  .sm\:w-2\/6 {
+    width: 33.333333%;
+  }
+
+  .sm\:w-3\/6 {
+    width: 50%;
+  }
+
+  .sm\:w-4\/6 {
+    width: 66.666667%;
+  }
+
+  .sm\:w-5\/6 {
+    width: 83.333333%;
+  }
+
+  .sm\:w-1\/12 {
+    width: 8.333333%;
+  }
+
+  .sm\:w-2\/12 {
+    width: 16.666667%;
+  }
+
+  .sm\:w-3\/12 {
+    width: 25%;
+  }
+
+  .sm\:w-4\/12 {
+    width: 33.333333%;
+  }
+
+  .sm\:w-5\/12 {
+    width: 41.666667%;
+  }
+
+  .sm\:w-6\/12 {
+    width: 50%;
+  }
+
+  .sm\:w-7\/12 {
+    width: 58.333333%;
+  }
+
+  .sm\:w-8\/12 {
+    width: 66.666667%;
+  }
+
+  .sm\:w-9\/12 {
+    width: 75%;
+  }
+
+  .sm\:w-10\/12 {
+    width: 83.333333%;
+  }
+
+  .sm\:w-11\/12 {
+    width: 91.666667%;
+  }
+
+  .sm\:w-full {
+    width: 100%;
+  }
+
+  .sm\:w-screen {
+    width: 100vw;
+  }
+
+  .sm\:z-0 {
+    z-index: 0;
+  }
+
+  .sm\:z-10 {
+    z-index: 10;
+  }
+
+  .sm\:z-20 {
+    z-index: 20;
+  }
+
+  .sm\:z-30 {
+    z-index: 30;
+  }
+
+  .sm\:z-40 {
+    z-index: 40;
+  }
+
+  .sm\:z-50 {
+    z-index: 50;
+  }
+
+  .sm\:z-auto {
+    z-index: auto;
+  }
+
+  .sm\:gap-0 {
+    grid-gap: 0;
+    gap: 0;
+  }
+
+  .sm\:gap-1 {
+    grid-gap: 0.25rem;
+    gap: 0.25rem;
+  }
+
+  .sm\:gap-2 {
+    grid-gap: 0.5rem;
+    gap: 0.5rem;
+  }
+
+  .sm\:gap-3 {
+    grid-gap: 0.75rem;
+    gap: 0.75rem;
+  }
+
+  .sm\:gap-4 {
+    grid-gap: 1rem;
+    gap: 1rem;
+  }
+
+  .sm\:gap-5 {
+    grid-gap: 1.25rem;
+    gap: 1.25rem;
+  }
+
+  .sm\:gap-6 {
+    grid-gap: 1.5rem;
+    gap: 1.5rem;
+  }
+
+  .sm\:gap-8 {
+    grid-gap: 2rem;
+    gap: 2rem;
+  }
+
+  .sm\:gap-10 {
+    grid-gap: 2.5rem;
+    gap: 2.5rem;
+  }
+
+  .sm\:gap-12 {
+    grid-gap: 3rem;
+    gap: 3rem;
+  }
+
+  .sm\:gap-16 {
+    grid-gap: 4rem;
+    gap: 4rem;
+  }
+
+  .sm\:gap-20 {
+    grid-gap: 5rem;
+    gap: 5rem;
+  }
+
+  .sm\:gap-24 {
+    grid-gap: 6rem;
+    gap: 6rem;
+  }
+
+  .sm\:gap-32 {
+    grid-gap: 8rem;
+    gap: 8rem;
+  }
+
+  .sm\:gap-40 {
+    grid-gap: 10rem;
+    gap: 10rem;
+  }
+
+  .sm\:gap-48 {
+    grid-gap: 12rem;
+    gap: 12rem;
+  }
+
+  .sm\:gap-56 {
+    grid-gap: 14rem;
+    gap: 14rem;
+  }
+
+  .sm\:gap-64 {
+    grid-gap: 16rem;
+    gap: 16rem;
+  }
+
+  .sm\:gap-px {
+    grid-gap: 1px;
+    gap: 1px;
+  }
+
+  .sm\:col-gap-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .sm\:col-gap-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .sm\:col-gap-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .sm\:col-gap-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .sm\:col-gap-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .sm\:col-gap-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .sm\:col-gap-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .sm\:col-gap-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .sm\:col-gap-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .sm\:col-gap-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .sm\:col-gap-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .sm\:col-gap-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .sm\:col-gap-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .sm\:col-gap-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .sm\:col-gap-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .sm\:col-gap-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .sm\:col-gap-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .sm\:col-gap-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .sm\:col-gap-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .sm\:gap-x-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .sm\:gap-x-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .sm\:gap-x-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .sm\:gap-x-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .sm\:gap-x-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .sm\:gap-x-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .sm\:gap-x-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .sm\:gap-x-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .sm\:gap-x-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .sm\:gap-x-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .sm\:gap-x-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .sm\:gap-x-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .sm\:gap-x-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .sm\:gap-x-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .sm\:gap-x-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .sm\:gap-x-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .sm\:gap-x-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .sm\:gap-x-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .sm\:gap-x-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .sm\:row-gap-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .sm\:row-gap-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .sm\:row-gap-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .sm\:row-gap-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .sm\:row-gap-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .sm\:row-gap-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .sm\:row-gap-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .sm\:row-gap-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .sm\:row-gap-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .sm\:row-gap-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .sm\:row-gap-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .sm\:row-gap-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .sm\:row-gap-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .sm\:row-gap-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .sm\:row-gap-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .sm\:row-gap-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .sm\:row-gap-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .sm\:row-gap-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .sm\:row-gap-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .sm\:gap-y-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .sm\:gap-y-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .sm\:gap-y-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .sm\:gap-y-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .sm\:gap-y-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .sm\:gap-y-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .sm\:gap-y-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .sm\:gap-y-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .sm\:gap-y-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .sm\:gap-y-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .sm\:gap-y-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .sm\:gap-y-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .sm\:gap-y-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .sm\:gap-y-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .sm\:gap-y-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .sm\:gap-y-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .sm\:gap-y-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .sm\:gap-y-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .sm\:gap-y-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .sm\:grid-flow-row {
+    grid-auto-flow: row;
+  }
+
+  .sm\:grid-flow-col {
+    grid-auto-flow: column;
+  }
+
+  .sm\:grid-flow-row-dense {
+    grid-auto-flow: row dense;
+  }
+
+  .sm\:grid-flow-col-dense {
+    grid-auto-flow: column dense;
+  }
+
+  .sm\:grid-cols-1 {
+    grid-template-columns: repeat(1, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-2 {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-3 {
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-4 {
+    grid-template-columns: repeat(4, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-5 {
+    grid-template-columns: repeat(5, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-6 {
+    grid-template-columns: repeat(6, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-7 {
+    grid-template-columns: repeat(7, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-8 {
+    grid-template-columns: repeat(8, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-9 {
+    grid-template-columns: repeat(9, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-10 {
+    grid-template-columns: repeat(10, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-11 {
+    grid-template-columns: repeat(11, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-12 {
+    grid-template-columns: repeat(12, minmax(0, 1fr));
+  }
+
+  .sm\:grid-cols-none {
+    grid-template-columns: none;
+  }
+
+  .sm\:col-auto {
+    grid-column: auto;
+  }
+
+  .sm\:col-span-1 {
+    grid-column: span 1 / span 1;
+  }
+
+  .sm\:col-span-2 {
+    grid-column: span 2 / span 2;
+  }
+
+  .sm\:col-span-3 {
+    grid-column: span 3 / span 3;
+  }
+
+  .sm\:col-span-4 {
+    grid-column: span 4 / span 4;
+  }
+
+  .sm\:col-span-5 {
+    grid-column: span 5 / span 5;
+  }
+
+  .sm\:col-span-6 {
+    grid-column: span 6 / span 6;
+  }
+
+  .sm\:col-span-7 {
+    grid-column: span 7 / span 7;
+  }
+
+  .sm\:col-span-8 {
+    grid-column: span 8 / span 8;
+  }
+
+  .sm\:col-span-9 {
+    grid-column: span 9 / span 9;
+  }
+
+  .sm\:col-span-10 {
+    grid-column: span 10 / span 10;
+  }
+
+  .sm\:col-span-11 {
+    grid-column: span 11 / span 11;
+  }
+
+  .sm\:col-span-12 {
+    grid-column: span 12 / span 12;
+  }
+
+  .sm\:col-start-1 {
+    grid-column-start: 1;
+  }
+
+  .sm\:col-start-2 {
+    grid-column-start: 2;
+  }
+
+  .sm\:col-start-3 {
+    grid-column-start: 3;
+  }
+
+  .sm\:col-start-4 {
+    grid-column-start: 4;
+  }
+
+  .sm\:col-start-5 {
+    grid-column-start: 5;
+  }
+
+  .sm\:col-start-6 {
+    grid-column-start: 6;
+  }
+
+  .sm\:col-start-7 {
+    grid-column-start: 7;
+  }
+
+  .sm\:col-start-8 {
+    grid-column-start: 8;
+  }
+
+  .sm\:col-start-9 {
+    grid-column-start: 9;
+  }
+
+  .sm\:col-start-10 {
+    grid-column-start: 10;
+  }
+
+  .sm\:col-start-11 {
+    grid-column-start: 11;
+  }
+
+  .sm\:col-start-12 {
+    grid-column-start: 12;
+  }
+
+  .sm\:col-start-13 {
+    grid-column-start: 13;
+  }
+
+  .sm\:col-start-auto {
+    grid-column-start: auto;
+  }
+
+  .sm\:col-end-1 {
+    grid-column-end: 1;
+  }
+
+  .sm\:col-end-2 {
+    grid-column-end: 2;
+  }
+
+  .sm\:col-end-3 {
+    grid-column-end: 3;
+  }
+
+  .sm\:col-end-4 {
+    grid-column-end: 4;
+  }
+
+  .sm\:col-end-5 {
+    grid-column-end: 5;
+  }
+
+  .sm\:col-end-6 {
+    grid-column-end: 6;
+  }
+
+  .sm\:col-end-7 {
+    grid-column-end: 7;
+  }
+
+  .sm\:col-end-8 {
+    grid-column-end: 8;
+  }
+
+  .sm\:col-end-9 {
+    grid-column-end: 9;
+  }
+
+  .sm\:col-end-10 {
+    grid-column-end: 10;
+  }
+
+  .sm\:col-end-11 {
+    grid-column-end: 11;
+  }
+
+  .sm\:col-end-12 {
+    grid-column-end: 12;
+  }
+
+  .sm\:col-end-13 {
+    grid-column-end: 13;
+  }
+
+  .sm\:col-end-auto {
+    grid-column-end: auto;
+  }
+
+  .sm\:grid-rows-1 {
+    grid-template-rows: repeat(1, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-2 {
+    grid-template-rows: repeat(2, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-3 {
+    grid-template-rows: repeat(3, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-4 {
+    grid-template-rows: repeat(4, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-5 {
+    grid-template-rows: repeat(5, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-6 {
+    grid-template-rows: repeat(6, minmax(0, 1fr));
+  }
+
+  .sm\:grid-rows-none {
+    grid-template-rows: none;
+  }
+
+  .sm\:row-auto {
+    grid-row: auto;
+  }
+
+  .sm\:row-span-1 {
+    grid-row: span 1 / span 1;
+  }
+
+  .sm\:row-span-2 {
+    grid-row: span 2 / span 2;
+  }
+
+  .sm\:row-span-3 {
+    grid-row: span 3 / span 3;
+  }
+
+  .sm\:row-span-4 {
+    grid-row: span 4 / span 4;
+  }
+
+  .sm\:row-span-5 {
+    grid-row: span 5 / span 5;
+  }
+
+  .sm\:row-span-6 {
+    grid-row: span 6 / span 6;
+  }
+
+  .sm\:row-start-1 {
+    grid-row-start: 1;
+  }
+
+  .sm\:row-start-2 {
+    grid-row-start: 2;
+  }
+
+  .sm\:row-start-3 {
+    grid-row-start: 3;
+  }
+
+  .sm\:row-start-4 {
+    grid-row-start: 4;
+  }
+
+  .sm\:row-start-5 {
+    grid-row-start: 5;
+  }
+
+  .sm\:row-start-6 {
+    grid-row-start: 6;
+  }
+
+  .sm\:row-start-7 {
+    grid-row-start: 7;
+  }
+
+  .sm\:row-start-auto {
+    grid-row-start: auto;
+  }
+
+  .sm\:row-end-1 {
+    grid-row-end: 1;
+  }
+
+  .sm\:row-end-2 {
+    grid-row-end: 2;
+  }
+
+  .sm\:row-end-3 {
+    grid-row-end: 3;
+  }
+
+  .sm\:row-end-4 {
+    grid-row-end: 4;
+  }
+
+  .sm\:row-end-5 {
+    grid-row-end: 5;
+  }
+
+  .sm\:row-end-6 {
+    grid-row-end: 6;
+  }
+
+  .sm\:row-end-7 {
+    grid-row-end: 7;
+  }
+
+  .sm\:row-end-auto {
+    grid-row-end: auto;
+  }
+
+  .sm\:transform {
+    --transform-translate-x: 0;
+    --transform-translate-y: 0;
+    --transform-rotate: 0;
+    --transform-skew-x: 0;
+    --transform-skew-y: 0;
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+    transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+  }
+
+  .sm\:transform-none {
+    transform: none;
+  }
+
+  .sm\:origin-center {
+    transform-origin: center;
+  }
+
+  .sm\:origin-top {
+    transform-origin: top;
+  }
+
+  .sm\:origin-top-right {
+    transform-origin: top right;
+  }
+
+  .sm\:origin-right {
+    transform-origin: right;
+  }
+
+  .sm\:origin-bottom-right {
+    transform-origin: bottom right;
+  }
+
+  .sm\:origin-bottom {
+    transform-origin: bottom;
+  }
+
+  .sm\:origin-bottom-left {
+    transform-origin: bottom left;
+  }
+
+  .sm\:origin-left {
+    transform-origin: left;
+  }
+
+  .sm\:origin-top-left {
+    transform-origin: top left;
+  }
+
+  .sm\:scale-0 {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .sm\:scale-50 {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .sm\:scale-75 {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .sm\:scale-90 {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .sm\:scale-95 {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .sm\:scale-100 {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .sm\:scale-105 {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:scale-110 {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:scale-125 {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:scale-150 {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:scale-x-0 {
+    --transform-scale-x: 0;
+  }
+
+  .sm\:scale-x-50 {
+    --transform-scale-x: .5;
+  }
+
+  .sm\:scale-x-75 {
+    --transform-scale-x: .75;
+  }
+
+  .sm\:scale-x-90 {
+    --transform-scale-x: .9;
+  }
+
+  .sm\:scale-x-95 {
+    --transform-scale-x: .95;
+  }
+
+  .sm\:scale-x-100 {
+    --transform-scale-x: 1;
+  }
+
+  .sm\:scale-x-105 {
+    --transform-scale-x: 1.05;
+  }
+
+  .sm\:scale-x-110 {
+    --transform-scale-x: 1.1;
+  }
+
+  .sm\:scale-x-125 {
+    --transform-scale-x: 1.25;
+  }
+
+  .sm\:scale-x-150 {
+    --transform-scale-x: 1.5;
+  }
+
+  .sm\:scale-y-0 {
+    --transform-scale-y: 0;
+  }
+
+  .sm\:scale-y-50 {
+    --transform-scale-y: .5;
+  }
+
+  .sm\:scale-y-75 {
+    --transform-scale-y: .75;
+  }
+
+  .sm\:scale-y-90 {
+    --transform-scale-y: .9;
+  }
+
+  .sm\:scale-y-95 {
+    --transform-scale-y: .95;
+  }
+
+  .sm\:scale-y-100 {
+    --transform-scale-y: 1;
+  }
+
+  .sm\:scale-y-105 {
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:scale-y-110 {
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:scale-y-125 {
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:scale-y-150 {
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:hover\:scale-0:hover {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .sm\:hover\:scale-50:hover {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .sm\:hover\:scale-75:hover {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .sm\:hover\:scale-90:hover {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .sm\:hover\:scale-95:hover {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .sm\:hover\:scale-100:hover {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .sm\:hover\:scale-105:hover {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:hover\:scale-110:hover {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:hover\:scale-125:hover {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:hover\:scale-150:hover {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:hover\:scale-x-0:hover {
+    --transform-scale-x: 0;
+  }
+
+  .sm\:hover\:scale-x-50:hover {
+    --transform-scale-x: .5;
+  }
+
+  .sm\:hover\:scale-x-75:hover {
+    --transform-scale-x: .75;
+  }
+
+  .sm\:hover\:scale-x-90:hover {
+    --transform-scale-x: .9;
+  }
+
+  .sm\:hover\:scale-x-95:hover {
+    --transform-scale-x: .95;
+  }
+
+  .sm\:hover\:scale-x-100:hover {
+    --transform-scale-x: 1;
+  }
+
+  .sm\:hover\:scale-x-105:hover {
+    --transform-scale-x: 1.05;
+  }
+
+  .sm\:hover\:scale-x-110:hover {
+    --transform-scale-x: 1.1;
+  }
+
+  .sm\:hover\:scale-x-125:hover {
+    --transform-scale-x: 1.25;
+  }
+
+  .sm\:hover\:scale-x-150:hover {
+    --transform-scale-x: 1.5;
+  }
+
+  .sm\:hover\:scale-y-0:hover {
+    --transform-scale-y: 0;
+  }
+
+  .sm\:hover\:scale-y-50:hover {
+    --transform-scale-y: .5;
+  }
+
+  .sm\:hover\:scale-y-75:hover {
+    --transform-scale-y: .75;
+  }
+
+  .sm\:hover\:scale-y-90:hover {
+    --transform-scale-y: .9;
+  }
+
+  .sm\:hover\:scale-y-95:hover {
+    --transform-scale-y: .95;
+  }
+
+  .sm\:hover\:scale-y-100:hover {
+    --transform-scale-y: 1;
+  }
+
+  .sm\:hover\:scale-y-105:hover {
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:hover\:scale-y-110:hover {
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:hover\:scale-y-125:hover {
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:hover\:scale-y-150:hover {
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:focus\:scale-0:focus {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .sm\:focus\:scale-50:focus {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .sm\:focus\:scale-75:focus {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .sm\:focus\:scale-90:focus {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .sm\:focus\:scale-95:focus {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .sm\:focus\:scale-100:focus {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .sm\:focus\:scale-105:focus {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:focus\:scale-110:focus {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:focus\:scale-125:focus {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:focus\:scale-150:focus {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:focus\:scale-x-0:focus {
+    --transform-scale-x: 0;
+  }
+
+  .sm\:focus\:scale-x-50:focus {
+    --transform-scale-x: .5;
+  }
+
+  .sm\:focus\:scale-x-75:focus {
+    --transform-scale-x: .75;
+  }
+
+  .sm\:focus\:scale-x-90:focus {
+    --transform-scale-x: .9;
+  }
+
+  .sm\:focus\:scale-x-95:focus {
+    --transform-scale-x: .95;
+  }
+
+  .sm\:focus\:scale-x-100:focus {
+    --transform-scale-x: 1;
+  }
+
+  .sm\:focus\:scale-x-105:focus {
+    --transform-scale-x: 1.05;
+  }
+
+  .sm\:focus\:scale-x-110:focus {
+    --transform-scale-x: 1.1;
+  }
+
+  .sm\:focus\:scale-x-125:focus {
+    --transform-scale-x: 1.25;
+  }
+
+  .sm\:focus\:scale-x-150:focus {
+    --transform-scale-x: 1.5;
+  }
+
+  .sm\:focus\:scale-y-0:focus {
+    --transform-scale-y: 0;
+  }
+
+  .sm\:focus\:scale-y-50:focus {
+    --transform-scale-y: .5;
+  }
+
+  .sm\:focus\:scale-y-75:focus {
+    --transform-scale-y: .75;
+  }
+
+  .sm\:focus\:scale-y-90:focus {
+    --transform-scale-y: .9;
+  }
+
+  .sm\:focus\:scale-y-95:focus {
+    --transform-scale-y: .95;
+  }
+
+  .sm\:focus\:scale-y-100:focus {
+    --transform-scale-y: 1;
+  }
+
+  .sm\:focus\:scale-y-105:focus {
+    --transform-scale-y: 1.05;
+  }
+
+  .sm\:focus\:scale-y-110:focus {
+    --transform-scale-y: 1.1;
+  }
+
+  .sm\:focus\:scale-y-125:focus {
+    --transform-scale-y: 1.25;
+  }
+
+  .sm\:focus\:scale-y-150:focus {
+    --transform-scale-y: 1.5;
+  }
+
+  .sm\:rotate-0 {
+    --transform-rotate: 0;
+  }
+
+  .sm\:rotate-45 {
+    --transform-rotate: 45deg;
+  }
+
+  .sm\:rotate-90 {
+    --transform-rotate: 90deg;
+  }
+
+  .sm\:rotate-180 {
+    --transform-rotate: 180deg;
+  }
+
+  .sm\:-rotate-180 {
+    --transform-rotate: -180deg;
+  }
+
+  .sm\:-rotate-90 {
+    --transform-rotate: -90deg;
+  }
+
+  .sm\:-rotate-45 {
+    --transform-rotate: -45deg;
+  }
+
+  .sm\:hover\:rotate-0:hover {
+    --transform-rotate: 0;
+  }
+
+  .sm\:hover\:rotate-45:hover {
+    --transform-rotate: 45deg;
+  }
+
+  .sm\:hover\:rotate-90:hover {
+    --transform-rotate: 90deg;
+  }
+
+  .sm\:hover\:rotate-180:hover {
+    --transform-rotate: 180deg;
+  }
+
+  .sm\:hover\:-rotate-180:hover {
+    --transform-rotate: -180deg;
+  }
+
+  .sm\:hover\:-rotate-90:hover {
+    --transform-rotate: -90deg;
+  }
+
+  .sm\:hover\:-rotate-45:hover {
+    --transform-rotate: -45deg;
+  }
+
+  .sm\:focus\:rotate-0:focus {
+    --transform-rotate: 0;
+  }
+
+  .sm\:focus\:rotate-45:focus {
+    --transform-rotate: 45deg;
+  }
+
+  .sm\:focus\:rotate-90:focus {
+    --transform-rotate: 90deg;
+  }
+
+  .sm\:focus\:rotate-180:focus {
+    --transform-rotate: 180deg;
+  }
+
+  .sm\:focus\:-rotate-180:focus {
+    --transform-rotate: -180deg;
+  }
+
+  .sm\:focus\:-rotate-90:focus {
+    --transform-rotate: -90deg;
+  }
+
+  .sm\:focus\:-rotate-45:focus {
+    --transform-rotate: -45deg;
+  }
+
+  .sm\:translate-x-0 {
+    --transform-translate-x: 0;
+  }
+
+  .sm\:translate-x-1 {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .sm\:translate-x-2 {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .sm\:translate-x-3 {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .sm\:translate-x-4 {
+    --transform-translate-x: 1rem;
+  }
+
+  .sm\:translate-x-5 {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .sm\:translate-x-6 {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .sm\:translate-x-8 {
+    --transform-translate-x: 2rem;
+  }
+
+  .sm\:translate-x-10 {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .sm\:translate-x-12 {
+    --transform-translate-x: 3rem;
+  }
+
+  .sm\:translate-x-16 {
+    --transform-translate-x: 4rem;
+  }
+
+  .sm\:translate-x-20 {
+    --transform-translate-x: 5rem;
+  }
+
+  .sm\:translate-x-24 {
+    --transform-translate-x: 6rem;
+  }
+
+  .sm\:translate-x-32 {
+    --transform-translate-x: 8rem;
+  }
+
+  .sm\:translate-x-40 {
+    --transform-translate-x: 10rem;
+  }
+
+  .sm\:translate-x-48 {
+    --transform-translate-x: 12rem;
+  }
+
+  .sm\:translate-x-56 {
+    --transform-translate-x: 14rem;
+  }
+
+  .sm\:translate-x-64 {
+    --transform-translate-x: 16rem;
+  }
+
+  .sm\:translate-x-px {
+    --transform-translate-x: 1px;
+  }
+
+  .sm\:-translate-x-1 {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .sm\:-translate-x-2 {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .sm\:-translate-x-3 {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .sm\:-translate-x-4 {
+    --transform-translate-x: -1rem;
+  }
+
+  .sm\:-translate-x-5 {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .sm\:-translate-x-6 {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .sm\:-translate-x-8 {
+    --transform-translate-x: -2rem;
+  }
+
+  .sm\:-translate-x-10 {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .sm\:-translate-x-12 {
+    --transform-translate-x: -3rem;
+  }
+
+  .sm\:-translate-x-16 {
+    --transform-translate-x: -4rem;
+  }
+
+  .sm\:-translate-x-20 {
+    --transform-translate-x: -5rem;
+  }
+
+  .sm\:-translate-x-24 {
+    --transform-translate-x: -6rem;
+  }
+
+  .sm\:-translate-x-32 {
+    --transform-translate-x: -8rem;
+  }
+
+  .sm\:-translate-x-40 {
+    --transform-translate-x: -10rem;
+  }
+
+  .sm\:-translate-x-48 {
+    --transform-translate-x: -12rem;
+  }
+
+  .sm\:-translate-x-56 {
+    --transform-translate-x: -14rem;
+  }
+
+  .sm\:-translate-x-64 {
+    --transform-translate-x: -16rem;
+  }
+
+  .sm\:-translate-x-px {
+    --transform-translate-x: -1px;
+  }
+
+  .sm\:-translate-x-full {
+    --transform-translate-x: -100%;
+  }
+
+  .sm\:-translate-x-1\/2 {
+    --transform-translate-x: -50%;
+  }
+
+  .sm\:translate-x-1\/2 {
+    --transform-translate-x: 50%;
+  }
+
+  .sm\:translate-x-full {
+    --transform-translate-x: 100%;
+  }
+
+  .sm\:translate-y-0 {
+    --transform-translate-y: 0;
+  }
+
+  .sm\:translate-y-1 {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .sm\:translate-y-2 {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .sm\:translate-y-3 {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .sm\:translate-y-4 {
+    --transform-translate-y: 1rem;
+  }
+
+  .sm\:translate-y-5 {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .sm\:translate-y-6 {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .sm\:translate-y-8 {
+    --transform-translate-y: 2rem;
+  }
+
+  .sm\:translate-y-10 {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .sm\:translate-y-12 {
+    --transform-translate-y: 3rem;
+  }
+
+  .sm\:translate-y-16 {
+    --transform-translate-y: 4rem;
+  }
+
+  .sm\:translate-y-20 {
+    --transform-translate-y: 5rem;
+  }
+
+  .sm\:translate-y-24 {
+    --transform-translate-y: 6rem;
+  }
+
+  .sm\:translate-y-32 {
+    --transform-translate-y: 8rem;
+  }
+
+  .sm\:translate-y-40 {
+    --transform-translate-y: 10rem;
+  }
+
+  .sm\:translate-y-48 {
+    --transform-translate-y: 12rem;
+  }
+
+  .sm\:translate-y-56 {
+    --transform-translate-y: 14rem;
+  }
+
+  .sm\:translate-y-64 {
+    --transform-translate-y: 16rem;
+  }
+
+  .sm\:translate-y-px {
+    --transform-translate-y: 1px;
+  }
+
+  .sm\:-translate-y-1 {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .sm\:-translate-y-2 {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .sm\:-translate-y-3 {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .sm\:-translate-y-4 {
+    --transform-translate-y: -1rem;
+  }
+
+  .sm\:-translate-y-5 {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .sm\:-translate-y-6 {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .sm\:-translate-y-8 {
+    --transform-translate-y: -2rem;
+  }
+
+  .sm\:-translate-y-10 {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .sm\:-translate-y-12 {
+    --transform-translate-y: -3rem;
+  }
+
+  .sm\:-translate-y-16 {
+    --transform-translate-y: -4rem;
+  }
+
+  .sm\:-translate-y-20 {
+    --transform-translate-y: -5rem;
+  }
+
+  .sm\:-translate-y-24 {
+    --transform-translate-y: -6rem;
+  }
+
+  .sm\:-translate-y-32 {
+    --transform-translate-y: -8rem;
+  }
+
+  .sm\:-translate-y-40 {
+    --transform-translate-y: -10rem;
+  }
+
+  .sm\:-translate-y-48 {
+    --transform-translate-y: -12rem;
+  }
+
+  .sm\:-translate-y-56 {
+    --transform-translate-y: -14rem;
+  }
+
+  .sm\:-translate-y-64 {
+    --transform-translate-y: -16rem;
+  }
+
+  .sm\:-translate-y-px {
+    --transform-translate-y: -1px;
+  }
+
+  .sm\:-translate-y-full {
+    --transform-translate-y: -100%;
+  }
+
+  .sm\:-translate-y-1\/2 {
+    --transform-translate-y: -50%;
+  }
+
+  .sm\:translate-y-1\/2 {
+    --transform-translate-y: 50%;
+  }
+
+  .sm\:translate-y-full {
+    --transform-translate-y: 100%;
+  }
+
+  .sm\:hover\:translate-x-0:hover {
+    --transform-translate-x: 0;
+  }
+
+  .sm\:hover\:translate-x-1:hover {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .sm\:hover\:translate-x-2:hover {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .sm\:hover\:translate-x-3:hover {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .sm\:hover\:translate-x-4:hover {
+    --transform-translate-x: 1rem;
+  }
+
+  .sm\:hover\:translate-x-5:hover {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .sm\:hover\:translate-x-6:hover {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .sm\:hover\:translate-x-8:hover {
+    --transform-translate-x: 2rem;
+  }
+
+  .sm\:hover\:translate-x-10:hover {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .sm\:hover\:translate-x-12:hover {
+    --transform-translate-x: 3rem;
+  }
+
+  .sm\:hover\:translate-x-16:hover {
+    --transform-translate-x: 4rem;
+  }
+
+  .sm\:hover\:translate-x-20:hover {
+    --transform-translate-x: 5rem;
+  }
+
+  .sm\:hover\:translate-x-24:hover {
+    --transform-translate-x: 6rem;
+  }
+
+  .sm\:hover\:translate-x-32:hover {
+    --transform-translate-x: 8rem;
+  }
+
+  .sm\:hover\:translate-x-40:hover {
+    --transform-translate-x: 10rem;
+  }
+
+  .sm\:hover\:translate-x-48:hover {
+    --transform-translate-x: 12rem;
+  }
+
+  .sm\:hover\:translate-x-56:hover {
+    --transform-translate-x: 14rem;
+  }
+
+  .sm\:hover\:translate-x-64:hover {
+    --transform-translate-x: 16rem;
+  }
+
+  .sm\:hover\:translate-x-px:hover {
+    --transform-translate-x: 1px;
+  }
+
+  .sm\:hover\:-translate-x-1:hover {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .sm\:hover\:-translate-x-2:hover {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .sm\:hover\:-translate-x-3:hover {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .sm\:hover\:-translate-x-4:hover {
+    --transform-translate-x: -1rem;
+  }
+
+  .sm\:hover\:-translate-x-5:hover {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .sm\:hover\:-translate-x-6:hover {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .sm\:hover\:-translate-x-8:hover {
+    --transform-translate-x: -2rem;
+  }
+
+  .sm\:hover\:-translate-x-10:hover {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .sm\:hover\:-translate-x-12:hover {
+    --transform-translate-x: -3rem;
+  }
+
+  .sm\:hover\:-translate-x-16:hover {
+    --transform-translate-x: -4rem;
+  }
+
+  .sm\:hover\:-translate-x-20:hover {
+    --transform-translate-x: -5rem;
+  }
+
+  .sm\:hover\:-translate-x-24:hover {
+    --transform-translate-x: -6rem;
+  }
+
+  .sm\:hover\:-translate-x-32:hover {
+    --transform-translate-x: -8rem;
+  }
+
+  .sm\:hover\:-translate-x-40:hover {
+    --transform-translate-x: -10rem;
+  }
+
+  .sm\:hover\:-translate-x-48:hover {
+    --transform-translate-x: -12rem;
+  }
+
+  .sm\:hover\:-translate-x-56:hover {
+    --transform-translate-x: -14rem;
+  }
+
+  .sm\:hover\:-translate-x-64:hover {
+    --transform-translate-x: -16rem;
+  }
+
+  .sm\:hover\:-translate-x-px:hover {
+    --transform-translate-x: -1px;
+  }
+
+  .sm\:hover\:-translate-x-full:hover {
+    --transform-translate-x: -100%;
+  }
+
+  .sm\:hover\:-translate-x-1\/2:hover {
+    --transform-translate-x: -50%;
+  }
+
+  .sm\:hover\:translate-x-1\/2:hover {
+    --transform-translate-x: 50%;
+  }
+
+  .sm\:hover\:translate-x-full:hover {
+    --transform-translate-x: 100%;
+  }
+
+  .sm\:hover\:translate-y-0:hover {
+    --transform-translate-y: 0;
+  }
+
+  .sm\:hover\:translate-y-1:hover {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .sm\:hover\:translate-y-2:hover {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .sm\:hover\:translate-y-3:hover {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .sm\:hover\:translate-y-4:hover {
+    --transform-translate-y: 1rem;
+  }
+
+  .sm\:hover\:translate-y-5:hover {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .sm\:hover\:translate-y-6:hover {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .sm\:hover\:translate-y-8:hover {
+    --transform-translate-y: 2rem;
+  }
+
+  .sm\:hover\:translate-y-10:hover {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .sm\:hover\:translate-y-12:hover {
+    --transform-translate-y: 3rem;
+  }
+
+  .sm\:hover\:translate-y-16:hover {
+    --transform-translate-y: 4rem;
+  }
+
+  .sm\:hover\:translate-y-20:hover {
+    --transform-translate-y: 5rem;
+  }
+
+  .sm\:hover\:translate-y-24:hover {
+    --transform-translate-y: 6rem;
+  }
+
+  .sm\:hover\:translate-y-32:hover {
+    --transform-translate-y: 8rem;
+  }
+
+  .sm\:hover\:translate-y-40:hover {
+    --transform-translate-y: 10rem;
+  }
+
+  .sm\:hover\:translate-y-48:hover {
+    --transform-translate-y: 12rem;
+  }
+
+  .sm\:hover\:translate-y-56:hover {
+    --transform-translate-y: 14rem;
+  }
+
+  .sm\:hover\:translate-y-64:hover {
+    --transform-translate-y: 16rem;
+  }
+
+  .sm\:hover\:translate-y-px:hover {
+    --transform-translate-y: 1px;
+  }
+
+  .sm\:hover\:-translate-y-1:hover {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .sm\:hover\:-translate-y-2:hover {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .sm\:hover\:-translate-y-3:hover {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .sm\:hover\:-translate-y-4:hover {
+    --transform-translate-y: -1rem;
+  }
+
+  .sm\:hover\:-translate-y-5:hover {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .sm\:hover\:-translate-y-6:hover {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .sm\:hover\:-translate-y-8:hover {
+    --transform-translate-y: -2rem;
+  }
+
+  .sm\:hover\:-translate-y-10:hover {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .sm\:hover\:-translate-y-12:hover {
+    --transform-translate-y: -3rem;
+  }
+
+  .sm\:hover\:-translate-y-16:hover {
+    --transform-translate-y: -4rem;
+  }
+
+  .sm\:hover\:-translate-y-20:hover {
+    --transform-translate-y: -5rem;
+  }
+
+  .sm\:hover\:-translate-y-24:hover {
+    --transform-translate-y: -6rem;
+  }
+
+  .sm\:hover\:-translate-y-32:hover {
+    --transform-translate-y: -8rem;
+  }
+
+  .sm\:hover\:-translate-y-40:hover {
+    --transform-translate-y: -10rem;
+  }
+
+  .sm\:hover\:-translate-y-48:hover {
+    --transform-translate-y: -12rem;
+  }
+
+  .sm\:hover\:-translate-y-56:hover {
+    --transform-translate-y: -14rem;
+  }
+
+  .sm\:hover\:-translate-y-64:hover {
+    --transform-translate-y: -16rem;
+  }
+
+  .sm\:hover\:-translate-y-px:hover {
+    --transform-translate-y: -1px;
+  }
+
+  .sm\:hover\:-translate-y-full:hover {
+    --transform-translate-y: -100%;
+  }
+
+  .sm\:hover\:-translate-y-1\/2:hover {
+    --transform-translate-y: -50%;
+  }
+
+  .sm\:hover\:translate-y-1\/2:hover {
+    --transform-translate-y: 50%;
+  }
+
+  .sm\:hover\:translate-y-full:hover {
+    --transform-translate-y: 100%;
+  }
+
+  .sm\:focus\:translate-x-0:focus {
+    --transform-translate-x: 0;
+  }
+
+  .sm\:focus\:translate-x-1:focus {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .sm\:focus\:translate-x-2:focus {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .sm\:focus\:translate-x-3:focus {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .sm\:focus\:translate-x-4:focus {
+    --transform-translate-x: 1rem;
+  }
+
+  .sm\:focus\:translate-x-5:focus {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .sm\:focus\:translate-x-6:focus {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .sm\:focus\:translate-x-8:focus {
+    --transform-translate-x: 2rem;
+  }
+
+  .sm\:focus\:translate-x-10:focus {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .sm\:focus\:translate-x-12:focus {
+    --transform-translate-x: 3rem;
+  }
+
+  .sm\:focus\:translate-x-16:focus {
+    --transform-translate-x: 4rem;
+  }
+
+  .sm\:focus\:translate-x-20:focus {
+    --transform-translate-x: 5rem;
+  }
+
+  .sm\:focus\:translate-x-24:focus {
+    --transform-translate-x: 6rem;
+  }
+
+  .sm\:focus\:translate-x-32:focus {
+    --transform-translate-x: 8rem;
+  }
+
+  .sm\:focus\:translate-x-40:focus {
+    --transform-translate-x: 10rem;
+  }
+
+  .sm\:focus\:translate-x-48:focus {
+    --transform-translate-x: 12rem;
+  }
+
+  .sm\:focus\:translate-x-56:focus {
+    --transform-translate-x: 14rem;
+  }
+
+  .sm\:focus\:translate-x-64:focus {
+    --transform-translate-x: 16rem;
+  }
+
+  .sm\:focus\:translate-x-px:focus {
+    --transform-translate-x: 1px;
+  }
+
+  .sm\:focus\:-translate-x-1:focus {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .sm\:focus\:-translate-x-2:focus {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .sm\:focus\:-translate-x-3:focus {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .sm\:focus\:-translate-x-4:focus {
+    --transform-translate-x: -1rem;
+  }
+
+  .sm\:focus\:-translate-x-5:focus {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .sm\:focus\:-translate-x-6:focus {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .sm\:focus\:-translate-x-8:focus {
+    --transform-translate-x: -2rem;
+  }
+
+  .sm\:focus\:-translate-x-10:focus {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .sm\:focus\:-translate-x-12:focus {
+    --transform-translate-x: -3rem;
+  }
+
+  .sm\:focus\:-translate-x-16:focus {
+    --transform-translate-x: -4rem;
+  }
+
+  .sm\:focus\:-translate-x-20:focus {
+    --transform-translate-x: -5rem;
+  }
+
+  .sm\:focus\:-translate-x-24:focus {
+    --transform-translate-x: -6rem;
+  }
+
+  .sm\:focus\:-translate-x-32:focus {
+    --transform-translate-x: -8rem;
+  }
+
+  .sm\:focus\:-translate-x-40:focus {
+    --transform-translate-x: -10rem;
+  }
+
+  .sm\:focus\:-translate-x-48:focus {
+    --transform-translate-x: -12rem;
+  }
+
+  .sm\:focus\:-translate-x-56:focus {
+    --transform-translate-x: -14rem;
+  }
+
+  .sm\:focus\:-translate-x-64:focus {
+    --transform-translate-x: -16rem;
+  }
+
+  .sm\:focus\:-translate-x-px:focus {
+    --transform-translate-x: -1px;
+  }
+
+  .sm\:focus\:-translate-x-full:focus {
+    --transform-translate-x: -100%;
+  }
+
+  .sm\:focus\:-translate-x-1\/2:focus {
+    --transform-translate-x: -50%;
+  }
+
+  .sm\:focus\:translate-x-1\/2:focus {
+    --transform-translate-x: 50%;
+  }
+
+  .sm\:focus\:translate-x-full:focus {
+    --transform-translate-x: 100%;
+  }
+
+  .sm\:focus\:translate-y-0:focus {
+    --transform-translate-y: 0;
+  }
+
+  .sm\:focus\:translate-y-1:focus {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .sm\:focus\:translate-y-2:focus {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .sm\:focus\:translate-y-3:focus {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .sm\:focus\:translate-y-4:focus {
+    --transform-translate-y: 1rem;
+  }
+
+  .sm\:focus\:translate-y-5:focus {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .sm\:focus\:translate-y-6:focus {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .sm\:focus\:translate-y-8:focus {
+    --transform-translate-y: 2rem;
+  }
+
+  .sm\:focus\:translate-y-10:focus {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .sm\:focus\:translate-y-12:focus {
+    --transform-translate-y: 3rem;
+  }
+
+  .sm\:focus\:translate-y-16:focus {
+    --transform-translate-y: 4rem;
+  }
+
+  .sm\:focus\:translate-y-20:focus {
+    --transform-translate-y: 5rem;
+  }
+
+  .sm\:focus\:translate-y-24:focus {
+    --transform-translate-y: 6rem;
+  }
+
+  .sm\:focus\:translate-y-32:focus {
+    --transform-translate-y: 8rem;
+  }
+
+  .sm\:focus\:translate-y-40:focus {
+    --transform-translate-y: 10rem;
+  }
+
+  .sm\:focus\:translate-y-48:focus {
+    --transform-translate-y: 12rem;
+  }
+
+  .sm\:focus\:translate-y-56:focus {
+    --transform-translate-y: 14rem;
+  }
+
+  .sm\:focus\:translate-y-64:focus {
+    --transform-translate-y: 16rem;
+  }
+
+  .sm\:focus\:translate-y-px:focus {
+    --transform-translate-y: 1px;
+  }
+
+  .sm\:focus\:-translate-y-1:focus {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .sm\:focus\:-translate-y-2:focus {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .sm\:focus\:-translate-y-3:focus {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .sm\:focus\:-translate-y-4:focus {
+    --transform-translate-y: -1rem;
+  }
+
+  .sm\:focus\:-translate-y-5:focus {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .sm\:focus\:-translate-y-6:focus {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .sm\:focus\:-translate-y-8:focus {
+    --transform-translate-y: -2rem;
+  }
+
+  .sm\:focus\:-translate-y-10:focus {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .sm\:focus\:-translate-y-12:focus {
+    --transform-translate-y: -3rem;
+  }
+
+  .sm\:focus\:-translate-y-16:focus {
+    --transform-translate-y: -4rem;
+  }
+
+  .sm\:focus\:-translate-y-20:focus {
+    --transform-translate-y: -5rem;
+  }
+
+  .sm\:focus\:-translate-y-24:focus {
+    --transform-translate-y: -6rem;
+  }
+
+  .sm\:focus\:-translate-y-32:focus {
+    --transform-translate-y: -8rem;
+  }
+
+  .sm\:focus\:-translate-y-40:focus {
+    --transform-translate-y: -10rem;
+  }
+
+  .sm\:focus\:-translate-y-48:focus {
+    --transform-translate-y: -12rem;
+  }
+
+  .sm\:focus\:-translate-y-56:focus {
+    --transform-translate-y: -14rem;
+  }
+
+  .sm\:focus\:-translate-y-64:focus {
+    --transform-translate-y: -16rem;
+  }
+
+  .sm\:focus\:-translate-y-px:focus {
+    --transform-translate-y: -1px;
+  }
+
+  .sm\:focus\:-translate-y-full:focus {
+    --transform-translate-y: -100%;
+  }
+
+  .sm\:focus\:-translate-y-1\/2:focus {
+    --transform-translate-y: -50%;
+  }
+
+  .sm\:focus\:translate-y-1\/2:focus {
+    --transform-translate-y: 50%;
+  }
+
+  .sm\:focus\:translate-y-full:focus {
+    --transform-translate-y: 100%;
+  }
+
+  .sm\:skew-x-0 {
+    --transform-skew-x: 0;
+  }
+
+  .sm\:skew-x-3 {
+    --transform-skew-x: 3deg;
+  }
+
+  .sm\:skew-x-6 {
+    --transform-skew-x: 6deg;
+  }
+
+  .sm\:skew-x-12 {
+    --transform-skew-x: 12deg;
+  }
+
+  .sm\:-skew-x-12 {
+    --transform-skew-x: -12deg;
+  }
+
+  .sm\:-skew-x-6 {
+    --transform-skew-x: -6deg;
+  }
+
+  .sm\:-skew-x-3 {
+    --transform-skew-x: -3deg;
+  }
+
+  .sm\:skew-y-0 {
+    --transform-skew-y: 0;
+  }
+
+  .sm\:skew-y-3 {
+    --transform-skew-y: 3deg;
+  }
+
+  .sm\:skew-y-6 {
+    --transform-skew-y: 6deg;
+  }
+
+  .sm\:skew-y-12 {
+    --transform-skew-y: 12deg;
+  }
+
+  .sm\:-skew-y-12 {
+    --transform-skew-y: -12deg;
+  }
+
+  .sm\:-skew-y-6 {
+    --transform-skew-y: -6deg;
+  }
+
+  .sm\:-skew-y-3 {
+    --transform-skew-y: -3deg;
+  }
+
+  .sm\:hover\:skew-x-0:hover {
+    --transform-skew-x: 0;
+  }
+
+  .sm\:hover\:skew-x-3:hover {
+    --transform-skew-x: 3deg;
+  }
+
+  .sm\:hover\:skew-x-6:hover {
+    --transform-skew-x: 6deg;
+  }
+
+  .sm\:hover\:skew-x-12:hover {
+    --transform-skew-x: 12deg;
+  }
+
+  .sm\:hover\:-skew-x-12:hover {
+    --transform-skew-x: -12deg;
+  }
+
+  .sm\:hover\:-skew-x-6:hover {
+    --transform-skew-x: -6deg;
+  }
+
+  .sm\:hover\:-skew-x-3:hover {
+    --transform-skew-x: -3deg;
+  }
+
+  .sm\:hover\:skew-y-0:hover {
+    --transform-skew-y: 0;
+  }
+
+  .sm\:hover\:skew-y-3:hover {
+    --transform-skew-y: 3deg;
+  }
+
+  .sm\:hover\:skew-y-6:hover {
+    --transform-skew-y: 6deg;
+  }
+
+  .sm\:hover\:skew-y-12:hover {
+    --transform-skew-y: 12deg;
+  }
+
+  .sm\:hover\:-skew-y-12:hover {
+    --transform-skew-y: -12deg;
+  }
+
+  .sm\:hover\:-skew-y-6:hover {
+    --transform-skew-y: -6deg;
+  }
+
+  .sm\:hover\:-skew-y-3:hover {
+    --transform-skew-y: -3deg;
+  }
+
+  .sm\:focus\:skew-x-0:focus {
+    --transform-skew-x: 0;
+  }
+
+  .sm\:focus\:skew-x-3:focus {
+    --transform-skew-x: 3deg;
+  }
+
+  .sm\:focus\:skew-x-6:focus {
+    --transform-skew-x: 6deg;
+  }
+
+  .sm\:focus\:skew-x-12:focus {
+    --transform-skew-x: 12deg;
+  }
+
+  .sm\:focus\:-skew-x-12:focus {
+    --transform-skew-x: -12deg;
+  }
+
+  .sm\:focus\:-skew-x-6:focus {
+    --transform-skew-x: -6deg;
+  }
+
+  .sm\:focus\:-skew-x-3:focus {
+    --transform-skew-x: -3deg;
+  }
+
+  .sm\:focus\:skew-y-0:focus {
+    --transform-skew-y: 0;
+  }
+
+  .sm\:focus\:skew-y-3:focus {
+    --transform-skew-y: 3deg;
+  }
+
+  .sm\:focus\:skew-y-6:focus {
+    --transform-skew-y: 6deg;
+  }
+
+  .sm\:focus\:skew-y-12:focus {
+    --transform-skew-y: 12deg;
+  }
+
+  .sm\:focus\:-skew-y-12:focus {
+    --transform-skew-y: -12deg;
+  }
+
+  .sm\:focus\:-skew-y-6:focus {
+    --transform-skew-y: -6deg;
+  }
+
+  .sm\:focus\:-skew-y-3:focus {
+    --transform-skew-y: -3deg;
+  }
+
+  .sm\:transition-none {
+    transition-property: none;
+  }
+
+  .sm\:transition-all {
+    transition-property: all;
+  }
+
+  .sm\:transition {
+    transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+  }
+
+  .sm\:transition-colors {
+    transition-property: background-color, border-color, color, fill, stroke;
+  }
+
+  .sm\:transition-opacity {
+    transition-property: opacity;
+  }
+
+  .sm\:transition-shadow {
+    transition-property: box-shadow;
+  }
+
+  .sm\:transition-transform {
+    transition-property: transform;
+  }
+
+  .sm\:ease-linear {
+    transition-timing-function: linear;
+  }
+
+  .sm\:ease-in {
+    transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+  }
+
+  .sm\:ease-out {
+    transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+  }
+
+  .sm\:ease-in-out {
+    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .sm\:duration-75 {
+    transition-duration: 75ms;
+  }
+
+  .sm\:duration-100 {
+    transition-duration: 100ms;
+  }
+
+  .sm\:duration-150 {
+    transition-duration: 150ms;
+  }
+
+  .sm\:duration-200 {
+    transition-duration: 200ms;
+  }
+
+  .sm\:duration-300 {
+    transition-duration: 300ms;
+  }
+
+  .sm\:duration-500 {
+    transition-duration: 500ms;
+  }
+
+  .sm\:duration-700 {
+    transition-duration: 700ms;
+  }
+
+  .sm\:duration-1000 {
+    transition-duration: 1000ms;
+  }
+
+  .sm\:delay-75 {
+    transition-delay: 75ms;
+  }
+
+  .sm\:delay-100 {
+    transition-delay: 100ms;
+  }
+
+  .sm\:delay-150 {
+    transition-delay: 150ms;
+  }
+
+  .sm\:delay-200 {
+    transition-delay: 200ms;
+  }
+
+  .sm\:delay-300 {
+    transition-delay: 300ms;
+  }
+
+  .sm\:delay-500 {
+    transition-delay: 500ms;
+  }
+
+  .sm\:delay-700 {
+    transition-delay: 700ms;
+  }
+
+  .sm\:delay-1000 {
+    transition-delay: 1000ms;
+  }
+
+  .sm\:animate-none {
+    -webkit-animation: none;
+            animation: none;
+  }
+
+  .sm\:animate-spin {
+    -webkit-animation: spin 1s linear infinite;
+            animation: spin 1s linear infinite;
+  }
+
+  .sm\:animate-ping {
+    -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+            animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+  }
+
+  .sm\:animate-pulse {
+    -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+            animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+  }
+
+  .sm\:animate-bounce {
+    -webkit-animation: bounce 1s infinite;
+            animation: bounce 1s infinite;
+  }
+}
+
+@media (min-width: 768px) {
+  .md\:container {
+    width: 100%;
+  }
+
+  @media (min-width: 640px) {
+    .md\:container {
+      max-width: 640px;
+    }
+  }
+
+  @media (min-width: 768px) {
+    .md\:container {
+      max-width: 768px;
+    }
+  }
+
+  @media (min-width: 1024px) {
+    .md\:container {
+      max-width: 1024px;
+    }
+  }
+
+  @media (min-width: 1280px) {
+    .md\:container {
+      max-width: 1280px;
+    }
+  }
+
+  .md\:space-y-0 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0px * var(--space-y-reverse));
+  }
+
+  .md\:space-x-0 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0px * var(--space-x-reverse));
+    margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.25rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.25rem * var(--space-x-reverse));
+    margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.5rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.5rem * var(--space-x-reverse));
+    margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.75rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.75rem * var(--space-x-reverse));
+    margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1rem * var(--space-x-reverse));
+    margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.25rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.25rem * var(--space-x-reverse));
+    margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.5rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.5rem * var(--space-x-reverse));
+    margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2rem * var(--space-x-reverse));
+    margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2.5rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2.5rem * var(--space-x-reverse));
+    margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(3rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(3rem * var(--space-x-reverse));
+    margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(4rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(4rem * var(--space-x-reverse));
+    margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(5rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(5rem * var(--space-x-reverse));
+    margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(6rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(6rem * var(--space-x-reverse));
+    margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(8rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(8rem * var(--space-x-reverse));
+    margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(10rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(10rem * var(--space-x-reverse));
+    margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(12rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(12rem * var(--space-x-reverse));
+    margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(14rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(14rem * var(--space-x-reverse));
+    margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(16rem * var(--space-y-reverse));
+  }
+
+  .md\:space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(16rem * var(--space-x-reverse));
+    margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1px * var(--space-y-reverse));
+  }
+
+  .md\:space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1px * var(--space-x-reverse));
+    margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.25rem * var(--space-x-reverse));
+    margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.5rem * var(--space-x-reverse));
+    margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.75rem * var(--space-x-reverse));
+    margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1rem * var(--space-x-reverse));
+    margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.25rem * var(--space-x-reverse));
+    margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.5rem * var(--space-x-reverse));
+    margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2rem * var(--space-x-reverse));
+    margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2.5rem * var(--space-x-reverse));
+    margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-3rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-3rem * var(--space-x-reverse));
+    margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-4rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-4rem * var(--space-x-reverse));
+    margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-5rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-5rem * var(--space-x-reverse));
+    margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-6rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-6rem * var(--space-x-reverse));
+    margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-8rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-8rem * var(--space-x-reverse));
+    margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-10rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-10rem * var(--space-x-reverse));
+    margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-12rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-12rem * var(--space-x-reverse));
+    margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-14rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-14rem * var(--space-x-reverse));
+    margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-16rem * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-16rem * var(--space-x-reverse));
+    margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:-space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1px * var(--space-y-reverse));
+  }
+
+  .md\:-space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1px * var(--space-x-reverse));
+    margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .md\:space-y-reverse > :not(template) ~ :not(template) {
+    --space-y-reverse: 1;
+  }
+
+  .md\:space-x-reverse > :not(template) ~ :not(template) {
+    --space-x-reverse: 1;
+  }
+
+  .md\:divide-y-0 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(0px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x-0 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(0px * var(--divide-x-reverse));
+    border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y-2 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(2px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x-2 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(2px * var(--divide-x-reverse));
+    border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y-4 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(4px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x-4 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(4px * var(--divide-x-reverse));
+    border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y-8 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(8px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x-8 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(8px * var(--divide-x-reverse));
+    border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(1px * var(--divide-y-reverse));
+  }
+
+  .md\:divide-x > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(1px * var(--divide-x-reverse));
+    border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .md\:divide-y-reverse > :not(template) ~ :not(template) {
+    --divide-y-reverse: 1;
+  }
+
+  .md\:divide-x-reverse > :not(template) ~ :not(template) {
+    --divide-x-reverse: 1;
+  }
+
+  .md\:divide-transparent > :not(template) ~ :not(template) {
+    border-color: transparent;
+  }
+
+  .md\:divide-current > :not(template) ~ :not(template) {
+    border-color: currentColor;
+  }
+
+  .md\:divide-black > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--divide-opacity));
+  }
+
+  .md\:divide-white > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--divide-opacity));
+  }
+
+  .md\:divide-gray-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--divide-opacity));
+  }
+
+  .md\:divide-red-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--divide-opacity));
+  }
+
+  .md\:divide-red-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--divide-opacity));
+  }
+
+  .md\:divide-red-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--divide-opacity));
+  }
+
+  .md\:divide-red-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--divide-opacity));
+  }
+
+  .md\:divide-red-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--divide-opacity));
+  }
+
+  .md\:divide-red-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--divide-opacity));
+  }
+
+  .md\:divide-red-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--divide-opacity));
+  }
+
+  .md\:divide-red-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--divide-opacity));
+  }
+
+  .md\:divide-red-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--divide-opacity));
+  }
+
+  .md\:divide-orange-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--divide-opacity));
+  }
+
+  .md\:divide-yellow-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--divide-opacity));
+  }
+
+  .md\:divide-green-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--divide-opacity));
+  }
+
+  .md\:divide-green-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--divide-opacity));
+  }
+
+  .md\:divide-green-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--divide-opacity));
+  }
+
+  .md\:divide-green-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--divide-opacity));
+  }
+
+  .md\:divide-green-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--divide-opacity));
+  }
+
+  .md\:divide-green-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--divide-opacity));
+  }
+
+  .md\:divide-green-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--divide-opacity));
+  }
+
+  .md\:divide-green-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--divide-opacity));
+  }
+
+  .md\:divide-green-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--divide-opacity));
+  }
+
+  .md\:divide-teal-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--divide-opacity));
+  }
+
+  .md\:divide-blue-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--divide-opacity));
+  }
+
+  .md\:divide-indigo-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--divide-opacity));
+  }
+
+  .md\:divide-purple-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--divide-opacity));
+  }
+
+  .md\:divide-pink-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--divide-opacity));
+  }
+
+  .md\:divide-solid > :not(template) ~ :not(template) {
+    border-style: solid;
+  }
+
+  .md\:divide-dashed > :not(template) ~ :not(template) {
+    border-style: dashed;
+  }
+
+  .md\:divide-dotted > :not(template) ~ :not(template) {
+    border-style: dotted;
+  }
+
+  .md\:divide-double > :not(template) ~ :not(template) {
+    border-style: double;
+  }
+
+  .md\:divide-none > :not(template) ~ :not(template) {
+    border-style: none;
+  }
+
+  .md\:divide-opacity-0 > :not(template) ~ :not(template) {
+    --divide-opacity: 0;
+  }
+
+  .md\:divide-opacity-25 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.25;
+  }
+
+  .md\:divide-opacity-50 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.5;
+  }
+
+  .md\:divide-opacity-75 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.75;
+  }
+
+  .md\:divide-opacity-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+  }
+
+  .md\:sr-only {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .md\:not-sr-only {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .md\:focus\:sr-only:focus {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .md\:focus\:not-sr-only:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .md\:appearance-none {
+    -webkit-appearance: none;
+       -moz-appearance: none;
+            appearance: none;
+  }
+
+  .md\:bg-fixed {
+    background-attachment: fixed;
+  }
+
+  .md\:bg-local {
+    background-attachment: local;
+  }
+
+  .md\:bg-scroll {
+    background-attachment: scroll;
+  }
+
+  .md\:bg-clip-border {
+    background-clip: border-box;
+  }
+
+  .md\:bg-clip-padding {
+    background-clip: padding-box;
+  }
+
+  .md\:bg-clip-content {
+    background-clip: content-box;
+  }
+
+  .md\:bg-clip-text {
+    -webkit-background-clip: text;
+            background-clip: text;
+  }
+
+  .md\:bg-transparent {
+    background-color: transparent;
+  }
+
+  .md\:bg-current {
+    background-color: currentColor;
+  }
+
+  .md\:bg-black {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .md\:bg-white {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-100 {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-200 {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-300 {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-400 {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-500 {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-600 {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-700 {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-800 {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .md\:bg-gray-900 {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .md\:bg-red-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .md\:bg-red-200 {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .md\:bg-red-300 {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .md\:bg-red-400 {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .md\:bg-red-500 {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .md\:bg-red-600 {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .md\:bg-red-700 {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .md\:bg-red-800 {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .md\:bg-red-900 {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-100 {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-200 {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-300 {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-400 {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-500 {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-600 {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-700 {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-800 {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .md\:bg-orange-900 {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-100 {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-200 {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-300 {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-400 {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-500 {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-600 {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-700 {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-800 {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .md\:bg-yellow-900 {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .md\:bg-green-100 {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .md\:bg-green-200 {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .md\:bg-green-300 {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .md\:bg-green-400 {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .md\:bg-green-500 {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .md\:bg-green-600 {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .md\:bg-green-700 {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .md\:bg-green-800 {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .md\:bg-green-900 {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-100 {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-200 {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-300 {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-400 {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-500 {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-600 {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-700 {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-800 {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .md\:bg-teal-900 {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-100 {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-200 {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-300 {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-400 {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-500 {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-600 {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-700 {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-800 {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .md\:bg-blue-900 {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-100 {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-200 {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-300 {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-400 {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-500 {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-600 {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-700 {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-800 {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .md\:bg-indigo-900 {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-100 {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-200 {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-300 {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-400 {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-500 {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-600 {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-700 {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-800 {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .md\:bg-purple-900 {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-200 {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-300 {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-400 {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-500 {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-600 {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-700 {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-800 {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .md\:bg-pink-900 {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-transparent:hover {
+    background-color: transparent;
+  }
+
+  .md\:hover\:bg-current:hover {
+    background-color: currentColor;
+  }
+
+  .md\:hover\:bg-black:hover {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-white:hover {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-100:hover {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-200:hover {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-300:hover {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-400:hover {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-500:hover {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-600:hover {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-700:hover {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-800:hover {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-gray-900:hover {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-300:hover {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-400:hover {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-500:hover {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-600:hover {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-700:hover {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-800:hover {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-red-900:hover {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-200:hover {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-600:hover {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-700:hover {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-800:hover {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-orange-900:hover {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-200:hover {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-300:hover {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-500:hover {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-600:hover {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-700:hover {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-800:hover {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-yellow-900:hover {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-100:hover {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-200:hover {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-300:hover {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-400:hover {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-500:hover {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-600:hover {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-700:hover {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-800:hover {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-green-900:hover {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-100:hover {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-200:hover {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-300:hover {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-400:hover {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-500:hover {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-600:hover {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-700:hover {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-800:hover {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-teal-900:hover {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-200:hover {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-300:hover {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-400:hover {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-500:hover {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-600:hover {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-700:hover {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-800:hover {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-blue-900:hover {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-200:hover {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-300:hover {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-400:hover {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-500:hover {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-600:hover {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-700:hover {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-800:hover {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-indigo-900:hover {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-100:hover {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-200:hover {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-300:hover {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-400:hover {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-500:hover {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-600:hover {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-700:hover {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-800:hover {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-purple-900:hover {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-400:hover {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-600:hover {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-700:hover {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-800:hover {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .md\:hover\:bg-pink-900:hover {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-transparent:focus {
+    background-color: transparent;
+  }
+
+  .md\:focus\:bg-current:focus {
+    background-color: currentColor;
+  }
+
+  .md\:focus\:bg-black:focus {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-white:focus {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-100:focus {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-200:focus {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-300:focus {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-400:focus {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-500:focus {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-600:focus {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-700:focus {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-800:focus {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-gray-900:focus {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-300:focus {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-400:focus {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-500:focus {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-600:focus {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-700:focus {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-800:focus {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-red-900:focus {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-200:focus {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-600:focus {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-700:focus {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-800:focus {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-orange-900:focus {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-200:focus {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-300:focus {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-500:focus {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-600:focus {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-700:focus {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-800:focus {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-yellow-900:focus {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-100:focus {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-200:focus {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-300:focus {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-400:focus {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-500:focus {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-600:focus {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-700:focus {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-800:focus {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-green-900:focus {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-100:focus {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-200:focus {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-300:focus {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-400:focus {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-500:focus {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-600:focus {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-700:focus {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-800:focus {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-teal-900:focus {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-200:focus {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-300:focus {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-400:focus {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-500:focus {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-600:focus {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-700:focus {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-800:focus {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-blue-900:focus {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-200:focus {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-300:focus {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-400:focus {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-500:focus {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-600:focus {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-700:focus {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-800:focus {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-indigo-900:focus {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-100:focus {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-200:focus {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-300:focus {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-400:focus {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-500:focus {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-600:focus {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-700:focus {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-800:focus {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-purple-900:focus {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-400:focus {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-600:focus {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-700:focus {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-800:focus {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .md\:focus\:bg-pink-900:focus {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .md\:bg-none {
+    background-image: none;
+  }
+
+  .md\:bg-gradient-to-t {
+    background-image: linear-gradient(to top, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-tr {
+    background-image: linear-gradient(to top right, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-r {
+    background-image: linear-gradient(to right, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-br {
+    background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-b {
+    background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-bl {
+    background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-l {
+    background-image: linear-gradient(to left, var(--gradient-color-stops));
+  }
+
+  .md\:bg-gradient-to-tl {
+    background-image: linear-gradient(to top left, var(--gradient-color-stops));
+  }
+
+  .md\:from-transparent {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:from-current {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:from-black {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:from-white {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:from-gray-100 {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:from-gray-200 {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:from-gray-300 {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:from-gray-400 {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:from-gray-500 {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:from-gray-600 {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:from-gray-700 {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:from-gray-800 {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:from-gray-900 {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:from-red-100 {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:from-red-200 {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:from-red-300 {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:from-red-400 {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:from-red-500 {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:from-red-600 {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:from-red-700 {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:from-red-800 {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:from-red-900 {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:from-orange-100 {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:from-orange-200 {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:from-orange-300 {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:from-orange-400 {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:from-orange-500 {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:from-orange-600 {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:from-orange-700 {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:from-orange-800 {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:from-orange-900 {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:from-yellow-100 {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:from-yellow-200 {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:from-yellow-300 {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:from-yellow-400 {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:from-yellow-500 {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:from-yellow-600 {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:from-yellow-700 {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:from-yellow-800 {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:from-yellow-900 {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:from-green-100 {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:from-green-200 {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:from-green-300 {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:from-green-400 {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:from-green-500 {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:from-green-600 {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:from-green-700 {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:from-green-800 {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:from-green-900 {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:from-teal-100 {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:from-teal-200 {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:from-teal-300 {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:from-teal-400 {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:from-teal-500 {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:from-teal-600 {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:from-teal-700 {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:from-teal-800 {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:from-teal-900 {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:from-blue-100 {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:from-blue-200 {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:from-blue-300 {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:from-blue-400 {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:from-blue-500 {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:from-blue-600 {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:from-blue-700 {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:from-blue-800 {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:from-blue-900 {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:from-indigo-100 {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:from-indigo-200 {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:from-indigo-300 {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:from-indigo-400 {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:from-indigo-500 {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:from-indigo-600 {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:from-indigo-700 {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:from-indigo-800 {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:from-indigo-900 {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:from-purple-100 {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:from-purple-200 {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:from-purple-300 {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:from-purple-400 {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:from-purple-500 {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:from-purple-600 {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:from-purple-700 {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:from-purple-800 {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:from-purple-900 {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:from-pink-100 {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:from-pink-200 {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:from-pink-300 {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:from-pink-400 {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:from-pink-500 {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:from-pink-600 {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:from-pink-700 {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:from-pink-800 {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:from-pink-900 {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:via-transparent {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:via-current {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:via-black {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:via-white {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:via-gray-100 {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:via-gray-200 {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:via-gray-300 {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:via-gray-400 {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:via-gray-500 {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:via-gray-600 {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:via-gray-700 {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:via-gray-800 {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:via-gray-900 {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:via-red-100 {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:via-red-200 {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:via-red-300 {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:via-red-400 {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:via-red-500 {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:via-red-600 {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:via-red-700 {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:via-red-800 {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:via-red-900 {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:via-orange-100 {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:via-orange-200 {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:via-orange-300 {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:via-orange-400 {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:via-orange-500 {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:via-orange-600 {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:via-orange-700 {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:via-orange-800 {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:via-orange-900 {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:via-yellow-100 {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:via-yellow-200 {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:via-yellow-300 {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:via-yellow-400 {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:via-yellow-500 {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:via-yellow-600 {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:via-yellow-700 {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:via-yellow-800 {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:via-yellow-900 {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:via-green-100 {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:via-green-200 {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:via-green-300 {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:via-green-400 {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:via-green-500 {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:via-green-600 {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:via-green-700 {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:via-green-800 {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:via-green-900 {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:via-teal-100 {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:via-teal-200 {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:via-teal-300 {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:via-teal-400 {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:via-teal-500 {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:via-teal-600 {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:via-teal-700 {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:via-teal-800 {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:via-teal-900 {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:via-blue-100 {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:via-blue-200 {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:via-blue-300 {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:via-blue-400 {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:via-blue-500 {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:via-blue-600 {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:via-blue-700 {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:via-blue-800 {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:via-blue-900 {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:via-indigo-100 {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:via-indigo-200 {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:via-indigo-300 {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:via-indigo-400 {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:via-indigo-500 {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:via-indigo-600 {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:via-indigo-700 {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:via-indigo-800 {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:via-indigo-900 {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:via-purple-100 {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:via-purple-200 {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:via-purple-300 {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:via-purple-400 {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:via-purple-500 {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:via-purple-600 {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:via-purple-700 {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:via-purple-800 {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:via-purple-900 {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:via-pink-100 {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:via-pink-200 {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:via-pink-300 {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:via-pink-400 {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:via-pink-500 {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:via-pink-600 {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:via-pink-700 {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:via-pink-800 {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:via-pink-900 {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:to-transparent {
+    --gradient-to-color: transparent;
+  }
+
+  .md\:to-current {
+    --gradient-to-color: currentColor;
+  }
+
+  .md\:to-black {
+    --gradient-to-color: #000;
+  }
+
+  .md\:to-white {
+    --gradient-to-color: #fff;
+  }
+
+  .md\:to-gray-100 {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .md\:to-gray-200 {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .md\:to-gray-300 {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .md\:to-gray-400 {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .md\:to-gray-500 {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .md\:to-gray-600 {
+    --gradient-to-color: #718096;
+  }
+
+  .md\:to-gray-700 {
+    --gradient-to-color: #4a5568;
+  }
+
+  .md\:to-gray-800 {
+    --gradient-to-color: #2d3748;
+  }
+
+  .md\:to-gray-900 {
+    --gradient-to-color: #1a202c;
+  }
+
+  .md\:to-red-100 {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .md\:to-red-200 {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .md\:to-red-300 {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .md\:to-red-400 {
+    --gradient-to-color: #fc8181;
+  }
+
+  .md\:to-red-500 {
+    --gradient-to-color: #f56565;
+  }
+
+  .md\:to-red-600 {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .md\:to-red-700 {
+    --gradient-to-color: #c53030;
+  }
+
+  .md\:to-red-800 {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .md\:to-red-900 {
+    --gradient-to-color: #742a2a;
+  }
+
+  .md\:to-orange-100 {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .md\:to-orange-200 {
+    --gradient-to-color: #feebc8;
+  }
+
+  .md\:to-orange-300 {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .md\:to-orange-400 {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .md\:to-orange-500 {
+    --gradient-to-color: #ed8936;
+  }
+
+  .md\:to-orange-600 {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .md\:to-orange-700 {
+    --gradient-to-color: #c05621;
+  }
+
+  .md\:to-orange-800 {
+    --gradient-to-color: #9c4221;
+  }
+
+  .md\:to-orange-900 {
+    --gradient-to-color: #7b341e;
+  }
+
+  .md\:to-yellow-100 {
+    --gradient-to-color: #fffff0;
+  }
+
+  .md\:to-yellow-200 {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .md\:to-yellow-300 {
+    --gradient-to-color: #faf089;
+  }
+
+  .md\:to-yellow-400 {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .md\:to-yellow-500 {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .md\:to-yellow-600 {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .md\:to-yellow-700 {
+    --gradient-to-color: #b7791f;
+  }
+
+  .md\:to-yellow-800 {
+    --gradient-to-color: #975a16;
+  }
+
+  .md\:to-yellow-900 {
+    --gradient-to-color: #744210;
+  }
+
+  .md\:to-green-100 {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .md\:to-green-200 {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .md\:to-green-300 {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .md\:to-green-400 {
+    --gradient-to-color: #68d391;
+  }
+
+  .md\:to-green-500 {
+    --gradient-to-color: #48bb78;
+  }
+
+  .md\:to-green-600 {
+    --gradient-to-color: #38a169;
+  }
+
+  .md\:to-green-700 {
+    --gradient-to-color: #2f855a;
+  }
+
+  .md\:to-green-800 {
+    --gradient-to-color: #276749;
+  }
+
+  .md\:to-green-900 {
+    --gradient-to-color: #22543d;
+  }
+
+  .md\:to-teal-100 {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .md\:to-teal-200 {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .md\:to-teal-300 {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .md\:to-teal-400 {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .md\:to-teal-500 {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .md\:to-teal-600 {
+    --gradient-to-color: #319795;
+  }
+
+  .md\:to-teal-700 {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .md\:to-teal-800 {
+    --gradient-to-color: #285e61;
+  }
+
+  .md\:to-teal-900 {
+    --gradient-to-color: #234e52;
+  }
+
+  .md\:to-blue-100 {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .md\:to-blue-200 {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .md\:to-blue-300 {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .md\:to-blue-400 {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .md\:to-blue-500 {
+    --gradient-to-color: #4299e1;
+  }
+
+  .md\:to-blue-600 {
+    --gradient-to-color: #3182ce;
+  }
+
+  .md\:to-blue-700 {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .md\:to-blue-800 {
+    --gradient-to-color: #2c5282;
+  }
+
+  .md\:to-blue-900 {
+    --gradient-to-color: #2a4365;
+  }
+
+  .md\:to-indigo-100 {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .md\:to-indigo-200 {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .md\:to-indigo-300 {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .md\:to-indigo-400 {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .md\:to-indigo-500 {
+    --gradient-to-color: #667eea;
+  }
+
+  .md\:to-indigo-600 {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .md\:to-indigo-700 {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .md\:to-indigo-800 {
+    --gradient-to-color: #434190;
+  }
+
+  .md\:to-indigo-900 {
+    --gradient-to-color: #3c366b;
+  }
+
+  .md\:to-purple-100 {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .md\:to-purple-200 {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .md\:to-purple-300 {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .md\:to-purple-400 {
+    --gradient-to-color: #b794f4;
+  }
+
+  .md\:to-purple-500 {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .md\:to-purple-600 {
+    --gradient-to-color: #805ad5;
+  }
+
+  .md\:to-purple-700 {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .md\:to-purple-800 {
+    --gradient-to-color: #553c9a;
+  }
+
+  .md\:to-purple-900 {
+    --gradient-to-color: #44337a;
+  }
+
+  .md\:to-pink-100 {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .md\:to-pink-200 {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .md\:to-pink-300 {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .md\:to-pink-400 {
+    --gradient-to-color: #f687b3;
+  }
+
+  .md\:to-pink-500 {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .md\:to-pink-600 {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .md\:to-pink-700 {
+    --gradient-to-color: #b83280;
+  }
+
+  .md\:to-pink-800 {
+    --gradient-to-color: #97266d;
+  }
+
+  .md\:to-pink-900 {
+    --gradient-to-color: #702459;
+  }
+
+  .md\:hover\:from-transparent:hover {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:hover\:from-current:hover {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:hover\:from-black:hover {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:hover\:from-white:hover {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:hover\:from-gray-100:hover {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:hover\:from-gray-200:hover {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:hover\:from-gray-300:hover {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:hover\:from-gray-400:hover {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:hover\:from-gray-500:hover {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:hover\:from-gray-600:hover {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:hover\:from-gray-700:hover {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:hover\:from-gray-800:hover {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:hover\:from-gray-900:hover {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:hover\:from-red-100:hover {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:hover\:from-red-200:hover {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:hover\:from-red-300:hover {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:hover\:from-red-400:hover {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:hover\:from-red-500:hover {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:hover\:from-red-600:hover {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:hover\:from-red-700:hover {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:hover\:from-red-800:hover {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:hover\:from-red-900:hover {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:hover\:from-orange-100:hover {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:hover\:from-orange-200:hover {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:hover\:from-orange-300:hover {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:hover\:from-orange-400:hover {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:hover\:from-orange-500:hover {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:hover\:from-orange-600:hover {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:hover\:from-orange-700:hover {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:hover\:from-orange-800:hover {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:hover\:from-orange-900:hover {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:hover\:from-yellow-100:hover {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:hover\:from-yellow-200:hover {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:hover\:from-yellow-300:hover {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:hover\:from-yellow-400:hover {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:hover\:from-yellow-500:hover {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:hover\:from-yellow-600:hover {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:hover\:from-yellow-700:hover {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:hover\:from-yellow-800:hover {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:hover\:from-yellow-900:hover {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:hover\:from-green-100:hover {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:hover\:from-green-200:hover {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:hover\:from-green-300:hover {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:hover\:from-green-400:hover {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:hover\:from-green-500:hover {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:hover\:from-green-600:hover {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:hover\:from-green-700:hover {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:hover\:from-green-800:hover {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:hover\:from-green-900:hover {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:hover\:from-teal-100:hover {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:hover\:from-teal-200:hover {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:hover\:from-teal-300:hover {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:hover\:from-teal-400:hover {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:hover\:from-teal-500:hover {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:hover\:from-teal-600:hover {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:hover\:from-teal-700:hover {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:hover\:from-teal-800:hover {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:hover\:from-teal-900:hover {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:hover\:from-blue-100:hover {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:hover\:from-blue-200:hover {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:hover\:from-blue-300:hover {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:hover\:from-blue-400:hover {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:hover\:from-blue-500:hover {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:hover\:from-blue-600:hover {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:hover\:from-blue-700:hover {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:hover\:from-blue-800:hover {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:hover\:from-blue-900:hover {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:hover\:from-indigo-100:hover {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:hover\:from-indigo-200:hover {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:hover\:from-indigo-300:hover {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:hover\:from-indigo-400:hover {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:hover\:from-indigo-500:hover {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:hover\:from-indigo-600:hover {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:hover\:from-indigo-700:hover {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:hover\:from-indigo-800:hover {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:hover\:from-indigo-900:hover {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:hover\:from-purple-100:hover {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:hover\:from-purple-200:hover {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:hover\:from-purple-300:hover {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:hover\:from-purple-400:hover {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:hover\:from-purple-500:hover {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:hover\:from-purple-600:hover {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:hover\:from-purple-700:hover {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:hover\:from-purple-800:hover {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:hover\:from-purple-900:hover {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:hover\:from-pink-100:hover {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:hover\:from-pink-200:hover {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:hover\:from-pink-300:hover {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:hover\:from-pink-400:hover {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:hover\:from-pink-500:hover {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:hover\:from-pink-600:hover {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:hover\:from-pink-700:hover {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:hover\:from-pink-800:hover {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:hover\:from-pink-900:hover {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:hover\:via-transparent:hover {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:hover\:via-current:hover {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:hover\:via-black:hover {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:hover\:via-white:hover {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:hover\:via-gray-100:hover {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:hover\:via-gray-200:hover {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:hover\:via-gray-300:hover {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:hover\:via-gray-400:hover {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:hover\:via-gray-500:hover {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:hover\:via-gray-600:hover {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:hover\:via-gray-700:hover {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:hover\:via-gray-800:hover {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:hover\:via-gray-900:hover {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:hover\:via-red-100:hover {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:hover\:via-red-200:hover {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:hover\:via-red-300:hover {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:hover\:via-red-400:hover {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:hover\:via-red-500:hover {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:hover\:via-red-600:hover {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:hover\:via-red-700:hover {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:hover\:via-red-800:hover {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:hover\:via-red-900:hover {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:hover\:via-orange-100:hover {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:hover\:via-orange-200:hover {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:hover\:via-orange-300:hover {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:hover\:via-orange-400:hover {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:hover\:via-orange-500:hover {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:hover\:via-orange-600:hover {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:hover\:via-orange-700:hover {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:hover\:via-orange-800:hover {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:hover\:via-orange-900:hover {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:hover\:via-yellow-100:hover {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:hover\:via-yellow-200:hover {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:hover\:via-yellow-300:hover {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:hover\:via-yellow-400:hover {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:hover\:via-yellow-500:hover {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:hover\:via-yellow-600:hover {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:hover\:via-yellow-700:hover {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:hover\:via-yellow-800:hover {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:hover\:via-yellow-900:hover {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:hover\:via-green-100:hover {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:hover\:via-green-200:hover {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:hover\:via-green-300:hover {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:hover\:via-green-400:hover {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:hover\:via-green-500:hover {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:hover\:via-green-600:hover {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:hover\:via-green-700:hover {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:hover\:via-green-800:hover {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:hover\:via-green-900:hover {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:hover\:via-teal-100:hover {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:hover\:via-teal-200:hover {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:hover\:via-teal-300:hover {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:hover\:via-teal-400:hover {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:hover\:via-teal-500:hover {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:hover\:via-teal-600:hover {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:hover\:via-teal-700:hover {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:hover\:via-teal-800:hover {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:hover\:via-teal-900:hover {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:hover\:via-blue-100:hover {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:hover\:via-blue-200:hover {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:hover\:via-blue-300:hover {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:hover\:via-blue-400:hover {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:hover\:via-blue-500:hover {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:hover\:via-blue-600:hover {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:hover\:via-blue-700:hover {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:hover\:via-blue-800:hover {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:hover\:via-blue-900:hover {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:hover\:via-indigo-100:hover {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:hover\:via-indigo-200:hover {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:hover\:via-indigo-300:hover {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:hover\:via-indigo-400:hover {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:hover\:via-indigo-500:hover {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:hover\:via-indigo-600:hover {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:hover\:via-indigo-700:hover {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:hover\:via-indigo-800:hover {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:hover\:via-indigo-900:hover {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:hover\:via-purple-100:hover {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:hover\:via-purple-200:hover {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:hover\:via-purple-300:hover {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:hover\:via-purple-400:hover {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:hover\:via-purple-500:hover {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:hover\:via-purple-600:hover {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:hover\:via-purple-700:hover {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:hover\:via-purple-800:hover {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:hover\:via-purple-900:hover {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:hover\:via-pink-100:hover {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:hover\:via-pink-200:hover {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:hover\:via-pink-300:hover {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:hover\:via-pink-400:hover {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:hover\:via-pink-500:hover {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:hover\:via-pink-600:hover {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:hover\:via-pink-700:hover {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:hover\:via-pink-800:hover {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:hover\:via-pink-900:hover {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:hover\:to-transparent:hover {
+    --gradient-to-color: transparent;
+  }
+
+  .md\:hover\:to-current:hover {
+    --gradient-to-color: currentColor;
+  }
+
+  .md\:hover\:to-black:hover {
+    --gradient-to-color: #000;
+  }
+
+  .md\:hover\:to-white:hover {
+    --gradient-to-color: #fff;
+  }
+
+  .md\:hover\:to-gray-100:hover {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .md\:hover\:to-gray-200:hover {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .md\:hover\:to-gray-300:hover {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .md\:hover\:to-gray-400:hover {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .md\:hover\:to-gray-500:hover {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .md\:hover\:to-gray-600:hover {
+    --gradient-to-color: #718096;
+  }
+
+  .md\:hover\:to-gray-700:hover {
+    --gradient-to-color: #4a5568;
+  }
+
+  .md\:hover\:to-gray-800:hover {
+    --gradient-to-color: #2d3748;
+  }
+
+  .md\:hover\:to-gray-900:hover {
+    --gradient-to-color: #1a202c;
+  }
+
+  .md\:hover\:to-red-100:hover {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .md\:hover\:to-red-200:hover {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .md\:hover\:to-red-300:hover {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .md\:hover\:to-red-400:hover {
+    --gradient-to-color: #fc8181;
+  }
+
+  .md\:hover\:to-red-500:hover {
+    --gradient-to-color: #f56565;
+  }
+
+  .md\:hover\:to-red-600:hover {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .md\:hover\:to-red-700:hover {
+    --gradient-to-color: #c53030;
+  }
+
+  .md\:hover\:to-red-800:hover {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .md\:hover\:to-red-900:hover {
+    --gradient-to-color: #742a2a;
+  }
+
+  .md\:hover\:to-orange-100:hover {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .md\:hover\:to-orange-200:hover {
+    --gradient-to-color: #feebc8;
+  }
+
+  .md\:hover\:to-orange-300:hover {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .md\:hover\:to-orange-400:hover {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .md\:hover\:to-orange-500:hover {
+    --gradient-to-color: #ed8936;
+  }
+
+  .md\:hover\:to-orange-600:hover {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .md\:hover\:to-orange-700:hover {
+    --gradient-to-color: #c05621;
+  }
+
+  .md\:hover\:to-orange-800:hover {
+    --gradient-to-color: #9c4221;
+  }
+
+  .md\:hover\:to-orange-900:hover {
+    --gradient-to-color: #7b341e;
+  }
+
+  .md\:hover\:to-yellow-100:hover {
+    --gradient-to-color: #fffff0;
+  }
+
+  .md\:hover\:to-yellow-200:hover {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .md\:hover\:to-yellow-300:hover {
+    --gradient-to-color: #faf089;
+  }
+
+  .md\:hover\:to-yellow-400:hover {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .md\:hover\:to-yellow-500:hover {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .md\:hover\:to-yellow-600:hover {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .md\:hover\:to-yellow-700:hover {
+    --gradient-to-color: #b7791f;
+  }
+
+  .md\:hover\:to-yellow-800:hover {
+    --gradient-to-color: #975a16;
+  }
+
+  .md\:hover\:to-yellow-900:hover {
+    --gradient-to-color: #744210;
+  }
+
+  .md\:hover\:to-green-100:hover {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .md\:hover\:to-green-200:hover {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .md\:hover\:to-green-300:hover {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .md\:hover\:to-green-400:hover {
+    --gradient-to-color: #68d391;
+  }
+
+  .md\:hover\:to-green-500:hover {
+    --gradient-to-color: #48bb78;
+  }
+
+  .md\:hover\:to-green-600:hover {
+    --gradient-to-color: #38a169;
+  }
+
+  .md\:hover\:to-green-700:hover {
+    --gradient-to-color: #2f855a;
+  }
+
+  .md\:hover\:to-green-800:hover {
+    --gradient-to-color: #276749;
+  }
+
+  .md\:hover\:to-green-900:hover {
+    --gradient-to-color: #22543d;
+  }
+
+  .md\:hover\:to-teal-100:hover {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .md\:hover\:to-teal-200:hover {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .md\:hover\:to-teal-300:hover {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .md\:hover\:to-teal-400:hover {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .md\:hover\:to-teal-500:hover {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .md\:hover\:to-teal-600:hover {
+    --gradient-to-color: #319795;
+  }
+
+  .md\:hover\:to-teal-700:hover {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .md\:hover\:to-teal-800:hover {
+    --gradient-to-color: #285e61;
+  }
+
+  .md\:hover\:to-teal-900:hover {
+    --gradient-to-color: #234e52;
+  }
+
+  .md\:hover\:to-blue-100:hover {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .md\:hover\:to-blue-200:hover {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .md\:hover\:to-blue-300:hover {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .md\:hover\:to-blue-400:hover {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .md\:hover\:to-blue-500:hover {
+    --gradient-to-color: #4299e1;
+  }
+
+  .md\:hover\:to-blue-600:hover {
+    --gradient-to-color: #3182ce;
+  }
+
+  .md\:hover\:to-blue-700:hover {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .md\:hover\:to-blue-800:hover {
+    --gradient-to-color: #2c5282;
+  }
+
+  .md\:hover\:to-blue-900:hover {
+    --gradient-to-color: #2a4365;
+  }
+
+  .md\:hover\:to-indigo-100:hover {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .md\:hover\:to-indigo-200:hover {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .md\:hover\:to-indigo-300:hover {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .md\:hover\:to-indigo-400:hover {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .md\:hover\:to-indigo-500:hover {
+    --gradient-to-color: #667eea;
+  }
+
+  .md\:hover\:to-indigo-600:hover {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .md\:hover\:to-indigo-700:hover {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .md\:hover\:to-indigo-800:hover {
+    --gradient-to-color: #434190;
+  }
+
+  .md\:hover\:to-indigo-900:hover {
+    --gradient-to-color: #3c366b;
+  }
+
+  .md\:hover\:to-purple-100:hover {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .md\:hover\:to-purple-200:hover {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .md\:hover\:to-purple-300:hover {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .md\:hover\:to-purple-400:hover {
+    --gradient-to-color: #b794f4;
+  }
+
+  .md\:hover\:to-purple-500:hover {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .md\:hover\:to-purple-600:hover {
+    --gradient-to-color: #805ad5;
+  }
+
+  .md\:hover\:to-purple-700:hover {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .md\:hover\:to-purple-800:hover {
+    --gradient-to-color: #553c9a;
+  }
+
+  .md\:hover\:to-purple-900:hover {
+    --gradient-to-color: #44337a;
+  }
+
+  .md\:hover\:to-pink-100:hover {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .md\:hover\:to-pink-200:hover {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .md\:hover\:to-pink-300:hover {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .md\:hover\:to-pink-400:hover {
+    --gradient-to-color: #f687b3;
+  }
+
+  .md\:hover\:to-pink-500:hover {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .md\:hover\:to-pink-600:hover {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .md\:hover\:to-pink-700:hover {
+    --gradient-to-color: #b83280;
+  }
+
+  .md\:hover\:to-pink-800:hover {
+    --gradient-to-color: #97266d;
+  }
+
+  .md\:hover\:to-pink-900:hover {
+    --gradient-to-color: #702459;
+  }
+
+  .md\:focus\:from-transparent:focus {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:focus\:from-current:focus {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:focus\:from-black:focus {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:focus\:from-white:focus {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:focus\:from-gray-100:focus {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:focus\:from-gray-200:focus {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:focus\:from-gray-300:focus {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:focus\:from-gray-400:focus {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:focus\:from-gray-500:focus {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:focus\:from-gray-600:focus {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:focus\:from-gray-700:focus {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:focus\:from-gray-800:focus {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:focus\:from-gray-900:focus {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:focus\:from-red-100:focus {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:focus\:from-red-200:focus {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:focus\:from-red-300:focus {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:focus\:from-red-400:focus {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:focus\:from-red-500:focus {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:focus\:from-red-600:focus {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:focus\:from-red-700:focus {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:focus\:from-red-800:focus {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:focus\:from-red-900:focus {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:focus\:from-orange-100:focus {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:focus\:from-orange-200:focus {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:focus\:from-orange-300:focus {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:focus\:from-orange-400:focus {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:focus\:from-orange-500:focus {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:focus\:from-orange-600:focus {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:focus\:from-orange-700:focus {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:focus\:from-orange-800:focus {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:focus\:from-orange-900:focus {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:focus\:from-yellow-100:focus {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:focus\:from-yellow-200:focus {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:focus\:from-yellow-300:focus {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:focus\:from-yellow-400:focus {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:focus\:from-yellow-500:focus {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:focus\:from-yellow-600:focus {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:focus\:from-yellow-700:focus {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:focus\:from-yellow-800:focus {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:focus\:from-yellow-900:focus {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:focus\:from-green-100:focus {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:focus\:from-green-200:focus {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:focus\:from-green-300:focus {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:focus\:from-green-400:focus {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:focus\:from-green-500:focus {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:focus\:from-green-600:focus {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:focus\:from-green-700:focus {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:focus\:from-green-800:focus {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:focus\:from-green-900:focus {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:focus\:from-teal-100:focus {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:focus\:from-teal-200:focus {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:focus\:from-teal-300:focus {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:focus\:from-teal-400:focus {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:focus\:from-teal-500:focus {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:focus\:from-teal-600:focus {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:focus\:from-teal-700:focus {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:focus\:from-teal-800:focus {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:focus\:from-teal-900:focus {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:focus\:from-blue-100:focus {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:focus\:from-blue-200:focus {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:focus\:from-blue-300:focus {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:focus\:from-blue-400:focus {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:focus\:from-blue-500:focus {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:focus\:from-blue-600:focus {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:focus\:from-blue-700:focus {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:focus\:from-blue-800:focus {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:focus\:from-blue-900:focus {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:focus\:from-indigo-100:focus {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:focus\:from-indigo-200:focus {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:focus\:from-indigo-300:focus {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:focus\:from-indigo-400:focus {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:focus\:from-indigo-500:focus {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:focus\:from-indigo-600:focus {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:focus\:from-indigo-700:focus {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:focus\:from-indigo-800:focus {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:focus\:from-indigo-900:focus {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:focus\:from-purple-100:focus {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:focus\:from-purple-200:focus {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:focus\:from-purple-300:focus {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:focus\:from-purple-400:focus {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:focus\:from-purple-500:focus {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:focus\:from-purple-600:focus {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:focus\:from-purple-700:focus {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:focus\:from-purple-800:focus {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:focus\:from-purple-900:focus {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:focus\:from-pink-100:focus {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:focus\:from-pink-200:focus {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:focus\:from-pink-300:focus {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:focus\:from-pink-400:focus {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:focus\:from-pink-500:focus {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:focus\:from-pink-600:focus {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:focus\:from-pink-700:focus {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:focus\:from-pink-800:focus {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:focus\:from-pink-900:focus {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:focus\:via-transparent:focus {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:focus\:via-current:focus {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:focus\:via-black:focus {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .md\:focus\:via-white:focus {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .md\:focus\:via-gray-100:focus {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .md\:focus\:via-gray-200:focus {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .md\:focus\:via-gray-300:focus {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .md\:focus\:via-gray-400:focus {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .md\:focus\:via-gray-500:focus {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .md\:focus\:via-gray-600:focus {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .md\:focus\:via-gray-700:focus {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .md\:focus\:via-gray-800:focus {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .md\:focus\:via-gray-900:focus {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .md\:focus\:via-red-100:focus {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .md\:focus\:via-red-200:focus {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .md\:focus\:via-red-300:focus {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .md\:focus\:via-red-400:focus {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .md\:focus\:via-red-500:focus {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .md\:focus\:via-red-600:focus {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .md\:focus\:via-red-700:focus {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .md\:focus\:via-red-800:focus {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .md\:focus\:via-red-900:focus {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .md\:focus\:via-orange-100:focus {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .md\:focus\:via-orange-200:focus {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .md\:focus\:via-orange-300:focus {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .md\:focus\:via-orange-400:focus {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .md\:focus\:via-orange-500:focus {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .md\:focus\:via-orange-600:focus {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .md\:focus\:via-orange-700:focus {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .md\:focus\:via-orange-800:focus {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .md\:focus\:via-orange-900:focus {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .md\:focus\:via-yellow-100:focus {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .md\:focus\:via-yellow-200:focus {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .md\:focus\:via-yellow-300:focus {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .md\:focus\:via-yellow-400:focus {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .md\:focus\:via-yellow-500:focus {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .md\:focus\:via-yellow-600:focus {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .md\:focus\:via-yellow-700:focus {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .md\:focus\:via-yellow-800:focus {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .md\:focus\:via-yellow-900:focus {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .md\:focus\:via-green-100:focus {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .md\:focus\:via-green-200:focus {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .md\:focus\:via-green-300:focus {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .md\:focus\:via-green-400:focus {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .md\:focus\:via-green-500:focus {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .md\:focus\:via-green-600:focus {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .md\:focus\:via-green-700:focus {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .md\:focus\:via-green-800:focus {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .md\:focus\:via-green-900:focus {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .md\:focus\:via-teal-100:focus {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .md\:focus\:via-teal-200:focus {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .md\:focus\:via-teal-300:focus {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .md\:focus\:via-teal-400:focus {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .md\:focus\:via-teal-500:focus {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .md\:focus\:via-teal-600:focus {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .md\:focus\:via-teal-700:focus {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .md\:focus\:via-teal-800:focus {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .md\:focus\:via-teal-900:focus {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .md\:focus\:via-blue-100:focus {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .md\:focus\:via-blue-200:focus {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .md\:focus\:via-blue-300:focus {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .md\:focus\:via-blue-400:focus {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .md\:focus\:via-blue-500:focus {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .md\:focus\:via-blue-600:focus {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .md\:focus\:via-blue-700:focus {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .md\:focus\:via-blue-800:focus {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .md\:focus\:via-blue-900:focus {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .md\:focus\:via-indigo-100:focus {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .md\:focus\:via-indigo-200:focus {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .md\:focus\:via-indigo-300:focus {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .md\:focus\:via-indigo-400:focus {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .md\:focus\:via-indigo-500:focus {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .md\:focus\:via-indigo-600:focus {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .md\:focus\:via-indigo-700:focus {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .md\:focus\:via-indigo-800:focus {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .md\:focus\:via-indigo-900:focus {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .md\:focus\:via-purple-100:focus {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .md\:focus\:via-purple-200:focus {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .md\:focus\:via-purple-300:focus {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .md\:focus\:via-purple-400:focus {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .md\:focus\:via-purple-500:focus {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .md\:focus\:via-purple-600:focus {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .md\:focus\:via-purple-700:focus {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .md\:focus\:via-purple-800:focus {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .md\:focus\:via-purple-900:focus {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .md\:focus\:via-pink-100:focus {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .md\:focus\:via-pink-200:focus {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .md\:focus\:via-pink-300:focus {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .md\:focus\:via-pink-400:focus {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .md\:focus\:via-pink-500:focus {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .md\:focus\:via-pink-600:focus {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .md\:focus\:via-pink-700:focus {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .md\:focus\:via-pink-800:focus {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .md\:focus\:via-pink-900:focus {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .md\:focus\:to-transparent:focus {
+    --gradient-to-color: transparent;
+  }
+
+  .md\:focus\:to-current:focus {
+    --gradient-to-color: currentColor;
+  }
+
+  .md\:focus\:to-black:focus {
+    --gradient-to-color: #000;
+  }
+
+  .md\:focus\:to-white:focus {
+    --gradient-to-color: #fff;
+  }
+
+  .md\:focus\:to-gray-100:focus {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .md\:focus\:to-gray-200:focus {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .md\:focus\:to-gray-300:focus {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .md\:focus\:to-gray-400:focus {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .md\:focus\:to-gray-500:focus {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .md\:focus\:to-gray-600:focus {
+    --gradient-to-color: #718096;
+  }
+
+  .md\:focus\:to-gray-700:focus {
+    --gradient-to-color: #4a5568;
+  }
+
+  .md\:focus\:to-gray-800:focus {
+    --gradient-to-color: #2d3748;
+  }
+
+  .md\:focus\:to-gray-900:focus {
+    --gradient-to-color: #1a202c;
+  }
+
+  .md\:focus\:to-red-100:focus {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .md\:focus\:to-red-200:focus {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .md\:focus\:to-red-300:focus {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .md\:focus\:to-red-400:focus {
+    --gradient-to-color: #fc8181;
+  }
+
+  .md\:focus\:to-red-500:focus {
+    --gradient-to-color: #f56565;
+  }
+
+  .md\:focus\:to-red-600:focus {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .md\:focus\:to-red-700:focus {
+    --gradient-to-color: #c53030;
+  }
+
+  .md\:focus\:to-red-800:focus {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .md\:focus\:to-red-900:focus {
+    --gradient-to-color: #742a2a;
+  }
+
+  .md\:focus\:to-orange-100:focus {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .md\:focus\:to-orange-200:focus {
+    --gradient-to-color: #feebc8;
+  }
+
+  .md\:focus\:to-orange-300:focus {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .md\:focus\:to-orange-400:focus {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .md\:focus\:to-orange-500:focus {
+    --gradient-to-color: #ed8936;
+  }
+
+  .md\:focus\:to-orange-600:focus {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .md\:focus\:to-orange-700:focus {
+    --gradient-to-color: #c05621;
+  }
+
+  .md\:focus\:to-orange-800:focus {
+    --gradient-to-color: #9c4221;
+  }
+
+  .md\:focus\:to-orange-900:focus {
+    --gradient-to-color: #7b341e;
+  }
+
+  .md\:focus\:to-yellow-100:focus {
+    --gradient-to-color: #fffff0;
+  }
+
+  .md\:focus\:to-yellow-200:focus {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .md\:focus\:to-yellow-300:focus {
+    --gradient-to-color: #faf089;
+  }
+
+  .md\:focus\:to-yellow-400:focus {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .md\:focus\:to-yellow-500:focus {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .md\:focus\:to-yellow-600:focus {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .md\:focus\:to-yellow-700:focus {
+    --gradient-to-color: #b7791f;
+  }
+
+  .md\:focus\:to-yellow-800:focus {
+    --gradient-to-color: #975a16;
+  }
+
+  .md\:focus\:to-yellow-900:focus {
+    --gradient-to-color: #744210;
+  }
+
+  .md\:focus\:to-green-100:focus {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .md\:focus\:to-green-200:focus {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .md\:focus\:to-green-300:focus {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .md\:focus\:to-green-400:focus {
+    --gradient-to-color: #68d391;
+  }
+
+  .md\:focus\:to-green-500:focus {
+    --gradient-to-color: #48bb78;
+  }
+
+  .md\:focus\:to-green-600:focus {
+    --gradient-to-color: #38a169;
+  }
+
+  .md\:focus\:to-green-700:focus {
+    --gradient-to-color: #2f855a;
+  }
+
+  .md\:focus\:to-green-800:focus {
+    --gradient-to-color: #276749;
+  }
+
+  .md\:focus\:to-green-900:focus {
+    --gradient-to-color: #22543d;
+  }
+
+  .md\:focus\:to-teal-100:focus {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .md\:focus\:to-teal-200:focus {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .md\:focus\:to-teal-300:focus {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .md\:focus\:to-teal-400:focus {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .md\:focus\:to-teal-500:focus {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .md\:focus\:to-teal-600:focus {
+    --gradient-to-color: #319795;
+  }
+
+  .md\:focus\:to-teal-700:focus {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .md\:focus\:to-teal-800:focus {
+    --gradient-to-color: #285e61;
+  }
+
+  .md\:focus\:to-teal-900:focus {
+    --gradient-to-color: #234e52;
+  }
+
+  .md\:focus\:to-blue-100:focus {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .md\:focus\:to-blue-200:focus {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .md\:focus\:to-blue-300:focus {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .md\:focus\:to-blue-400:focus {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .md\:focus\:to-blue-500:focus {
+    --gradient-to-color: #4299e1;
+  }
+
+  .md\:focus\:to-blue-600:focus {
+    --gradient-to-color: #3182ce;
+  }
+
+  .md\:focus\:to-blue-700:focus {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .md\:focus\:to-blue-800:focus {
+    --gradient-to-color: #2c5282;
+  }
+
+  .md\:focus\:to-blue-900:focus {
+    --gradient-to-color: #2a4365;
+  }
+
+  .md\:focus\:to-indigo-100:focus {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .md\:focus\:to-indigo-200:focus {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .md\:focus\:to-indigo-300:focus {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .md\:focus\:to-indigo-400:focus {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .md\:focus\:to-indigo-500:focus {
+    --gradient-to-color: #667eea;
+  }
+
+  .md\:focus\:to-indigo-600:focus {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .md\:focus\:to-indigo-700:focus {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .md\:focus\:to-indigo-800:focus {
+    --gradient-to-color: #434190;
+  }
+
+  .md\:focus\:to-indigo-900:focus {
+    --gradient-to-color: #3c366b;
+  }
+
+  .md\:focus\:to-purple-100:focus {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .md\:focus\:to-purple-200:focus {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .md\:focus\:to-purple-300:focus {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .md\:focus\:to-purple-400:focus {
+    --gradient-to-color: #b794f4;
+  }
+
+  .md\:focus\:to-purple-500:focus {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .md\:focus\:to-purple-600:focus {
+    --gradient-to-color: #805ad5;
+  }
+
+  .md\:focus\:to-purple-700:focus {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .md\:focus\:to-purple-800:focus {
+    --gradient-to-color: #553c9a;
+  }
+
+  .md\:focus\:to-purple-900:focus {
+    --gradient-to-color: #44337a;
+  }
+
+  .md\:focus\:to-pink-100:focus {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .md\:focus\:to-pink-200:focus {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .md\:focus\:to-pink-300:focus {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .md\:focus\:to-pink-400:focus {
+    --gradient-to-color: #f687b3;
+  }
+
+  .md\:focus\:to-pink-500:focus {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .md\:focus\:to-pink-600:focus {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .md\:focus\:to-pink-700:focus {
+    --gradient-to-color: #b83280;
+  }
+
+  .md\:focus\:to-pink-800:focus {
+    --gradient-to-color: #97266d;
+  }
+
+  .md\:focus\:to-pink-900:focus {
+    --gradient-to-color: #702459;
+  }
+
+  .md\:bg-opacity-0 {
+    --bg-opacity: 0;
+  }
+
+  .md\:bg-opacity-25 {
+    --bg-opacity: 0.25;
+  }
+
+  .md\:bg-opacity-50 {
+    --bg-opacity: 0.5;
+  }
+
+  .md\:bg-opacity-75 {
+    --bg-opacity: 0.75;
+  }
+
+  .md\:bg-opacity-100 {
+    --bg-opacity: 1;
+  }
+
+  .md\:hover\:bg-opacity-0:hover {
+    --bg-opacity: 0;
+  }
+
+  .md\:hover\:bg-opacity-25:hover {
+    --bg-opacity: 0.25;
+  }
+
+  .md\:hover\:bg-opacity-50:hover {
+    --bg-opacity: 0.5;
+  }
+
+  .md\:hover\:bg-opacity-75:hover {
+    --bg-opacity: 0.75;
+  }
+
+  .md\:hover\:bg-opacity-100:hover {
+    --bg-opacity: 1;
+  }
+
+  .md\:focus\:bg-opacity-0:focus {
+    --bg-opacity: 0;
+  }
+
+  .md\:focus\:bg-opacity-25:focus {
+    --bg-opacity: 0.25;
+  }
+
+  .md\:focus\:bg-opacity-50:focus {
+    --bg-opacity: 0.5;
+  }
+
+  .md\:focus\:bg-opacity-75:focus {
+    --bg-opacity: 0.75;
+  }
+
+  .md\:focus\:bg-opacity-100:focus {
+    --bg-opacity: 1;
+  }
+
+  .md\:bg-bottom {
+    background-position: bottom;
+  }
+
+  .md\:bg-center {
+    background-position: center;
+  }
+
+  .md\:bg-left {
+    background-position: left;
+  }
+
+  .md\:bg-left-bottom {
+    background-position: left bottom;
+  }
+
+  .md\:bg-left-top {
+    background-position: left top;
+  }
+
+  .md\:bg-right {
+    background-position: right;
+  }
+
+  .md\:bg-right-bottom {
+    background-position: right bottom;
+  }
+
+  .md\:bg-right-top {
+    background-position: right top;
+  }
+
+  .md\:bg-top {
+    background-position: top;
+  }
+
+  .md\:bg-repeat {
+    background-repeat: repeat;
+  }
+
+  .md\:bg-no-repeat {
+    background-repeat: no-repeat;
+  }
+
+  .md\:bg-repeat-x {
+    background-repeat: repeat-x;
+  }
+
+  .md\:bg-repeat-y {
+    background-repeat: repeat-y;
+  }
+
+  .md\:bg-repeat-round {
+    background-repeat: round;
+  }
+
+  .md\:bg-repeat-space {
+    background-repeat: space;
+  }
+
+  .md\:bg-auto {
+    background-size: auto;
+  }
+
+  .md\:bg-cover {
+    background-size: cover;
+  }
+
+  .md\:bg-contain {
+    background-size: contain;
+  }
+
+  .md\:border-collapse {
+    border-collapse: collapse;
+  }
+
+  .md\:border-separate {
+    border-collapse: separate;
+  }
+
+  .md\:border-transparent {
+    border-color: transparent;
+  }
+
+  .md\:border-current {
+    border-color: currentColor;
+  }
+
+  .md\:border-black {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .md\:border-white {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .md\:border-gray-100 {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .md\:border-gray-200 {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .md\:border-gray-300 {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .md\:border-gray-400 {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .md\:border-gray-500 {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .md\:border-gray-600 {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .md\:border-gray-700 {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .md\:border-gray-800 {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .md\:border-gray-900 {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .md\:border-red-100 {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .md\:border-red-200 {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .md\:border-red-300 {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .md\:border-red-400 {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .md\:border-red-500 {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .md\:border-red-600 {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .md\:border-red-700 {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .md\:border-red-800 {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .md\:border-red-900 {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .md\:border-orange-100 {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .md\:border-orange-200 {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .md\:border-orange-300 {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .md\:border-orange-400 {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .md\:border-orange-500 {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .md\:border-orange-600 {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .md\:border-orange-700 {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .md\:border-orange-800 {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .md\:border-orange-900 {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .md\:border-yellow-100 {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .md\:border-yellow-200 {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .md\:border-yellow-300 {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .md\:border-yellow-400 {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .md\:border-yellow-500 {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .md\:border-yellow-600 {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .md\:border-yellow-700 {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .md\:border-yellow-800 {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .md\:border-yellow-900 {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .md\:border-green-100 {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .md\:border-green-200 {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .md\:border-green-300 {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .md\:border-green-400 {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .md\:border-green-500 {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .md\:border-green-600 {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .md\:border-green-700 {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .md\:border-green-800 {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .md\:border-green-900 {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .md\:border-teal-100 {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .md\:border-teal-200 {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .md\:border-teal-300 {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .md\:border-teal-400 {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .md\:border-teal-500 {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .md\:border-teal-600 {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .md\:border-teal-700 {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .md\:border-teal-800 {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .md\:border-teal-900 {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .md\:border-blue-100 {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .md\:border-blue-200 {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .md\:border-blue-300 {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .md\:border-blue-400 {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .md\:border-blue-500 {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .md\:border-blue-600 {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .md\:border-blue-700 {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .md\:border-blue-800 {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .md\:border-blue-900 {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .md\:border-indigo-100 {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .md\:border-indigo-200 {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .md\:border-indigo-300 {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .md\:border-indigo-400 {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .md\:border-indigo-500 {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .md\:border-indigo-600 {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .md\:border-indigo-700 {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .md\:border-indigo-800 {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .md\:border-indigo-900 {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .md\:border-purple-100 {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .md\:border-purple-200 {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .md\:border-purple-300 {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .md\:border-purple-400 {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .md\:border-purple-500 {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .md\:border-purple-600 {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .md\:border-purple-700 {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .md\:border-purple-800 {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .md\:border-purple-900 {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .md\:border-pink-100 {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .md\:border-pink-200 {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .md\:border-pink-300 {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .md\:border-pink-400 {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .md\:border-pink-500 {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .md\:border-pink-600 {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .md\:border-pink-700 {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .md\:border-pink-800 {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .md\:border-pink-900 {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .md\:hover\:border-transparent:hover {
+    border-color: transparent;
+  }
+
+  .md\:hover\:border-current:hover {
+    border-color: currentColor;
+  }
+
+  .md\:hover\:border-black:hover {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .md\:hover\:border-white:hover {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-100:hover {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-200:hover {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-300:hover {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-400:hover {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-500:hover {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-600:hover {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-700:hover {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-800:hover {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .md\:hover\:border-gray-900:hover {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-300:hover {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-400:hover {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-500:hover {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-600:hover {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-700:hover {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-800:hover {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .md\:hover\:border-red-900:hover {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-100:hover {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-200:hover {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-300:hover {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-400:hover {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-500:hover {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-600:hover {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-700:hover {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-800:hover {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .md\:hover\:border-orange-900:hover {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-100:hover {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-200:hover {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-300:hover {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-400:hover {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-500:hover {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-600:hover {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-700:hover {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-800:hover {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .md\:hover\:border-yellow-900:hover {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-100:hover {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-200:hover {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-300:hover {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-400:hover {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-500:hover {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-600:hover {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-700:hover {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-800:hover {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .md\:hover\:border-green-900:hover {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-100:hover {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-200:hover {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-300:hover {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-400:hover {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-500:hover {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-600:hover {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-700:hover {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-800:hover {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .md\:hover\:border-teal-900:hover {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-200:hover {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-300:hover {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-400:hover {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-500:hover {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-600:hover {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-700:hover {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-800:hover {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .md\:hover\:border-blue-900:hover {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-200:hover {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-300:hover {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-400:hover {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-500:hover {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-600:hover {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-700:hover {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-800:hover {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .md\:hover\:border-indigo-900:hover {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-100:hover {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-200:hover {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-300:hover {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-400:hover {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-500:hover {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-600:hover {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-700:hover {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-800:hover {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .md\:hover\:border-purple-900:hover {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-300:hover {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-400:hover {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-500:hover {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-600:hover {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-700:hover {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-800:hover {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .md\:hover\:border-pink-900:hover {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .md\:focus\:border-transparent:focus {
+    border-color: transparent;
+  }
+
+  .md\:focus\:border-current:focus {
+    border-color: currentColor;
+  }
+
+  .md\:focus\:border-black:focus {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .md\:focus\:border-white:focus {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-100:focus {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-200:focus {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-300:focus {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-400:focus {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-500:focus {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-600:focus {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-700:focus {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-800:focus {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .md\:focus\:border-gray-900:focus {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-300:focus {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-400:focus {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-500:focus {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-600:focus {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-700:focus {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-800:focus {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .md\:focus\:border-red-900:focus {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-100:focus {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-200:focus {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-300:focus {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-400:focus {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-500:focus {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-600:focus {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-700:focus {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-800:focus {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .md\:focus\:border-orange-900:focus {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-100:focus {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-200:focus {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-300:focus {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-400:focus {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-500:focus {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-600:focus {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-700:focus {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-800:focus {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .md\:focus\:border-yellow-900:focus {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-100:focus {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-200:focus {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-300:focus {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-400:focus {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-500:focus {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-600:focus {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-700:focus {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-800:focus {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .md\:focus\:border-green-900:focus {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-100:focus {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-200:focus {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-300:focus {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-400:focus {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-500:focus {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-600:focus {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-700:focus {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-800:focus {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .md\:focus\:border-teal-900:focus {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-200:focus {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-300:focus {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-400:focus {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-500:focus {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-600:focus {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-700:focus {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-800:focus {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .md\:focus\:border-blue-900:focus {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-200:focus {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-300:focus {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-400:focus {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-500:focus {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-600:focus {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-700:focus {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-800:focus {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .md\:focus\:border-indigo-900:focus {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-100:focus {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-200:focus {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-300:focus {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-400:focus {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-500:focus {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-600:focus {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-700:focus {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-800:focus {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .md\:focus\:border-purple-900:focus {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-300:focus {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-400:focus {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-500:focus {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-600:focus {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-700:focus {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-800:focus {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .md\:focus\:border-pink-900:focus {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .md\:border-opacity-0 {
+    --border-opacity: 0;
+  }
+
+  .md\:border-opacity-25 {
+    --border-opacity: 0.25;
+  }
+
+  .md\:border-opacity-50 {
+    --border-opacity: 0.5;
+  }
+
+  .md\:border-opacity-75 {
+    --border-opacity: 0.75;
+  }
+
+  .md\:border-opacity-100 {
+    --border-opacity: 1;
+  }
+
+  .md\:hover\:border-opacity-0:hover {
+    --border-opacity: 0;
+  }
+
+  .md\:hover\:border-opacity-25:hover {
+    --border-opacity: 0.25;
+  }
+
+  .md\:hover\:border-opacity-50:hover {
+    --border-opacity: 0.5;
+  }
+
+  .md\:hover\:border-opacity-75:hover {
+    --border-opacity: 0.75;
+  }
+
+  .md\:hover\:border-opacity-100:hover {
+    --border-opacity: 1;
+  }
+
+  .md\:focus\:border-opacity-0:focus {
+    --border-opacity: 0;
+  }
+
+  .md\:focus\:border-opacity-25:focus {
+    --border-opacity: 0.25;
+  }
+
+  .md\:focus\:border-opacity-50:focus {
+    --border-opacity: 0.5;
+  }
+
+  .md\:focus\:border-opacity-75:focus {
+    --border-opacity: 0.75;
+  }
+
+  .md\:focus\:border-opacity-100:focus {
+    --border-opacity: 1;
+  }
+
+  .md\:rounded-none {
+    border-radius: 0;
+  }
+
+  .md\:rounded-sm {
+    border-radius: 0.125rem;
+  }
+
+  .md\:rounded {
+    border-radius: 0.25rem;
+  }
+
+  .md\:rounded-md {
+    border-radius: 0.375rem;
+  }
+
+  .md\:rounded-lg {
+    border-radius: 0.5rem;
+  }
+
+  .md\:rounded-full {
+    border-radius: 9999px;
+  }
+
+  .md\:rounded-t-none {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+  }
+
+  .md\:rounded-r-none {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+  }
+
+  .md\:rounded-b-none {
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .md\:rounded-l-none {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .md\:rounded-t-sm {
+    border-top-left-radius: 0.125rem;
+    border-top-right-radius: 0.125rem;
+  }
+
+  .md\:rounded-r-sm {
+    border-top-right-radius: 0.125rem;
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .md\:rounded-b-sm {
+    border-bottom-right-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .md\:rounded-l-sm {
+    border-top-left-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .md\:rounded-t {
+    border-top-left-radius: 0.25rem;
+    border-top-right-radius: 0.25rem;
+  }
+
+  .md\:rounded-r {
+    border-top-right-radius: 0.25rem;
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .md\:rounded-b {
+    border-bottom-right-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .md\:rounded-l {
+    border-top-left-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .md\:rounded-t-md {
+    border-top-left-radius: 0.375rem;
+    border-top-right-radius: 0.375rem;
+  }
+
+  .md\:rounded-r-md {
+    border-top-right-radius: 0.375rem;
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .md\:rounded-b-md {
+    border-bottom-right-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .md\:rounded-l-md {
+    border-top-left-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .md\:rounded-t-lg {
+    border-top-left-radius: 0.5rem;
+    border-top-right-radius: 0.5rem;
+  }
+
+  .md\:rounded-r-lg {
+    border-top-right-radius: 0.5rem;
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .md\:rounded-b-lg {
+    border-bottom-right-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .md\:rounded-l-lg {
+    border-top-left-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .md\:rounded-t-full {
+    border-top-left-radius: 9999px;
+    border-top-right-radius: 9999px;
+  }
+
+  .md\:rounded-r-full {
+    border-top-right-radius: 9999px;
+    border-bottom-right-radius: 9999px;
+  }
+
+  .md\:rounded-b-full {
+    border-bottom-right-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .md\:rounded-l-full {
+    border-top-left-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .md\:rounded-tl-none {
+    border-top-left-radius: 0;
+  }
+
+  .md\:rounded-tr-none {
+    border-top-right-radius: 0;
+  }
+
+  .md\:rounded-br-none {
+    border-bottom-right-radius: 0;
+  }
+
+  .md\:rounded-bl-none {
+    border-bottom-left-radius: 0;
+  }
+
+  .md\:rounded-tl-sm {
+    border-top-left-radius: 0.125rem;
+  }
+
+  .md\:rounded-tr-sm {
+    border-top-right-radius: 0.125rem;
+  }
+
+  .md\:rounded-br-sm {
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .md\:rounded-bl-sm {
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .md\:rounded-tl {
+    border-top-left-radius: 0.25rem;
+  }
+
+  .md\:rounded-tr {
+    border-top-right-radius: 0.25rem;
+  }
+
+  .md\:rounded-br {
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .md\:rounded-bl {
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .md\:rounded-tl-md {
+    border-top-left-radius: 0.375rem;
+  }
+
+  .md\:rounded-tr-md {
+    border-top-right-radius: 0.375rem;
+  }
+
+  .md\:rounded-br-md {
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .md\:rounded-bl-md {
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .md\:rounded-tl-lg {
+    border-top-left-radius: 0.5rem;
+  }
+
+  .md\:rounded-tr-lg {
+    border-top-right-radius: 0.5rem;
+  }
+
+  .md\:rounded-br-lg {
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .md\:rounded-bl-lg {
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .md\:rounded-tl-full {
+    border-top-left-radius: 9999px;
+  }
+
+  .md\:rounded-tr-full {
+    border-top-right-radius: 9999px;
+  }
+
+  .md\:rounded-br-full {
+    border-bottom-right-radius: 9999px;
+  }
+
+  .md\:rounded-bl-full {
+    border-bottom-left-radius: 9999px;
+  }
+
+  .md\:border-solid {
+    border-style: solid;
+  }
+
+  .md\:border-dashed {
+    border-style: dashed;
+  }
+
+  .md\:border-dotted {
+    border-style: dotted;
+  }
+
+  .md\:border-double {
+    border-style: double;
+  }
+
+  .md\:border-none {
+    border-style: none;
+  }
+
+  .md\:border-0 {
+    border-width: 0;
+  }
+
+  .md\:border-2 {
+    border-width: 2px;
+  }
+
+  .md\:border-4 {
+    border-width: 4px;
+  }
+
+  .md\:border-8 {
+    border-width: 8px;
+  }
+
+  .md\:border {
+    border-width: 1px;
+  }
+
+  .md\:border-t-0 {
+    border-top-width: 0;
+  }
+
+  .md\:border-r-0 {
+    border-right-width: 0;
+  }
+
+  .md\:border-b-0 {
+    border-bottom-width: 0;
+  }
+
+  .md\:border-l-0 {
+    border-left-width: 0;
+  }
+
+  .md\:border-t-2 {
+    border-top-width: 2px;
+  }
+
+  .md\:border-r-2 {
+    border-right-width: 2px;
+  }
+
+  .md\:border-b-2 {
+    border-bottom-width: 2px;
+  }
+
+  .md\:border-l-2 {
+    border-left-width: 2px;
+  }
+
+  .md\:border-t-4 {
+    border-top-width: 4px;
+  }
+
+  .md\:border-r-4 {
+    border-right-width: 4px;
+  }
+
+  .md\:border-b-4 {
+    border-bottom-width: 4px;
+  }
+
+  .md\:border-l-4 {
+    border-left-width: 4px;
+  }
+
+  .md\:border-t-8 {
+    border-top-width: 8px;
+  }
+
+  .md\:border-r-8 {
+    border-right-width: 8px;
+  }
+
+  .md\:border-b-8 {
+    border-bottom-width: 8px;
+  }
+
+  .md\:border-l-8 {
+    border-left-width: 8px;
+  }
+
+  .md\:border-t {
+    border-top-width: 1px;
+  }
+
+  .md\:border-r {
+    border-right-width: 1px;
+  }
+
+  .md\:border-b {
+    border-bottom-width: 1px;
+  }
+
+  .md\:border-l {
+    border-left-width: 1px;
+  }
+
+  .md\:box-border {
+    box-sizing: border-box;
+  }
+
+  .md\:box-content {
+    box-sizing: content-box;
+  }
+
+  .md\:cursor-auto {
+    cursor: auto;
+  }
+
+  .md\:cursor-default {
+    cursor: default;
+  }
+
+  .md\:cursor-pointer {
+    cursor: pointer;
+  }
+
+  .md\:cursor-wait {
+    cursor: wait;
+  }
+
+  .md\:cursor-text {
+    cursor: text;
+  }
+
+  .md\:cursor-move {
+    cursor: move;
+  }
+
+  .md\:cursor-not-allowed {
+    cursor: not-allowed;
+  }
+
+  .md\:block {
+    display: block;
+  }
+
+  .md\:inline-block {
+    display: inline-block;
+  }
+
+  .md\:inline {
+    display: inline;
+  }
+
+  .md\:flex {
+    display: flex;
+  }
+
+  .md\:inline-flex {
+    display: inline-flex;
+  }
+
+  .md\:table {
+    display: table;
+  }
+
+  .md\:table-caption {
+    display: table-caption;
+  }
+
+  .md\:table-cell {
+    display: table-cell;
+  }
+
+  .md\:table-column {
+    display: table-column;
+  }
+
+  .md\:table-column-group {
+    display: table-column-group;
+  }
+
+  .md\:table-footer-group {
+    display: table-footer-group;
+  }
+
+  .md\:table-header-group {
+    display: table-header-group;
+  }
+
+  .md\:table-row-group {
+    display: table-row-group;
+  }
+
+  .md\:table-row {
+    display: table-row;
+  }
+
+  .md\:flow-root {
+    display: flow-root;
+  }
+
+  .md\:grid {
+    display: grid;
+  }
+
+  .md\:inline-grid {
+    display: inline-grid;
+  }
+
+  .md\:contents {
+    display: contents;
+  }
+
+  .md\:hidden {
+    display: none;
+  }
+
+  .md\:flex-row {
+    flex-direction: row;
+  }
+
+  .md\:flex-row-reverse {
+    flex-direction: row-reverse;
+  }
+
+  .md\:flex-col {
+    flex-direction: column;
+  }
+
+  .md\:flex-col-reverse {
+    flex-direction: column-reverse;
+  }
+
+  .md\:flex-wrap {
+    flex-wrap: wrap;
+  }
+
+  .md\:flex-wrap-reverse {
+    flex-wrap: wrap-reverse;
+  }
+
+  .md\:flex-no-wrap {
+    flex-wrap: nowrap;
+  }
+
+  .md\:place-items-auto {
+    place-items: auto;
+  }
+
+  .md\:place-items-start {
+    place-items: start;
+  }
+
+  .md\:place-items-end {
+    place-items: end;
+  }
+
+  .md\:place-items-center {
+    place-items: center;
+  }
+
+  .md\:place-items-stretch {
+    place-items: stretch;
+  }
+
+  .md\:place-content-center {
+    place-content: center;
+  }
+
+  .md\:place-content-start {
+    place-content: start;
+  }
+
+  .md\:place-content-end {
+    place-content: end;
+  }
+
+  .md\:place-content-between {
+    place-content: space-between;
+  }
+
+  .md\:place-content-around {
+    place-content: space-around;
+  }
+
+  .md\:place-content-evenly {
+    place-content: space-evenly;
+  }
+
+  .md\:place-content-stretch {
+    place-content: stretch;
+  }
+
+  .md\:place-self-auto {
+    place-self: auto;
+  }
+
+  .md\:place-self-start {
+    place-self: start;
+  }
+
+  .md\:place-self-end {
+    place-self: end;
+  }
+
+  .md\:place-self-center {
+    place-self: center;
+  }
+
+  .md\:place-self-stretch {
+    place-self: stretch;
+  }
+
+  .md\:items-start {
+    align-items: flex-start;
+  }
+
+  .md\:items-end {
+    align-items: flex-end;
+  }
+
+  .md\:items-center {
+    align-items: center;
+  }
+
+  .md\:items-baseline {
+    align-items: baseline;
+  }
+
+  .md\:items-stretch {
+    align-items: stretch;
+  }
+
+  .md\:content-center {
+    align-content: center;
+  }
+
+  .md\:content-start {
+    align-content: flex-start;
+  }
+
+  .md\:content-end {
+    align-content: flex-end;
+  }
+
+  .md\:content-between {
+    align-content: space-between;
+  }
+
+  .md\:content-around {
+    align-content: space-around;
+  }
+
+  .md\:content-evenly {
+    align-content: space-evenly;
+  }
+
+  .md\:self-auto {
+    align-self: auto;
+  }
+
+  .md\:self-start {
+    align-self: flex-start;
+  }
+
+  .md\:self-end {
+    align-self: flex-end;
+  }
+
+  .md\:self-center {
+    align-self: center;
+  }
+
+  .md\:self-stretch {
+    align-self: stretch;
+  }
+
+  .md\:justify-items-auto {
+    justify-items: auto;
+  }
+
+  .md\:justify-items-start {
+    justify-items: start;
+  }
+
+  .md\:justify-items-end {
+    justify-items: end;
+  }
+
+  .md\:justify-items-center {
+    justify-items: center;
+  }
+
+  .md\:justify-items-stretch {
+    justify-items: stretch;
+  }
+
+  .md\:justify-start {
+    justify-content: flex-start;
+  }
+
+  .md\:justify-end {
+    justify-content: flex-end;
+  }
+
+  .md\:justify-center {
+    justify-content: center;
+  }
+
+  .md\:justify-between {
+    justify-content: space-between;
+  }
+
+  .md\:justify-around {
+    justify-content: space-around;
+  }
+
+  .md\:justify-evenly {
+    justify-content: space-evenly;
+  }
+
+  .md\:justify-self-auto {
+    justify-self: auto;
+  }
+
+  .md\:justify-self-start {
+    justify-self: start;
+  }
+
+  .md\:justify-self-end {
+    justify-self: end;
+  }
+
+  .md\:justify-self-center {
+    justify-self: center;
+  }
+
+  .md\:justify-self-stretch {
+    justify-self: stretch;
+  }
+
+  .md\:flex-1 {
+    flex: 1 1 0%;
+  }
+
+  .md\:flex-auto {
+    flex: 1 1 auto;
+  }
+
+  .md\:flex-initial {
+    flex: 0 1 auto;
+  }
+
+  .md\:flex-none {
+    flex: none;
+  }
+
+  .md\:flex-grow-0 {
+    flex-grow: 0;
+  }
+
+  .md\:flex-grow {
+    flex-grow: 1;
+  }
+
+  .md\:flex-shrink-0 {
+    flex-shrink: 0;
+  }
+
+  .md\:flex-shrink {
+    flex-shrink: 1;
+  }
+
+  .md\:order-1 {
+    order: 1;
+  }
+
+  .md\:order-2 {
+    order: 2;
+  }
+
+  .md\:order-3 {
+    order: 3;
+  }
+
+  .md\:order-4 {
+    order: 4;
+  }
+
+  .md\:order-5 {
+    order: 5;
+  }
+
+  .md\:order-6 {
+    order: 6;
+  }
+
+  .md\:order-7 {
+    order: 7;
+  }
+
+  .md\:order-8 {
+    order: 8;
+  }
+
+  .md\:order-9 {
+    order: 9;
+  }
+
+  .md\:order-10 {
+    order: 10;
+  }
+
+  .md\:order-11 {
+    order: 11;
+  }
+
+  .md\:order-12 {
+    order: 12;
+  }
+
+  .md\:order-first {
+    order: -9999;
+  }
+
+  .md\:order-last {
+    order: 9999;
+  }
+
+  .md\:order-none {
+    order: 0;
+  }
+
+  .md\:float-right {
+    float: right;
+  }
+
+  .md\:float-left {
+    float: left;
+  }
+
+  .md\:float-none {
+    float: none;
+  }
+
+  .md\:clearfix:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+
+  .md\:clear-left {
+    clear: left;
+  }
+
+  .md\:clear-right {
+    clear: right;
+  }
+
+  .md\:clear-both {
+    clear: both;
+  }
+
+  .md\:clear-none {
+    clear: none;
+  }
+
+  .md\:font-sans {
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  }
+
+  .md\:font-serif {
+    font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+  }
+
+  .md\:font-mono {
+    font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+  }
+
+  .md\:font-hairline {
+    font-weight: 100;
+  }
+
+  .md\:font-thin {
+    font-weight: 200;
+  }
+
+  .md\:font-light {
+    font-weight: 300;
+  }
+
+  .md\:font-normal {
+    font-weight: 400;
+  }
+
+  .md\:font-medium {
+    font-weight: 500;
+  }
+
+  .md\:font-semibold {
+    font-weight: 600;
+  }
+
+  .md\:font-bold {
+    font-weight: 700;
+  }
+
+  .md\:font-extrabold {
+    font-weight: 800;
+  }
+
+  .md\:font-black {
+    font-weight: 900;
+  }
+
+  .md\:hover\:font-hairline:hover {
+    font-weight: 100;
+  }
+
+  .md\:hover\:font-thin:hover {
+    font-weight: 200;
+  }
+
+  .md\:hover\:font-light:hover {
+    font-weight: 300;
+  }
+
+  .md\:hover\:font-normal:hover {
+    font-weight: 400;
+  }
+
+  .md\:hover\:font-medium:hover {
+    font-weight: 500;
+  }
+
+  .md\:hover\:font-semibold:hover {
+    font-weight: 600;
+  }
+
+  .md\:hover\:font-bold:hover {
+    font-weight: 700;
+  }
+
+  .md\:hover\:font-extrabold:hover {
+    font-weight: 800;
+  }
+
+  .md\:hover\:font-black:hover {
+    font-weight: 900;
+  }
+
+  .md\:focus\:font-hairline:focus {
+    font-weight: 100;
+  }
+
+  .md\:focus\:font-thin:focus {
+    font-weight: 200;
+  }
+
+  .md\:focus\:font-light:focus {
+    font-weight: 300;
+  }
+
+  .md\:focus\:font-normal:focus {
+    font-weight: 400;
+  }
+
+  .md\:focus\:font-medium:focus {
+    font-weight: 500;
+  }
+
+  .md\:focus\:font-semibold:focus {
+    font-weight: 600;
+  }
+
+  .md\:focus\:font-bold:focus {
+    font-weight: 700;
+  }
+
+  .md\:focus\:font-extrabold:focus {
+    font-weight: 800;
+  }
+
+  .md\:focus\:font-black:focus {
+    font-weight: 900;
+  }
+
+  .md\:h-0 {
+    height: 0;
+  }
+
+  .md\:h-1 {
+    height: 0.25rem;
+  }
+
+  .md\:h-2 {
+    height: 0.5rem;
+  }
+
+  .md\:h-3 {
+    height: 0.75rem;
+  }
+
+  .md\:h-4 {
+    height: 1rem;
+  }
+
+  .md\:h-5 {
+    height: 1.25rem;
+  }
+
+  .md\:h-6 {
+    height: 1.5rem;
+  }
+
+  .md\:h-8 {
+    height: 2rem;
+  }
+
+  .md\:h-10 {
+    height: 2.5rem;
+  }
+
+  .md\:h-12 {
+    height: 3rem;
+  }
+
+  .md\:h-16 {
+    height: 4rem;
+  }
+
+  .md\:h-20 {
+    height: 5rem;
+  }
+
+  .md\:h-24 {
+    height: 6rem;
+  }
+
+  .md\:h-32 {
+    height: 8rem;
+  }
+
+  .md\:h-40 {
+    height: 10rem;
+  }
+
+  .md\:h-48 {
+    height: 12rem;
+  }
+
+  .md\:h-56 {
+    height: 14rem;
+  }
+
+  .md\:h-64 {
+    height: 16rem;
+  }
+
+  .md\:h-auto {
+    height: auto;
+  }
+
+  .md\:h-px {
+    height: 1px;
+  }
+
+  .md\:h-full {
+    height: 100%;
+  }
+
+  .md\:h-screen {
+    height: 100vh;
+  }
+
+  .md\:text-xs {
+    font-size: 0.75rem;
+  }
+
+  .md\:text-sm {
+    font-size: 0.875rem;
+  }
+
+  .md\:text-base {
+    font-size: 1rem;
+  }
+
+  .md\:text-lg {
+    font-size: 1.125rem;
+  }
+
+  .md\:text-xl {
+    font-size: 1.25rem;
+  }
+
+  .md\:text-2xl {
+    font-size: 1.5rem;
+  }
+
+  .md\:text-3xl {
+    font-size: 1.875rem;
+  }
+
+  .md\:text-4xl {
+    font-size: 2.25rem;
+  }
+
+  .md\:text-5xl {
+    font-size: 3rem;
+  }
+
+  .md\:text-6xl {
+    font-size: 4rem;
+  }
+
+  .md\:leading-3 {
+    line-height: .75rem;
+  }
+
+  .md\:leading-4 {
+    line-height: 1rem;
+  }
+
+  .md\:leading-5 {
+    line-height: 1.25rem;
+  }
+
+  .md\:leading-6 {
+    line-height: 1.5rem;
+  }
+
+  .md\:leading-7 {
+    line-height: 1.75rem;
+  }
+
+  .md\:leading-8 {
+    line-height: 2rem;
+  }
+
+  .md\:leading-9 {
+    line-height: 2.25rem;
+  }
+
+  .md\:leading-10 {
+    line-height: 2.5rem;
+  }
+
+  .md\:leading-none {
+    line-height: 1;
+  }
+
+  .md\:leading-tight {
+    line-height: 1.25;
+  }
+
+  .md\:leading-snug {
+    line-height: 1.375;
+  }
+
+  .md\:leading-normal {
+    line-height: 1.5;
+  }
+
+  .md\:leading-relaxed {
+    line-height: 1.625;
+  }
+
+  .md\:leading-loose {
+    line-height: 2;
+  }
+
+  .md\:list-inside {
+    list-style-position: inside;
+  }
+
+  .md\:list-outside {
+    list-style-position: outside;
+  }
+
+  .md\:list-none {
+    list-style-type: none;
+  }
+
+  .md\:list-disc {
+    list-style-type: disc;
+  }
+
+  .md\:list-decimal {
+    list-style-type: decimal;
+  }
+
+  .md\:m-0 {
+    margin: 0;
+  }
+
+  .md\:m-1 {
+    margin: 0.25rem;
+  }
+
+  .md\:m-2 {
+    margin: 0.5rem;
+  }
+
+  .md\:m-3 {
+    margin: 0.75rem;
+  }
+
+  .md\:m-4 {
+    margin: 1rem;
+  }
+
+  .md\:m-5 {
+    margin: 1.25rem;
+  }
+
+  .md\:m-6 {
+    margin: 1.5rem;
+  }
+
+  .md\:m-8 {
+    margin: 2rem;
+  }
+
+  .md\:m-10 {
+    margin: 2.5rem;
+  }
+
+  .md\:m-12 {
+    margin: 3rem;
+  }
+
+  .md\:m-16 {
+    margin: 4rem;
+  }
+
+  .md\:m-20 {
+    margin: 5rem;
+  }
+
+  .md\:m-24 {
+    margin: 6rem;
+  }
+
+  .md\:m-32 {
+    margin: 8rem;
+  }
+
+  .md\:m-40 {
+    margin: 10rem;
+  }
+
+  .md\:m-48 {
+    margin: 12rem;
+  }
+
+  .md\:m-56 {
+    margin: 14rem;
+  }
+
+  .md\:m-64 {
+    margin: 16rem;
+  }
+
+  .md\:m-auto {
+    margin: auto;
+  }
+
+  .md\:m-px {
+    margin: 1px;
+  }
+
+  .md\:-m-1 {
+    margin: -0.25rem;
+  }
+
+  .md\:-m-2 {
+    margin: -0.5rem;
+  }
+
+  .md\:-m-3 {
+    margin: -0.75rem;
+  }
+
+  .md\:-m-4 {
+    margin: -1rem;
+  }
+
+  .md\:-m-5 {
+    margin: -1.25rem;
+  }
+
+  .md\:-m-6 {
+    margin: -1.5rem;
+  }
+
+  .md\:-m-8 {
+    margin: -2rem;
+  }
+
+  .md\:-m-10 {
+    margin: -2.5rem;
+  }
+
+  .md\:-m-12 {
+    margin: -3rem;
+  }
+
+  .md\:-m-16 {
+    margin: -4rem;
+  }
+
+  .md\:-m-20 {
+    margin: -5rem;
+  }
+
+  .md\:-m-24 {
+    margin: -6rem;
+  }
+
+  .md\:-m-32 {
+    margin: -8rem;
+  }
+
+  .md\:-m-40 {
+    margin: -10rem;
+  }
+
+  .md\:-m-48 {
+    margin: -12rem;
+  }
+
+  .md\:-m-56 {
+    margin: -14rem;
+  }
+
+  .md\:-m-64 {
+    margin: -16rem;
+  }
+
+  .md\:-m-px {
+    margin: -1px;
+  }
+
+  .md\:my-0 {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .md\:mx-0 {
+    margin-left: 0;
+    margin-right: 0;
+  }
+
+  .md\:my-1 {
+    margin-top: 0.25rem;
+    margin-bottom: 0.25rem;
+  }
+
+  .md\:mx-1 {
+    margin-left: 0.25rem;
+    margin-right: 0.25rem;
+  }
+
+  .md\:my-2 {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+  }
+
+  .md\:mx-2 {
+    margin-left: 0.5rem;
+    margin-right: 0.5rem;
+  }
+
+  .md\:my-3 {
+    margin-top: 0.75rem;
+    margin-bottom: 0.75rem;
+  }
+
+  .md\:mx-3 {
+    margin-left: 0.75rem;
+    margin-right: 0.75rem;
+  }
+
+  .md\:my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+  }
+
+  .md\:mx-4 {
+    margin-left: 1rem;
+    margin-right: 1rem;
+  }
+
+  .md\:my-5 {
+    margin-top: 1.25rem;
+    margin-bottom: 1.25rem;
+  }
+
+  .md\:mx-5 {
+    margin-left: 1.25rem;
+    margin-right: 1.25rem;
+  }
+
+  .md\:my-6 {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+
+  .md\:mx-6 {
+    margin-left: 1.5rem;
+    margin-right: 1.5rem;
+  }
+
+  .md\:my-8 {
+    margin-top: 2rem;
+    margin-bottom: 2rem;
+  }
+
+  .md\:mx-8 {
+    margin-left: 2rem;
+    margin-right: 2rem;
+  }
+
+  .md\:my-10 {
+    margin-top: 2.5rem;
+    margin-bottom: 2.5rem;
+  }
+
+  .md\:mx-10 {
+    margin-left: 2.5rem;
+    margin-right: 2.5rem;
+  }
+
+  .md\:my-12 {
+    margin-top: 3rem;
+    margin-bottom: 3rem;
+  }
+
+  .md\:mx-12 {
+    margin-left: 3rem;
+    margin-right: 3rem;
+  }
+
+  .md\:my-16 {
+    margin-top: 4rem;
+    margin-bottom: 4rem;
+  }
+
+  .md\:mx-16 {
+    margin-left: 4rem;
+    margin-right: 4rem;
+  }
+
+  .md\:my-20 {
+    margin-top: 5rem;
+    margin-bottom: 5rem;
+  }
+
+  .md\:mx-20 {
+    margin-left: 5rem;
+    margin-right: 5rem;
+  }
+
+  .md\:my-24 {
+    margin-top: 6rem;
+    margin-bottom: 6rem;
+  }
+
+  .md\:mx-24 {
+    margin-left: 6rem;
+    margin-right: 6rem;
+  }
+
+  .md\:my-32 {
+    margin-top: 8rem;
+    margin-bottom: 8rem;
+  }
+
+  .md\:mx-32 {
+    margin-left: 8rem;
+    margin-right: 8rem;
+  }
+
+  .md\:my-40 {
+    margin-top: 10rem;
+    margin-bottom: 10rem;
+  }
+
+  .md\:mx-40 {
+    margin-left: 10rem;
+    margin-right: 10rem;
+  }
+
+  .md\:my-48 {
+    margin-top: 12rem;
+    margin-bottom: 12rem;
+  }
+
+  .md\:mx-48 {
+    margin-left: 12rem;
+    margin-right: 12rem;
+  }
+
+  .md\:my-56 {
+    margin-top: 14rem;
+    margin-bottom: 14rem;
+  }
+
+  .md\:mx-56 {
+    margin-left: 14rem;
+    margin-right: 14rem;
+  }
+
+  .md\:my-64 {
+    margin-top: 16rem;
+    margin-bottom: 16rem;
+  }
+
+  .md\:mx-64 {
+    margin-left: 16rem;
+    margin-right: 16rem;
+  }
+
+  .md\:my-auto {
+    margin-top: auto;
+    margin-bottom: auto;
+  }
+
+  .md\:mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .md\:my-px {
+    margin-top: 1px;
+    margin-bottom: 1px;
+  }
+
+  .md\:mx-px {
+    margin-left: 1px;
+    margin-right: 1px;
+  }
+
+  .md\:-my-1 {
+    margin-top: -0.25rem;
+    margin-bottom: -0.25rem;
+  }
+
+  .md\:-mx-1 {
+    margin-left: -0.25rem;
+    margin-right: -0.25rem;
+  }
+
+  .md\:-my-2 {
+    margin-top: -0.5rem;
+    margin-bottom: -0.5rem;
+  }
+
+  .md\:-mx-2 {
+    margin-left: -0.5rem;
+    margin-right: -0.5rem;
+  }
+
+  .md\:-my-3 {
+    margin-top: -0.75rem;
+    margin-bottom: -0.75rem;
+  }
+
+  .md\:-mx-3 {
+    margin-left: -0.75rem;
+    margin-right: -0.75rem;
+  }
+
+  .md\:-my-4 {
+    margin-top: -1rem;
+    margin-bottom: -1rem;
+  }
+
+  .md\:-mx-4 {
+    margin-left: -1rem;
+    margin-right: -1rem;
+  }
+
+  .md\:-my-5 {
+    margin-top: -1.25rem;
+    margin-bottom: -1.25rem;
+  }
+
+  .md\:-mx-5 {
+    margin-left: -1.25rem;
+    margin-right: -1.25rem;
+  }
+
+  .md\:-my-6 {
+    margin-top: -1.5rem;
+    margin-bottom: -1.5rem;
+  }
+
+  .md\:-mx-6 {
+    margin-left: -1.5rem;
+    margin-right: -1.5rem;
+  }
+
+  .md\:-my-8 {
+    margin-top: -2rem;
+    margin-bottom: -2rem;
+  }
+
+  .md\:-mx-8 {
+    margin-left: -2rem;
+    margin-right: -2rem;
+  }
+
+  .md\:-my-10 {
+    margin-top: -2.5rem;
+    margin-bottom: -2.5rem;
+  }
+
+  .md\:-mx-10 {
+    margin-left: -2.5rem;
+    margin-right: -2.5rem;
+  }
+
+  .md\:-my-12 {
+    margin-top: -3rem;
+    margin-bottom: -3rem;
+  }
+
+  .md\:-mx-12 {
+    margin-left: -3rem;
+    margin-right: -3rem;
+  }
+
+  .md\:-my-16 {
+    margin-top: -4rem;
+    margin-bottom: -4rem;
+  }
+
+  .md\:-mx-16 {
+    margin-left: -4rem;
+    margin-right: -4rem;
+  }
+
+  .md\:-my-20 {
+    margin-top: -5rem;
+    margin-bottom: -5rem;
+  }
+
+  .md\:-mx-20 {
+    margin-left: -5rem;
+    margin-right: -5rem;
+  }
+
+  .md\:-my-24 {
+    margin-top: -6rem;
+    margin-bottom: -6rem;
+  }
+
+  .md\:-mx-24 {
+    margin-left: -6rem;
+    margin-right: -6rem;
+  }
+
+  .md\:-my-32 {
+    margin-top: -8rem;
+    margin-bottom: -8rem;
+  }
+
+  .md\:-mx-32 {
+    margin-left: -8rem;
+    margin-right: -8rem;
+  }
+
+  .md\:-my-40 {
+    margin-top: -10rem;
+    margin-bottom: -10rem;
+  }
+
+  .md\:-mx-40 {
+    margin-left: -10rem;
+    margin-right: -10rem;
+  }
+
+  .md\:-my-48 {
+    margin-top: -12rem;
+    margin-bottom: -12rem;
+  }
+
+  .md\:-mx-48 {
+    margin-left: -12rem;
+    margin-right: -12rem;
+  }
+
+  .md\:-my-56 {
+    margin-top: -14rem;
+    margin-bottom: -14rem;
+  }
+
+  .md\:-mx-56 {
+    margin-left: -14rem;
+    margin-right: -14rem;
+  }
+
+  .md\:-my-64 {
+    margin-top: -16rem;
+    margin-bottom: -16rem;
+  }
+
+  .md\:-mx-64 {
+    margin-left: -16rem;
+    margin-right: -16rem;
+  }
+
+  .md\:-my-px {
+    margin-top: -1px;
+    margin-bottom: -1px;
+  }
+
+  .md\:-mx-px {
+    margin-left: -1px;
+    margin-right: -1px;
+  }
+
+  .md\:mt-0 {
+    margin-top: 0;
+  }
+
+  .md\:mr-0 {
+    margin-right: 0;
+  }
+
+  .md\:mb-0 {
+    margin-bottom: 0;
+  }
+
+  .md\:ml-0 {
+    margin-left: 0;
+  }
+
+  .md\:mt-1 {
+    margin-top: 0.25rem;
+  }
+
+  .md\:mr-1 {
+    margin-right: 0.25rem;
+  }
+
+  .md\:mb-1 {
+    margin-bottom: 0.25rem;
+  }
+
+  .md\:ml-1 {
+    margin-left: 0.25rem;
+  }
+
+  .md\:mt-2 {
+    margin-top: 0.5rem;
+  }
+
+  .md\:mr-2 {
+    margin-right: 0.5rem;
+  }
+
+  .md\:mb-2 {
+    margin-bottom: 0.5rem;
+  }
+
+  .md\:ml-2 {
+    margin-left: 0.5rem;
+  }
+
+  .md\:mt-3 {
+    margin-top: 0.75rem;
+  }
+
+  .md\:mr-3 {
+    margin-right: 0.75rem;
+  }
+
+  .md\:mb-3 {
+    margin-bottom: 0.75rem;
+  }
+
+  .md\:ml-3 {
+    margin-left: 0.75rem;
+  }
+
+  .md\:mt-4 {
+    margin-top: 1rem;
+  }
+
+  .md\:mr-4 {
+    margin-right: 1rem;
+  }
+
+  .md\:mb-4 {
+    margin-bottom: 1rem;
+  }
+
+  .md\:ml-4 {
+    margin-left: 1rem;
+  }
+
+  .md\:mt-5 {
+    margin-top: 1.25rem;
+  }
+
+  .md\:mr-5 {
+    margin-right: 1.25rem;
+  }
+
+  .md\:mb-5 {
+    margin-bottom: 1.25rem;
+  }
+
+  .md\:ml-5 {
+    margin-left: 1.25rem;
+  }
+
+  .md\:mt-6 {
+    margin-top: 1.5rem;
+  }
+
+  .md\:mr-6 {
+    margin-right: 1.5rem;
+  }
+
+  .md\:mb-6 {
+    margin-bottom: 1.5rem;
+  }
+
+  .md\:ml-6 {
+    margin-left: 1.5rem;
+  }
+
+  .md\:mt-8 {
+    margin-top: 2rem;
+  }
+
+  .md\:mr-8 {
+    margin-right: 2rem;
+  }
+
+  .md\:mb-8 {
+    margin-bottom: 2rem;
+  }
+
+  .md\:ml-8 {
+    margin-left: 2rem;
+  }
+
+  .md\:mt-10 {
+    margin-top: 2.5rem;
+  }
+
+  .md\:mr-10 {
+    margin-right: 2.5rem;
+  }
+
+  .md\:mb-10 {
+    margin-bottom: 2.5rem;
+  }
+
+  .md\:ml-10 {
+    margin-left: 2.5rem;
+  }
+
+  .md\:mt-12 {
+    margin-top: 3rem;
+  }
+
+  .md\:mr-12 {
+    margin-right: 3rem;
+  }
+
+  .md\:mb-12 {
+    margin-bottom: 3rem;
+  }
+
+  .md\:ml-12 {
+    margin-left: 3rem;
+  }
+
+  .md\:mt-16 {
+    margin-top: 4rem;
+  }
+
+  .md\:mr-16 {
+    margin-right: 4rem;
+  }
+
+  .md\:mb-16 {
+    margin-bottom: 4rem;
+  }
+
+  .md\:ml-16 {
+    margin-left: 4rem;
+  }
+
+  .md\:mt-20 {
+    margin-top: 5rem;
+  }
+
+  .md\:mr-20 {
+    margin-right: 5rem;
+  }
+
+  .md\:mb-20 {
+    margin-bottom: 5rem;
+  }
+
+  .md\:ml-20 {
+    margin-left: 5rem;
+  }
+
+  .md\:mt-24 {
+    margin-top: 6rem;
+  }
+
+  .md\:mr-24 {
+    margin-right: 6rem;
+  }
+
+  .md\:mb-24 {
+    margin-bottom: 6rem;
+  }
+
+  .md\:ml-24 {
+    margin-left: 6rem;
+  }
+
+  .md\:mt-32 {
+    margin-top: 8rem;
+  }
+
+  .md\:mr-32 {
+    margin-right: 8rem;
+  }
+
+  .md\:mb-32 {
+    margin-bottom: 8rem;
+  }
+
+  .md\:ml-32 {
+    margin-left: 8rem;
+  }
+
+  .md\:mt-40 {
+    margin-top: 10rem;
+  }
+
+  .md\:mr-40 {
+    margin-right: 10rem;
+  }
+
+  .md\:mb-40 {
+    margin-bottom: 10rem;
+  }
+
+  .md\:ml-40 {
+    margin-left: 10rem;
+  }
+
+  .md\:mt-48 {
+    margin-top: 12rem;
+  }
+
+  .md\:mr-48 {
+    margin-right: 12rem;
+  }
+
+  .md\:mb-48 {
+    margin-bottom: 12rem;
+  }
+
+  .md\:ml-48 {
+    margin-left: 12rem;
+  }
+
+  .md\:mt-56 {
+    margin-top: 14rem;
+  }
+
+  .md\:mr-56 {
+    margin-right: 14rem;
+  }
+
+  .md\:mb-56 {
+    margin-bottom: 14rem;
+  }
+
+  .md\:ml-56 {
+    margin-left: 14rem;
+  }
+
+  .md\:mt-64 {
+    margin-top: 16rem;
+  }
+
+  .md\:mr-64 {
+    margin-right: 16rem;
+  }
+
+  .md\:mb-64 {
+    margin-bottom: 16rem;
+  }
+
+  .md\:ml-64 {
+    margin-left: 16rem;
+  }
+
+  .md\:mt-auto {
+    margin-top: auto;
+  }
+
+  .md\:mr-auto {
+    margin-right: auto;
+  }
+
+  .md\:mb-auto {
+    margin-bottom: auto;
+  }
+
+  .md\:ml-auto {
+    margin-left: auto;
+  }
+
+  .md\:mt-px {
+    margin-top: 1px;
+  }
+
+  .md\:mr-px {
+    margin-right: 1px;
+  }
+
+  .md\:mb-px {
+    margin-bottom: 1px;
+  }
+
+  .md\:ml-px {
+    margin-left: 1px;
+  }
+
+  .md\:-mt-1 {
+    margin-top: -0.25rem;
+  }
+
+  .md\:-mr-1 {
+    margin-right: -0.25rem;
+  }
+
+  .md\:-mb-1 {
+    margin-bottom: -0.25rem;
+  }
+
+  .md\:-ml-1 {
+    margin-left: -0.25rem;
+  }
+
+  .md\:-mt-2 {
+    margin-top: -0.5rem;
+  }
+
+  .md\:-mr-2 {
+    margin-right: -0.5rem;
+  }
+
+  .md\:-mb-2 {
+    margin-bottom: -0.5rem;
+  }
+
+  .md\:-ml-2 {
+    margin-left: -0.5rem;
+  }
+
+  .md\:-mt-3 {
+    margin-top: -0.75rem;
+  }
+
+  .md\:-mr-3 {
+    margin-right: -0.75rem;
+  }
+
+  .md\:-mb-3 {
+    margin-bottom: -0.75rem;
+  }
+
+  .md\:-ml-3 {
+    margin-left: -0.75rem;
+  }
+
+  .md\:-mt-4 {
+    margin-top: -1rem;
+  }
+
+  .md\:-mr-4 {
+    margin-right: -1rem;
+  }
+
+  .md\:-mb-4 {
+    margin-bottom: -1rem;
+  }
+
+  .md\:-ml-4 {
+    margin-left: -1rem;
+  }
+
+  .md\:-mt-5 {
+    margin-top: -1.25rem;
+  }
+
+  .md\:-mr-5 {
+    margin-right: -1.25rem;
+  }
+
+  .md\:-mb-5 {
+    margin-bottom: -1.25rem;
+  }
+
+  .md\:-ml-5 {
+    margin-left: -1.25rem;
+  }
+
+  .md\:-mt-6 {
+    margin-top: -1.5rem;
+  }
+
+  .md\:-mr-6 {
+    margin-right: -1.5rem;
+  }
+
+  .md\:-mb-6 {
+    margin-bottom: -1.5rem;
+  }
+
+  .md\:-ml-6 {
+    margin-left: -1.5rem;
+  }
+
+  .md\:-mt-8 {
+    margin-top: -2rem;
+  }
+
+  .md\:-mr-8 {
+    margin-right: -2rem;
+  }
+
+  .md\:-mb-8 {
+    margin-bottom: -2rem;
+  }
+
+  .md\:-ml-8 {
+    margin-left: -2rem;
+  }
+
+  .md\:-mt-10 {
+    margin-top: -2.5rem;
+  }
+
+  .md\:-mr-10 {
+    margin-right: -2.5rem;
+  }
+
+  .md\:-mb-10 {
+    margin-bottom: -2.5rem;
+  }
+
+  .md\:-ml-10 {
+    margin-left: -2.5rem;
+  }
+
+  .md\:-mt-12 {
+    margin-top: -3rem;
+  }
+
+  .md\:-mr-12 {
+    margin-right: -3rem;
+  }
+
+  .md\:-mb-12 {
+    margin-bottom: -3rem;
+  }
+
+  .md\:-ml-12 {
+    margin-left: -3rem;
+  }
+
+  .md\:-mt-16 {
+    margin-top: -4rem;
+  }
+
+  .md\:-mr-16 {
+    margin-right: -4rem;
+  }
+
+  .md\:-mb-16 {
+    margin-bottom: -4rem;
+  }
+
+  .md\:-ml-16 {
+    margin-left: -4rem;
+  }
+
+  .md\:-mt-20 {
+    margin-top: -5rem;
+  }
+
+  .md\:-mr-20 {
+    margin-right: -5rem;
+  }
+
+  .md\:-mb-20 {
+    margin-bottom: -5rem;
+  }
+
+  .md\:-ml-20 {
+    margin-left: -5rem;
+  }
+
+  .md\:-mt-24 {
+    margin-top: -6rem;
+  }
+
+  .md\:-mr-24 {
+    margin-right: -6rem;
+  }
+
+  .md\:-mb-24 {
+    margin-bottom: -6rem;
+  }
+
+  .md\:-ml-24 {
+    margin-left: -6rem;
+  }
+
+  .md\:-mt-32 {
+    margin-top: -8rem;
+  }
+
+  .md\:-mr-32 {
+    margin-right: -8rem;
+  }
+
+  .md\:-mb-32 {
+    margin-bottom: -8rem;
+  }
+
+  .md\:-ml-32 {
+    margin-left: -8rem;
+  }
+
+  .md\:-mt-40 {
+    margin-top: -10rem;
+  }
+
+  .md\:-mr-40 {
+    margin-right: -10rem;
+  }
+
+  .md\:-mb-40 {
+    margin-bottom: -10rem;
+  }
+
+  .md\:-ml-40 {
+    margin-left: -10rem;
+  }
+
+  .md\:-mt-48 {
+    margin-top: -12rem;
+  }
+
+  .md\:-mr-48 {
+    margin-right: -12rem;
+  }
+
+  .md\:-mb-48 {
+    margin-bottom: -12rem;
+  }
+
+  .md\:-ml-48 {
+    margin-left: -12rem;
+  }
+
+  .md\:-mt-56 {
+    margin-top: -14rem;
+  }
+
+  .md\:-mr-56 {
+    margin-right: -14rem;
+  }
+
+  .md\:-mb-56 {
+    margin-bottom: -14rem;
+  }
+
+  .md\:-ml-56 {
+    margin-left: -14rem;
+  }
+
+  .md\:-mt-64 {
+    margin-top: -16rem;
+  }
+
+  .md\:-mr-64 {
+    margin-right: -16rem;
+  }
+
+  .md\:-mb-64 {
+    margin-bottom: -16rem;
+  }
+
+  .md\:-ml-64 {
+    margin-left: -16rem;
+  }
+
+  .md\:-mt-px {
+    margin-top: -1px;
+  }
+
+  .md\:-mr-px {
+    margin-right: -1px;
+  }
+
+  .md\:-mb-px {
+    margin-bottom: -1px;
+  }
+
+  .md\:-ml-px {
+    margin-left: -1px;
+  }
+
+  .md\:max-h-full {
+    max-height: 100%;
+  }
+
+  .md\:max-h-screen {
+    max-height: 100vh;
+  }
+
+  .md\:max-w-none {
+    max-width: none;
+  }
+
+  .md\:max-w-xs {
+    max-width: 20rem;
+  }
+
+  .md\:max-w-sm {
+    max-width: 24rem;
+  }
+
+  .md\:max-w-md {
+    max-width: 28rem;
+  }
+
+  .md\:max-w-lg {
+    max-width: 32rem;
+  }
+
+  .md\:max-w-xl {
+    max-width: 36rem;
+  }
+
+  .md\:max-w-2xl {
+    max-width: 42rem;
+  }
+
+  .md\:max-w-3xl {
+    max-width: 48rem;
+  }
+
+  .md\:max-w-4xl {
+    max-width: 56rem;
+  }
+
+  .md\:max-w-5xl {
+    max-width: 64rem;
+  }
+
+  .md\:max-w-6xl {
+    max-width: 72rem;
+  }
+
+  .md\:max-w-full {
+    max-width: 100%;
+  }
+
+  .md\:max-w-screen-sm {
+    max-width: 640px;
+  }
+
+  .md\:max-w-screen-md {
+    max-width: 768px;
+  }
+
+  .md\:max-w-screen-lg {
+    max-width: 1024px;
+  }
+
+  .md\:max-w-screen-xl {
+    max-width: 1280px;
+  }
+
+  .md\:min-h-0 {
+    min-height: 0;
+  }
+
+  .md\:min-h-full {
+    min-height: 100%;
+  }
+
+  .md\:min-h-screen {
+    min-height: 100vh;
+  }
+
+  .md\:min-w-0 {
+    min-width: 0;
+  }
+
+  .md\:min-w-full {
+    min-width: 100%;
+  }
+
+  .md\:object-contain {
+    -o-object-fit: contain;
+       object-fit: contain;
+  }
+
+  .md\:object-cover {
+    -o-object-fit: cover;
+       object-fit: cover;
+  }
+
+  .md\:object-fill {
+    -o-object-fit: fill;
+       object-fit: fill;
+  }
+
+  .md\:object-none {
+    -o-object-fit: none;
+       object-fit: none;
+  }
+
+  .md\:object-scale-down {
+    -o-object-fit: scale-down;
+       object-fit: scale-down;
+  }
+
+  .md\:object-bottom {
+    -o-object-position: bottom;
+       object-position: bottom;
+  }
+
+  .md\:object-center {
+    -o-object-position: center;
+       object-position: center;
+  }
+
+  .md\:object-left {
+    -o-object-position: left;
+       object-position: left;
+  }
+
+  .md\:object-left-bottom {
+    -o-object-position: left bottom;
+       object-position: left bottom;
+  }
+
+  .md\:object-left-top {
+    -o-object-position: left top;
+       object-position: left top;
+  }
+
+  .md\:object-right {
+    -o-object-position: right;
+       object-position: right;
+  }
+
+  .md\:object-right-bottom {
+    -o-object-position: right bottom;
+       object-position: right bottom;
+  }
+
+  .md\:object-right-top {
+    -o-object-position: right top;
+       object-position: right top;
+  }
+
+  .md\:object-top {
+    -o-object-position: top;
+       object-position: top;
+  }
+
+  .md\:opacity-0 {
+    opacity: 0;
+  }
+
+  .md\:opacity-25 {
+    opacity: 0.25;
+  }
+
+  .md\:opacity-50 {
+    opacity: 0.5;
+  }
+
+  .md\:opacity-75 {
+    opacity: 0.75;
+  }
+
+  .md\:opacity-100 {
+    opacity: 1;
+  }
+
+  .md\:hover\:opacity-0:hover {
+    opacity: 0;
+  }
+
+  .md\:hover\:opacity-25:hover {
+    opacity: 0.25;
+  }
+
+  .md\:hover\:opacity-50:hover {
+    opacity: 0.5;
+  }
+
+  .md\:hover\:opacity-75:hover {
+    opacity: 0.75;
+  }
+
+  .md\:hover\:opacity-100:hover {
+    opacity: 1;
+  }
+
+  .md\:focus\:opacity-0:focus {
+    opacity: 0;
+  }
+
+  .md\:focus\:opacity-25:focus {
+    opacity: 0.25;
+  }
+
+  .md\:focus\:opacity-50:focus {
+    opacity: 0.5;
+  }
+
+  .md\:focus\:opacity-75:focus {
+    opacity: 0.75;
+  }
+
+  .md\:focus\:opacity-100:focus {
+    opacity: 1;
+  }
+
+  .md\:outline-none {
+    outline: 0;
+  }
+
+  .md\:focus\:outline-none:focus {
+    outline: 0;
+  }
+
+  .md\:overflow-auto {
+    overflow: auto;
+  }
+
+  .md\:overflow-hidden {
+    overflow: hidden;
+  }
+
+  .md\:overflow-visible {
+    overflow: visible;
+  }
+
+  .md\:overflow-scroll {
+    overflow: scroll;
+  }
+
+  .md\:overflow-x-auto {
+    overflow-x: auto;
+  }
+
+  .md\:overflow-y-auto {
+    overflow-y: auto;
+  }
+
+  .md\:overflow-x-hidden {
+    overflow-x: hidden;
+  }
+
+  .md\:overflow-y-hidden {
+    overflow-y: hidden;
+  }
+
+  .md\:overflow-x-visible {
+    overflow-x: visible;
+  }
+
+  .md\:overflow-y-visible {
+    overflow-y: visible;
+  }
+
+  .md\:overflow-x-scroll {
+    overflow-x: scroll;
+  }
+
+  .md\:overflow-y-scroll {
+    overflow-y: scroll;
+  }
+
+  .md\:scrolling-touch {
+    -webkit-overflow-scrolling: touch;
+  }
+
+  .md\:scrolling-auto {
+    -webkit-overflow-scrolling: auto;
+  }
+
+  .md\:overscroll-auto {
+    -ms-scroll-chaining: chained;
+        overscroll-behavior: auto;
+  }
+
+  .md\:overscroll-contain {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: contain;
+  }
+
+  .md\:overscroll-none {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: none;
+  }
+
+  .md\:overscroll-y-auto {
+    overscroll-behavior-y: auto;
+  }
+
+  .md\:overscroll-y-contain {
+    overscroll-behavior-y: contain;
+  }
+
+  .md\:overscroll-y-none {
+    overscroll-behavior-y: none;
+  }
+
+  .md\:overscroll-x-auto {
+    overscroll-behavior-x: auto;
+  }
+
+  .md\:overscroll-x-contain {
+    overscroll-behavior-x: contain;
+  }
+
+  .md\:overscroll-x-none {
+    overscroll-behavior-x: none;
+  }
+
+  .md\:p-0 {
+    padding: 0;
+  }
+
+  .md\:p-1 {
+    padding: 0.25rem;
+  }
+
+  .md\:p-2 {
+    padding: 0.5rem;
+  }
+
+  .md\:p-3 {
+    padding: 0.75rem;
+  }
+
+  .md\:p-4 {
+    padding: 1rem;
+  }
+
+  .md\:p-5 {
+    padding: 1.25rem;
+  }
+
+  .md\:p-6 {
+    padding: 1.5rem;
+  }
+
+  .md\:p-8 {
+    padding: 2rem;
+  }
+
+  .md\:p-10 {
+    padding: 2.5rem;
+  }
+
+  .md\:p-12 {
+    padding: 3rem;
+  }
+
+  .md\:p-16 {
+    padding: 4rem;
+  }
+
+  .md\:p-20 {
+    padding: 5rem;
+  }
+
+  .md\:p-24 {
+    padding: 6rem;
+  }
+
+  .md\:p-32 {
+    padding: 8rem;
+  }
+
+  .md\:p-40 {
+    padding: 10rem;
+  }
+
+  .md\:p-48 {
+    padding: 12rem;
+  }
+
+  .md\:p-56 {
+    padding: 14rem;
+  }
+
+  .md\:p-64 {
+    padding: 16rem;
+  }
+
+  .md\:p-px {
+    padding: 1px;
+  }
+
+  .md\:py-0 {
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+
+  .md\:px-0 {
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  .md\:py-1 {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+  }
+
+  .md\:px-1 {
+    padding-left: 0.25rem;
+    padding-right: 0.25rem;
+  }
+
+  .md\:py-2 {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
+
+  .md\:px-2 {
+    padding-left: 0.5rem;
+    padding-right: 0.5rem;
+  }
+
+  .md\:py-3 {
+    padding-top: 0.75rem;
+    padding-bottom: 0.75rem;
+  }
+
+  .md\:px-3 {
+    padding-left: 0.75rem;
+    padding-right: 0.75rem;
+  }
+
+  .md\:py-4 {
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+  }
+
+  .md\:px-4 {
+    padding-left: 1rem;
+    padding-right: 1rem;
+  }
+
+  .md\:py-5 {
+    padding-top: 1.25rem;
+    padding-bottom: 1.25rem;
+  }
+
+  .md\:px-5 {
+    padding-left: 1.25rem;
+    padding-right: 1.25rem;
+  }
+
+  .md\:py-6 {
+    padding-top: 1.5rem;
+    padding-bottom: 1.5rem;
+  }
+
+  .md\:px-6 {
+    padding-left: 1.5rem;
+    padding-right: 1.5rem;
+  }
+
+  .md\:py-8 {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
+
+  .md\:px-8 {
+    padding-left: 2rem;
+    padding-right: 2rem;
+  }
+
+  .md\:py-10 {
+    padding-top: 2.5rem;
+    padding-bottom: 2.5rem;
+  }
+
+  .md\:px-10 {
+    padding-left: 2.5rem;
+    padding-right: 2.5rem;
+  }
+
+  .md\:py-12 {
+    padding-top: 3rem;
+    padding-bottom: 3rem;
+  }
+
+  .md\:px-12 {
+    padding-left: 3rem;
+    padding-right: 3rem;
+  }
+
+  .md\:py-16 {
+    padding-top: 4rem;
+    padding-bottom: 4rem;
+  }
+
+  .md\:px-16 {
+    padding-left: 4rem;
+    padding-right: 4rem;
+  }
+
+  .md\:py-20 {
+    padding-top: 5rem;
+    padding-bottom: 5rem;
+  }
+
+  .md\:px-20 {
+    padding-left: 5rem;
+    padding-right: 5rem;
+  }
+
+  .md\:py-24 {
+    padding-top: 6rem;
+    padding-bottom: 6rem;
+  }
+
+  .md\:px-24 {
+    padding-left: 6rem;
+    padding-right: 6rem;
+  }
+
+  .md\:py-32 {
+    padding-top: 8rem;
+    padding-bottom: 8rem;
+  }
+
+  .md\:px-32 {
+    padding-left: 8rem;
+    padding-right: 8rem;
+  }
+
+  .md\:py-40 {
+    padding-top: 10rem;
+    padding-bottom: 10rem;
+  }
+
+  .md\:px-40 {
+    padding-left: 10rem;
+    padding-right: 10rem;
+  }
+
+  .md\:py-48 {
+    padding-top: 12rem;
+    padding-bottom: 12rem;
+  }
+
+  .md\:px-48 {
+    padding-left: 12rem;
+    padding-right: 12rem;
+  }
+
+  .md\:py-56 {
+    padding-top: 14rem;
+    padding-bottom: 14rem;
+  }
+
+  .md\:px-56 {
+    padding-left: 14rem;
+    padding-right: 14rem;
+  }
+
+  .md\:py-64 {
+    padding-top: 16rem;
+    padding-bottom: 16rem;
+  }
+
+  .md\:px-64 {
+    padding-left: 16rem;
+    padding-right: 16rem;
+  }
+
+  .md\:py-px {
+    padding-top: 1px;
+    padding-bottom: 1px;
+  }
+
+  .md\:px-px {
+    padding-left: 1px;
+    padding-right: 1px;
+  }
+
+  .md\:pt-0 {
+    padding-top: 0;
+  }
+
+  .md\:pr-0 {
+    padding-right: 0;
+  }
+
+  .md\:pb-0 {
+    padding-bottom: 0;
+  }
+
+  .md\:pl-0 {
+    padding-left: 0;
+  }
+
+  .md\:pt-1 {
+    padding-top: 0.25rem;
+  }
+
+  .md\:pr-1 {
+    padding-right: 0.25rem;
+  }
+
+  .md\:pb-1 {
+    padding-bottom: 0.25rem;
+  }
+
+  .md\:pl-1 {
+    padding-left: 0.25rem;
+  }
+
+  .md\:pt-2 {
+    padding-top: 0.5rem;
+  }
+
+  .md\:pr-2 {
+    padding-right: 0.5rem;
+  }
+
+  .md\:pb-2 {
+    padding-bottom: 0.5rem;
+  }
+
+  .md\:pl-2 {
+    padding-left: 0.5rem;
+  }
+
+  .md\:pt-3 {
+    padding-top: 0.75rem;
+  }
+
+  .md\:pr-3 {
+    padding-right: 0.75rem;
+  }
+
+  .md\:pb-3 {
+    padding-bottom: 0.75rem;
+  }
+
+  .md\:pl-3 {
+    padding-left: 0.75rem;
+  }
+
+  .md\:pt-4 {
+    padding-top: 1rem;
+  }
+
+  .md\:pr-4 {
+    padding-right: 1rem;
+  }
+
+  .md\:pb-4 {
+    padding-bottom: 1rem;
+  }
+
+  .md\:pl-4 {
+    padding-left: 1rem;
+  }
+
+  .md\:pt-5 {
+    padding-top: 1.25rem;
+  }
+
+  .md\:pr-5 {
+    padding-right: 1.25rem;
+  }
+
+  .md\:pb-5 {
+    padding-bottom: 1.25rem;
+  }
+
+  .md\:pl-5 {
+    padding-left: 1.25rem;
+  }
+
+  .md\:pt-6 {
+    padding-top: 1.5rem;
+  }
+
+  .md\:pr-6 {
+    padding-right: 1.5rem;
+  }
+
+  .md\:pb-6 {
+    padding-bottom: 1.5rem;
+  }
+
+  .md\:pl-6 {
+    padding-left: 1.5rem;
+  }
+
+  .md\:pt-8 {
+    padding-top: 2rem;
+  }
+
+  .md\:pr-8 {
+    padding-right: 2rem;
+  }
+
+  .md\:pb-8 {
+    padding-bottom: 2rem;
+  }
+
+  .md\:pl-8 {
+    padding-left: 2rem;
+  }
+
+  .md\:pt-10 {
+    padding-top: 2.5rem;
+  }
+
+  .md\:pr-10 {
+    padding-right: 2.5rem;
+  }
+
+  .md\:pb-10 {
+    padding-bottom: 2.5rem;
+  }
+
+  .md\:pl-10 {
+    padding-left: 2.5rem;
+  }
+
+  .md\:pt-12 {
+    padding-top: 3rem;
+  }
+
+  .md\:pr-12 {
+    padding-right: 3rem;
+  }
+
+  .md\:pb-12 {
+    padding-bottom: 3rem;
+  }
+
+  .md\:pl-12 {
+    padding-left: 3rem;
+  }
+
+  .md\:pt-16 {
+    padding-top: 4rem;
+  }
+
+  .md\:pr-16 {
+    padding-right: 4rem;
+  }
+
+  .md\:pb-16 {
+    padding-bottom: 4rem;
+  }
+
+  .md\:pl-16 {
+    padding-left: 4rem;
+  }
+
+  .md\:pt-20 {
+    padding-top: 5rem;
+  }
+
+  .md\:pr-20 {
+    padding-right: 5rem;
+  }
+
+  .md\:pb-20 {
+    padding-bottom: 5rem;
+  }
+
+  .md\:pl-20 {
+    padding-left: 5rem;
+  }
+
+  .md\:pt-24 {
+    padding-top: 6rem;
+  }
+
+  .md\:pr-24 {
+    padding-right: 6rem;
+  }
+
+  .md\:pb-24 {
+    padding-bottom: 6rem;
+  }
+
+  .md\:pl-24 {
+    padding-left: 6rem;
+  }
+
+  .md\:pt-32 {
+    padding-top: 8rem;
+  }
+
+  .md\:pr-32 {
+    padding-right: 8rem;
+  }
+
+  .md\:pb-32 {
+    padding-bottom: 8rem;
+  }
+
+  .md\:pl-32 {
+    padding-left: 8rem;
+  }
+
+  .md\:pt-40 {
+    padding-top: 10rem;
+  }
+
+  .md\:pr-40 {
+    padding-right: 10rem;
+  }
+
+  .md\:pb-40 {
+    padding-bottom: 10rem;
+  }
+
+  .md\:pl-40 {
+    padding-left: 10rem;
+  }
+
+  .md\:pt-48 {
+    padding-top: 12rem;
+  }
+
+  .md\:pr-48 {
+    padding-right: 12rem;
+  }
+
+  .md\:pb-48 {
+    padding-bottom: 12rem;
+  }
+
+  .md\:pl-48 {
+    padding-left: 12rem;
+  }
+
+  .md\:pt-56 {
+    padding-top: 14rem;
+  }
+
+  .md\:pr-56 {
+    padding-right: 14rem;
+  }
+
+  .md\:pb-56 {
+    padding-bottom: 14rem;
+  }
+
+  .md\:pl-56 {
+    padding-left: 14rem;
+  }
+
+  .md\:pt-64 {
+    padding-top: 16rem;
+  }
+
+  .md\:pr-64 {
+    padding-right: 16rem;
+  }
+
+  .md\:pb-64 {
+    padding-bottom: 16rem;
+  }
+
+  .md\:pl-64 {
+    padding-left: 16rem;
+  }
+
+  .md\:pt-px {
+    padding-top: 1px;
+  }
+
+  .md\:pr-px {
+    padding-right: 1px;
+  }
+
+  .md\:pb-px {
+    padding-bottom: 1px;
+  }
+
+  .md\:pl-px {
+    padding-left: 1px;
+  }
+
+  .md\:placeholder-transparent::-moz-placeholder {
+    color: transparent;
+  }
+
+  .md\:placeholder-transparent:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .md\:placeholder-transparent::placeholder {
+    color: transparent;
+  }
+
+  .md\:placeholder-current::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .md\:placeholder-current:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .md\:placeholder-current::placeholder {
+    color: currentColor;
+  }
+
+  .md\:placeholder-black::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-black:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-black::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-white::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-white:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-white::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-gray-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-red-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-orange-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-yellow-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-green-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-teal-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-blue-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-indigo-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-purple-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-pink-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-transparent:focus::-moz-placeholder {
+    color: transparent;
+  }
+
+  .md\:focus\:placeholder-transparent:focus:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .md\:focus\:placeholder-transparent:focus::placeholder {
+    color: transparent;
+  }
+
+  .md\:focus\:placeholder-current:focus::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .md\:focus\:placeholder-current:focus:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .md\:focus\:placeholder-current:focus::placeholder {
+    color: currentColor;
+  }
+
+  .md\:focus\:placeholder-black:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-black:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-black:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-white:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-white:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-white:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-gray-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-red-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-orange-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-yellow-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-green-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-teal-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-blue-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-indigo-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-purple-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:focus\:placeholder-pink-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .md\:placeholder-opacity-0::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:placeholder-opacity-0:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:placeholder-opacity-0::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:placeholder-opacity-25::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:placeholder-opacity-25:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:placeholder-opacity-25::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:placeholder-opacity-50::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:placeholder-opacity-50:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:placeholder-opacity-50::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:placeholder-opacity-75::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:placeholder-opacity-75:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:placeholder-opacity-75::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:placeholder-opacity-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:placeholder-opacity-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:placeholder-opacity-100::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:focus\:placeholder-opacity-0:focus::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:focus\:placeholder-opacity-0:focus::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .md\:focus\:placeholder-opacity-25:focus::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:focus\:placeholder-opacity-25:focus::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .md\:focus\:placeholder-opacity-50:focus::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:focus\:placeholder-opacity-50:focus::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .md\:focus\:placeholder-opacity-75:focus::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:focus\:placeholder-opacity-75:focus::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .md\:focus\:placeholder-opacity-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:focus\:placeholder-opacity-100:focus::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .md\:pointer-events-none {
+    pointer-events: none;
+  }
+
+  .md\:pointer-events-auto {
+    pointer-events: auto;
+  }
+
+  .md\:static {
+    position: static;
+  }
+
+  .md\:fixed {
+    position: fixed;
+  }
+
+  .md\:absolute {
+    position: absolute;
+  }
+
+  .md\:relative {
+    position: relative;
+  }
+
+  .md\:sticky {
+    position: -webkit-sticky;
+    position: sticky;
+  }
+
+  .md\:inset-0 {
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+  }
+
+  .md\:inset-auto {
+    top: auto;
+    right: auto;
+    bottom: auto;
+    left: auto;
+  }
+
+  .md\:inset-y-0 {
+    top: 0;
+    bottom: 0;
+  }
+
+  .md\:inset-x-0 {
+    right: 0;
+    left: 0;
+  }
+
+  .md\:inset-y-auto {
+    top: auto;
+    bottom: auto;
+  }
+
+  .md\:inset-x-auto {
+    right: auto;
+    left: auto;
+  }
+
+  .md\:top-0 {
+    top: 0;
+  }
+
+  .md\:right-0 {
+    right: 0;
+  }
+
+  .md\:bottom-0 {
+    bottom: 0;
+  }
+
+  .md\:left-0 {
+    left: 0;
+  }
+
+  .md\:top-auto {
+    top: auto;
+  }
+
+  .md\:right-auto {
+    right: auto;
+  }
+
+  .md\:bottom-auto {
+    bottom: auto;
+  }
+
+  .md\:left-auto {
+    left: auto;
+  }
+
+  .md\:resize-none {
+    resize: none;
+  }
+
+  .md\:resize-y {
+    resize: vertical;
+  }
+
+  .md\:resize-x {
+    resize: horizontal;
+  }
+
+  .md\:resize {
+    resize: both;
+  }
+
+  .md\:shadow-xs {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:shadow-sm {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:shadow {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:shadow-md {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:shadow-lg {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:shadow-xl {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .md\:shadow-2xl {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .md\:shadow-inner {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:shadow-outline {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .md\:shadow-none {
+    box-shadow: none;
+  }
+
+  .md\:hover\:shadow-xs:hover {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:hover\:shadow-sm:hover {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:hover\:shadow:hover {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:hover\:shadow-md:hover {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:hover\:shadow-lg:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:hover\:shadow-xl:hover {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .md\:hover\:shadow-2xl:hover {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .md\:hover\:shadow-inner:hover {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:hover\:shadow-outline:hover {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .md\:hover\:shadow-none:hover {
+    box-shadow: none;
+  }
+
+  .md\:focus\:shadow-xs:focus {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:focus\:shadow-sm:focus {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:focus\:shadow:focus {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:focus\:shadow-md:focus {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:focus\:shadow-lg:focus {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .md\:focus\:shadow-xl:focus {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .md\:focus\:shadow-2xl:focus {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .md\:focus\:shadow-inner:focus {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .md\:focus\:shadow-outline:focus {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .md\:focus\:shadow-none:focus {
+    box-shadow: none;
+  }
+
+  .md\:fill-current {
+    fill: currentColor;
+  }
+
+  .md\:stroke-current {
+    stroke: currentColor;
+  }
+
+  .md\:stroke-0 {
+    stroke-width: 0;
+  }
+
+  .md\:stroke-1 {
+    stroke-width: 1;
+  }
+
+  .md\:stroke-2 {
+    stroke-width: 2;
+  }
+
+  .md\:table-auto {
+    table-layout: auto;
+  }
+
+  .md\:table-fixed {
+    table-layout: fixed;
+  }
+
+  .md\:text-left {
+    text-align: left;
+  }
+
+  .md\:text-center {
+    text-align: center;
+  }
+
+  .md\:text-right {
+    text-align: right;
+  }
+
+  .md\:text-justify {
+    text-align: justify;
+  }
+
+  .md\:text-transparent {
+    color: transparent;
+  }
+
+  .md\:text-current {
+    color: currentColor;
+  }
+
+  .md\:text-black {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .md\:text-white {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .md\:text-gray-100 {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .md\:text-gray-200 {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .md\:text-gray-300 {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .md\:text-gray-400 {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .md\:text-gray-500 {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .md\:text-gray-600 {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .md\:text-gray-700 {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .md\:text-gray-800 {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .md\:text-gray-900 {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .md\:text-red-100 {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .md\:text-red-200 {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .md\:text-red-300 {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .md\:text-red-400 {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .md\:text-red-500 {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .md\:text-red-600 {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .md\:text-red-700 {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .md\:text-red-800 {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .md\:text-red-900 {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .md\:text-orange-100 {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .md\:text-orange-200 {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .md\:text-orange-300 {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .md\:text-orange-400 {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .md\:text-orange-500 {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .md\:text-orange-600 {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .md\:text-orange-700 {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .md\:text-orange-800 {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .md\:text-orange-900 {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .md\:text-yellow-100 {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .md\:text-yellow-200 {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .md\:text-yellow-300 {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .md\:text-yellow-400 {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .md\:text-yellow-500 {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .md\:text-yellow-600 {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .md\:text-yellow-700 {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .md\:text-yellow-800 {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .md\:text-yellow-900 {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .md\:text-green-100 {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .md\:text-green-200 {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .md\:text-green-300 {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .md\:text-green-400 {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .md\:text-green-500 {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .md\:text-green-600 {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .md\:text-green-700 {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .md\:text-green-800 {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .md\:text-green-900 {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .md\:text-teal-100 {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .md\:text-teal-200 {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .md\:text-teal-300 {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .md\:text-teal-400 {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .md\:text-teal-500 {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .md\:text-teal-600 {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .md\:text-teal-700 {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .md\:text-teal-800 {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .md\:text-teal-900 {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .md\:text-blue-100 {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .md\:text-blue-200 {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .md\:text-blue-300 {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .md\:text-blue-400 {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .md\:text-blue-500 {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .md\:text-blue-600 {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .md\:text-blue-700 {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .md\:text-blue-800 {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .md\:text-blue-900 {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .md\:text-indigo-100 {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .md\:text-indigo-200 {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .md\:text-indigo-300 {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .md\:text-indigo-400 {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .md\:text-indigo-500 {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .md\:text-indigo-600 {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .md\:text-indigo-700 {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .md\:text-indigo-800 {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .md\:text-indigo-900 {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .md\:text-purple-100 {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .md\:text-purple-200 {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .md\:text-purple-300 {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .md\:text-purple-400 {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .md\:text-purple-500 {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .md\:text-purple-600 {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .md\:text-purple-700 {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .md\:text-purple-800 {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .md\:text-purple-900 {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .md\:text-pink-100 {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .md\:text-pink-200 {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .md\:text-pink-300 {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .md\:text-pink-400 {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .md\:text-pink-500 {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .md\:text-pink-600 {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .md\:text-pink-700 {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .md\:text-pink-800 {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .md\:text-pink-900 {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .md\:hover\:text-transparent:hover {
+    color: transparent;
+  }
+
+  .md\:hover\:text-current:hover {
+    color: currentColor;
+  }
+
+  .md\:hover\:text-black:hover {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .md\:hover\:text-white:hover {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-100:hover {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-200:hover {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-300:hover {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-400:hover {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-500:hover {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-600:hover {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-700:hover {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-800:hover {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .md\:hover\:text-gray-900:hover {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-100:hover {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-200:hover {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-300:hover {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-400:hover {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-500:hover {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-600:hover {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-700:hover {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-800:hover {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .md\:hover\:text-red-900:hover {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-100:hover {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-200:hover {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-300:hover {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-400:hover {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-500:hover {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-600:hover {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-700:hover {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-800:hover {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .md\:hover\:text-orange-900:hover {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-100:hover {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-200:hover {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-300:hover {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-400:hover {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-500:hover {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-600:hover {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-700:hover {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-800:hover {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .md\:hover\:text-yellow-900:hover {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-100:hover {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-200:hover {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-300:hover {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-400:hover {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-500:hover {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-600:hover {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-700:hover {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-800:hover {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .md\:hover\:text-green-900:hover {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-100:hover {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-200:hover {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-300:hover {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-400:hover {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-500:hover {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-600:hover {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-700:hover {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-800:hover {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .md\:hover\:text-teal-900:hover {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-100:hover {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-200:hover {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-300:hover {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-400:hover {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-500:hover {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-600:hover {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-700:hover {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-800:hover {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .md\:hover\:text-blue-900:hover {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-100:hover {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-200:hover {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-300:hover {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-400:hover {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-500:hover {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-600:hover {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-700:hover {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-800:hover {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .md\:hover\:text-indigo-900:hover {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-100:hover {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-200:hover {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-300:hover {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-400:hover {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-500:hover {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-600:hover {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-700:hover {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-800:hover {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .md\:hover\:text-purple-900:hover {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-100:hover {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-200:hover {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-300:hover {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-400:hover {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-500:hover {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-600:hover {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-700:hover {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-800:hover {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .md\:hover\:text-pink-900:hover {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .md\:focus\:text-transparent:focus {
+    color: transparent;
+  }
+
+  .md\:focus\:text-current:focus {
+    color: currentColor;
+  }
+
+  .md\:focus\:text-black:focus {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .md\:focus\:text-white:focus {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-100:focus {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-200:focus {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-300:focus {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-400:focus {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-500:focus {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-600:focus {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-700:focus {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-800:focus {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .md\:focus\:text-gray-900:focus {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-100:focus {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-200:focus {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-300:focus {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-400:focus {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-500:focus {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-600:focus {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-700:focus {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-800:focus {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .md\:focus\:text-red-900:focus {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-100:focus {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-200:focus {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-300:focus {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-400:focus {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-500:focus {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-600:focus {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-700:focus {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-800:focus {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .md\:focus\:text-orange-900:focus {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-100:focus {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-200:focus {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-300:focus {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-400:focus {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-500:focus {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-600:focus {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-700:focus {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-800:focus {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .md\:focus\:text-yellow-900:focus {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-100:focus {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-200:focus {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-300:focus {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-400:focus {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-500:focus {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-600:focus {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-700:focus {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-800:focus {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .md\:focus\:text-green-900:focus {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-100:focus {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-200:focus {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-300:focus {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-400:focus {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-500:focus {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-600:focus {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-700:focus {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-800:focus {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .md\:focus\:text-teal-900:focus {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-100:focus {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-200:focus {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-300:focus {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-400:focus {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-500:focus {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-600:focus {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-700:focus {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-800:focus {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .md\:focus\:text-blue-900:focus {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-100:focus {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-200:focus {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-300:focus {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-400:focus {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-500:focus {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-600:focus {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-700:focus {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-800:focus {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .md\:focus\:text-indigo-900:focus {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-100:focus {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-200:focus {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-300:focus {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-400:focus {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-500:focus {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-600:focus {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-700:focus {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-800:focus {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .md\:focus\:text-purple-900:focus {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-100:focus {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-200:focus {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-300:focus {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-400:focus {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-500:focus {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-600:focus {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-700:focus {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-800:focus {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .md\:focus\:text-pink-900:focus {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .md\:text-opacity-0 {
+    --text-opacity: 0;
+  }
+
+  .md\:text-opacity-25 {
+    --text-opacity: 0.25;
+  }
+
+  .md\:text-opacity-50 {
+    --text-opacity: 0.5;
+  }
+
+  .md\:text-opacity-75 {
+    --text-opacity: 0.75;
+  }
+
+  .md\:text-opacity-100 {
+    --text-opacity: 1;
+  }
+
+  .md\:hover\:text-opacity-0:hover {
+    --text-opacity: 0;
+  }
+
+  .md\:hover\:text-opacity-25:hover {
+    --text-opacity: 0.25;
+  }
+
+  .md\:hover\:text-opacity-50:hover {
+    --text-opacity: 0.5;
+  }
+
+  .md\:hover\:text-opacity-75:hover {
+    --text-opacity: 0.75;
+  }
+
+  .md\:hover\:text-opacity-100:hover {
+    --text-opacity: 1;
+  }
+
+  .md\:focus\:text-opacity-0:focus {
+    --text-opacity: 0;
+  }
+
+  .md\:focus\:text-opacity-25:focus {
+    --text-opacity: 0.25;
+  }
+
+  .md\:focus\:text-opacity-50:focus {
+    --text-opacity: 0.5;
+  }
+
+  .md\:focus\:text-opacity-75:focus {
+    --text-opacity: 0.75;
+  }
+
+  .md\:focus\:text-opacity-100:focus {
+    --text-opacity: 1;
+  }
+
+  .md\:italic {
+    font-style: italic;
+  }
+
+  .md\:not-italic {
+    font-style: normal;
+  }
+
+  .md\:uppercase {
+    text-transform: uppercase;
+  }
+
+  .md\:lowercase {
+    text-transform: lowercase;
+  }
+
+  .md\:capitalize {
+    text-transform: capitalize;
+  }
+
+  .md\:normal-case {
+    text-transform: none;
+  }
+
+  .md\:underline {
+    text-decoration: underline;
+  }
+
+  .md\:line-through {
+    text-decoration: line-through;
+  }
+
+  .md\:no-underline {
+    text-decoration: none;
+  }
+
+  .md\:hover\:underline:hover {
+    text-decoration: underline;
+  }
+
+  .md\:hover\:line-through:hover {
+    text-decoration: line-through;
+  }
+
+  .md\:hover\:no-underline:hover {
+    text-decoration: none;
+  }
+
+  .md\:focus\:underline:focus {
+    text-decoration: underline;
+  }
+
+  .md\:focus\:line-through:focus {
+    text-decoration: line-through;
+  }
+
+  .md\:focus\:no-underline:focus {
+    text-decoration: none;
+  }
+
+  .md\:antialiased {
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+
+  .md\:subpixel-antialiased {
+    -webkit-font-smoothing: auto;
+    -moz-osx-font-smoothing: auto;
+  }
+
+  .md\:ordinal, .md\:slashed-zero, .md\:lining-nums, .md\:oldstyle-nums, .md\:proportional-nums, .md\:tabular-nums, .md\:diagonal-fractions, .md\:stacked-fractions {
+    --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+    font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+  }
+
+  .md\:normal-nums {
+    font-variant-numeric: normal;
+  }
+
+  .md\:ordinal {
+    --font-variant-numeric-ordinal: ordinal;
+  }
+
+  .md\:slashed-zero {
+    --font-variant-numeric-slashed-zero: slashed-zero;
+  }
+
+  .md\:lining-nums {
+    --font-variant-numeric-figure: lining-nums;
+  }
+
+  .md\:oldstyle-nums {
+    --font-variant-numeric-figure: oldstyle-nums;
+  }
+
+  .md\:proportional-nums {
+    --font-variant-numeric-spacing: proportional-nums;
+  }
+
+  .md\:tabular-nums {
+    --font-variant-numeric-spacing: tabular-nums;
+  }
+
+  .md\:diagonal-fractions {
+    --font-variant-numeric-fraction: diagonal-fractions;
+  }
+
+  .md\:stacked-fractions {
+    --font-variant-numeric-fraction: stacked-fractions;
+  }
+
+  .md\:tracking-tighter {
+    letter-spacing: -0.05em;
+  }
+
+  .md\:tracking-tight {
+    letter-spacing: -0.025em;
+  }
+
+  .md\:tracking-normal {
+    letter-spacing: 0;
+  }
+
+  .md\:tracking-wide {
+    letter-spacing: 0.025em;
+  }
+
+  .md\:tracking-wider {
+    letter-spacing: 0.05em;
+  }
+
+  .md\:tracking-widest {
+    letter-spacing: 0.1em;
+  }
+
+  .md\:select-none {
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+  }
+
+  .md\:select-text {
+    -webkit-user-select: text;
+       -moz-user-select: text;
+        -ms-user-select: text;
+            user-select: text;
+  }
+
+  .md\:select-all {
+    -webkit-user-select: all;
+       -moz-user-select: all;
+        -ms-user-select: all;
+            user-select: all;
+  }
+
+  .md\:select-auto {
+    -webkit-user-select: auto;
+       -moz-user-select: auto;
+        -ms-user-select: auto;
+            user-select: auto;
+  }
+
+  .md\:align-baseline {
+    vertical-align: baseline;
+  }
+
+  .md\:align-top {
+    vertical-align: top;
+  }
+
+  .md\:align-middle {
+    vertical-align: middle;
+  }
+
+  .md\:align-bottom {
+    vertical-align: bottom;
+  }
+
+  .md\:align-text-top {
+    vertical-align: text-top;
+  }
+
+  .md\:align-text-bottom {
+    vertical-align: text-bottom;
+  }
+
+  .md\:visible {
+    visibility: visible;
+  }
+
+  .md\:invisible {
+    visibility: hidden;
+  }
+
+  .md\:whitespace-normal {
+    white-space: normal;
+  }
+
+  .md\:whitespace-no-wrap {
+    white-space: nowrap;
+  }
+
+  .md\:whitespace-pre {
+    white-space: pre;
+  }
+
+  .md\:whitespace-pre-line {
+    white-space: pre-line;
+  }
+
+  .md\:whitespace-pre-wrap {
+    white-space: pre-wrap;
+  }
+
+  .md\:break-normal {
+    overflow-wrap: normal;
+    word-break: normal;
+  }
+
+  .md\:break-words {
+    overflow-wrap: break-word;
+  }
+
+  .md\:break-all {
+    word-break: break-all;
+  }
+
+  .md\:truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .md\:w-0 {
+    width: 0;
+  }
+
+  .md\:w-1 {
+    width: 0.25rem;
+  }
+
+  .md\:w-2 {
+    width: 0.5rem;
+  }
+
+  .md\:w-3 {
+    width: 0.75rem;
+  }
+
+  .md\:w-4 {
+    width: 1rem;
+  }
+
+  .md\:w-5 {
+    width: 1.25rem;
+  }
+
+  .md\:w-6 {
+    width: 1.5rem;
+  }
+
+  .md\:w-8 {
+    width: 2rem;
+  }
+
+  .md\:w-10 {
+    width: 2.5rem;
+  }
+
+  .md\:w-12 {
+    width: 3rem;
+  }
+
+  .md\:w-16 {
+    width: 4rem;
+  }
+
+  .md\:w-20 {
+    width: 5rem;
+  }
+
+  .md\:w-24 {
+    width: 6rem;
+  }
+
+  .md\:w-32 {
+    width: 8rem;
+  }
+
+  .md\:w-40 {
+    width: 10rem;
+  }
+
+  .md\:w-48 {
+    width: 12rem;
+  }
+
+  .md\:w-56 {
+    width: 14rem;
+  }
+
+  .md\:w-64 {
+    width: 16rem;
+  }
+
+  .md\:w-auto {
+    width: auto;
+  }
+
+  .md\:w-px {
+    width: 1px;
+  }
+
+  .md\:w-1\/2 {
+    width: 50%;
+  }
+
+  .md\:w-1\/3 {
+    width: 33.333333%;
+  }
+
+  .md\:w-2\/3 {
+    width: 66.666667%;
+  }
+
+  .md\:w-1\/4 {
+    width: 25%;
+  }
+
+  .md\:w-2\/4 {
+    width: 50%;
+  }
+
+  .md\:w-3\/4 {
+    width: 75%;
+  }
+
+  .md\:w-1\/5 {
+    width: 20%;
+  }
+
+  .md\:w-2\/5 {
+    width: 40%;
+  }
+
+  .md\:w-3\/5 {
+    width: 60%;
+  }
+
+  .md\:w-4\/5 {
+    width: 80%;
+  }
+
+  .md\:w-1\/6 {
+    width: 16.666667%;
+  }
+
+  .md\:w-2\/6 {
+    width: 33.333333%;
+  }
+
+  .md\:w-3\/6 {
+    width: 50%;
+  }
+
+  .md\:w-4\/6 {
+    width: 66.666667%;
+  }
+
+  .md\:w-5\/6 {
+    width: 83.333333%;
+  }
+
+  .md\:w-1\/12 {
+    width: 8.333333%;
+  }
+
+  .md\:w-2\/12 {
+    width: 16.666667%;
+  }
+
+  .md\:w-3\/12 {
+    width: 25%;
+  }
+
+  .md\:w-4\/12 {
+    width: 33.333333%;
+  }
+
+  .md\:w-5\/12 {
+    width: 41.666667%;
+  }
+
+  .md\:w-6\/12 {
+    width: 50%;
+  }
+
+  .md\:w-7\/12 {
+    width: 58.333333%;
+  }
+
+  .md\:w-8\/12 {
+    width: 66.666667%;
+  }
+
+  .md\:w-9\/12 {
+    width: 75%;
+  }
+
+  .md\:w-10\/12 {
+    width: 83.333333%;
+  }
+
+  .md\:w-11\/12 {
+    width: 91.666667%;
+  }
+
+  .md\:w-full {
+    width: 100%;
+  }
+
+  .md\:w-screen {
+    width: 100vw;
+  }
+
+  .md\:z-0 {
+    z-index: 0;
+  }
+
+  .md\:z-10 {
+    z-index: 10;
+  }
+
+  .md\:z-20 {
+    z-index: 20;
+  }
+
+  .md\:z-30 {
+    z-index: 30;
+  }
+
+  .md\:z-40 {
+    z-index: 40;
+  }
+
+  .md\:z-50 {
+    z-index: 50;
+  }
+
+  .md\:z-auto {
+    z-index: auto;
+  }
+
+  .md\:gap-0 {
+    grid-gap: 0;
+    gap: 0;
+  }
+
+  .md\:gap-1 {
+    grid-gap: 0.25rem;
+    gap: 0.25rem;
+  }
+
+  .md\:gap-2 {
+    grid-gap: 0.5rem;
+    gap: 0.5rem;
+  }
+
+  .md\:gap-3 {
+    grid-gap: 0.75rem;
+    gap: 0.75rem;
+  }
+
+  .md\:gap-4 {
+    grid-gap: 1rem;
+    gap: 1rem;
+  }
+
+  .md\:gap-5 {
+    grid-gap: 1.25rem;
+    gap: 1.25rem;
+  }
+
+  .md\:gap-6 {
+    grid-gap: 1.5rem;
+    gap: 1.5rem;
+  }
+
+  .md\:gap-8 {
+    grid-gap: 2rem;
+    gap: 2rem;
+  }
+
+  .md\:gap-10 {
+    grid-gap: 2.5rem;
+    gap: 2.5rem;
+  }
+
+  .md\:gap-12 {
+    grid-gap: 3rem;
+    gap: 3rem;
+  }
+
+  .md\:gap-16 {
+    grid-gap: 4rem;
+    gap: 4rem;
+  }
+
+  .md\:gap-20 {
+    grid-gap: 5rem;
+    gap: 5rem;
+  }
+
+  .md\:gap-24 {
+    grid-gap: 6rem;
+    gap: 6rem;
+  }
+
+  .md\:gap-32 {
+    grid-gap: 8rem;
+    gap: 8rem;
+  }
+
+  .md\:gap-40 {
+    grid-gap: 10rem;
+    gap: 10rem;
+  }
+
+  .md\:gap-48 {
+    grid-gap: 12rem;
+    gap: 12rem;
+  }
+
+  .md\:gap-56 {
+    grid-gap: 14rem;
+    gap: 14rem;
+  }
+
+  .md\:gap-64 {
+    grid-gap: 16rem;
+    gap: 16rem;
+  }
+
+  .md\:gap-px {
+    grid-gap: 1px;
+    gap: 1px;
+  }
+
+  .md\:col-gap-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .md\:col-gap-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .md\:col-gap-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .md\:col-gap-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .md\:col-gap-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .md\:col-gap-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .md\:col-gap-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .md\:col-gap-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .md\:col-gap-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .md\:col-gap-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .md\:col-gap-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .md\:col-gap-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .md\:col-gap-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .md\:col-gap-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .md\:col-gap-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .md\:col-gap-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .md\:col-gap-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .md\:col-gap-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .md\:col-gap-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .md\:gap-x-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .md\:gap-x-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .md\:gap-x-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .md\:gap-x-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .md\:gap-x-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .md\:gap-x-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .md\:gap-x-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .md\:gap-x-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .md\:gap-x-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .md\:gap-x-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .md\:gap-x-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .md\:gap-x-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .md\:gap-x-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .md\:gap-x-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .md\:gap-x-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .md\:gap-x-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .md\:gap-x-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .md\:gap-x-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .md\:gap-x-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .md\:row-gap-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .md\:row-gap-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .md\:row-gap-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .md\:row-gap-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .md\:row-gap-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .md\:row-gap-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .md\:row-gap-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .md\:row-gap-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .md\:row-gap-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .md\:row-gap-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .md\:row-gap-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .md\:row-gap-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .md\:row-gap-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .md\:row-gap-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .md\:row-gap-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .md\:row-gap-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .md\:row-gap-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .md\:row-gap-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .md\:row-gap-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .md\:gap-y-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .md\:gap-y-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .md\:gap-y-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .md\:gap-y-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .md\:gap-y-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .md\:gap-y-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .md\:gap-y-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .md\:gap-y-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .md\:gap-y-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .md\:gap-y-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .md\:gap-y-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .md\:gap-y-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .md\:gap-y-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .md\:gap-y-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .md\:gap-y-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .md\:gap-y-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .md\:gap-y-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .md\:gap-y-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .md\:gap-y-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .md\:grid-flow-row {
+    grid-auto-flow: row;
+  }
+
+  .md\:grid-flow-col {
+    grid-auto-flow: column;
+  }
+
+  .md\:grid-flow-row-dense {
+    grid-auto-flow: row dense;
+  }
+
+  .md\:grid-flow-col-dense {
+    grid-auto-flow: column dense;
+  }
+
+  .md\:grid-cols-1 {
+    grid-template-columns: repeat(1, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-2 {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-3 {
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-4 {
+    grid-template-columns: repeat(4, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-5 {
+    grid-template-columns: repeat(5, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-6 {
+    grid-template-columns: repeat(6, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-7 {
+    grid-template-columns: repeat(7, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-8 {
+    grid-template-columns: repeat(8, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-9 {
+    grid-template-columns: repeat(9, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-10 {
+    grid-template-columns: repeat(10, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-11 {
+    grid-template-columns: repeat(11, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-12 {
+    grid-template-columns: repeat(12, minmax(0, 1fr));
+  }
+
+  .md\:grid-cols-none {
+    grid-template-columns: none;
+  }
+
+  .md\:col-auto {
+    grid-column: auto;
+  }
+
+  .md\:col-span-1 {
+    grid-column: span 1 / span 1;
+  }
+
+  .md\:col-span-2 {
+    grid-column: span 2 / span 2;
+  }
+
+  .md\:col-span-3 {
+    grid-column: span 3 / span 3;
+  }
+
+  .md\:col-span-4 {
+    grid-column: span 4 / span 4;
+  }
+
+  .md\:col-span-5 {
+    grid-column: span 5 / span 5;
+  }
+
+  .md\:col-span-6 {
+    grid-column: span 6 / span 6;
+  }
+
+  .md\:col-span-7 {
+    grid-column: span 7 / span 7;
+  }
+
+  .md\:col-span-8 {
+    grid-column: span 8 / span 8;
+  }
+
+  .md\:col-span-9 {
+    grid-column: span 9 / span 9;
+  }
+
+  .md\:col-span-10 {
+    grid-column: span 10 / span 10;
+  }
+
+  .md\:col-span-11 {
+    grid-column: span 11 / span 11;
+  }
+
+  .md\:col-span-12 {
+    grid-column: span 12 / span 12;
+  }
+
+  .md\:col-start-1 {
+    grid-column-start: 1;
+  }
+
+  .md\:col-start-2 {
+    grid-column-start: 2;
+  }
+
+  .md\:col-start-3 {
+    grid-column-start: 3;
+  }
+
+  .md\:col-start-4 {
+    grid-column-start: 4;
+  }
+
+  .md\:col-start-5 {
+    grid-column-start: 5;
+  }
+
+  .md\:col-start-6 {
+    grid-column-start: 6;
+  }
+
+  .md\:col-start-7 {
+    grid-column-start: 7;
+  }
+
+  .md\:col-start-8 {
+    grid-column-start: 8;
+  }
+
+  .md\:col-start-9 {
+    grid-column-start: 9;
+  }
+
+  .md\:col-start-10 {
+    grid-column-start: 10;
+  }
+
+  .md\:col-start-11 {
+    grid-column-start: 11;
+  }
+
+  .md\:col-start-12 {
+    grid-column-start: 12;
+  }
+
+  .md\:col-start-13 {
+    grid-column-start: 13;
+  }
+
+  .md\:col-start-auto {
+    grid-column-start: auto;
+  }
+
+  .md\:col-end-1 {
+    grid-column-end: 1;
+  }
+
+  .md\:col-end-2 {
+    grid-column-end: 2;
+  }
+
+  .md\:col-end-3 {
+    grid-column-end: 3;
+  }
+
+  .md\:col-end-4 {
+    grid-column-end: 4;
+  }
+
+  .md\:col-end-5 {
+    grid-column-end: 5;
+  }
+
+  .md\:col-end-6 {
+    grid-column-end: 6;
+  }
+
+  .md\:col-end-7 {
+    grid-column-end: 7;
+  }
+
+  .md\:col-end-8 {
+    grid-column-end: 8;
+  }
+
+  .md\:col-end-9 {
+    grid-column-end: 9;
+  }
+
+  .md\:col-end-10 {
+    grid-column-end: 10;
+  }
+
+  .md\:col-end-11 {
+    grid-column-end: 11;
+  }
+
+  .md\:col-end-12 {
+    grid-column-end: 12;
+  }
+
+  .md\:col-end-13 {
+    grid-column-end: 13;
+  }
+
+  .md\:col-end-auto {
+    grid-column-end: auto;
+  }
+
+  .md\:grid-rows-1 {
+    grid-template-rows: repeat(1, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-2 {
+    grid-template-rows: repeat(2, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-3 {
+    grid-template-rows: repeat(3, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-4 {
+    grid-template-rows: repeat(4, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-5 {
+    grid-template-rows: repeat(5, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-6 {
+    grid-template-rows: repeat(6, minmax(0, 1fr));
+  }
+
+  .md\:grid-rows-none {
+    grid-template-rows: none;
+  }
+
+  .md\:row-auto {
+    grid-row: auto;
+  }
+
+  .md\:row-span-1 {
+    grid-row: span 1 / span 1;
+  }
+
+  .md\:row-span-2 {
+    grid-row: span 2 / span 2;
+  }
+
+  .md\:row-span-3 {
+    grid-row: span 3 / span 3;
+  }
+
+  .md\:row-span-4 {
+    grid-row: span 4 / span 4;
+  }
+
+  .md\:row-span-5 {
+    grid-row: span 5 / span 5;
+  }
+
+  .md\:row-span-6 {
+    grid-row: span 6 / span 6;
+  }
+
+  .md\:row-start-1 {
+    grid-row-start: 1;
+  }
+
+  .md\:row-start-2 {
+    grid-row-start: 2;
+  }
+
+  .md\:row-start-3 {
+    grid-row-start: 3;
+  }
+
+  .md\:row-start-4 {
+    grid-row-start: 4;
+  }
+
+  .md\:row-start-5 {
+    grid-row-start: 5;
+  }
+
+  .md\:row-start-6 {
+    grid-row-start: 6;
+  }
+
+  .md\:row-start-7 {
+    grid-row-start: 7;
+  }
+
+  .md\:row-start-auto {
+    grid-row-start: auto;
+  }
+
+  .md\:row-end-1 {
+    grid-row-end: 1;
+  }
+
+  .md\:row-end-2 {
+    grid-row-end: 2;
+  }
+
+  .md\:row-end-3 {
+    grid-row-end: 3;
+  }
+
+  .md\:row-end-4 {
+    grid-row-end: 4;
+  }
+
+  .md\:row-end-5 {
+    grid-row-end: 5;
+  }
+
+  .md\:row-end-6 {
+    grid-row-end: 6;
+  }
+
+  .md\:row-end-7 {
+    grid-row-end: 7;
+  }
+
+  .md\:row-end-auto {
+    grid-row-end: auto;
+  }
+
+  .md\:transform {
+    --transform-translate-x: 0;
+    --transform-translate-y: 0;
+    --transform-rotate: 0;
+    --transform-skew-x: 0;
+    --transform-skew-y: 0;
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+    transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+  }
+
+  .md\:transform-none {
+    transform: none;
+  }
+
+  .md\:origin-center {
+    transform-origin: center;
+  }
+
+  .md\:origin-top {
+    transform-origin: top;
+  }
+
+  .md\:origin-top-right {
+    transform-origin: top right;
+  }
+
+  .md\:origin-right {
+    transform-origin: right;
+  }
+
+  .md\:origin-bottom-right {
+    transform-origin: bottom right;
+  }
+
+  .md\:origin-bottom {
+    transform-origin: bottom;
+  }
+
+  .md\:origin-bottom-left {
+    transform-origin: bottom left;
+  }
+
+  .md\:origin-left {
+    transform-origin: left;
+  }
+
+  .md\:origin-top-left {
+    transform-origin: top left;
+  }
+
+  .md\:scale-0 {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .md\:scale-50 {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .md\:scale-75 {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .md\:scale-90 {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .md\:scale-95 {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .md\:scale-100 {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .md\:scale-105 {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:scale-110 {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:scale-125 {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:scale-150 {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:scale-x-0 {
+    --transform-scale-x: 0;
+  }
+
+  .md\:scale-x-50 {
+    --transform-scale-x: .5;
+  }
+
+  .md\:scale-x-75 {
+    --transform-scale-x: .75;
+  }
+
+  .md\:scale-x-90 {
+    --transform-scale-x: .9;
+  }
+
+  .md\:scale-x-95 {
+    --transform-scale-x: .95;
+  }
+
+  .md\:scale-x-100 {
+    --transform-scale-x: 1;
+  }
+
+  .md\:scale-x-105 {
+    --transform-scale-x: 1.05;
+  }
+
+  .md\:scale-x-110 {
+    --transform-scale-x: 1.1;
+  }
+
+  .md\:scale-x-125 {
+    --transform-scale-x: 1.25;
+  }
+
+  .md\:scale-x-150 {
+    --transform-scale-x: 1.5;
+  }
+
+  .md\:scale-y-0 {
+    --transform-scale-y: 0;
+  }
+
+  .md\:scale-y-50 {
+    --transform-scale-y: .5;
+  }
+
+  .md\:scale-y-75 {
+    --transform-scale-y: .75;
+  }
+
+  .md\:scale-y-90 {
+    --transform-scale-y: .9;
+  }
+
+  .md\:scale-y-95 {
+    --transform-scale-y: .95;
+  }
+
+  .md\:scale-y-100 {
+    --transform-scale-y: 1;
+  }
+
+  .md\:scale-y-105 {
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:scale-y-110 {
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:scale-y-125 {
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:scale-y-150 {
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:hover\:scale-0:hover {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .md\:hover\:scale-50:hover {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .md\:hover\:scale-75:hover {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .md\:hover\:scale-90:hover {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .md\:hover\:scale-95:hover {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .md\:hover\:scale-100:hover {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .md\:hover\:scale-105:hover {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:hover\:scale-110:hover {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:hover\:scale-125:hover {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:hover\:scale-150:hover {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:hover\:scale-x-0:hover {
+    --transform-scale-x: 0;
+  }
+
+  .md\:hover\:scale-x-50:hover {
+    --transform-scale-x: .5;
+  }
+
+  .md\:hover\:scale-x-75:hover {
+    --transform-scale-x: .75;
+  }
+
+  .md\:hover\:scale-x-90:hover {
+    --transform-scale-x: .9;
+  }
+
+  .md\:hover\:scale-x-95:hover {
+    --transform-scale-x: .95;
+  }
+
+  .md\:hover\:scale-x-100:hover {
+    --transform-scale-x: 1;
+  }
+
+  .md\:hover\:scale-x-105:hover {
+    --transform-scale-x: 1.05;
+  }
+
+  .md\:hover\:scale-x-110:hover {
+    --transform-scale-x: 1.1;
+  }
+
+  .md\:hover\:scale-x-125:hover {
+    --transform-scale-x: 1.25;
+  }
+
+  .md\:hover\:scale-x-150:hover {
+    --transform-scale-x: 1.5;
+  }
+
+  .md\:hover\:scale-y-0:hover {
+    --transform-scale-y: 0;
+  }
+
+  .md\:hover\:scale-y-50:hover {
+    --transform-scale-y: .5;
+  }
+
+  .md\:hover\:scale-y-75:hover {
+    --transform-scale-y: .75;
+  }
+
+  .md\:hover\:scale-y-90:hover {
+    --transform-scale-y: .9;
+  }
+
+  .md\:hover\:scale-y-95:hover {
+    --transform-scale-y: .95;
+  }
+
+  .md\:hover\:scale-y-100:hover {
+    --transform-scale-y: 1;
+  }
+
+  .md\:hover\:scale-y-105:hover {
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:hover\:scale-y-110:hover {
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:hover\:scale-y-125:hover {
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:hover\:scale-y-150:hover {
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:focus\:scale-0:focus {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .md\:focus\:scale-50:focus {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .md\:focus\:scale-75:focus {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .md\:focus\:scale-90:focus {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .md\:focus\:scale-95:focus {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .md\:focus\:scale-100:focus {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .md\:focus\:scale-105:focus {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:focus\:scale-110:focus {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:focus\:scale-125:focus {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:focus\:scale-150:focus {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:focus\:scale-x-0:focus {
+    --transform-scale-x: 0;
+  }
+
+  .md\:focus\:scale-x-50:focus {
+    --transform-scale-x: .5;
+  }
+
+  .md\:focus\:scale-x-75:focus {
+    --transform-scale-x: .75;
+  }
+
+  .md\:focus\:scale-x-90:focus {
+    --transform-scale-x: .9;
+  }
+
+  .md\:focus\:scale-x-95:focus {
+    --transform-scale-x: .95;
+  }
+
+  .md\:focus\:scale-x-100:focus {
+    --transform-scale-x: 1;
+  }
+
+  .md\:focus\:scale-x-105:focus {
+    --transform-scale-x: 1.05;
+  }
+
+  .md\:focus\:scale-x-110:focus {
+    --transform-scale-x: 1.1;
+  }
+
+  .md\:focus\:scale-x-125:focus {
+    --transform-scale-x: 1.25;
+  }
+
+  .md\:focus\:scale-x-150:focus {
+    --transform-scale-x: 1.5;
+  }
+
+  .md\:focus\:scale-y-0:focus {
+    --transform-scale-y: 0;
+  }
+
+  .md\:focus\:scale-y-50:focus {
+    --transform-scale-y: .5;
+  }
+
+  .md\:focus\:scale-y-75:focus {
+    --transform-scale-y: .75;
+  }
+
+  .md\:focus\:scale-y-90:focus {
+    --transform-scale-y: .9;
+  }
+
+  .md\:focus\:scale-y-95:focus {
+    --transform-scale-y: .95;
+  }
+
+  .md\:focus\:scale-y-100:focus {
+    --transform-scale-y: 1;
+  }
+
+  .md\:focus\:scale-y-105:focus {
+    --transform-scale-y: 1.05;
+  }
+
+  .md\:focus\:scale-y-110:focus {
+    --transform-scale-y: 1.1;
+  }
+
+  .md\:focus\:scale-y-125:focus {
+    --transform-scale-y: 1.25;
+  }
+
+  .md\:focus\:scale-y-150:focus {
+    --transform-scale-y: 1.5;
+  }
+
+  .md\:rotate-0 {
+    --transform-rotate: 0;
+  }
+
+  .md\:rotate-45 {
+    --transform-rotate: 45deg;
+  }
+
+  .md\:rotate-90 {
+    --transform-rotate: 90deg;
+  }
+
+  .md\:rotate-180 {
+    --transform-rotate: 180deg;
+  }
+
+  .md\:-rotate-180 {
+    --transform-rotate: -180deg;
+  }
+
+  .md\:-rotate-90 {
+    --transform-rotate: -90deg;
+  }
+
+  .md\:-rotate-45 {
+    --transform-rotate: -45deg;
+  }
+
+  .md\:hover\:rotate-0:hover {
+    --transform-rotate: 0;
+  }
+
+  .md\:hover\:rotate-45:hover {
+    --transform-rotate: 45deg;
+  }
+
+  .md\:hover\:rotate-90:hover {
+    --transform-rotate: 90deg;
+  }
+
+  .md\:hover\:rotate-180:hover {
+    --transform-rotate: 180deg;
+  }
+
+  .md\:hover\:-rotate-180:hover {
+    --transform-rotate: -180deg;
+  }
+
+  .md\:hover\:-rotate-90:hover {
+    --transform-rotate: -90deg;
+  }
+
+  .md\:hover\:-rotate-45:hover {
+    --transform-rotate: -45deg;
+  }
+
+  .md\:focus\:rotate-0:focus {
+    --transform-rotate: 0;
+  }
+
+  .md\:focus\:rotate-45:focus {
+    --transform-rotate: 45deg;
+  }
+
+  .md\:focus\:rotate-90:focus {
+    --transform-rotate: 90deg;
+  }
+
+  .md\:focus\:rotate-180:focus {
+    --transform-rotate: 180deg;
+  }
+
+  .md\:focus\:-rotate-180:focus {
+    --transform-rotate: -180deg;
+  }
+
+  .md\:focus\:-rotate-90:focus {
+    --transform-rotate: -90deg;
+  }
+
+  .md\:focus\:-rotate-45:focus {
+    --transform-rotate: -45deg;
+  }
+
+  .md\:translate-x-0 {
+    --transform-translate-x: 0;
+  }
+
+  .md\:translate-x-1 {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .md\:translate-x-2 {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .md\:translate-x-3 {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .md\:translate-x-4 {
+    --transform-translate-x: 1rem;
+  }
+
+  .md\:translate-x-5 {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .md\:translate-x-6 {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .md\:translate-x-8 {
+    --transform-translate-x: 2rem;
+  }
+
+  .md\:translate-x-10 {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .md\:translate-x-12 {
+    --transform-translate-x: 3rem;
+  }
+
+  .md\:translate-x-16 {
+    --transform-translate-x: 4rem;
+  }
+
+  .md\:translate-x-20 {
+    --transform-translate-x: 5rem;
+  }
+
+  .md\:translate-x-24 {
+    --transform-translate-x: 6rem;
+  }
+
+  .md\:translate-x-32 {
+    --transform-translate-x: 8rem;
+  }
+
+  .md\:translate-x-40 {
+    --transform-translate-x: 10rem;
+  }
+
+  .md\:translate-x-48 {
+    --transform-translate-x: 12rem;
+  }
+
+  .md\:translate-x-56 {
+    --transform-translate-x: 14rem;
+  }
+
+  .md\:translate-x-64 {
+    --transform-translate-x: 16rem;
+  }
+
+  .md\:translate-x-px {
+    --transform-translate-x: 1px;
+  }
+
+  .md\:-translate-x-1 {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .md\:-translate-x-2 {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .md\:-translate-x-3 {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .md\:-translate-x-4 {
+    --transform-translate-x: -1rem;
+  }
+
+  .md\:-translate-x-5 {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .md\:-translate-x-6 {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .md\:-translate-x-8 {
+    --transform-translate-x: -2rem;
+  }
+
+  .md\:-translate-x-10 {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .md\:-translate-x-12 {
+    --transform-translate-x: -3rem;
+  }
+
+  .md\:-translate-x-16 {
+    --transform-translate-x: -4rem;
+  }
+
+  .md\:-translate-x-20 {
+    --transform-translate-x: -5rem;
+  }
+
+  .md\:-translate-x-24 {
+    --transform-translate-x: -6rem;
+  }
+
+  .md\:-translate-x-32 {
+    --transform-translate-x: -8rem;
+  }
+
+  .md\:-translate-x-40 {
+    --transform-translate-x: -10rem;
+  }
+
+  .md\:-translate-x-48 {
+    --transform-translate-x: -12rem;
+  }
+
+  .md\:-translate-x-56 {
+    --transform-translate-x: -14rem;
+  }
+
+  .md\:-translate-x-64 {
+    --transform-translate-x: -16rem;
+  }
+
+  .md\:-translate-x-px {
+    --transform-translate-x: -1px;
+  }
+
+  .md\:-translate-x-full {
+    --transform-translate-x: -100%;
+  }
+
+  .md\:-translate-x-1\/2 {
+    --transform-translate-x: -50%;
+  }
+
+  .md\:translate-x-1\/2 {
+    --transform-translate-x: 50%;
+  }
+
+  .md\:translate-x-full {
+    --transform-translate-x: 100%;
+  }
+
+  .md\:translate-y-0 {
+    --transform-translate-y: 0;
+  }
+
+  .md\:translate-y-1 {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .md\:translate-y-2 {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .md\:translate-y-3 {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .md\:translate-y-4 {
+    --transform-translate-y: 1rem;
+  }
+
+  .md\:translate-y-5 {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .md\:translate-y-6 {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .md\:translate-y-8 {
+    --transform-translate-y: 2rem;
+  }
+
+  .md\:translate-y-10 {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .md\:translate-y-12 {
+    --transform-translate-y: 3rem;
+  }
+
+  .md\:translate-y-16 {
+    --transform-translate-y: 4rem;
+  }
+
+  .md\:translate-y-20 {
+    --transform-translate-y: 5rem;
+  }
+
+  .md\:translate-y-24 {
+    --transform-translate-y: 6rem;
+  }
+
+  .md\:translate-y-32 {
+    --transform-translate-y: 8rem;
+  }
+
+  .md\:translate-y-40 {
+    --transform-translate-y: 10rem;
+  }
+
+  .md\:translate-y-48 {
+    --transform-translate-y: 12rem;
+  }
+
+  .md\:translate-y-56 {
+    --transform-translate-y: 14rem;
+  }
+
+  .md\:translate-y-64 {
+    --transform-translate-y: 16rem;
+  }
+
+  .md\:translate-y-px {
+    --transform-translate-y: 1px;
+  }
+
+  .md\:-translate-y-1 {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .md\:-translate-y-2 {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .md\:-translate-y-3 {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .md\:-translate-y-4 {
+    --transform-translate-y: -1rem;
+  }
+
+  .md\:-translate-y-5 {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .md\:-translate-y-6 {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .md\:-translate-y-8 {
+    --transform-translate-y: -2rem;
+  }
+
+  .md\:-translate-y-10 {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .md\:-translate-y-12 {
+    --transform-translate-y: -3rem;
+  }
+
+  .md\:-translate-y-16 {
+    --transform-translate-y: -4rem;
+  }
+
+  .md\:-translate-y-20 {
+    --transform-translate-y: -5rem;
+  }
+
+  .md\:-translate-y-24 {
+    --transform-translate-y: -6rem;
+  }
+
+  .md\:-translate-y-32 {
+    --transform-translate-y: -8rem;
+  }
+
+  .md\:-translate-y-40 {
+    --transform-translate-y: -10rem;
+  }
+
+  .md\:-translate-y-48 {
+    --transform-translate-y: -12rem;
+  }
+
+  .md\:-translate-y-56 {
+    --transform-translate-y: -14rem;
+  }
+
+  .md\:-translate-y-64 {
+    --transform-translate-y: -16rem;
+  }
+
+  .md\:-translate-y-px {
+    --transform-translate-y: -1px;
+  }
+
+  .md\:-translate-y-full {
+    --transform-translate-y: -100%;
+  }
+
+  .md\:-translate-y-1\/2 {
+    --transform-translate-y: -50%;
+  }
+
+  .md\:translate-y-1\/2 {
+    --transform-translate-y: 50%;
+  }
+
+  .md\:translate-y-full {
+    --transform-translate-y: 100%;
+  }
+
+  .md\:hover\:translate-x-0:hover {
+    --transform-translate-x: 0;
+  }
+
+  .md\:hover\:translate-x-1:hover {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .md\:hover\:translate-x-2:hover {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .md\:hover\:translate-x-3:hover {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .md\:hover\:translate-x-4:hover {
+    --transform-translate-x: 1rem;
+  }
+
+  .md\:hover\:translate-x-5:hover {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .md\:hover\:translate-x-6:hover {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .md\:hover\:translate-x-8:hover {
+    --transform-translate-x: 2rem;
+  }
+
+  .md\:hover\:translate-x-10:hover {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .md\:hover\:translate-x-12:hover {
+    --transform-translate-x: 3rem;
+  }
+
+  .md\:hover\:translate-x-16:hover {
+    --transform-translate-x: 4rem;
+  }
+
+  .md\:hover\:translate-x-20:hover {
+    --transform-translate-x: 5rem;
+  }
+
+  .md\:hover\:translate-x-24:hover {
+    --transform-translate-x: 6rem;
+  }
+
+  .md\:hover\:translate-x-32:hover {
+    --transform-translate-x: 8rem;
+  }
+
+  .md\:hover\:translate-x-40:hover {
+    --transform-translate-x: 10rem;
+  }
+
+  .md\:hover\:translate-x-48:hover {
+    --transform-translate-x: 12rem;
+  }
+
+  .md\:hover\:translate-x-56:hover {
+    --transform-translate-x: 14rem;
+  }
+
+  .md\:hover\:translate-x-64:hover {
+    --transform-translate-x: 16rem;
+  }
+
+  .md\:hover\:translate-x-px:hover {
+    --transform-translate-x: 1px;
+  }
+
+  .md\:hover\:-translate-x-1:hover {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .md\:hover\:-translate-x-2:hover {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .md\:hover\:-translate-x-3:hover {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .md\:hover\:-translate-x-4:hover {
+    --transform-translate-x: -1rem;
+  }
+
+  .md\:hover\:-translate-x-5:hover {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .md\:hover\:-translate-x-6:hover {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .md\:hover\:-translate-x-8:hover {
+    --transform-translate-x: -2rem;
+  }
+
+  .md\:hover\:-translate-x-10:hover {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .md\:hover\:-translate-x-12:hover {
+    --transform-translate-x: -3rem;
+  }
+
+  .md\:hover\:-translate-x-16:hover {
+    --transform-translate-x: -4rem;
+  }
+
+  .md\:hover\:-translate-x-20:hover {
+    --transform-translate-x: -5rem;
+  }
+
+  .md\:hover\:-translate-x-24:hover {
+    --transform-translate-x: -6rem;
+  }
+
+  .md\:hover\:-translate-x-32:hover {
+    --transform-translate-x: -8rem;
+  }
+
+  .md\:hover\:-translate-x-40:hover {
+    --transform-translate-x: -10rem;
+  }
+
+  .md\:hover\:-translate-x-48:hover {
+    --transform-translate-x: -12rem;
+  }
+
+  .md\:hover\:-translate-x-56:hover {
+    --transform-translate-x: -14rem;
+  }
+
+  .md\:hover\:-translate-x-64:hover {
+    --transform-translate-x: -16rem;
+  }
+
+  .md\:hover\:-translate-x-px:hover {
+    --transform-translate-x: -1px;
+  }
+
+  .md\:hover\:-translate-x-full:hover {
+    --transform-translate-x: -100%;
+  }
+
+  .md\:hover\:-translate-x-1\/2:hover {
+    --transform-translate-x: -50%;
+  }
+
+  .md\:hover\:translate-x-1\/2:hover {
+    --transform-translate-x: 50%;
+  }
+
+  .md\:hover\:translate-x-full:hover {
+    --transform-translate-x: 100%;
+  }
+
+  .md\:hover\:translate-y-0:hover {
+    --transform-translate-y: 0;
+  }
+
+  .md\:hover\:translate-y-1:hover {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .md\:hover\:translate-y-2:hover {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .md\:hover\:translate-y-3:hover {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .md\:hover\:translate-y-4:hover {
+    --transform-translate-y: 1rem;
+  }
+
+  .md\:hover\:translate-y-5:hover {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .md\:hover\:translate-y-6:hover {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .md\:hover\:translate-y-8:hover {
+    --transform-translate-y: 2rem;
+  }
+
+  .md\:hover\:translate-y-10:hover {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .md\:hover\:translate-y-12:hover {
+    --transform-translate-y: 3rem;
+  }
+
+  .md\:hover\:translate-y-16:hover {
+    --transform-translate-y: 4rem;
+  }
+
+  .md\:hover\:translate-y-20:hover {
+    --transform-translate-y: 5rem;
+  }
+
+  .md\:hover\:translate-y-24:hover {
+    --transform-translate-y: 6rem;
+  }
+
+  .md\:hover\:translate-y-32:hover {
+    --transform-translate-y: 8rem;
+  }
+
+  .md\:hover\:translate-y-40:hover {
+    --transform-translate-y: 10rem;
+  }
+
+  .md\:hover\:translate-y-48:hover {
+    --transform-translate-y: 12rem;
+  }
+
+  .md\:hover\:translate-y-56:hover {
+    --transform-translate-y: 14rem;
+  }
+
+  .md\:hover\:translate-y-64:hover {
+    --transform-translate-y: 16rem;
+  }
+
+  .md\:hover\:translate-y-px:hover {
+    --transform-translate-y: 1px;
+  }
+
+  .md\:hover\:-translate-y-1:hover {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .md\:hover\:-translate-y-2:hover {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .md\:hover\:-translate-y-3:hover {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .md\:hover\:-translate-y-4:hover {
+    --transform-translate-y: -1rem;
+  }
+
+  .md\:hover\:-translate-y-5:hover {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .md\:hover\:-translate-y-6:hover {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .md\:hover\:-translate-y-8:hover {
+    --transform-translate-y: -2rem;
+  }
+
+  .md\:hover\:-translate-y-10:hover {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .md\:hover\:-translate-y-12:hover {
+    --transform-translate-y: -3rem;
+  }
+
+  .md\:hover\:-translate-y-16:hover {
+    --transform-translate-y: -4rem;
+  }
+
+  .md\:hover\:-translate-y-20:hover {
+    --transform-translate-y: -5rem;
+  }
+
+  .md\:hover\:-translate-y-24:hover {
+    --transform-translate-y: -6rem;
+  }
+
+  .md\:hover\:-translate-y-32:hover {
+    --transform-translate-y: -8rem;
+  }
+
+  .md\:hover\:-translate-y-40:hover {
+    --transform-translate-y: -10rem;
+  }
+
+  .md\:hover\:-translate-y-48:hover {
+    --transform-translate-y: -12rem;
+  }
+
+  .md\:hover\:-translate-y-56:hover {
+    --transform-translate-y: -14rem;
+  }
+
+  .md\:hover\:-translate-y-64:hover {
+    --transform-translate-y: -16rem;
+  }
+
+  .md\:hover\:-translate-y-px:hover {
+    --transform-translate-y: -1px;
+  }
+
+  .md\:hover\:-translate-y-full:hover {
+    --transform-translate-y: -100%;
+  }
+
+  .md\:hover\:-translate-y-1\/2:hover {
+    --transform-translate-y: -50%;
+  }
+
+  .md\:hover\:translate-y-1\/2:hover {
+    --transform-translate-y: 50%;
+  }
+
+  .md\:hover\:translate-y-full:hover {
+    --transform-translate-y: 100%;
+  }
+
+  .md\:focus\:translate-x-0:focus {
+    --transform-translate-x: 0;
+  }
+
+  .md\:focus\:translate-x-1:focus {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .md\:focus\:translate-x-2:focus {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .md\:focus\:translate-x-3:focus {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .md\:focus\:translate-x-4:focus {
+    --transform-translate-x: 1rem;
+  }
+
+  .md\:focus\:translate-x-5:focus {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .md\:focus\:translate-x-6:focus {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .md\:focus\:translate-x-8:focus {
+    --transform-translate-x: 2rem;
+  }
+
+  .md\:focus\:translate-x-10:focus {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .md\:focus\:translate-x-12:focus {
+    --transform-translate-x: 3rem;
+  }
+
+  .md\:focus\:translate-x-16:focus {
+    --transform-translate-x: 4rem;
+  }
+
+  .md\:focus\:translate-x-20:focus {
+    --transform-translate-x: 5rem;
+  }
+
+  .md\:focus\:translate-x-24:focus {
+    --transform-translate-x: 6rem;
+  }
+
+  .md\:focus\:translate-x-32:focus {
+    --transform-translate-x: 8rem;
+  }
+
+  .md\:focus\:translate-x-40:focus {
+    --transform-translate-x: 10rem;
+  }
+
+  .md\:focus\:translate-x-48:focus {
+    --transform-translate-x: 12rem;
+  }
+
+  .md\:focus\:translate-x-56:focus {
+    --transform-translate-x: 14rem;
+  }
+
+  .md\:focus\:translate-x-64:focus {
+    --transform-translate-x: 16rem;
+  }
+
+  .md\:focus\:translate-x-px:focus {
+    --transform-translate-x: 1px;
+  }
+
+  .md\:focus\:-translate-x-1:focus {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .md\:focus\:-translate-x-2:focus {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .md\:focus\:-translate-x-3:focus {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .md\:focus\:-translate-x-4:focus {
+    --transform-translate-x: -1rem;
+  }
+
+  .md\:focus\:-translate-x-5:focus {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .md\:focus\:-translate-x-6:focus {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .md\:focus\:-translate-x-8:focus {
+    --transform-translate-x: -2rem;
+  }
+
+  .md\:focus\:-translate-x-10:focus {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .md\:focus\:-translate-x-12:focus {
+    --transform-translate-x: -3rem;
+  }
+
+  .md\:focus\:-translate-x-16:focus {
+    --transform-translate-x: -4rem;
+  }
+
+  .md\:focus\:-translate-x-20:focus {
+    --transform-translate-x: -5rem;
+  }
+
+  .md\:focus\:-translate-x-24:focus {
+    --transform-translate-x: -6rem;
+  }
+
+  .md\:focus\:-translate-x-32:focus {
+    --transform-translate-x: -8rem;
+  }
+
+  .md\:focus\:-translate-x-40:focus {
+    --transform-translate-x: -10rem;
+  }
+
+  .md\:focus\:-translate-x-48:focus {
+    --transform-translate-x: -12rem;
+  }
+
+  .md\:focus\:-translate-x-56:focus {
+    --transform-translate-x: -14rem;
+  }
+
+  .md\:focus\:-translate-x-64:focus {
+    --transform-translate-x: -16rem;
+  }
+
+  .md\:focus\:-translate-x-px:focus {
+    --transform-translate-x: -1px;
+  }
+
+  .md\:focus\:-translate-x-full:focus {
+    --transform-translate-x: -100%;
+  }
+
+  .md\:focus\:-translate-x-1\/2:focus {
+    --transform-translate-x: -50%;
+  }
+
+  .md\:focus\:translate-x-1\/2:focus {
+    --transform-translate-x: 50%;
+  }
+
+  .md\:focus\:translate-x-full:focus {
+    --transform-translate-x: 100%;
+  }
+
+  .md\:focus\:translate-y-0:focus {
+    --transform-translate-y: 0;
+  }
+
+  .md\:focus\:translate-y-1:focus {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .md\:focus\:translate-y-2:focus {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .md\:focus\:translate-y-3:focus {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .md\:focus\:translate-y-4:focus {
+    --transform-translate-y: 1rem;
+  }
+
+  .md\:focus\:translate-y-5:focus {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .md\:focus\:translate-y-6:focus {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .md\:focus\:translate-y-8:focus {
+    --transform-translate-y: 2rem;
+  }
+
+  .md\:focus\:translate-y-10:focus {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .md\:focus\:translate-y-12:focus {
+    --transform-translate-y: 3rem;
+  }
+
+  .md\:focus\:translate-y-16:focus {
+    --transform-translate-y: 4rem;
+  }
+
+  .md\:focus\:translate-y-20:focus {
+    --transform-translate-y: 5rem;
+  }
+
+  .md\:focus\:translate-y-24:focus {
+    --transform-translate-y: 6rem;
+  }
+
+  .md\:focus\:translate-y-32:focus {
+    --transform-translate-y: 8rem;
+  }
+
+  .md\:focus\:translate-y-40:focus {
+    --transform-translate-y: 10rem;
+  }
+
+  .md\:focus\:translate-y-48:focus {
+    --transform-translate-y: 12rem;
+  }
+
+  .md\:focus\:translate-y-56:focus {
+    --transform-translate-y: 14rem;
+  }
+
+  .md\:focus\:translate-y-64:focus {
+    --transform-translate-y: 16rem;
+  }
+
+  .md\:focus\:translate-y-px:focus {
+    --transform-translate-y: 1px;
+  }
+
+  .md\:focus\:-translate-y-1:focus {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .md\:focus\:-translate-y-2:focus {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .md\:focus\:-translate-y-3:focus {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .md\:focus\:-translate-y-4:focus {
+    --transform-translate-y: -1rem;
+  }
+
+  .md\:focus\:-translate-y-5:focus {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .md\:focus\:-translate-y-6:focus {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .md\:focus\:-translate-y-8:focus {
+    --transform-translate-y: -2rem;
+  }
+
+  .md\:focus\:-translate-y-10:focus {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .md\:focus\:-translate-y-12:focus {
+    --transform-translate-y: -3rem;
+  }
+
+  .md\:focus\:-translate-y-16:focus {
+    --transform-translate-y: -4rem;
+  }
+
+  .md\:focus\:-translate-y-20:focus {
+    --transform-translate-y: -5rem;
+  }
+
+  .md\:focus\:-translate-y-24:focus {
+    --transform-translate-y: -6rem;
+  }
+
+  .md\:focus\:-translate-y-32:focus {
+    --transform-translate-y: -8rem;
+  }
+
+  .md\:focus\:-translate-y-40:focus {
+    --transform-translate-y: -10rem;
+  }
+
+  .md\:focus\:-translate-y-48:focus {
+    --transform-translate-y: -12rem;
+  }
+
+  .md\:focus\:-translate-y-56:focus {
+    --transform-translate-y: -14rem;
+  }
+
+  .md\:focus\:-translate-y-64:focus {
+    --transform-translate-y: -16rem;
+  }
+
+  .md\:focus\:-translate-y-px:focus {
+    --transform-translate-y: -1px;
+  }
+
+  .md\:focus\:-translate-y-full:focus {
+    --transform-translate-y: -100%;
+  }
+
+  .md\:focus\:-translate-y-1\/2:focus {
+    --transform-translate-y: -50%;
+  }
+
+  .md\:focus\:translate-y-1\/2:focus {
+    --transform-translate-y: 50%;
+  }
+
+  .md\:focus\:translate-y-full:focus {
+    --transform-translate-y: 100%;
+  }
+
+  .md\:skew-x-0 {
+    --transform-skew-x: 0;
+  }
+
+  .md\:skew-x-3 {
+    --transform-skew-x: 3deg;
+  }
+
+  .md\:skew-x-6 {
+    --transform-skew-x: 6deg;
+  }
+
+  .md\:skew-x-12 {
+    --transform-skew-x: 12deg;
+  }
+
+  .md\:-skew-x-12 {
+    --transform-skew-x: -12deg;
+  }
+
+  .md\:-skew-x-6 {
+    --transform-skew-x: -6deg;
+  }
+
+  .md\:-skew-x-3 {
+    --transform-skew-x: -3deg;
+  }
+
+  .md\:skew-y-0 {
+    --transform-skew-y: 0;
+  }
+
+  .md\:skew-y-3 {
+    --transform-skew-y: 3deg;
+  }
+
+  .md\:skew-y-6 {
+    --transform-skew-y: 6deg;
+  }
+
+  .md\:skew-y-12 {
+    --transform-skew-y: 12deg;
+  }
+
+  .md\:-skew-y-12 {
+    --transform-skew-y: -12deg;
+  }
+
+  .md\:-skew-y-6 {
+    --transform-skew-y: -6deg;
+  }
+
+  .md\:-skew-y-3 {
+    --transform-skew-y: -3deg;
+  }
+
+  .md\:hover\:skew-x-0:hover {
+    --transform-skew-x: 0;
+  }
+
+  .md\:hover\:skew-x-3:hover {
+    --transform-skew-x: 3deg;
+  }
+
+  .md\:hover\:skew-x-6:hover {
+    --transform-skew-x: 6deg;
+  }
+
+  .md\:hover\:skew-x-12:hover {
+    --transform-skew-x: 12deg;
+  }
+
+  .md\:hover\:-skew-x-12:hover {
+    --transform-skew-x: -12deg;
+  }
+
+  .md\:hover\:-skew-x-6:hover {
+    --transform-skew-x: -6deg;
+  }
+
+  .md\:hover\:-skew-x-3:hover {
+    --transform-skew-x: -3deg;
+  }
+
+  .md\:hover\:skew-y-0:hover {
+    --transform-skew-y: 0;
+  }
+
+  .md\:hover\:skew-y-3:hover {
+    --transform-skew-y: 3deg;
+  }
+
+  .md\:hover\:skew-y-6:hover {
+    --transform-skew-y: 6deg;
+  }
+
+  .md\:hover\:skew-y-12:hover {
+    --transform-skew-y: 12deg;
+  }
+
+  .md\:hover\:-skew-y-12:hover {
+    --transform-skew-y: -12deg;
+  }
+
+  .md\:hover\:-skew-y-6:hover {
+    --transform-skew-y: -6deg;
+  }
+
+  .md\:hover\:-skew-y-3:hover {
+    --transform-skew-y: -3deg;
+  }
+
+  .md\:focus\:skew-x-0:focus {
+    --transform-skew-x: 0;
+  }
+
+  .md\:focus\:skew-x-3:focus {
+    --transform-skew-x: 3deg;
+  }
+
+  .md\:focus\:skew-x-6:focus {
+    --transform-skew-x: 6deg;
+  }
+
+  .md\:focus\:skew-x-12:focus {
+    --transform-skew-x: 12deg;
+  }
+
+  .md\:focus\:-skew-x-12:focus {
+    --transform-skew-x: -12deg;
+  }
+
+  .md\:focus\:-skew-x-6:focus {
+    --transform-skew-x: -6deg;
+  }
+
+  .md\:focus\:-skew-x-3:focus {
+    --transform-skew-x: -3deg;
+  }
+
+  .md\:focus\:skew-y-0:focus {
+    --transform-skew-y: 0;
+  }
+
+  .md\:focus\:skew-y-3:focus {
+    --transform-skew-y: 3deg;
+  }
+
+  .md\:focus\:skew-y-6:focus {
+    --transform-skew-y: 6deg;
+  }
+
+  .md\:focus\:skew-y-12:focus {
+    --transform-skew-y: 12deg;
+  }
+
+  .md\:focus\:-skew-y-12:focus {
+    --transform-skew-y: -12deg;
+  }
+
+  .md\:focus\:-skew-y-6:focus {
+    --transform-skew-y: -6deg;
+  }
+
+  .md\:focus\:-skew-y-3:focus {
+    --transform-skew-y: -3deg;
+  }
+
+  .md\:transition-none {
+    transition-property: none;
+  }
+
+  .md\:transition-all {
+    transition-property: all;
+  }
+
+  .md\:transition {
+    transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+  }
+
+  .md\:transition-colors {
+    transition-property: background-color, border-color, color, fill, stroke;
+  }
+
+  .md\:transition-opacity {
+    transition-property: opacity;
+  }
+
+  .md\:transition-shadow {
+    transition-property: box-shadow;
+  }
+
+  .md\:transition-transform {
+    transition-property: transform;
+  }
+
+  .md\:ease-linear {
+    transition-timing-function: linear;
+  }
+
+  .md\:ease-in {
+    transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+  }
+
+  .md\:ease-out {
+    transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+  }
+
+  .md\:ease-in-out {
+    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .md\:duration-75 {
+    transition-duration: 75ms;
+  }
+
+  .md\:duration-100 {
+    transition-duration: 100ms;
+  }
+
+  .md\:duration-150 {
+    transition-duration: 150ms;
+  }
+
+  .md\:duration-200 {
+    transition-duration: 200ms;
+  }
+
+  .md\:duration-300 {
+    transition-duration: 300ms;
+  }
+
+  .md\:duration-500 {
+    transition-duration: 500ms;
+  }
+
+  .md\:duration-700 {
+    transition-duration: 700ms;
+  }
+
+  .md\:duration-1000 {
+    transition-duration: 1000ms;
+  }
+
+  .md\:delay-75 {
+    transition-delay: 75ms;
+  }
+
+  .md\:delay-100 {
+    transition-delay: 100ms;
+  }
+
+  .md\:delay-150 {
+    transition-delay: 150ms;
+  }
+
+  .md\:delay-200 {
+    transition-delay: 200ms;
+  }
+
+  .md\:delay-300 {
+    transition-delay: 300ms;
+  }
+
+  .md\:delay-500 {
+    transition-delay: 500ms;
+  }
+
+  .md\:delay-700 {
+    transition-delay: 700ms;
+  }
+
+  .md\:delay-1000 {
+    transition-delay: 1000ms;
+  }
+
+  .md\:animate-none {
+    -webkit-animation: none;
+            animation: none;
+  }
+
+  .md\:animate-spin {
+    -webkit-animation: spin 1s linear infinite;
+            animation: spin 1s linear infinite;
+  }
+
+  .md\:animate-ping {
+    -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+            animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+  }
+
+  .md\:animate-pulse {
+    -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+            animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+  }
+
+  .md\:animate-bounce {
+    -webkit-animation: bounce 1s infinite;
+            animation: bounce 1s infinite;
+  }
+}
+
+@media (min-width: 1024px) {
+  .lg\:container {
+    width: 100%;
+  }
+
+  @media (min-width: 640px) {
+    .lg\:container {
+      max-width: 640px;
+    }
+  }
+
+  @media (min-width: 768px) {
+    .lg\:container {
+      max-width: 768px;
+    }
+  }
+
+  @media (min-width: 1024px) {
+    .lg\:container {
+      max-width: 1024px;
+    }
+  }
+
+  @media (min-width: 1280px) {
+    .lg\:container {
+      max-width: 1280px;
+    }
+  }
+
+  .lg\:space-y-0 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0px * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-0 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0px * var(--space-x-reverse));
+    margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.25rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.25rem * var(--space-x-reverse));
+    margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.5rem * var(--space-x-reverse));
+    margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.75rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.75rem * var(--space-x-reverse));
+    margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1rem * var(--space-x-reverse));
+    margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.25rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.25rem * var(--space-x-reverse));
+    margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.5rem * var(--space-x-reverse));
+    margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2rem * var(--space-x-reverse));
+    margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2.5rem * var(--space-x-reverse));
+    margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(3rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(3rem * var(--space-x-reverse));
+    margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(4rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(4rem * var(--space-x-reverse));
+    margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(5rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(5rem * var(--space-x-reverse));
+    margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(6rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(6rem * var(--space-x-reverse));
+    margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(8rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(8rem * var(--space-x-reverse));
+    margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(10rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(10rem * var(--space-x-reverse));
+    margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(12rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(12rem * var(--space-x-reverse));
+    margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(14rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(14rem * var(--space-x-reverse));
+    margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(16rem * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(16rem * var(--space-x-reverse));
+    margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1px * var(--space-y-reverse));
+  }
+
+  .lg\:space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1px * var(--space-x-reverse));
+    margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.25rem * var(--space-x-reverse));
+    margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.5rem * var(--space-x-reverse));
+    margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.75rem * var(--space-x-reverse));
+    margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1rem * var(--space-x-reverse));
+    margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.25rem * var(--space-x-reverse));
+    margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.5rem * var(--space-x-reverse));
+    margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2rem * var(--space-x-reverse));
+    margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2.5rem * var(--space-x-reverse));
+    margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-3rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-3rem * var(--space-x-reverse));
+    margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-4rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-4rem * var(--space-x-reverse));
+    margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-5rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-5rem * var(--space-x-reverse));
+    margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-6rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-6rem * var(--space-x-reverse));
+    margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-8rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-8rem * var(--space-x-reverse));
+    margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-10rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-10rem * var(--space-x-reverse));
+    margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-12rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-12rem * var(--space-x-reverse));
+    margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-14rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-14rem * var(--space-x-reverse));
+    margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-16rem * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-16rem * var(--space-x-reverse));
+    margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:-space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1px * var(--space-y-reverse));
+  }
+
+  .lg\:-space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1px * var(--space-x-reverse));
+    margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .lg\:space-y-reverse > :not(template) ~ :not(template) {
+    --space-y-reverse: 1;
+  }
+
+  .lg\:space-x-reverse > :not(template) ~ :not(template) {
+    --space-x-reverse: 1;
+  }
+
+  .lg\:divide-y-0 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(0px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x-0 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(0px * var(--divide-x-reverse));
+    border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y-2 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(2px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x-2 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(2px * var(--divide-x-reverse));
+    border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y-4 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(4px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x-4 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(4px * var(--divide-x-reverse));
+    border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y-8 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(8px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x-8 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(8px * var(--divide-x-reverse));
+    border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(1px * var(--divide-y-reverse));
+  }
+
+  .lg\:divide-x > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(1px * var(--divide-x-reverse));
+    border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .lg\:divide-y-reverse > :not(template) ~ :not(template) {
+    --divide-y-reverse: 1;
+  }
+
+  .lg\:divide-x-reverse > :not(template) ~ :not(template) {
+    --divide-x-reverse: 1;
+  }
+
+  .lg\:divide-transparent > :not(template) ~ :not(template) {
+    border-color: transparent;
+  }
+
+  .lg\:divide-current > :not(template) ~ :not(template) {
+    border-color: currentColor;
+  }
+
+  .lg\:divide-black > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--divide-opacity));
+  }
+
+  .lg\:divide-white > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--divide-opacity));
+  }
+
+  .lg\:divide-gray-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--divide-opacity));
+  }
+
+  .lg\:divide-red-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--divide-opacity));
+  }
+
+  .lg\:divide-orange-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--divide-opacity));
+  }
+
+  .lg\:divide-yellow-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--divide-opacity));
+  }
+
+  .lg\:divide-green-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--divide-opacity));
+  }
+
+  .lg\:divide-teal-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--divide-opacity));
+  }
+
+  .lg\:divide-blue-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--divide-opacity));
+  }
+
+  .lg\:divide-indigo-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--divide-opacity));
+  }
+
+  .lg\:divide-purple-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--divide-opacity));
+  }
+
+  .lg\:divide-pink-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--divide-opacity));
+  }
+
+  .lg\:divide-solid > :not(template) ~ :not(template) {
+    border-style: solid;
+  }
+
+  .lg\:divide-dashed > :not(template) ~ :not(template) {
+    border-style: dashed;
+  }
+
+  .lg\:divide-dotted > :not(template) ~ :not(template) {
+    border-style: dotted;
+  }
+
+  .lg\:divide-double > :not(template) ~ :not(template) {
+    border-style: double;
+  }
+
+  .lg\:divide-none > :not(template) ~ :not(template) {
+    border-style: none;
+  }
+
+  .lg\:divide-opacity-0 > :not(template) ~ :not(template) {
+    --divide-opacity: 0;
+  }
+
+  .lg\:divide-opacity-25 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.25;
+  }
+
+  .lg\:divide-opacity-50 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.5;
+  }
+
+  .lg\:divide-opacity-75 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.75;
+  }
+
+  .lg\:divide-opacity-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+  }
+
+  .lg\:sr-only {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .lg\:not-sr-only {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .lg\:focus\:sr-only:focus {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .lg\:focus\:not-sr-only:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .lg\:appearance-none {
+    -webkit-appearance: none;
+       -moz-appearance: none;
+            appearance: none;
+  }
+
+  .lg\:bg-fixed {
+    background-attachment: fixed;
+  }
+
+  .lg\:bg-local {
+    background-attachment: local;
+  }
+
+  .lg\:bg-scroll {
+    background-attachment: scroll;
+  }
+
+  .lg\:bg-clip-border {
+    background-clip: border-box;
+  }
+
+  .lg\:bg-clip-padding {
+    background-clip: padding-box;
+  }
+
+  .lg\:bg-clip-content {
+    background-clip: content-box;
+  }
+
+  .lg\:bg-clip-text {
+    -webkit-background-clip: text;
+            background-clip: text;
+  }
+
+  .lg\:bg-transparent {
+    background-color: transparent;
+  }
+
+  .lg\:bg-current {
+    background-color: currentColor;
+  }
+
+  .lg\:bg-black {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .lg\:bg-white {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-100 {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-200 {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-300 {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-400 {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-500 {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-600 {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-700 {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-800 {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .lg\:bg-gray-900 {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-200 {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-300 {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-400 {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-500 {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-600 {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-700 {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-800 {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .lg\:bg-red-900 {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-100 {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-200 {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-300 {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-400 {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-500 {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-600 {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-700 {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-800 {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .lg\:bg-orange-900 {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-100 {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-200 {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-300 {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-400 {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-500 {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-600 {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-700 {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-800 {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .lg\:bg-yellow-900 {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-100 {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-200 {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-300 {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-400 {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-500 {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-600 {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-700 {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-800 {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .lg\:bg-green-900 {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-100 {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-200 {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-300 {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-400 {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-500 {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-600 {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-700 {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-800 {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .lg\:bg-teal-900 {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-100 {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-200 {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-300 {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-400 {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-500 {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-600 {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-700 {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-800 {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .lg\:bg-blue-900 {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-100 {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-200 {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-300 {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-400 {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-500 {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-600 {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-700 {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-800 {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .lg\:bg-indigo-900 {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-100 {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-200 {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-300 {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-400 {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-500 {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-600 {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-700 {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-800 {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .lg\:bg-purple-900 {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-200 {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-300 {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-400 {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-500 {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-600 {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-700 {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-800 {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .lg\:bg-pink-900 {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-transparent:hover {
+    background-color: transparent;
+  }
+
+  .lg\:hover\:bg-current:hover {
+    background-color: currentColor;
+  }
+
+  .lg\:hover\:bg-black:hover {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-white:hover {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-100:hover {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-200:hover {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-300:hover {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-400:hover {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-500:hover {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-600:hover {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-700:hover {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-800:hover {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-gray-900:hover {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-300:hover {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-400:hover {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-500:hover {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-600:hover {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-700:hover {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-800:hover {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-red-900:hover {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-200:hover {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-600:hover {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-700:hover {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-800:hover {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-orange-900:hover {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-200:hover {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-300:hover {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-500:hover {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-600:hover {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-700:hover {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-800:hover {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-yellow-900:hover {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-100:hover {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-200:hover {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-300:hover {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-400:hover {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-500:hover {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-600:hover {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-700:hover {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-800:hover {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-green-900:hover {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-100:hover {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-200:hover {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-300:hover {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-400:hover {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-500:hover {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-600:hover {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-700:hover {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-800:hover {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-teal-900:hover {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-200:hover {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-300:hover {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-400:hover {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-500:hover {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-600:hover {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-700:hover {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-800:hover {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-blue-900:hover {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-200:hover {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-300:hover {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-400:hover {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-500:hover {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-600:hover {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-700:hover {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-800:hover {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-indigo-900:hover {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-100:hover {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-200:hover {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-300:hover {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-400:hover {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-500:hover {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-600:hover {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-700:hover {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-800:hover {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-purple-900:hover {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-400:hover {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-600:hover {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-700:hover {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-800:hover {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .lg\:hover\:bg-pink-900:hover {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-transparent:focus {
+    background-color: transparent;
+  }
+
+  .lg\:focus\:bg-current:focus {
+    background-color: currentColor;
+  }
+
+  .lg\:focus\:bg-black:focus {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-white:focus {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-100:focus {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-200:focus {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-300:focus {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-400:focus {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-500:focus {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-600:focus {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-700:focus {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-800:focus {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-gray-900:focus {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-300:focus {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-400:focus {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-500:focus {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-600:focus {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-700:focus {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-800:focus {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-red-900:focus {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-200:focus {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-600:focus {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-700:focus {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-800:focus {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-orange-900:focus {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-200:focus {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-300:focus {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-500:focus {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-600:focus {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-700:focus {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-800:focus {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-yellow-900:focus {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-100:focus {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-200:focus {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-300:focus {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-400:focus {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-500:focus {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-600:focus {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-700:focus {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-800:focus {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-green-900:focus {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-100:focus {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-200:focus {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-300:focus {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-400:focus {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-500:focus {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-600:focus {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-700:focus {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-800:focus {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-teal-900:focus {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-200:focus {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-300:focus {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-400:focus {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-500:focus {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-600:focus {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-700:focus {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-800:focus {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-blue-900:focus {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-200:focus {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-300:focus {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-400:focus {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-500:focus {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-600:focus {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-700:focus {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-800:focus {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-indigo-900:focus {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-100:focus {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-200:focus {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-300:focus {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-400:focus {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-500:focus {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-600:focus {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-700:focus {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-800:focus {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-purple-900:focus {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-400:focus {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-600:focus {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-700:focus {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-800:focus {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .lg\:focus\:bg-pink-900:focus {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .lg\:bg-none {
+    background-image: none;
+  }
+
+  .lg\:bg-gradient-to-t {
+    background-image: linear-gradient(to top, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-tr {
+    background-image: linear-gradient(to top right, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-r {
+    background-image: linear-gradient(to right, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-br {
+    background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-b {
+    background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-bl {
+    background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-l {
+    background-image: linear-gradient(to left, var(--gradient-color-stops));
+  }
+
+  .lg\:bg-gradient-to-tl {
+    background-image: linear-gradient(to top left, var(--gradient-color-stops));
+  }
+
+  .lg\:from-transparent {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:from-current {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:from-black {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:from-white {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:from-gray-100 {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:from-gray-200 {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:from-gray-300 {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:from-gray-400 {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:from-gray-500 {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:from-gray-600 {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:from-gray-700 {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:from-gray-800 {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:from-gray-900 {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:from-red-100 {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:from-red-200 {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:from-red-300 {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:from-red-400 {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:from-red-500 {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:from-red-600 {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:from-red-700 {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:from-red-800 {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:from-red-900 {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:from-orange-100 {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:from-orange-200 {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:from-orange-300 {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:from-orange-400 {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:from-orange-500 {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:from-orange-600 {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:from-orange-700 {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:from-orange-800 {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:from-orange-900 {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:from-yellow-100 {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:from-yellow-200 {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:from-yellow-300 {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:from-yellow-400 {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:from-yellow-500 {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:from-yellow-600 {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:from-yellow-700 {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:from-yellow-800 {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:from-yellow-900 {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:from-green-100 {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:from-green-200 {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:from-green-300 {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:from-green-400 {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:from-green-500 {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:from-green-600 {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:from-green-700 {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:from-green-800 {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:from-green-900 {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:from-teal-100 {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:from-teal-200 {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:from-teal-300 {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:from-teal-400 {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:from-teal-500 {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:from-teal-600 {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:from-teal-700 {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:from-teal-800 {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:from-teal-900 {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:from-blue-100 {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:from-blue-200 {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:from-blue-300 {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:from-blue-400 {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:from-blue-500 {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:from-blue-600 {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:from-blue-700 {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:from-blue-800 {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:from-blue-900 {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:from-indigo-100 {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:from-indigo-200 {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:from-indigo-300 {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:from-indigo-400 {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:from-indigo-500 {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:from-indigo-600 {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:from-indigo-700 {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:from-indigo-800 {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:from-indigo-900 {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:from-purple-100 {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:from-purple-200 {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:from-purple-300 {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:from-purple-400 {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:from-purple-500 {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:from-purple-600 {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:from-purple-700 {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:from-purple-800 {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:from-purple-900 {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:from-pink-100 {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:from-pink-200 {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:from-pink-300 {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:from-pink-400 {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:from-pink-500 {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:from-pink-600 {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:from-pink-700 {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:from-pink-800 {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:from-pink-900 {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:via-transparent {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:via-current {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:via-black {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:via-white {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:via-gray-100 {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:via-gray-200 {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:via-gray-300 {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:via-gray-400 {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:via-gray-500 {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:via-gray-600 {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:via-gray-700 {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:via-gray-800 {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:via-gray-900 {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:via-red-100 {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:via-red-200 {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:via-red-300 {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:via-red-400 {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:via-red-500 {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:via-red-600 {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:via-red-700 {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:via-red-800 {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:via-red-900 {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:via-orange-100 {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:via-orange-200 {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:via-orange-300 {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:via-orange-400 {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:via-orange-500 {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:via-orange-600 {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:via-orange-700 {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:via-orange-800 {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:via-orange-900 {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:via-yellow-100 {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:via-yellow-200 {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:via-yellow-300 {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:via-yellow-400 {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:via-yellow-500 {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:via-yellow-600 {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:via-yellow-700 {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:via-yellow-800 {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:via-yellow-900 {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:via-green-100 {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:via-green-200 {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:via-green-300 {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:via-green-400 {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:via-green-500 {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:via-green-600 {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:via-green-700 {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:via-green-800 {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:via-green-900 {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:via-teal-100 {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:via-teal-200 {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:via-teal-300 {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:via-teal-400 {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:via-teal-500 {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:via-teal-600 {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:via-teal-700 {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:via-teal-800 {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:via-teal-900 {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:via-blue-100 {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:via-blue-200 {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:via-blue-300 {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:via-blue-400 {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:via-blue-500 {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:via-blue-600 {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:via-blue-700 {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:via-blue-800 {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:via-blue-900 {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:via-indigo-100 {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:via-indigo-200 {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:via-indigo-300 {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:via-indigo-400 {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:via-indigo-500 {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:via-indigo-600 {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:via-indigo-700 {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:via-indigo-800 {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:via-indigo-900 {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:via-purple-100 {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:via-purple-200 {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:via-purple-300 {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:via-purple-400 {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:via-purple-500 {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:via-purple-600 {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:via-purple-700 {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:via-purple-800 {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:via-purple-900 {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:via-pink-100 {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:via-pink-200 {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:via-pink-300 {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:via-pink-400 {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:via-pink-500 {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:via-pink-600 {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:via-pink-700 {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:via-pink-800 {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:via-pink-900 {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:to-transparent {
+    --gradient-to-color: transparent;
+  }
+
+  .lg\:to-current {
+    --gradient-to-color: currentColor;
+  }
+
+  .lg\:to-black {
+    --gradient-to-color: #000;
+  }
+
+  .lg\:to-white {
+    --gradient-to-color: #fff;
+  }
+
+  .lg\:to-gray-100 {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .lg\:to-gray-200 {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .lg\:to-gray-300 {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .lg\:to-gray-400 {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .lg\:to-gray-500 {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .lg\:to-gray-600 {
+    --gradient-to-color: #718096;
+  }
+
+  .lg\:to-gray-700 {
+    --gradient-to-color: #4a5568;
+  }
+
+  .lg\:to-gray-800 {
+    --gradient-to-color: #2d3748;
+  }
+
+  .lg\:to-gray-900 {
+    --gradient-to-color: #1a202c;
+  }
+
+  .lg\:to-red-100 {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .lg\:to-red-200 {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .lg\:to-red-300 {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .lg\:to-red-400 {
+    --gradient-to-color: #fc8181;
+  }
+
+  .lg\:to-red-500 {
+    --gradient-to-color: #f56565;
+  }
+
+  .lg\:to-red-600 {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .lg\:to-red-700 {
+    --gradient-to-color: #c53030;
+  }
+
+  .lg\:to-red-800 {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .lg\:to-red-900 {
+    --gradient-to-color: #742a2a;
+  }
+
+  .lg\:to-orange-100 {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .lg\:to-orange-200 {
+    --gradient-to-color: #feebc8;
+  }
+
+  .lg\:to-orange-300 {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .lg\:to-orange-400 {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .lg\:to-orange-500 {
+    --gradient-to-color: #ed8936;
+  }
+
+  .lg\:to-orange-600 {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .lg\:to-orange-700 {
+    --gradient-to-color: #c05621;
+  }
+
+  .lg\:to-orange-800 {
+    --gradient-to-color: #9c4221;
+  }
+
+  .lg\:to-orange-900 {
+    --gradient-to-color: #7b341e;
+  }
+
+  .lg\:to-yellow-100 {
+    --gradient-to-color: #fffff0;
+  }
+
+  .lg\:to-yellow-200 {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .lg\:to-yellow-300 {
+    --gradient-to-color: #faf089;
+  }
+
+  .lg\:to-yellow-400 {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .lg\:to-yellow-500 {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .lg\:to-yellow-600 {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .lg\:to-yellow-700 {
+    --gradient-to-color: #b7791f;
+  }
+
+  .lg\:to-yellow-800 {
+    --gradient-to-color: #975a16;
+  }
+
+  .lg\:to-yellow-900 {
+    --gradient-to-color: #744210;
+  }
+
+  .lg\:to-green-100 {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .lg\:to-green-200 {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .lg\:to-green-300 {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .lg\:to-green-400 {
+    --gradient-to-color: #68d391;
+  }
+
+  .lg\:to-green-500 {
+    --gradient-to-color: #48bb78;
+  }
+
+  .lg\:to-green-600 {
+    --gradient-to-color: #38a169;
+  }
+
+  .lg\:to-green-700 {
+    --gradient-to-color: #2f855a;
+  }
+
+  .lg\:to-green-800 {
+    --gradient-to-color: #276749;
+  }
+
+  .lg\:to-green-900 {
+    --gradient-to-color: #22543d;
+  }
+
+  .lg\:to-teal-100 {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .lg\:to-teal-200 {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .lg\:to-teal-300 {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .lg\:to-teal-400 {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .lg\:to-teal-500 {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .lg\:to-teal-600 {
+    --gradient-to-color: #319795;
+  }
+
+  .lg\:to-teal-700 {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .lg\:to-teal-800 {
+    --gradient-to-color: #285e61;
+  }
+
+  .lg\:to-teal-900 {
+    --gradient-to-color: #234e52;
+  }
+
+  .lg\:to-blue-100 {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .lg\:to-blue-200 {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .lg\:to-blue-300 {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .lg\:to-blue-400 {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .lg\:to-blue-500 {
+    --gradient-to-color: #4299e1;
+  }
+
+  .lg\:to-blue-600 {
+    --gradient-to-color: #3182ce;
+  }
+
+  .lg\:to-blue-700 {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .lg\:to-blue-800 {
+    --gradient-to-color: #2c5282;
+  }
+
+  .lg\:to-blue-900 {
+    --gradient-to-color: #2a4365;
+  }
+
+  .lg\:to-indigo-100 {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .lg\:to-indigo-200 {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .lg\:to-indigo-300 {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .lg\:to-indigo-400 {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .lg\:to-indigo-500 {
+    --gradient-to-color: #667eea;
+  }
+
+  .lg\:to-indigo-600 {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .lg\:to-indigo-700 {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .lg\:to-indigo-800 {
+    --gradient-to-color: #434190;
+  }
+
+  .lg\:to-indigo-900 {
+    --gradient-to-color: #3c366b;
+  }
+
+  .lg\:to-purple-100 {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .lg\:to-purple-200 {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .lg\:to-purple-300 {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .lg\:to-purple-400 {
+    --gradient-to-color: #b794f4;
+  }
+
+  .lg\:to-purple-500 {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .lg\:to-purple-600 {
+    --gradient-to-color: #805ad5;
+  }
+
+  .lg\:to-purple-700 {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .lg\:to-purple-800 {
+    --gradient-to-color: #553c9a;
+  }
+
+  .lg\:to-purple-900 {
+    --gradient-to-color: #44337a;
+  }
+
+  .lg\:to-pink-100 {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .lg\:to-pink-200 {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .lg\:to-pink-300 {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .lg\:to-pink-400 {
+    --gradient-to-color: #f687b3;
+  }
+
+  .lg\:to-pink-500 {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .lg\:to-pink-600 {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .lg\:to-pink-700 {
+    --gradient-to-color: #b83280;
+  }
+
+  .lg\:to-pink-800 {
+    --gradient-to-color: #97266d;
+  }
+
+  .lg\:to-pink-900 {
+    --gradient-to-color: #702459;
+  }
+
+  .lg\:hover\:from-transparent:hover {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:hover\:from-current:hover {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:hover\:from-black:hover {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:hover\:from-white:hover {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:hover\:from-gray-100:hover {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:hover\:from-gray-200:hover {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:hover\:from-gray-300:hover {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:hover\:from-gray-400:hover {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:hover\:from-gray-500:hover {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:hover\:from-gray-600:hover {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:hover\:from-gray-700:hover {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:hover\:from-gray-800:hover {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:hover\:from-gray-900:hover {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:hover\:from-red-100:hover {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:hover\:from-red-200:hover {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:hover\:from-red-300:hover {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:hover\:from-red-400:hover {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:hover\:from-red-500:hover {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:hover\:from-red-600:hover {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:hover\:from-red-700:hover {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:hover\:from-red-800:hover {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:hover\:from-red-900:hover {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:hover\:from-orange-100:hover {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:hover\:from-orange-200:hover {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:hover\:from-orange-300:hover {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:hover\:from-orange-400:hover {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:hover\:from-orange-500:hover {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:hover\:from-orange-600:hover {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:hover\:from-orange-700:hover {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:hover\:from-orange-800:hover {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:hover\:from-orange-900:hover {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:hover\:from-yellow-100:hover {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:hover\:from-yellow-200:hover {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:hover\:from-yellow-300:hover {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:hover\:from-yellow-400:hover {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:hover\:from-yellow-500:hover {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:hover\:from-yellow-600:hover {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:hover\:from-yellow-700:hover {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:hover\:from-yellow-800:hover {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:hover\:from-yellow-900:hover {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:hover\:from-green-100:hover {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:hover\:from-green-200:hover {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:hover\:from-green-300:hover {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:hover\:from-green-400:hover {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:hover\:from-green-500:hover {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:hover\:from-green-600:hover {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:hover\:from-green-700:hover {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:hover\:from-green-800:hover {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:hover\:from-green-900:hover {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:hover\:from-teal-100:hover {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:hover\:from-teal-200:hover {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:hover\:from-teal-300:hover {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:hover\:from-teal-400:hover {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:hover\:from-teal-500:hover {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:hover\:from-teal-600:hover {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:hover\:from-teal-700:hover {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:hover\:from-teal-800:hover {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:hover\:from-teal-900:hover {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:hover\:from-blue-100:hover {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:hover\:from-blue-200:hover {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:hover\:from-blue-300:hover {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:hover\:from-blue-400:hover {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:hover\:from-blue-500:hover {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:hover\:from-blue-600:hover {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:hover\:from-blue-700:hover {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:hover\:from-blue-800:hover {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:hover\:from-blue-900:hover {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:hover\:from-indigo-100:hover {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:hover\:from-indigo-200:hover {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:hover\:from-indigo-300:hover {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:hover\:from-indigo-400:hover {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:hover\:from-indigo-500:hover {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:hover\:from-indigo-600:hover {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:hover\:from-indigo-700:hover {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:hover\:from-indigo-800:hover {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:hover\:from-indigo-900:hover {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:hover\:from-purple-100:hover {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:hover\:from-purple-200:hover {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:hover\:from-purple-300:hover {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:hover\:from-purple-400:hover {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:hover\:from-purple-500:hover {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:hover\:from-purple-600:hover {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:hover\:from-purple-700:hover {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:hover\:from-purple-800:hover {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:hover\:from-purple-900:hover {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:hover\:from-pink-100:hover {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:hover\:from-pink-200:hover {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:hover\:from-pink-300:hover {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:hover\:from-pink-400:hover {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:hover\:from-pink-500:hover {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:hover\:from-pink-600:hover {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:hover\:from-pink-700:hover {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:hover\:from-pink-800:hover {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:hover\:from-pink-900:hover {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:hover\:via-transparent:hover {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:hover\:via-current:hover {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:hover\:via-black:hover {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:hover\:via-white:hover {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:hover\:via-gray-100:hover {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:hover\:via-gray-200:hover {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:hover\:via-gray-300:hover {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:hover\:via-gray-400:hover {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:hover\:via-gray-500:hover {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:hover\:via-gray-600:hover {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:hover\:via-gray-700:hover {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:hover\:via-gray-800:hover {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:hover\:via-gray-900:hover {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:hover\:via-red-100:hover {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:hover\:via-red-200:hover {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:hover\:via-red-300:hover {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:hover\:via-red-400:hover {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:hover\:via-red-500:hover {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:hover\:via-red-600:hover {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:hover\:via-red-700:hover {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:hover\:via-red-800:hover {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:hover\:via-red-900:hover {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:hover\:via-orange-100:hover {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:hover\:via-orange-200:hover {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:hover\:via-orange-300:hover {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:hover\:via-orange-400:hover {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:hover\:via-orange-500:hover {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:hover\:via-orange-600:hover {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:hover\:via-orange-700:hover {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:hover\:via-orange-800:hover {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:hover\:via-orange-900:hover {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:hover\:via-yellow-100:hover {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:hover\:via-yellow-200:hover {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:hover\:via-yellow-300:hover {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:hover\:via-yellow-400:hover {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:hover\:via-yellow-500:hover {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:hover\:via-yellow-600:hover {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:hover\:via-yellow-700:hover {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:hover\:via-yellow-800:hover {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:hover\:via-yellow-900:hover {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:hover\:via-green-100:hover {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:hover\:via-green-200:hover {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:hover\:via-green-300:hover {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:hover\:via-green-400:hover {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:hover\:via-green-500:hover {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:hover\:via-green-600:hover {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:hover\:via-green-700:hover {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:hover\:via-green-800:hover {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:hover\:via-green-900:hover {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:hover\:via-teal-100:hover {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:hover\:via-teal-200:hover {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:hover\:via-teal-300:hover {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:hover\:via-teal-400:hover {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:hover\:via-teal-500:hover {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:hover\:via-teal-600:hover {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:hover\:via-teal-700:hover {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:hover\:via-teal-800:hover {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:hover\:via-teal-900:hover {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:hover\:via-blue-100:hover {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:hover\:via-blue-200:hover {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:hover\:via-blue-300:hover {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:hover\:via-blue-400:hover {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:hover\:via-blue-500:hover {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:hover\:via-blue-600:hover {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:hover\:via-blue-700:hover {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:hover\:via-blue-800:hover {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:hover\:via-blue-900:hover {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:hover\:via-indigo-100:hover {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:hover\:via-indigo-200:hover {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:hover\:via-indigo-300:hover {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:hover\:via-indigo-400:hover {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:hover\:via-indigo-500:hover {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:hover\:via-indigo-600:hover {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:hover\:via-indigo-700:hover {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:hover\:via-indigo-800:hover {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:hover\:via-indigo-900:hover {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:hover\:via-purple-100:hover {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:hover\:via-purple-200:hover {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:hover\:via-purple-300:hover {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:hover\:via-purple-400:hover {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:hover\:via-purple-500:hover {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:hover\:via-purple-600:hover {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:hover\:via-purple-700:hover {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:hover\:via-purple-800:hover {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:hover\:via-purple-900:hover {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:hover\:via-pink-100:hover {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:hover\:via-pink-200:hover {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:hover\:via-pink-300:hover {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:hover\:via-pink-400:hover {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:hover\:via-pink-500:hover {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:hover\:via-pink-600:hover {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:hover\:via-pink-700:hover {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:hover\:via-pink-800:hover {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:hover\:via-pink-900:hover {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:hover\:to-transparent:hover {
+    --gradient-to-color: transparent;
+  }
+
+  .lg\:hover\:to-current:hover {
+    --gradient-to-color: currentColor;
+  }
+
+  .lg\:hover\:to-black:hover {
+    --gradient-to-color: #000;
+  }
+
+  .lg\:hover\:to-white:hover {
+    --gradient-to-color: #fff;
+  }
+
+  .lg\:hover\:to-gray-100:hover {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .lg\:hover\:to-gray-200:hover {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .lg\:hover\:to-gray-300:hover {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .lg\:hover\:to-gray-400:hover {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .lg\:hover\:to-gray-500:hover {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .lg\:hover\:to-gray-600:hover {
+    --gradient-to-color: #718096;
+  }
+
+  .lg\:hover\:to-gray-700:hover {
+    --gradient-to-color: #4a5568;
+  }
+
+  .lg\:hover\:to-gray-800:hover {
+    --gradient-to-color: #2d3748;
+  }
+
+  .lg\:hover\:to-gray-900:hover {
+    --gradient-to-color: #1a202c;
+  }
+
+  .lg\:hover\:to-red-100:hover {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .lg\:hover\:to-red-200:hover {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .lg\:hover\:to-red-300:hover {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .lg\:hover\:to-red-400:hover {
+    --gradient-to-color: #fc8181;
+  }
+
+  .lg\:hover\:to-red-500:hover {
+    --gradient-to-color: #f56565;
+  }
+
+  .lg\:hover\:to-red-600:hover {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .lg\:hover\:to-red-700:hover {
+    --gradient-to-color: #c53030;
+  }
+
+  .lg\:hover\:to-red-800:hover {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .lg\:hover\:to-red-900:hover {
+    --gradient-to-color: #742a2a;
+  }
+
+  .lg\:hover\:to-orange-100:hover {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .lg\:hover\:to-orange-200:hover {
+    --gradient-to-color: #feebc8;
+  }
+
+  .lg\:hover\:to-orange-300:hover {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .lg\:hover\:to-orange-400:hover {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .lg\:hover\:to-orange-500:hover {
+    --gradient-to-color: #ed8936;
+  }
+
+  .lg\:hover\:to-orange-600:hover {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .lg\:hover\:to-orange-700:hover {
+    --gradient-to-color: #c05621;
+  }
+
+  .lg\:hover\:to-orange-800:hover {
+    --gradient-to-color: #9c4221;
+  }
+
+  .lg\:hover\:to-orange-900:hover {
+    --gradient-to-color: #7b341e;
+  }
+
+  .lg\:hover\:to-yellow-100:hover {
+    --gradient-to-color: #fffff0;
+  }
+
+  .lg\:hover\:to-yellow-200:hover {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .lg\:hover\:to-yellow-300:hover {
+    --gradient-to-color: #faf089;
+  }
+
+  .lg\:hover\:to-yellow-400:hover {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .lg\:hover\:to-yellow-500:hover {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .lg\:hover\:to-yellow-600:hover {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .lg\:hover\:to-yellow-700:hover {
+    --gradient-to-color: #b7791f;
+  }
+
+  .lg\:hover\:to-yellow-800:hover {
+    --gradient-to-color: #975a16;
+  }
+
+  .lg\:hover\:to-yellow-900:hover {
+    --gradient-to-color: #744210;
+  }
+
+  .lg\:hover\:to-green-100:hover {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .lg\:hover\:to-green-200:hover {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .lg\:hover\:to-green-300:hover {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .lg\:hover\:to-green-400:hover {
+    --gradient-to-color: #68d391;
+  }
+
+  .lg\:hover\:to-green-500:hover {
+    --gradient-to-color: #48bb78;
+  }
+
+  .lg\:hover\:to-green-600:hover {
+    --gradient-to-color: #38a169;
+  }
+
+  .lg\:hover\:to-green-700:hover {
+    --gradient-to-color: #2f855a;
+  }
+
+  .lg\:hover\:to-green-800:hover {
+    --gradient-to-color: #276749;
+  }
+
+  .lg\:hover\:to-green-900:hover {
+    --gradient-to-color: #22543d;
+  }
+
+  .lg\:hover\:to-teal-100:hover {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .lg\:hover\:to-teal-200:hover {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .lg\:hover\:to-teal-300:hover {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .lg\:hover\:to-teal-400:hover {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .lg\:hover\:to-teal-500:hover {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .lg\:hover\:to-teal-600:hover {
+    --gradient-to-color: #319795;
+  }
+
+  .lg\:hover\:to-teal-700:hover {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .lg\:hover\:to-teal-800:hover {
+    --gradient-to-color: #285e61;
+  }
+
+  .lg\:hover\:to-teal-900:hover {
+    --gradient-to-color: #234e52;
+  }
+
+  .lg\:hover\:to-blue-100:hover {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .lg\:hover\:to-blue-200:hover {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .lg\:hover\:to-blue-300:hover {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .lg\:hover\:to-blue-400:hover {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .lg\:hover\:to-blue-500:hover {
+    --gradient-to-color: #4299e1;
+  }
+
+  .lg\:hover\:to-blue-600:hover {
+    --gradient-to-color: #3182ce;
+  }
+
+  .lg\:hover\:to-blue-700:hover {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .lg\:hover\:to-blue-800:hover {
+    --gradient-to-color: #2c5282;
+  }
+
+  .lg\:hover\:to-blue-900:hover {
+    --gradient-to-color: #2a4365;
+  }
+
+  .lg\:hover\:to-indigo-100:hover {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .lg\:hover\:to-indigo-200:hover {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .lg\:hover\:to-indigo-300:hover {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .lg\:hover\:to-indigo-400:hover {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .lg\:hover\:to-indigo-500:hover {
+    --gradient-to-color: #667eea;
+  }
+
+  .lg\:hover\:to-indigo-600:hover {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .lg\:hover\:to-indigo-700:hover {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .lg\:hover\:to-indigo-800:hover {
+    --gradient-to-color: #434190;
+  }
+
+  .lg\:hover\:to-indigo-900:hover {
+    --gradient-to-color: #3c366b;
+  }
+
+  .lg\:hover\:to-purple-100:hover {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .lg\:hover\:to-purple-200:hover {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .lg\:hover\:to-purple-300:hover {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .lg\:hover\:to-purple-400:hover {
+    --gradient-to-color: #b794f4;
+  }
+
+  .lg\:hover\:to-purple-500:hover {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .lg\:hover\:to-purple-600:hover {
+    --gradient-to-color: #805ad5;
+  }
+
+  .lg\:hover\:to-purple-700:hover {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .lg\:hover\:to-purple-800:hover {
+    --gradient-to-color: #553c9a;
+  }
+
+  .lg\:hover\:to-purple-900:hover {
+    --gradient-to-color: #44337a;
+  }
+
+  .lg\:hover\:to-pink-100:hover {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .lg\:hover\:to-pink-200:hover {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .lg\:hover\:to-pink-300:hover {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .lg\:hover\:to-pink-400:hover {
+    --gradient-to-color: #f687b3;
+  }
+
+  .lg\:hover\:to-pink-500:hover {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .lg\:hover\:to-pink-600:hover {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .lg\:hover\:to-pink-700:hover {
+    --gradient-to-color: #b83280;
+  }
+
+  .lg\:hover\:to-pink-800:hover {
+    --gradient-to-color: #97266d;
+  }
+
+  .lg\:hover\:to-pink-900:hover {
+    --gradient-to-color: #702459;
+  }
+
+  .lg\:focus\:from-transparent:focus {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:focus\:from-current:focus {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:focus\:from-black:focus {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:focus\:from-white:focus {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:focus\:from-gray-100:focus {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:focus\:from-gray-200:focus {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:focus\:from-gray-300:focus {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:focus\:from-gray-400:focus {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:focus\:from-gray-500:focus {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:focus\:from-gray-600:focus {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:focus\:from-gray-700:focus {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:focus\:from-gray-800:focus {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:focus\:from-gray-900:focus {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:focus\:from-red-100:focus {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:focus\:from-red-200:focus {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:focus\:from-red-300:focus {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:focus\:from-red-400:focus {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:focus\:from-red-500:focus {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:focus\:from-red-600:focus {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:focus\:from-red-700:focus {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:focus\:from-red-800:focus {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:focus\:from-red-900:focus {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:focus\:from-orange-100:focus {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:focus\:from-orange-200:focus {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:focus\:from-orange-300:focus {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:focus\:from-orange-400:focus {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:focus\:from-orange-500:focus {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:focus\:from-orange-600:focus {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:focus\:from-orange-700:focus {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:focus\:from-orange-800:focus {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:focus\:from-orange-900:focus {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:focus\:from-yellow-100:focus {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:focus\:from-yellow-200:focus {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:focus\:from-yellow-300:focus {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:focus\:from-yellow-400:focus {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:focus\:from-yellow-500:focus {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:focus\:from-yellow-600:focus {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:focus\:from-yellow-700:focus {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:focus\:from-yellow-800:focus {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:focus\:from-yellow-900:focus {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:focus\:from-green-100:focus {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:focus\:from-green-200:focus {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:focus\:from-green-300:focus {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:focus\:from-green-400:focus {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:focus\:from-green-500:focus {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:focus\:from-green-600:focus {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:focus\:from-green-700:focus {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:focus\:from-green-800:focus {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:focus\:from-green-900:focus {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:focus\:from-teal-100:focus {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:focus\:from-teal-200:focus {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:focus\:from-teal-300:focus {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:focus\:from-teal-400:focus {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:focus\:from-teal-500:focus {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:focus\:from-teal-600:focus {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:focus\:from-teal-700:focus {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:focus\:from-teal-800:focus {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:focus\:from-teal-900:focus {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:focus\:from-blue-100:focus {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:focus\:from-blue-200:focus {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:focus\:from-blue-300:focus {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:focus\:from-blue-400:focus {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:focus\:from-blue-500:focus {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:focus\:from-blue-600:focus {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:focus\:from-blue-700:focus {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:focus\:from-blue-800:focus {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:focus\:from-blue-900:focus {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:focus\:from-indigo-100:focus {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:focus\:from-indigo-200:focus {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:focus\:from-indigo-300:focus {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:focus\:from-indigo-400:focus {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:focus\:from-indigo-500:focus {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:focus\:from-indigo-600:focus {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:focus\:from-indigo-700:focus {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:focus\:from-indigo-800:focus {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:focus\:from-indigo-900:focus {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:focus\:from-purple-100:focus {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:focus\:from-purple-200:focus {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:focus\:from-purple-300:focus {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:focus\:from-purple-400:focus {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:focus\:from-purple-500:focus {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:focus\:from-purple-600:focus {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:focus\:from-purple-700:focus {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:focus\:from-purple-800:focus {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:focus\:from-purple-900:focus {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:focus\:from-pink-100:focus {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:focus\:from-pink-200:focus {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:focus\:from-pink-300:focus {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:focus\:from-pink-400:focus {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:focus\:from-pink-500:focus {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:focus\:from-pink-600:focus {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:focus\:from-pink-700:focus {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:focus\:from-pink-800:focus {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:focus\:from-pink-900:focus {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:focus\:via-transparent:focus {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:focus\:via-current:focus {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:focus\:via-black:focus {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .lg\:focus\:via-white:focus {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .lg\:focus\:via-gray-100:focus {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .lg\:focus\:via-gray-200:focus {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .lg\:focus\:via-gray-300:focus {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .lg\:focus\:via-gray-400:focus {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .lg\:focus\:via-gray-500:focus {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .lg\:focus\:via-gray-600:focus {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .lg\:focus\:via-gray-700:focus {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .lg\:focus\:via-gray-800:focus {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .lg\:focus\:via-gray-900:focus {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .lg\:focus\:via-red-100:focus {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .lg\:focus\:via-red-200:focus {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .lg\:focus\:via-red-300:focus {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .lg\:focus\:via-red-400:focus {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .lg\:focus\:via-red-500:focus {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .lg\:focus\:via-red-600:focus {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .lg\:focus\:via-red-700:focus {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .lg\:focus\:via-red-800:focus {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .lg\:focus\:via-red-900:focus {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .lg\:focus\:via-orange-100:focus {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .lg\:focus\:via-orange-200:focus {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .lg\:focus\:via-orange-300:focus {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .lg\:focus\:via-orange-400:focus {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .lg\:focus\:via-orange-500:focus {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .lg\:focus\:via-orange-600:focus {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .lg\:focus\:via-orange-700:focus {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .lg\:focus\:via-orange-800:focus {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .lg\:focus\:via-orange-900:focus {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .lg\:focus\:via-yellow-100:focus {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .lg\:focus\:via-yellow-200:focus {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .lg\:focus\:via-yellow-300:focus {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .lg\:focus\:via-yellow-400:focus {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .lg\:focus\:via-yellow-500:focus {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .lg\:focus\:via-yellow-600:focus {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .lg\:focus\:via-yellow-700:focus {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .lg\:focus\:via-yellow-800:focus {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .lg\:focus\:via-yellow-900:focus {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .lg\:focus\:via-green-100:focus {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .lg\:focus\:via-green-200:focus {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .lg\:focus\:via-green-300:focus {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .lg\:focus\:via-green-400:focus {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .lg\:focus\:via-green-500:focus {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .lg\:focus\:via-green-600:focus {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .lg\:focus\:via-green-700:focus {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .lg\:focus\:via-green-800:focus {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .lg\:focus\:via-green-900:focus {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .lg\:focus\:via-teal-100:focus {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .lg\:focus\:via-teal-200:focus {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .lg\:focus\:via-teal-300:focus {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .lg\:focus\:via-teal-400:focus {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .lg\:focus\:via-teal-500:focus {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .lg\:focus\:via-teal-600:focus {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .lg\:focus\:via-teal-700:focus {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .lg\:focus\:via-teal-800:focus {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .lg\:focus\:via-teal-900:focus {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .lg\:focus\:via-blue-100:focus {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .lg\:focus\:via-blue-200:focus {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .lg\:focus\:via-blue-300:focus {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .lg\:focus\:via-blue-400:focus {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .lg\:focus\:via-blue-500:focus {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .lg\:focus\:via-blue-600:focus {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .lg\:focus\:via-blue-700:focus {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .lg\:focus\:via-blue-800:focus {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .lg\:focus\:via-blue-900:focus {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .lg\:focus\:via-indigo-100:focus {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .lg\:focus\:via-indigo-200:focus {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .lg\:focus\:via-indigo-300:focus {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .lg\:focus\:via-indigo-400:focus {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .lg\:focus\:via-indigo-500:focus {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .lg\:focus\:via-indigo-600:focus {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .lg\:focus\:via-indigo-700:focus {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .lg\:focus\:via-indigo-800:focus {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .lg\:focus\:via-indigo-900:focus {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .lg\:focus\:via-purple-100:focus {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .lg\:focus\:via-purple-200:focus {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .lg\:focus\:via-purple-300:focus {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .lg\:focus\:via-purple-400:focus {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .lg\:focus\:via-purple-500:focus {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .lg\:focus\:via-purple-600:focus {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .lg\:focus\:via-purple-700:focus {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .lg\:focus\:via-purple-800:focus {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .lg\:focus\:via-purple-900:focus {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .lg\:focus\:via-pink-100:focus {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .lg\:focus\:via-pink-200:focus {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .lg\:focus\:via-pink-300:focus {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .lg\:focus\:via-pink-400:focus {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .lg\:focus\:via-pink-500:focus {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .lg\:focus\:via-pink-600:focus {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .lg\:focus\:via-pink-700:focus {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .lg\:focus\:via-pink-800:focus {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .lg\:focus\:via-pink-900:focus {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .lg\:focus\:to-transparent:focus {
+    --gradient-to-color: transparent;
+  }
+
+  .lg\:focus\:to-current:focus {
+    --gradient-to-color: currentColor;
+  }
+
+  .lg\:focus\:to-black:focus {
+    --gradient-to-color: #000;
+  }
+
+  .lg\:focus\:to-white:focus {
+    --gradient-to-color: #fff;
+  }
+
+  .lg\:focus\:to-gray-100:focus {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .lg\:focus\:to-gray-200:focus {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .lg\:focus\:to-gray-300:focus {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .lg\:focus\:to-gray-400:focus {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .lg\:focus\:to-gray-500:focus {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .lg\:focus\:to-gray-600:focus {
+    --gradient-to-color: #718096;
+  }
+
+  .lg\:focus\:to-gray-700:focus {
+    --gradient-to-color: #4a5568;
+  }
+
+  .lg\:focus\:to-gray-800:focus {
+    --gradient-to-color: #2d3748;
+  }
+
+  .lg\:focus\:to-gray-900:focus {
+    --gradient-to-color: #1a202c;
+  }
+
+  .lg\:focus\:to-red-100:focus {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .lg\:focus\:to-red-200:focus {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .lg\:focus\:to-red-300:focus {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .lg\:focus\:to-red-400:focus {
+    --gradient-to-color: #fc8181;
+  }
+
+  .lg\:focus\:to-red-500:focus {
+    --gradient-to-color: #f56565;
+  }
+
+  .lg\:focus\:to-red-600:focus {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .lg\:focus\:to-red-700:focus {
+    --gradient-to-color: #c53030;
+  }
+
+  .lg\:focus\:to-red-800:focus {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .lg\:focus\:to-red-900:focus {
+    --gradient-to-color: #742a2a;
+  }
+
+  .lg\:focus\:to-orange-100:focus {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .lg\:focus\:to-orange-200:focus {
+    --gradient-to-color: #feebc8;
+  }
+
+  .lg\:focus\:to-orange-300:focus {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .lg\:focus\:to-orange-400:focus {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .lg\:focus\:to-orange-500:focus {
+    --gradient-to-color: #ed8936;
+  }
+
+  .lg\:focus\:to-orange-600:focus {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .lg\:focus\:to-orange-700:focus {
+    --gradient-to-color: #c05621;
+  }
+
+  .lg\:focus\:to-orange-800:focus {
+    --gradient-to-color: #9c4221;
+  }
+
+  .lg\:focus\:to-orange-900:focus {
+    --gradient-to-color: #7b341e;
+  }
+
+  .lg\:focus\:to-yellow-100:focus {
+    --gradient-to-color: #fffff0;
+  }
+
+  .lg\:focus\:to-yellow-200:focus {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .lg\:focus\:to-yellow-300:focus {
+    --gradient-to-color: #faf089;
+  }
+
+  .lg\:focus\:to-yellow-400:focus {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .lg\:focus\:to-yellow-500:focus {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .lg\:focus\:to-yellow-600:focus {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .lg\:focus\:to-yellow-700:focus {
+    --gradient-to-color: #b7791f;
+  }
+
+  .lg\:focus\:to-yellow-800:focus {
+    --gradient-to-color: #975a16;
+  }
+
+  .lg\:focus\:to-yellow-900:focus {
+    --gradient-to-color: #744210;
+  }
+
+  .lg\:focus\:to-green-100:focus {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .lg\:focus\:to-green-200:focus {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .lg\:focus\:to-green-300:focus {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .lg\:focus\:to-green-400:focus {
+    --gradient-to-color: #68d391;
+  }
+
+  .lg\:focus\:to-green-500:focus {
+    --gradient-to-color: #48bb78;
+  }
+
+  .lg\:focus\:to-green-600:focus {
+    --gradient-to-color: #38a169;
+  }
+
+  .lg\:focus\:to-green-700:focus {
+    --gradient-to-color: #2f855a;
+  }
+
+  .lg\:focus\:to-green-800:focus {
+    --gradient-to-color: #276749;
+  }
+
+  .lg\:focus\:to-green-900:focus {
+    --gradient-to-color: #22543d;
+  }
+
+  .lg\:focus\:to-teal-100:focus {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .lg\:focus\:to-teal-200:focus {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .lg\:focus\:to-teal-300:focus {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .lg\:focus\:to-teal-400:focus {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .lg\:focus\:to-teal-500:focus {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .lg\:focus\:to-teal-600:focus {
+    --gradient-to-color: #319795;
+  }
+
+  .lg\:focus\:to-teal-700:focus {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .lg\:focus\:to-teal-800:focus {
+    --gradient-to-color: #285e61;
+  }
+
+  .lg\:focus\:to-teal-900:focus {
+    --gradient-to-color: #234e52;
+  }
+
+  .lg\:focus\:to-blue-100:focus {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .lg\:focus\:to-blue-200:focus {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .lg\:focus\:to-blue-300:focus {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .lg\:focus\:to-blue-400:focus {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .lg\:focus\:to-blue-500:focus {
+    --gradient-to-color: #4299e1;
+  }
+
+  .lg\:focus\:to-blue-600:focus {
+    --gradient-to-color: #3182ce;
+  }
+
+  .lg\:focus\:to-blue-700:focus {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .lg\:focus\:to-blue-800:focus {
+    --gradient-to-color: #2c5282;
+  }
+
+  .lg\:focus\:to-blue-900:focus {
+    --gradient-to-color: #2a4365;
+  }
+
+  .lg\:focus\:to-indigo-100:focus {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .lg\:focus\:to-indigo-200:focus {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .lg\:focus\:to-indigo-300:focus {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .lg\:focus\:to-indigo-400:focus {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .lg\:focus\:to-indigo-500:focus {
+    --gradient-to-color: #667eea;
+  }
+
+  .lg\:focus\:to-indigo-600:focus {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .lg\:focus\:to-indigo-700:focus {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .lg\:focus\:to-indigo-800:focus {
+    --gradient-to-color: #434190;
+  }
+
+  .lg\:focus\:to-indigo-900:focus {
+    --gradient-to-color: #3c366b;
+  }
+
+  .lg\:focus\:to-purple-100:focus {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .lg\:focus\:to-purple-200:focus {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .lg\:focus\:to-purple-300:focus {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .lg\:focus\:to-purple-400:focus {
+    --gradient-to-color: #b794f4;
+  }
+
+  .lg\:focus\:to-purple-500:focus {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .lg\:focus\:to-purple-600:focus {
+    --gradient-to-color: #805ad5;
+  }
+
+  .lg\:focus\:to-purple-700:focus {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .lg\:focus\:to-purple-800:focus {
+    --gradient-to-color: #553c9a;
+  }
+
+  .lg\:focus\:to-purple-900:focus {
+    --gradient-to-color: #44337a;
+  }
+
+  .lg\:focus\:to-pink-100:focus {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .lg\:focus\:to-pink-200:focus {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .lg\:focus\:to-pink-300:focus {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .lg\:focus\:to-pink-400:focus {
+    --gradient-to-color: #f687b3;
+  }
+
+  .lg\:focus\:to-pink-500:focus {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .lg\:focus\:to-pink-600:focus {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .lg\:focus\:to-pink-700:focus {
+    --gradient-to-color: #b83280;
+  }
+
+  .lg\:focus\:to-pink-800:focus {
+    --gradient-to-color: #97266d;
+  }
+
+  .lg\:focus\:to-pink-900:focus {
+    --gradient-to-color: #702459;
+  }
+
+  .lg\:bg-opacity-0 {
+    --bg-opacity: 0;
+  }
+
+  .lg\:bg-opacity-25 {
+    --bg-opacity: 0.25;
+  }
+
+  .lg\:bg-opacity-50 {
+    --bg-opacity: 0.5;
+  }
+
+  .lg\:bg-opacity-75 {
+    --bg-opacity: 0.75;
+  }
+
+  .lg\:bg-opacity-100 {
+    --bg-opacity: 1;
+  }
+
+  .lg\:hover\:bg-opacity-0:hover {
+    --bg-opacity: 0;
+  }
+
+  .lg\:hover\:bg-opacity-25:hover {
+    --bg-opacity: 0.25;
+  }
+
+  .lg\:hover\:bg-opacity-50:hover {
+    --bg-opacity: 0.5;
+  }
+
+  .lg\:hover\:bg-opacity-75:hover {
+    --bg-opacity: 0.75;
+  }
+
+  .lg\:hover\:bg-opacity-100:hover {
+    --bg-opacity: 1;
+  }
+
+  .lg\:focus\:bg-opacity-0:focus {
+    --bg-opacity: 0;
+  }
+
+  .lg\:focus\:bg-opacity-25:focus {
+    --bg-opacity: 0.25;
+  }
+
+  .lg\:focus\:bg-opacity-50:focus {
+    --bg-opacity: 0.5;
+  }
+
+  .lg\:focus\:bg-opacity-75:focus {
+    --bg-opacity: 0.75;
+  }
+
+  .lg\:focus\:bg-opacity-100:focus {
+    --bg-opacity: 1;
+  }
+
+  .lg\:bg-bottom {
+    background-position: bottom;
+  }
+
+  .lg\:bg-center {
+    background-position: center;
+  }
+
+  .lg\:bg-left {
+    background-position: left;
+  }
+
+  .lg\:bg-left-bottom {
+    background-position: left bottom;
+  }
+
+  .lg\:bg-left-top {
+    background-position: left top;
+  }
+
+  .lg\:bg-right {
+    background-position: right;
+  }
+
+  .lg\:bg-right-bottom {
+    background-position: right bottom;
+  }
+
+  .lg\:bg-right-top {
+    background-position: right top;
+  }
+
+  .lg\:bg-top {
+    background-position: top;
+  }
+
+  .lg\:bg-repeat {
+    background-repeat: repeat;
+  }
+
+  .lg\:bg-no-repeat {
+    background-repeat: no-repeat;
+  }
+
+  .lg\:bg-repeat-x {
+    background-repeat: repeat-x;
+  }
+
+  .lg\:bg-repeat-y {
+    background-repeat: repeat-y;
+  }
+
+  .lg\:bg-repeat-round {
+    background-repeat: round;
+  }
+
+  .lg\:bg-repeat-space {
+    background-repeat: space;
+  }
+
+  .lg\:bg-auto {
+    background-size: auto;
+  }
+
+  .lg\:bg-cover {
+    background-size: cover;
+  }
+
+  .lg\:bg-contain {
+    background-size: contain;
+  }
+
+  .lg\:border-collapse {
+    border-collapse: collapse;
+  }
+
+  .lg\:border-separate {
+    border-collapse: separate;
+  }
+
+  .lg\:border-transparent {
+    border-color: transparent;
+  }
+
+  .lg\:border-current {
+    border-color: currentColor;
+  }
+
+  .lg\:border-black {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .lg\:border-white {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .lg\:border-gray-100 {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .lg\:border-gray-200 {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .lg\:border-gray-300 {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .lg\:border-gray-400 {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .lg\:border-gray-500 {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .lg\:border-gray-600 {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .lg\:border-gray-700 {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .lg\:border-gray-800 {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .lg\:border-gray-900 {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .lg\:border-red-100 {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .lg\:border-red-200 {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .lg\:border-red-300 {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .lg\:border-red-400 {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .lg\:border-red-500 {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .lg\:border-red-600 {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .lg\:border-red-700 {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .lg\:border-red-800 {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .lg\:border-red-900 {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .lg\:border-orange-100 {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .lg\:border-orange-200 {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .lg\:border-orange-300 {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .lg\:border-orange-400 {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .lg\:border-orange-500 {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .lg\:border-orange-600 {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .lg\:border-orange-700 {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .lg\:border-orange-800 {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .lg\:border-orange-900 {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-100 {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-200 {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-300 {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-400 {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-500 {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-600 {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-700 {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-800 {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .lg\:border-yellow-900 {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .lg\:border-green-100 {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .lg\:border-green-200 {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .lg\:border-green-300 {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .lg\:border-green-400 {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .lg\:border-green-500 {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .lg\:border-green-600 {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .lg\:border-green-700 {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .lg\:border-green-800 {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .lg\:border-green-900 {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .lg\:border-teal-100 {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .lg\:border-teal-200 {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .lg\:border-teal-300 {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .lg\:border-teal-400 {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .lg\:border-teal-500 {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .lg\:border-teal-600 {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .lg\:border-teal-700 {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .lg\:border-teal-800 {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .lg\:border-teal-900 {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .lg\:border-blue-100 {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .lg\:border-blue-200 {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .lg\:border-blue-300 {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .lg\:border-blue-400 {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .lg\:border-blue-500 {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .lg\:border-blue-600 {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .lg\:border-blue-700 {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .lg\:border-blue-800 {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .lg\:border-blue-900 {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-100 {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-200 {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-300 {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-400 {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-500 {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-600 {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-700 {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-800 {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .lg\:border-indigo-900 {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .lg\:border-purple-100 {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .lg\:border-purple-200 {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .lg\:border-purple-300 {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .lg\:border-purple-400 {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .lg\:border-purple-500 {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .lg\:border-purple-600 {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .lg\:border-purple-700 {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .lg\:border-purple-800 {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .lg\:border-purple-900 {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .lg\:border-pink-100 {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .lg\:border-pink-200 {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .lg\:border-pink-300 {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .lg\:border-pink-400 {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .lg\:border-pink-500 {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .lg\:border-pink-600 {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .lg\:border-pink-700 {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .lg\:border-pink-800 {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .lg\:border-pink-900 {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-transparent:hover {
+    border-color: transparent;
+  }
+
+  .lg\:hover\:border-current:hover {
+    border-color: currentColor;
+  }
+
+  .lg\:hover\:border-black:hover {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-white:hover {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-100:hover {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-200:hover {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-300:hover {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-400:hover {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-500:hover {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-600:hover {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-700:hover {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-800:hover {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-gray-900:hover {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-300:hover {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-400:hover {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-500:hover {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-600:hover {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-700:hover {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-800:hover {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-red-900:hover {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-100:hover {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-200:hover {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-300:hover {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-400:hover {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-500:hover {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-600:hover {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-700:hover {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-800:hover {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-orange-900:hover {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-100:hover {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-200:hover {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-300:hover {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-400:hover {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-500:hover {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-600:hover {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-700:hover {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-800:hover {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-yellow-900:hover {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-100:hover {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-200:hover {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-300:hover {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-400:hover {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-500:hover {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-600:hover {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-700:hover {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-800:hover {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-green-900:hover {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-100:hover {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-200:hover {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-300:hover {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-400:hover {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-500:hover {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-600:hover {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-700:hover {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-800:hover {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-teal-900:hover {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-200:hover {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-300:hover {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-400:hover {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-500:hover {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-600:hover {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-700:hover {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-800:hover {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-blue-900:hover {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-200:hover {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-300:hover {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-400:hover {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-500:hover {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-600:hover {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-700:hover {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-800:hover {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-indigo-900:hover {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-100:hover {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-200:hover {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-300:hover {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-400:hover {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-500:hover {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-600:hover {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-700:hover {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-800:hover {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-purple-900:hover {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-300:hover {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-400:hover {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-500:hover {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-600:hover {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-700:hover {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-800:hover {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .lg\:hover\:border-pink-900:hover {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-transparent:focus {
+    border-color: transparent;
+  }
+
+  .lg\:focus\:border-current:focus {
+    border-color: currentColor;
+  }
+
+  .lg\:focus\:border-black:focus {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-white:focus {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-100:focus {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-200:focus {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-300:focus {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-400:focus {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-500:focus {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-600:focus {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-700:focus {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-800:focus {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-gray-900:focus {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-300:focus {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-400:focus {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-500:focus {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-600:focus {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-700:focus {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-800:focus {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-red-900:focus {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-100:focus {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-200:focus {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-300:focus {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-400:focus {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-500:focus {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-600:focus {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-700:focus {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-800:focus {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-orange-900:focus {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-100:focus {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-200:focus {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-300:focus {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-400:focus {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-500:focus {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-600:focus {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-700:focus {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-800:focus {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-yellow-900:focus {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-100:focus {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-200:focus {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-300:focus {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-400:focus {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-500:focus {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-600:focus {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-700:focus {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-800:focus {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-green-900:focus {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-100:focus {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-200:focus {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-300:focus {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-400:focus {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-500:focus {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-600:focus {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-700:focus {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-800:focus {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-teal-900:focus {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-200:focus {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-300:focus {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-400:focus {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-500:focus {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-600:focus {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-700:focus {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-800:focus {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-blue-900:focus {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-200:focus {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-300:focus {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-400:focus {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-500:focus {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-600:focus {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-700:focus {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-800:focus {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-indigo-900:focus {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-100:focus {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-200:focus {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-300:focus {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-400:focus {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-500:focus {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-600:focus {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-700:focus {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-800:focus {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-purple-900:focus {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-300:focus {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-400:focus {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-500:focus {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-600:focus {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-700:focus {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-800:focus {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .lg\:focus\:border-pink-900:focus {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .lg\:border-opacity-0 {
+    --border-opacity: 0;
+  }
+
+  .lg\:border-opacity-25 {
+    --border-opacity: 0.25;
+  }
+
+  .lg\:border-opacity-50 {
+    --border-opacity: 0.5;
+  }
+
+  .lg\:border-opacity-75 {
+    --border-opacity: 0.75;
+  }
+
+  .lg\:border-opacity-100 {
+    --border-opacity: 1;
+  }
+
+  .lg\:hover\:border-opacity-0:hover {
+    --border-opacity: 0;
+  }
+
+  .lg\:hover\:border-opacity-25:hover {
+    --border-opacity: 0.25;
+  }
+
+  .lg\:hover\:border-opacity-50:hover {
+    --border-opacity: 0.5;
+  }
+
+  .lg\:hover\:border-opacity-75:hover {
+    --border-opacity: 0.75;
+  }
+
+  .lg\:hover\:border-opacity-100:hover {
+    --border-opacity: 1;
+  }
+
+  .lg\:focus\:border-opacity-0:focus {
+    --border-opacity: 0;
+  }
+
+  .lg\:focus\:border-opacity-25:focus {
+    --border-opacity: 0.25;
+  }
+
+  .lg\:focus\:border-opacity-50:focus {
+    --border-opacity: 0.5;
+  }
+
+  .lg\:focus\:border-opacity-75:focus {
+    --border-opacity: 0.75;
+  }
+
+  .lg\:focus\:border-opacity-100:focus {
+    --border-opacity: 1;
+  }
+
+  .lg\:rounded-none {
+    border-radius: 0;
+  }
+
+  .lg\:rounded-sm {
+    border-radius: 0.125rem;
+  }
+
+  .lg\:rounded {
+    border-radius: 0.25rem;
+  }
+
+  .lg\:rounded-md {
+    border-radius: 0.375rem;
+  }
+
+  .lg\:rounded-lg {
+    border-radius: 0.5rem;
+  }
+
+  .lg\:rounded-full {
+    border-radius: 9999px;
+  }
+
+  .lg\:rounded-t-none {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+  }
+
+  .lg\:rounded-r-none {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+  }
+
+  .lg\:rounded-b-none {
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .lg\:rounded-l-none {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .lg\:rounded-t-sm {
+    border-top-left-radius: 0.125rem;
+    border-top-right-radius: 0.125rem;
+  }
+
+  .lg\:rounded-r-sm {
+    border-top-right-radius: 0.125rem;
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .lg\:rounded-b-sm {
+    border-bottom-right-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .lg\:rounded-l-sm {
+    border-top-left-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .lg\:rounded-t {
+    border-top-left-radius: 0.25rem;
+    border-top-right-radius: 0.25rem;
+  }
+
+  .lg\:rounded-r {
+    border-top-right-radius: 0.25rem;
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .lg\:rounded-b {
+    border-bottom-right-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .lg\:rounded-l {
+    border-top-left-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .lg\:rounded-t-md {
+    border-top-left-radius: 0.375rem;
+    border-top-right-radius: 0.375rem;
+  }
+
+  .lg\:rounded-r-md {
+    border-top-right-radius: 0.375rem;
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .lg\:rounded-b-md {
+    border-bottom-right-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .lg\:rounded-l-md {
+    border-top-left-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .lg\:rounded-t-lg {
+    border-top-left-radius: 0.5rem;
+    border-top-right-radius: 0.5rem;
+  }
+
+  .lg\:rounded-r-lg {
+    border-top-right-radius: 0.5rem;
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .lg\:rounded-b-lg {
+    border-bottom-right-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .lg\:rounded-l-lg {
+    border-top-left-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .lg\:rounded-t-full {
+    border-top-left-radius: 9999px;
+    border-top-right-radius: 9999px;
+  }
+
+  .lg\:rounded-r-full {
+    border-top-right-radius: 9999px;
+    border-bottom-right-radius: 9999px;
+  }
+
+  .lg\:rounded-b-full {
+    border-bottom-right-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .lg\:rounded-l-full {
+    border-top-left-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .lg\:rounded-tl-none {
+    border-top-left-radius: 0;
+  }
+
+  .lg\:rounded-tr-none {
+    border-top-right-radius: 0;
+  }
+
+  .lg\:rounded-br-none {
+    border-bottom-right-radius: 0;
+  }
+
+  .lg\:rounded-bl-none {
+    border-bottom-left-radius: 0;
+  }
+
+  .lg\:rounded-tl-sm {
+    border-top-left-radius: 0.125rem;
+  }
+
+  .lg\:rounded-tr-sm {
+    border-top-right-radius: 0.125rem;
+  }
+
+  .lg\:rounded-br-sm {
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .lg\:rounded-bl-sm {
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .lg\:rounded-tl {
+    border-top-left-radius: 0.25rem;
+  }
+
+  .lg\:rounded-tr {
+    border-top-right-radius: 0.25rem;
+  }
+
+  .lg\:rounded-br {
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .lg\:rounded-bl {
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .lg\:rounded-tl-md {
+    border-top-left-radius: 0.375rem;
+  }
+
+  .lg\:rounded-tr-md {
+    border-top-right-radius: 0.375rem;
+  }
+
+  .lg\:rounded-br-md {
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .lg\:rounded-bl-md {
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .lg\:rounded-tl-lg {
+    border-top-left-radius: 0.5rem;
+  }
+
+  .lg\:rounded-tr-lg {
+    border-top-right-radius: 0.5rem;
+  }
+
+  .lg\:rounded-br-lg {
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .lg\:rounded-bl-lg {
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .lg\:rounded-tl-full {
+    border-top-left-radius: 9999px;
+  }
+
+  .lg\:rounded-tr-full {
+    border-top-right-radius: 9999px;
+  }
+
+  .lg\:rounded-br-full {
+    border-bottom-right-radius: 9999px;
+  }
+
+  .lg\:rounded-bl-full {
+    border-bottom-left-radius: 9999px;
+  }
+
+  .lg\:border-solid {
+    border-style: solid;
+  }
+
+  .lg\:border-dashed {
+    border-style: dashed;
+  }
+
+  .lg\:border-dotted {
+    border-style: dotted;
+  }
+
+  .lg\:border-double {
+    border-style: double;
+  }
+
+  .lg\:border-none {
+    border-style: none;
+  }
+
+  .lg\:border-0 {
+    border-width: 0;
+  }
+
+  .lg\:border-2 {
+    border-width: 2px;
+  }
+
+  .lg\:border-4 {
+    border-width: 4px;
+  }
+
+  .lg\:border-8 {
+    border-width: 8px;
+  }
+
+  .lg\:border {
+    border-width: 1px;
+  }
+
+  .lg\:border-t-0 {
+    border-top-width: 0;
+  }
+
+  .lg\:border-r-0 {
+    border-right-width: 0;
+  }
+
+  .lg\:border-b-0 {
+    border-bottom-width: 0;
+  }
+
+  .lg\:border-l-0 {
+    border-left-width: 0;
+  }
+
+  .lg\:border-t-2 {
+    border-top-width: 2px;
+  }
+
+  .lg\:border-r-2 {
+    border-right-width: 2px;
+  }
+
+  .lg\:border-b-2 {
+    border-bottom-width: 2px;
+  }
+
+  .lg\:border-l-2 {
+    border-left-width: 2px;
+  }
+
+  .lg\:border-t-4 {
+    border-top-width: 4px;
+  }
+
+  .lg\:border-r-4 {
+    border-right-width: 4px;
+  }
+
+  .lg\:border-b-4 {
+    border-bottom-width: 4px;
+  }
+
+  .lg\:border-l-4 {
+    border-left-width: 4px;
+  }
+
+  .lg\:border-t-8 {
+    border-top-width: 8px;
+  }
+
+  .lg\:border-r-8 {
+    border-right-width: 8px;
+  }
+
+  .lg\:border-b-8 {
+    border-bottom-width: 8px;
+  }
+
+  .lg\:border-l-8 {
+    border-left-width: 8px;
+  }
+
+  .lg\:border-t {
+    border-top-width: 1px;
+  }
+
+  .lg\:border-r {
+    border-right-width: 1px;
+  }
+
+  .lg\:border-b {
+    border-bottom-width: 1px;
+  }
+
+  .lg\:border-l {
+    border-left-width: 1px;
+  }
+
+  .lg\:box-border {
+    box-sizing: border-box;
+  }
+
+  .lg\:box-content {
+    box-sizing: content-box;
+  }
+
+  .lg\:cursor-auto {
+    cursor: auto;
+  }
+
+  .lg\:cursor-default {
+    cursor: default;
+  }
+
+  .lg\:cursor-pointer {
+    cursor: pointer;
+  }
+
+  .lg\:cursor-wait {
+    cursor: wait;
+  }
+
+  .lg\:cursor-text {
+    cursor: text;
+  }
+
+  .lg\:cursor-move {
+    cursor: move;
+  }
+
+  .lg\:cursor-not-allowed {
+    cursor: not-allowed;
+  }
+
+  .lg\:block {
+    display: block;
+  }
+
+  .lg\:inline-block {
+    display: inline-block;
+  }
+
+  .lg\:inline {
+    display: inline;
+  }
+
+  .lg\:flex {
+    display: flex;
+  }
+
+  .lg\:inline-flex {
+    display: inline-flex;
+  }
+
+  .lg\:table {
+    display: table;
+  }
+
+  .lg\:table-caption {
+    display: table-caption;
+  }
+
+  .lg\:table-cell {
+    display: table-cell;
+  }
+
+  .lg\:table-column {
+    display: table-column;
+  }
+
+  .lg\:table-column-group {
+    display: table-column-group;
+  }
+
+  .lg\:table-footer-group {
+    display: table-footer-group;
+  }
+
+  .lg\:table-header-group {
+    display: table-header-group;
+  }
+
+  .lg\:table-row-group {
+    display: table-row-group;
+  }
+
+  .lg\:table-row {
+    display: table-row;
+  }
+
+  .lg\:flow-root {
+    display: flow-root;
+  }
+
+  .lg\:grid {
+    display: grid;
+  }
+
+  .lg\:inline-grid {
+    display: inline-grid;
+  }
+
+  .lg\:contents {
+    display: contents;
+  }
+
+  .lg\:hidden {
+    display: none;
+  }
+
+  .lg\:flex-row {
+    flex-direction: row;
+  }
+
+  .lg\:flex-row-reverse {
+    flex-direction: row-reverse;
+  }
+
+  .lg\:flex-col {
+    flex-direction: column;
+  }
+
+  .lg\:flex-col-reverse {
+    flex-direction: column-reverse;
+  }
+
+  .lg\:flex-wrap {
+    flex-wrap: wrap;
+  }
+
+  .lg\:flex-wrap-reverse {
+    flex-wrap: wrap-reverse;
+  }
+
+  .lg\:flex-no-wrap {
+    flex-wrap: nowrap;
+  }
+
+  .lg\:place-items-auto {
+    place-items: auto;
+  }
+
+  .lg\:place-items-start {
+    place-items: start;
+  }
+
+  .lg\:place-items-end {
+    place-items: end;
+  }
+
+  .lg\:place-items-center {
+    place-items: center;
+  }
+
+  .lg\:place-items-stretch {
+    place-items: stretch;
+  }
+
+  .lg\:place-content-center {
+    place-content: center;
+  }
+
+  .lg\:place-content-start {
+    place-content: start;
+  }
+
+  .lg\:place-content-end {
+    place-content: end;
+  }
+
+  .lg\:place-content-between {
+    place-content: space-between;
+  }
+
+  .lg\:place-content-around {
+    place-content: space-around;
+  }
+
+  .lg\:place-content-evenly {
+    place-content: space-evenly;
+  }
+
+  .lg\:place-content-stretch {
+    place-content: stretch;
+  }
+
+  .lg\:place-self-auto {
+    place-self: auto;
+  }
+
+  .lg\:place-self-start {
+    place-self: start;
+  }
+
+  .lg\:place-self-end {
+    place-self: end;
+  }
+
+  .lg\:place-self-center {
+    place-self: center;
+  }
+
+  .lg\:place-self-stretch {
+    place-self: stretch;
+  }
+
+  .lg\:items-start {
+    align-items: flex-start;
+  }
+
+  .lg\:items-end {
+    align-items: flex-end;
+  }
+
+  .lg\:items-center {
+    align-items: center;
+  }
+
+  .lg\:items-baseline {
+    align-items: baseline;
+  }
+
+  .lg\:items-stretch {
+    align-items: stretch;
+  }
+
+  .lg\:content-center {
+    align-content: center;
+  }
+
+  .lg\:content-start {
+    align-content: flex-start;
+  }
+
+  .lg\:content-end {
+    align-content: flex-end;
+  }
+
+  .lg\:content-between {
+    align-content: space-between;
+  }
+
+  .lg\:content-around {
+    align-content: space-around;
+  }
+
+  .lg\:content-evenly {
+    align-content: space-evenly;
+  }
+
+  .lg\:self-auto {
+    align-self: auto;
+  }
+
+  .lg\:self-start {
+    align-self: flex-start;
+  }
+
+  .lg\:self-end {
+    align-self: flex-end;
+  }
+
+  .lg\:self-center {
+    align-self: center;
+  }
+
+  .lg\:self-stretch {
+    align-self: stretch;
+  }
+
+  .lg\:justify-items-auto {
+    justify-items: auto;
+  }
+
+  .lg\:justify-items-start {
+    justify-items: start;
+  }
+
+  .lg\:justify-items-end {
+    justify-items: end;
+  }
+
+  .lg\:justify-items-center {
+    justify-items: center;
+  }
+
+  .lg\:justify-items-stretch {
+    justify-items: stretch;
+  }
+
+  .lg\:justify-start {
+    justify-content: flex-start;
+  }
+
+  .lg\:justify-end {
+    justify-content: flex-end;
+  }
+
+  .lg\:justify-center {
+    justify-content: center;
+  }
+
+  .lg\:justify-between {
+    justify-content: space-between;
+  }
+
+  .lg\:justify-around {
+    justify-content: space-around;
+  }
+
+  .lg\:justify-evenly {
+    justify-content: space-evenly;
+  }
+
+  .lg\:justify-self-auto {
+    justify-self: auto;
+  }
+
+  .lg\:justify-self-start {
+    justify-self: start;
+  }
+
+  .lg\:justify-self-end {
+    justify-self: end;
+  }
+
+  .lg\:justify-self-center {
+    justify-self: center;
+  }
+
+  .lg\:justify-self-stretch {
+    justify-self: stretch;
+  }
+
+  .lg\:flex-1 {
+    flex: 1 1 0%;
+  }
+
+  .lg\:flex-auto {
+    flex: 1 1 auto;
+  }
+
+  .lg\:flex-initial {
+    flex: 0 1 auto;
+  }
+
+  .lg\:flex-none {
+    flex: none;
+  }
+
+  .lg\:flex-grow-0 {
+    flex-grow: 0;
+  }
+
+  .lg\:flex-grow {
+    flex-grow: 1;
+  }
+
+  .lg\:flex-shrink-0 {
+    flex-shrink: 0;
+  }
+
+  .lg\:flex-shrink {
+    flex-shrink: 1;
+  }
+
+  .lg\:order-1 {
+    order: 1;
+  }
+
+  .lg\:order-2 {
+    order: 2;
+  }
+
+  .lg\:order-3 {
+    order: 3;
+  }
+
+  .lg\:order-4 {
+    order: 4;
+  }
+
+  .lg\:order-5 {
+    order: 5;
+  }
+
+  .lg\:order-6 {
+    order: 6;
+  }
+
+  .lg\:order-7 {
+    order: 7;
+  }
+
+  .lg\:order-8 {
+    order: 8;
+  }
+
+  .lg\:order-9 {
+    order: 9;
+  }
+
+  .lg\:order-10 {
+    order: 10;
+  }
+
+  .lg\:order-11 {
+    order: 11;
+  }
+
+  .lg\:order-12 {
+    order: 12;
+  }
+
+  .lg\:order-first {
+    order: -9999;
+  }
+
+  .lg\:order-last {
+    order: 9999;
+  }
+
+  .lg\:order-none {
+    order: 0;
+  }
+
+  .lg\:float-right {
+    float: right;
+  }
+
+  .lg\:float-left {
+    float: left;
+  }
+
+  .lg\:float-none {
+    float: none;
+  }
+
+  .lg\:clearfix:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+
+  .lg\:clear-left {
+    clear: left;
+  }
+
+  .lg\:clear-right {
+    clear: right;
+  }
+
+  .lg\:clear-both {
+    clear: both;
+  }
+
+  .lg\:clear-none {
+    clear: none;
+  }
+
+  .lg\:font-sans {
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  }
+
+  .lg\:font-serif {
+    font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+  }
+
+  .lg\:font-mono {
+    font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+  }
+
+  .lg\:font-hairline {
+    font-weight: 100;
+  }
+
+  .lg\:font-thin {
+    font-weight: 200;
+  }
+
+  .lg\:font-light {
+    font-weight: 300;
+  }
+
+  .lg\:font-normal {
+    font-weight: 400;
+  }
+
+  .lg\:font-medium {
+    font-weight: 500;
+  }
+
+  .lg\:font-semibold {
+    font-weight: 600;
+  }
+
+  .lg\:font-bold {
+    font-weight: 700;
+  }
+
+  .lg\:font-extrabold {
+    font-weight: 800;
+  }
+
+  .lg\:font-black {
+    font-weight: 900;
+  }
+
+  .lg\:hover\:font-hairline:hover {
+    font-weight: 100;
+  }
+
+  .lg\:hover\:font-thin:hover {
+    font-weight: 200;
+  }
+
+  .lg\:hover\:font-light:hover {
+    font-weight: 300;
+  }
+
+  .lg\:hover\:font-normal:hover {
+    font-weight: 400;
+  }
+
+  .lg\:hover\:font-medium:hover {
+    font-weight: 500;
+  }
+
+  .lg\:hover\:font-semibold:hover {
+    font-weight: 600;
+  }
+
+  .lg\:hover\:font-bold:hover {
+    font-weight: 700;
+  }
+
+  .lg\:hover\:font-extrabold:hover {
+    font-weight: 800;
+  }
+
+  .lg\:hover\:font-black:hover {
+    font-weight: 900;
+  }
+
+  .lg\:focus\:font-hairline:focus {
+    font-weight: 100;
+  }
+
+  .lg\:focus\:font-thin:focus {
+    font-weight: 200;
+  }
+
+  .lg\:focus\:font-light:focus {
+    font-weight: 300;
+  }
+
+  .lg\:focus\:font-normal:focus {
+    font-weight: 400;
+  }
+
+  .lg\:focus\:font-medium:focus {
+    font-weight: 500;
+  }
+
+  .lg\:focus\:font-semibold:focus {
+    font-weight: 600;
+  }
+
+  .lg\:focus\:font-bold:focus {
+    font-weight: 700;
+  }
+
+  .lg\:focus\:font-extrabold:focus {
+    font-weight: 800;
+  }
+
+  .lg\:focus\:font-black:focus {
+    font-weight: 900;
+  }
+
+  .lg\:h-0 {
+    height: 0;
+  }
+
+  .lg\:h-1 {
+    height: 0.25rem;
+  }
+
+  .lg\:h-2 {
+    height: 0.5rem;
+  }
+
+  .lg\:h-3 {
+    height: 0.75rem;
+  }
+
+  .lg\:h-4 {
+    height: 1rem;
+  }
+
+  .lg\:h-5 {
+    height: 1.25rem;
+  }
+
+  .lg\:h-6 {
+    height: 1.5rem;
+  }
+
+  .lg\:h-8 {
+    height: 2rem;
+  }
+
+  .lg\:h-10 {
+    height: 2.5rem;
+  }
+
+  .lg\:h-12 {
+    height: 3rem;
+  }
+
+  .lg\:h-16 {
+    height: 4rem;
+  }
+
+  .lg\:h-20 {
+    height: 5rem;
+  }
+
+  .lg\:h-24 {
+    height: 6rem;
+  }
+
+  .lg\:h-32 {
+    height: 8rem;
+  }
+
+  .lg\:h-40 {
+    height: 10rem;
+  }
+
+  .lg\:h-48 {
+    height: 12rem;
+  }
+
+  .lg\:h-56 {
+    height: 14rem;
+  }
+
+  .lg\:h-64 {
+    height: 16rem;
+  }
+
+  .lg\:h-auto {
+    height: auto;
+  }
+
+  .lg\:h-px {
+    height: 1px;
+  }
+
+  .lg\:h-full {
+    height: 100%;
+  }
+
+  .lg\:h-screen {
+    height: 100vh;
+  }
+
+  .lg\:text-xs {
+    font-size: 0.75rem;
+  }
+
+  .lg\:text-sm {
+    font-size: 0.875rem;
+  }
+
+  .lg\:text-base {
+    font-size: 1rem;
+  }
+
+  .lg\:text-lg {
+    font-size: 1.125rem;
+  }
+
+  .lg\:text-xl {
+    font-size: 1.25rem;
+  }
+
+  .lg\:text-2xl {
+    font-size: 1.5rem;
+  }
+
+  .lg\:text-3xl {
+    font-size: 1.875rem;
+  }
+
+  .lg\:text-4xl {
+    font-size: 2.25rem;
+  }
+
+  .lg\:text-5xl {
+    font-size: 3rem;
+  }
+
+  .lg\:text-6xl {
+    font-size: 4rem;
+  }
+
+  .lg\:leading-3 {
+    line-height: .75rem;
+  }
+
+  .lg\:leading-4 {
+    line-height: 1rem;
+  }
+
+  .lg\:leading-5 {
+    line-height: 1.25rem;
+  }
+
+  .lg\:leading-6 {
+    line-height: 1.5rem;
+  }
+
+  .lg\:leading-7 {
+    line-height: 1.75rem;
+  }
+
+  .lg\:leading-8 {
+    line-height: 2rem;
+  }
+
+  .lg\:leading-9 {
+    line-height: 2.25rem;
+  }
+
+  .lg\:leading-10 {
+    line-height: 2.5rem;
+  }
+
+  .lg\:leading-none {
+    line-height: 1;
+  }
+
+  .lg\:leading-tight {
+    line-height: 1.25;
+  }
+
+  .lg\:leading-snug {
+    line-height: 1.375;
+  }
+
+  .lg\:leading-normal {
+    line-height: 1.5;
+  }
+
+  .lg\:leading-relaxed {
+    line-height: 1.625;
+  }
+
+  .lg\:leading-loose {
+    line-height: 2;
+  }
+
+  .lg\:list-inside {
+    list-style-position: inside;
+  }
+
+  .lg\:list-outside {
+    list-style-position: outside;
+  }
+
+  .lg\:list-none {
+    list-style-type: none;
+  }
+
+  .lg\:list-disc {
+    list-style-type: disc;
+  }
+
+  .lg\:list-decimal {
+    list-style-type: decimal;
+  }
+
+  .lg\:m-0 {
+    margin: 0;
+  }
+
+  .lg\:m-1 {
+    margin: 0.25rem;
+  }
+
+  .lg\:m-2 {
+    margin: 0.5rem;
+  }
+
+  .lg\:m-3 {
+    margin: 0.75rem;
+  }
+
+  .lg\:m-4 {
+    margin: 1rem;
+  }
+
+  .lg\:m-5 {
+    margin: 1.25rem;
+  }
+
+  .lg\:m-6 {
+    margin: 1.5rem;
+  }
+
+  .lg\:m-8 {
+    margin: 2rem;
+  }
+
+  .lg\:m-10 {
+    margin: 2.5rem;
+  }
+
+  .lg\:m-12 {
+    margin: 3rem;
+  }
+
+  .lg\:m-16 {
+    margin: 4rem;
+  }
+
+  .lg\:m-20 {
+    margin: 5rem;
+  }
+
+  .lg\:m-24 {
+    margin: 6rem;
+  }
+
+  .lg\:m-32 {
+    margin: 8rem;
+  }
+
+  .lg\:m-40 {
+    margin: 10rem;
+  }
+
+  .lg\:m-48 {
+    margin: 12rem;
+  }
+
+  .lg\:m-56 {
+    margin: 14rem;
+  }
+
+  .lg\:m-64 {
+    margin: 16rem;
+  }
+
+  .lg\:m-auto {
+    margin: auto;
+  }
+
+  .lg\:m-px {
+    margin: 1px;
+  }
+
+  .lg\:-m-1 {
+    margin: -0.25rem;
+  }
+
+  .lg\:-m-2 {
+    margin: -0.5rem;
+  }
+
+  .lg\:-m-3 {
+    margin: -0.75rem;
+  }
+
+  .lg\:-m-4 {
+    margin: -1rem;
+  }
+
+  .lg\:-m-5 {
+    margin: -1.25rem;
+  }
+
+  .lg\:-m-6 {
+    margin: -1.5rem;
+  }
+
+  .lg\:-m-8 {
+    margin: -2rem;
+  }
+
+  .lg\:-m-10 {
+    margin: -2.5rem;
+  }
+
+  .lg\:-m-12 {
+    margin: -3rem;
+  }
+
+  .lg\:-m-16 {
+    margin: -4rem;
+  }
+
+  .lg\:-m-20 {
+    margin: -5rem;
+  }
+
+  .lg\:-m-24 {
+    margin: -6rem;
+  }
+
+  .lg\:-m-32 {
+    margin: -8rem;
+  }
+
+  .lg\:-m-40 {
+    margin: -10rem;
+  }
+
+  .lg\:-m-48 {
+    margin: -12rem;
+  }
+
+  .lg\:-m-56 {
+    margin: -14rem;
+  }
+
+  .lg\:-m-64 {
+    margin: -16rem;
+  }
+
+  .lg\:-m-px {
+    margin: -1px;
+  }
+
+  .lg\:my-0 {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .lg\:mx-0 {
+    margin-left: 0;
+    margin-right: 0;
+  }
+
+  .lg\:my-1 {
+    margin-top: 0.25rem;
+    margin-bottom: 0.25rem;
+  }
+
+  .lg\:mx-1 {
+    margin-left: 0.25rem;
+    margin-right: 0.25rem;
+  }
+
+  .lg\:my-2 {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+  }
+
+  .lg\:mx-2 {
+    margin-left: 0.5rem;
+    margin-right: 0.5rem;
+  }
+
+  .lg\:my-3 {
+    margin-top: 0.75rem;
+    margin-bottom: 0.75rem;
+  }
+
+  .lg\:mx-3 {
+    margin-left: 0.75rem;
+    margin-right: 0.75rem;
+  }
+
+  .lg\:my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+  }
+
+  .lg\:mx-4 {
+    margin-left: 1rem;
+    margin-right: 1rem;
+  }
+
+  .lg\:my-5 {
+    margin-top: 1.25rem;
+    margin-bottom: 1.25rem;
+  }
+
+  .lg\:mx-5 {
+    margin-left: 1.25rem;
+    margin-right: 1.25rem;
+  }
+
+  .lg\:my-6 {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+
+  .lg\:mx-6 {
+    margin-left: 1.5rem;
+    margin-right: 1.5rem;
+  }
+
+  .lg\:my-8 {
+    margin-top: 2rem;
+    margin-bottom: 2rem;
+  }
+
+  .lg\:mx-8 {
+    margin-left: 2rem;
+    margin-right: 2rem;
+  }
+
+  .lg\:my-10 {
+    margin-top: 2.5rem;
+    margin-bottom: 2.5rem;
+  }
+
+  .lg\:mx-10 {
+    margin-left: 2.5rem;
+    margin-right: 2.5rem;
+  }
+
+  .lg\:my-12 {
+    margin-top: 3rem;
+    margin-bottom: 3rem;
+  }
+
+  .lg\:mx-12 {
+    margin-left: 3rem;
+    margin-right: 3rem;
+  }
+
+  .lg\:my-16 {
+    margin-top: 4rem;
+    margin-bottom: 4rem;
+  }
+
+  .lg\:mx-16 {
+    margin-left: 4rem;
+    margin-right: 4rem;
+  }
+
+  .lg\:my-20 {
+    margin-top: 5rem;
+    margin-bottom: 5rem;
+  }
+
+  .lg\:mx-20 {
+    margin-left: 5rem;
+    margin-right: 5rem;
+  }
+
+  .lg\:my-24 {
+    margin-top: 6rem;
+    margin-bottom: 6rem;
+  }
+
+  .lg\:mx-24 {
+    margin-left: 6rem;
+    margin-right: 6rem;
+  }
+
+  .lg\:my-32 {
+    margin-top: 8rem;
+    margin-bottom: 8rem;
+  }
+
+  .lg\:mx-32 {
+    margin-left: 8rem;
+    margin-right: 8rem;
+  }
+
+  .lg\:my-40 {
+    margin-top: 10rem;
+    margin-bottom: 10rem;
+  }
+
+  .lg\:mx-40 {
+    margin-left: 10rem;
+    margin-right: 10rem;
+  }
+
+  .lg\:my-48 {
+    margin-top: 12rem;
+    margin-bottom: 12rem;
+  }
+
+  .lg\:mx-48 {
+    margin-left: 12rem;
+    margin-right: 12rem;
+  }
+
+  .lg\:my-56 {
+    margin-top: 14rem;
+    margin-bottom: 14rem;
+  }
+
+  .lg\:mx-56 {
+    margin-left: 14rem;
+    margin-right: 14rem;
+  }
+
+  .lg\:my-64 {
+    margin-top: 16rem;
+    margin-bottom: 16rem;
+  }
+
+  .lg\:mx-64 {
+    margin-left: 16rem;
+    margin-right: 16rem;
+  }
+
+  .lg\:my-auto {
+    margin-top: auto;
+    margin-bottom: auto;
+  }
+
+  .lg\:mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .lg\:my-px {
+    margin-top: 1px;
+    margin-bottom: 1px;
+  }
+
+  .lg\:mx-px {
+    margin-left: 1px;
+    margin-right: 1px;
+  }
+
+  .lg\:-my-1 {
+    margin-top: -0.25rem;
+    margin-bottom: -0.25rem;
+  }
+
+  .lg\:-mx-1 {
+    margin-left: -0.25rem;
+    margin-right: -0.25rem;
+  }
+
+  .lg\:-my-2 {
+    margin-top: -0.5rem;
+    margin-bottom: -0.5rem;
+  }
+
+  .lg\:-mx-2 {
+    margin-left: -0.5rem;
+    margin-right: -0.5rem;
+  }
+
+  .lg\:-my-3 {
+    margin-top: -0.75rem;
+    margin-bottom: -0.75rem;
+  }
+
+  .lg\:-mx-3 {
+    margin-left: -0.75rem;
+    margin-right: -0.75rem;
+  }
+
+  .lg\:-my-4 {
+    margin-top: -1rem;
+    margin-bottom: -1rem;
+  }
+
+  .lg\:-mx-4 {
+    margin-left: -1rem;
+    margin-right: -1rem;
+  }
+
+  .lg\:-my-5 {
+    margin-top: -1.25rem;
+    margin-bottom: -1.25rem;
+  }
+
+  .lg\:-mx-5 {
+    margin-left: -1.25rem;
+    margin-right: -1.25rem;
+  }
+
+  .lg\:-my-6 {
+    margin-top: -1.5rem;
+    margin-bottom: -1.5rem;
+  }
+
+  .lg\:-mx-6 {
+    margin-left: -1.5rem;
+    margin-right: -1.5rem;
+  }
+
+  .lg\:-my-8 {
+    margin-top: -2rem;
+    margin-bottom: -2rem;
+  }
+
+  .lg\:-mx-8 {
+    margin-left: -2rem;
+    margin-right: -2rem;
+  }
+
+  .lg\:-my-10 {
+    margin-top: -2.5rem;
+    margin-bottom: -2.5rem;
+  }
+
+  .lg\:-mx-10 {
+    margin-left: -2.5rem;
+    margin-right: -2.5rem;
+  }
+
+  .lg\:-my-12 {
+    margin-top: -3rem;
+    margin-bottom: -3rem;
+  }
+
+  .lg\:-mx-12 {
+    margin-left: -3rem;
+    margin-right: -3rem;
+  }
+
+  .lg\:-my-16 {
+    margin-top: -4rem;
+    margin-bottom: -4rem;
+  }
+
+  .lg\:-mx-16 {
+    margin-left: -4rem;
+    margin-right: -4rem;
+  }
+
+  .lg\:-my-20 {
+    margin-top: -5rem;
+    margin-bottom: -5rem;
+  }
+
+  .lg\:-mx-20 {
+    margin-left: -5rem;
+    margin-right: -5rem;
+  }
+
+  .lg\:-my-24 {
+    margin-top: -6rem;
+    margin-bottom: -6rem;
+  }
+
+  .lg\:-mx-24 {
+    margin-left: -6rem;
+    margin-right: -6rem;
+  }
+
+  .lg\:-my-32 {
+    margin-top: -8rem;
+    margin-bottom: -8rem;
+  }
+
+  .lg\:-mx-32 {
+    margin-left: -8rem;
+    margin-right: -8rem;
+  }
+
+  .lg\:-my-40 {
+    margin-top: -10rem;
+    margin-bottom: -10rem;
+  }
+
+  .lg\:-mx-40 {
+    margin-left: -10rem;
+    margin-right: -10rem;
+  }
+
+  .lg\:-my-48 {
+    margin-top: -12rem;
+    margin-bottom: -12rem;
+  }
+
+  .lg\:-mx-48 {
+    margin-left: -12rem;
+    margin-right: -12rem;
+  }
+
+  .lg\:-my-56 {
+    margin-top: -14rem;
+    margin-bottom: -14rem;
+  }
+
+  .lg\:-mx-56 {
+    margin-left: -14rem;
+    margin-right: -14rem;
+  }
+
+  .lg\:-my-64 {
+    margin-top: -16rem;
+    margin-bottom: -16rem;
+  }
+
+  .lg\:-mx-64 {
+    margin-left: -16rem;
+    margin-right: -16rem;
+  }
+
+  .lg\:-my-px {
+    margin-top: -1px;
+    margin-bottom: -1px;
+  }
+
+  .lg\:-mx-px {
+    margin-left: -1px;
+    margin-right: -1px;
+  }
+
+  .lg\:mt-0 {
+    margin-top: 0;
+  }
+
+  .lg\:mr-0 {
+    margin-right: 0;
+  }
+
+  .lg\:mb-0 {
+    margin-bottom: 0;
+  }
+
+  .lg\:ml-0 {
+    margin-left: 0;
+  }
+
+  .lg\:mt-1 {
+    margin-top: 0.25rem;
+  }
+
+  .lg\:mr-1 {
+    margin-right: 0.25rem;
+  }
+
+  .lg\:mb-1 {
+    margin-bottom: 0.25rem;
+  }
+
+  .lg\:ml-1 {
+    margin-left: 0.25rem;
+  }
+
+  .lg\:mt-2 {
+    margin-top: 0.5rem;
+  }
+
+  .lg\:mr-2 {
+    margin-right: 0.5rem;
+  }
+
+  .lg\:mb-2 {
+    margin-bottom: 0.5rem;
+  }
+
+  .lg\:ml-2 {
+    margin-left: 0.5rem;
+  }
+
+  .lg\:mt-3 {
+    margin-top: 0.75rem;
+  }
+
+  .lg\:mr-3 {
+    margin-right: 0.75rem;
+  }
+
+  .lg\:mb-3 {
+    margin-bottom: 0.75rem;
+  }
+
+  .lg\:ml-3 {
+    margin-left: 0.75rem;
+  }
+
+  .lg\:mt-4 {
+    margin-top: 1rem;
+  }
+
+  .lg\:mr-4 {
+    margin-right: 1rem;
+  }
+
+  .lg\:mb-4 {
+    margin-bottom: 1rem;
+  }
+
+  .lg\:ml-4 {
+    margin-left: 1rem;
+  }
+
+  .lg\:mt-5 {
+    margin-top: 1.25rem;
+  }
+
+  .lg\:mr-5 {
+    margin-right: 1.25rem;
+  }
+
+  .lg\:mb-5 {
+    margin-bottom: 1.25rem;
+  }
+
+  .lg\:ml-5 {
+    margin-left: 1.25rem;
+  }
+
+  .lg\:mt-6 {
+    margin-top: 1.5rem;
+  }
+
+  .lg\:mr-6 {
+    margin-right: 1.5rem;
+  }
+
+  .lg\:mb-6 {
+    margin-bottom: 1.5rem;
+  }
+
+  .lg\:ml-6 {
+    margin-left: 1.5rem;
+  }
+
+  .lg\:mt-8 {
+    margin-top: 2rem;
+  }
+
+  .lg\:mr-8 {
+    margin-right: 2rem;
+  }
+
+  .lg\:mb-8 {
+    margin-bottom: 2rem;
+  }
+
+  .lg\:ml-8 {
+    margin-left: 2rem;
+  }
+
+  .lg\:mt-10 {
+    margin-top: 2.5rem;
+  }
+
+  .lg\:mr-10 {
+    margin-right: 2.5rem;
+  }
+
+  .lg\:mb-10 {
+    margin-bottom: 2.5rem;
+  }
+
+  .lg\:ml-10 {
+    margin-left: 2.5rem;
+  }
+
+  .lg\:mt-12 {
+    margin-top: 3rem;
+  }
+
+  .lg\:mr-12 {
+    margin-right: 3rem;
+  }
+
+  .lg\:mb-12 {
+    margin-bottom: 3rem;
+  }
+
+  .lg\:ml-12 {
+    margin-left: 3rem;
+  }
+
+  .lg\:mt-16 {
+    margin-top: 4rem;
+  }
+
+  .lg\:mr-16 {
+    margin-right: 4rem;
+  }
+
+  .lg\:mb-16 {
+    margin-bottom: 4rem;
+  }
+
+  .lg\:ml-16 {
+    margin-left: 4rem;
+  }
+
+  .lg\:mt-20 {
+    margin-top: 5rem;
+  }
+
+  .lg\:mr-20 {
+    margin-right: 5rem;
+  }
+
+  .lg\:mb-20 {
+    margin-bottom: 5rem;
+  }
+
+  .lg\:ml-20 {
+    margin-left: 5rem;
+  }
+
+  .lg\:mt-24 {
+    margin-top: 6rem;
+  }
+
+  .lg\:mr-24 {
+    margin-right: 6rem;
+  }
+
+  .lg\:mb-24 {
+    margin-bottom: 6rem;
+  }
+
+  .lg\:ml-24 {
+    margin-left: 6rem;
+  }
+
+  .lg\:mt-32 {
+    margin-top: 8rem;
+  }
+
+  .lg\:mr-32 {
+    margin-right: 8rem;
+  }
+
+  .lg\:mb-32 {
+    margin-bottom: 8rem;
+  }
+
+  .lg\:ml-32 {
+    margin-left: 8rem;
+  }
+
+  .lg\:mt-40 {
+    margin-top: 10rem;
+  }
+
+  .lg\:mr-40 {
+    margin-right: 10rem;
+  }
+
+  .lg\:mb-40 {
+    margin-bottom: 10rem;
+  }
+
+  .lg\:ml-40 {
+    margin-left: 10rem;
+  }
+
+  .lg\:mt-48 {
+    margin-top: 12rem;
+  }
+
+  .lg\:mr-48 {
+    margin-right: 12rem;
+  }
+
+  .lg\:mb-48 {
+    margin-bottom: 12rem;
+  }
+
+  .lg\:ml-48 {
+    margin-left: 12rem;
+  }
+
+  .lg\:mt-56 {
+    margin-top: 14rem;
+  }
+
+  .lg\:mr-56 {
+    margin-right: 14rem;
+  }
+
+  .lg\:mb-56 {
+    margin-bottom: 14rem;
+  }
+
+  .lg\:ml-56 {
+    margin-left: 14rem;
+  }
+
+  .lg\:mt-64 {
+    margin-top: 16rem;
+  }
+
+  .lg\:mr-64 {
+    margin-right: 16rem;
+  }
+
+  .lg\:mb-64 {
+    margin-bottom: 16rem;
+  }
+
+  .lg\:ml-64 {
+    margin-left: 16rem;
+  }
+
+  .lg\:mt-auto {
+    margin-top: auto;
+  }
+
+  .lg\:mr-auto {
+    margin-right: auto;
+  }
+
+  .lg\:mb-auto {
+    margin-bottom: auto;
+  }
+
+  .lg\:ml-auto {
+    margin-left: auto;
+  }
+
+  .lg\:mt-px {
+    margin-top: 1px;
+  }
+
+  .lg\:mr-px {
+    margin-right: 1px;
+  }
+
+  .lg\:mb-px {
+    margin-bottom: 1px;
+  }
+
+  .lg\:ml-px {
+    margin-left: 1px;
+  }
+
+  .lg\:-mt-1 {
+    margin-top: -0.25rem;
+  }
+
+  .lg\:-mr-1 {
+    margin-right: -0.25rem;
+  }
+
+  .lg\:-mb-1 {
+    margin-bottom: -0.25rem;
+  }
+
+  .lg\:-ml-1 {
+    margin-left: -0.25rem;
+  }
+
+  .lg\:-mt-2 {
+    margin-top: -0.5rem;
+  }
+
+  .lg\:-mr-2 {
+    margin-right: -0.5rem;
+  }
+
+  .lg\:-mb-2 {
+    margin-bottom: -0.5rem;
+  }
+
+  .lg\:-ml-2 {
+    margin-left: -0.5rem;
+  }
+
+  .lg\:-mt-3 {
+    margin-top: -0.75rem;
+  }
+
+  .lg\:-mr-3 {
+    margin-right: -0.75rem;
+  }
+
+  .lg\:-mb-3 {
+    margin-bottom: -0.75rem;
+  }
+
+  .lg\:-ml-3 {
+    margin-left: -0.75rem;
+  }
+
+  .lg\:-mt-4 {
+    margin-top: -1rem;
+  }
+
+  .lg\:-mr-4 {
+    margin-right: -1rem;
+  }
+
+  .lg\:-mb-4 {
+    margin-bottom: -1rem;
+  }
+
+  .lg\:-ml-4 {
+    margin-left: -1rem;
+  }
+
+  .lg\:-mt-5 {
+    margin-top: -1.25rem;
+  }
+
+  .lg\:-mr-5 {
+    margin-right: -1.25rem;
+  }
+
+  .lg\:-mb-5 {
+    margin-bottom: -1.25rem;
+  }
+
+  .lg\:-ml-5 {
+    margin-left: -1.25rem;
+  }
+
+  .lg\:-mt-6 {
+    margin-top: -1.5rem;
+  }
+
+  .lg\:-mr-6 {
+    margin-right: -1.5rem;
+  }
+
+  .lg\:-mb-6 {
+    margin-bottom: -1.5rem;
+  }
+
+  .lg\:-ml-6 {
+    margin-left: -1.5rem;
+  }
+
+  .lg\:-mt-8 {
+    margin-top: -2rem;
+  }
+
+  .lg\:-mr-8 {
+    margin-right: -2rem;
+  }
+
+  .lg\:-mb-8 {
+    margin-bottom: -2rem;
+  }
+
+  .lg\:-ml-8 {
+    margin-left: -2rem;
+  }
+
+  .lg\:-mt-10 {
+    margin-top: -2.5rem;
+  }
+
+  .lg\:-mr-10 {
+    margin-right: -2.5rem;
+  }
+
+  .lg\:-mb-10 {
+    margin-bottom: -2.5rem;
+  }
+
+  .lg\:-ml-10 {
+    margin-left: -2.5rem;
+  }
+
+  .lg\:-mt-12 {
+    margin-top: -3rem;
+  }
+
+  .lg\:-mr-12 {
+    margin-right: -3rem;
+  }
+
+  .lg\:-mb-12 {
+    margin-bottom: -3rem;
+  }
+
+  .lg\:-ml-12 {
+    margin-left: -3rem;
+  }
+
+  .lg\:-mt-16 {
+    margin-top: -4rem;
+  }
+
+  .lg\:-mr-16 {
+    margin-right: -4rem;
+  }
+
+  .lg\:-mb-16 {
+    margin-bottom: -4rem;
+  }
+
+  .lg\:-ml-16 {
+    margin-left: -4rem;
+  }
+
+  .lg\:-mt-20 {
+    margin-top: -5rem;
+  }
+
+  .lg\:-mr-20 {
+    margin-right: -5rem;
+  }
+
+  .lg\:-mb-20 {
+    margin-bottom: -5rem;
+  }
+
+  .lg\:-ml-20 {
+    margin-left: -5rem;
+  }
+
+  .lg\:-mt-24 {
+    margin-top: -6rem;
+  }
+
+  .lg\:-mr-24 {
+    margin-right: -6rem;
+  }
+
+  .lg\:-mb-24 {
+    margin-bottom: -6rem;
+  }
+
+  .lg\:-ml-24 {
+    margin-left: -6rem;
+  }
+
+  .lg\:-mt-32 {
+    margin-top: -8rem;
+  }
+
+  .lg\:-mr-32 {
+    margin-right: -8rem;
+  }
+
+  .lg\:-mb-32 {
+    margin-bottom: -8rem;
+  }
+
+  .lg\:-ml-32 {
+    margin-left: -8rem;
+  }
+
+  .lg\:-mt-40 {
+    margin-top: -10rem;
+  }
+
+  .lg\:-mr-40 {
+    margin-right: -10rem;
+  }
+
+  .lg\:-mb-40 {
+    margin-bottom: -10rem;
+  }
+
+  .lg\:-ml-40 {
+    margin-left: -10rem;
+  }
+
+  .lg\:-mt-48 {
+    margin-top: -12rem;
+  }
+
+  .lg\:-mr-48 {
+    margin-right: -12rem;
+  }
+
+  .lg\:-mb-48 {
+    margin-bottom: -12rem;
+  }
+
+  .lg\:-ml-48 {
+    margin-left: -12rem;
+  }
+
+  .lg\:-mt-56 {
+    margin-top: -14rem;
+  }
+
+  .lg\:-mr-56 {
+    margin-right: -14rem;
+  }
+
+  .lg\:-mb-56 {
+    margin-bottom: -14rem;
+  }
+
+  .lg\:-ml-56 {
+    margin-left: -14rem;
+  }
+
+  .lg\:-mt-64 {
+    margin-top: -16rem;
+  }
+
+  .lg\:-mr-64 {
+    margin-right: -16rem;
+  }
+
+  .lg\:-mb-64 {
+    margin-bottom: -16rem;
+  }
+
+  .lg\:-ml-64 {
+    margin-left: -16rem;
+  }
+
+  .lg\:-mt-px {
+    margin-top: -1px;
+  }
+
+  .lg\:-mr-px {
+    margin-right: -1px;
+  }
+
+  .lg\:-mb-px {
+    margin-bottom: -1px;
+  }
+
+  .lg\:-ml-px {
+    margin-left: -1px;
+  }
+
+  .lg\:max-h-full {
+    max-height: 100%;
+  }
+
+  .lg\:max-h-screen {
+    max-height: 100vh;
+  }
+
+  .lg\:max-w-none {
+    max-width: none;
+  }
+
+  .lg\:max-w-xs {
+    max-width: 20rem;
+  }
+
+  .lg\:max-w-sm {
+    max-width: 24rem;
+  }
+
+  .lg\:max-w-md {
+    max-width: 28rem;
+  }
+
+  .lg\:max-w-lg {
+    max-width: 32rem;
+  }
+
+  .lg\:max-w-xl {
+    max-width: 36rem;
+  }
+
+  .lg\:max-w-2xl {
+    max-width: 42rem;
+  }
+
+  .lg\:max-w-3xl {
+    max-width: 48rem;
+  }
+
+  .lg\:max-w-4xl {
+    max-width: 56rem;
+  }
+
+  .lg\:max-w-5xl {
+    max-width: 64rem;
+  }
+
+  .lg\:max-w-6xl {
+    max-width: 72rem;
+  }
+
+  .lg\:max-w-full {
+    max-width: 100%;
+  }
+
+  .lg\:max-w-screen-sm {
+    max-width: 640px;
+  }
+
+  .lg\:max-w-screen-md {
+    max-width: 768px;
+  }
+
+  .lg\:max-w-screen-lg {
+    max-width: 1024px;
+  }
+
+  .lg\:max-w-screen-xl {
+    max-width: 1280px;
+  }
+
+  .lg\:min-h-0 {
+    min-height: 0;
+  }
+
+  .lg\:min-h-full {
+    min-height: 100%;
+  }
+
+  .lg\:min-h-screen {
+    min-height: 100vh;
+  }
+
+  .lg\:min-w-0 {
+    min-width: 0;
+  }
+
+  .lg\:min-w-full {
+    min-width: 100%;
+  }
+
+  .lg\:object-contain {
+    -o-object-fit: contain;
+       object-fit: contain;
+  }
+
+  .lg\:object-cover {
+    -o-object-fit: cover;
+       object-fit: cover;
+  }
+
+  .lg\:object-fill {
+    -o-object-fit: fill;
+       object-fit: fill;
+  }
+
+  .lg\:object-none {
+    -o-object-fit: none;
+       object-fit: none;
+  }
+
+  .lg\:object-scale-down {
+    -o-object-fit: scale-down;
+       object-fit: scale-down;
+  }
+
+  .lg\:object-bottom {
+    -o-object-position: bottom;
+       object-position: bottom;
+  }
+
+  .lg\:object-center {
+    -o-object-position: center;
+       object-position: center;
+  }
+
+  .lg\:object-left {
+    -o-object-position: left;
+       object-position: left;
+  }
+
+  .lg\:object-left-bottom {
+    -o-object-position: left bottom;
+       object-position: left bottom;
+  }
+
+  .lg\:object-left-top {
+    -o-object-position: left top;
+       object-position: left top;
+  }
+
+  .lg\:object-right {
+    -o-object-position: right;
+       object-position: right;
+  }
+
+  .lg\:object-right-bottom {
+    -o-object-position: right bottom;
+       object-position: right bottom;
+  }
+
+  .lg\:object-right-top {
+    -o-object-position: right top;
+       object-position: right top;
+  }
+
+  .lg\:object-top {
+    -o-object-position: top;
+       object-position: top;
+  }
+
+  .lg\:opacity-0 {
+    opacity: 0;
+  }
+
+  .lg\:opacity-25 {
+    opacity: 0.25;
+  }
+
+  .lg\:opacity-50 {
+    opacity: 0.5;
+  }
+
+  .lg\:opacity-75 {
+    opacity: 0.75;
+  }
+
+  .lg\:opacity-100 {
+    opacity: 1;
+  }
+
+  .lg\:hover\:opacity-0:hover {
+    opacity: 0;
+  }
+
+  .lg\:hover\:opacity-25:hover {
+    opacity: 0.25;
+  }
+
+  .lg\:hover\:opacity-50:hover {
+    opacity: 0.5;
+  }
+
+  .lg\:hover\:opacity-75:hover {
+    opacity: 0.75;
+  }
+
+  .lg\:hover\:opacity-100:hover {
+    opacity: 1;
+  }
+
+  .lg\:focus\:opacity-0:focus {
+    opacity: 0;
+  }
+
+  .lg\:focus\:opacity-25:focus {
+    opacity: 0.25;
+  }
+
+  .lg\:focus\:opacity-50:focus {
+    opacity: 0.5;
+  }
+
+  .lg\:focus\:opacity-75:focus {
+    opacity: 0.75;
+  }
+
+  .lg\:focus\:opacity-100:focus {
+    opacity: 1;
+  }
+
+  .lg\:outline-none {
+    outline: 0;
+  }
+
+  .lg\:focus\:outline-none:focus {
+    outline: 0;
+  }
+
+  .lg\:overflow-auto {
+    overflow: auto;
+  }
+
+  .lg\:overflow-hidden {
+    overflow: hidden;
+  }
+
+  .lg\:overflow-visible {
+    overflow: visible;
+  }
+
+  .lg\:overflow-scroll {
+    overflow: scroll;
+  }
+
+  .lg\:overflow-x-auto {
+    overflow-x: auto;
+  }
+
+  .lg\:overflow-y-auto {
+    overflow-y: auto;
+  }
+
+  .lg\:overflow-x-hidden {
+    overflow-x: hidden;
+  }
+
+  .lg\:overflow-y-hidden {
+    overflow-y: hidden;
+  }
+
+  .lg\:overflow-x-visible {
+    overflow-x: visible;
+  }
+
+  .lg\:overflow-y-visible {
+    overflow-y: visible;
+  }
+
+  .lg\:overflow-x-scroll {
+    overflow-x: scroll;
+  }
+
+  .lg\:overflow-y-scroll {
+    overflow-y: scroll;
+  }
+
+  .lg\:scrolling-touch {
+    -webkit-overflow-scrolling: touch;
+  }
+
+  .lg\:scrolling-auto {
+    -webkit-overflow-scrolling: auto;
+  }
+
+  .lg\:overscroll-auto {
+    -ms-scroll-chaining: chained;
+        overscroll-behavior: auto;
+  }
+
+  .lg\:overscroll-contain {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: contain;
+  }
+
+  .lg\:overscroll-none {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: none;
+  }
+
+  .lg\:overscroll-y-auto {
+    overscroll-behavior-y: auto;
+  }
+
+  .lg\:overscroll-y-contain {
+    overscroll-behavior-y: contain;
+  }
+
+  .lg\:overscroll-y-none {
+    overscroll-behavior-y: none;
+  }
+
+  .lg\:overscroll-x-auto {
+    overscroll-behavior-x: auto;
+  }
+
+  .lg\:overscroll-x-contain {
+    overscroll-behavior-x: contain;
+  }
+
+  .lg\:overscroll-x-none {
+    overscroll-behavior-x: none;
+  }
+
+  .lg\:p-0 {
+    padding: 0;
+  }
+
+  .lg\:p-1 {
+    padding: 0.25rem;
+  }
+
+  .lg\:p-2 {
+    padding: 0.5rem;
+  }
+
+  .lg\:p-3 {
+    padding: 0.75rem;
+  }
+
+  .lg\:p-4 {
+    padding: 1rem;
+  }
+
+  .lg\:p-5 {
+    padding: 1.25rem;
+  }
+
+  .lg\:p-6 {
+    padding: 1.5rem;
+  }
+
+  .lg\:p-8 {
+    padding: 2rem;
+  }
+
+  .lg\:p-10 {
+    padding: 2.5rem;
+  }
+
+  .lg\:p-12 {
+    padding: 3rem;
+  }
+
+  .lg\:p-16 {
+    padding: 4rem;
+  }
+
+  .lg\:p-20 {
+    padding: 5rem;
+  }
+
+  .lg\:p-24 {
+    padding: 6rem;
+  }
+
+  .lg\:p-32 {
+    padding: 8rem;
+  }
+
+  .lg\:p-40 {
+    padding: 10rem;
+  }
+
+  .lg\:p-48 {
+    padding: 12rem;
+  }
+
+  .lg\:p-56 {
+    padding: 14rem;
+  }
+
+  .lg\:p-64 {
+    padding: 16rem;
+  }
+
+  .lg\:p-px {
+    padding: 1px;
+  }
+
+  .lg\:py-0 {
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+
+  .lg\:px-0 {
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  .lg\:py-1 {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+  }
+
+  .lg\:px-1 {
+    padding-left: 0.25rem;
+    padding-right: 0.25rem;
+  }
+
+  .lg\:py-2 {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
+
+  .lg\:px-2 {
+    padding-left: 0.5rem;
+    padding-right: 0.5rem;
+  }
+
+  .lg\:py-3 {
+    padding-top: 0.75rem;
+    padding-bottom: 0.75rem;
+  }
+
+  .lg\:px-3 {
+    padding-left: 0.75rem;
+    padding-right: 0.75rem;
+  }
+
+  .lg\:py-4 {
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+  }
+
+  .lg\:px-4 {
+    padding-left: 1rem;
+    padding-right: 1rem;
+  }
+
+  .lg\:py-5 {
+    padding-top: 1.25rem;
+    padding-bottom: 1.25rem;
+  }
+
+  .lg\:px-5 {
+    padding-left: 1.25rem;
+    padding-right: 1.25rem;
+  }
+
+  .lg\:py-6 {
+    padding-top: 1.5rem;
+    padding-bottom: 1.5rem;
+  }
+
+  .lg\:px-6 {
+    padding-left: 1.5rem;
+    padding-right: 1.5rem;
+  }
+
+  .lg\:py-8 {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
+
+  .lg\:px-8 {
+    padding-left: 2rem;
+    padding-right: 2rem;
+  }
+
+  .lg\:py-10 {
+    padding-top: 2.5rem;
+    padding-bottom: 2.5rem;
+  }
+
+  .lg\:px-10 {
+    padding-left: 2.5rem;
+    padding-right: 2.5rem;
+  }
+
+  .lg\:py-12 {
+    padding-top: 3rem;
+    padding-bottom: 3rem;
+  }
+
+  .lg\:px-12 {
+    padding-left: 3rem;
+    padding-right: 3rem;
+  }
+
+  .lg\:py-16 {
+    padding-top: 4rem;
+    padding-bottom: 4rem;
+  }
+
+  .lg\:px-16 {
+    padding-left: 4rem;
+    padding-right: 4rem;
+  }
+
+  .lg\:py-20 {
+    padding-top: 5rem;
+    padding-bottom: 5rem;
+  }
+
+  .lg\:px-20 {
+    padding-left: 5rem;
+    padding-right: 5rem;
+  }
+
+  .lg\:py-24 {
+    padding-top: 6rem;
+    padding-bottom: 6rem;
+  }
+
+  .lg\:px-24 {
+    padding-left: 6rem;
+    padding-right: 6rem;
+  }
+
+  .lg\:py-32 {
+    padding-top: 8rem;
+    padding-bottom: 8rem;
+  }
+
+  .lg\:px-32 {
+    padding-left: 8rem;
+    padding-right: 8rem;
+  }
+
+  .lg\:py-40 {
+    padding-top: 10rem;
+    padding-bottom: 10rem;
+  }
+
+  .lg\:px-40 {
+    padding-left: 10rem;
+    padding-right: 10rem;
+  }
+
+  .lg\:py-48 {
+    padding-top: 12rem;
+    padding-bottom: 12rem;
+  }
+
+  .lg\:px-48 {
+    padding-left: 12rem;
+    padding-right: 12rem;
+  }
+
+  .lg\:py-56 {
+    padding-top: 14rem;
+    padding-bottom: 14rem;
+  }
+
+  .lg\:px-56 {
+    padding-left: 14rem;
+    padding-right: 14rem;
+  }
+
+  .lg\:py-64 {
+    padding-top: 16rem;
+    padding-bottom: 16rem;
+  }
+
+  .lg\:px-64 {
+    padding-left: 16rem;
+    padding-right: 16rem;
+  }
+
+  .lg\:py-px {
+    padding-top: 1px;
+    padding-bottom: 1px;
+  }
+
+  .lg\:px-px {
+    padding-left: 1px;
+    padding-right: 1px;
+  }
+
+  .lg\:pt-0 {
+    padding-top: 0;
+  }
+
+  .lg\:pr-0 {
+    padding-right: 0;
+  }
+
+  .lg\:pb-0 {
+    padding-bottom: 0;
+  }
+
+  .lg\:pl-0 {
+    padding-left: 0;
+  }
+
+  .lg\:pt-1 {
+    padding-top: 0.25rem;
+  }
+
+  .lg\:pr-1 {
+    padding-right: 0.25rem;
+  }
+
+  .lg\:pb-1 {
+    padding-bottom: 0.25rem;
+  }
+
+  .lg\:pl-1 {
+    padding-left: 0.25rem;
+  }
+
+  .lg\:pt-2 {
+    padding-top: 0.5rem;
+  }
+
+  .lg\:pr-2 {
+    padding-right: 0.5rem;
+  }
+
+  .lg\:pb-2 {
+    padding-bottom: 0.5rem;
+  }
+
+  .lg\:pl-2 {
+    padding-left: 0.5rem;
+  }
+
+  .lg\:pt-3 {
+    padding-top: 0.75rem;
+  }
+
+  .lg\:pr-3 {
+    padding-right: 0.75rem;
+  }
+
+  .lg\:pb-3 {
+    padding-bottom: 0.75rem;
+  }
+
+  .lg\:pl-3 {
+    padding-left: 0.75rem;
+  }
+
+  .lg\:pt-4 {
+    padding-top: 1rem;
+  }
+
+  .lg\:pr-4 {
+    padding-right: 1rem;
+  }
+
+  .lg\:pb-4 {
+    padding-bottom: 1rem;
+  }
+
+  .lg\:pl-4 {
+    padding-left: 1rem;
+  }
+
+  .lg\:pt-5 {
+    padding-top: 1.25rem;
+  }
+
+  .lg\:pr-5 {
+    padding-right: 1.25rem;
+  }
+
+  .lg\:pb-5 {
+    padding-bottom: 1.25rem;
+  }
+
+  .lg\:pl-5 {
+    padding-left: 1.25rem;
+  }
+
+  .lg\:pt-6 {
+    padding-top: 1.5rem;
+  }
+
+  .lg\:pr-6 {
+    padding-right: 1.5rem;
+  }
+
+  .lg\:pb-6 {
+    padding-bottom: 1.5rem;
+  }
+
+  .lg\:pl-6 {
+    padding-left: 1.5rem;
+  }
+
+  .lg\:pt-8 {
+    padding-top: 2rem;
+  }
+
+  .lg\:pr-8 {
+    padding-right: 2rem;
+  }
+
+  .lg\:pb-8 {
+    padding-bottom: 2rem;
+  }
+
+  .lg\:pl-8 {
+    padding-left: 2rem;
+  }
+
+  .lg\:pt-10 {
+    padding-top: 2.5rem;
+  }
+
+  .lg\:pr-10 {
+    padding-right: 2.5rem;
+  }
+
+  .lg\:pb-10 {
+    padding-bottom: 2.5rem;
+  }
+
+  .lg\:pl-10 {
+    padding-left: 2.5rem;
+  }
+
+  .lg\:pt-12 {
+    padding-top: 3rem;
+  }
+
+  .lg\:pr-12 {
+    padding-right: 3rem;
+  }
+
+  .lg\:pb-12 {
+    padding-bottom: 3rem;
+  }
+
+  .lg\:pl-12 {
+    padding-left: 3rem;
+  }
+
+  .lg\:pt-16 {
+    padding-top: 4rem;
+  }
+
+  .lg\:pr-16 {
+    padding-right: 4rem;
+  }
+
+  .lg\:pb-16 {
+    padding-bottom: 4rem;
+  }
+
+  .lg\:pl-16 {
+    padding-left: 4rem;
+  }
+
+  .lg\:pt-20 {
+    padding-top: 5rem;
+  }
+
+  .lg\:pr-20 {
+    padding-right: 5rem;
+  }
+
+  .lg\:pb-20 {
+    padding-bottom: 5rem;
+  }
+
+  .lg\:pl-20 {
+    padding-left: 5rem;
+  }
+
+  .lg\:pt-24 {
+    padding-top: 6rem;
+  }
+
+  .lg\:pr-24 {
+    padding-right: 6rem;
+  }
+
+  .lg\:pb-24 {
+    padding-bottom: 6rem;
+  }
+
+  .lg\:pl-24 {
+    padding-left: 6rem;
+  }
+
+  .lg\:pt-32 {
+    padding-top: 8rem;
+  }
+
+  .lg\:pr-32 {
+    padding-right: 8rem;
+  }
+
+  .lg\:pb-32 {
+    padding-bottom: 8rem;
+  }
+
+  .lg\:pl-32 {
+    padding-left: 8rem;
+  }
+
+  .lg\:pt-40 {
+    padding-top: 10rem;
+  }
+
+  .lg\:pr-40 {
+    padding-right: 10rem;
+  }
+
+  .lg\:pb-40 {
+    padding-bottom: 10rem;
+  }
+
+  .lg\:pl-40 {
+    padding-left: 10rem;
+  }
+
+  .lg\:pt-48 {
+    padding-top: 12rem;
+  }
+
+  .lg\:pr-48 {
+    padding-right: 12rem;
+  }
+
+  .lg\:pb-48 {
+    padding-bottom: 12rem;
+  }
+
+  .lg\:pl-48 {
+    padding-left: 12rem;
+  }
+
+  .lg\:pt-56 {
+    padding-top: 14rem;
+  }
+
+  .lg\:pr-56 {
+    padding-right: 14rem;
+  }
+
+  .lg\:pb-56 {
+    padding-bottom: 14rem;
+  }
+
+  .lg\:pl-56 {
+    padding-left: 14rem;
+  }
+
+  .lg\:pt-64 {
+    padding-top: 16rem;
+  }
+
+  .lg\:pr-64 {
+    padding-right: 16rem;
+  }
+
+  .lg\:pb-64 {
+    padding-bottom: 16rem;
+  }
+
+  .lg\:pl-64 {
+    padding-left: 16rem;
+  }
+
+  .lg\:pt-px {
+    padding-top: 1px;
+  }
+
+  .lg\:pr-px {
+    padding-right: 1px;
+  }
+
+  .lg\:pb-px {
+    padding-bottom: 1px;
+  }
+
+  .lg\:pl-px {
+    padding-left: 1px;
+  }
+
+  .lg\:placeholder-transparent::-moz-placeholder {
+    color: transparent;
+  }
+
+  .lg\:placeholder-transparent:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .lg\:placeholder-transparent::placeholder {
+    color: transparent;
+  }
+
+  .lg\:placeholder-current::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .lg\:placeholder-current:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .lg\:placeholder-current::placeholder {
+    color: currentColor;
+  }
+
+  .lg\:placeholder-black::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-black:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-black::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-white::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-white:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-white::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-gray-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-red-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-orange-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-yellow-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-green-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-teal-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-blue-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-indigo-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-purple-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-pink-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-transparent:focus::-moz-placeholder {
+    color: transparent;
+  }
+
+  .lg\:focus\:placeholder-transparent:focus:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .lg\:focus\:placeholder-transparent:focus::placeholder {
+    color: transparent;
+  }
+
+  .lg\:focus\:placeholder-current:focus::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .lg\:focus\:placeholder-current:focus:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .lg\:focus\:placeholder-current:focus::placeholder {
+    color: currentColor;
+  }
+
+  .lg\:focus\:placeholder-black:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-black:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-black:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-white:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-white:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-white:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-gray-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-red-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-orange-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-yellow-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-green-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-teal-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-blue-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-indigo-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-purple-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:focus\:placeholder-pink-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .lg\:placeholder-opacity-0::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:placeholder-opacity-0:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:placeholder-opacity-0::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:placeholder-opacity-25::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:placeholder-opacity-25:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:placeholder-opacity-25::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:placeholder-opacity-50::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:placeholder-opacity-50:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:placeholder-opacity-50::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:placeholder-opacity-75::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:placeholder-opacity-75:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:placeholder-opacity-75::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:placeholder-opacity-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:placeholder-opacity-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:placeholder-opacity-100::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:focus\:placeholder-opacity-0:focus::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:focus\:placeholder-opacity-0:focus::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .lg\:focus\:placeholder-opacity-25:focus::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:focus\:placeholder-opacity-25:focus::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .lg\:focus\:placeholder-opacity-50:focus::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:focus\:placeholder-opacity-50:focus::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .lg\:focus\:placeholder-opacity-75:focus::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:focus\:placeholder-opacity-75:focus::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .lg\:focus\:placeholder-opacity-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:focus\:placeholder-opacity-100:focus::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .lg\:pointer-events-none {
+    pointer-events: none;
+  }
+
+  .lg\:pointer-events-auto {
+    pointer-events: auto;
+  }
+
+  .lg\:static {
+    position: static;
+  }
+
+  .lg\:fixed {
+    position: fixed;
+  }
+
+  .lg\:absolute {
+    position: absolute;
+  }
+
+  .lg\:relative {
+    position: relative;
+  }
+
+  .lg\:sticky {
+    position: -webkit-sticky;
+    position: sticky;
+  }
+
+  .lg\:inset-0 {
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+  }
+
+  .lg\:inset-auto {
+    top: auto;
+    right: auto;
+    bottom: auto;
+    left: auto;
+  }
+
+  .lg\:inset-y-0 {
+    top: 0;
+    bottom: 0;
+  }
+
+  .lg\:inset-x-0 {
+    right: 0;
+    left: 0;
+  }
+
+  .lg\:inset-y-auto {
+    top: auto;
+    bottom: auto;
+  }
+
+  .lg\:inset-x-auto {
+    right: auto;
+    left: auto;
+  }
+
+  .lg\:top-0 {
+    top: 0;
+  }
+
+  .lg\:right-0 {
+    right: 0;
+  }
+
+  .lg\:bottom-0 {
+    bottom: 0;
+  }
+
+  .lg\:left-0 {
+    left: 0;
+  }
+
+  .lg\:top-auto {
+    top: auto;
+  }
+
+  .lg\:right-auto {
+    right: auto;
+  }
+
+  .lg\:bottom-auto {
+    bottom: auto;
+  }
+
+  .lg\:left-auto {
+    left: auto;
+  }
+
+  .lg\:resize-none {
+    resize: none;
+  }
+
+  .lg\:resize-y {
+    resize: vertical;
+  }
+
+  .lg\:resize-x {
+    resize: horizontal;
+  }
+
+  .lg\:resize {
+    resize: both;
+  }
+
+  .lg\:shadow-xs {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:shadow-sm {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:shadow {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:shadow-md {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:shadow-lg {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:shadow-xl {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .lg\:shadow-2xl {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .lg\:shadow-inner {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:shadow-outline {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .lg\:shadow-none {
+    box-shadow: none;
+  }
+
+  .lg\:hover\:shadow-xs:hover {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:hover\:shadow-sm:hover {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:hover\:shadow:hover {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:hover\:shadow-md:hover {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:hover\:shadow-lg:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:hover\:shadow-xl:hover {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .lg\:hover\:shadow-2xl:hover {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .lg\:hover\:shadow-inner:hover {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:hover\:shadow-outline:hover {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .lg\:hover\:shadow-none:hover {
+    box-shadow: none;
+  }
+
+  .lg\:focus\:shadow-xs:focus {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:focus\:shadow-sm:focus {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:focus\:shadow:focus {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:focus\:shadow-md:focus {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:focus\:shadow-lg:focus {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .lg\:focus\:shadow-xl:focus {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .lg\:focus\:shadow-2xl:focus {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .lg\:focus\:shadow-inner:focus {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .lg\:focus\:shadow-outline:focus {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .lg\:focus\:shadow-none:focus {
+    box-shadow: none;
+  }
+
+  .lg\:fill-current {
+    fill: currentColor;
+  }
+
+  .lg\:stroke-current {
+    stroke: currentColor;
+  }
+
+  .lg\:stroke-0 {
+    stroke-width: 0;
+  }
+
+  .lg\:stroke-1 {
+    stroke-width: 1;
+  }
+
+  .lg\:stroke-2 {
+    stroke-width: 2;
+  }
+
+  .lg\:table-auto {
+    table-layout: auto;
+  }
+
+  .lg\:table-fixed {
+    table-layout: fixed;
+  }
+
+  .lg\:text-left {
+    text-align: left;
+  }
+
+  .lg\:text-center {
+    text-align: center;
+  }
+
+  .lg\:text-right {
+    text-align: right;
+  }
+
+  .lg\:text-justify {
+    text-align: justify;
+  }
+
+  .lg\:text-transparent {
+    color: transparent;
+  }
+
+  .lg\:text-current {
+    color: currentColor;
+  }
+
+  .lg\:text-black {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .lg\:text-white {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .lg\:text-gray-100 {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .lg\:text-gray-200 {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .lg\:text-gray-300 {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .lg\:text-gray-400 {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .lg\:text-gray-500 {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .lg\:text-gray-600 {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .lg\:text-gray-700 {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .lg\:text-gray-800 {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .lg\:text-gray-900 {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .lg\:text-red-100 {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .lg\:text-red-200 {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .lg\:text-red-300 {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .lg\:text-red-400 {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .lg\:text-red-500 {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .lg\:text-red-600 {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .lg\:text-red-700 {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .lg\:text-red-800 {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .lg\:text-red-900 {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .lg\:text-orange-100 {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .lg\:text-orange-200 {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .lg\:text-orange-300 {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .lg\:text-orange-400 {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .lg\:text-orange-500 {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .lg\:text-orange-600 {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .lg\:text-orange-700 {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .lg\:text-orange-800 {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .lg\:text-orange-900 {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-100 {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-200 {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-300 {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-400 {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-500 {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-600 {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-700 {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-800 {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .lg\:text-yellow-900 {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .lg\:text-green-100 {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .lg\:text-green-200 {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .lg\:text-green-300 {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .lg\:text-green-400 {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .lg\:text-green-500 {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .lg\:text-green-600 {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .lg\:text-green-700 {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .lg\:text-green-800 {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .lg\:text-green-900 {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .lg\:text-teal-100 {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .lg\:text-teal-200 {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .lg\:text-teal-300 {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .lg\:text-teal-400 {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .lg\:text-teal-500 {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .lg\:text-teal-600 {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .lg\:text-teal-700 {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .lg\:text-teal-800 {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .lg\:text-teal-900 {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .lg\:text-blue-100 {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .lg\:text-blue-200 {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .lg\:text-blue-300 {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .lg\:text-blue-400 {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .lg\:text-blue-500 {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .lg\:text-blue-600 {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .lg\:text-blue-700 {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .lg\:text-blue-800 {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .lg\:text-blue-900 {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-100 {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-200 {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-300 {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-400 {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-500 {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-600 {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-700 {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-800 {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .lg\:text-indigo-900 {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .lg\:text-purple-100 {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .lg\:text-purple-200 {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .lg\:text-purple-300 {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .lg\:text-purple-400 {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .lg\:text-purple-500 {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .lg\:text-purple-600 {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .lg\:text-purple-700 {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .lg\:text-purple-800 {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .lg\:text-purple-900 {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .lg\:text-pink-100 {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .lg\:text-pink-200 {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .lg\:text-pink-300 {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .lg\:text-pink-400 {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .lg\:text-pink-500 {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .lg\:text-pink-600 {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .lg\:text-pink-700 {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .lg\:text-pink-800 {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .lg\:text-pink-900 {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-transparent:hover {
+    color: transparent;
+  }
+
+  .lg\:hover\:text-current:hover {
+    color: currentColor;
+  }
+
+  .lg\:hover\:text-black:hover {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-white:hover {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-100:hover {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-200:hover {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-300:hover {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-400:hover {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-500:hover {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-600:hover {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-700:hover {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-800:hover {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-gray-900:hover {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-100:hover {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-200:hover {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-300:hover {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-400:hover {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-500:hover {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-600:hover {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-700:hover {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-800:hover {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-red-900:hover {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-100:hover {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-200:hover {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-300:hover {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-400:hover {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-500:hover {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-600:hover {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-700:hover {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-800:hover {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-orange-900:hover {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-100:hover {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-200:hover {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-300:hover {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-400:hover {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-500:hover {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-600:hover {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-700:hover {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-800:hover {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-yellow-900:hover {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-100:hover {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-200:hover {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-300:hover {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-400:hover {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-500:hover {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-600:hover {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-700:hover {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-800:hover {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-green-900:hover {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-100:hover {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-200:hover {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-300:hover {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-400:hover {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-500:hover {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-600:hover {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-700:hover {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-800:hover {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-teal-900:hover {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-100:hover {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-200:hover {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-300:hover {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-400:hover {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-500:hover {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-600:hover {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-700:hover {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-800:hover {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-blue-900:hover {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-100:hover {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-200:hover {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-300:hover {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-400:hover {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-500:hover {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-600:hover {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-700:hover {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-800:hover {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-indigo-900:hover {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-100:hover {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-200:hover {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-300:hover {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-400:hover {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-500:hover {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-600:hover {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-700:hover {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-800:hover {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-purple-900:hover {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-100:hover {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-200:hover {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-300:hover {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-400:hover {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-500:hover {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-600:hover {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-700:hover {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-800:hover {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .lg\:hover\:text-pink-900:hover {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-transparent:focus {
+    color: transparent;
+  }
+
+  .lg\:focus\:text-current:focus {
+    color: currentColor;
+  }
+
+  .lg\:focus\:text-black:focus {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-white:focus {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-100:focus {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-200:focus {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-300:focus {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-400:focus {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-500:focus {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-600:focus {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-700:focus {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-800:focus {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-gray-900:focus {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-100:focus {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-200:focus {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-300:focus {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-400:focus {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-500:focus {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-600:focus {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-700:focus {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-800:focus {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-red-900:focus {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-100:focus {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-200:focus {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-300:focus {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-400:focus {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-500:focus {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-600:focus {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-700:focus {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-800:focus {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-orange-900:focus {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-100:focus {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-200:focus {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-300:focus {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-400:focus {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-500:focus {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-600:focus {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-700:focus {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-800:focus {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-yellow-900:focus {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-100:focus {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-200:focus {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-300:focus {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-400:focus {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-500:focus {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-600:focus {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-700:focus {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-800:focus {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-green-900:focus {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-100:focus {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-200:focus {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-300:focus {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-400:focus {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-500:focus {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-600:focus {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-700:focus {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-800:focus {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-teal-900:focus {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-100:focus {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-200:focus {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-300:focus {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-400:focus {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-500:focus {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-600:focus {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-700:focus {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-800:focus {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-blue-900:focus {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-100:focus {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-200:focus {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-300:focus {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-400:focus {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-500:focus {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-600:focus {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-700:focus {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-800:focus {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-indigo-900:focus {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-100:focus {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-200:focus {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-300:focus {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-400:focus {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-500:focus {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-600:focus {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-700:focus {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-800:focus {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-purple-900:focus {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-100:focus {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-200:focus {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-300:focus {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-400:focus {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-500:focus {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-600:focus {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-700:focus {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-800:focus {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .lg\:focus\:text-pink-900:focus {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .lg\:text-opacity-0 {
+    --text-opacity: 0;
+  }
+
+  .lg\:text-opacity-25 {
+    --text-opacity: 0.25;
+  }
+
+  .lg\:text-opacity-50 {
+    --text-opacity: 0.5;
+  }
+
+  .lg\:text-opacity-75 {
+    --text-opacity: 0.75;
+  }
+
+  .lg\:text-opacity-100 {
+    --text-opacity: 1;
+  }
+
+  .lg\:hover\:text-opacity-0:hover {
+    --text-opacity: 0;
+  }
+
+  .lg\:hover\:text-opacity-25:hover {
+    --text-opacity: 0.25;
+  }
+
+  .lg\:hover\:text-opacity-50:hover {
+    --text-opacity: 0.5;
+  }
+
+  .lg\:hover\:text-opacity-75:hover {
+    --text-opacity: 0.75;
+  }
+
+  .lg\:hover\:text-opacity-100:hover {
+    --text-opacity: 1;
+  }
+
+  .lg\:focus\:text-opacity-0:focus {
+    --text-opacity: 0;
+  }
+
+  .lg\:focus\:text-opacity-25:focus {
+    --text-opacity: 0.25;
+  }
+
+  .lg\:focus\:text-opacity-50:focus {
+    --text-opacity: 0.5;
+  }
+
+  .lg\:focus\:text-opacity-75:focus {
+    --text-opacity: 0.75;
+  }
+
+  .lg\:focus\:text-opacity-100:focus {
+    --text-opacity: 1;
+  }
+
+  .lg\:italic {
+    font-style: italic;
+  }
+
+  .lg\:not-italic {
+    font-style: normal;
+  }
+
+  .lg\:uppercase {
+    text-transform: uppercase;
+  }
+
+  .lg\:lowercase {
+    text-transform: lowercase;
+  }
+
+  .lg\:capitalize {
+    text-transform: capitalize;
+  }
+
+  .lg\:normal-case {
+    text-transform: none;
+  }
+
+  .lg\:underline {
+    text-decoration: underline;
+  }
+
+  .lg\:line-through {
+    text-decoration: line-through;
+  }
+
+  .lg\:no-underline {
+    text-decoration: none;
+  }
+
+  .lg\:hover\:underline:hover {
+    text-decoration: underline;
+  }
+
+  .lg\:hover\:line-through:hover {
+    text-decoration: line-through;
+  }
+
+  .lg\:hover\:no-underline:hover {
+    text-decoration: none;
+  }
+
+  .lg\:focus\:underline:focus {
+    text-decoration: underline;
+  }
+
+  .lg\:focus\:line-through:focus {
+    text-decoration: line-through;
+  }
+
+  .lg\:focus\:no-underline:focus {
+    text-decoration: none;
+  }
+
+  .lg\:antialiased {
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+
+  .lg\:subpixel-antialiased {
+    -webkit-font-smoothing: auto;
+    -moz-osx-font-smoothing: auto;
+  }
+
+  .lg\:ordinal, .lg\:slashed-zero, .lg\:lining-nums, .lg\:oldstyle-nums, .lg\:proportional-nums, .lg\:tabular-nums, .lg\:diagonal-fractions, .lg\:stacked-fractions {
+    --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+    font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+  }
+
+  .lg\:normal-nums {
+    font-variant-numeric: normal;
+  }
+
+  .lg\:ordinal {
+    --font-variant-numeric-ordinal: ordinal;
+  }
+
+  .lg\:slashed-zero {
+    --font-variant-numeric-slashed-zero: slashed-zero;
+  }
+
+  .lg\:lining-nums {
+    --font-variant-numeric-figure: lining-nums;
+  }
+
+  .lg\:oldstyle-nums {
+    --font-variant-numeric-figure: oldstyle-nums;
+  }
+
+  .lg\:proportional-nums {
+    --font-variant-numeric-spacing: proportional-nums;
+  }
+
+  .lg\:tabular-nums {
+    --font-variant-numeric-spacing: tabular-nums;
+  }
+
+  .lg\:diagonal-fractions {
+    --font-variant-numeric-fraction: diagonal-fractions;
+  }
+
+  .lg\:stacked-fractions {
+    --font-variant-numeric-fraction: stacked-fractions;
+  }
+
+  .lg\:tracking-tighter {
+    letter-spacing: -0.05em;
+  }
+
+  .lg\:tracking-tight {
+    letter-spacing: -0.025em;
+  }
+
+  .lg\:tracking-normal {
+    letter-spacing: 0;
+  }
+
+  .lg\:tracking-wide {
+    letter-spacing: 0.025em;
+  }
+
+  .lg\:tracking-wider {
+    letter-spacing: 0.05em;
+  }
+
+  .lg\:tracking-widest {
+    letter-spacing: 0.1em;
+  }
+
+  .lg\:select-none {
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+  }
+
+  .lg\:select-text {
+    -webkit-user-select: text;
+       -moz-user-select: text;
+        -ms-user-select: text;
+            user-select: text;
+  }
+
+  .lg\:select-all {
+    -webkit-user-select: all;
+       -moz-user-select: all;
+        -ms-user-select: all;
+            user-select: all;
+  }
+
+  .lg\:select-auto {
+    -webkit-user-select: auto;
+       -moz-user-select: auto;
+        -ms-user-select: auto;
+            user-select: auto;
+  }
+
+  .lg\:align-baseline {
+    vertical-align: baseline;
+  }
+
+  .lg\:align-top {
+    vertical-align: top;
+  }
+
+  .lg\:align-middle {
+    vertical-align: middle;
+  }
+
+  .lg\:align-bottom {
+    vertical-align: bottom;
+  }
+
+  .lg\:align-text-top {
+    vertical-align: text-top;
+  }
+
+  .lg\:align-text-bottom {
+    vertical-align: text-bottom;
+  }
+
+  .lg\:visible {
+    visibility: visible;
+  }
+
+  .lg\:invisible {
+    visibility: hidden;
+  }
+
+  .lg\:whitespace-normal {
+    white-space: normal;
+  }
+
+  .lg\:whitespace-no-wrap {
+    white-space: nowrap;
+  }
+
+  .lg\:whitespace-pre {
+    white-space: pre;
+  }
+
+  .lg\:whitespace-pre-line {
+    white-space: pre-line;
+  }
+
+  .lg\:whitespace-pre-wrap {
+    white-space: pre-wrap;
+  }
+
+  .lg\:break-normal {
+    overflow-wrap: normal;
+    word-break: normal;
+  }
+
+  .lg\:break-words {
+    overflow-wrap: break-word;
+  }
+
+  .lg\:break-all {
+    word-break: break-all;
+  }
+
+  .lg\:truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .lg\:w-0 {
+    width: 0;
+  }
+
+  .lg\:w-1 {
+    width: 0.25rem;
+  }
+
+  .lg\:w-2 {
+    width: 0.5rem;
+  }
+
+  .lg\:w-3 {
+    width: 0.75rem;
+  }
+
+  .lg\:w-4 {
+    width: 1rem;
+  }
+
+  .lg\:w-5 {
+    width: 1.25rem;
+  }
+
+  .lg\:w-6 {
+    width: 1.5rem;
+  }
+
+  .lg\:w-8 {
+    width: 2rem;
+  }
+
+  .lg\:w-10 {
+    width: 2.5rem;
+  }
+
+  .lg\:w-12 {
+    width: 3rem;
+  }
+
+  .lg\:w-16 {
+    width: 4rem;
+  }
+
+  .lg\:w-20 {
+    width: 5rem;
+  }
+
+  .lg\:w-24 {
+    width: 6rem;
+  }
+
+  .lg\:w-32 {
+    width: 8rem;
+  }
+
+  .lg\:w-40 {
+    width: 10rem;
+  }
+
+  .lg\:w-48 {
+    width: 12rem;
+  }
+
+  .lg\:w-56 {
+    width: 14rem;
+  }
+
+  .lg\:w-64 {
+    width: 16rem;
+  }
+
+  .lg\:w-auto {
+    width: auto;
+  }
+
+  .lg\:w-px {
+    width: 1px;
+  }
+
+  .lg\:w-1\/2 {
+    width: 50%;
+  }
+
+  .lg\:w-1\/3 {
+    width: 33.333333%;
+  }
+
+  .lg\:w-2\/3 {
+    width: 66.666667%;
+  }
+
+  .lg\:w-1\/4 {
+    width: 25%;
+  }
+
+  .lg\:w-2\/4 {
+    width: 50%;
+  }
+
+  .lg\:w-3\/4 {
+    width: 75%;
+  }
+
+  .lg\:w-1\/5 {
+    width: 20%;
+  }
+
+  .lg\:w-2\/5 {
+    width: 40%;
+  }
+
+  .lg\:w-3\/5 {
+    width: 60%;
+  }
+
+  .lg\:w-4\/5 {
+    width: 80%;
+  }
+
+  .lg\:w-1\/6 {
+    width: 16.666667%;
+  }
+
+  .lg\:w-2\/6 {
+    width: 33.333333%;
+  }
+
+  .lg\:w-3\/6 {
+    width: 50%;
+  }
+
+  .lg\:w-4\/6 {
+    width: 66.666667%;
+  }
+
+  .lg\:w-5\/6 {
+    width: 83.333333%;
+  }
+
+  .lg\:w-1\/12 {
+    width: 8.333333%;
+  }
+
+  .lg\:w-2\/12 {
+    width: 16.666667%;
+  }
+
+  .lg\:w-3\/12 {
+    width: 25%;
+  }
+
+  .lg\:w-4\/12 {
+    width: 33.333333%;
+  }
+
+  .lg\:w-5\/12 {
+    width: 41.666667%;
+  }
+
+  .lg\:w-6\/12 {
+    width: 50%;
+  }
+
+  .lg\:w-7\/12 {
+    width: 58.333333%;
+  }
+
+  .lg\:w-8\/12 {
+    width: 66.666667%;
+  }
+
+  .lg\:w-9\/12 {
+    width: 75%;
+  }
+
+  .lg\:w-10\/12 {
+    width: 83.333333%;
+  }
+
+  .lg\:w-11\/12 {
+    width: 91.666667%;
+  }
+
+  .lg\:w-full {
+    width: 100%;
+  }
+
+  .lg\:w-screen {
+    width: 100vw;
+  }
+
+  .lg\:z-0 {
+    z-index: 0;
+  }
+
+  .lg\:z-10 {
+    z-index: 10;
+  }
+
+  .lg\:z-20 {
+    z-index: 20;
+  }
+
+  .lg\:z-30 {
+    z-index: 30;
+  }
+
+  .lg\:z-40 {
+    z-index: 40;
+  }
+
+  .lg\:z-50 {
+    z-index: 50;
+  }
+
+  .lg\:z-auto {
+    z-index: auto;
+  }
+
+  .lg\:gap-0 {
+    grid-gap: 0;
+    gap: 0;
+  }
+
+  .lg\:gap-1 {
+    grid-gap: 0.25rem;
+    gap: 0.25rem;
+  }
+
+  .lg\:gap-2 {
+    grid-gap: 0.5rem;
+    gap: 0.5rem;
+  }
+
+  .lg\:gap-3 {
+    grid-gap: 0.75rem;
+    gap: 0.75rem;
+  }
+
+  .lg\:gap-4 {
+    grid-gap: 1rem;
+    gap: 1rem;
+  }
+
+  .lg\:gap-5 {
+    grid-gap: 1.25rem;
+    gap: 1.25rem;
+  }
+
+  .lg\:gap-6 {
+    grid-gap: 1.5rem;
+    gap: 1.5rem;
+  }
+
+  .lg\:gap-8 {
+    grid-gap: 2rem;
+    gap: 2rem;
+  }
+
+  .lg\:gap-10 {
+    grid-gap: 2.5rem;
+    gap: 2.5rem;
+  }
+
+  .lg\:gap-12 {
+    grid-gap: 3rem;
+    gap: 3rem;
+  }
+
+  .lg\:gap-16 {
+    grid-gap: 4rem;
+    gap: 4rem;
+  }
+
+  .lg\:gap-20 {
+    grid-gap: 5rem;
+    gap: 5rem;
+  }
+
+  .lg\:gap-24 {
+    grid-gap: 6rem;
+    gap: 6rem;
+  }
+
+  .lg\:gap-32 {
+    grid-gap: 8rem;
+    gap: 8rem;
+  }
+
+  .lg\:gap-40 {
+    grid-gap: 10rem;
+    gap: 10rem;
+  }
+
+  .lg\:gap-48 {
+    grid-gap: 12rem;
+    gap: 12rem;
+  }
+
+  .lg\:gap-56 {
+    grid-gap: 14rem;
+    gap: 14rem;
+  }
+
+  .lg\:gap-64 {
+    grid-gap: 16rem;
+    gap: 16rem;
+  }
+
+  .lg\:gap-px {
+    grid-gap: 1px;
+    gap: 1px;
+  }
+
+  .lg\:col-gap-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .lg\:col-gap-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .lg\:col-gap-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .lg\:col-gap-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .lg\:col-gap-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .lg\:col-gap-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .lg\:col-gap-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .lg\:col-gap-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .lg\:col-gap-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .lg\:col-gap-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .lg\:col-gap-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .lg\:col-gap-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .lg\:col-gap-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .lg\:col-gap-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .lg\:col-gap-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .lg\:col-gap-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .lg\:col-gap-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .lg\:col-gap-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .lg\:col-gap-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .lg\:gap-x-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .lg\:gap-x-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .lg\:gap-x-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .lg\:gap-x-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .lg\:gap-x-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .lg\:gap-x-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .lg\:gap-x-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .lg\:gap-x-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .lg\:gap-x-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .lg\:gap-x-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .lg\:gap-x-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .lg\:gap-x-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .lg\:gap-x-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .lg\:gap-x-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .lg\:gap-x-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .lg\:gap-x-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .lg\:gap-x-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .lg\:gap-x-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .lg\:gap-x-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .lg\:row-gap-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .lg\:row-gap-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .lg\:row-gap-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .lg\:row-gap-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .lg\:row-gap-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .lg\:row-gap-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .lg\:row-gap-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .lg\:row-gap-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .lg\:row-gap-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .lg\:row-gap-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .lg\:row-gap-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .lg\:row-gap-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .lg\:row-gap-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .lg\:row-gap-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .lg\:row-gap-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .lg\:row-gap-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .lg\:row-gap-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .lg\:row-gap-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .lg\:row-gap-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .lg\:gap-y-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .lg\:gap-y-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .lg\:gap-y-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .lg\:gap-y-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .lg\:gap-y-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .lg\:gap-y-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .lg\:gap-y-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .lg\:gap-y-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .lg\:gap-y-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .lg\:gap-y-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .lg\:gap-y-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .lg\:gap-y-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .lg\:gap-y-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .lg\:gap-y-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .lg\:gap-y-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .lg\:gap-y-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .lg\:gap-y-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .lg\:gap-y-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .lg\:gap-y-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .lg\:grid-flow-row {
+    grid-auto-flow: row;
+  }
+
+  .lg\:grid-flow-col {
+    grid-auto-flow: column;
+  }
+
+  .lg\:grid-flow-row-dense {
+    grid-auto-flow: row dense;
+  }
+
+  .lg\:grid-flow-col-dense {
+    grid-auto-flow: column dense;
+  }
+
+  .lg\:grid-cols-1 {
+    grid-template-columns: repeat(1, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-2 {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-3 {
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-4 {
+    grid-template-columns: repeat(4, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-5 {
+    grid-template-columns: repeat(5, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-6 {
+    grid-template-columns: repeat(6, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-7 {
+    grid-template-columns: repeat(7, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-8 {
+    grid-template-columns: repeat(8, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-9 {
+    grid-template-columns: repeat(9, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-10 {
+    grid-template-columns: repeat(10, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-11 {
+    grid-template-columns: repeat(11, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-12 {
+    grid-template-columns: repeat(12, minmax(0, 1fr));
+  }
+
+  .lg\:grid-cols-none {
+    grid-template-columns: none;
+  }
+
+  .lg\:col-auto {
+    grid-column: auto;
+  }
+
+  .lg\:col-span-1 {
+    grid-column: span 1 / span 1;
+  }
+
+  .lg\:col-span-2 {
+    grid-column: span 2 / span 2;
+  }
+
+  .lg\:col-span-3 {
+    grid-column: span 3 / span 3;
+  }
+
+  .lg\:col-span-4 {
+    grid-column: span 4 / span 4;
+  }
+
+  .lg\:col-span-5 {
+    grid-column: span 5 / span 5;
+  }
+
+  .lg\:col-span-6 {
+    grid-column: span 6 / span 6;
+  }
+
+  .lg\:col-span-7 {
+    grid-column: span 7 / span 7;
+  }
+
+  .lg\:col-span-8 {
+    grid-column: span 8 / span 8;
+  }
+
+  .lg\:col-span-9 {
+    grid-column: span 9 / span 9;
+  }
+
+  .lg\:col-span-10 {
+    grid-column: span 10 / span 10;
+  }
+
+  .lg\:col-span-11 {
+    grid-column: span 11 / span 11;
+  }
+
+  .lg\:col-span-12 {
+    grid-column: span 12 / span 12;
+  }
+
+  .lg\:col-start-1 {
+    grid-column-start: 1;
+  }
+
+  .lg\:col-start-2 {
+    grid-column-start: 2;
+  }
+
+  .lg\:col-start-3 {
+    grid-column-start: 3;
+  }
+
+  .lg\:col-start-4 {
+    grid-column-start: 4;
+  }
+
+  .lg\:col-start-5 {
+    grid-column-start: 5;
+  }
+
+  .lg\:col-start-6 {
+    grid-column-start: 6;
+  }
+
+  .lg\:col-start-7 {
+    grid-column-start: 7;
+  }
+
+  .lg\:col-start-8 {
+    grid-column-start: 8;
+  }
+
+  .lg\:col-start-9 {
+    grid-column-start: 9;
+  }
+
+  .lg\:col-start-10 {
+    grid-column-start: 10;
+  }
+
+  .lg\:col-start-11 {
+    grid-column-start: 11;
+  }
+
+  .lg\:col-start-12 {
+    grid-column-start: 12;
+  }
+
+  .lg\:col-start-13 {
+    grid-column-start: 13;
+  }
+
+  .lg\:col-start-auto {
+    grid-column-start: auto;
+  }
+
+  .lg\:col-end-1 {
+    grid-column-end: 1;
+  }
+
+  .lg\:col-end-2 {
+    grid-column-end: 2;
+  }
+
+  .lg\:col-end-3 {
+    grid-column-end: 3;
+  }
+
+  .lg\:col-end-4 {
+    grid-column-end: 4;
+  }
+
+  .lg\:col-end-5 {
+    grid-column-end: 5;
+  }
+
+  .lg\:col-end-6 {
+    grid-column-end: 6;
+  }
+
+  .lg\:col-end-7 {
+    grid-column-end: 7;
+  }
+
+  .lg\:col-end-8 {
+    grid-column-end: 8;
+  }
+
+  .lg\:col-end-9 {
+    grid-column-end: 9;
+  }
+
+  .lg\:col-end-10 {
+    grid-column-end: 10;
+  }
+
+  .lg\:col-end-11 {
+    grid-column-end: 11;
+  }
+
+  .lg\:col-end-12 {
+    grid-column-end: 12;
+  }
+
+  .lg\:col-end-13 {
+    grid-column-end: 13;
+  }
+
+  .lg\:col-end-auto {
+    grid-column-end: auto;
+  }
+
+  .lg\:grid-rows-1 {
+    grid-template-rows: repeat(1, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-2 {
+    grid-template-rows: repeat(2, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-3 {
+    grid-template-rows: repeat(3, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-4 {
+    grid-template-rows: repeat(4, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-5 {
+    grid-template-rows: repeat(5, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-6 {
+    grid-template-rows: repeat(6, minmax(0, 1fr));
+  }
+
+  .lg\:grid-rows-none {
+    grid-template-rows: none;
+  }
+
+  .lg\:row-auto {
+    grid-row: auto;
+  }
+
+  .lg\:row-span-1 {
+    grid-row: span 1 / span 1;
+  }
+
+  .lg\:row-span-2 {
+    grid-row: span 2 / span 2;
+  }
+
+  .lg\:row-span-3 {
+    grid-row: span 3 / span 3;
+  }
+
+  .lg\:row-span-4 {
+    grid-row: span 4 / span 4;
+  }
+
+  .lg\:row-span-5 {
+    grid-row: span 5 / span 5;
+  }
+
+  .lg\:row-span-6 {
+    grid-row: span 6 / span 6;
+  }
+
+  .lg\:row-start-1 {
+    grid-row-start: 1;
+  }
+
+  .lg\:row-start-2 {
+    grid-row-start: 2;
+  }
+
+  .lg\:row-start-3 {
+    grid-row-start: 3;
+  }
+
+  .lg\:row-start-4 {
+    grid-row-start: 4;
+  }
+
+  .lg\:row-start-5 {
+    grid-row-start: 5;
+  }
+
+  .lg\:row-start-6 {
+    grid-row-start: 6;
+  }
+
+  .lg\:row-start-7 {
+    grid-row-start: 7;
+  }
+
+  .lg\:row-start-auto {
+    grid-row-start: auto;
+  }
+
+  .lg\:row-end-1 {
+    grid-row-end: 1;
+  }
+
+  .lg\:row-end-2 {
+    grid-row-end: 2;
+  }
+
+  .lg\:row-end-3 {
+    grid-row-end: 3;
+  }
+
+  .lg\:row-end-4 {
+    grid-row-end: 4;
+  }
+
+  .lg\:row-end-5 {
+    grid-row-end: 5;
+  }
+
+  .lg\:row-end-6 {
+    grid-row-end: 6;
+  }
+
+  .lg\:row-end-7 {
+    grid-row-end: 7;
+  }
+
+  .lg\:row-end-auto {
+    grid-row-end: auto;
+  }
+
+  .lg\:transform {
+    --transform-translate-x: 0;
+    --transform-translate-y: 0;
+    --transform-rotate: 0;
+    --transform-skew-x: 0;
+    --transform-skew-y: 0;
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+    transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+  }
+
+  .lg\:transform-none {
+    transform: none;
+  }
+
+  .lg\:origin-center {
+    transform-origin: center;
+  }
+
+  .lg\:origin-top {
+    transform-origin: top;
+  }
+
+  .lg\:origin-top-right {
+    transform-origin: top right;
+  }
+
+  .lg\:origin-right {
+    transform-origin: right;
+  }
+
+  .lg\:origin-bottom-right {
+    transform-origin: bottom right;
+  }
+
+  .lg\:origin-bottom {
+    transform-origin: bottom;
+  }
+
+  .lg\:origin-bottom-left {
+    transform-origin: bottom left;
+  }
+
+  .lg\:origin-left {
+    transform-origin: left;
+  }
+
+  .lg\:origin-top-left {
+    transform-origin: top left;
+  }
+
+  .lg\:scale-0 {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .lg\:scale-50 {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .lg\:scale-75 {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .lg\:scale-90 {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .lg\:scale-95 {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .lg\:scale-100 {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .lg\:scale-105 {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:scale-110 {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:scale-125 {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:scale-150 {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:scale-x-0 {
+    --transform-scale-x: 0;
+  }
+
+  .lg\:scale-x-50 {
+    --transform-scale-x: .5;
+  }
+
+  .lg\:scale-x-75 {
+    --transform-scale-x: .75;
+  }
+
+  .lg\:scale-x-90 {
+    --transform-scale-x: .9;
+  }
+
+  .lg\:scale-x-95 {
+    --transform-scale-x: .95;
+  }
+
+  .lg\:scale-x-100 {
+    --transform-scale-x: 1;
+  }
+
+  .lg\:scale-x-105 {
+    --transform-scale-x: 1.05;
+  }
+
+  .lg\:scale-x-110 {
+    --transform-scale-x: 1.1;
+  }
+
+  .lg\:scale-x-125 {
+    --transform-scale-x: 1.25;
+  }
+
+  .lg\:scale-x-150 {
+    --transform-scale-x: 1.5;
+  }
+
+  .lg\:scale-y-0 {
+    --transform-scale-y: 0;
+  }
+
+  .lg\:scale-y-50 {
+    --transform-scale-y: .5;
+  }
+
+  .lg\:scale-y-75 {
+    --transform-scale-y: .75;
+  }
+
+  .lg\:scale-y-90 {
+    --transform-scale-y: .9;
+  }
+
+  .lg\:scale-y-95 {
+    --transform-scale-y: .95;
+  }
+
+  .lg\:scale-y-100 {
+    --transform-scale-y: 1;
+  }
+
+  .lg\:scale-y-105 {
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:scale-y-110 {
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:scale-y-125 {
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:scale-y-150 {
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:hover\:scale-0:hover {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .lg\:hover\:scale-50:hover {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .lg\:hover\:scale-75:hover {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .lg\:hover\:scale-90:hover {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .lg\:hover\:scale-95:hover {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .lg\:hover\:scale-100:hover {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .lg\:hover\:scale-105:hover {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:hover\:scale-110:hover {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:hover\:scale-125:hover {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:hover\:scale-150:hover {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:hover\:scale-x-0:hover {
+    --transform-scale-x: 0;
+  }
+
+  .lg\:hover\:scale-x-50:hover {
+    --transform-scale-x: .5;
+  }
+
+  .lg\:hover\:scale-x-75:hover {
+    --transform-scale-x: .75;
+  }
+
+  .lg\:hover\:scale-x-90:hover {
+    --transform-scale-x: .9;
+  }
+
+  .lg\:hover\:scale-x-95:hover {
+    --transform-scale-x: .95;
+  }
+
+  .lg\:hover\:scale-x-100:hover {
+    --transform-scale-x: 1;
+  }
+
+  .lg\:hover\:scale-x-105:hover {
+    --transform-scale-x: 1.05;
+  }
+
+  .lg\:hover\:scale-x-110:hover {
+    --transform-scale-x: 1.1;
+  }
+
+  .lg\:hover\:scale-x-125:hover {
+    --transform-scale-x: 1.25;
+  }
+
+  .lg\:hover\:scale-x-150:hover {
+    --transform-scale-x: 1.5;
+  }
+
+  .lg\:hover\:scale-y-0:hover {
+    --transform-scale-y: 0;
+  }
+
+  .lg\:hover\:scale-y-50:hover {
+    --transform-scale-y: .5;
+  }
+
+  .lg\:hover\:scale-y-75:hover {
+    --transform-scale-y: .75;
+  }
+
+  .lg\:hover\:scale-y-90:hover {
+    --transform-scale-y: .9;
+  }
+
+  .lg\:hover\:scale-y-95:hover {
+    --transform-scale-y: .95;
+  }
+
+  .lg\:hover\:scale-y-100:hover {
+    --transform-scale-y: 1;
+  }
+
+  .lg\:hover\:scale-y-105:hover {
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:hover\:scale-y-110:hover {
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:hover\:scale-y-125:hover {
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:hover\:scale-y-150:hover {
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:focus\:scale-0:focus {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .lg\:focus\:scale-50:focus {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .lg\:focus\:scale-75:focus {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .lg\:focus\:scale-90:focus {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .lg\:focus\:scale-95:focus {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .lg\:focus\:scale-100:focus {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .lg\:focus\:scale-105:focus {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:focus\:scale-110:focus {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:focus\:scale-125:focus {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:focus\:scale-150:focus {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:focus\:scale-x-0:focus {
+    --transform-scale-x: 0;
+  }
+
+  .lg\:focus\:scale-x-50:focus {
+    --transform-scale-x: .5;
+  }
+
+  .lg\:focus\:scale-x-75:focus {
+    --transform-scale-x: .75;
+  }
+
+  .lg\:focus\:scale-x-90:focus {
+    --transform-scale-x: .9;
+  }
+
+  .lg\:focus\:scale-x-95:focus {
+    --transform-scale-x: .95;
+  }
+
+  .lg\:focus\:scale-x-100:focus {
+    --transform-scale-x: 1;
+  }
+
+  .lg\:focus\:scale-x-105:focus {
+    --transform-scale-x: 1.05;
+  }
+
+  .lg\:focus\:scale-x-110:focus {
+    --transform-scale-x: 1.1;
+  }
+
+  .lg\:focus\:scale-x-125:focus {
+    --transform-scale-x: 1.25;
+  }
+
+  .lg\:focus\:scale-x-150:focus {
+    --transform-scale-x: 1.5;
+  }
+
+  .lg\:focus\:scale-y-0:focus {
+    --transform-scale-y: 0;
+  }
+
+  .lg\:focus\:scale-y-50:focus {
+    --transform-scale-y: .5;
+  }
+
+  .lg\:focus\:scale-y-75:focus {
+    --transform-scale-y: .75;
+  }
+
+  .lg\:focus\:scale-y-90:focus {
+    --transform-scale-y: .9;
+  }
+
+  .lg\:focus\:scale-y-95:focus {
+    --transform-scale-y: .95;
+  }
+
+  .lg\:focus\:scale-y-100:focus {
+    --transform-scale-y: 1;
+  }
+
+  .lg\:focus\:scale-y-105:focus {
+    --transform-scale-y: 1.05;
+  }
+
+  .lg\:focus\:scale-y-110:focus {
+    --transform-scale-y: 1.1;
+  }
+
+  .lg\:focus\:scale-y-125:focus {
+    --transform-scale-y: 1.25;
+  }
+
+  .lg\:focus\:scale-y-150:focus {
+    --transform-scale-y: 1.5;
+  }
+
+  .lg\:rotate-0 {
+    --transform-rotate: 0;
+  }
+
+  .lg\:rotate-45 {
+    --transform-rotate: 45deg;
+  }
+
+  .lg\:rotate-90 {
+    --transform-rotate: 90deg;
+  }
+
+  .lg\:rotate-180 {
+    --transform-rotate: 180deg;
+  }
+
+  .lg\:-rotate-180 {
+    --transform-rotate: -180deg;
+  }
+
+  .lg\:-rotate-90 {
+    --transform-rotate: -90deg;
+  }
+
+  .lg\:-rotate-45 {
+    --transform-rotate: -45deg;
+  }
+
+  .lg\:hover\:rotate-0:hover {
+    --transform-rotate: 0;
+  }
+
+  .lg\:hover\:rotate-45:hover {
+    --transform-rotate: 45deg;
+  }
+
+  .lg\:hover\:rotate-90:hover {
+    --transform-rotate: 90deg;
+  }
+
+  .lg\:hover\:rotate-180:hover {
+    --transform-rotate: 180deg;
+  }
+
+  .lg\:hover\:-rotate-180:hover {
+    --transform-rotate: -180deg;
+  }
+
+  .lg\:hover\:-rotate-90:hover {
+    --transform-rotate: -90deg;
+  }
+
+  .lg\:hover\:-rotate-45:hover {
+    --transform-rotate: -45deg;
+  }
+
+  .lg\:focus\:rotate-0:focus {
+    --transform-rotate: 0;
+  }
+
+  .lg\:focus\:rotate-45:focus {
+    --transform-rotate: 45deg;
+  }
+
+  .lg\:focus\:rotate-90:focus {
+    --transform-rotate: 90deg;
+  }
+
+  .lg\:focus\:rotate-180:focus {
+    --transform-rotate: 180deg;
+  }
+
+  .lg\:focus\:-rotate-180:focus {
+    --transform-rotate: -180deg;
+  }
+
+  .lg\:focus\:-rotate-90:focus {
+    --transform-rotate: -90deg;
+  }
+
+  .lg\:focus\:-rotate-45:focus {
+    --transform-rotate: -45deg;
+  }
+
+  .lg\:translate-x-0 {
+    --transform-translate-x: 0;
+  }
+
+  .lg\:translate-x-1 {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .lg\:translate-x-2 {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .lg\:translate-x-3 {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .lg\:translate-x-4 {
+    --transform-translate-x: 1rem;
+  }
+
+  .lg\:translate-x-5 {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .lg\:translate-x-6 {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .lg\:translate-x-8 {
+    --transform-translate-x: 2rem;
+  }
+
+  .lg\:translate-x-10 {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .lg\:translate-x-12 {
+    --transform-translate-x: 3rem;
+  }
+
+  .lg\:translate-x-16 {
+    --transform-translate-x: 4rem;
+  }
+
+  .lg\:translate-x-20 {
+    --transform-translate-x: 5rem;
+  }
+
+  .lg\:translate-x-24 {
+    --transform-translate-x: 6rem;
+  }
+
+  .lg\:translate-x-32 {
+    --transform-translate-x: 8rem;
+  }
+
+  .lg\:translate-x-40 {
+    --transform-translate-x: 10rem;
+  }
+
+  .lg\:translate-x-48 {
+    --transform-translate-x: 12rem;
+  }
+
+  .lg\:translate-x-56 {
+    --transform-translate-x: 14rem;
+  }
+
+  .lg\:translate-x-64 {
+    --transform-translate-x: 16rem;
+  }
+
+  .lg\:translate-x-px {
+    --transform-translate-x: 1px;
+  }
+
+  .lg\:-translate-x-1 {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .lg\:-translate-x-2 {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .lg\:-translate-x-3 {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .lg\:-translate-x-4 {
+    --transform-translate-x: -1rem;
+  }
+
+  .lg\:-translate-x-5 {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .lg\:-translate-x-6 {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .lg\:-translate-x-8 {
+    --transform-translate-x: -2rem;
+  }
+
+  .lg\:-translate-x-10 {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .lg\:-translate-x-12 {
+    --transform-translate-x: -3rem;
+  }
+
+  .lg\:-translate-x-16 {
+    --transform-translate-x: -4rem;
+  }
+
+  .lg\:-translate-x-20 {
+    --transform-translate-x: -5rem;
+  }
+
+  .lg\:-translate-x-24 {
+    --transform-translate-x: -6rem;
+  }
+
+  .lg\:-translate-x-32 {
+    --transform-translate-x: -8rem;
+  }
+
+  .lg\:-translate-x-40 {
+    --transform-translate-x: -10rem;
+  }
+
+  .lg\:-translate-x-48 {
+    --transform-translate-x: -12rem;
+  }
+
+  .lg\:-translate-x-56 {
+    --transform-translate-x: -14rem;
+  }
+
+  .lg\:-translate-x-64 {
+    --transform-translate-x: -16rem;
+  }
+
+  .lg\:-translate-x-px {
+    --transform-translate-x: -1px;
+  }
+
+  .lg\:-translate-x-full {
+    --transform-translate-x: -100%;
+  }
+
+  .lg\:-translate-x-1\/2 {
+    --transform-translate-x: -50%;
+  }
+
+  .lg\:translate-x-1\/2 {
+    --transform-translate-x: 50%;
+  }
+
+  .lg\:translate-x-full {
+    --transform-translate-x: 100%;
+  }
+
+  .lg\:translate-y-0 {
+    --transform-translate-y: 0;
+  }
+
+  .lg\:translate-y-1 {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .lg\:translate-y-2 {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .lg\:translate-y-3 {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .lg\:translate-y-4 {
+    --transform-translate-y: 1rem;
+  }
+
+  .lg\:translate-y-5 {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .lg\:translate-y-6 {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .lg\:translate-y-8 {
+    --transform-translate-y: 2rem;
+  }
+
+  .lg\:translate-y-10 {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .lg\:translate-y-12 {
+    --transform-translate-y: 3rem;
+  }
+
+  .lg\:translate-y-16 {
+    --transform-translate-y: 4rem;
+  }
+
+  .lg\:translate-y-20 {
+    --transform-translate-y: 5rem;
+  }
+
+  .lg\:translate-y-24 {
+    --transform-translate-y: 6rem;
+  }
+
+  .lg\:translate-y-32 {
+    --transform-translate-y: 8rem;
+  }
+
+  .lg\:translate-y-40 {
+    --transform-translate-y: 10rem;
+  }
+
+  .lg\:translate-y-48 {
+    --transform-translate-y: 12rem;
+  }
+
+  .lg\:translate-y-56 {
+    --transform-translate-y: 14rem;
+  }
+
+  .lg\:translate-y-64 {
+    --transform-translate-y: 16rem;
+  }
+
+  .lg\:translate-y-px {
+    --transform-translate-y: 1px;
+  }
+
+  .lg\:-translate-y-1 {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .lg\:-translate-y-2 {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .lg\:-translate-y-3 {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .lg\:-translate-y-4 {
+    --transform-translate-y: -1rem;
+  }
+
+  .lg\:-translate-y-5 {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .lg\:-translate-y-6 {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .lg\:-translate-y-8 {
+    --transform-translate-y: -2rem;
+  }
+
+  .lg\:-translate-y-10 {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .lg\:-translate-y-12 {
+    --transform-translate-y: -3rem;
+  }
+
+  .lg\:-translate-y-16 {
+    --transform-translate-y: -4rem;
+  }
+
+  .lg\:-translate-y-20 {
+    --transform-translate-y: -5rem;
+  }
+
+  .lg\:-translate-y-24 {
+    --transform-translate-y: -6rem;
+  }
+
+  .lg\:-translate-y-32 {
+    --transform-translate-y: -8rem;
+  }
+
+  .lg\:-translate-y-40 {
+    --transform-translate-y: -10rem;
+  }
+
+  .lg\:-translate-y-48 {
+    --transform-translate-y: -12rem;
+  }
+
+  .lg\:-translate-y-56 {
+    --transform-translate-y: -14rem;
+  }
+
+  .lg\:-translate-y-64 {
+    --transform-translate-y: -16rem;
+  }
+
+  .lg\:-translate-y-px {
+    --transform-translate-y: -1px;
+  }
+
+  .lg\:-translate-y-full {
+    --transform-translate-y: -100%;
+  }
+
+  .lg\:-translate-y-1\/2 {
+    --transform-translate-y: -50%;
+  }
+
+  .lg\:translate-y-1\/2 {
+    --transform-translate-y: 50%;
+  }
+
+  .lg\:translate-y-full {
+    --transform-translate-y: 100%;
+  }
+
+  .lg\:hover\:translate-x-0:hover {
+    --transform-translate-x: 0;
+  }
+
+  .lg\:hover\:translate-x-1:hover {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .lg\:hover\:translate-x-2:hover {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .lg\:hover\:translate-x-3:hover {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .lg\:hover\:translate-x-4:hover {
+    --transform-translate-x: 1rem;
+  }
+
+  .lg\:hover\:translate-x-5:hover {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .lg\:hover\:translate-x-6:hover {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .lg\:hover\:translate-x-8:hover {
+    --transform-translate-x: 2rem;
+  }
+
+  .lg\:hover\:translate-x-10:hover {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .lg\:hover\:translate-x-12:hover {
+    --transform-translate-x: 3rem;
+  }
+
+  .lg\:hover\:translate-x-16:hover {
+    --transform-translate-x: 4rem;
+  }
+
+  .lg\:hover\:translate-x-20:hover {
+    --transform-translate-x: 5rem;
+  }
+
+  .lg\:hover\:translate-x-24:hover {
+    --transform-translate-x: 6rem;
+  }
+
+  .lg\:hover\:translate-x-32:hover {
+    --transform-translate-x: 8rem;
+  }
+
+  .lg\:hover\:translate-x-40:hover {
+    --transform-translate-x: 10rem;
+  }
+
+  .lg\:hover\:translate-x-48:hover {
+    --transform-translate-x: 12rem;
+  }
+
+  .lg\:hover\:translate-x-56:hover {
+    --transform-translate-x: 14rem;
+  }
+
+  .lg\:hover\:translate-x-64:hover {
+    --transform-translate-x: 16rem;
+  }
+
+  .lg\:hover\:translate-x-px:hover {
+    --transform-translate-x: 1px;
+  }
+
+  .lg\:hover\:-translate-x-1:hover {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .lg\:hover\:-translate-x-2:hover {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .lg\:hover\:-translate-x-3:hover {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .lg\:hover\:-translate-x-4:hover {
+    --transform-translate-x: -1rem;
+  }
+
+  .lg\:hover\:-translate-x-5:hover {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .lg\:hover\:-translate-x-6:hover {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .lg\:hover\:-translate-x-8:hover {
+    --transform-translate-x: -2rem;
+  }
+
+  .lg\:hover\:-translate-x-10:hover {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .lg\:hover\:-translate-x-12:hover {
+    --transform-translate-x: -3rem;
+  }
+
+  .lg\:hover\:-translate-x-16:hover {
+    --transform-translate-x: -4rem;
+  }
+
+  .lg\:hover\:-translate-x-20:hover {
+    --transform-translate-x: -5rem;
+  }
+
+  .lg\:hover\:-translate-x-24:hover {
+    --transform-translate-x: -6rem;
+  }
+
+  .lg\:hover\:-translate-x-32:hover {
+    --transform-translate-x: -8rem;
+  }
+
+  .lg\:hover\:-translate-x-40:hover {
+    --transform-translate-x: -10rem;
+  }
+
+  .lg\:hover\:-translate-x-48:hover {
+    --transform-translate-x: -12rem;
+  }
+
+  .lg\:hover\:-translate-x-56:hover {
+    --transform-translate-x: -14rem;
+  }
+
+  .lg\:hover\:-translate-x-64:hover {
+    --transform-translate-x: -16rem;
+  }
+
+  .lg\:hover\:-translate-x-px:hover {
+    --transform-translate-x: -1px;
+  }
+
+  .lg\:hover\:-translate-x-full:hover {
+    --transform-translate-x: -100%;
+  }
+
+  .lg\:hover\:-translate-x-1\/2:hover {
+    --transform-translate-x: -50%;
+  }
+
+  .lg\:hover\:translate-x-1\/2:hover {
+    --transform-translate-x: 50%;
+  }
+
+  .lg\:hover\:translate-x-full:hover {
+    --transform-translate-x: 100%;
+  }
+
+  .lg\:hover\:translate-y-0:hover {
+    --transform-translate-y: 0;
+  }
+
+  .lg\:hover\:translate-y-1:hover {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .lg\:hover\:translate-y-2:hover {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .lg\:hover\:translate-y-3:hover {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .lg\:hover\:translate-y-4:hover {
+    --transform-translate-y: 1rem;
+  }
+
+  .lg\:hover\:translate-y-5:hover {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .lg\:hover\:translate-y-6:hover {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .lg\:hover\:translate-y-8:hover {
+    --transform-translate-y: 2rem;
+  }
+
+  .lg\:hover\:translate-y-10:hover {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .lg\:hover\:translate-y-12:hover {
+    --transform-translate-y: 3rem;
+  }
+
+  .lg\:hover\:translate-y-16:hover {
+    --transform-translate-y: 4rem;
+  }
+
+  .lg\:hover\:translate-y-20:hover {
+    --transform-translate-y: 5rem;
+  }
+
+  .lg\:hover\:translate-y-24:hover {
+    --transform-translate-y: 6rem;
+  }
+
+  .lg\:hover\:translate-y-32:hover {
+    --transform-translate-y: 8rem;
+  }
+
+  .lg\:hover\:translate-y-40:hover {
+    --transform-translate-y: 10rem;
+  }
+
+  .lg\:hover\:translate-y-48:hover {
+    --transform-translate-y: 12rem;
+  }
+
+  .lg\:hover\:translate-y-56:hover {
+    --transform-translate-y: 14rem;
+  }
+
+  .lg\:hover\:translate-y-64:hover {
+    --transform-translate-y: 16rem;
+  }
+
+  .lg\:hover\:translate-y-px:hover {
+    --transform-translate-y: 1px;
+  }
+
+  .lg\:hover\:-translate-y-1:hover {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .lg\:hover\:-translate-y-2:hover {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .lg\:hover\:-translate-y-3:hover {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .lg\:hover\:-translate-y-4:hover {
+    --transform-translate-y: -1rem;
+  }
+
+  .lg\:hover\:-translate-y-5:hover {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .lg\:hover\:-translate-y-6:hover {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .lg\:hover\:-translate-y-8:hover {
+    --transform-translate-y: -2rem;
+  }
+
+  .lg\:hover\:-translate-y-10:hover {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .lg\:hover\:-translate-y-12:hover {
+    --transform-translate-y: -3rem;
+  }
+
+  .lg\:hover\:-translate-y-16:hover {
+    --transform-translate-y: -4rem;
+  }
+
+  .lg\:hover\:-translate-y-20:hover {
+    --transform-translate-y: -5rem;
+  }
+
+  .lg\:hover\:-translate-y-24:hover {
+    --transform-translate-y: -6rem;
+  }
+
+  .lg\:hover\:-translate-y-32:hover {
+    --transform-translate-y: -8rem;
+  }
+
+  .lg\:hover\:-translate-y-40:hover {
+    --transform-translate-y: -10rem;
+  }
+
+  .lg\:hover\:-translate-y-48:hover {
+    --transform-translate-y: -12rem;
+  }
+
+  .lg\:hover\:-translate-y-56:hover {
+    --transform-translate-y: -14rem;
+  }
+
+  .lg\:hover\:-translate-y-64:hover {
+    --transform-translate-y: -16rem;
+  }
+
+  .lg\:hover\:-translate-y-px:hover {
+    --transform-translate-y: -1px;
+  }
+
+  .lg\:hover\:-translate-y-full:hover {
+    --transform-translate-y: -100%;
+  }
+
+  .lg\:hover\:-translate-y-1\/2:hover {
+    --transform-translate-y: -50%;
+  }
+
+  .lg\:hover\:translate-y-1\/2:hover {
+    --transform-translate-y: 50%;
+  }
+
+  .lg\:hover\:translate-y-full:hover {
+    --transform-translate-y: 100%;
+  }
+
+  .lg\:focus\:translate-x-0:focus {
+    --transform-translate-x: 0;
+  }
+
+  .lg\:focus\:translate-x-1:focus {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .lg\:focus\:translate-x-2:focus {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .lg\:focus\:translate-x-3:focus {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .lg\:focus\:translate-x-4:focus {
+    --transform-translate-x: 1rem;
+  }
+
+  .lg\:focus\:translate-x-5:focus {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .lg\:focus\:translate-x-6:focus {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .lg\:focus\:translate-x-8:focus {
+    --transform-translate-x: 2rem;
+  }
+
+  .lg\:focus\:translate-x-10:focus {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .lg\:focus\:translate-x-12:focus {
+    --transform-translate-x: 3rem;
+  }
+
+  .lg\:focus\:translate-x-16:focus {
+    --transform-translate-x: 4rem;
+  }
+
+  .lg\:focus\:translate-x-20:focus {
+    --transform-translate-x: 5rem;
+  }
+
+  .lg\:focus\:translate-x-24:focus {
+    --transform-translate-x: 6rem;
+  }
+
+  .lg\:focus\:translate-x-32:focus {
+    --transform-translate-x: 8rem;
+  }
+
+  .lg\:focus\:translate-x-40:focus {
+    --transform-translate-x: 10rem;
+  }
+
+  .lg\:focus\:translate-x-48:focus {
+    --transform-translate-x: 12rem;
+  }
+
+  .lg\:focus\:translate-x-56:focus {
+    --transform-translate-x: 14rem;
+  }
+
+  .lg\:focus\:translate-x-64:focus {
+    --transform-translate-x: 16rem;
+  }
+
+  .lg\:focus\:translate-x-px:focus {
+    --transform-translate-x: 1px;
+  }
+
+  .lg\:focus\:-translate-x-1:focus {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .lg\:focus\:-translate-x-2:focus {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .lg\:focus\:-translate-x-3:focus {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .lg\:focus\:-translate-x-4:focus {
+    --transform-translate-x: -1rem;
+  }
+
+  .lg\:focus\:-translate-x-5:focus {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .lg\:focus\:-translate-x-6:focus {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .lg\:focus\:-translate-x-8:focus {
+    --transform-translate-x: -2rem;
+  }
+
+  .lg\:focus\:-translate-x-10:focus {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .lg\:focus\:-translate-x-12:focus {
+    --transform-translate-x: -3rem;
+  }
+
+  .lg\:focus\:-translate-x-16:focus {
+    --transform-translate-x: -4rem;
+  }
+
+  .lg\:focus\:-translate-x-20:focus {
+    --transform-translate-x: -5rem;
+  }
+
+  .lg\:focus\:-translate-x-24:focus {
+    --transform-translate-x: -6rem;
+  }
+
+  .lg\:focus\:-translate-x-32:focus {
+    --transform-translate-x: -8rem;
+  }
+
+  .lg\:focus\:-translate-x-40:focus {
+    --transform-translate-x: -10rem;
+  }
+
+  .lg\:focus\:-translate-x-48:focus {
+    --transform-translate-x: -12rem;
+  }
+
+  .lg\:focus\:-translate-x-56:focus {
+    --transform-translate-x: -14rem;
+  }
+
+  .lg\:focus\:-translate-x-64:focus {
+    --transform-translate-x: -16rem;
+  }
+
+  .lg\:focus\:-translate-x-px:focus {
+    --transform-translate-x: -1px;
+  }
+
+  .lg\:focus\:-translate-x-full:focus {
+    --transform-translate-x: -100%;
+  }
+
+  .lg\:focus\:-translate-x-1\/2:focus {
+    --transform-translate-x: -50%;
+  }
+
+  .lg\:focus\:translate-x-1\/2:focus {
+    --transform-translate-x: 50%;
+  }
+
+  .lg\:focus\:translate-x-full:focus {
+    --transform-translate-x: 100%;
+  }
+
+  .lg\:focus\:translate-y-0:focus {
+    --transform-translate-y: 0;
+  }
+
+  .lg\:focus\:translate-y-1:focus {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .lg\:focus\:translate-y-2:focus {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .lg\:focus\:translate-y-3:focus {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .lg\:focus\:translate-y-4:focus {
+    --transform-translate-y: 1rem;
+  }
+
+  .lg\:focus\:translate-y-5:focus {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .lg\:focus\:translate-y-6:focus {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .lg\:focus\:translate-y-8:focus {
+    --transform-translate-y: 2rem;
+  }
+
+  .lg\:focus\:translate-y-10:focus {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .lg\:focus\:translate-y-12:focus {
+    --transform-translate-y: 3rem;
+  }
+
+  .lg\:focus\:translate-y-16:focus {
+    --transform-translate-y: 4rem;
+  }
+
+  .lg\:focus\:translate-y-20:focus {
+    --transform-translate-y: 5rem;
+  }
+
+  .lg\:focus\:translate-y-24:focus {
+    --transform-translate-y: 6rem;
+  }
+
+  .lg\:focus\:translate-y-32:focus {
+    --transform-translate-y: 8rem;
+  }
+
+  .lg\:focus\:translate-y-40:focus {
+    --transform-translate-y: 10rem;
+  }
+
+  .lg\:focus\:translate-y-48:focus {
+    --transform-translate-y: 12rem;
+  }
+
+  .lg\:focus\:translate-y-56:focus {
+    --transform-translate-y: 14rem;
+  }
+
+  .lg\:focus\:translate-y-64:focus {
+    --transform-translate-y: 16rem;
+  }
+
+  .lg\:focus\:translate-y-px:focus {
+    --transform-translate-y: 1px;
+  }
+
+  .lg\:focus\:-translate-y-1:focus {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .lg\:focus\:-translate-y-2:focus {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .lg\:focus\:-translate-y-3:focus {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .lg\:focus\:-translate-y-4:focus {
+    --transform-translate-y: -1rem;
+  }
+
+  .lg\:focus\:-translate-y-5:focus {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .lg\:focus\:-translate-y-6:focus {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .lg\:focus\:-translate-y-8:focus {
+    --transform-translate-y: -2rem;
+  }
+
+  .lg\:focus\:-translate-y-10:focus {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .lg\:focus\:-translate-y-12:focus {
+    --transform-translate-y: -3rem;
+  }
+
+  .lg\:focus\:-translate-y-16:focus {
+    --transform-translate-y: -4rem;
+  }
+
+  .lg\:focus\:-translate-y-20:focus {
+    --transform-translate-y: -5rem;
+  }
+
+  .lg\:focus\:-translate-y-24:focus {
+    --transform-translate-y: -6rem;
+  }
+
+  .lg\:focus\:-translate-y-32:focus {
+    --transform-translate-y: -8rem;
+  }
+
+  .lg\:focus\:-translate-y-40:focus {
+    --transform-translate-y: -10rem;
+  }
+
+  .lg\:focus\:-translate-y-48:focus {
+    --transform-translate-y: -12rem;
+  }
+
+  .lg\:focus\:-translate-y-56:focus {
+    --transform-translate-y: -14rem;
+  }
+
+  .lg\:focus\:-translate-y-64:focus {
+    --transform-translate-y: -16rem;
+  }
+
+  .lg\:focus\:-translate-y-px:focus {
+    --transform-translate-y: -1px;
+  }
+
+  .lg\:focus\:-translate-y-full:focus {
+    --transform-translate-y: -100%;
+  }
+
+  .lg\:focus\:-translate-y-1\/2:focus {
+    --transform-translate-y: -50%;
+  }
+
+  .lg\:focus\:translate-y-1\/2:focus {
+    --transform-translate-y: 50%;
+  }
+
+  .lg\:focus\:translate-y-full:focus {
+    --transform-translate-y: 100%;
+  }
+
+  .lg\:skew-x-0 {
+    --transform-skew-x: 0;
+  }
+
+  .lg\:skew-x-3 {
+    --transform-skew-x: 3deg;
+  }
+
+  .lg\:skew-x-6 {
+    --transform-skew-x: 6deg;
+  }
+
+  .lg\:skew-x-12 {
+    --transform-skew-x: 12deg;
+  }
+
+  .lg\:-skew-x-12 {
+    --transform-skew-x: -12deg;
+  }
+
+  .lg\:-skew-x-6 {
+    --transform-skew-x: -6deg;
+  }
+
+  .lg\:-skew-x-3 {
+    --transform-skew-x: -3deg;
+  }
+
+  .lg\:skew-y-0 {
+    --transform-skew-y: 0;
+  }
+
+  .lg\:skew-y-3 {
+    --transform-skew-y: 3deg;
+  }
+
+  .lg\:skew-y-6 {
+    --transform-skew-y: 6deg;
+  }
+
+  .lg\:skew-y-12 {
+    --transform-skew-y: 12deg;
+  }
+
+  .lg\:-skew-y-12 {
+    --transform-skew-y: -12deg;
+  }
+
+  .lg\:-skew-y-6 {
+    --transform-skew-y: -6deg;
+  }
+
+  .lg\:-skew-y-3 {
+    --transform-skew-y: -3deg;
+  }
+
+  .lg\:hover\:skew-x-0:hover {
+    --transform-skew-x: 0;
+  }
+
+  .lg\:hover\:skew-x-3:hover {
+    --transform-skew-x: 3deg;
+  }
+
+  .lg\:hover\:skew-x-6:hover {
+    --transform-skew-x: 6deg;
+  }
+
+  .lg\:hover\:skew-x-12:hover {
+    --transform-skew-x: 12deg;
+  }
+
+  .lg\:hover\:-skew-x-12:hover {
+    --transform-skew-x: -12deg;
+  }
+
+  .lg\:hover\:-skew-x-6:hover {
+    --transform-skew-x: -6deg;
+  }
+
+  .lg\:hover\:-skew-x-3:hover {
+    --transform-skew-x: -3deg;
+  }
+
+  .lg\:hover\:skew-y-0:hover {
+    --transform-skew-y: 0;
+  }
+
+  .lg\:hover\:skew-y-3:hover {
+    --transform-skew-y: 3deg;
+  }
+
+  .lg\:hover\:skew-y-6:hover {
+    --transform-skew-y: 6deg;
+  }
+
+  .lg\:hover\:skew-y-12:hover {
+    --transform-skew-y: 12deg;
+  }
+
+  .lg\:hover\:-skew-y-12:hover {
+    --transform-skew-y: -12deg;
+  }
+
+  .lg\:hover\:-skew-y-6:hover {
+    --transform-skew-y: -6deg;
+  }
+
+  .lg\:hover\:-skew-y-3:hover {
+    --transform-skew-y: -3deg;
+  }
+
+  .lg\:focus\:skew-x-0:focus {
+    --transform-skew-x: 0;
+  }
+
+  .lg\:focus\:skew-x-3:focus {
+    --transform-skew-x: 3deg;
+  }
+
+  .lg\:focus\:skew-x-6:focus {
+    --transform-skew-x: 6deg;
+  }
+
+  .lg\:focus\:skew-x-12:focus {
+    --transform-skew-x: 12deg;
+  }
+
+  .lg\:focus\:-skew-x-12:focus {
+    --transform-skew-x: -12deg;
+  }
+
+  .lg\:focus\:-skew-x-6:focus {
+    --transform-skew-x: -6deg;
+  }
+
+  .lg\:focus\:-skew-x-3:focus {
+    --transform-skew-x: -3deg;
+  }
+
+  .lg\:focus\:skew-y-0:focus {
+    --transform-skew-y: 0;
+  }
+
+  .lg\:focus\:skew-y-3:focus {
+    --transform-skew-y: 3deg;
+  }
+
+  .lg\:focus\:skew-y-6:focus {
+    --transform-skew-y: 6deg;
+  }
+
+  .lg\:focus\:skew-y-12:focus {
+    --transform-skew-y: 12deg;
+  }
+
+  .lg\:focus\:-skew-y-12:focus {
+    --transform-skew-y: -12deg;
+  }
+
+  .lg\:focus\:-skew-y-6:focus {
+    --transform-skew-y: -6deg;
+  }
+
+  .lg\:focus\:-skew-y-3:focus {
+    --transform-skew-y: -3deg;
+  }
+
+  .lg\:transition-none {
+    transition-property: none;
+  }
+
+  .lg\:transition-all {
+    transition-property: all;
+  }
+
+  .lg\:transition {
+    transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+  }
+
+  .lg\:transition-colors {
+    transition-property: background-color, border-color, color, fill, stroke;
+  }
+
+  .lg\:transition-opacity {
+    transition-property: opacity;
+  }
+
+  .lg\:transition-shadow {
+    transition-property: box-shadow;
+  }
+
+  .lg\:transition-transform {
+    transition-property: transform;
+  }
+
+  .lg\:ease-linear {
+    transition-timing-function: linear;
+  }
+
+  .lg\:ease-in {
+    transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+  }
+
+  .lg\:ease-out {
+    transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+  }
+
+  .lg\:ease-in-out {
+    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .lg\:duration-75 {
+    transition-duration: 75ms;
+  }
+
+  .lg\:duration-100 {
+    transition-duration: 100ms;
+  }
+
+  .lg\:duration-150 {
+    transition-duration: 150ms;
+  }
+
+  .lg\:duration-200 {
+    transition-duration: 200ms;
+  }
+
+  .lg\:duration-300 {
+    transition-duration: 300ms;
+  }
+
+  .lg\:duration-500 {
+    transition-duration: 500ms;
+  }
+
+  .lg\:duration-700 {
+    transition-duration: 700ms;
+  }
+
+  .lg\:duration-1000 {
+    transition-duration: 1000ms;
+  }
+
+  .lg\:delay-75 {
+    transition-delay: 75ms;
+  }
+
+  .lg\:delay-100 {
+    transition-delay: 100ms;
+  }
+
+  .lg\:delay-150 {
+    transition-delay: 150ms;
+  }
+
+  .lg\:delay-200 {
+    transition-delay: 200ms;
+  }
+
+  .lg\:delay-300 {
+    transition-delay: 300ms;
+  }
+
+  .lg\:delay-500 {
+    transition-delay: 500ms;
+  }
+
+  .lg\:delay-700 {
+    transition-delay: 700ms;
+  }
+
+  .lg\:delay-1000 {
+    transition-delay: 1000ms;
+  }
+
+  .lg\:animate-none {
+    -webkit-animation: none;
+            animation: none;
+  }
+
+  .lg\:animate-spin {
+    -webkit-animation: spin 1s linear infinite;
+            animation: spin 1s linear infinite;
+  }
+
+  .lg\:animate-ping {
+    -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+            animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+  }
+
+  .lg\:animate-pulse {
+    -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+            animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+  }
+
+  .lg\:animate-bounce {
+    -webkit-animation: bounce 1s infinite;
+            animation: bounce 1s infinite;
+  }
+}
+
+@media (min-width: 1280px) {
+  .xl\:container {
+    width: 100%;
+  }
+
+  @media (min-width: 640px) {
+    .xl\:container {
+      max-width: 640px;
+    }
+  }
+
+  @media (min-width: 768px) {
+    .xl\:container {
+      max-width: 768px;
+    }
+  }
+
+  @media (min-width: 1024px) {
+    .xl\:container {
+      max-width: 1024px;
+    }
+  }
+
+  @media (min-width: 1280px) {
+    .xl\:container {
+      max-width: 1280px;
+    }
+  }
+
+  .xl\:space-y-0 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0px * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-0 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0px * var(--space-x-reverse));
+    margin-left: calc(0px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.25rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.25rem * var(--space-x-reverse));
+    margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.5rem * var(--space-x-reverse));
+    margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(0.75rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(0.75rem * var(--space-x-reverse));
+    margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1rem * var(--space-x-reverse));
+    margin-left: calc(1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.25rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.25rem * var(--space-x-reverse));
+    margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1.5rem * var(--space-x-reverse));
+    margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2rem * var(--space-x-reverse));
+    margin-left: calc(2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(2.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(2.5rem * var(--space-x-reverse));
+    margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(3rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(3rem * var(--space-x-reverse));
+    margin-left: calc(3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(4rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(4rem * var(--space-x-reverse));
+    margin-left: calc(4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(5rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(5rem * var(--space-x-reverse));
+    margin-left: calc(5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(6rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(6rem * var(--space-x-reverse));
+    margin-left: calc(6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(8rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(8rem * var(--space-x-reverse));
+    margin-left: calc(8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(10rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(10rem * var(--space-x-reverse));
+    margin-left: calc(10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(12rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(12rem * var(--space-x-reverse));
+    margin-left: calc(12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(14rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(14rem * var(--space-x-reverse));
+    margin-left: calc(14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(16rem * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(16rem * var(--space-x-reverse));
+    margin-left: calc(16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(1px * var(--space-y-reverse));
+  }
+
+  .xl\:space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(1px * var(--space-x-reverse));
+    margin-left: calc(1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-1 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.25rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-1 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.25rem * var(--space-x-reverse));
+    margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-2 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-2 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.5rem * var(--space-x-reverse));
+    margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-3 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-0.75rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-3 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-0.75rem * var(--space-x-reverse));
+    margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-4 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-4 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1rem * var(--space-x-reverse));
+    margin-left: calc(-1rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-5 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.25rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-5 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.25rem * var(--space-x-reverse));
+    margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-6 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-6 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1.5rem * var(--space-x-reverse));
+    margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-8 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-8 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2rem * var(--space-x-reverse));
+    margin-left: calc(-2rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-10 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-2.5rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-10 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-2.5rem * var(--space-x-reverse));
+    margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-12 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-3rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-3rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-12 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-3rem * var(--space-x-reverse));
+    margin-left: calc(-3rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-16 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-4rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-4rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-16 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-4rem * var(--space-x-reverse));
+    margin-left: calc(-4rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-20 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-5rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-5rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-20 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-5rem * var(--space-x-reverse));
+    margin-left: calc(-5rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-24 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-6rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-6rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-24 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-6rem * var(--space-x-reverse));
+    margin-left: calc(-6rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-32 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-8rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-8rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-32 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-8rem * var(--space-x-reverse));
+    margin-left: calc(-8rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-40 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-10rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-10rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-40 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-10rem * var(--space-x-reverse));
+    margin-left: calc(-10rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-48 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-12rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-12rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-48 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-12rem * var(--space-x-reverse));
+    margin-left: calc(-12rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-56 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-14rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-14rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-56 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-14rem * var(--space-x-reverse));
+    margin-left: calc(-14rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-64 > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-16rem * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-16rem * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-64 > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-16rem * var(--space-x-reverse));
+    margin-left: calc(-16rem * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:-space-y-px > :not(template) ~ :not(template) {
+    --space-y-reverse: 0;
+    margin-top: calc(-1px * calc(1 - var(--space-y-reverse)));
+    margin-bottom: calc(-1px * var(--space-y-reverse));
+  }
+
+  .xl\:-space-x-px > :not(template) ~ :not(template) {
+    --space-x-reverse: 0;
+    margin-right: calc(-1px * var(--space-x-reverse));
+    margin-left: calc(-1px * calc(1 - var(--space-x-reverse)));
+  }
+
+  .xl\:space-y-reverse > :not(template) ~ :not(template) {
+    --space-y-reverse: 1;
+  }
+
+  .xl\:space-x-reverse > :not(template) ~ :not(template) {
+    --space-x-reverse: 1;
+  }
+
+  .xl\:divide-y-0 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(0px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(0px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x-0 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(0px * var(--divide-x-reverse));
+    border-left-width: calc(0px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y-2 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(2px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(2px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x-2 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(2px * var(--divide-x-reverse));
+    border-left-width: calc(2px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y-4 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(4px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(4px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x-4 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(4px * var(--divide-x-reverse));
+    border-left-width: calc(4px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y-8 > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(8px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(8px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x-8 > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(8px * var(--divide-x-reverse));
+    border-left-width: calc(8px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y > :not(template) ~ :not(template) {
+    --divide-y-reverse: 0;
+    border-top-width: calc(1px * calc(1 - var(--divide-y-reverse)));
+    border-bottom-width: calc(1px * var(--divide-y-reverse));
+  }
+
+  .xl\:divide-x > :not(template) ~ :not(template) {
+    --divide-x-reverse: 0;
+    border-right-width: calc(1px * var(--divide-x-reverse));
+    border-left-width: calc(1px * calc(1 - var(--divide-x-reverse)));
+  }
+
+  .xl\:divide-y-reverse > :not(template) ~ :not(template) {
+    --divide-y-reverse: 1;
+  }
+
+  .xl\:divide-x-reverse > :not(template) ~ :not(template) {
+    --divide-x-reverse: 1;
+  }
+
+  .xl\:divide-transparent > :not(template) ~ :not(template) {
+    border-color: transparent;
+  }
+
+  .xl\:divide-current > :not(template) ~ :not(template) {
+    border-color: currentColor;
+  }
+
+  .xl\:divide-black > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--divide-opacity));
+  }
+
+  .xl\:divide-white > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--divide-opacity));
+  }
+
+  .xl\:divide-gray-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--divide-opacity));
+  }
+
+  .xl\:divide-red-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--divide-opacity));
+  }
+
+  .xl\:divide-orange-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--divide-opacity));
+  }
+
+  .xl\:divide-yellow-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--divide-opacity));
+  }
+
+  .xl\:divide-green-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--divide-opacity));
+  }
+
+  .xl\:divide-teal-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--divide-opacity));
+  }
+
+  .xl\:divide-blue-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--divide-opacity));
+  }
+
+  .xl\:divide-indigo-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--divide-opacity));
+  }
+
+  .xl\:divide-purple-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-200 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-300 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-400 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-500 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-600 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-700 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-800 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--divide-opacity));
+  }
+
+  .xl\:divide-pink-900 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--divide-opacity));
+  }
+
+  .xl\:divide-solid > :not(template) ~ :not(template) {
+    border-style: solid;
+  }
+
+  .xl\:divide-dashed > :not(template) ~ :not(template) {
+    border-style: dashed;
+  }
+
+  .xl\:divide-dotted > :not(template) ~ :not(template) {
+    border-style: dotted;
+  }
+
+  .xl\:divide-double > :not(template) ~ :not(template) {
+    border-style: double;
+  }
+
+  .xl\:divide-none > :not(template) ~ :not(template) {
+    border-style: none;
+  }
+
+  .xl\:divide-opacity-0 > :not(template) ~ :not(template) {
+    --divide-opacity: 0;
+  }
+
+  .xl\:divide-opacity-25 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.25;
+  }
+
+  .xl\:divide-opacity-50 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.5;
+  }
+
+  .xl\:divide-opacity-75 > :not(template) ~ :not(template) {
+    --divide-opacity: 0.75;
+  }
+
+  .xl\:divide-opacity-100 > :not(template) ~ :not(template) {
+    --divide-opacity: 1;
+  }
+
+  .xl\:sr-only {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .xl\:not-sr-only {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .xl\:focus\:sr-only:focus {
+    position: absolute;
+    width: 1px;
+    height: 1px;
+    padding: 0;
+    margin: -1px;
+    overflow: hidden;
+    clip: rect(0, 0, 0, 0);
+    white-space: nowrap;
+    border-width: 0;
+  }
+
+  .xl\:focus\:not-sr-only:focus {
+    position: static;
+    width: auto;
+    height: auto;
+    padding: 0;
+    margin: 0;
+    overflow: visible;
+    clip: auto;
+    white-space: normal;
+  }
+
+  .xl\:appearance-none {
+    -webkit-appearance: none;
+       -moz-appearance: none;
+            appearance: none;
+  }
+
+  .xl\:bg-fixed {
+    background-attachment: fixed;
+  }
+
+  .xl\:bg-local {
+    background-attachment: local;
+  }
+
+  .xl\:bg-scroll {
+    background-attachment: scroll;
+  }
+
+  .xl\:bg-clip-border {
+    background-clip: border-box;
+  }
+
+  .xl\:bg-clip-padding {
+    background-clip: padding-box;
+  }
+
+  .xl\:bg-clip-content {
+    background-clip: content-box;
+  }
+
+  .xl\:bg-clip-text {
+    -webkit-background-clip: text;
+            background-clip: text;
+  }
+
+  .xl\:bg-transparent {
+    background-color: transparent;
+  }
+
+  .xl\:bg-current {
+    background-color: currentColor;
+  }
+
+  .xl\:bg-black {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .xl\:bg-white {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-100 {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-200 {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-300 {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-400 {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-500 {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-600 {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-700 {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-800 {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .xl\:bg-gray-900 {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-200 {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-300 {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-400 {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-500 {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-600 {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-700 {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-800 {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .xl\:bg-red-900 {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-100 {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-200 {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-300 {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-400 {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-500 {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-600 {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-700 {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-800 {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .xl\:bg-orange-900 {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-100 {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-200 {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-300 {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-400 {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-500 {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-600 {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-700 {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-800 {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .xl\:bg-yellow-900 {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-100 {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-200 {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-300 {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-400 {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-500 {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-600 {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-700 {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-800 {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .xl\:bg-green-900 {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-100 {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-200 {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-300 {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-400 {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-500 {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-600 {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-700 {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-800 {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .xl\:bg-teal-900 {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-100 {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-200 {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-300 {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-400 {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-500 {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-600 {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-700 {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-800 {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .xl\:bg-blue-900 {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-100 {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-200 {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-300 {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-400 {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-500 {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-600 {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-700 {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-800 {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .xl\:bg-indigo-900 {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-100 {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-200 {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-300 {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-400 {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-500 {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-600 {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-700 {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-800 {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .xl\:bg-purple-900 {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-100 {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-200 {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-300 {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-400 {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-500 {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-600 {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-700 {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-800 {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .xl\:bg-pink-900 {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-transparent:hover {
+    background-color: transparent;
+  }
+
+  .xl\:hover\:bg-current:hover {
+    background-color: currentColor;
+  }
+
+  .xl\:hover\:bg-black:hover {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-white:hover {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-100:hover {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-200:hover {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-300:hover {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-400:hover {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-500:hover {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-600:hover {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-700:hover {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-800:hover {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-gray-900:hover {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-300:hover {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-400:hover {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-500:hover {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-600:hover {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-700:hover {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-800:hover {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-red-900:hover {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-200:hover {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-600:hover {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-700:hover {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-800:hover {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-orange-900:hover {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-100:hover {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-200:hover {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-300:hover {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-400:hover {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-500:hover {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-600:hover {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-700:hover {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-800:hover {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-yellow-900:hover {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-100:hover {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-200:hover {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-300:hover {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-400:hover {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-500:hover {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-600:hover {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-700:hover {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-800:hover {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-green-900:hover {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-100:hover {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-200:hover {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-300:hover {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-400:hover {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-500:hover {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-600:hover {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-700:hover {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-800:hover {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-teal-900:hover {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-200:hover {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-300:hover {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-400:hover {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-500:hover {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-600:hover {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-700:hover {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-800:hover {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-blue-900:hover {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-100:hover {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-200:hover {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-300:hover {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-400:hover {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-500:hover {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-600:hover {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-700:hover {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-800:hover {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-indigo-900:hover {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-100:hover {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-200:hover {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-300:hover {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-400:hover {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-500:hover {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-600:hover {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-700:hover {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-800:hover {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-purple-900:hover {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-100:hover {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-200:hover {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-300:hover {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-400:hover {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-500:hover {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-600:hover {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-700:hover {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-800:hover {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .xl\:hover\:bg-pink-900:hover {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-transparent:focus {
+    background-color: transparent;
+  }
+
+  .xl\:focus\:bg-current:focus {
+    background-color: currentColor;
+  }
+
+  .xl\:focus\:bg-black:focus {
+    --bg-opacity: 1;
+    background-color: #000;
+    background-color: rgba(0, 0, 0, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-white:focus {
+    --bg-opacity: 1;
+    background-color: #fff;
+    background-color: rgba(255, 255, 255, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-100:focus {
+    --bg-opacity: 1;
+    background-color: #f7fafc;
+    background-color: rgba(247, 250, 252, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-200:focus {
+    --bg-opacity: 1;
+    background-color: #edf2f7;
+    background-color: rgba(237, 242, 247, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-300:focus {
+    --bg-opacity: 1;
+    background-color: #e2e8f0;
+    background-color: rgba(226, 232, 240, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-400:focus {
+    --bg-opacity: 1;
+    background-color: #cbd5e0;
+    background-color: rgba(203, 213, 224, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-500:focus {
+    --bg-opacity: 1;
+    background-color: #a0aec0;
+    background-color: rgba(160, 174, 192, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-600:focus {
+    --bg-opacity: 1;
+    background-color: #718096;
+    background-color: rgba(113, 128, 150, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-700:focus {
+    --bg-opacity: 1;
+    background-color: #4a5568;
+    background-color: rgba(74, 85, 104, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-800:focus {
+    --bg-opacity: 1;
+    background-color: #2d3748;
+    background-color: rgba(45, 55, 72, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-gray-900:focus {
+    --bg-opacity: 1;
+    background-color: #1a202c;
+    background-color: rgba(26, 32, 44, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f5;
+    background-color: rgba(255, 245, 245, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7d7;
+    background-color: rgba(254, 215, 215, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-300:focus {
+    --bg-opacity: 1;
+    background-color: #feb2b2;
+    background-color: rgba(254, 178, 178, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-400:focus {
+    --bg-opacity: 1;
+    background-color: #fc8181;
+    background-color: rgba(252, 129, 129, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-500:focus {
+    --bg-opacity: 1;
+    background-color: #f56565;
+    background-color: rgba(245, 101, 101, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-600:focus {
+    --bg-opacity: 1;
+    background-color: #e53e3e;
+    background-color: rgba(229, 62, 62, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-700:focus {
+    --bg-opacity: 1;
+    background-color: #c53030;
+    background-color: rgba(197, 48, 48, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-800:focus {
+    --bg-opacity: 1;
+    background-color: #9b2c2c;
+    background-color: rgba(155, 44, 44, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-red-900:focus {
+    --bg-opacity: 1;
+    background-color: #742a2a;
+    background-color: rgba(116, 42, 42, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffaf0;
+    background-color: rgba(255, 250, 240, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-200:focus {
+    --bg-opacity: 1;
+    background-color: #feebc8;
+    background-color: rgba(254, 235, 200, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbd38d;
+    background-color: rgba(251, 211, 141, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6ad55;
+    background-color: rgba(246, 173, 85, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed8936;
+    background-color: rgba(237, 137, 54, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-600:focus {
+    --bg-opacity: 1;
+    background-color: #dd6b20;
+    background-color: rgba(221, 107, 32, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-700:focus {
+    --bg-opacity: 1;
+    background-color: #c05621;
+    background-color: rgba(192, 86, 33, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-800:focus {
+    --bg-opacity: 1;
+    background-color: #9c4221;
+    background-color: rgba(156, 66, 33, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-orange-900:focus {
+    --bg-opacity: 1;
+    background-color: #7b341e;
+    background-color: rgba(123, 52, 30, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-100:focus {
+    --bg-opacity: 1;
+    background-color: #fffff0;
+    background-color: rgba(255, 255, 240, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-200:focus {
+    --bg-opacity: 1;
+    background-color: #fefcbf;
+    background-color: rgba(254, 252, 191, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-300:focus {
+    --bg-opacity: 1;
+    background-color: #faf089;
+    background-color: rgba(250, 240, 137, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-400:focus {
+    --bg-opacity: 1;
+    background-color: #f6e05e;
+    background-color: rgba(246, 224, 94, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-500:focus {
+    --bg-opacity: 1;
+    background-color: #ecc94b;
+    background-color: rgba(236, 201, 75, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-600:focus {
+    --bg-opacity: 1;
+    background-color: #d69e2e;
+    background-color: rgba(214, 158, 46, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-700:focus {
+    --bg-opacity: 1;
+    background-color: #b7791f;
+    background-color: rgba(183, 121, 31, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-800:focus {
+    --bg-opacity: 1;
+    background-color: #975a16;
+    background-color: rgba(151, 90, 22, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-yellow-900:focus {
+    --bg-opacity: 1;
+    background-color: #744210;
+    background-color: rgba(116, 66, 16, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-100:focus {
+    --bg-opacity: 1;
+    background-color: #f0fff4;
+    background-color: rgba(240, 255, 244, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-200:focus {
+    --bg-opacity: 1;
+    background-color: #c6f6d5;
+    background-color: rgba(198, 246, 213, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-300:focus {
+    --bg-opacity: 1;
+    background-color: #9ae6b4;
+    background-color: rgba(154, 230, 180, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-400:focus {
+    --bg-opacity: 1;
+    background-color: #68d391;
+    background-color: rgba(104, 211, 145, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-500:focus {
+    --bg-opacity: 1;
+    background-color: #48bb78;
+    background-color: rgba(72, 187, 120, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-600:focus {
+    --bg-opacity: 1;
+    background-color: #38a169;
+    background-color: rgba(56, 161, 105, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-700:focus {
+    --bg-opacity: 1;
+    background-color: #2f855a;
+    background-color: rgba(47, 133, 90, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-800:focus {
+    --bg-opacity: 1;
+    background-color: #276749;
+    background-color: rgba(39, 103, 73, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-green-900:focus {
+    --bg-opacity: 1;
+    background-color: #22543d;
+    background-color: rgba(34, 84, 61, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-100:focus {
+    --bg-opacity: 1;
+    background-color: #e6fffa;
+    background-color: rgba(230, 255, 250, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-200:focus {
+    --bg-opacity: 1;
+    background-color: #b2f5ea;
+    background-color: rgba(178, 245, 234, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-300:focus {
+    --bg-opacity: 1;
+    background-color: #81e6d9;
+    background-color: rgba(129, 230, 217, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-400:focus {
+    --bg-opacity: 1;
+    background-color: #4fd1c5;
+    background-color: rgba(79, 209, 197, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-500:focus {
+    --bg-opacity: 1;
+    background-color: #38b2ac;
+    background-color: rgba(56, 178, 172, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-600:focus {
+    --bg-opacity: 1;
+    background-color: #319795;
+    background-color: rgba(49, 151, 149, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-700:focus {
+    --bg-opacity: 1;
+    background-color: #2c7a7b;
+    background-color: rgba(44, 122, 123, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-800:focus {
+    --bg-opacity: 1;
+    background-color: #285e61;
+    background-color: rgba(40, 94, 97, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-teal-900:focus {
+    --bg-opacity: 1;
+    background-color: #234e52;
+    background-color: rgba(35, 78, 82, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf8ff;
+    background-color: rgba(235, 248, 255, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-200:focus {
+    --bg-opacity: 1;
+    background-color: #bee3f8;
+    background-color: rgba(190, 227, 248, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-300:focus {
+    --bg-opacity: 1;
+    background-color: #90cdf4;
+    background-color: rgba(144, 205, 244, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-400:focus {
+    --bg-opacity: 1;
+    background-color: #63b3ed;
+    background-color: rgba(99, 179, 237, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-500:focus {
+    --bg-opacity: 1;
+    background-color: #4299e1;
+    background-color: rgba(66, 153, 225, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-600:focus {
+    --bg-opacity: 1;
+    background-color: #3182ce;
+    background-color: rgba(49, 130, 206, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-700:focus {
+    --bg-opacity: 1;
+    background-color: #2b6cb0;
+    background-color: rgba(43, 108, 176, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-800:focus {
+    --bg-opacity: 1;
+    background-color: #2c5282;
+    background-color: rgba(44, 82, 130, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-blue-900:focus {
+    --bg-opacity: 1;
+    background-color: #2a4365;
+    background-color: rgba(42, 67, 101, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-100:focus {
+    --bg-opacity: 1;
+    background-color: #ebf4ff;
+    background-color: rgba(235, 244, 255, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-200:focus {
+    --bg-opacity: 1;
+    background-color: #c3dafe;
+    background-color: rgba(195, 218, 254, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-300:focus {
+    --bg-opacity: 1;
+    background-color: #a3bffa;
+    background-color: rgba(163, 191, 250, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-400:focus {
+    --bg-opacity: 1;
+    background-color: #7f9cf5;
+    background-color: rgba(127, 156, 245, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-500:focus {
+    --bg-opacity: 1;
+    background-color: #667eea;
+    background-color: rgba(102, 126, 234, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-600:focus {
+    --bg-opacity: 1;
+    background-color: #5a67d8;
+    background-color: rgba(90, 103, 216, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-700:focus {
+    --bg-opacity: 1;
+    background-color: #4c51bf;
+    background-color: rgba(76, 81, 191, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-800:focus {
+    --bg-opacity: 1;
+    background-color: #434190;
+    background-color: rgba(67, 65, 144, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-indigo-900:focus {
+    --bg-opacity: 1;
+    background-color: #3c366b;
+    background-color: rgba(60, 54, 107, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-100:focus {
+    --bg-opacity: 1;
+    background-color: #faf5ff;
+    background-color: rgba(250, 245, 255, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-200:focus {
+    --bg-opacity: 1;
+    background-color: #e9d8fd;
+    background-color: rgba(233, 216, 253, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-300:focus {
+    --bg-opacity: 1;
+    background-color: #d6bcfa;
+    background-color: rgba(214, 188, 250, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-400:focus {
+    --bg-opacity: 1;
+    background-color: #b794f4;
+    background-color: rgba(183, 148, 244, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-500:focus {
+    --bg-opacity: 1;
+    background-color: #9f7aea;
+    background-color: rgba(159, 122, 234, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-600:focus {
+    --bg-opacity: 1;
+    background-color: #805ad5;
+    background-color: rgba(128, 90, 213, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-700:focus {
+    --bg-opacity: 1;
+    background-color: #6b46c1;
+    background-color: rgba(107, 70, 193, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-800:focus {
+    --bg-opacity: 1;
+    background-color: #553c9a;
+    background-color: rgba(85, 60, 154, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-purple-900:focus {
+    --bg-opacity: 1;
+    background-color: #44337a;
+    background-color: rgba(68, 51, 122, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-100:focus {
+    --bg-opacity: 1;
+    background-color: #fff5f7;
+    background-color: rgba(255, 245, 247, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-200:focus {
+    --bg-opacity: 1;
+    background-color: #fed7e2;
+    background-color: rgba(254, 215, 226, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-300:focus {
+    --bg-opacity: 1;
+    background-color: #fbb6ce;
+    background-color: rgba(251, 182, 206, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-400:focus {
+    --bg-opacity: 1;
+    background-color: #f687b3;
+    background-color: rgba(246, 135, 179, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-500:focus {
+    --bg-opacity: 1;
+    background-color: #ed64a6;
+    background-color: rgba(237, 100, 166, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-600:focus {
+    --bg-opacity: 1;
+    background-color: #d53f8c;
+    background-color: rgba(213, 63, 140, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-700:focus {
+    --bg-opacity: 1;
+    background-color: #b83280;
+    background-color: rgba(184, 50, 128, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-800:focus {
+    --bg-opacity: 1;
+    background-color: #97266d;
+    background-color: rgba(151, 38, 109, var(--bg-opacity));
+  }
+
+  .xl\:focus\:bg-pink-900:focus {
+    --bg-opacity: 1;
+    background-color: #702459;
+    background-color: rgba(112, 36, 89, var(--bg-opacity));
+  }
+
+  .xl\:bg-none {
+    background-image: none;
+  }
+
+  .xl\:bg-gradient-to-t {
+    background-image: linear-gradient(to top, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-tr {
+    background-image: linear-gradient(to top right, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-r {
+    background-image: linear-gradient(to right, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-br {
+    background-image: linear-gradient(to bottom right, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-b {
+    background-image: linear-gradient(to bottom, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-bl {
+    background-image: linear-gradient(to bottom left, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-l {
+    background-image: linear-gradient(to left, var(--gradient-color-stops));
+  }
+
+  .xl\:bg-gradient-to-tl {
+    background-image: linear-gradient(to top left, var(--gradient-color-stops));
+  }
+
+  .xl\:from-transparent {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:from-current {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:from-black {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:from-white {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:from-gray-100 {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:from-gray-200 {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:from-gray-300 {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:from-gray-400 {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:from-gray-500 {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:from-gray-600 {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:from-gray-700 {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:from-gray-800 {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:from-gray-900 {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:from-red-100 {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:from-red-200 {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:from-red-300 {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:from-red-400 {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:from-red-500 {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:from-red-600 {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:from-red-700 {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:from-red-800 {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:from-red-900 {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:from-orange-100 {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:from-orange-200 {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:from-orange-300 {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:from-orange-400 {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:from-orange-500 {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:from-orange-600 {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:from-orange-700 {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:from-orange-800 {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:from-orange-900 {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:from-yellow-100 {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:from-yellow-200 {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:from-yellow-300 {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:from-yellow-400 {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:from-yellow-500 {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:from-yellow-600 {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:from-yellow-700 {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:from-yellow-800 {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:from-yellow-900 {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:from-green-100 {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:from-green-200 {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:from-green-300 {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:from-green-400 {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:from-green-500 {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:from-green-600 {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:from-green-700 {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:from-green-800 {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:from-green-900 {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:from-teal-100 {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:from-teal-200 {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:from-teal-300 {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:from-teal-400 {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:from-teal-500 {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:from-teal-600 {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:from-teal-700 {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:from-teal-800 {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:from-teal-900 {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:from-blue-100 {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:from-blue-200 {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:from-blue-300 {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:from-blue-400 {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:from-blue-500 {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:from-blue-600 {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:from-blue-700 {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:from-blue-800 {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:from-blue-900 {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:from-indigo-100 {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:from-indigo-200 {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:from-indigo-300 {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:from-indigo-400 {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:from-indigo-500 {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:from-indigo-600 {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:from-indigo-700 {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:from-indigo-800 {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:from-indigo-900 {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:from-purple-100 {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:from-purple-200 {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:from-purple-300 {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:from-purple-400 {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:from-purple-500 {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:from-purple-600 {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:from-purple-700 {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:from-purple-800 {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:from-purple-900 {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:from-pink-100 {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:from-pink-200 {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:from-pink-300 {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:from-pink-400 {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:from-pink-500 {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:from-pink-600 {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:from-pink-700 {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:from-pink-800 {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:from-pink-900 {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:via-transparent {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:via-current {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:via-black {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:via-white {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:via-gray-100 {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:via-gray-200 {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:via-gray-300 {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:via-gray-400 {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:via-gray-500 {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:via-gray-600 {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:via-gray-700 {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:via-gray-800 {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:via-gray-900 {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:via-red-100 {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:via-red-200 {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:via-red-300 {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:via-red-400 {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:via-red-500 {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:via-red-600 {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:via-red-700 {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:via-red-800 {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:via-red-900 {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:via-orange-100 {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:via-orange-200 {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:via-orange-300 {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:via-orange-400 {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:via-orange-500 {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:via-orange-600 {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:via-orange-700 {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:via-orange-800 {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:via-orange-900 {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:via-yellow-100 {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:via-yellow-200 {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:via-yellow-300 {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:via-yellow-400 {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:via-yellow-500 {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:via-yellow-600 {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:via-yellow-700 {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:via-yellow-800 {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:via-yellow-900 {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:via-green-100 {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:via-green-200 {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:via-green-300 {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:via-green-400 {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:via-green-500 {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:via-green-600 {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:via-green-700 {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:via-green-800 {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:via-green-900 {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:via-teal-100 {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:via-teal-200 {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:via-teal-300 {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:via-teal-400 {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:via-teal-500 {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:via-teal-600 {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:via-teal-700 {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:via-teal-800 {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:via-teal-900 {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:via-blue-100 {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:via-blue-200 {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:via-blue-300 {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:via-blue-400 {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:via-blue-500 {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:via-blue-600 {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:via-blue-700 {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:via-blue-800 {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:via-blue-900 {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:via-indigo-100 {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:via-indigo-200 {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:via-indigo-300 {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:via-indigo-400 {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:via-indigo-500 {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:via-indigo-600 {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:via-indigo-700 {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:via-indigo-800 {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:via-indigo-900 {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:via-purple-100 {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:via-purple-200 {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:via-purple-300 {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:via-purple-400 {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:via-purple-500 {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:via-purple-600 {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:via-purple-700 {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:via-purple-800 {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:via-purple-900 {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:via-pink-100 {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:via-pink-200 {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:via-pink-300 {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:via-pink-400 {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:via-pink-500 {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:via-pink-600 {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:via-pink-700 {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:via-pink-800 {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:via-pink-900 {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:to-transparent {
+    --gradient-to-color: transparent;
+  }
+
+  .xl\:to-current {
+    --gradient-to-color: currentColor;
+  }
+
+  .xl\:to-black {
+    --gradient-to-color: #000;
+  }
+
+  .xl\:to-white {
+    --gradient-to-color: #fff;
+  }
+
+  .xl\:to-gray-100 {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .xl\:to-gray-200 {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .xl\:to-gray-300 {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .xl\:to-gray-400 {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .xl\:to-gray-500 {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .xl\:to-gray-600 {
+    --gradient-to-color: #718096;
+  }
+
+  .xl\:to-gray-700 {
+    --gradient-to-color: #4a5568;
+  }
+
+  .xl\:to-gray-800 {
+    --gradient-to-color: #2d3748;
+  }
+
+  .xl\:to-gray-900 {
+    --gradient-to-color: #1a202c;
+  }
+
+  .xl\:to-red-100 {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .xl\:to-red-200 {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .xl\:to-red-300 {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .xl\:to-red-400 {
+    --gradient-to-color: #fc8181;
+  }
+
+  .xl\:to-red-500 {
+    --gradient-to-color: #f56565;
+  }
+
+  .xl\:to-red-600 {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .xl\:to-red-700 {
+    --gradient-to-color: #c53030;
+  }
+
+  .xl\:to-red-800 {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .xl\:to-red-900 {
+    --gradient-to-color: #742a2a;
+  }
+
+  .xl\:to-orange-100 {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .xl\:to-orange-200 {
+    --gradient-to-color: #feebc8;
+  }
+
+  .xl\:to-orange-300 {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .xl\:to-orange-400 {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .xl\:to-orange-500 {
+    --gradient-to-color: #ed8936;
+  }
+
+  .xl\:to-orange-600 {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .xl\:to-orange-700 {
+    --gradient-to-color: #c05621;
+  }
+
+  .xl\:to-orange-800 {
+    --gradient-to-color: #9c4221;
+  }
+
+  .xl\:to-orange-900 {
+    --gradient-to-color: #7b341e;
+  }
+
+  .xl\:to-yellow-100 {
+    --gradient-to-color: #fffff0;
+  }
+
+  .xl\:to-yellow-200 {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .xl\:to-yellow-300 {
+    --gradient-to-color: #faf089;
+  }
+
+  .xl\:to-yellow-400 {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .xl\:to-yellow-500 {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .xl\:to-yellow-600 {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .xl\:to-yellow-700 {
+    --gradient-to-color: #b7791f;
+  }
+
+  .xl\:to-yellow-800 {
+    --gradient-to-color: #975a16;
+  }
+
+  .xl\:to-yellow-900 {
+    --gradient-to-color: #744210;
+  }
+
+  .xl\:to-green-100 {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .xl\:to-green-200 {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .xl\:to-green-300 {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .xl\:to-green-400 {
+    --gradient-to-color: #68d391;
+  }
+
+  .xl\:to-green-500 {
+    --gradient-to-color: #48bb78;
+  }
+
+  .xl\:to-green-600 {
+    --gradient-to-color: #38a169;
+  }
+
+  .xl\:to-green-700 {
+    --gradient-to-color: #2f855a;
+  }
+
+  .xl\:to-green-800 {
+    --gradient-to-color: #276749;
+  }
+
+  .xl\:to-green-900 {
+    --gradient-to-color: #22543d;
+  }
+
+  .xl\:to-teal-100 {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .xl\:to-teal-200 {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .xl\:to-teal-300 {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .xl\:to-teal-400 {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .xl\:to-teal-500 {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .xl\:to-teal-600 {
+    --gradient-to-color: #319795;
+  }
+
+  .xl\:to-teal-700 {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .xl\:to-teal-800 {
+    --gradient-to-color: #285e61;
+  }
+
+  .xl\:to-teal-900 {
+    --gradient-to-color: #234e52;
+  }
+
+  .xl\:to-blue-100 {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .xl\:to-blue-200 {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .xl\:to-blue-300 {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .xl\:to-blue-400 {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .xl\:to-blue-500 {
+    --gradient-to-color: #4299e1;
+  }
+
+  .xl\:to-blue-600 {
+    --gradient-to-color: #3182ce;
+  }
+
+  .xl\:to-blue-700 {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .xl\:to-blue-800 {
+    --gradient-to-color: #2c5282;
+  }
+
+  .xl\:to-blue-900 {
+    --gradient-to-color: #2a4365;
+  }
+
+  .xl\:to-indigo-100 {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .xl\:to-indigo-200 {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .xl\:to-indigo-300 {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .xl\:to-indigo-400 {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .xl\:to-indigo-500 {
+    --gradient-to-color: #667eea;
+  }
+
+  .xl\:to-indigo-600 {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .xl\:to-indigo-700 {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .xl\:to-indigo-800 {
+    --gradient-to-color: #434190;
+  }
+
+  .xl\:to-indigo-900 {
+    --gradient-to-color: #3c366b;
+  }
+
+  .xl\:to-purple-100 {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .xl\:to-purple-200 {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .xl\:to-purple-300 {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .xl\:to-purple-400 {
+    --gradient-to-color: #b794f4;
+  }
+
+  .xl\:to-purple-500 {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .xl\:to-purple-600 {
+    --gradient-to-color: #805ad5;
+  }
+
+  .xl\:to-purple-700 {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .xl\:to-purple-800 {
+    --gradient-to-color: #553c9a;
+  }
+
+  .xl\:to-purple-900 {
+    --gradient-to-color: #44337a;
+  }
+
+  .xl\:to-pink-100 {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .xl\:to-pink-200 {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .xl\:to-pink-300 {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .xl\:to-pink-400 {
+    --gradient-to-color: #f687b3;
+  }
+
+  .xl\:to-pink-500 {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .xl\:to-pink-600 {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .xl\:to-pink-700 {
+    --gradient-to-color: #b83280;
+  }
+
+  .xl\:to-pink-800 {
+    --gradient-to-color: #97266d;
+  }
+
+  .xl\:to-pink-900 {
+    --gradient-to-color: #702459;
+  }
+
+  .xl\:hover\:from-transparent:hover {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:hover\:from-current:hover {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:hover\:from-black:hover {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:hover\:from-white:hover {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:hover\:from-gray-100:hover {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:hover\:from-gray-200:hover {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:hover\:from-gray-300:hover {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:hover\:from-gray-400:hover {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:hover\:from-gray-500:hover {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:hover\:from-gray-600:hover {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:hover\:from-gray-700:hover {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:hover\:from-gray-800:hover {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:hover\:from-gray-900:hover {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:hover\:from-red-100:hover {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:hover\:from-red-200:hover {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:hover\:from-red-300:hover {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:hover\:from-red-400:hover {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:hover\:from-red-500:hover {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:hover\:from-red-600:hover {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:hover\:from-red-700:hover {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:hover\:from-red-800:hover {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:hover\:from-red-900:hover {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:hover\:from-orange-100:hover {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:hover\:from-orange-200:hover {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:hover\:from-orange-300:hover {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:hover\:from-orange-400:hover {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:hover\:from-orange-500:hover {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:hover\:from-orange-600:hover {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:hover\:from-orange-700:hover {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:hover\:from-orange-800:hover {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:hover\:from-orange-900:hover {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:hover\:from-yellow-100:hover {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:hover\:from-yellow-200:hover {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:hover\:from-yellow-300:hover {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:hover\:from-yellow-400:hover {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:hover\:from-yellow-500:hover {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:hover\:from-yellow-600:hover {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:hover\:from-yellow-700:hover {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:hover\:from-yellow-800:hover {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:hover\:from-yellow-900:hover {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:hover\:from-green-100:hover {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:hover\:from-green-200:hover {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:hover\:from-green-300:hover {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:hover\:from-green-400:hover {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:hover\:from-green-500:hover {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:hover\:from-green-600:hover {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:hover\:from-green-700:hover {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:hover\:from-green-800:hover {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:hover\:from-green-900:hover {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:hover\:from-teal-100:hover {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:hover\:from-teal-200:hover {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:hover\:from-teal-300:hover {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:hover\:from-teal-400:hover {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:hover\:from-teal-500:hover {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:hover\:from-teal-600:hover {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:hover\:from-teal-700:hover {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:hover\:from-teal-800:hover {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:hover\:from-teal-900:hover {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:hover\:from-blue-100:hover {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:hover\:from-blue-200:hover {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:hover\:from-blue-300:hover {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:hover\:from-blue-400:hover {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:hover\:from-blue-500:hover {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:hover\:from-blue-600:hover {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:hover\:from-blue-700:hover {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:hover\:from-blue-800:hover {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:hover\:from-blue-900:hover {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:hover\:from-indigo-100:hover {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:hover\:from-indigo-200:hover {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:hover\:from-indigo-300:hover {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:hover\:from-indigo-400:hover {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:hover\:from-indigo-500:hover {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:hover\:from-indigo-600:hover {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:hover\:from-indigo-700:hover {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:hover\:from-indigo-800:hover {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:hover\:from-indigo-900:hover {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:hover\:from-purple-100:hover {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:hover\:from-purple-200:hover {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:hover\:from-purple-300:hover {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:hover\:from-purple-400:hover {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:hover\:from-purple-500:hover {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:hover\:from-purple-600:hover {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:hover\:from-purple-700:hover {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:hover\:from-purple-800:hover {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:hover\:from-purple-900:hover {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:hover\:from-pink-100:hover {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:hover\:from-pink-200:hover {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:hover\:from-pink-300:hover {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:hover\:from-pink-400:hover {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:hover\:from-pink-500:hover {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:hover\:from-pink-600:hover {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:hover\:from-pink-700:hover {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:hover\:from-pink-800:hover {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:hover\:from-pink-900:hover {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:hover\:via-transparent:hover {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:hover\:via-current:hover {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:hover\:via-black:hover {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:hover\:via-white:hover {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:hover\:via-gray-100:hover {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:hover\:via-gray-200:hover {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:hover\:via-gray-300:hover {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:hover\:via-gray-400:hover {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:hover\:via-gray-500:hover {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:hover\:via-gray-600:hover {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:hover\:via-gray-700:hover {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:hover\:via-gray-800:hover {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:hover\:via-gray-900:hover {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:hover\:via-red-100:hover {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:hover\:via-red-200:hover {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:hover\:via-red-300:hover {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:hover\:via-red-400:hover {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:hover\:via-red-500:hover {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:hover\:via-red-600:hover {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:hover\:via-red-700:hover {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:hover\:via-red-800:hover {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:hover\:via-red-900:hover {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:hover\:via-orange-100:hover {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:hover\:via-orange-200:hover {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:hover\:via-orange-300:hover {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:hover\:via-orange-400:hover {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:hover\:via-orange-500:hover {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:hover\:via-orange-600:hover {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:hover\:via-orange-700:hover {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:hover\:via-orange-800:hover {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:hover\:via-orange-900:hover {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:hover\:via-yellow-100:hover {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:hover\:via-yellow-200:hover {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:hover\:via-yellow-300:hover {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:hover\:via-yellow-400:hover {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:hover\:via-yellow-500:hover {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:hover\:via-yellow-600:hover {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:hover\:via-yellow-700:hover {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:hover\:via-yellow-800:hover {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:hover\:via-yellow-900:hover {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:hover\:via-green-100:hover {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:hover\:via-green-200:hover {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:hover\:via-green-300:hover {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:hover\:via-green-400:hover {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:hover\:via-green-500:hover {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:hover\:via-green-600:hover {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:hover\:via-green-700:hover {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:hover\:via-green-800:hover {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:hover\:via-green-900:hover {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:hover\:via-teal-100:hover {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:hover\:via-teal-200:hover {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:hover\:via-teal-300:hover {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:hover\:via-teal-400:hover {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:hover\:via-teal-500:hover {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:hover\:via-teal-600:hover {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:hover\:via-teal-700:hover {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:hover\:via-teal-800:hover {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:hover\:via-teal-900:hover {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:hover\:via-blue-100:hover {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:hover\:via-blue-200:hover {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:hover\:via-blue-300:hover {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:hover\:via-blue-400:hover {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:hover\:via-blue-500:hover {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:hover\:via-blue-600:hover {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:hover\:via-blue-700:hover {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:hover\:via-blue-800:hover {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:hover\:via-blue-900:hover {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:hover\:via-indigo-100:hover {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:hover\:via-indigo-200:hover {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:hover\:via-indigo-300:hover {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:hover\:via-indigo-400:hover {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:hover\:via-indigo-500:hover {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:hover\:via-indigo-600:hover {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:hover\:via-indigo-700:hover {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:hover\:via-indigo-800:hover {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:hover\:via-indigo-900:hover {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:hover\:via-purple-100:hover {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:hover\:via-purple-200:hover {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:hover\:via-purple-300:hover {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:hover\:via-purple-400:hover {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:hover\:via-purple-500:hover {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:hover\:via-purple-600:hover {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:hover\:via-purple-700:hover {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:hover\:via-purple-800:hover {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:hover\:via-purple-900:hover {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:hover\:via-pink-100:hover {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:hover\:via-pink-200:hover {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:hover\:via-pink-300:hover {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:hover\:via-pink-400:hover {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:hover\:via-pink-500:hover {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:hover\:via-pink-600:hover {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:hover\:via-pink-700:hover {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:hover\:via-pink-800:hover {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:hover\:via-pink-900:hover {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:hover\:to-transparent:hover {
+    --gradient-to-color: transparent;
+  }
+
+  .xl\:hover\:to-current:hover {
+    --gradient-to-color: currentColor;
+  }
+
+  .xl\:hover\:to-black:hover {
+    --gradient-to-color: #000;
+  }
+
+  .xl\:hover\:to-white:hover {
+    --gradient-to-color: #fff;
+  }
+
+  .xl\:hover\:to-gray-100:hover {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .xl\:hover\:to-gray-200:hover {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .xl\:hover\:to-gray-300:hover {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .xl\:hover\:to-gray-400:hover {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .xl\:hover\:to-gray-500:hover {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .xl\:hover\:to-gray-600:hover {
+    --gradient-to-color: #718096;
+  }
+
+  .xl\:hover\:to-gray-700:hover {
+    --gradient-to-color: #4a5568;
+  }
+
+  .xl\:hover\:to-gray-800:hover {
+    --gradient-to-color: #2d3748;
+  }
+
+  .xl\:hover\:to-gray-900:hover {
+    --gradient-to-color: #1a202c;
+  }
+
+  .xl\:hover\:to-red-100:hover {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .xl\:hover\:to-red-200:hover {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .xl\:hover\:to-red-300:hover {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .xl\:hover\:to-red-400:hover {
+    --gradient-to-color: #fc8181;
+  }
+
+  .xl\:hover\:to-red-500:hover {
+    --gradient-to-color: #f56565;
+  }
+
+  .xl\:hover\:to-red-600:hover {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .xl\:hover\:to-red-700:hover {
+    --gradient-to-color: #c53030;
+  }
+
+  .xl\:hover\:to-red-800:hover {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .xl\:hover\:to-red-900:hover {
+    --gradient-to-color: #742a2a;
+  }
+
+  .xl\:hover\:to-orange-100:hover {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .xl\:hover\:to-orange-200:hover {
+    --gradient-to-color: #feebc8;
+  }
+
+  .xl\:hover\:to-orange-300:hover {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .xl\:hover\:to-orange-400:hover {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .xl\:hover\:to-orange-500:hover {
+    --gradient-to-color: #ed8936;
+  }
+
+  .xl\:hover\:to-orange-600:hover {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .xl\:hover\:to-orange-700:hover {
+    --gradient-to-color: #c05621;
+  }
+
+  .xl\:hover\:to-orange-800:hover {
+    --gradient-to-color: #9c4221;
+  }
+
+  .xl\:hover\:to-orange-900:hover {
+    --gradient-to-color: #7b341e;
+  }
+
+  .xl\:hover\:to-yellow-100:hover {
+    --gradient-to-color: #fffff0;
+  }
+
+  .xl\:hover\:to-yellow-200:hover {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .xl\:hover\:to-yellow-300:hover {
+    --gradient-to-color: #faf089;
+  }
+
+  .xl\:hover\:to-yellow-400:hover {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .xl\:hover\:to-yellow-500:hover {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .xl\:hover\:to-yellow-600:hover {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .xl\:hover\:to-yellow-700:hover {
+    --gradient-to-color: #b7791f;
+  }
+
+  .xl\:hover\:to-yellow-800:hover {
+    --gradient-to-color: #975a16;
+  }
+
+  .xl\:hover\:to-yellow-900:hover {
+    --gradient-to-color: #744210;
+  }
+
+  .xl\:hover\:to-green-100:hover {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .xl\:hover\:to-green-200:hover {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .xl\:hover\:to-green-300:hover {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .xl\:hover\:to-green-400:hover {
+    --gradient-to-color: #68d391;
+  }
+
+  .xl\:hover\:to-green-500:hover {
+    --gradient-to-color: #48bb78;
+  }
+
+  .xl\:hover\:to-green-600:hover {
+    --gradient-to-color: #38a169;
+  }
+
+  .xl\:hover\:to-green-700:hover {
+    --gradient-to-color: #2f855a;
+  }
+
+  .xl\:hover\:to-green-800:hover {
+    --gradient-to-color: #276749;
+  }
+
+  .xl\:hover\:to-green-900:hover {
+    --gradient-to-color: #22543d;
+  }
+
+  .xl\:hover\:to-teal-100:hover {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .xl\:hover\:to-teal-200:hover {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .xl\:hover\:to-teal-300:hover {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .xl\:hover\:to-teal-400:hover {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .xl\:hover\:to-teal-500:hover {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .xl\:hover\:to-teal-600:hover {
+    --gradient-to-color: #319795;
+  }
+
+  .xl\:hover\:to-teal-700:hover {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .xl\:hover\:to-teal-800:hover {
+    --gradient-to-color: #285e61;
+  }
+
+  .xl\:hover\:to-teal-900:hover {
+    --gradient-to-color: #234e52;
+  }
+
+  .xl\:hover\:to-blue-100:hover {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .xl\:hover\:to-blue-200:hover {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .xl\:hover\:to-blue-300:hover {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .xl\:hover\:to-blue-400:hover {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .xl\:hover\:to-blue-500:hover {
+    --gradient-to-color: #4299e1;
+  }
+
+  .xl\:hover\:to-blue-600:hover {
+    --gradient-to-color: #3182ce;
+  }
+
+  .xl\:hover\:to-blue-700:hover {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .xl\:hover\:to-blue-800:hover {
+    --gradient-to-color: #2c5282;
+  }
+
+  .xl\:hover\:to-blue-900:hover {
+    --gradient-to-color: #2a4365;
+  }
+
+  .xl\:hover\:to-indigo-100:hover {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .xl\:hover\:to-indigo-200:hover {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .xl\:hover\:to-indigo-300:hover {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .xl\:hover\:to-indigo-400:hover {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .xl\:hover\:to-indigo-500:hover {
+    --gradient-to-color: #667eea;
+  }
+
+  .xl\:hover\:to-indigo-600:hover {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .xl\:hover\:to-indigo-700:hover {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .xl\:hover\:to-indigo-800:hover {
+    --gradient-to-color: #434190;
+  }
+
+  .xl\:hover\:to-indigo-900:hover {
+    --gradient-to-color: #3c366b;
+  }
+
+  .xl\:hover\:to-purple-100:hover {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .xl\:hover\:to-purple-200:hover {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .xl\:hover\:to-purple-300:hover {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .xl\:hover\:to-purple-400:hover {
+    --gradient-to-color: #b794f4;
+  }
+
+  .xl\:hover\:to-purple-500:hover {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .xl\:hover\:to-purple-600:hover {
+    --gradient-to-color: #805ad5;
+  }
+
+  .xl\:hover\:to-purple-700:hover {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .xl\:hover\:to-purple-800:hover {
+    --gradient-to-color: #553c9a;
+  }
+
+  .xl\:hover\:to-purple-900:hover {
+    --gradient-to-color: #44337a;
+  }
+
+  .xl\:hover\:to-pink-100:hover {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .xl\:hover\:to-pink-200:hover {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .xl\:hover\:to-pink-300:hover {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .xl\:hover\:to-pink-400:hover {
+    --gradient-to-color: #f687b3;
+  }
+
+  .xl\:hover\:to-pink-500:hover {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .xl\:hover\:to-pink-600:hover {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .xl\:hover\:to-pink-700:hover {
+    --gradient-to-color: #b83280;
+  }
+
+  .xl\:hover\:to-pink-800:hover {
+    --gradient-to-color: #97266d;
+  }
+
+  .xl\:hover\:to-pink-900:hover {
+    --gradient-to-color: #702459;
+  }
+
+  .xl\:focus\:from-transparent:focus {
+    --gradient-from-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:focus\:from-current:focus {
+    --gradient-from-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:focus\:from-black:focus {
+    --gradient-from-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:focus\:from-white:focus {
+    --gradient-from-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:focus\:from-gray-100:focus {
+    --gradient-from-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:focus\:from-gray-200:focus {
+    --gradient-from-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:focus\:from-gray-300:focus {
+    --gradient-from-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:focus\:from-gray-400:focus {
+    --gradient-from-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:focus\:from-gray-500:focus {
+    --gradient-from-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:focus\:from-gray-600:focus {
+    --gradient-from-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:focus\:from-gray-700:focus {
+    --gradient-from-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:focus\:from-gray-800:focus {
+    --gradient-from-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:focus\:from-gray-900:focus {
+    --gradient-from-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:focus\:from-red-100:focus {
+    --gradient-from-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:focus\:from-red-200:focus {
+    --gradient-from-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:focus\:from-red-300:focus {
+    --gradient-from-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:focus\:from-red-400:focus {
+    --gradient-from-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:focus\:from-red-500:focus {
+    --gradient-from-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:focus\:from-red-600:focus {
+    --gradient-from-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:focus\:from-red-700:focus {
+    --gradient-from-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:focus\:from-red-800:focus {
+    --gradient-from-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:focus\:from-red-900:focus {
+    --gradient-from-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:focus\:from-orange-100:focus {
+    --gradient-from-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:focus\:from-orange-200:focus {
+    --gradient-from-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:focus\:from-orange-300:focus {
+    --gradient-from-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:focus\:from-orange-400:focus {
+    --gradient-from-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:focus\:from-orange-500:focus {
+    --gradient-from-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:focus\:from-orange-600:focus {
+    --gradient-from-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:focus\:from-orange-700:focus {
+    --gradient-from-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:focus\:from-orange-800:focus {
+    --gradient-from-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:focus\:from-orange-900:focus {
+    --gradient-from-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:focus\:from-yellow-100:focus {
+    --gradient-from-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:focus\:from-yellow-200:focus {
+    --gradient-from-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:focus\:from-yellow-300:focus {
+    --gradient-from-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:focus\:from-yellow-400:focus {
+    --gradient-from-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:focus\:from-yellow-500:focus {
+    --gradient-from-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:focus\:from-yellow-600:focus {
+    --gradient-from-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:focus\:from-yellow-700:focus {
+    --gradient-from-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:focus\:from-yellow-800:focus {
+    --gradient-from-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:focus\:from-yellow-900:focus {
+    --gradient-from-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:focus\:from-green-100:focus {
+    --gradient-from-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:focus\:from-green-200:focus {
+    --gradient-from-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:focus\:from-green-300:focus {
+    --gradient-from-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:focus\:from-green-400:focus {
+    --gradient-from-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:focus\:from-green-500:focus {
+    --gradient-from-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:focus\:from-green-600:focus {
+    --gradient-from-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:focus\:from-green-700:focus {
+    --gradient-from-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:focus\:from-green-800:focus {
+    --gradient-from-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:focus\:from-green-900:focus {
+    --gradient-from-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:focus\:from-teal-100:focus {
+    --gradient-from-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:focus\:from-teal-200:focus {
+    --gradient-from-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:focus\:from-teal-300:focus {
+    --gradient-from-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:focus\:from-teal-400:focus {
+    --gradient-from-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:focus\:from-teal-500:focus {
+    --gradient-from-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:focus\:from-teal-600:focus {
+    --gradient-from-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:focus\:from-teal-700:focus {
+    --gradient-from-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:focus\:from-teal-800:focus {
+    --gradient-from-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:focus\:from-teal-900:focus {
+    --gradient-from-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:focus\:from-blue-100:focus {
+    --gradient-from-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:focus\:from-blue-200:focus {
+    --gradient-from-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:focus\:from-blue-300:focus {
+    --gradient-from-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:focus\:from-blue-400:focus {
+    --gradient-from-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:focus\:from-blue-500:focus {
+    --gradient-from-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:focus\:from-blue-600:focus {
+    --gradient-from-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:focus\:from-blue-700:focus {
+    --gradient-from-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:focus\:from-blue-800:focus {
+    --gradient-from-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:focus\:from-blue-900:focus {
+    --gradient-from-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:focus\:from-indigo-100:focus {
+    --gradient-from-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:focus\:from-indigo-200:focus {
+    --gradient-from-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:focus\:from-indigo-300:focus {
+    --gradient-from-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:focus\:from-indigo-400:focus {
+    --gradient-from-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:focus\:from-indigo-500:focus {
+    --gradient-from-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:focus\:from-indigo-600:focus {
+    --gradient-from-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:focus\:from-indigo-700:focus {
+    --gradient-from-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:focus\:from-indigo-800:focus {
+    --gradient-from-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:focus\:from-indigo-900:focus {
+    --gradient-from-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:focus\:from-purple-100:focus {
+    --gradient-from-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:focus\:from-purple-200:focus {
+    --gradient-from-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:focus\:from-purple-300:focus {
+    --gradient-from-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:focus\:from-purple-400:focus {
+    --gradient-from-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:focus\:from-purple-500:focus {
+    --gradient-from-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:focus\:from-purple-600:focus {
+    --gradient-from-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:focus\:from-purple-700:focus {
+    --gradient-from-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:focus\:from-purple-800:focus {
+    --gradient-from-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:focus\:from-purple-900:focus {
+    --gradient-from-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:focus\:from-pink-100:focus {
+    --gradient-from-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:focus\:from-pink-200:focus {
+    --gradient-from-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:focus\:from-pink-300:focus {
+    --gradient-from-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:focus\:from-pink-400:focus {
+    --gradient-from-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:focus\:from-pink-500:focus {
+    --gradient-from-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:focus\:from-pink-600:focus {
+    --gradient-from-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:focus\:from-pink-700:focus {
+    --gradient-from-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:focus\:from-pink-800:focus {
+    --gradient-from-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:focus\:from-pink-900:focus {
+    --gradient-from-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:focus\:via-transparent:focus {
+    --gradient-via-color: transparent;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:focus\:via-current:focus {
+    --gradient-via-color: currentColor;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:focus\:via-black:focus {
+    --gradient-via-color: #000;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(0, 0, 0, 0));
+  }
+
+  .xl\:focus\:via-white:focus {
+    --gradient-via-color: #fff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 255, 0));
+  }
+
+  .xl\:focus\:via-gray-100:focus {
+    --gradient-via-color: #f7fafc;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(247, 250, 252, 0));
+  }
+
+  .xl\:focus\:via-gray-200:focus {
+    --gradient-via-color: #edf2f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 242, 247, 0));
+  }
+
+  .xl\:focus\:via-gray-300:focus {
+    --gradient-via-color: #e2e8f0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(226, 232, 240, 0));
+  }
+
+  .xl\:focus\:via-gray-400:focus {
+    --gradient-via-color: #cbd5e0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(203, 213, 224, 0));
+  }
+
+  .xl\:focus\:via-gray-500:focus {
+    --gradient-via-color: #a0aec0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(160, 174, 192, 0));
+  }
+
+  .xl\:focus\:via-gray-600:focus {
+    --gradient-via-color: #718096;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(113, 128, 150, 0));
+  }
+
+  .xl\:focus\:via-gray-700:focus {
+    --gradient-via-color: #4a5568;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(74, 85, 104, 0));
+  }
+
+  .xl\:focus\:via-gray-800:focus {
+    --gradient-via-color: #2d3748;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(45, 55, 72, 0));
+  }
+
+  .xl\:focus\:via-gray-900:focus {
+    --gradient-via-color: #1a202c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(26, 32, 44, 0));
+  }
+
+  .xl\:focus\:via-red-100:focus {
+    --gradient-via-color: #fff5f5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 245, 0));
+  }
+
+  .xl\:focus\:via-red-200:focus {
+    --gradient-via-color: #fed7d7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 215, 0));
+  }
+
+  .xl\:focus\:via-red-300:focus {
+    --gradient-via-color: #feb2b2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 178, 178, 0));
+  }
+
+  .xl\:focus\:via-red-400:focus {
+    --gradient-via-color: #fc8181;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(252, 129, 129, 0));
+  }
+
+  .xl\:focus\:via-red-500:focus {
+    --gradient-via-color: #f56565;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(245, 101, 101, 0));
+  }
+
+  .xl\:focus\:via-red-600:focus {
+    --gradient-via-color: #e53e3e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(229, 62, 62, 0));
+  }
+
+  .xl\:focus\:via-red-700:focus {
+    --gradient-via-color: #c53030;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(197, 48, 48, 0));
+  }
+
+  .xl\:focus\:via-red-800:focus {
+    --gradient-via-color: #9b2c2c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(155, 44, 44, 0));
+  }
+
+  .xl\:focus\:via-red-900:focus {
+    --gradient-via-color: #742a2a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 42, 42, 0));
+  }
+
+  .xl\:focus\:via-orange-100:focus {
+    --gradient-via-color: #fffaf0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 250, 240, 0));
+  }
+
+  .xl\:focus\:via-orange-200:focus {
+    --gradient-via-color: #feebc8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 235, 200, 0));
+  }
+
+  .xl\:focus\:via-orange-300:focus {
+    --gradient-via-color: #fbd38d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 211, 141, 0));
+  }
+
+  .xl\:focus\:via-orange-400:focus {
+    --gradient-via-color: #f6ad55;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 173, 85, 0));
+  }
+
+  .xl\:focus\:via-orange-500:focus {
+    --gradient-via-color: #ed8936;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 137, 54, 0));
+  }
+
+  .xl\:focus\:via-orange-600:focus {
+    --gradient-via-color: #dd6b20;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(221, 107, 32, 0));
+  }
+
+  .xl\:focus\:via-orange-700:focus {
+    --gradient-via-color: #c05621;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(192, 86, 33, 0));
+  }
+
+  .xl\:focus\:via-orange-800:focus {
+    --gradient-via-color: #9c4221;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(156, 66, 33, 0));
+  }
+
+  .xl\:focus\:via-orange-900:focus {
+    --gradient-via-color: #7b341e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(123, 52, 30, 0));
+  }
+
+  .xl\:focus\:via-yellow-100:focus {
+    --gradient-via-color: #fffff0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 255, 240, 0));
+  }
+
+  .xl\:focus\:via-yellow-200:focus {
+    --gradient-via-color: #fefcbf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 252, 191, 0));
+  }
+
+  .xl\:focus\:via-yellow-300:focus {
+    --gradient-via-color: #faf089;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 240, 137, 0));
+  }
+
+  .xl\:focus\:via-yellow-400:focus {
+    --gradient-via-color: #f6e05e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 224, 94, 0));
+  }
+
+  .xl\:focus\:via-yellow-500:focus {
+    --gradient-via-color: #ecc94b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(236, 201, 75, 0));
+  }
+
+  .xl\:focus\:via-yellow-600:focus {
+    --gradient-via-color: #d69e2e;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 158, 46, 0));
+  }
+
+  .xl\:focus\:via-yellow-700:focus {
+    --gradient-via-color: #b7791f;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 121, 31, 0));
+  }
+
+  .xl\:focus\:via-yellow-800:focus {
+    --gradient-via-color: #975a16;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 90, 22, 0));
+  }
+
+  .xl\:focus\:via-yellow-900:focus {
+    --gradient-via-color: #744210;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(116, 66, 16, 0));
+  }
+
+  .xl\:focus\:via-green-100:focus {
+    --gradient-via-color: #f0fff4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(240, 255, 244, 0));
+  }
+
+  .xl\:focus\:via-green-200:focus {
+    --gradient-via-color: #c6f6d5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(198, 246, 213, 0));
+  }
+
+  .xl\:focus\:via-green-300:focus {
+    --gradient-via-color: #9ae6b4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(154, 230, 180, 0));
+  }
+
+  .xl\:focus\:via-green-400:focus {
+    --gradient-via-color: #68d391;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(104, 211, 145, 0));
+  }
+
+  .xl\:focus\:via-green-500:focus {
+    --gradient-via-color: #48bb78;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(72, 187, 120, 0));
+  }
+
+  .xl\:focus\:via-green-600:focus {
+    --gradient-via-color: #38a169;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 161, 105, 0));
+  }
+
+  .xl\:focus\:via-green-700:focus {
+    --gradient-via-color: #2f855a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(47, 133, 90, 0));
+  }
+
+  .xl\:focus\:via-green-800:focus {
+    --gradient-via-color: #276749;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(39, 103, 73, 0));
+  }
+
+  .xl\:focus\:via-green-900:focus {
+    --gradient-via-color: #22543d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(34, 84, 61, 0));
+  }
+
+  .xl\:focus\:via-teal-100:focus {
+    --gradient-via-color: #e6fffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(230, 255, 250, 0));
+  }
+
+  .xl\:focus\:via-teal-200:focus {
+    --gradient-via-color: #b2f5ea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(178, 245, 234, 0));
+  }
+
+  .xl\:focus\:via-teal-300:focus {
+    --gradient-via-color: #81e6d9;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(129, 230, 217, 0));
+  }
+
+  .xl\:focus\:via-teal-400:focus {
+    --gradient-via-color: #4fd1c5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(79, 209, 197, 0));
+  }
+
+  .xl\:focus\:via-teal-500:focus {
+    --gradient-via-color: #38b2ac;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(56, 178, 172, 0));
+  }
+
+  .xl\:focus\:via-teal-600:focus {
+    --gradient-via-color: #319795;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 151, 149, 0));
+  }
+
+  .xl\:focus\:via-teal-700:focus {
+    --gradient-via-color: #2c7a7b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 122, 123, 0));
+  }
+
+  .xl\:focus\:via-teal-800:focus {
+    --gradient-via-color: #285e61;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(40, 94, 97, 0));
+  }
+
+  .xl\:focus\:via-teal-900:focus {
+    --gradient-via-color: #234e52;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(35, 78, 82, 0));
+  }
+
+  .xl\:focus\:via-blue-100:focus {
+    --gradient-via-color: #ebf8ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 248, 255, 0));
+  }
+
+  .xl\:focus\:via-blue-200:focus {
+    --gradient-via-color: #bee3f8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(190, 227, 248, 0));
+  }
+
+  .xl\:focus\:via-blue-300:focus {
+    --gradient-via-color: #90cdf4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(144, 205, 244, 0));
+  }
+
+  .xl\:focus\:via-blue-400:focus {
+    --gradient-via-color: #63b3ed;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(99, 179, 237, 0));
+  }
+
+  .xl\:focus\:via-blue-500:focus {
+    --gradient-via-color: #4299e1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(66, 153, 225, 0));
+  }
+
+  .xl\:focus\:via-blue-600:focus {
+    --gradient-via-color: #3182ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(49, 130, 206, 0));
+  }
+
+  .xl\:focus\:via-blue-700:focus {
+    --gradient-via-color: #2b6cb0;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(43, 108, 176, 0));
+  }
+
+  .xl\:focus\:via-blue-800:focus {
+    --gradient-via-color: #2c5282;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(44, 82, 130, 0));
+  }
+
+  .xl\:focus\:via-blue-900:focus {
+    --gradient-via-color: #2a4365;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(42, 67, 101, 0));
+  }
+
+  .xl\:focus\:via-indigo-100:focus {
+    --gradient-via-color: #ebf4ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(235, 244, 255, 0));
+  }
+
+  .xl\:focus\:via-indigo-200:focus {
+    --gradient-via-color: #c3dafe;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(195, 218, 254, 0));
+  }
+
+  .xl\:focus\:via-indigo-300:focus {
+    --gradient-via-color: #a3bffa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(163, 191, 250, 0));
+  }
+
+  .xl\:focus\:via-indigo-400:focus {
+    --gradient-via-color: #7f9cf5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(127, 156, 245, 0));
+  }
+
+  .xl\:focus\:via-indigo-500:focus {
+    --gradient-via-color: #667eea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(102, 126, 234, 0));
+  }
+
+  .xl\:focus\:via-indigo-600:focus {
+    --gradient-via-color: #5a67d8;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(90, 103, 216, 0));
+  }
+
+  .xl\:focus\:via-indigo-700:focus {
+    --gradient-via-color: #4c51bf;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(76, 81, 191, 0));
+  }
+
+  .xl\:focus\:via-indigo-800:focus {
+    --gradient-via-color: #434190;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(67, 65, 144, 0));
+  }
+
+  .xl\:focus\:via-indigo-900:focus {
+    --gradient-via-color: #3c366b;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(60, 54, 107, 0));
+  }
+
+  .xl\:focus\:via-purple-100:focus {
+    --gradient-via-color: #faf5ff;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(250, 245, 255, 0));
+  }
+
+  .xl\:focus\:via-purple-200:focus {
+    --gradient-via-color: #e9d8fd;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(233, 216, 253, 0));
+  }
+
+  .xl\:focus\:via-purple-300:focus {
+    --gradient-via-color: #d6bcfa;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(214, 188, 250, 0));
+  }
+
+  .xl\:focus\:via-purple-400:focus {
+    --gradient-via-color: #b794f4;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(183, 148, 244, 0));
+  }
+
+  .xl\:focus\:via-purple-500:focus {
+    --gradient-via-color: #9f7aea;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(159, 122, 234, 0));
+  }
+
+  .xl\:focus\:via-purple-600:focus {
+    --gradient-via-color: #805ad5;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(128, 90, 213, 0));
+  }
+
+  .xl\:focus\:via-purple-700:focus {
+    --gradient-via-color: #6b46c1;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(107, 70, 193, 0));
+  }
+
+  .xl\:focus\:via-purple-800:focus {
+    --gradient-via-color: #553c9a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(85, 60, 154, 0));
+  }
+
+  .xl\:focus\:via-purple-900:focus {
+    --gradient-via-color: #44337a;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(68, 51, 122, 0));
+  }
+
+  .xl\:focus\:via-pink-100:focus {
+    --gradient-via-color: #fff5f7;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(255, 245, 247, 0));
+  }
+
+  .xl\:focus\:via-pink-200:focus {
+    --gradient-via-color: #fed7e2;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(254, 215, 226, 0));
+  }
+
+  .xl\:focus\:via-pink-300:focus {
+    --gradient-via-color: #fbb6ce;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(251, 182, 206, 0));
+  }
+
+  .xl\:focus\:via-pink-400:focus {
+    --gradient-via-color: #f687b3;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(246, 135, 179, 0));
+  }
+
+  .xl\:focus\:via-pink-500:focus {
+    --gradient-via-color: #ed64a6;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(237, 100, 166, 0));
+  }
+
+  .xl\:focus\:via-pink-600:focus {
+    --gradient-via-color: #d53f8c;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(213, 63, 140, 0));
+  }
+
+  .xl\:focus\:via-pink-700:focus {
+    --gradient-via-color: #b83280;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(184, 50, 128, 0));
+  }
+
+  .xl\:focus\:via-pink-800:focus {
+    --gradient-via-color: #97266d;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(151, 38, 109, 0));
+  }
+
+  .xl\:focus\:via-pink-900:focus {
+    --gradient-via-color: #702459;
+    --gradient-color-stops: var(--gradient-from-color), var(--gradient-via-color), var(--gradient-to-color, rgba(112, 36, 89, 0));
+  }
+
+  .xl\:focus\:to-transparent:focus {
+    --gradient-to-color: transparent;
+  }
+
+  .xl\:focus\:to-current:focus {
+    --gradient-to-color: currentColor;
+  }
+
+  .xl\:focus\:to-black:focus {
+    --gradient-to-color: #000;
+  }
+
+  .xl\:focus\:to-white:focus {
+    --gradient-to-color: #fff;
+  }
+
+  .xl\:focus\:to-gray-100:focus {
+    --gradient-to-color: #f7fafc;
+  }
+
+  .xl\:focus\:to-gray-200:focus {
+    --gradient-to-color: #edf2f7;
+  }
+
+  .xl\:focus\:to-gray-300:focus {
+    --gradient-to-color: #e2e8f0;
+  }
+
+  .xl\:focus\:to-gray-400:focus {
+    --gradient-to-color: #cbd5e0;
+  }
+
+  .xl\:focus\:to-gray-500:focus {
+    --gradient-to-color: #a0aec0;
+  }
+
+  .xl\:focus\:to-gray-600:focus {
+    --gradient-to-color: #718096;
+  }
+
+  .xl\:focus\:to-gray-700:focus {
+    --gradient-to-color: #4a5568;
+  }
+
+  .xl\:focus\:to-gray-800:focus {
+    --gradient-to-color: #2d3748;
+  }
+
+  .xl\:focus\:to-gray-900:focus {
+    --gradient-to-color: #1a202c;
+  }
+
+  .xl\:focus\:to-red-100:focus {
+    --gradient-to-color: #fff5f5;
+  }
+
+  .xl\:focus\:to-red-200:focus {
+    --gradient-to-color: #fed7d7;
+  }
+
+  .xl\:focus\:to-red-300:focus {
+    --gradient-to-color: #feb2b2;
+  }
+
+  .xl\:focus\:to-red-400:focus {
+    --gradient-to-color: #fc8181;
+  }
+
+  .xl\:focus\:to-red-500:focus {
+    --gradient-to-color: #f56565;
+  }
+
+  .xl\:focus\:to-red-600:focus {
+    --gradient-to-color: #e53e3e;
+  }
+
+  .xl\:focus\:to-red-700:focus {
+    --gradient-to-color: #c53030;
+  }
+
+  .xl\:focus\:to-red-800:focus {
+    --gradient-to-color: #9b2c2c;
+  }
+
+  .xl\:focus\:to-red-900:focus {
+    --gradient-to-color: #742a2a;
+  }
+
+  .xl\:focus\:to-orange-100:focus {
+    --gradient-to-color: #fffaf0;
+  }
+
+  .xl\:focus\:to-orange-200:focus {
+    --gradient-to-color: #feebc8;
+  }
+
+  .xl\:focus\:to-orange-300:focus {
+    --gradient-to-color: #fbd38d;
+  }
+
+  .xl\:focus\:to-orange-400:focus {
+    --gradient-to-color: #f6ad55;
+  }
+
+  .xl\:focus\:to-orange-500:focus {
+    --gradient-to-color: #ed8936;
+  }
+
+  .xl\:focus\:to-orange-600:focus {
+    --gradient-to-color: #dd6b20;
+  }
+
+  .xl\:focus\:to-orange-700:focus {
+    --gradient-to-color: #c05621;
+  }
+
+  .xl\:focus\:to-orange-800:focus {
+    --gradient-to-color: #9c4221;
+  }
+
+  .xl\:focus\:to-orange-900:focus {
+    --gradient-to-color: #7b341e;
+  }
+
+  .xl\:focus\:to-yellow-100:focus {
+    --gradient-to-color: #fffff0;
+  }
+
+  .xl\:focus\:to-yellow-200:focus {
+    --gradient-to-color: #fefcbf;
+  }
+
+  .xl\:focus\:to-yellow-300:focus {
+    --gradient-to-color: #faf089;
+  }
+
+  .xl\:focus\:to-yellow-400:focus {
+    --gradient-to-color: #f6e05e;
+  }
+
+  .xl\:focus\:to-yellow-500:focus {
+    --gradient-to-color: #ecc94b;
+  }
+
+  .xl\:focus\:to-yellow-600:focus {
+    --gradient-to-color: #d69e2e;
+  }
+
+  .xl\:focus\:to-yellow-700:focus {
+    --gradient-to-color: #b7791f;
+  }
+
+  .xl\:focus\:to-yellow-800:focus {
+    --gradient-to-color: #975a16;
+  }
+
+  .xl\:focus\:to-yellow-900:focus {
+    --gradient-to-color: #744210;
+  }
+
+  .xl\:focus\:to-green-100:focus {
+    --gradient-to-color: #f0fff4;
+  }
+
+  .xl\:focus\:to-green-200:focus {
+    --gradient-to-color: #c6f6d5;
+  }
+
+  .xl\:focus\:to-green-300:focus {
+    --gradient-to-color: #9ae6b4;
+  }
+
+  .xl\:focus\:to-green-400:focus {
+    --gradient-to-color: #68d391;
+  }
+
+  .xl\:focus\:to-green-500:focus {
+    --gradient-to-color: #48bb78;
+  }
+
+  .xl\:focus\:to-green-600:focus {
+    --gradient-to-color: #38a169;
+  }
+
+  .xl\:focus\:to-green-700:focus {
+    --gradient-to-color: #2f855a;
+  }
+
+  .xl\:focus\:to-green-800:focus {
+    --gradient-to-color: #276749;
+  }
+
+  .xl\:focus\:to-green-900:focus {
+    --gradient-to-color: #22543d;
+  }
+
+  .xl\:focus\:to-teal-100:focus {
+    --gradient-to-color: #e6fffa;
+  }
+
+  .xl\:focus\:to-teal-200:focus {
+    --gradient-to-color: #b2f5ea;
+  }
+
+  .xl\:focus\:to-teal-300:focus {
+    --gradient-to-color: #81e6d9;
+  }
+
+  .xl\:focus\:to-teal-400:focus {
+    --gradient-to-color: #4fd1c5;
+  }
+
+  .xl\:focus\:to-teal-500:focus {
+    --gradient-to-color: #38b2ac;
+  }
+
+  .xl\:focus\:to-teal-600:focus {
+    --gradient-to-color: #319795;
+  }
+
+  .xl\:focus\:to-teal-700:focus {
+    --gradient-to-color: #2c7a7b;
+  }
+
+  .xl\:focus\:to-teal-800:focus {
+    --gradient-to-color: #285e61;
+  }
+
+  .xl\:focus\:to-teal-900:focus {
+    --gradient-to-color: #234e52;
+  }
+
+  .xl\:focus\:to-blue-100:focus {
+    --gradient-to-color: #ebf8ff;
+  }
+
+  .xl\:focus\:to-blue-200:focus {
+    --gradient-to-color: #bee3f8;
+  }
+
+  .xl\:focus\:to-blue-300:focus {
+    --gradient-to-color: #90cdf4;
+  }
+
+  .xl\:focus\:to-blue-400:focus {
+    --gradient-to-color: #63b3ed;
+  }
+
+  .xl\:focus\:to-blue-500:focus {
+    --gradient-to-color: #4299e1;
+  }
+
+  .xl\:focus\:to-blue-600:focus {
+    --gradient-to-color: #3182ce;
+  }
+
+  .xl\:focus\:to-blue-700:focus {
+    --gradient-to-color: #2b6cb0;
+  }
+
+  .xl\:focus\:to-blue-800:focus {
+    --gradient-to-color: #2c5282;
+  }
+
+  .xl\:focus\:to-blue-900:focus {
+    --gradient-to-color: #2a4365;
+  }
+
+  .xl\:focus\:to-indigo-100:focus {
+    --gradient-to-color: #ebf4ff;
+  }
+
+  .xl\:focus\:to-indigo-200:focus {
+    --gradient-to-color: #c3dafe;
+  }
+
+  .xl\:focus\:to-indigo-300:focus {
+    --gradient-to-color: #a3bffa;
+  }
+
+  .xl\:focus\:to-indigo-400:focus {
+    --gradient-to-color: #7f9cf5;
+  }
+
+  .xl\:focus\:to-indigo-500:focus {
+    --gradient-to-color: #667eea;
+  }
+
+  .xl\:focus\:to-indigo-600:focus {
+    --gradient-to-color: #5a67d8;
+  }
+
+  .xl\:focus\:to-indigo-700:focus {
+    --gradient-to-color: #4c51bf;
+  }
+
+  .xl\:focus\:to-indigo-800:focus {
+    --gradient-to-color: #434190;
+  }
+
+  .xl\:focus\:to-indigo-900:focus {
+    --gradient-to-color: #3c366b;
+  }
+
+  .xl\:focus\:to-purple-100:focus {
+    --gradient-to-color: #faf5ff;
+  }
+
+  .xl\:focus\:to-purple-200:focus {
+    --gradient-to-color: #e9d8fd;
+  }
+
+  .xl\:focus\:to-purple-300:focus {
+    --gradient-to-color: #d6bcfa;
+  }
+
+  .xl\:focus\:to-purple-400:focus {
+    --gradient-to-color: #b794f4;
+  }
+
+  .xl\:focus\:to-purple-500:focus {
+    --gradient-to-color: #9f7aea;
+  }
+
+  .xl\:focus\:to-purple-600:focus {
+    --gradient-to-color: #805ad5;
+  }
+
+  .xl\:focus\:to-purple-700:focus {
+    --gradient-to-color: #6b46c1;
+  }
+
+  .xl\:focus\:to-purple-800:focus {
+    --gradient-to-color: #553c9a;
+  }
+
+  .xl\:focus\:to-purple-900:focus {
+    --gradient-to-color: #44337a;
+  }
+
+  .xl\:focus\:to-pink-100:focus {
+    --gradient-to-color: #fff5f7;
+  }
+
+  .xl\:focus\:to-pink-200:focus {
+    --gradient-to-color: #fed7e2;
+  }
+
+  .xl\:focus\:to-pink-300:focus {
+    --gradient-to-color: #fbb6ce;
+  }
+
+  .xl\:focus\:to-pink-400:focus {
+    --gradient-to-color: #f687b3;
+  }
+
+  .xl\:focus\:to-pink-500:focus {
+    --gradient-to-color: #ed64a6;
+  }
+
+  .xl\:focus\:to-pink-600:focus {
+    --gradient-to-color: #d53f8c;
+  }
+
+  .xl\:focus\:to-pink-700:focus {
+    --gradient-to-color: #b83280;
+  }
+
+  .xl\:focus\:to-pink-800:focus {
+    --gradient-to-color: #97266d;
+  }
+
+  .xl\:focus\:to-pink-900:focus {
+    --gradient-to-color: #702459;
+  }
+
+  .xl\:bg-opacity-0 {
+    --bg-opacity: 0;
+  }
+
+  .xl\:bg-opacity-25 {
+    --bg-opacity: 0.25;
+  }
+
+  .xl\:bg-opacity-50 {
+    --bg-opacity: 0.5;
+  }
+
+  .xl\:bg-opacity-75 {
+    --bg-opacity: 0.75;
+  }
+
+  .xl\:bg-opacity-100 {
+    --bg-opacity: 1;
+  }
+
+  .xl\:hover\:bg-opacity-0:hover {
+    --bg-opacity: 0;
+  }
+
+  .xl\:hover\:bg-opacity-25:hover {
+    --bg-opacity: 0.25;
+  }
+
+  .xl\:hover\:bg-opacity-50:hover {
+    --bg-opacity: 0.5;
+  }
+
+  .xl\:hover\:bg-opacity-75:hover {
+    --bg-opacity: 0.75;
+  }
+
+  .xl\:hover\:bg-opacity-100:hover {
+    --bg-opacity: 1;
+  }
+
+  .xl\:focus\:bg-opacity-0:focus {
+    --bg-opacity: 0;
+  }
+
+  .xl\:focus\:bg-opacity-25:focus {
+    --bg-opacity: 0.25;
+  }
+
+  .xl\:focus\:bg-opacity-50:focus {
+    --bg-opacity: 0.5;
+  }
+
+  .xl\:focus\:bg-opacity-75:focus {
+    --bg-opacity: 0.75;
+  }
+
+  .xl\:focus\:bg-opacity-100:focus {
+    --bg-opacity: 1;
+  }
+
+  .xl\:bg-bottom {
+    background-position: bottom;
+  }
+
+  .xl\:bg-center {
+    background-position: center;
+  }
+
+  .xl\:bg-left {
+    background-position: left;
+  }
+
+  .xl\:bg-left-bottom {
+    background-position: left bottom;
+  }
+
+  .xl\:bg-left-top {
+    background-position: left top;
+  }
+
+  .xl\:bg-right {
+    background-position: right;
+  }
+
+  .xl\:bg-right-bottom {
+    background-position: right bottom;
+  }
+
+  .xl\:bg-right-top {
+    background-position: right top;
+  }
+
+  .xl\:bg-top {
+    background-position: top;
+  }
+
+  .xl\:bg-repeat {
+    background-repeat: repeat;
+  }
+
+  .xl\:bg-no-repeat {
+    background-repeat: no-repeat;
+  }
+
+  .xl\:bg-repeat-x {
+    background-repeat: repeat-x;
+  }
+
+  .xl\:bg-repeat-y {
+    background-repeat: repeat-y;
+  }
+
+  .xl\:bg-repeat-round {
+    background-repeat: round;
+  }
+
+  .xl\:bg-repeat-space {
+    background-repeat: space;
+  }
+
+  .xl\:bg-auto {
+    background-size: auto;
+  }
+
+  .xl\:bg-cover {
+    background-size: cover;
+  }
+
+  .xl\:bg-contain {
+    background-size: contain;
+  }
+
+  .xl\:border-collapse {
+    border-collapse: collapse;
+  }
+
+  .xl\:border-separate {
+    border-collapse: separate;
+  }
+
+  .xl\:border-transparent {
+    border-color: transparent;
+  }
+
+  .xl\:border-current {
+    border-color: currentColor;
+  }
+
+  .xl\:border-black {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .xl\:border-white {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .xl\:border-gray-100 {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .xl\:border-gray-200 {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .xl\:border-gray-300 {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .xl\:border-gray-400 {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .xl\:border-gray-500 {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .xl\:border-gray-600 {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .xl\:border-gray-700 {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .xl\:border-gray-800 {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .xl\:border-gray-900 {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .xl\:border-red-100 {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .xl\:border-red-200 {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .xl\:border-red-300 {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .xl\:border-red-400 {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .xl\:border-red-500 {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .xl\:border-red-600 {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .xl\:border-red-700 {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .xl\:border-red-800 {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .xl\:border-red-900 {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .xl\:border-orange-100 {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .xl\:border-orange-200 {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .xl\:border-orange-300 {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .xl\:border-orange-400 {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .xl\:border-orange-500 {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .xl\:border-orange-600 {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .xl\:border-orange-700 {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .xl\:border-orange-800 {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .xl\:border-orange-900 {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-100 {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-200 {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-300 {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-400 {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-500 {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-600 {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-700 {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-800 {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .xl\:border-yellow-900 {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .xl\:border-green-100 {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .xl\:border-green-200 {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .xl\:border-green-300 {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .xl\:border-green-400 {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .xl\:border-green-500 {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .xl\:border-green-600 {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .xl\:border-green-700 {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .xl\:border-green-800 {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .xl\:border-green-900 {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .xl\:border-teal-100 {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .xl\:border-teal-200 {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .xl\:border-teal-300 {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .xl\:border-teal-400 {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .xl\:border-teal-500 {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .xl\:border-teal-600 {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .xl\:border-teal-700 {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .xl\:border-teal-800 {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .xl\:border-teal-900 {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .xl\:border-blue-100 {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .xl\:border-blue-200 {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .xl\:border-blue-300 {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .xl\:border-blue-400 {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .xl\:border-blue-500 {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .xl\:border-blue-600 {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .xl\:border-blue-700 {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .xl\:border-blue-800 {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .xl\:border-blue-900 {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-100 {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-200 {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-300 {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-400 {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-500 {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-600 {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-700 {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-800 {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .xl\:border-indigo-900 {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .xl\:border-purple-100 {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .xl\:border-purple-200 {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .xl\:border-purple-300 {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .xl\:border-purple-400 {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .xl\:border-purple-500 {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .xl\:border-purple-600 {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .xl\:border-purple-700 {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .xl\:border-purple-800 {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .xl\:border-purple-900 {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .xl\:border-pink-100 {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .xl\:border-pink-200 {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .xl\:border-pink-300 {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .xl\:border-pink-400 {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .xl\:border-pink-500 {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .xl\:border-pink-600 {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .xl\:border-pink-700 {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .xl\:border-pink-800 {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .xl\:border-pink-900 {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-transparent:hover {
+    border-color: transparent;
+  }
+
+  .xl\:hover\:border-current:hover {
+    border-color: currentColor;
+  }
+
+  .xl\:hover\:border-black:hover {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-white:hover {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-100:hover {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-200:hover {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-300:hover {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-400:hover {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-500:hover {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-600:hover {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-700:hover {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-800:hover {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-gray-900:hover {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-300:hover {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-400:hover {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-500:hover {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-600:hover {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-700:hover {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-800:hover {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-red-900:hover {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-100:hover {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-200:hover {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-300:hover {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-400:hover {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-500:hover {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-600:hover {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-700:hover {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-800:hover {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-orange-900:hover {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-100:hover {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-200:hover {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-300:hover {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-400:hover {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-500:hover {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-600:hover {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-700:hover {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-800:hover {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-yellow-900:hover {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-100:hover {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-200:hover {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-300:hover {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-400:hover {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-500:hover {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-600:hover {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-700:hover {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-800:hover {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-green-900:hover {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-100:hover {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-200:hover {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-300:hover {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-400:hover {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-500:hover {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-600:hover {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-700:hover {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-800:hover {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-teal-900:hover {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-200:hover {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-300:hover {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-400:hover {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-500:hover {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-600:hover {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-700:hover {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-800:hover {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-blue-900:hover {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-100:hover {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-200:hover {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-300:hover {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-400:hover {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-500:hover {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-600:hover {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-700:hover {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-800:hover {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-indigo-900:hover {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-100:hover {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-200:hover {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-300:hover {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-400:hover {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-500:hover {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-600:hover {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-700:hover {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-800:hover {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-purple-900:hover {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-100:hover {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-200:hover {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-300:hover {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-400:hover {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-500:hover {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-600:hover {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-700:hover {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-800:hover {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .xl\:hover\:border-pink-900:hover {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-transparent:focus {
+    border-color: transparent;
+  }
+
+  .xl\:focus\:border-current:focus {
+    border-color: currentColor;
+  }
+
+  .xl\:focus\:border-black:focus {
+    --border-opacity: 1;
+    border-color: #000;
+    border-color: rgba(0, 0, 0, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-white:focus {
+    --border-opacity: 1;
+    border-color: #fff;
+    border-color: rgba(255, 255, 255, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-100:focus {
+    --border-opacity: 1;
+    border-color: #f7fafc;
+    border-color: rgba(247, 250, 252, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-200:focus {
+    --border-opacity: 1;
+    border-color: #edf2f7;
+    border-color: rgba(237, 242, 247, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-300:focus {
+    --border-opacity: 1;
+    border-color: #e2e8f0;
+    border-color: rgba(226, 232, 240, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-400:focus {
+    --border-opacity: 1;
+    border-color: #cbd5e0;
+    border-color: rgba(203, 213, 224, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-500:focus {
+    --border-opacity: 1;
+    border-color: #a0aec0;
+    border-color: rgba(160, 174, 192, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-600:focus {
+    --border-opacity: 1;
+    border-color: #718096;
+    border-color: rgba(113, 128, 150, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-700:focus {
+    --border-opacity: 1;
+    border-color: #4a5568;
+    border-color: rgba(74, 85, 104, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-800:focus {
+    --border-opacity: 1;
+    border-color: #2d3748;
+    border-color: rgba(45, 55, 72, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-gray-900:focus {
+    --border-opacity: 1;
+    border-color: #1a202c;
+    border-color: rgba(26, 32, 44, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f5;
+    border-color: rgba(255, 245, 245, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7d7;
+    border-color: rgba(254, 215, 215, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-300:focus {
+    --border-opacity: 1;
+    border-color: #feb2b2;
+    border-color: rgba(254, 178, 178, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-400:focus {
+    --border-opacity: 1;
+    border-color: #fc8181;
+    border-color: rgba(252, 129, 129, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-500:focus {
+    --border-opacity: 1;
+    border-color: #f56565;
+    border-color: rgba(245, 101, 101, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-600:focus {
+    --border-opacity: 1;
+    border-color: #e53e3e;
+    border-color: rgba(229, 62, 62, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-700:focus {
+    --border-opacity: 1;
+    border-color: #c53030;
+    border-color: rgba(197, 48, 48, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-800:focus {
+    --border-opacity: 1;
+    border-color: #9b2c2c;
+    border-color: rgba(155, 44, 44, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-red-900:focus {
+    --border-opacity: 1;
+    border-color: #742a2a;
+    border-color: rgba(116, 42, 42, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-100:focus {
+    --border-opacity: 1;
+    border-color: #fffaf0;
+    border-color: rgba(255, 250, 240, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-200:focus {
+    --border-opacity: 1;
+    border-color: #feebc8;
+    border-color: rgba(254, 235, 200, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-300:focus {
+    --border-opacity: 1;
+    border-color: #fbd38d;
+    border-color: rgba(251, 211, 141, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-400:focus {
+    --border-opacity: 1;
+    border-color: #f6ad55;
+    border-color: rgba(246, 173, 85, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-500:focus {
+    --border-opacity: 1;
+    border-color: #ed8936;
+    border-color: rgba(237, 137, 54, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-600:focus {
+    --border-opacity: 1;
+    border-color: #dd6b20;
+    border-color: rgba(221, 107, 32, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-700:focus {
+    --border-opacity: 1;
+    border-color: #c05621;
+    border-color: rgba(192, 86, 33, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-800:focus {
+    --border-opacity: 1;
+    border-color: #9c4221;
+    border-color: rgba(156, 66, 33, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-orange-900:focus {
+    --border-opacity: 1;
+    border-color: #7b341e;
+    border-color: rgba(123, 52, 30, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-100:focus {
+    --border-opacity: 1;
+    border-color: #fffff0;
+    border-color: rgba(255, 255, 240, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-200:focus {
+    --border-opacity: 1;
+    border-color: #fefcbf;
+    border-color: rgba(254, 252, 191, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-300:focus {
+    --border-opacity: 1;
+    border-color: #faf089;
+    border-color: rgba(250, 240, 137, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-400:focus {
+    --border-opacity: 1;
+    border-color: #f6e05e;
+    border-color: rgba(246, 224, 94, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-500:focus {
+    --border-opacity: 1;
+    border-color: #ecc94b;
+    border-color: rgba(236, 201, 75, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-600:focus {
+    --border-opacity: 1;
+    border-color: #d69e2e;
+    border-color: rgba(214, 158, 46, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-700:focus {
+    --border-opacity: 1;
+    border-color: #b7791f;
+    border-color: rgba(183, 121, 31, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-800:focus {
+    --border-opacity: 1;
+    border-color: #975a16;
+    border-color: rgba(151, 90, 22, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-yellow-900:focus {
+    --border-opacity: 1;
+    border-color: #744210;
+    border-color: rgba(116, 66, 16, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-100:focus {
+    --border-opacity: 1;
+    border-color: #f0fff4;
+    border-color: rgba(240, 255, 244, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-200:focus {
+    --border-opacity: 1;
+    border-color: #c6f6d5;
+    border-color: rgba(198, 246, 213, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-300:focus {
+    --border-opacity: 1;
+    border-color: #9ae6b4;
+    border-color: rgba(154, 230, 180, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-400:focus {
+    --border-opacity: 1;
+    border-color: #68d391;
+    border-color: rgba(104, 211, 145, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-500:focus {
+    --border-opacity: 1;
+    border-color: #48bb78;
+    border-color: rgba(72, 187, 120, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-600:focus {
+    --border-opacity: 1;
+    border-color: #38a169;
+    border-color: rgba(56, 161, 105, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-700:focus {
+    --border-opacity: 1;
+    border-color: #2f855a;
+    border-color: rgba(47, 133, 90, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-800:focus {
+    --border-opacity: 1;
+    border-color: #276749;
+    border-color: rgba(39, 103, 73, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-green-900:focus {
+    --border-opacity: 1;
+    border-color: #22543d;
+    border-color: rgba(34, 84, 61, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-100:focus {
+    --border-opacity: 1;
+    border-color: #e6fffa;
+    border-color: rgba(230, 255, 250, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-200:focus {
+    --border-opacity: 1;
+    border-color: #b2f5ea;
+    border-color: rgba(178, 245, 234, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-300:focus {
+    --border-opacity: 1;
+    border-color: #81e6d9;
+    border-color: rgba(129, 230, 217, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-400:focus {
+    --border-opacity: 1;
+    border-color: #4fd1c5;
+    border-color: rgba(79, 209, 197, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-500:focus {
+    --border-opacity: 1;
+    border-color: #38b2ac;
+    border-color: rgba(56, 178, 172, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-600:focus {
+    --border-opacity: 1;
+    border-color: #319795;
+    border-color: rgba(49, 151, 149, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-700:focus {
+    --border-opacity: 1;
+    border-color: #2c7a7b;
+    border-color: rgba(44, 122, 123, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-800:focus {
+    --border-opacity: 1;
+    border-color: #285e61;
+    border-color: rgba(40, 94, 97, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-teal-900:focus {
+    --border-opacity: 1;
+    border-color: #234e52;
+    border-color: rgba(35, 78, 82, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf8ff;
+    border-color: rgba(235, 248, 255, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-200:focus {
+    --border-opacity: 1;
+    border-color: #bee3f8;
+    border-color: rgba(190, 227, 248, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-300:focus {
+    --border-opacity: 1;
+    border-color: #90cdf4;
+    border-color: rgba(144, 205, 244, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-400:focus {
+    --border-opacity: 1;
+    border-color: #63b3ed;
+    border-color: rgba(99, 179, 237, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-500:focus {
+    --border-opacity: 1;
+    border-color: #4299e1;
+    border-color: rgba(66, 153, 225, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-600:focus {
+    --border-opacity: 1;
+    border-color: #3182ce;
+    border-color: rgba(49, 130, 206, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-700:focus {
+    --border-opacity: 1;
+    border-color: #2b6cb0;
+    border-color: rgba(43, 108, 176, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-800:focus {
+    --border-opacity: 1;
+    border-color: #2c5282;
+    border-color: rgba(44, 82, 130, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-blue-900:focus {
+    --border-opacity: 1;
+    border-color: #2a4365;
+    border-color: rgba(42, 67, 101, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-100:focus {
+    --border-opacity: 1;
+    border-color: #ebf4ff;
+    border-color: rgba(235, 244, 255, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-200:focus {
+    --border-opacity: 1;
+    border-color: #c3dafe;
+    border-color: rgba(195, 218, 254, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-300:focus {
+    --border-opacity: 1;
+    border-color: #a3bffa;
+    border-color: rgba(163, 191, 250, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-400:focus {
+    --border-opacity: 1;
+    border-color: #7f9cf5;
+    border-color: rgba(127, 156, 245, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-500:focus {
+    --border-opacity: 1;
+    border-color: #667eea;
+    border-color: rgba(102, 126, 234, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-600:focus {
+    --border-opacity: 1;
+    border-color: #5a67d8;
+    border-color: rgba(90, 103, 216, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-700:focus {
+    --border-opacity: 1;
+    border-color: #4c51bf;
+    border-color: rgba(76, 81, 191, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-800:focus {
+    --border-opacity: 1;
+    border-color: #434190;
+    border-color: rgba(67, 65, 144, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-indigo-900:focus {
+    --border-opacity: 1;
+    border-color: #3c366b;
+    border-color: rgba(60, 54, 107, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-100:focus {
+    --border-opacity: 1;
+    border-color: #faf5ff;
+    border-color: rgba(250, 245, 255, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-200:focus {
+    --border-opacity: 1;
+    border-color: #e9d8fd;
+    border-color: rgba(233, 216, 253, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-300:focus {
+    --border-opacity: 1;
+    border-color: #d6bcfa;
+    border-color: rgba(214, 188, 250, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-400:focus {
+    --border-opacity: 1;
+    border-color: #b794f4;
+    border-color: rgba(183, 148, 244, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-500:focus {
+    --border-opacity: 1;
+    border-color: #9f7aea;
+    border-color: rgba(159, 122, 234, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-600:focus {
+    --border-opacity: 1;
+    border-color: #805ad5;
+    border-color: rgba(128, 90, 213, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-700:focus {
+    --border-opacity: 1;
+    border-color: #6b46c1;
+    border-color: rgba(107, 70, 193, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-800:focus {
+    --border-opacity: 1;
+    border-color: #553c9a;
+    border-color: rgba(85, 60, 154, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-purple-900:focus {
+    --border-opacity: 1;
+    border-color: #44337a;
+    border-color: rgba(68, 51, 122, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-100:focus {
+    --border-opacity: 1;
+    border-color: #fff5f7;
+    border-color: rgba(255, 245, 247, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-200:focus {
+    --border-opacity: 1;
+    border-color: #fed7e2;
+    border-color: rgba(254, 215, 226, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-300:focus {
+    --border-opacity: 1;
+    border-color: #fbb6ce;
+    border-color: rgba(251, 182, 206, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-400:focus {
+    --border-opacity: 1;
+    border-color: #f687b3;
+    border-color: rgba(246, 135, 179, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-500:focus {
+    --border-opacity: 1;
+    border-color: #ed64a6;
+    border-color: rgba(237, 100, 166, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-600:focus {
+    --border-opacity: 1;
+    border-color: #d53f8c;
+    border-color: rgba(213, 63, 140, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-700:focus {
+    --border-opacity: 1;
+    border-color: #b83280;
+    border-color: rgba(184, 50, 128, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-800:focus {
+    --border-opacity: 1;
+    border-color: #97266d;
+    border-color: rgba(151, 38, 109, var(--border-opacity));
+  }
+
+  .xl\:focus\:border-pink-900:focus {
+    --border-opacity: 1;
+    border-color: #702459;
+    border-color: rgba(112, 36, 89, var(--border-opacity));
+  }
+
+  .xl\:border-opacity-0 {
+    --border-opacity: 0;
+  }
+
+  .xl\:border-opacity-25 {
+    --border-opacity: 0.25;
+  }
+
+  .xl\:border-opacity-50 {
+    --border-opacity: 0.5;
+  }
+
+  .xl\:border-opacity-75 {
+    --border-opacity: 0.75;
+  }
+
+  .xl\:border-opacity-100 {
+    --border-opacity: 1;
+  }
+
+  .xl\:hover\:border-opacity-0:hover {
+    --border-opacity: 0;
+  }
+
+  .xl\:hover\:border-opacity-25:hover {
+    --border-opacity: 0.25;
+  }
+
+  .xl\:hover\:border-opacity-50:hover {
+    --border-opacity: 0.5;
+  }
+
+  .xl\:hover\:border-opacity-75:hover {
+    --border-opacity: 0.75;
+  }
+
+  .xl\:hover\:border-opacity-100:hover {
+    --border-opacity: 1;
+  }
+
+  .xl\:focus\:border-opacity-0:focus {
+    --border-opacity: 0;
+  }
+
+  .xl\:focus\:border-opacity-25:focus {
+    --border-opacity: 0.25;
+  }
+
+  .xl\:focus\:border-opacity-50:focus {
+    --border-opacity: 0.5;
+  }
+
+  .xl\:focus\:border-opacity-75:focus {
+    --border-opacity: 0.75;
+  }
+
+  .xl\:focus\:border-opacity-100:focus {
+    --border-opacity: 1;
+  }
+
+  .xl\:rounded-none {
+    border-radius: 0;
+  }
+
+  .xl\:rounded-sm {
+    border-radius: 0.125rem;
+  }
+
+  .xl\:rounded {
+    border-radius: 0.25rem;
+  }
+
+  .xl\:rounded-md {
+    border-radius: 0.375rem;
+  }
+
+  .xl\:rounded-lg {
+    border-radius: 0.5rem;
+  }
+
+  .xl\:rounded-full {
+    border-radius: 9999px;
+  }
+
+  .xl\:rounded-t-none {
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+  }
+
+  .xl\:rounded-r-none {
+    border-top-right-radius: 0;
+    border-bottom-right-radius: 0;
+  }
+
+  .xl\:rounded-b-none {
+    border-bottom-right-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .xl\:rounded-l-none {
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
+  }
+
+  .xl\:rounded-t-sm {
+    border-top-left-radius: 0.125rem;
+    border-top-right-radius: 0.125rem;
+  }
+
+  .xl\:rounded-r-sm {
+    border-top-right-radius: 0.125rem;
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .xl\:rounded-b-sm {
+    border-bottom-right-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .xl\:rounded-l-sm {
+    border-top-left-radius: 0.125rem;
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .xl\:rounded-t {
+    border-top-left-radius: 0.25rem;
+    border-top-right-radius: 0.25rem;
+  }
+
+  .xl\:rounded-r {
+    border-top-right-radius: 0.25rem;
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .xl\:rounded-b {
+    border-bottom-right-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .xl\:rounded-l {
+    border-top-left-radius: 0.25rem;
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .xl\:rounded-t-md {
+    border-top-left-radius: 0.375rem;
+    border-top-right-radius: 0.375rem;
+  }
+
+  .xl\:rounded-r-md {
+    border-top-right-radius: 0.375rem;
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .xl\:rounded-b-md {
+    border-bottom-right-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .xl\:rounded-l-md {
+    border-top-left-radius: 0.375rem;
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .xl\:rounded-t-lg {
+    border-top-left-radius: 0.5rem;
+    border-top-right-radius: 0.5rem;
+  }
+
+  .xl\:rounded-r-lg {
+    border-top-right-radius: 0.5rem;
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .xl\:rounded-b-lg {
+    border-bottom-right-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .xl\:rounded-l-lg {
+    border-top-left-radius: 0.5rem;
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .xl\:rounded-t-full {
+    border-top-left-radius: 9999px;
+    border-top-right-radius: 9999px;
+  }
+
+  .xl\:rounded-r-full {
+    border-top-right-radius: 9999px;
+    border-bottom-right-radius: 9999px;
+  }
+
+  .xl\:rounded-b-full {
+    border-bottom-right-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .xl\:rounded-l-full {
+    border-top-left-radius: 9999px;
+    border-bottom-left-radius: 9999px;
+  }
+
+  .xl\:rounded-tl-none {
+    border-top-left-radius: 0;
+  }
+
+  .xl\:rounded-tr-none {
+    border-top-right-radius: 0;
+  }
+
+  .xl\:rounded-br-none {
+    border-bottom-right-radius: 0;
+  }
+
+  .xl\:rounded-bl-none {
+    border-bottom-left-radius: 0;
+  }
+
+  .xl\:rounded-tl-sm {
+    border-top-left-radius: 0.125rem;
+  }
+
+  .xl\:rounded-tr-sm {
+    border-top-right-radius: 0.125rem;
+  }
+
+  .xl\:rounded-br-sm {
+    border-bottom-right-radius: 0.125rem;
+  }
+
+  .xl\:rounded-bl-sm {
+    border-bottom-left-radius: 0.125rem;
+  }
+
+  .xl\:rounded-tl {
+    border-top-left-radius: 0.25rem;
+  }
+
+  .xl\:rounded-tr {
+    border-top-right-radius: 0.25rem;
+  }
+
+  .xl\:rounded-br {
+    border-bottom-right-radius: 0.25rem;
+  }
+
+  .xl\:rounded-bl {
+    border-bottom-left-radius: 0.25rem;
+  }
+
+  .xl\:rounded-tl-md {
+    border-top-left-radius: 0.375rem;
+  }
+
+  .xl\:rounded-tr-md {
+    border-top-right-radius: 0.375rem;
+  }
+
+  .xl\:rounded-br-md {
+    border-bottom-right-radius: 0.375rem;
+  }
+
+  .xl\:rounded-bl-md {
+    border-bottom-left-radius: 0.375rem;
+  }
+
+  .xl\:rounded-tl-lg {
+    border-top-left-radius: 0.5rem;
+  }
+
+  .xl\:rounded-tr-lg {
+    border-top-right-radius: 0.5rem;
+  }
+
+  .xl\:rounded-br-lg {
+    border-bottom-right-radius: 0.5rem;
+  }
+
+  .xl\:rounded-bl-lg {
+    border-bottom-left-radius: 0.5rem;
+  }
+
+  .xl\:rounded-tl-full {
+    border-top-left-radius: 9999px;
+  }
+
+  .xl\:rounded-tr-full {
+    border-top-right-radius: 9999px;
+  }
+
+  .xl\:rounded-br-full {
+    border-bottom-right-radius: 9999px;
+  }
+
+  .xl\:rounded-bl-full {
+    border-bottom-left-radius: 9999px;
+  }
+
+  .xl\:border-solid {
+    border-style: solid;
+  }
+
+  .xl\:border-dashed {
+    border-style: dashed;
+  }
+
+  .xl\:border-dotted {
+    border-style: dotted;
+  }
+
+  .xl\:border-double {
+    border-style: double;
+  }
+
+  .xl\:border-none {
+    border-style: none;
+  }
+
+  .xl\:border-0 {
+    border-width: 0;
+  }
+
+  .xl\:border-2 {
+    border-width: 2px;
+  }
+
+  .xl\:border-4 {
+    border-width: 4px;
+  }
+
+  .xl\:border-8 {
+    border-width: 8px;
+  }
+
+  .xl\:border {
+    border-width: 1px;
+  }
+
+  .xl\:border-t-0 {
+    border-top-width: 0;
+  }
+
+  .xl\:border-r-0 {
+    border-right-width: 0;
+  }
+
+  .xl\:border-b-0 {
+    border-bottom-width: 0;
+  }
+
+  .xl\:border-l-0 {
+    border-left-width: 0;
+  }
+
+  .xl\:border-t-2 {
+    border-top-width: 2px;
+  }
+
+  .xl\:border-r-2 {
+    border-right-width: 2px;
+  }
+
+  .xl\:border-b-2 {
+    border-bottom-width: 2px;
+  }
+
+  .xl\:border-l-2 {
+    border-left-width: 2px;
+  }
+
+  .xl\:border-t-4 {
+    border-top-width: 4px;
+  }
+
+  .xl\:border-r-4 {
+    border-right-width: 4px;
+  }
+
+  .xl\:border-b-4 {
+    border-bottom-width: 4px;
+  }
+
+  .xl\:border-l-4 {
+    border-left-width: 4px;
+  }
+
+  .xl\:border-t-8 {
+    border-top-width: 8px;
+  }
+
+  .xl\:border-r-8 {
+    border-right-width: 8px;
+  }
+
+  .xl\:border-b-8 {
+    border-bottom-width: 8px;
+  }
+
+  .xl\:border-l-8 {
+    border-left-width: 8px;
+  }
+
+  .xl\:border-t {
+    border-top-width: 1px;
+  }
+
+  .xl\:border-r {
+    border-right-width: 1px;
+  }
+
+  .xl\:border-b {
+    border-bottom-width: 1px;
+  }
+
+  .xl\:border-l {
+    border-left-width: 1px;
+  }
+
+  .xl\:box-border {
+    box-sizing: border-box;
+  }
+
+  .xl\:box-content {
+    box-sizing: content-box;
+  }
+
+  .xl\:cursor-auto {
+    cursor: auto;
+  }
+
+  .xl\:cursor-default {
+    cursor: default;
+  }
+
+  .xl\:cursor-pointer {
+    cursor: pointer;
+  }
+
+  .xl\:cursor-wait {
+    cursor: wait;
+  }
+
+  .xl\:cursor-text {
+    cursor: text;
+  }
+
+  .xl\:cursor-move {
+    cursor: move;
+  }
+
+  .xl\:cursor-not-allowed {
+    cursor: not-allowed;
+  }
+
+  .xl\:block {
+    display: block;
+  }
+
+  .xl\:inline-block {
+    display: inline-block;
+  }
+
+  .xl\:inline {
+    display: inline;
+  }
+
+  .xl\:flex {
+    display: flex;
+  }
+
+  .xl\:inline-flex {
+    display: inline-flex;
+  }
+
+  .xl\:table {
+    display: table;
+  }
+
+  .xl\:table-caption {
+    display: table-caption;
+  }
+
+  .xl\:table-cell {
+    display: table-cell;
+  }
+
+  .xl\:table-column {
+    display: table-column;
+  }
+
+  .xl\:table-column-group {
+    display: table-column-group;
+  }
+
+  .xl\:table-footer-group {
+    display: table-footer-group;
+  }
+
+  .xl\:table-header-group {
+    display: table-header-group;
+  }
+
+  .xl\:table-row-group {
+    display: table-row-group;
+  }
+
+  .xl\:table-row {
+    display: table-row;
+  }
+
+  .xl\:flow-root {
+    display: flow-root;
+  }
+
+  .xl\:grid {
+    display: grid;
+  }
+
+  .xl\:inline-grid {
+    display: inline-grid;
+  }
+
+  .xl\:contents {
+    display: contents;
+  }
+
+  .xl\:hidden {
+    display: none;
+  }
+
+  .xl\:flex-row {
+    flex-direction: row;
+  }
+
+  .xl\:flex-row-reverse {
+    flex-direction: row-reverse;
+  }
+
+  .xl\:flex-col {
+    flex-direction: column;
+  }
+
+  .xl\:flex-col-reverse {
+    flex-direction: column-reverse;
+  }
+
+  .xl\:flex-wrap {
+    flex-wrap: wrap;
+  }
+
+  .xl\:flex-wrap-reverse {
+    flex-wrap: wrap-reverse;
+  }
+
+  .xl\:flex-no-wrap {
+    flex-wrap: nowrap;
+  }
+
+  .xl\:place-items-auto {
+    place-items: auto;
+  }
+
+  .xl\:place-items-start {
+    place-items: start;
+  }
+
+  .xl\:place-items-end {
+    place-items: end;
+  }
+
+  .xl\:place-items-center {
+    place-items: center;
+  }
+
+  .xl\:place-items-stretch {
+    place-items: stretch;
+  }
+
+  .xl\:place-content-center {
+    place-content: center;
+  }
+
+  .xl\:place-content-start {
+    place-content: start;
+  }
+
+  .xl\:place-content-end {
+    place-content: end;
+  }
+
+  .xl\:place-content-between {
+    place-content: space-between;
+  }
+
+  .xl\:place-content-around {
+    place-content: space-around;
+  }
+
+  .xl\:place-content-evenly {
+    place-content: space-evenly;
+  }
+
+  .xl\:place-content-stretch {
+    place-content: stretch;
+  }
+
+  .xl\:place-self-auto {
+    place-self: auto;
+  }
+
+  .xl\:place-self-start {
+    place-self: start;
+  }
+
+  .xl\:place-self-end {
+    place-self: end;
+  }
+
+  .xl\:place-self-center {
+    place-self: center;
+  }
+
+  .xl\:place-self-stretch {
+    place-self: stretch;
+  }
+
+  .xl\:items-start {
+    align-items: flex-start;
+  }
+
+  .xl\:items-end {
+    align-items: flex-end;
+  }
+
+  .xl\:items-center {
+    align-items: center;
+  }
+
+  .xl\:items-baseline {
+    align-items: baseline;
+  }
+
+  .xl\:items-stretch {
+    align-items: stretch;
+  }
+
+  .xl\:content-center {
+    align-content: center;
+  }
+
+  .xl\:content-start {
+    align-content: flex-start;
+  }
+
+  .xl\:content-end {
+    align-content: flex-end;
+  }
+
+  .xl\:content-between {
+    align-content: space-between;
+  }
+
+  .xl\:content-around {
+    align-content: space-around;
+  }
+
+  .xl\:content-evenly {
+    align-content: space-evenly;
+  }
+
+  .xl\:self-auto {
+    align-self: auto;
+  }
+
+  .xl\:self-start {
+    align-self: flex-start;
+  }
+
+  .xl\:self-end {
+    align-self: flex-end;
+  }
+
+  .xl\:self-center {
+    align-self: center;
+  }
+
+  .xl\:self-stretch {
+    align-self: stretch;
+  }
+
+  .xl\:justify-items-auto {
+    justify-items: auto;
+  }
+
+  .xl\:justify-items-start {
+    justify-items: start;
+  }
+
+  .xl\:justify-items-end {
+    justify-items: end;
+  }
+
+  .xl\:justify-items-center {
+    justify-items: center;
+  }
+
+  .xl\:justify-items-stretch {
+    justify-items: stretch;
+  }
+
+  .xl\:justify-start {
+    justify-content: flex-start;
+  }
+
+  .xl\:justify-end {
+    justify-content: flex-end;
+  }
+
+  .xl\:justify-center {
+    justify-content: center;
+  }
+
+  .xl\:justify-between {
+    justify-content: space-between;
+  }
+
+  .xl\:justify-around {
+    justify-content: space-around;
+  }
+
+  .xl\:justify-evenly {
+    justify-content: space-evenly;
+  }
+
+  .xl\:justify-self-auto {
+    justify-self: auto;
+  }
+
+  .xl\:justify-self-start {
+    justify-self: start;
+  }
+
+  .xl\:justify-self-end {
+    justify-self: end;
+  }
+
+  .xl\:justify-self-center {
+    justify-self: center;
+  }
+
+  .xl\:justify-self-stretch {
+    justify-self: stretch;
+  }
+
+  .xl\:flex-1 {
+    flex: 1 1 0%;
+  }
+
+  .xl\:flex-auto {
+    flex: 1 1 auto;
+  }
+
+  .xl\:flex-initial {
+    flex: 0 1 auto;
+  }
+
+  .xl\:flex-none {
+    flex: none;
+  }
+
+  .xl\:flex-grow-0 {
+    flex-grow: 0;
+  }
+
+  .xl\:flex-grow {
+    flex-grow: 1;
+  }
+
+  .xl\:flex-shrink-0 {
+    flex-shrink: 0;
+  }
+
+  .xl\:flex-shrink {
+    flex-shrink: 1;
+  }
+
+  .xl\:order-1 {
+    order: 1;
+  }
+
+  .xl\:order-2 {
+    order: 2;
+  }
+
+  .xl\:order-3 {
+    order: 3;
+  }
+
+  .xl\:order-4 {
+    order: 4;
+  }
+
+  .xl\:order-5 {
+    order: 5;
+  }
+
+  .xl\:order-6 {
+    order: 6;
+  }
+
+  .xl\:order-7 {
+    order: 7;
+  }
+
+  .xl\:order-8 {
+    order: 8;
+  }
+
+  .xl\:order-9 {
+    order: 9;
+  }
+
+  .xl\:order-10 {
+    order: 10;
+  }
+
+  .xl\:order-11 {
+    order: 11;
+  }
+
+  .xl\:order-12 {
+    order: 12;
+  }
+
+  .xl\:order-first {
+    order: -9999;
+  }
+
+  .xl\:order-last {
+    order: 9999;
+  }
+
+  .xl\:order-none {
+    order: 0;
+  }
+
+  .xl\:float-right {
+    float: right;
+  }
+
+  .xl\:float-left {
+    float: left;
+  }
+
+  .xl\:float-none {
+    float: none;
+  }
+
+  .xl\:clearfix:after {
+    content: "";
+    display: table;
+    clear: both;
+  }
+
+  .xl\:clear-left {
+    clear: left;
+  }
+
+  .xl\:clear-right {
+    clear: right;
+  }
+
+  .xl\:clear-both {
+    clear: both;
+  }
+
+  .xl\:clear-none {
+    clear: none;
+  }
+
+  .xl\:font-sans {
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  }
+
+  .xl\:font-serif {
+    font-family: Georgia, Cambria, "Times New Roman", Times, serif;
+  }
+
+  .xl\:font-mono {
+    font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+  }
+
+  .xl\:font-hairline {
+    font-weight: 100;
+  }
+
+  .xl\:font-thin {
+    font-weight: 200;
+  }
+
+  .xl\:font-light {
+    font-weight: 300;
+  }
+
+  .xl\:font-normal {
+    font-weight: 400;
+  }
+
+  .xl\:font-medium {
+    font-weight: 500;
+  }
+
+  .xl\:font-semibold {
+    font-weight: 600;
+  }
+
+  .xl\:font-bold {
+    font-weight: 700;
+  }
+
+  .xl\:font-extrabold {
+    font-weight: 800;
+  }
+
+  .xl\:font-black {
+    font-weight: 900;
+  }
+
+  .xl\:hover\:font-hairline:hover {
+    font-weight: 100;
+  }
+
+  .xl\:hover\:font-thin:hover {
+    font-weight: 200;
+  }
+
+  .xl\:hover\:font-light:hover {
+    font-weight: 300;
+  }
+
+  .xl\:hover\:font-normal:hover {
+    font-weight: 400;
+  }
+
+  .xl\:hover\:font-medium:hover {
+    font-weight: 500;
+  }
+
+  .xl\:hover\:font-semibold:hover {
+    font-weight: 600;
+  }
+
+  .xl\:hover\:font-bold:hover {
+    font-weight: 700;
+  }
+
+  .xl\:hover\:font-extrabold:hover {
+    font-weight: 800;
+  }
+
+  .xl\:hover\:font-black:hover {
+    font-weight: 900;
+  }
+
+  .xl\:focus\:font-hairline:focus {
+    font-weight: 100;
+  }
+
+  .xl\:focus\:font-thin:focus {
+    font-weight: 200;
+  }
+
+  .xl\:focus\:font-light:focus {
+    font-weight: 300;
+  }
+
+  .xl\:focus\:font-normal:focus {
+    font-weight: 400;
+  }
+
+  .xl\:focus\:font-medium:focus {
+    font-weight: 500;
+  }
+
+  .xl\:focus\:font-semibold:focus {
+    font-weight: 600;
+  }
+
+  .xl\:focus\:font-bold:focus {
+    font-weight: 700;
+  }
+
+  .xl\:focus\:font-extrabold:focus {
+    font-weight: 800;
+  }
+
+  .xl\:focus\:font-black:focus {
+    font-weight: 900;
+  }
+
+  .xl\:h-0 {
+    height: 0;
+  }
+
+  .xl\:h-1 {
+    height: 0.25rem;
+  }
+
+  .xl\:h-2 {
+    height: 0.5rem;
+  }
+
+  .xl\:h-3 {
+    height: 0.75rem;
+  }
+
+  .xl\:h-4 {
+    height: 1rem;
+  }
+
+  .xl\:h-5 {
+    height: 1.25rem;
+  }
+
+  .xl\:h-6 {
+    height: 1.5rem;
+  }
+
+  .xl\:h-8 {
+    height: 2rem;
+  }
+
+  .xl\:h-10 {
+    height: 2.5rem;
+  }
+
+  .xl\:h-12 {
+    height: 3rem;
+  }
+
+  .xl\:h-16 {
+    height: 4rem;
+  }
+
+  .xl\:h-20 {
+    height: 5rem;
+  }
+
+  .xl\:h-24 {
+    height: 6rem;
+  }
+
+  .xl\:h-32 {
+    height: 8rem;
+  }
+
+  .xl\:h-40 {
+    height: 10rem;
+  }
+
+  .xl\:h-48 {
+    height: 12rem;
+  }
+
+  .xl\:h-56 {
+    height: 14rem;
+  }
+
+  .xl\:h-64 {
+    height: 16rem;
+  }
+
+  .xl\:h-auto {
+    height: auto;
+  }
+
+  .xl\:h-px {
+    height: 1px;
+  }
+
+  .xl\:h-full {
+    height: 100%;
+  }
+
+  .xl\:h-screen {
+    height: 100vh;
+  }
+
+  .xl\:text-xs {
+    font-size: 0.75rem;
+  }
+
+  .xl\:text-sm {
+    font-size: 0.875rem;
+  }
+
+  .xl\:text-base {
+    font-size: 1rem;
+  }
+
+  .xl\:text-lg {
+    font-size: 1.125rem;
+  }
+
+  .xl\:text-xl {
+    font-size: 1.25rem;
+  }
+
+  .xl\:text-2xl {
+    font-size: 1.5rem;
+  }
+
+  .xl\:text-3xl {
+    font-size: 1.875rem;
+  }
+
+  .xl\:text-4xl {
+    font-size: 2.25rem;
+  }
+
+  .xl\:text-5xl {
+    font-size: 3rem;
+  }
+
+  .xl\:text-6xl {
+    font-size: 4rem;
+  }
+
+  .xl\:leading-3 {
+    line-height: .75rem;
+  }
+
+  .xl\:leading-4 {
+    line-height: 1rem;
+  }
+
+  .xl\:leading-5 {
+    line-height: 1.25rem;
+  }
+
+  .xl\:leading-6 {
+    line-height: 1.5rem;
+  }
+
+  .xl\:leading-7 {
+    line-height: 1.75rem;
+  }
+
+  .xl\:leading-8 {
+    line-height: 2rem;
+  }
+
+  .xl\:leading-9 {
+    line-height: 2.25rem;
+  }
+
+  .xl\:leading-10 {
+    line-height: 2.5rem;
+  }
+
+  .xl\:leading-none {
+    line-height: 1;
+  }
+
+  .xl\:leading-tight {
+    line-height: 1.25;
+  }
+
+  .xl\:leading-snug {
+    line-height: 1.375;
+  }
+
+  .xl\:leading-normal {
+    line-height: 1.5;
+  }
+
+  .xl\:leading-relaxed {
+    line-height: 1.625;
+  }
+
+  .xl\:leading-loose {
+    line-height: 2;
+  }
+
+  .xl\:list-inside {
+    list-style-position: inside;
+  }
+
+  .xl\:list-outside {
+    list-style-position: outside;
+  }
+
+  .xl\:list-none {
+    list-style-type: none;
+  }
+
+  .xl\:list-disc {
+    list-style-type: disc;
+  }
+
+  .xl\:list-decimal {
+    list-style-type: decimal;
+  }
+
+  .xl\:m-0 {
+    margin: 0;
+  }
+
+  .xl\:m-1 {
+    margin: 0.25rem;
+  }
+
+  .xl\:m-2 {
+    margin: 0.5rem;
+  }
+
+  .xl\:m-3 {
+    margin: 0.75rem;
+  }
+
+  .xl\:m-4 {
+    margin: 1rem;
+  }
+
+  .xl\:m-5 {
+    margin: 1.25rem;
+  }
+
+  .xl\:m-6 {
+    margin: 1.5rem;
+  }
+
+  .xl\:m-8 {
+    margin: 2rem;
+  }
+
+  .xl\:m-10 {
+    margin: 2.5rem;
+  }
+
+  .xl\:m-12 {
+    margin: 3rem;
+  }
+
+  .xl\:m-16 {
+    margin: 4rem;
+  }
+
+  .xl\:m-20 {
+    margin: 5rem;
+  }
+
+  .xl\:m-24 {
+    margin: 6rem;
+  }
+
+  .xl\:m-32 {
+    margin: 8rem;
+  }
+
+  .xl\:m-40 {
+    margin: 10rem;
+  }
+
+  .xl\:m-48 {
+    margin: 12rem;
+  }
+
+  .xl\:m-56 {
+    margin: 14rem;
+  }
+
+  .xl\:m-64 {
+    margin: 16rem;
+  }
+
+  .xl\:m-auto {
+    margin: auto;
+  }
+
+  .xl\:m-px {
+    margin: 1px;
+  }
+
+  .xl\:-m-1 {
+    margin: -0.25rem;
+  }
+
+  .xl\:-m-2 {
+    margin: -0.5rem;
+  }
+
+  .xl\:-m-3 {
+    margin: -0.75rem;
+  }
+
+  .xl\:-m-4 {
+    margin: -1rem;
+  }
+
+  .xl\:-m-5 {
+    margin: -1.25rem;
+  }
+
+  .xl\:-m-6 {
+    margin: -1.5rem;
+  }
+
+  .xl\:-m-8 {
+    margin: -2rem;
+  }
+
+  .xl\:-m-10 {
+    margin: -2.5rem;
+  }
+
+  .xl\:-m-12 {
+    margin: -3rem;
+  }
+
+  .xl\:-m-16 {
+    margin: -4rem;
+  }
+
+  .xl\:-m-20 {
+    margin: -5rem;
+  }
+
+  .xl\:-m-24 {
+    margin: -6rem;
+  }
+
+  .xl\:-m-32 {
+    margin: -8rem;
+  }
+
+  .xl\:-m-40 {
+    margin: -10rem;
+  }
+
+  .xl\:-m-48 {
+    margin: -12rem;
+  }
+
+  .xl\:-m-56 {
+    margin: -14rem;
+  }
+
+  .xl\:-m-64 {
+    margin: -16rem;
+  }
+
+  .xl\:-m-px {
+    margin: -1px;
+  }
+
+  .xl\:my-0 {
+    margin-top: 0;
+    margin-bottom: 0;
+  }
+
+  .xl\:mx-0 {
+    margin-left: 0;
+    margin-right: 0;
+  }
+
+  .xl\:my-1 {
+    margin-top: 0.25rem;
+    margin-bottom: 0.25rem;
+  }
+
+  .xl\:mx-1 {
+    margin-left: 0.25rem;
+    margin-right: 0.25rem;
+  }
+
+  .xl\:my-2 {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+  }
+
+  .xl\:mx-2 {
+    margin-left: 0.5rem;
+    margin-right: 0.5rem;
+  }
+
+  .xl\:my-3 {
+    margin-top: 0.75rem;
+    margin-bottom: 0.75rem;
+  }
+
+  .xl\:mx-3 {
+    margin-left: 0.75rem;
+    margin-right: 0.75rem;
+  }
+
+  .xl\:my-4 {
+    margin-top: 1rem;
+    margin-bottom: 1rem;
+  }
+
+  .xl\:mx-4 {
+    margin-left: 1rem;
+    margin-right: 1rem;
+  }
+
+  .xl\:my-5 {
+    margin-top: 1.25rem;
+    margin-bottom: 1.25rem;
+  }
+
+  .xl\:mx-5 {
+    margin-left: 1.25rem;
+    margin-right: 1.25rem;
+  }
+
+  .xl\:my-6 {
+    margin-top: 1.5rem;
+    margin-bottom: 1.5rem;
+  }
+
+  .xl\:mx-6 {
+    margin-left: 1.5rem;
+    margin-right: 1.5rem;
+  }
+
+  .xl\:my-8 {
+    margin-top: 2rem;
+    margin-bottom: 2rem;
+  }
+
+  .xl\:mx-8 {
+    margin-left: 2rem;
+    margin-right: 2rem;
+  }
+
+  .xl\:my-10 {
+    margin-top: 2.5rem;
+    margin-bottom: 2.5rem;
+  }
+
+  .xl\:mx-10 {
+    margin-left: 2.5rem;
+    margin-right: 2.5rem;
+  }
+
+  .xl\:my-12 {
+    margin-top: 3rem;
+    margin-bottom: 3rem;
+  }
+
+  .xl\:mx-12 {
+    margin-left: 3rem;
+    margin-right: 3rem;
+  }
+
+  .xl\:my-16 {
+    margin-top: 4rem;
+    margin-bottom: 4rem;
+  }
+
+  .xl\:mx-16 {
+    margin-left: 4rem;
+    margin-right: 4rem;
+  }
+
+  .xl\:my-20 {
+    margin-top: 5rem;
+    margin-bottom: 5rem;
+  }
+
+  .xl\:mx-20 {
+    margin-left: 5rem;
+    margin-right: 5rem;
+  }
+
+  .xl\:my-24 {
+    margin-top: 6rem;
+    margin-bottom: 6rem;
+  }
+
+  .xl\:mx-24 {
+    margin-left: 6rem;
+    margin-right: 6rem;
+  }
+
+  .xl\:my-32 {
+    margin-top: 8rem;
+    margin-bottom: 8rem;
+  }
+
+  .xl\:mx-32 {
+    margin-left: 8rem;
+    margin-right: 8rem;
+  }
+
+  .xl\:my-40 {
+    margin-top: 10rem;
+    margin-bottom: 10rem;
+  }
+
+  .xl\:mx-40 {
+    margin-left: 10rem;
+    margin-right: 10rem;
+  }
+
+  .xl\:my-48 {
+    margin-top: 12rem;
+    margin-bottom: 12rem;
+  }
+
+  .xl\:mx-48 {
+    margin-left: 12rem;
+    margin-right: 12rem;
+  }
+
+  .xl\:my-56 {
+    margin-top: 14rem;
+    margin-bottom: 14rem;
+  }
+
+  .xl\:mx-56 {
+    margin-left: 14rem;
+    margin-right: 14rem;
+  }
+
+  .xl\:my-64 {
+    margin-top: 16rem;
+    margin-bottom: 16rem;
+  }
+
+  .xl\:mx-64 {
+    margin-left: 16rem;
+    margin-right: 16rem;
+  }
+
+  .xl\:my-auto {
+    margin-top: auto;
+    margin-bottom: auto;
+  }
+
+  .xl\:mx-auto {
+    margin-left: auto;
+    margin-right: auto;
+  }
+
+  .xl\:my-px {
+    margin-top: 1px;
+    margin-bottom: 1px;
+  }
+
+  .xl\:mx-px {
+    margin-left: 1px;
+    margin-right: 1px;
+  }
+
+  .xl\:-my-1 {
+    margin-top: -0.25rem;
+    margin-bottom: -0.25rem;
+  }
+
+  .xl\:-mx-1 {
+    margin-left: -0.25rem;
+    margin-right: -0.25rem;
+  }
+
+  .xl\:-my-2 {
+    margin-top: -0.5rem;
+    margin-bottom: -0.5rem;
+  }
+
+  .xl\:-mx-2 {
+    margin-left: -0.5rem;
+    margin-right: -0.5rem;
+  }
+
+  .xl\:-my-3 {
+    margin-top: -0.75rem;
+    margin-bottom: -0.75rem;
+  }
+
+  .xl\:-mx-3 {
+    margin-left: -0.75rem;
+    margin-right: -0.75rem;
+  }
+
+  .xl\:-my-4 {
+    margin-top: -1rem;
+    margin-bottom: -1rem;
+  }
+
+  .xl\:-mx-4 {
+    margin-left: -1rem;
+    margin-right: -1rem;
+  }
+
+  .xl\:-my-5 {
+    margin-top: -1.25rem;
+    margin-bottom: -1.25rem;
+  }
+
+  .xl\:-mx-5 {
+    margin-left: -1.25rem;
+    margin-right: -1.25rem;
+  }
+
+  .xl\:-my-6 {
+    margin-top: -1.5rem;
+    margin-bottom: -1.5rem;
+  }
+
+  .xl\:-mx-6 {
+    margin-left: -1.5rem;
+    margin-right: -1.5rem;
+  }
+
+  .xl\:-my-8 {
+    margin-top: -2rem;
+    margin-bottom: -2rem;
+  }
+
+  .xl\:-mx-8 {
+    margin-left: -2rem;
+    margin-right: -2rem;
+  }
+
+  .xl\:-my-10 {
+    margin-top: -2.5rem;
+    margin-bottom: -2.5rem;
+  }
+
+  .xl\:-mx-10 {
+    margin-left: -2.5rem;
+    margin-right: -2.5rem;
+  }
+
+  .xl\:-my-12 {
+    margin-top: -3rem;
+    margin-bottom: -3rem;
+  }
+
+  .xl\:-mx-12 {
+    margin-left: -3rem;
+    margin-right: -3rem;
+  }
+
+  .xl\:-my-16 {
+    margin-top: -4rem;
+    margin-bottom: -4rem;
+  }
+
+  .xl\:-mx-16 {
+    margin-left: -4rem;
+    margin-right: -4rem;
+  }
+
+  .xl\:-my-20 {
+    margin-top: -5rem;
+    margin-bottom: -5rem;
+  }
+
+  .xl\:-mx-20 {
+    margin-left: -5rem;
+    margin-right: -5rem;
+  }
+
+  .xl\:-my-24 {
+    margin-top: -6rem;
+    margin-bottom: -6rem;
+  }
+
+  .xl\:-mx-24 {
+    margin-left: -6rem;
+    margin-right: -6rem;
+  }
+
+  .xl\:-my-32 {
+    margin-top: -8rem;
+    margin-bottom: -8rem;
+  }
+
+  .xl\:-mx-32 {
+    margin-left: -8rem;
+    margin-right: -8rem;
+  }
+
+  .xl\:-my-40 {
+    margin-top: -10rem;
+    margin-bottom: -10rem;
+  }
+
+  .xl\:-mx-40 {
+    margin-left: -10rem;
+    margin-right: -10rem;
+  }
+
+  .xl\:-my-48 {
+    margin-top: -12rem;
+    margin-bottom: -12rem;
+  }
+
+  .xl\:-mx-48 {
+    margin-left: -12rem;
+    margin-right: -12rem;
+  }
+
+  .xl\:-my-56 {
+    margin-top: -14rem;
+    margin-bottom: -14rem;
+  }
+
+  .xl\:-mx-56 {
+    margin-left: -14rem;
+    margin-right: -14rem;
+  }
+
+  .xl\:-my-64 {
+    margin-top: -16rem;
+    margin-bottom: -16rem;
+  }
+
+  .xl\:-mx-64 {
+    margin-left: -16rem;
+    margin-right: -16rem;
+  }
+
+  .xl\:-my-px {
+    margin-top: -1px;
+    margin-bottom: -1px;
+  }
+
+  .xl\:-mx-px {
+    margin-left: -1px;
+    margin-right: -1px;
+  }
+
+  .xl\:mt-0 {
+    margin-top: 0;
+  }
+
+  .xl\:mr-0 {
+    margin-right: 0;
+  }
+
+  .xl\:mb-0 {
+    margin-bottom: 0;
+  }
+
+  .xl\:ml-0 {
+    margin-left: 0;
+  }
+
+  .xl\:mt-1 {
+    margin-top: 0.25rem;
+  }
+
+  .xl\:mr-1 {
+    margin-right: 0.25rem;
+  }
+
+  .xl\:mb-1 {
+    margin-bottom: 0.25rem;
+  }
+
+  .xl\:ml-1 {
+    margin-left: 0.25rem;
+  }
+
+  .xl\:mt-2 {
+    margin-top: 0.5rem;
+  }
+
+  .xl\:mr-2 {
+    margin-right: 0.5rem;
+  }
+
+  .xl\:mb-2 {
+    margin-bottom: 0.5rem;
+  }
+
+  .xl\:ml-2 {
+    margin-left: 0.5rem;
+  }
+
+  .xl\:mt-3 {
+    margin-top: 0.75rem;
+  }
+
+  .xl\:mr-3 {
+    margin-right: 0.75rem;
+  }
+
+  .xl\:mb-3 {
+    margin-bottom: 0.75rem;
+  }
+
+  .xl\:ml-3 {
+    margin-left: 0.75rem;
+  }
+
+  .xl\:mt-4 {
+    margin-top: 1rem;
+  }
+
+  .xl\:mr-4 {
+    margin-right: 1rem;
+  }
+
+  .xl\:mb-4 {
+    margin-bottom: 1rem;
+  }
+
+  .xl\:ml-4 {
+    margin-left: 1rem;
+  }
+
+  .xl\:mt-5 {
+    margin-top: 1.25rem;
+  }
+
+  .xl\:mr-5 {
+    margin-right: 1.25rem;
+  }
+
+  .xl\:mb-5 {
+    margin-bottom: 1.25rem;
+  }
+
+  .xl\:ml-5 {
+    margin-left: 1.25rem;
+  }
+
+  .xl\:mt-6 {
+    margin-top: 1.5rem;
+  }
+
+  .xl\:mr-6 {
+    margin-right: 1.5rem;
+  }
+
+  .xl\:mb-6 {
+    margin-bottom: 1.5rem;
+  }
+
+  .xl\:ml-6 {
+    margin-left: 1.5rem;
+  }
+
+  .xl\:mt-8 {
+    margin-top: 2rem;
+  }
+
+  .xl\:mr-8 {
+    margin-right: 2rem;
+  }
+
+  .xl\:mb-8 {
+    margin-bottom: 2rem;
+  }
+
+  .xl\:ml-8 {
+    margin-left: 2rem;
+  }
+
+  .xl\:mt-10 {
+    margin-top: 2.5rem;
+  }
+
+  .xl\:mr-10 {
+    margin-right: 2.5rem;
+  }
+
+  .xl\:mb-10 {
+    margin-bottom: 2.5rem;
+  }
+
+  .xl\:ml-10 {
+    margin-left: 2.5rem;
+  }
+
+  .xl\:mt-12 {
+    margin-top: 3rem;
+  }
+
+  .xl\:mr-12 {
+    margin-right: 3rem;
+  }
+
+  .xl\:mb-12 {
+    margin-bottom: 3rem;
+  }
+
+  .xl\:ml-12 {
+    margin-left: 3rem;
+  }
+
+  .xl\:mt-16 {
+    margin-top: 4rem;
+  }
+
+  .xl\:mr-16 {
+    margin-right: 4rem;
+  }
+
+  .xl\:mb-16 {
+    margin-bottom: 4rem;
+  }
+
+  .xl\:ml-16 {
+    margin-left: 4rem;
+  }
+
+  .xl\:mt-20 {
+    margin-top: 5rem;
+  }
+
+  .xl\:mr-20 {
+    margin-right: 5rem;
+  }
+
+  .xl\:mb-20 {
+    margin-bottom: 5rem;
+  }
+
+  .xl\:ml-20 {
+    margin-left: 5rem;
+  }
+
+  .xl\:mt-24 {
+    margin-top: 6rem;
+  }
+
+  .xl\:mr-24 {
+    margin-right: 6rem;
+  }
+
+  .xl\:mb-24 {
+    margin-bottom: 6rem;
+  }
+
+  .xl\:ml-24 {
+    margin-left: 6rem;
+  }
+
+  .xl\:mt-32 {
+    margin-top: 8rem;
+  }
+
+  .xl\:mr-32 {
+    margin-right: 8rem;
+  }
+
+  .xl\:mb-32 {
+    margin-bottom: 8rem;
+  }
+
+  .xl\:ml-32 {
+    margin-left: 8rem;
+  }
+
+  .xl\:mt-40 {
+    margin-top: 10rem;
+  }
+
+  .xl\:mr-40 {
+    margin-right: 10rem;
+  }
+
+  .xl\:mb-40 {
+    margin-bottom: 10rem;
+  }
+
+  .xl\:ml-40 {
+    margin-left: 10rem;
+  }
+
+  .xl\:mt-48 {
+    margin-top: 12rem;
+  }
+
+  .xl\:mr-48 {
+    margin-right: 12rem;
+  }
+
+  .xl\:mb-48 {
+    margin-bottom: 12rem;
+  }
+
+  .xl\:ml-48 {
+    margin-left: 12rem;
+  }
+
+  .xl\:mt-56 {
+    margin-top: 14rem;
+  }
+
+  .xl\:mr-56 {
+    margin-right: 14rem;
+  }
+
+  .xl\:mb-56 {
+    margin-bottom: 14rem;
+  }
+
+  .xl\:ml-56 {
+    margin-left: 14rem;
+  }
+
+  .xl\:mt-64 {
+    margin-top: 16rem;
+  }
+
+  .xl\:mr-64 {
+    margin-right: 16rem;
+  }
+
+  .xl\:mb-64 {
+    margin-bottom: 16rem;
+  }
+
+  .xl\:ml-64 {
+    margin-left: 16rem;
+  }
+
+  .xl\:mt-auto {
+    margin-top: auto;
+  }
+
+  .xl\:mr-auto {
+    margin-right: auto;
+  }
+
+  .xl\:mb-auto {
+    margin-bottom: auto;
+  }
+
+  .xl\:ml-auto {
+    margin-left: auto;
+  }
+
+  .xl\:mt-px {
+    margin-top: 1px;
+  }
+
+  .xl\:mr-px {
+    margin-right: 1px;
+  }
+
+  .xl\:mb-px {
+    margin-bottom: 1px;
+  }
+
+  .xl\:ml-px {
+    margin-left: 1px;
+  }
+
+  .xl\:-mt-1 {
+    margin-top: -0.25rem;
+  }
+
+  .xl\:-mr-1 {
+    margin-right: -0.25rem;
+  }
+
+  .xl\:-mb-1 {
+    margin-bottom: -0.25rem;
+  }
+
+  .xl\:-ml-1 {
+    margin-left: -0.25rem;
+  }
+
+  .xl\:-mt-2 {
+    margin-top: -0.5rem;
+  }
+
+  .xl\:-mr-2 {
+    margin-right: -0.5rem;
+  }
+
+  .xl\:-mb-2 {
+    margin-bottom: -0.5rem;
+  }
+
+  .xl\:-ml-2 {
+    margin-left: -0.5rem;
+  }
+
+  .xl\:-mt-3 {
+    margin-top: -0.75rem;
+  }
+
+  .xl\:-mr-3 {
+    margin-right: -0.75rem;
+  }
+
+  .xl\:-mb-3 {
+    margin-bottom: -0.75rem;
+  }
+
+  .xl\:-ml-3 {
+    margin-left: -0.75rem;
+  }
+
+  .xl\:-mt-4 {
+    margin-top: -1rem;
+  }
+
+  .xl\:-mr-4 {
+    margin-right: -1rem;
+  }
+
+  .xl\:-mb-4 {
+    margin-bottom: -1rem;
+  }
+
+  .xl\:-ml-4 {
+    margin-left: -1rem;
+  }
+
+  .xl\:-mt-5 {
+    margin-top: -1.25rem;
+  }
+
+  .xl\:-mr-5 {
+    margin-right: -1.25rem;
+  }
+
+  .xl\:-mb-5 {
+    margin-bottom: -1.25rem;
+  }
+
+  .xl\:-ml-5 {
+    margin-left: -1.25rem;
+  }
+
+  .xl\:-mt-6 {
+    margin-top: -1.5rem;
+  }
+
+  .xl\:-mr-6 {
+    margin-right: -1.5rem;
+  }
+
+  .xl\:-mb-6 {
+    margin-bottom: -1.5rem;
+  }
+
+  .xl\:-ml-6 {
+    margin-left: -1.5rem;
+  }
+
+  .xl\:-mt-8 {
+    margin-top: -2rem;
+  }
+
+  .xl\:-mr-8 {
+    margin-right: -2rem;
+  }
+
+  .xl\:-mb-8 {
+    margin-bottom: -2rem;
+  }
+
+  .xl\:-ml-8 {
+    margin-left: -2rem;
+  }
+
+  .xl\:-mt-10 {
+    margin-top: -2.5rem;
+  }
+
+  .xl\:-mr-10 {
+    margin-right: -2.5rem;
+  }
+
+  .xl\:-mb-10 {
+    margin-bottom: -2.5rem;
+  }
+
+  .xl\:-ml-10 {
+    margin-left: -2.5rem;
+  }
+
+  .xl\:-mt-12 {
+    margin-top: -3rem;
+  }
+
+  .xl\:-mr-12 {
+    margin-right: -3rem;
+  }
+
+  .xl\:-mb-12 {
+    margin-bottom: -3rem;
+  }
+
+  .xl\:-ml-12 {
+    margin-left: -3rem;
+  }
+
+  .xl\:-mt-16 {
+    margin-top: -4rem;
+  }
+
+  .xl\:-mr-16 {
+    margin-right: -4rem;
+  }
+
+  .xl\:-mb-16 {
+    margin-bottom: -4rem;
+  }
+
+  .xl\:-ml-16 {
+    margin-left: -4rem;
+  }
+
+  .xl\:-mt-20 {
+    margin-top: -5rem;
+  }
+
+  .xl\:-mr-20 {
+    margin-right: -5rem;
+  }
+
+  .xl\:-mb-20 {
+    margin-bottom: -5rem;
+  }
+
+  .xl\:-ml-20 {
+    margin-left: -5rem;
+  }
+
+  .xl\:-mt-24 {
+    margin-top: -6rem;
+  }
+
+  .xl\:-mr-24 {
+    margin-right: -6rem;
+  }
+
+  .xl\:-mb-24 {
+    margin-bottom: -6rem;
+  }
+
+  .xl\:-ml-24 {
+    margin-left: -6rem;
+  }
+
+  .xl\:-mt-32 {
+    margin-top: -8rem;
+  }
+
+  .xl\:-mr-32 {
+    margin-right: -8rem;
+  }
+
+  .xl\:-mb-32 {
+    margin-bottom: -8rem;
+  }
+
+  .xl\:-ml-32 {
+    margin-left: -8rem;
+  }
+
+  .xl\:-mt-40 {
+    margin-top: -10rem;
+  }
+
+  .xl\:-mr-40 {
+    margin-right: -10rem;
+  }
+
+  .xl\:-mb-40 {
+    margin-bottom: -10rem;
+  }
+
+  .xl\:-ml-40 {
+    margin-left: -10rem;
+  }
+
+  .xl\:-mt-48 {
+    margin-top: -12rem;
+  }
+
+  .xl\:-mr-48 {
+    margin-right: -12rem;
+  }
+
+  .xl\:-mb-48 {
+    margin-bottom: -12rem;
+  }
+
+  .xl\:-ml-48 {
+    margin-left: -12rem;
+  }
+
+  .xl\:-mt-56 {
+    margin-top: -14rem;
+  }
+
+  .xl\:-mr-56 {
+    margin-right: -14rem;
+  }
+
+  .xl\:-mb-56 {
+    margin-bottom: -14rem;
+  }
+
+  .xl\:-ml-56 {
+    margin-left: -14rem;
+  }
+
+  .xl\:-mt-64 {
+    margin-top: -16rem;
+  }
+
+  .xl\:-mr-64 {
+    margin-right: -16rem;
+  }
+
+  .xl\:-mb-64 {
+    margin-bottom: -16rem;
+  }
+
+  .xl\:-ml-64 {
+    margin-left: -16rem;
+  }
+
+  .xl\:-mt-px {
+    margin-top: -1px;
+  }
+
+  .xl\:-mr-px {
+    margin-right: -1px;
+  }
+
+  .xl\:-mb-px {
+    margin-bottom: -1px;
+  }
+
+  .xl\:-ml-px {
+    margin-left: -1px;
+  }
+
+  .xl\:max-h-full {
+    max-height: 100%;
+  }
+
+  .xl\:max-h-screen {
+    max-height: 100vh;
+  }
+
+  .xl\:max-w-none {
+    max-width: none;
+  }
+
+  .xl\:max-w-xs {
+    max-width: 20rem;
+  }
+
+  .xl\:max-w-sm {
+    max-width: 24rem;
+  }
+
+  .xl\:max-w-md {
+    max-width: 28rem;
+  }
+
+  .xl\:max-w-lg {
+    max-width: 32rem;
+  }
+
+  .xl\:max-w-xl {
+    max-width: 36rem;
+  }
+
+  .xl\:max-w-2xl {
+    max-width: 42rem;
+  }
+
+  .xl\:max-w-3xl {
+    max-width: 48rem;
+  }
+
+  .xl\:max-w-4xl {
+    max-width: 56rem;
+  }
+
+  .xl\:max-w-5xl {
+    max-width: 64rem;
+  }
+
+  .xl\:max-w-6xl {
+    max-width: 72rem;
+  }
+
+  .xl\:max-w-full {
+    max-width: 100%;
+  }
+
+  .xl\:max-w-screen-sm {
+    max-width: 640px;
+  }
+
+  .xl\:max-w-screen-md {
+    max-width: 768px;
+  }
+
+  .xl\:max-w-screen-lg {
+    max-width: 1024px;
+  }
+
+  .xl\:max-w-screen-xl {
+    max-width: 1280px;
+  }
+
+  .xl\:min-h-0 {
+    min-height: 0;
+  }
+
+  .xl\:min-h-full {
+    min-height: 100%;
+  }
+
+  .xl\:min-h-screen {
+    min-height: 100vh;
+  }
+
+  .xl\:min-w-0 {
+    min-width: 0;
+  }
+
+  .xl\:min-w-full {
+    min-width: 100%;
+  }
+
+  .xl\:object-contain {
+    -o-object-fit: contain;
+       object-fit: contain;
+  }
+
+  .xl\:object-cover {
+    -o-object-fit: cover;
+       object-fit: cover;
+  }
+
+  .xl\:object-fill {
+    -o-object-fit: fill;
+       object-fit: fill;
+  }
+
+  .xl\:object-none {
+    -o-object-fit: none;
+       object-fit: none;
+  }
+
+  .xl\:object-scale-down {
+    -o-object-fit: scale-down;
+       object-fit: scale-down;
+  }
+
+  .xl\:object-bottom {
+    -o-object-position: bottom;
+       object-position: bottom;
+  }
+
+  .xl\:object-center {
+    -o-object-position: center;
+       object-position: center;
+  }
+
+  .xl\:object-left {
+    -o-object-position: left;
+       object-position: left;
+  }
+
+  .xl\:object-left-bottom {
+    -o-object-position: left bottom;
+       object-position: left bottom;
+  }
+
+  .xl\:object-left-top {
+    -o-object-position: left top;
+       object-position: left top;
+  }
+
+  .xl\:object-right {
+    -o-object-position: right;
+       object-position: right;
+  }
+
+  .xl\:object-right-bottom {
+    -o-object-position: right bottom;
+       object-position: right bottom;
+  }
+
+  .xl\:object-right-top {
+    -o-object-position: right top;
+       object-position: right top;
+  }
+
+  .xl\:object-top {
+    -o-object-position: top;
+       object-position: top;
+  }
+
+  .xl\:opacity-0 {
+    opacity: 0;
+  }
+
+  .xl\:opacity-25 {
+    opacity: 0.25;
+  }
+
+  .xl\:opacity-50 {
+    opacity: 0.5;
+  }
+
+  .xl\:opacity-75 {
+    opacity: 0.75;
+  }
+
+  .xl\:opacity-100 {
+    opacity: 1;
+  }
+
+  .xl\:hover\:opacity-0:hover {
+    opacity: 0;
+  }
+
+  .xl\:hover\:opacity-25:hover {
+    opacity: 0.25;
+  }
+
+  .xl\:hover\:opacity-50:hover {
+    opacity: 0.5;
+  }
+
+  .xl\:hover\:opacity-75:hover {
+    opacity: 0.75;
+  }
+
+  .xl\:hover\:opacity-100:hover {
+    opacity: 1;
+  }
+
+  .xl\:focus\:opacity-0:focus {
+    opacity: 0;
+  }
+
+  .xl\:focus\:opacity-25:focus {
+    opacity: 0.25;
+  }
+
+  .xl\:focus\:opacity-50:focus {
+    opacity: 0.5;
+  }
+
+  .xl\:focus\:opacity-75:focus {
+    opacity: 0.75;
+  }
+
+  .xl\:focus\:opacity-100:focus {
+    opacity: 1;
+  }
+
+  .xl\:outline-none {
+    outline: 0;
+  }
+
+  .xl\:focus\:outline-none:focus {
+    outline: 0;
+  }
+
+  .xl\:overflow-auto {
+    overflow: auto;
+  }
+
+  .xl\:overflow-hidden {
+    overflow: hidden;
+  }
+
+  .xl\:overflow-visible {
+    overflow: visible;
+  }
+
+  .xl\:overflow-scroll {
+    overflow: scroll;
+  }
+
+  .xl\:overflow-x-auto {
+    overflow-x: auto;
+  }
+
+  .xl\:overflow-y-auto {
+    overflow-y: auto;
+  }
+
+  .xl\:overflow-x-hidden {
+    overflow-x: hidden;
+  }
+
+  .xl\:overflow-y-hidden {
+    overflow-y: hidden;
+  }
+
+  .xl\:overflow-x-visible {
+    overflow-x: visible;
+  }
+
+  .xl\:overflow-y-visible {
+    overflow-y: visible;
+  }
+
+  .xl\:overflow-x-scroll {
+    overflow-x: scroll;
+  }
+
+  .xl\:overflow-y-scroll {
+    overflow-y: scroll;
+  }
+
+  .xl\:scrolling-touch {
+    -webkit-overflow-scrolling: touch;
+  }
+
+  .xl\:scrolling-auto {
+    -webkit-overflow-scrolling: auto;
+  }
+
+  .xl\:overscroll-auto {
+    -ms-scroll-chaining: chained;
+        overscroll-behavior: auto;
+  }
+
+  .xl\:overscroll-contain {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: contain;
+  }
+
+  .xl\:overscroll-none {
+    -ms-scroll-chaining: none;
+        overscroll-behavior: none;
+  }
+
+  .xl\:overscroll-y-auto {
+    overscroll-behavior-y: auto;
+  }
+
+  .xl\:overscroll-y-contain {
+    overscroll-behavior-y: contain;
+  }
+
+  .xl\:overscroll-y-none {
+    overscroll-behavior-y: none;
+  }
+
+  .xl\:overscroll-x-auto {
+    overscroll-behavior-x: auto;
+  }
+
+  .xl\:overscroll-x-contain {
+    overscroll-behavior-x: contain;
+  }
+
+  .xl\:overscroll-x-none {
+    overscroll-behavior-x: none;
+  }
+
+  .xl\:p-0 {
+    padding: 0;
+  }
+
+  .xl\:p-1 {
+    padding: 0.25rem;
+  }
+
+  .xl\:p-2 {
+    padding: 0.5rem;
+  }
+
+  .xl\:p-3 {
+    padding: 0.75rem;
+  }
+
+  .xl\:p-4 {
+    padding: 1rem;
+  }
+
+  .xl\:p-5 {
+    padding: 1.25rem;
+  }
+
+  .xl\:p-6 {
+    padding: 1.5rem;
+  }
+
+  .xl\:p-8 {
+    padding: 2rem;
+  }
+
+  .xl\:p-10 {
+    padding: 2.5rem;
+  }
+
+  .xl\:p-12 {
+    padding: 3rem;
+  }
+
+  .xl\:p-16 {
+    padding: 4rem;
+  }
+
+  .xl\:p-20 {
+    padding: 5rem;
+  }
+
+  .xl\:p-24 {
+    padding: 6rem;
+  }
+
+  .xl\:p-32 {
+    padding: 8rem;
+  }
+
+  .xl\:p-40 {
+    padding: 10rem;
+  }
+
+  .xl\:p-48 {
+    padding: 12rem;
+  }
+
+  .xl\:p-56 {
+    padding: 14rem;
+  }
+
+  .xl\:p-64 {
+    padding: 16rem;
+  }
+
+  .xl\:p-px {
+    padding: 1px;
+  }
+
+  .xl\:py-0 {
+    padding-top: 0;
+    padding-bottom: 0;
+  }
+
+  .xl\:px-0 {
+    padding-left: 0;
+    padding-right: 0;
+  }
+
+  .xl\:py-1 {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+  }
+
+  .xl\:px-1 {
+    padding-left: 0.25rem;
+    padding-right: 0.25rem;
+  }
+
+  .xl\:py-2 {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
+
+  .xl\:px-2 {
+    padding-left: 0.5rem;
+    padding-right: 0.5rem;
+  }
+
+  .xl\:py-3 {
+    padding-top: 0.75rem;
+    padding-bottom: 0.75rem;
+  }
+
+  .xl\:px-3 {
+    padding-left: 0.75rem;
+    padding-right: 0.75rem;
+  }
+
+  .xl\:py-4 {
+    padding-top: 1rem;
+    padding-bottom: 1rem;
+  }
+
+  .xl\:px-4 {
+    padding-left: 1rem;
+    padding-right: 1rem;
+  }
+
+  .xl\:py-5 {
+    padding-top: 1.25rem;
+    padding-bottom: 1.25rem;
+  }
+
+  .xl\:px-5 {
+    padding-left: 1.25rem;
+    padding-right: 1.25rem;
+  }
+
+  .xl\:py-6 {
+    padding-top: 1.5rem;
+    padding-bottom: 1.5rem;
+  }
+
+  .xl\:px-6 {
+    padding-left: 1.5rem;
+    padding-right: 1.5rem;
+  }
+
+  .xl\:py-8 {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
+
+  .xl\:px-8 {
+    padding-left: 2rem;
+    padding-right: 2rem;
+  }
+
+  .xl\:py-10 {
+    padding-top: 2.5rem;
+    padding-bottom: 2.5rem;
+  }
+
+  .xl\:px-10 {
+    padding-left: 2.5rem;
+    padding-right: 2.5rem;
+  }
+
+  .xl\:py-12 {
+    padding-top: 3rem;
+    padding-bottom: 3rem;
+  }
+
+  .xl\:px-12 {
+    padding-left: 3rem;
+    padding-right: 3rem;
+  }
+
+  .xl\:py-16 {
+    padding-top: 4rem;
+    padding-bottom: 4rem;
+  }
+
+  .xl\:px-16 {
+    padding-left: 4rem;
+    padding-right: 4rem;
+  }
+
+  .xl\:py-20 {
+    padding-top: 5rem;
+    padding-bottom: 5rem;
+  }
+
+  .xl\:px-20 {
+    padding-left: 5rem;
+    padding-right: 5rem;
+  }
+
+  .xl\:py-24 {
+    padding-top: 6rem;
+    padding-bottom: 6rem;
+  }
+
+  .xl\:px-24 {
+    padding-left: 6rem;
+    padding-right: 6rem;
+  }
+
+  .xl\:py-32 {
+    padding-top: 8rem;
+    padding-bottom: 8rem;
+  }
+
+  .xl\:px-32 {
+    padding-left: 8rem;
+    padding-right: 8rem;
+  }
+
+  .xl\:py-40 {
+    padding-top: 10rem;
+    padding-bottom: 10rem;
+  }
+
+  .xl\:px-40 {
+    padding-left: 10rem;
+    padding-right: 10rem;
+  }
+
+  .xl\:py-48 {
+    padding-top: 12rem;
+    padding-bottom: 12rem;
+  }
+
+  .xl\:px-48 {
+    padding-left: 12rem;
+    padding-right: 12rem;
+  }
+
+  .xl\:py-56 {
+    padding-top: 14rem;
+    padding-bottom: 14rem;
+  }
+
+  .xl\:px-56 {
+    padding-left: 14rem;
+    padding-right: 14rem;
+  }
+
+  .xl\:py-64 {
+    padding-top: 16rem;
+    padding-bottom: 16rem;
+  }
+
+  .xl\:px-64 {
+    padding-left: 16rem;
+    padding-right: 16rem;
+  }
+
+  .xl\:py-px {
+    padding-top: 1px;
+    padding-bottom: 1px;
+  }
+
+  .xl\:px-px {
+    padding-left: 1px;
+    padding-right: 1px;
+  }
+
+  .xl\:pt-0 {
+    padding-top: 0;
+  }
+
+  .xl\:pr-0 {
+    padding-right: 0;
+  }
+
+  .xl\:pb-0 {
+    padding-bottom: 0;
+  }
+
+  .xl\:pl-0 {
+    padding-left: 0;
+  }
+
+  .xl\:pt-1 {
+    padding-top: 0.25rem;
+  }
+
+  .xl\:pr-1 {
+    padding-right: 0.25rem;
+  }
+
+  .xl\:pb-1 {
+    padding-bottom: 0.25rem;
+  }
+
+  .xl\:pl-1 {
+    padding-left: 0.25rem;
+  }
+
+  .xl\:pt-2 {
+    padding-top: 0.5rem;
+  }
+
+  .xl\:pr-2 {
+    padding-right: 0.5rem;
+  }
+
+  .xl\:pb-2 {
+    padding-bottom: 0.5rem;
+  }
+
+  .xl\:pl-2 {
+    padding-left: 0.5rem;
+  }
+
+  .xl\:pt-3 {
+    padding-top: 0.75rem;
+  }
+
+  .xl\:pr-3 {
+    padding-right: 0.75rem;
+  }
+
+  .xl\:pb-3 {
+    padding-bottom: 0.75rem;
+  }
+
+  .xl\:pl-3 {
+    padding-left: 0.75rem;
+  }
+
+  .xl\:pt-4 {
+    padding-top: 1rem;
+  }
+
+  .xl\:pr-4 {
+    padding-right: 1rem;
+  }
+
+  .xl\:pb-4 {
+    padding-bottom: 1rem;
+  }
+
+  .xl\:pl-4 {
+    padding-left: 1rem;
+  }
+
+  .xl\:pt-5 {
+    padding-top: 1.25rem;
+  }
+
+  .xl\:pr-5 {
+    padding-right: 1.25rem;
+  }
+
+  .xl\:pb-5 {
+    padding-bottom: 1.25rem;
+  }
+
+  .xl\:pl-5 {
+    padding-left: 1.25rem;
+  }
+
+  .xl\:pt-6 {
+    padding-top: 1.5rem;
+  }
+
+  .xl\:pr-6 {
+    padding-right: 1.5rem;
+  }
+
+  .xl\:pb-6 {
+    padding-bottom: 1.5rem;
+  }
+
+  .xl\:pl-6 {
+    padding-left: 1.5rem;
+  }
+
+  .xl\:pt-8 {
+    padding-top: 2rem;
+  }
+
+  .xl\:pr-8 {
+    padding-right: 2rem;
+  }
+
+  .xl\:pb-8 {
+    padding-bottom: 2rem;
+  }
+
+  .xl\:pl-8 {
+    padding-left: 2rem;
+  }
+
+  .xl\:pt-10 {
+    padding-top: 2.5rem;
+  }
+
+  .xl\:pr-10 {
+    padding-right: 2.5rem;
+  }
+
+  .xl\:pb-10 {
+    padding-bottom: 2.5rem;
+  }
+
+  .xl\:pl-10 {
+    padding-left: 2.5rem;
+  }
+
+  .xl\:pt-12 {
+    padding-top: 3rem;
+  }
+
+  .xl\:pr-12 {
+    padding-right: 3rem;
+  }
+
+  .xl\:pb-12 {
+    padding-bottom: 3rem;
+  }
+
+  .xl\:pl-12 {
+    padding-left: 3rem;
+  }
+
+  .xl\:pt-16 {
+    padding-top: 4rem;
+  }
+
+  .xl\:pr-16 {
+    padding-right: 4rem;
+  }
+
+  .xl\:pb-16 {
+    padding-bottom: 4rem;
+  }
+
+  .xl\:pl-16 {
+    padding-left: 4rem;
+  }
+
+  .xl\:pt-20 {
+    padding-top: 5rem;
+  }
+
+  .xl\:pr-20 {
+    padding-right: 5rem;
+  }
+
+  .xl\:pb-20 {
+    padding-bottom: 5rem;
+  }
+
+  .xl\:pl-20 {
+    padding-left: 5rem;
+  }
+
+  .xl\:pt-24 {
+    padding-top: 6rem;
+  }
+
+  .xl\:pr-24 {
+    padding-right: 6rem;
+  }
+
+  .xl\:pb-24 {
+    padding-bottom: 6rem;
+  }
+
+  .xl\:pl-24 {
+    padding-left: 6rem;
+  }
+
+  .xl\:pt-32 {
+    padding-top: 8rem;
+  }
+
+  .xl\:pr-32 {
+    padding-right: 8rem;
+  }
+
+  .xl\:pb-32 {
+    padding-bottom: 8rem;
+  }
+
+  .xl\:pl-32 {
+    padding-left: 8rem;
+  }
+
+  .xl\:pt-40 {
+    padding-top: 10rem;
+  }
+
+  .xl\:pr-40 {
+    padding-right: 10rem;
+  }
+
+  .xl\:pb-40 {
+    padding-bottom: 10rem;
+  }
+
+  .xl\:pl-40 {
+    padding-left: 10rem;
+  }
+
+  .xl\:pt-48 {
+    padding-top: 12rem;
+  }
+
+  .xl\:pr-48 {
+    padding-right: 12rem;
+  }
+
+  .xl\:pb-48 {
+    padding-bottom: 12rem;
+  }
+
+  .xl\:pl-48 {
+    padding-left: 12rem;
+  }
+
+  .xl\:pt-56 {
+    padding-top: 14rem;
+  }
+
+  .xl\:pr-56 {
+    padding-right: 14rem;
+  }
+
+  .xl\:pb-56 {
+    padding-bottom: 14rem;
+  }
+
+  .xl\:pl-56 {
+    padding-left: 14rem;
+  }
+
+  .xl\:pt-64 {
+    padding-top: 16rem;
+  }
+
+  .xl\:pr-64 {
+    padding-right: 16rem;
+  }
+
+  .xl\:pb-64 {
+    padding-bottom: 16rem;
+  }
+
+  .xl\:pl-64 {
+    padding-left: 16rem;
+  }
+
+  .xl\:pt-px {
+    padding-top: 1px;
+  }
+
+  .xl\:pr-px {
+    padding-right: 1px;
+  }
+
+  .xl\:pb-px {
+    padding-bottom: 1px;
+  }
+
+  .xl\:pl-px {
+    padding-left: 1px;
+  }
+
+  .xl\:placeholder-transparent::-moz-placeholder {
+    color: transparent;
+  }
+
+  .xl\:placeholder-transparent:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .xl\:placeholder-transparent::placeholder {
+    color: transparent;
+  }
+
+  .xl\:placeholder-current::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .xl\:placeholder-current:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .xl\:placeholder-current::placeholder {
+    color: currentColor;
+  }
+
+  .xl\:placeholder-black::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-black:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-black::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-white::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-white:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-white::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-gray-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-red-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-orange-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-yellow-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-green-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-teal-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-blue-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-indigo-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-purple-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-100::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-200::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-200:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-200::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-300::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-300:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-300::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-400::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-400:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-400::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-500::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-500:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-500::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-600::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-600:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-600::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-700::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-700:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-700::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-800::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-800:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-800::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-900::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-900:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-pink-900::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-transparent:focus::-moz-placeholder {
+    color: transparent;
+  }
+
+  .xl\:focus\:placeholder-transparent:focus:-ms-input-placeholder {
+    color: transparent;
+  }
+
+  .xl\:focus\:placeholder-transparent:focus::placeholder {
+    color: transparent;
+  }
+
+  .xl\:focus\:placeholder-current:focus::-moz-placeholder {
+    color: currentColor;
+  }
+
+  .xl\:focus\:placeholder-current:focus:-ms-input-placeholder {
+    color: currentColor;
+  }
+
+  .xl\:focus\:placeholder-current:focus::placeholder {
+    color: currentColor;
+  }
+
+  .xl\:focus\:placeholder-black:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-black:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-black:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-white:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-white:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-white:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-gray-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-red-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-orange-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-yellow-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-green-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-teal-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-blue-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-indigo-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-purple-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-100:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-200:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-200:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-200:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-300:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-300:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-300:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-400:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-400:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-400:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-500:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-500:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-500:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-600:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-600:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-600:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-700:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-700:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-700:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-800:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-800:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-800:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-900:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-900:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:focus\:placeholder-pink-900:focus::placeholder {
+    --placeholder-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--placeholder-opacity));
+  }
+
+  .xl\:placeholder-opacity-0::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:placeholder-opacity-0:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:placeholder-opacity-0::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:placeholder-opacity-25::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:placeholder-opacity-25:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:placeholder-opacity-25::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:placeholder-opacity-50::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:placeholder-opacity-50:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:placeholder-opacity-50::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:placeholder-opacity-75::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:placeholder-opacity-75:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:placeholder-opacity-75::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:placeholder-opacity-100::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:placeholder-opacity-100:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:placeholder-opacity-100::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:focus\:placeholder-opacity-0:focus::-moz-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:focus\:placeholder-opacity-0:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:focus\:placeholder-opacity-0:focus::placeholder {
+    --placeholder-opacity: 0;
+  }
+
+  .xl\:focus\:placeholder-opacity-25:focus::-moz-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:focus\:placeholder-opacity-25:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:focus\:placeholder-opacity-25:focus::placeholder {
+    --placeholder-opacity: 0.25;
+  }
+
+  .xl\:focus\:placeholder-opacity-50:focus::-moz-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:focus\:placeholder-opacity-50:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:focus\:placeholder-opacity-50:focus::placeholder {
+    --placeholder-opacity: 0.5;
+  }
+
+  .xl\:focus\:placeholder-opacity-75:focus::-moz-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:focus\:placeholder-opacity-75:focus:-ms-input-placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:focus\:placeholder-opacity-75:focus::placeholder {
+    --placeholder-opacity: 0.75;
+  }
+
+  .xl\:focus\:placeholder-opacity-100:focus::-moz-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:focus\:placeholder-opacity-100:focus:-ms-input-placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:focus\:placeholder-opacity-100:focus::placeholder {
+    --placeholder-opacity: 1;
+  }
+
+  .xl\:pointer-events-none {
+    pointer-events: none;
+  }
+
+  .xl\:pointer-events-auto {
+    pointer-events: auto;
+  }
+
+  .xl\:static {
+    position: static;
+  }
+
+  .xl\:fixed {
+    position: fixed;
+  }
+
+  .xl\:absolute {
+    position: absolute;
+  }
+
+  .xl\:relative {
+    position: relative;
+  }
+
+  .xl\:sticky {
+    position: -webkit-sticky;
+    position: sticky;
+  }
+
+  .xl\:inset-0 {
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+  }
+
+  .xl\:inset-auto {
+    top: auto;
+    right: auto;
+    bottom: auto;
+    left: auto;
+  }
+
+  .xl\:inset-y-0 {
+    top: 0;
+    bottom: 0;
+  }
+
+  .xl\:inset-x-0 {
+    right: 0;
+    left: 0;
+  }
+
+  .xl\:inset-y-auto {
+    top: auto;
+    bottom: auto;
+  }
+
+  .xl\:inset-x-auto {
+    right: auto;
+    left: auto;
+  }
+
+  .xl\:top-0 {
+    top: 0;
+  }
+
+  .xl\:right-0 {
+    right: 0;
+  }
+
+  .xl\:bottom-0 {
+    bottom: 0;
+  }
+
+  .xl\:left-0 {
+    left: 0;
+  }
+
+  .xl\:top-auto {
+    top: auto;
+  }
+
+  .xl\:right-auto {
+    right: auto;
+  }
+
+  .xl\:bottom-auto {
+    bottom: auto;
+  }
+
+  .xl\:left-auto {
+    left: auto;
+  }
+
+  .xl\:resize-none {
+    resize: none;
+  }
+
+  .xl\:resize-y {
+    resize: vertical;
+  }
+
+  .xl\:resize-x {
+    resize: horizontal;
+  }
+
+  .xl\:resize {
+    resize: both;
+  }
+
+  .xl\:shadow-xs {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:shadow-sm {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:shadow {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:shadow-md {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:shadow-lg {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:shadow-xl {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .xl\:shadow-2xl {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .xl\:shadow-inner {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:shadow-outline {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .xl\:shadow-none {
+    box-shadow: none;
+  }
+
+  .xl\:hover\:shadow-xs:hover {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:hover\:shadow-sm:hover {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:hover\:shadow:hover {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:hover\:shadow-md:hover {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:hover\:shadow-lg:hover {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:hover\:shadow-xl:hover {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .xl\:hover\:shadow-2xl:hover {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .xl\:hover\:shadow-inner:hover {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:hover\:shadow-outline:hover {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .xl\:hover\:shadow-none:hover {
+    box-shadow: none;
+  }
+
+  .xl\:focus\:shadow-xs:focus {
+    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:focus\:shadow-sm:focus {
+    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:focus\:shadow:focus {
+    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:focus\:shadow-md:focus {
+    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:focus\:shadow-lg:focus {
+    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+  }
+
+  .xl\:focus\:shadow-xl:focus {
+    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+  }
+
+  .xl\:focus\:shadow-2xl:focus {
+    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
+  }
+
+  .xl\:focus\:shadow-inner:focus {
+    box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
+  }
+
+  .xl\:focus\:shadow-outline:focus {
+    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
+  }
+
+  .xl\:focus\:shadow-none:focus {
+    box-shadow: none;
+  }
+
+  .xl\:fill-current {
+    fill: currentColor;
+  }
+
+  .xl\:stroke-current {
+    stroke: currentColor;
+  }
+
+  .xl\:stroke-0 {
+    stroke-width: 0;
+  }
+
+  .xl\:stroke-1 {
+    stroke-width: 1;
+  }
+
+  .xl\:stroke-2 {
+    stroke-width: 2;
+  }
+
+  .xl\:table-auto {
+    table-layout: auto;
+  }
+
+  .xl\:table-fixed {
+    table-layout: fixed;
+  }
+
+  .xl\:text-left {
+    text-align: left;
+  }
+
+  .xl\:text-center {
+    text-align: center;
+  }
+
+  .xl\:text-right {
+    text-align: right;
+  }
+
+  .xl\:text-justify {
+    text-align: justify;
+  }
+
+  .xl\:text-transparent {
+    color: transparent;
+  }
+
+  .xl\:text-current {
+    color: currentColor;
+  }
+
+  .xl\:text-black {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .xl\:text-white {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .xl\:text-gray-100 {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .xl\:text-gray-200 {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .xl\:text-gray-300 {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .xl\:text-gray-400 {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .xl\:text-gray-500 {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .xl\:text-gray-600 {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .xl\:text-gray-700 {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .xl\:text-gray-800 {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .xl\:text-gray-900 {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .xl\:text-red-100 {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .xl\:text-red-200 {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .xl\:text-red-300 {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .xl\:text-red-400 {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .xl\:text-red-500 {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .xl\:text-red-600 {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .xl\:text-red-700 {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .xl\:text-red-800 {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .xl\:text-red-900 {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .xl\:text-orange-100 {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .xl\:text-orange-200 {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .xl\:text-orange-300 {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .xl\:text-orange-400 {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .xl\:text-orange-500 {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .xl\:text-orange-600 {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .xl\:text-orange-700 {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .xl\:text-orange-800 {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .xl\:text-orange-900 {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-100 {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-200 {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-300 {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-400 {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-500 {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-600 {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-700 {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-800 {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .xl\:text-yellow-900 {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .xl\:text-green-100 {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .xl\:text-green-200 {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .xl\:text-green-300 {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .xl\:text-green-400 {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .xl\:text-green-500 {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .xl\:text-green-600 {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .xl\:text-green-700 {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .xl\:text-green-800 {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .xl\:text-green-900 {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .xl\:text-teal-100 {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .xl\:text-teal-200 {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .xl\:text-teal-300 {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .xl\:text-teal-400 {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .xl\:text-teal-500 {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .xl\:text-teal-600 {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .xl\:text-teal-700 {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .xl\:text-teal-800 {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .xl\:text-teal-900 {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .xl\:text-blue-100 {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .xl\:text-blue-200 {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .xl\:text-blue-300 {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .xl\:text-blue-400 {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .xl\:text-blue-500 {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .xl\:text-blue-600 {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .xl\:text-blue-700 {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .xl\:text-blue-800 {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .xl\:text-blue-900 {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-100 {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-200 {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-300 {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-400 {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-500 {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-600 {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-700 {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-800 {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .xl\:text-indigo-900 {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .xl\:text-purple-100 {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .xl\:text-purple-200 {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .xl\:text-purple-300 {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .xl\:text-purple-400 {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .xl\:text-purple-500 {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .xl\:text-purple-600 {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .xl\:text-purple-700 {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .xl\:text-purple-800 {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .xl\:text-purple-900 {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .xl\:text-pink-100 {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .xl\:text-pink-200 {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .xl\:text-pink-300 {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .xl\:text-pink-400 {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .xl\:text-pink-500 {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .xl\:text-pink-600 {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .xl\:text-pink-700 {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .xl\:text-pink-800 {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .xl\:text-pink-900 {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-transparent:hover {
+    color: transparent;
+  }
+
+  .xl\:hover\:text-current:hover {
+    color: currentColor;
+  }
+
+  .xl\:hover\:text-black:hover {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-white:hover {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-100:hover {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-200:hover {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-300:hover {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-400:hover {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-500:hover {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-600:hover {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-700:hover {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-800:hover {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-gray-900:hover {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-100:hover {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-200:hover {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-300:hover {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-400:hover {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-500:hover {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-600:hover {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-700:hover {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-800:hover {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-red-900:hover {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-100:hover {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-200:hover {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-300:hover {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-400:hover {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-500:hover {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-600:hover {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-700:hover {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-800:hover {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-orange-900:hover {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-100:hover {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-200:hover {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-300:hover {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-400:hover {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-500:hover {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-600:hover {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-700:hover {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-800:hover {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-yellow-900:hover {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-100:hover {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-200:hover {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-300:hover {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-400:hover {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-500:hover {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-600:hover {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-700:hover {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-800:hover {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-green-900:hover {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-100:hover {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-200:hover {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-300:hover {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-400:hover {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-500:hover {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-600:hover {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-700:hover {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-800:hover {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-teal-900:hover {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-100:hover {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-200:hover {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-300:hover {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-400:hover {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-500:hover {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-600:hover {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-700:hover {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-800:hover {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-blue-900:hover {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-100:hover {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-200:hover {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-300:hover {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-400:hover {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-500:hover {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-600:hover {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-700:hover {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-800:hover {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-indigo-900:hover {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-100:hover {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-200:hover {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-300:hover {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-400:hover {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-500:hover {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-600:hover {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-700:hover {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-800:hover {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-purple-900:hover {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-100:hover {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-200:hover {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-300:hover {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-400:hover {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-500:hover {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-600:hover {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-700:hover {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-800:hover {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .xl\:hover\:text-pink-900:hover {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-transparent:focus {
+    color: transparent;
+  }
+
+  .xl\:focus\:text-current:focus {
+    color: currentColor;
+  }
+
+  .xl\:focus\:text-black:focus {
+    --text-opacity: 1;
+    color: #000;
+    color: rgba(0, 0, 0, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-white:focus {
+    --text-opacity: 1;
+    color: #fff;
+    color: rgba(255, 255, 255, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-100:focus {
+    --text-opacity: 1;
+    color: #f7fafc;
+    color: rgba(247, 250, 252, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-200:focus {
+    --text-opacity: 1;
+    color: #edf2f7;
+    color: rgba(237, 242, 247, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-300:focus {
+    --text-opacity: 1;
+    color: #e2e8f0;
+    color: rgba(226, 232, 240, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-400:focus {
+    --text-opacity: 1;
+    color: #cbd5e0;
+    color: rgba(203, 213, 224, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-500:focus {
+    --text-opacity: 1;
+    color: #a0aec0;
+    color: rgba(160, 174, 192, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-600:focus {
+    --text-opacity: 1;
+    color: #718096;
+    color: rgba(113, 128, 150, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-700:focus {
+    --text-opacity: 1;
+    color: #4a5568;
+    color: rgba(74, 85, 104, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-800:focus {
+    --text-opacity: 1;
+    color: #2d3748;
+    color: rgba(45, 55, 72, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-gray-900:focus {
+    --text-opacity: 1;
+    color: #1a202c;
+    color: rgba(26, 32, 44, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-100:focus {
+    --text-opacity: 1;
+    color: #fff5f5;
+    color: rgba(255, 245, 245, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-200:focus {
+    --text-opacity: 1;
+    color: #fed7d7;
+    color: rgba(254, 215, 215, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-300:focus {
+    --text-opacity: 1;
+    color: #feb2b2;
+    color: rgba(254, 178, 178, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-400:focus {
+    --text-opacity: 1;
+    color: #fc8181;
+    color: rgba(252, 129, 129, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-500:focus {
+    --text-opacity: 1;
+    color: #f56565;
+    color: rgba(245, 101, 101, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-600:focus {
+    --text-opacity: 1;
+    color: #e53e3e;
+    color: rgba(229, 62, 62, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-700:focus {
+    --text-opacity: 1;
+    color: #c53030;
+    color: rgba(197, 48, 48, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-800:focus {
+    --text-opacity: 1;
+    color: #9b2c2c;
+    color: rgba(155, 44, 44, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-red-900:focus {
+    --text-opacity: 1;
+    color: #742a2a;
+    color: rgba(116, 42, 42, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-100:focus {
+    --text-opacity: 1;
+    color: #fffaf0;
+    color: rgba(255, 250, 240, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-200:focus {
+    --text-opacity: 1;
+    color: #feebc8;
+    color: rgba(254, 235, 200, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-300:focus {
+    --text-opacity: 1;
+    color: #fbd38d;
+    color: rgba(251, 211, 141, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-400:focus {
+    --text-opacity: 1;
+    color: #f6ad55;
+    color: rgba(246, 173, 85, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-500:focus {
+    --text-opacity: 1;
+    color: #ed8936;
+    color: rgba(237, 137, 54, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-600:focus {
+    --text-opacity: 1;
+    color: #dd6b20;
+    color: rgba(221, 107, 32, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-700:focus {
+    --text-opacity: 1;
+    color: #c05621;
+    color: rgba(192, 86, 33, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-800:focus {
+    --text-opacity: 1;
+    color: #9c4221;
+    color: rgba(156, 66, 33, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-orange-900:focus {
+    --text-opacity: 1;
+    color: #7b341e;
+    color: rgba(123, 52, 30, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-100:focus {
+    --text-opacity: 1;
+    color: #fffff0;
+    color: rgba(255, 255, 240, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-200:focus {
+    --text-opacity: 1;
+    color: #fefcbf;
+    color: rgba(254, 252, 191, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-300:focus {
+    --text-opacity: 1;
+    color: #faf089;
+    color: rgba(250, 240, 137, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-400:focus {
+    --text-opacity: 1;
+    color: #f6e05e;
+    color: rgba(246, 224, 94, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-500:focus {
+    --text-opacity: 1;
+    color: #ecc94b;
+    color: rgba(236, 201, 75, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-600:focus {
+    --text-opacity: 1;
+    color: #d69e2e;
+    color: rgba(214, 158, 46, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-700:focus {
+    --text-opacity: 1;
+    color: #b7791f;
+    color: rgba(183, 121, 31, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-800:focus {
+    --text-opacity: 1;
+    color: #975a16;
+    color: rgba(151, 90, 22, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-yellow-900:focus {
+    --text-opacity: 1;
+    color: #744210;
+    color: rgba(116, 66, 16, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-100:focus {
+    --text-opacity: 1;
+    color: #f0fff4;
+    color: rgba(240, 255, 244, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-200:focus {
+    --text-opacity: 1;
+    color: #c6f6d5;
+    color: rgba(198, 246, 213, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-300:focus {
+    --text-opacity: 1;
+    color: #9ae6b4;
+    color: rgba(154, 230, 180, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-400:focus {
+    --text-opacity: 1;
+    color: #68d391;
+    color: rgba(104, 211, 145, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-500:focus {
+    --text-opacity: 1;
+    color: #48bb78;
+    color: rgba(72, 187, 120, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-600:focus {
+    --text-opacity: 1;
+    color: #38a169;
+    color: rgba(56, 161, 105, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-700:focus {
+    --text-opacity: 1;
+    color: #2f855a;
+    color: rgba(47, 133, 90, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-800:focus {
+    --text-opacity: 1;
+    color: #276749;
+    color: rgba(39, 103, 73, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-green-900:focus {
+    --text-opacity: 1;
+    color: #22543d;
+    color: rgba(34, 84, 61, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-100:focus {
+    --text-opacity: 1;
+    color: #e6fffa;
+    color: rgba(230, 255, 250, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-200:focus {
+    --text-opacity: 1;
+    color: #b2f5ea;
+    color: rgba(178, 245, 234, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-300:focus {
+    --text-opacity: 1;
+    color: #81e6d9;
+    color: rgba(129, 230, 217, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-400:focus {
+    --text-opacity: 1;
+    color: #4fd1c5;
+    color: rgba(79, 209, 197, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-500:focus {
+    --text-opacity: 1;
+    color: #38b2ac;
+    color: rgba(56, 178, 172, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-600:focus {
+    --text-opacity: 1;
+    color: #319795;
+    color: rgba(49, 151, 149, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-700:focus {
+    --text-opacity: 1;
+    color: #2c7a7b;
+    color: rgba(44, 122, 123, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-800:focus {
+    --text-opacity: 1;
+    color: #285e61;
+    color: rgba(40, 94, 97, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-teal-900:focus {
+    --text-opacity: 1;
+    color: #234e52;
+    color: rgba(35, 78, 82, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-100:focus {
+    --text-opacity: 1;
+    color: #ebf8ff;
+    color: rgba(235, 248, 255, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-200:focus {
+    --text-opacity: 1;
+    color: #bee3f8;
+    color: rgba(190, 227, 248, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-300:focus {
+    --text-opacity: 1;
+    color: #90cdf4;
+    color: rgba(144, 205, 244, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-400:focus {
+    --text-opacity: 1;
+    color: #63b3ed;
+    color: rgba(99, 179, 237, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-500:focus {
+    --text-opacity: 1;
+    color: #4299e1;
+    color: rgba(66, 153, 225, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-600:focus {
+    --text-opacity: 1;
+    color: #3182ce;
+    color: rgba(49, 130, 206, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-700:focus {
+    --text-opacity: 1;
+    color: #2b6cb0;
+    color: rgba(43, 108, 176, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-800:focus {
+    --text-opacity: 1;
+    color: #2c5282;
+    color: rgba(44, 82, 130, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-blue-900:focus {
+    --text-opacity: 1;
+    color: #2a4365;
+    color: rgba(42, 67, 101, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-100:focus {
+    --text-opacity: 1;
+    color: #ebf4ff;
+    color: rgba(235, 244, 255, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-200:focus {
+    --text-opacity: 1;
+    color: #c3dafe;
+    color: rgba(195, 218, 254, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-300:focus {
+    --text-opacity: 1;
+    color: #a3bffa;
+    color: rgba(163, 191, 250, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-400:focus {
+    --text-opacity: 1;
+    color: #7f9cf5;
+    color: rgba(127, 156, 245, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-500:focus {
+    --text-opacity: 1;
+    color: #667eea;
+    color: rgba(102, 126, 234, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-600:focus {
+    --text-opacity: 1;
+    color: #5a67d8;
+    color: rgba(90, 103, 216, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-700:focus {
+    --text-opacity: 1;
+    color: #4c51bf;
+    color: rgba(76, 81, 191, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-800:focus {
+    --text-opacity: 1;
+    color: #434190;
+    color: rgba(67, 65, 144, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-indigo-900:focus {
+    --text-opacity: 1;
+    color: #3c366b;
+    color: rgba(60, 54, 107, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-100:focus {
+    --text-opacity: 1;
+    color: #faf5ff;
+    color: rgba(250, 245, 255, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-200:focus {
+    --text-opacity: 1;
+    color: #e9d8fd;
+    color: rgba(233, 216, 253, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-300:focus {
+    --text-opacity: 1;
+    color: #d6bcfa;
+    color: rgba(214, 188, 250, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-400:focus {
+    --text-opacity: 1;
+    color: #b794f4;
+    color: rgba(183, 148, 244, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-500:focus {
+    --text-opacity: 1;
+    color: #9f7aea;
+    color: rgba(159, 122, 234, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-600:focus {
+    --text-opacity: 1;
+    color: #805ad5;
+    color: rgba(128, 90, 213, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-700:focus {
+    --text-opacity: 1;
+    color: #6b46c1;
+    color: rgba(107, 70, 193, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-800:focus {
+    --text-opacity: 1;
+    color: #553c9a;
+    color: rgba(85, 60, 154, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-purple-900:focus {
+    --text-opacity: 1;
+    color: #44337a;
+    color: rgba(68, 51, 122, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-100:focus {
+    --text-opacity: 1;
+    color: #fff5f7;
+    color: rgba(255, 245, 247, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-200:focus {
+    --text-opacity: 1;
+    color: #fed7e2;
+    color: rgba(254, 215, 226, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-300:focus {
+    --text-opacity: 1;
+    color: #fbb6ce;
+    color: rgba(251, 182, 206, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-400:focus {
+    --text-opacity: 1;
+    color: #f687b3;
+    color: rgba(246, 135, 179, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-500:focus {
+    --text-opacity: 1;
+    color: #ed64a6;
+    color: rgba(237, 100, 166, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-600:focus {
+    --text-opacity: 1;
+    color: #d53f8c;
+    color: rgba(213, 63, 140, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-700:focus {
+    --text-opacity: 1;
+    color: #b83280;
+    color: rgba(184, 50, 128, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-800:focus {
+    --text-opacity: 1;
+    color: #97266d;
+    color: rgba(151, 38, 109, var(--text-opacity));
+  }
+
+  .xl\:focus\:text-pink-900:focus {
+    --text-opacity: 1;
+    color: #702459;
+    color: rgba(112, 36, 89, var(--text-opacity));
+  }
+
+  .xl\:text-opacity-0 {
+    --text-opacity: 0;
+  }
+
+  .xl\:text-opacity-25 {
+    --text-opacity: 0.25;
+  }
+
+  .xl\:text-opacity-50 {
+    --text-opacity: 0.5;
+  }
+
+  .xl\:text-opacity-75 {
+    --text-opacity: 0.75;
+  }
+
+  .xl\:text-opacity-100 {
+    --text-opacity: 1;
+  }
+
+  .xl\:hover\:text-opacity-0:hover {
+    --text-opacity: 0;
+  }
+
+  .xl\:hover\:text-opacity-25:hover {
+    --text-opacity: 0.25;
+  }
+
+  .xl\:hover\:text-opacity-50:hover {
+    --text-opacity: 0.5;
+  }
+
+  .xl\:hover\:text-opacity-75:hover {
+    --text-opacity: 0.75;
+  }
+
+  .xl\:hover\:text-opacity-100:hover {
+    --text-opacity: 1;
+  }
+
+  .xl\:focus\:text-opacity-0:focus {
+    --text-opacity: 0;
+  }
+
+  .xl\:focus\:text-opacity-25:focus {
+    --text-opacity: 0.25;
+  }
+
+  .xl\:focus\:text-opacity-50:focus {
+    --text-opacity: 0.5;
+  }
+
+  .xl\:focus\:text-opacity-75:focus {
+    --text-opacity: 0.75;
+  }
+
+  .xl\:focus\:text-opacity-100:focus {
+    --text-opacity: 1;
+  }
+
+  .xl\:italic {
+    font-style: italic;
+  }
+
+  .xl\:not-italic {
+    font-style: normal;
+  }
+
+  .xl\:uppercase {
+    text-transform: uppercase;
+  }
+
+  .xl\:lowercase {
+    text-transform: lowercase;
+  }
+
+  .xl\:capitalize {
+    text-transform: capitalize;
+  }
+
+  .xl\:normal-case {
+    text-transform: none;
+  }
+
+  .xl\:underline {
+    text-decoration: underline;
+  }
+
+  .xl\:line-through {
+    text-decoration: line-through;
+  }
+
+  .xl\:no-underline {
+    text-decoration: none;
+  }
+
+  .xl\:hover\:underline:hover {
+    text-decoration: underline;
+  }
+
+  .xl\:hover\:line-through:hover {
+    text-decoration: line-through;
+  }
+
+  .xl\:hover\:no-underline:hover {
+    text-decoration: none;
+  }
+
+  .xl\:focus\:underline:focus {
+    text-decoration: underline;
+  }
+
+  .xl\:focus\:line-through:focus {
+    text-decoration: line-through;
+  }
+
+  .xl\:focus\:no-underline:focus {
+    text-decoration: none;
+  }
+
+  .xl\:antialiased {
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+
+  .xl\:subpixel-antialiased {
+    -webkit-font-smoothing: auto;
+    -moz-osx-font-smoothing: auto;
+  }
+
+  .xl\:ordinal, .xl\:slashed-zero, .xl\:lining-nums, .xl\:oldstyle-nums, .xl\:proportional-nums, .xl\:tabular-nums, .xl\:diagonal-fractions, .xl\:stacked-fractions {
+    --font-variant-numeric-ordinal: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-slashed-zero: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-figure: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-spacing: var(--tailwind-empty,/*!*/ /*!*/);
+    --font-variant-numeric-fraction: var(--tailwind-empty,/*!*/ /*!*/);
+    font-variant-numeric: var(--font-variant-numeric-ordinal) var(--font-variant-numeric-slashed-zero) var(--font-variant-numeric-figure) var(--font-variant-numeric-spacing) var(--font-variant-numeric-fraction);
+  }
+
+  .xl\:normal-nums {
+    font-variant-numeric: normal;
+  }
+
+  .xl\:ordinal {
+    --font-variant-numeric-ordinal: ordinal;
+  }
+
+  .xl\:slashed-zero {
+    --font-variant-numeric-slashed-zero: slashed-zero;
+  }
+
+  .xl\:lining-nums {
+    --font-variant-numeric-figure: lining-nums;
+  }
+
+  .xl\:oldstyle-nums {
+    --font-variant-numeric-figure: oldstyle-nums;
+  }
+
+  .xl\:proportional-nums {
+    --font-variant-numeric-spacing: proportional-nums;
+  }
+
+  .xl\:tabular-nums {
+    --font-variant-numeric-spacing: tabular-nums;
+  }
+
+  .xl\:diagonal-fractions {
+    --font-variant-numeric-fraction: diagonal-fractions;
+  }
+
+  .xl\:stacked-fractions {
+    --font-variant-numeric-fraction: stacked-fractions;
+  }
+
+  .xl\:tracking-tighter {
+    letter-spacing: -0.05em;
+  }
+
+  .xl\:tracking-tight {
+    letter-spacing: -0.025em;
+  }
+
+  .xl\:tracking-normal {
+    letter-spacing: 0;
+  }
+
+  .xl\:tracking-wide {
+    letter-spacing: 0.025em;
+  }
+
+  .xl\:tracking-wider {
+    letter-spacing: 0.05em;
+  }
+
+  .xl\:tracking-widest {
+    letter-spacing: 0.1em;
+  }
+
+  .xl\:select-none {
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+  }
+
+  .xl\:select-text {
+    -webkit-user-select: text;
+       -moz-user-select: text;
+        -ms-user-select: text;
+            user-select: text;
+  }
+
+  .xl\:select-all {
+    -webkit-user-select: all;
+       -moz-user-select: all;
+        -ms-user-select: all;
+            user-select: all;
+  }
+
+  .xl\:select-auto {
+    -webkit-user-select: auto;
+       -moz-user-select: auto;
+        -ms-user-select: auto;
+            user-select: auto;
+  }
+
+  .xl\:align-baseline {
+    vertical-align: baseline;
+  }
+
+  .xl\:align-top {
+    vertical-align: top;
+  }
+
+  .xl\:align-middle {
+    vertical-align: middle;
+  }
+
+  .xl\:align-bottom {
+    vertical-align: bottom;
+  }
+
+  .xl\:align-text-top {
+    vertical-align: text-top;
+  }
+
+  .xl\:align-text-bottom {
+    vertical-align: text-bottom;
+  }
+
+  .xl\:visible {
+    visibility: visible;
+  }
+
+  .xl\:invisible {
+    visibility: hidden;
+  }
+
+  .xl\:whitespace-normal {
+    white-space: normal;
+  }
+
+  .xl\:whitespace-no-wrap {
+    white-space: nowrap;
+  }
+
+  .xl\:whitespace-pre {
+    white-space: pre;
+  }
+
+  .xl\:whitespace-pre-line {
+    white-space: pre-line;
+  }
+
+  .xl\:whitespace-pre-wrap {
+    white-space: pre-wrap;
+  }
+
+  .xl\:break-normal {
+    overflow-wrap: normal;
+    word-break: normal;
+  }
+
+  .xl\:break-words {
+    overflow-wrap: break-word;
+  }
+
+  .xl\:break-all {
+    word-break: break-all;
+  }
+
+  .xl\:truncate {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .xl\:w-0 {
+    width: 0;
+  }
+
+  .xl\:w-1 {
+    width: 0.25rem;
+  }
+
+  .xl\:w-2 {
+    width: 0.5rem;
+  }
+
+  .xl\:w-3 {
+    width: 0.75rem;
+  }
+
+  .xl\:w-4 {
+    width: 1rem;
+  }
+
+  .xl\:w-5 {
+    width: 1.25rem;
+  }
+
+  .xl\:w-6 {
+    width: 1.5rem;
+  }
+
+  .xl\:w-8 {
+    width: 2rem;
+  }
+
+  .xl\:w-10 {
+    width: 2.5rem;
+  }
+
+  .xl\:w-12 {
+    width: 3rem;
+  }
+
+  .xl\:w-16 {
+    width: 4rem;
+  }
+
+  .xl\:w-20 {
+    width: 5rem;
+  }
+
+  .xl\:w-24 {
+    width: 6rem;
+  }
+
+  .xl\:w-32 {
+    width: 8rem;
+  }
+
+  .xl\:w-40 {
+    width: 10rem;
+  }
+
+  .xl\:w-48 {
+    width: 12rem;
+  }
+
+  .xl\:w-56 {
+    width: 14rem;
+  }
+
+  .xl\:w-64 {
+    width: 16rem;
+  }
+
+  .xl\:w-auto {
+    width: auto;
+  }
+
+  .xl\:w-px {
+    width: 1px;
+  }
+
+  .xl\:w-1\/2 {
+    width: 50%;
+  }
+
+  .xl\:w-1\/3 {
+    width: 33.333333%;
+  }
+
+  .xl\:w-2\/3 {
+    width: 66.666667%;
+  }
+
+  .xl\:w-1\/4 {
+    width: 25%;
+  }
+
+  .xl\:w-2\/4 {
+    width: 50%;
+  }
+
+  .xl\:w-3\/4 {
+    width: 75%;
+  }
+
+  .xl\:w-1\/5 {
+    width: 20%;
+  }
+
+  .xl\:w-2\/5 {
+    width: 40%;
+  }
+
+  .xl\:w-3\/5 {
+    width: 60%;
+  }
+
+  .xl\:w-4\/5 {
+    width: 80%;
+  }
+
+  .xl\:w-1\/6 {
+    width: 16.666667%;
+  }
+
+  .xl\:w-2\/6 {
+    width: 33.333333%;
+  }
+
+  .xl\:w-3\/6 {
+    width: 50%;
+  }
+
+  .xl\:w-4\/6 {
+    width: 66.666667%;
+  }
+
+  .xl\:w-5\/6 {
+    width: 83.333333%;
+  }
+
+  .xl\:w-1\/12 {
+    width: 8.333333%;
+  }
+
+  .xl\:w-2\/12 {
+    width: 16.666667%;
+  }
+
+  .xl\:w-3\/12 {
+    width: 25%;
+  }
+
+  .xl\:w-4\/12 {
+    width: 33.333333%;
+  }
+
+  .xl\:w-5\/12 {
+    width: 41.666667%;
+  }
+
+  .xl\:w-6\/12 {
+    width: 50%;
+  }
+
+  .xl\:w-7\/12 {
+    width: 58.333333%;
+  }
+
+  .xl\:w-8\/12 {
+    width: 66.666667%;
+  }
+
+  .xl\:w-9\/12 {
+    width: 75%;
+  }
+
+  .xl\:w-10\/12 {
+    width: 83.333333%;
+  }
+
+  .xl\:w-11\/12 {
+    width: 91.666667%;
+  }
+
+  .xl\:w-full {
+    width: 100%;
+  }
+
+  .xl\:w-screen {
+    width: 100vw;
+  }
+
+  .xl\:z-0 {
+    z-index: 0;
+  }
+
+  .xl\:z-10 {
+    z-index: 10;
+  }
+
+  .xl\:z-20 {
+    z-index: 20;
+  }
+
+  .xl\:z-30 {
+    z-index: 30;
+  }
+
+  .xl\:z-40 {
+    z-index: 40;
+  }
+
+  .xl\:z-50 {
+    z-index: 50;
+  }
+
+  .xl\:z-auto {
+    z-index: auto;
+  }
+
+  .xl\:gap-0 {
+    grid-gap: 0;
+    gap: 0;
+  }
+
+  .xl\:gap-1 {
+    grid-gap: 0.25rem;
+    gap: 0.25rem;
+  }
+
+  .xl\:gap-2 {
+    grid-gap: 0.5rem;
+    gap: 0.5rem;
+  }
+
+  .xl\:gap-3 {
+    grid-gap: 0.75rem;
+    gap: 0.75rem;
+  }
+
+  .xl\:gap-4 {
+    grid-gap: 1rem;
+    gap: 1rem;
+  }
+
+  .xl\:gap-5 {
+    grid-gap: 1.25rem;
+    gap: 1.25rem;
+  }
+
+  .xl\:gap-6 {
+    grid-gap: 1.5rem;
+    gap: 1.5rem;
+  }
+
+  .xl\:gap-8 {
+    grid-gap: 2rem;
+    gap: 2rem;
+  }
+
+  .xl\:gap-10 {
+    grid-gap: 2.5rem;
+    gap: 2.5rem;
+  }
+
+  .xl\:gap-12 {
+    grid-gap: 3rem;
+    gap: 3rem;
+  }
+
+  .xl\:gap-16 {
+    grid-gap: 4rem;
+    gap: 4rem;
+  }
+
+  .xl\:gap-20 {
+    grid-gap: 5rem;
+    gap: 5rem;
+  }
+
+  .xl\:gap-24 {
+    grid-gap: 6rem;
+    gap: 6rem;
+  }
+
+  .xl\:gap-32 {
+    grid-gap: 8rem;
+    gap: 8rem;
+  }
+
+  .xl\:gap-40 {
+    grid-gap: 10rem;
+    gap: 10rem;
+  }
+
+  .xl\:gap-48 {
+    grid-gap: 12rem;
+    gap: 12rem;
+  }
+
+  .xl\:gap-56 {
+    grid-gap: 14rem;
+    gap: 14rem;
+  }
+
+  .xl\:gap-64 {
+    grid-gap: 16rem;
+    gap: 16rem;
+  }
+
+  .xl\:gap-px {
+    grid-gap: 1px;
+    gap: 1px;
+  }
+
+  .xl\:col-gap-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .xl\:col-gap-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .xl\:col-gap-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .xl\:col-gap-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .xl\:col-gap-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .xl\:col-gap-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .xl\:col-gap-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .xl\:col-gap-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .xl\:col-gap-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .xl\:col-gap-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .xl\:col-gap-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .xl\:col-gap-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .xl\:col-gap-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .xl\:col-gap-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .xl\:col-gap-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .xl\:col-gap-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .xl\:col-gap-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .xl\:col-gap-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .xl\:col-gap-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .xl\:gap-x-0 {
+    grid-column-gap: 0;
+    -moz-column-gap: 0;
+         column-gap: 0;
+  }
+
+  .xl\:gap-x-1 {
+    grid-column-gap: 0.25rem;
+    -moz-column-gap: 0.25rem;
+         column-gap: 0.25rem;
+  }
+
+  .xl\:gap-x-2 {
+    grid-column-gap: 0.5rem;
+    -moz-column-gap: 0.5rem;
+         column-gap: 0.5rem;
+  }
+
+  .xl\:gap-x-3 {
+    grid-column-gap: 0.75rem;
+    -moz-column-gap: 0.75rem;
+         column-gap: 0.75rem;
+  }
+
+  .xl\:gap-x-4 {
+    grid-column-gap: 1rem;
+    -moz-column-gap: 1rem;
+         column-gap: 1rem;
+  }
+
+  .xl\:gap-x-5 {
+    grid-column-gap: 1.25rem;
+    -moz-column-gap: 1.25rem;
+         column-gap: 1.25rem;
+  }
+
+  .xl\:gap-x-6 {
+    grid-column-gap: 1.5rem;
+    -moz-column-gap: 1.5rem;
+         column-gap: 1.5rem;
+  }
+
+  .xl\:gap-x-8 {
+    grid-column-gap: 2rem;
+    -moz-column-gap: 2rem;
+         column-gap: 2rem;
+  }
+
+  .xl\:gap-x-10 {
+    grid-column-gap: 2.5rem;
+    -moz-column-gap: 2.5rem;
+         column-gap: 2.5rem;
+  }
+
+  .xl\:gap-x-12 {
+    grid-column-gap: 3rem;
+    -moz-column-gap: 3rem;
+         column-gap: 3rem;
+  }
+
+  .xl\:gap-x-16 {
+    grid-column-gap: 4rem;
+    -moz-column-gap: 4rem;
+         column-gap: 4rem;
+  }
+
+  .xl\:gap-x-20 {
+    grid-column-gap: 5rem;
+    -moz-column-gap: 5rem;
+         column-gap: 5rem;
+  }
+
+  .xl\:gap-x-24 {
+    grid-column-gap: 6rem;
+    -moz-column-gap: 6rem;
+         column-gap: 6rem;
+  }
+
+  .xl\:gap-x-32 {
+    grid-column-gap: 8rem;
+    -moz-column-gap: 8rem;
+         column-gap: 8rem;
+  }
+
+  .xl\:gap-x-40 {
+    grid-column-gap: 10rem;
+    -moz-column-gap: 10rem;
+         column-gap: 10rem;
+  }
+
+  .xl\:gap-x-48 {
+    grid-column-gap: 12rem;
+    -moz-column-gap: 12rem;
+         column-gap: 12rem;
+  }
+
+  .xl\:gap-x-56 {
+    grid-column-gap: 14rem;
+    -moz-column-gap: 14rem;
+         column-gap: 14rem;
+  }
+
+  .xl\:gap-x-64 {
+    grid-column-gap: 16rem;
+    -moz-column-gap: 16rem;
+         column-gap: 16rem;
+  }
+
+  .xl\:gap-x-px {
+    grid-column-gap: 1px;
+    -moz-column-gap: 1px;
+         column-gap: 1px;
+  }
+
+  .xl\:row-gap-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .xl\:row-gap-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .xl\:row-gap-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .xl\:row-gap-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .xl\:row-gap-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .xl\:row-gap-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .xl\:row-gap-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .xl\:row-gap-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .xl\:row-gap-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .xl\:row-gap-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .xl\:row-gap-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .xl\:row-gap-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .xl\:row-gap-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .xl\:row-gap-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .xl\:row-gap-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .xl\:row-gap-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .xl\:row-gap-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .xl\:row-gap-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .xl\:row-gap-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .xl\:gap-y-0 {
+    grid-row-gap: 0;
+    row-gap: 0;
+  }
+
+  .xl\:gap-y-1 {
+    grid-row-gap: 0.25rem;
+    row-gap: 0.25rem;
+  }
+
+  .xl\:gap-y-2 {
+    grid-row-gap: 0.5rem;
+    row-gap: 0.5rem;
+  }
+
+  .xl\:gap-y-3 {
+    grid-row-gap: 0.75rem;
+    row-gap: 0.75rem;
+  }
+
+  .xl\:gap-y-4 {
+    grid-row-gap: 1rem;
+    row-gap: 1rem;
+  }
+
+  .xl\:gap-y-5 {
+    grid-row-gap: 1.25rem;
+    row-gap: 1.25rem;
+  }
+
+  .xl\:gap-y-6 {
+    grid-row-gap: 1.5rem;
+    row-gap: 1.5rem;
+  }
+
+  .xl\:gap-y-8 {
+    grid-row-gap: 2rem;
+    row-gap: 2rem;
+  }
+
+  .xl\:gap-y-10 {
+    grid-row-gap: 2.5rem;
+    row-gap: 2.5rem;
+  }
+
+  .xl\:gap-y-12 {
+    grid-row-gap: 3rem;
+    row-gap: 3rem;
+  }
+
+  .xl\:gap-y-16 {
+    grid-row-gap: 4rem;
+    row-gap: 4rem;
+  }
+
+  .xl\:gap-y-20 {
+    grid-row-gap: 5rem;
+    row-gap: 5rem;
+  }
+
+  .xl\:gap-y-24 {
+    grid-row-gap: 6rem;
+    row-gap: 6rem;
+  }
+
+  .xl\:gap-y-32 {
+    grid-row-gap: 8rem;
+    row-gap: 8rem;
+  }
+
+  .xl\:gap-y-40 {
+    grid-row-gap: 10rem;
+    row-gap: 10rem;
+  }
+
+  .xl\:gap-y-48 {
+    grid-row-gap: 12rem;
+    row-gap: 12rem;
+  }
+
+  .xl\:gap-y-56 {
+    grid-row-gap: 14rem;
+    row-gap: 14rem;
+  }
+
+  .xl\:gap-y-64 {
+    grid-row-gap: 16rem;
+    row-gap: 16rem;
+  }
+
+  .xl\:gap-y-px {
+    grid-row-gap: 1px;
+    row-gap: 1px;
+  }
+
+  .xl\:grid-flow-row {
+    grid-auto-flow: row;
+  }
+
+  .xl\:grid-flow-col {
+    grid-auto-flow: column;
+  }
+
+  .xl\:grid-flow-row-dense {
+    grid-auto-flow: row dense;
+  }
+
+  .xl\:grid-flow-col-dense {
+    grid-auto-flow: column dense;
+  }
+
+  .xl\:grid-cols-1 {
+    grid-template-columns: repeat(1, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-2 {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-3 {
+    grid-template-columns: repeat(3, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-4 {
+    grid-template-columns: repeat(4, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-5 {
+    grid-template-columns: repeat(5, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-6 {
+    grid-template-columns: repeat(6, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-7 {
+    grid-template-columns: repeat(7, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-8 {
+    grid-template-columns: repeat(8, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-9 {
+    grid-template-columns: repeat(9, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-10 {
+    grid-template-columns: repeat(10, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-11 {
+    grid-template-columns: repeat(11, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-12 {
+    grid-template-columns: repeat(12, minmax(0, 1fr));
+  }
+
+  .xl\:grid-cols-none {
+    grid-template-columns: none;
+  }
+
+  .xl\:col-auto {
+    grid-column: auto;
+  }
+
+  .xl\:col-span-1 {
+    grid-column: span 1 / span 1;
+  }
+
+  .xl\:col-span-2 {
+    grid-column: span 2 / span 2;
+  }
+
+  .xl\:col-span-3 {
+    grid-column: span 3 / span 3;
+  }
+
+  .xl\:col-span-4 {
+    grid-column: span 4 / span 4;
+  }
+
+  .xl\:col-span-5 {
+    grid-column: span 5 / span 5;
+  }
+
+  .xl\:col-span-6 {
+    grid-column: span 6 / span 6;
+  }
+
+  .xl\:col-span-7 {
+    grid-column: span 7 / span 7;
+  }
+
+  .xl\:col-span-8 {
+    grid-column: span 8 / span 8;
+  }
+
+  .xl\:col-span-9 {
+    grid-column: span 9 / span 9;
+  }
+
+  .xl\:col-span-10 {
+    grid-column: span 10 / span 10;
+  }
+
+  .xl\:col-span-11 {
+    grid-column: span 11 / span 11;
+  }
+
+  .xl\:col-span-12 {
+    grid-column: span 12 / span 12;
+  }
+
+  .xl\:col-start-1 {
+    grid-column-start: 1;
+  }
+
+  .xl\:col-start-2 {
+    grid-column-start: 2;
+  }
+
+  .xl\:col-start-3 {
+    grid-column-start: 3;
+  }
+
+  .xl\:col-start-4 {
+    grid-column-start: 4;
+  }
+
+  .xl\:col-start-5 {
+    grid-column-start: 5;
+  }
+
+  .xl\:col-start-6 {
+    grid-column-start: 6;
+  }
+
+  .xl\:col-start-7 {
+    grid-column-start: 7;
+  }
+
+  .xl\:col-start-8 {
+    grid-column-start: 8;
+  }
+
+  .xl\:col-start-9 {
+    grid-column-start: 9;
+  }
+
+  .xl\:col-start-10 {
+    grid-column-start: 10;
+  }
+
+  .xl\:col-start-11 {
+    grid-column-start: 11;
+  }
+
+  .xl\:col-start-12 {
+    grid-column-start: 12;
+  }
+
+  .xl\:col-start-13 {
+    grid-column-start: 13;
+  }
+
+  .xl\:col-start-auto {
+    grid-column-start: auto;
+  }
+
+  .xl\:col-end-1 {
+    grid-column-end: 1;
+  }
+
+  .xl\:col-end-2 {
+    grid-column-end: 2;
+  }
+
+  .xl\:col-end-3 {
+    grid-column-end: 3;
+  }
+
+  .xl\:col-end-4 {
+    grid-column-end: 4;
+  }
+
+  .xl\:col-end-5 {
+    grid-column-end: 5;
+  }
+
+  .xl\:col-end-6 {
+    grid-column-end: 6;
+  }
+
+  .xl\:col-end-7 {
+    grid-column-end: 7;
+  }
+
+  .xl\:col-end-8 {
+    grid-column-end: 8;
+  }
+
+  .xl\:col-end-9 {
+    grid-column-end: 9;
+  }
+
+  .xl\:col-end-10 {
+    grid-column-end: 10;
+  }
+
+  .xl\:col-end-11 {
+    grid-column-end: 11;
+  }
+
+  .xl\:col-end-12 {
+    grid-column-end: 12;
+  }
+
+  .xl\:col-end-13 {
+    grid-column-end: 13;
+  }
+
+  .xl\:col-end-auto {
+    grid-column-end: auto;
+  }
+
+  .xl\:grid-rows-1 {
+    grid-template-rows: repeat(1, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-2 {
+    grid-template-rows: repeat(2, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-3 {
+    grid-template-rows: repeat(3, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-4 {
+    grid-template-rows: repeat(4, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-5 {
+    grid-template-rows: repeat(5, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-6 {
+    grid-template-rows: repeat(6, minmax(0, 1fr));
+  }
+
+  .xl\:grid-rows-none {
+    grid-template-rows: none;
+  }
+
+  .xl\:row-auto {
+    grid-row: auto;
+  }
+
+  .xl\:row-span-1 {
+    grid-row: span 1 / span 1;
+  }
+
+  .xl\:row-span-2 {
+    grid-row: span 2 / span 2;
+  }
+
+  .xl\:row-span-3 {
+    grid-row: span 3 / span 3;
+  }
+
+  .xl\:row-span-4 {
+    grid-row: span 4 / span 4;
+  }
+
+  .xl\:row-span-5 {
+    grid-row: span 5 / span 5;
+  }
+
+  .xl\:row-span-6 {
+    grid-row: span 6 / span 6;
+  }
+
+  .xl\:row-start-1 {
+    grid-row-start: 1;
+  }
+
+  .xl\:row-start-2 {
+    grid-row-start: 2;
+  }
+
+  .xl\:row-start-3 {
+    grid-row-start: 3;
+  }
+
+  .xl\:row-start-4 {
+    grid-row-start: 4;
+  }
+
+  .xl\:row-start-5 {
+    grid-row-start: 5;
+  }
+
+  .xl\:row-start-6 {
+    grid-row-start: 6;
+  }
+
+  .xl\:row-start-7 {
+    grid-row-start: 7;
+  }
+
+  .xl\:row-start-auto {
+    grid-row-start: auto;
+  }
+
+  .xl\:row-end-1 {
+    grid-row-end: 1;
+  }
+
+  .xl\:row-end-2 {
+    grid-row-end: 2;
+  }
+
+  .xl\:row-end-3 {
+    grid-row-end: 3;
+  }
+
+  .xl\:row-end-4 {
+    grid-row-end: 4;
+  }
+
+  .xl\:row-end-5 {
+    grid-row-end: 5;
+  }
+
+  .xl\:row-end-6 {
+    grid-row-end: 6;
+  }
+
+  .xl\:row-end-7 {
+    grid-row-end: 7;
+  }
+
+  .xl\:row-end-auto {
+    grid-row-end: auto;
+  }
+
+  .xl\:transform {
+    --transform-translate-x: 0;
+    --transform-translate-y: 0;
+    --transform-rotate: 0;
+    --transform-skew-x: 0;
+    --transform-skew-y: 0;
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+    transform: translateX(var(--transform-translate-x)) translateY(var(--transform-translate-y)) rotate(var(--transform-rotate)) skewX(var(--transform-skew-x)) skewY(var(--transform-skew-y)) scaleX(var(--transform-scale-x)) scaleY(var(--transform-scale-y));
+  }
+
+  .xl\:transform-none {
+    transform: none;
+  }
+
+  .xl\:origin-center {
+    transform-origin: center;
+  }
+
+  .xl\:origin-top {
+    transform-origin: top;
+  }
+
+  .xl\:origin-top-right {
+    transform-origin: top right;
+  }
+
+  .xl\:origin-right {
+    transform-origin: right;
+  }
+
+  .xl\:origin-bottom-right {
+    transform-origin: bottom right;
+  }
+
+  .xl\:origin-bottom {
+    transform-origin: bottom;
+  }
+
+  .xl\:origin-bottom-left {
+    transform-origin: bottom left;
+  }
+
+  .xl\:origin-left {
+    transform-origin: left;
+  }
+
+  .xl\:origin-top-left {
+    transform-origin: top left;
+  }
+
+  .xl\:scale-0 {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .xl\:scale-50 {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .xl\:scale-75 {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .xl\:scale-90 {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .xl\:scale-95 {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .xl\:scale-100 {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .xl\:scale-105 {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:scale-110 {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:scale-125 {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:scale-150 {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:scale-x-0 {
+    --transform-scale-x: 0;
+  }
+
+  .xl\:scale-x-50 {
+    --transform-scale-x: .5;
+  }
+
+  .xl\:scale-x-75 {
+    --transform-scale-x: .75;
+  }
+
+  .xl\:scale-x-90 {
+    --transform-scale-x: .9;
+  }
+
+  .xl\:scale-x-95 {
+    --transform-scale-x: .95;
+  }
+
+  .xl\:scale-x-100 {
+    --transform-scale-x: 1;
+  }
+
+  .xl\:scale-x-105 {
+    --transform-scale-x: 1.05;
+  }
+
+  .xl\:scale-x-110 {
+    --transform-scale-x: 1.1;
+  }
+
+  .xl\:scale-x-125 {
+    --transform-scale-x: 1.25;
+  }
+
+  .xl\:scale-x-150 {
+    --transform-scale-x: 1.5;
+  }
+
+  .xl\:scale-y-0 {
+    --transform-scale-y: 0;
+  }
+
+  .xl\:scale-y-50 {
+    --transform-scale-y: .5;
+  }
+
+  .xl\:scale-y-75 {
+    --transform-scale-y: .75;
+  }
+
+  .xl\:scale-y-90 {
+    --transform-scale-y: .9;
+  }
+
+  .xl\:scale-y-95 {
+    --transform-scale-y: .95;
+  }
+
+  .xl\:scale-y-100 {
+    --transform-scale-y: 1;
+  }
+
+  .xl\:scale-y-105 {
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:scale-y-110 {
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:scale-y-125 {
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:scale-y-150 {
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:hover\:scale-0:hover {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .xl\:hover\:scale-50:hover {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .xl\:hover\:scale-75:hover {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .xl\:hover\:scale-90:hover {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .xl\:hover\:scale-95:hover {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .xl\:hover\:scale-100:hover {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .xl\:hover\:scale-105:hover {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:hover\:scale-110:hover {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:hover\:scale-125:hover {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:hover\:scale-150:hover {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:hover\:scale-x-0:hover {
+    --transform-scale-x: 0;
+  }
+
+  .xl\:hover\:scale-x-50:hover {
+    --transform-scale-x: .5;
+  }
+
+  .xl\:hover\:scale-x-75:hover {
+    --transform-scale-x: .75;
+  }
+
+  .xl\:hover\:scale-x-90:hover {
+    --transform-scale-x: .9;
+  }
+
+  .xl\:hover\:scale-x-95:hover {
+    --transform-scale-x: .95;
+  }
+
+  .xl\:hover\:scale-x-100:hover {
+    --transform-scale-x: 1;
+  }
+
+  .xl\:hover\:scale-x-105:hover {
+    --transform-scale-x: 1.05;
+  }
+
+  .xl\:hover\:scale-x-110:hover {
+    --transform-scale-x: 1.1;
+  }
+
+  .xl\:hover\:scale-x-125:hover {
+    --transform-scale-x: 1.25;
+  }
+
+  .xl\:hover\:scale-x-150:hover {
+    --transform-scale-x: 1.5;
+  }
+
+  .xl\:hover\:scale-y-0:hover {
+    --transform-scale-y: 0;
+  }
+
+  .xl\:hover\:scale-y-50:hover {
+    --transform-scale-y: .5;
+  }
+
+  .xl\:hover\:scale-y-75:hover {
+    --transform-scale-y: .75;
+  }
+
+  .xl\:hover\:scale-y-90:hover {
+    --transform-scale-y: .9;
+  }
+
+  .xl\:hover\:scale-y-95:hover {
+    --transform-scale-y: .95;
+  }
+
+  .xl\:hover\:scale-y-100:hover {
+    --transform-scale-y: 1;
+  }
+
+  .xl\:hover\:scale-y-105:hover {
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:hover\:scale-y-110:hover {
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:hover\:scale-y-125:hover {
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:hover\:scale-y-150:hover {
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:focus\:scale-0:focus {
+    --transform-scale-x: 0;
+    --transform-scale-y: 0;
+  }
+
+  .xl\:focus\:scale-50:focus {
+    --transform-scale-x: .5;
+    --transform-scale-y: .5;
+  }
+
+  .xl\:focus\:scale-75:focus {
+    --transform-scale-x: .75;
+    --transform-scale-y: .75;
+  }
+
+  .xl\:focus\:scale-90:focus {
+    --transform-scale-x: .9;
+    --transform-scale-y: .9;
+  }
+
+  .xl\:focus\:scale-95:focus {
+    --transform-scale-x: .95;
+    --transform-scale-y: .95;
+  }
+
+  .xl\:focus\:scale-100:focus {
+    --transform-scale-x: 1;
+    --transform-scale-y: 1;
+  }
+
+  .xl\:focus\:scale-105:focus {
+    --transform-scale-x: 1.05;
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:focus\:scale-110:focus {
+    --transform-scale-x: 1.1;
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:focus\:scale-125:focus {
+    --transform-scale-x: 1.25;
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:focus\:scale-150:focus {
+    --transform-scale-x: 1.5;
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:focus\:scale-x-0:focus {
+    --transform-scale-x: 0;
+  }
+
+  .xl\:focus\:scale-x-50:focus {
+    --transform-scale-x: .5;
+  }
+
+  .xl\:focus\:scale-x-75:focus {
+    --transform-scale-x: .75;
+  }
+
+  .xl\:focus\:scale-x-90:focus {
+    --transform-scale-x: .9;
+  }
+
+  .xl\:focus\:scale-x-95:focus {
+    --transform-scale-x: .95;
+  }
+
+  .xl\:focus\:scale-x-100:focus {
+    --transform-scale-x: 1;
+  }
+
+  .xl\:focus\:scale-x-105:focus {
+    --transform-scale-x: 1.05;
+  }
+
+  .xl\:focus\:scale-x-110:focus {
+    --transform-scale-x: 1.1;
+  }
+
+  .xl\:focus\:scale-x-125:focus {
+    --transform-scale-x: 1.25;
+  }
+
+  .xl\:focus\:scale-x-150:focus {
+    --transform-scale-x: 1.5;
+  }
+
+  .xl\:focus\:scale-y-0:focus {
+    --transform-scale-y: 0;
+  }
+
+  .xl\:focus\:scale-y-50:focus {
+    --transform-scale-y: .5;
+  }
+
+  .xl\:focus\:scale-y-75:focus {
+    --transform-scale-y: .75;
+  }
+
+  .xl\:focus\:scale-y-90:focus {
+    --transform-scale-y: .9;
+  }
+
+  .xl\:focus\:scale-y-95:focus {
+    --transform-scale-y: .95;
+  }
+
+  .xl\:focus\:scale-y-100:focus {
+    --transform-scale-y: 1;
+  }
+
+  .xl\:focus\:scale-y-105:focus {
+    --transform-scale-y: 1.05;
+  }
+
+  .xl\:focus\:scale-y-110:focus {
+    --transform-scale-y: 1.1;
+  }
+
+  .xl\:focus\:scale-y-125:focus {
+    --transform-scale-y: 1.25;
+  }
+
+  .xl\:focus\:scale-y-150:focus {
+    --transform-scale-y: 1.5;
+  }
+
+  .xl\:rotate-0 {
+    --transform-rotate: 0;
+  }
+
+  .xl\:rotate-45 {
+    --transform-rotate: 45deg;
+  }
+
+  .xl\:rotate-90 {
+    --transform-rotate: 90deg;
+  }
+
+  .xl\:rotate-180 {
+    --transform-rotate: 180deg;
+  }
+
+  .xl\:-rotate-180 {
+    --transform-rotate: -180deg;
+  }
+
+  .xl\:-rotate-90 {
+    --transform-rotate: -90deg;
+  }
+
+  .xl\:-rotate-45 {
+    --transform-rotate: -45deg;
+  }
+
+  .xl\:hover\:rotate-0:hover {
+    --transform-rotate: 0;
+  }
+
+  .xl\:hover\:rotate-45:hover {
+    --transform-rotate: 45deg;
+  }
+
+  .xl\:hover\:rotate-90:hover {
+    --transform-rotate: 90deg;
+  }
+
+  .xl\:hover\:rotate-180:hover {
+    --transform-rotate: 180deg;
+  }
+
+  .xl\:hover\:-rotate-180:hover {
+    --transform-rotate: -180deg;
+  }
+
+  .xl\:hover\:-rotate-90:hover {
+    --transform-rotate: -90deg;
+  }
+
+  .xl\:hover\:-rotate-45:hover {
+    --transform-rotate: -45deg;
+  }
+
+  .xl\:focus\:rotate-0:focus {
+    --transform-rotate: 0;
+  }
+
+  .xl\:focus\:rotate-45:focus {
+    --transform-rotate: 45deg;
+  }
+
+  .xl\:focus\:rotate-90:focus {
+    --transform-rotate: 90deg;
+  }
+
+  .xl\:focus\:rotate-180:focus {
+    --transform-rotate: 180deg;
+  }
+
+  .xl\:focus\:-rotate-180:focus {
+    --transform-rotate: -180deg;
+  }
+
+  .xl\:focus\:-rotate-90:focus {
+    --transform-rotate: -90deg;
+  }
+
+  .xl\:focus\:-rotate-45:focus {
+    --transform-rotate: -45deg;
+  }
+
+  .xl\:translate-x-0 {
+    --transform-translate-x: 0;
+  }
+
+  .xl\:translate-x-1 {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .xl\:translate-x-2 {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .xl\:translate-x-3 {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .xl\:translate-x-4 {
+    --transform-translate-x: 1rem;
+  }
+
+  .xl\:translate-x-5 {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .xl\:translate-x-6 {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .xl\:translate-x-8 {
+    --transform-translate-x: 2rem;
+  }
+
+  .xl\:translate-x-10 {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .xl\:translate-x-12 {
+    --transform-translate-x: 3rem;
+  }
+
+  .xl\:translate-x-16 {
+    --transform-translate-x: 4rem;
+  }
+
+  .xl\:translate-x-20 {
+    --transform-translate-x: 5rem;
+  }
+
+  .xl\:translate-x-24 {
+    --transform-translate-x: 6rem;
+  }
+
+  .xl\:translate-x-32 {
+    --transform-translate-x: 8rem;
+  }
+
+  .xl\:translate-x-40 {
+    --transform-translate-x: 10rem;
+  }
+
+  .xl\:translate-x-48 {
+    --transform-translate-x: 12rem;
+  }
+
+  .xl\:translate-x-56 {
+    --transform-translate-x: 14rem;
+  }
+
+  .xl\:translate-x-64 {
+    --transform-translate-x: 16rem;
+  }
+
+  .xl\:translate-x-px {
+    --transform-translate-x: 1px;
+  }
+
+  .xl\:-translate-x-1 {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .xl\:-translate-x-2 {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .xl\:-translate-x-3 {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .xl\:-translate-x-4 {
+    --transform-translate-x: -1rem;
+  }
+
+  .xl\:-translate-x-5 {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .xl\:-translate-x-6 {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .xl\:-translate-x-8 {
+    --transform-translate-x: -2rem;
+  }
+
+  .xl\:-translate-x-10 {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .xl\:-translate-x-12 {
+    --transform-translate-x: -3rem;
+  }
+
+  .xl\:-translate-x-16 {
+    --transform-translate-x: -4rem;
+  }
+
+  .xl\:-translate-x-20 {
+    --transform-translate-x: -5rem;
+  }
+
+  .xl\:-translate-x-24 {
+    --transform-translate-x: -6rem;
+  }
+
+  .xl\:-translate-x-32 {
+    --transform-translate-x: -8rem;
+  }
+
+  .xl\:-translate-x-40 {
+    --transform-translate-x: -10rem;
+  }
+
+  .xl\:-translate-x-48 {
+    --transform-translate-x: -12rem;
+  }
+
+  .xl\:-translate-x-56 {
+    --transform-translate-x: -14rem;
+  }
+
+  .xl\:-translate-x-64 {
+    --transform-translate-x: -16rem;
+  }
+
+  .xl\:-translate-x-px {
+    --transform-translate-x: -1px;
+  }
+
+  .xl\:-translate-x-full {
+    --transform-translate-x: -100%;
+  }
+
+  .xl\:-translate-x-1\/2 {
+    --transform-translate-x: -50%;
+  }
+
+  .xl\:translate-x-1\/2 {
+    --transform-translate-x: 50%;
+  }
+
+  .xl\:translate-x-full {
+    --transform-translate-x: 100%;
+  }
+
+  .xl\:translate-y-0 {
+    --transform-translate-y: 0;
+  }
+
+  .xl\:translate-y-1 {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .xl\:translate-y-2 {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .xl\:translate-y-3 {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .xl\:translate-y-4 {
+    --transform-translate-y: 1rem;
+  }
+
+  .xl\:translate-y-5 {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .xl\:translate-y-6 {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .xl\:translate-y-8 {
+    --transform-translate-y: 2rem;
+  }
+
+  .xl\:translate-y-10 {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .xl\:translate-y-12 {
+    --transform-translate-y: 3rem;
+  }
+
+  .xl\:translate-y-16 {
+    --transform-translate-y: 4rem;
+  }
+
+  .xl\:translate-y-20 {
+    --transform-translate-y: 5rem;
+  }
+
+  .xl\:translate-y-24 {
+    --transform-translate-y: 6rem;
+  }
+
+  .xl\:translate-y-32 {
+    --transform-translate-y: 8rem;
+  }
+
+  .xl\:translate-y-40 {
+    --transform-translate-y: 10rem;
+  }
+
+  .xl\:translate-y-48 {
+    --transform-translate-y: 12rem;
+  }
+
+  .xl\:translate-y-56 {
+    --transform-translate-y: 14rem;
+  }
+
+  .xl\:translate-y-64 {
+    --transform-translate-y: 16rem;
+  }
+
+  .xl\:translate-y-px {
+    --transform-translate-y: 1px;
+  }
+
+  .xl\:-translate-y-1 {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .xl\:-translate-y-2 {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .xl\:-translate-y-3 {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .xl\:-translate-y-4 {
+    --transform-translate-y: -1rem;
+  }
+
+  .xl\:-translate-y-5 {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .xl\:-translate-y-6 {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .xl\:-translate-y-8 {
+    --transform-translate-y: -2rem;
+  }
+
+  .xl\:-translate-y-10 {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .xl\:-translate-y-12 {
+    --transform-translate-y: -3rem;
+  }
+
+  .xl\:-translate-y-16 {
+    --transform-translate-y: -4rem;
+  }
+
+  .xl\:-translate-y-20 {
+    --transform-translate-y: -5rem;
+  }
+
+  .xl\:-translate-y-24 {
+    --transform-translate-y: -6rem;
+  }
+
+  .xl\:-translate-y-32 {
+    --transform-translate-y: -8rem;
+  }
+
+  .xl\:-translate-y-40 {
+    --transform-translate-y: -10rem;
+  }
+
+  .xl\:-translate-y-48 {
+    --transform-translate-y: -12rem;
+  }
+
+  .xl\:-translate-y-56 {
+    --transform-translate-y: -14rem;
+  }
+
+  .xl\:-translate-y-64 {
+    --transform-translate-y: -16rem;
+  }
+
+  .xl\:-translate-y-px {
+    --transform-translate-y: -1px;
+  }
+
+  .xl\:-translate-y-full {
+    --transform-translate-y: -100%;
+  }
+
+  .xl\:-translate-y-1\/2 {
+    --transform-translate-y: -50%;
+  }
+
+  .xl\:translate-y-1\/2 {
+    --transform-translate-y: 50%;
+  }
+
+  .xl\:translate-y-full {
+    --transform-translate-y: 100%;
+  }
+
+  .xl\:hover\:translate-x-0:hover {
+    --transform-translate-x: 0;
+  }
+
+  .xl\:hover\:translate-x-1:hover {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .xl\:hover\:translate-x-2:hover {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .xl\:hover\:translate-x-3:hover {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .xl\:hover\:translate-x-4:hover {
+    --transform-translate-x: 1rem;
+  }
+
+  .xl\:hover\:translate-x-5:hover {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .xl\:hover\:translate-x-6:hover {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .xl\:hover\:translate-x-8:hover {
+    --transform-translate-x: 2rem;
+  }
+
+  .xl\:hover\:translate-x-10:hover {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .xl\:hover\:translate-x-12:hover {
+    --transform-translate-x: 3rem;
+  }
+
+  .xl\:hover\:translate-x-16:hover {
+    --transform-translate-x: 4rem;
+  }
+
+  .xl\:hover\:translate-x-20:hover {
+    --transform-translate-x: 5rem;
+  }
+
+  .xl\:hover\:translate-x-24:hover {
+    --transform-translate-x: 6rem;
+  }
+
+  .xl\:hover\:translate-x-32:hover {
+    --transform-translate-x: 8rem;
+  }
+
+  .xl\:hover\:translate-x-40:hover {
+    --transform-translate-x: 10rem;
+  }
+
+  .xl\:hover\:translate-x-48:hover {
+    --transform-translate-x: 12rem;
+  }
+
+  .xl\:hover\:translate-x-56:hover {
+    --transform-translate-x: 14rem;
+  }
+
+  .xl\:hover\:translate-x-64:hover {
+    --transform-translate-x: 16rem;
+  }
+
+  .xl\:hover\:translate-x-px:hover {
+    --transform-translate-x: 1px;
+  }
+
+  .xl\:hover\:-translate-x-1:hover {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .xl\:hover\:-translate-x-2:hover {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .xl\:hover\:-translate-x-3:hover {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .xl\:hover\:-translate-x-4:hover {
+    --transform-translate-x: -1rem;
+  }
+
+  .xl\:hover\:-translate-x-5:hover {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .xl\:hover\:-translate-x-6:hover {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .xl\:hover\:-translate-x-8:hover {
+    --transform-translate-x: -2rem;
+  }
+
+  .xl\:hover\:-translate-x-10:hover {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .xl\:hover\:-translate-x-12:hover {
+    --transform-translate-x: -3rem;
+  }
+
+  .xl\:hover\:-translate-x-16:hover {
+    --transform-translate-x: -4rem;
+  }
+
+  .xl\:hover\:-translate-x-20:hover {
+    --transform-translate-x: -5rem;
+  }
+
+  .xl\:hover\:-translate-x-24:hover {
+    --transform-translate-x: -6rem;
+  }
+
+  .xl\:hover\:-translate-x-32:hover {
+    --transform-translate-x: -8rem;
+  }
+
+  .xl\:hover\:-translate-x-40:hover {
+    --transform-translate-x: -10rem;
+  }
+
+  .xl\:hover\:-translate-x-48:hover {
+    --transform-translate-x: -12rem;
+  }
+
+  .xl\:hover\:-translate-x-56:hover {
+    --transform-translate-x: -14rem;
+  }
+
+  .xl\:hover\:-translate-x-64:hover {
+    --transform-translate-x: -16rem;
+  }
+
+  .xl\:hover\:-translate-x-px:hover {
+    --transform-translate-x: -1px;
+  }
+
+  .xl\:hover\:-translate-x-full:hover {
+    --transform-translate-x: -100%;
+  }
+
+  .xl\:hover\:-translate-x-1\/2:hover {
+    --transform-translate-x: -50%;
+  }
+
+  .xl\:hover\:translate-x-1\/2:hover {
+    --transform-translate-x: 50%;
+  }
+
+  .xl\:hover\:translate-x-full:hover {
+    --transform-translate-x: 100%;
+  }
+
+  .xl\:hover\:translate-y-0:hover {
+    --transform-translate-y: 0;
+  }
+
+  .xl\:hover\:translate-y-1:hover {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .xl\:hover\:translate-y-2:hover {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .xl\:hover\:translate-y-3:hover {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .xl\:hover\:translate-y-4:hover {
+    --transform-translate-y: 1rem;
+  }
+
+  .xl\:hover\:translate-y-5:hover {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .xl\:hover\:translate-y-6:hover {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .xl\:hover\:translate-y-8:hover {
+    --transform-translate-y: 2rem;
+  }
+
+  .xl\:hover\:translate-y-10:hover {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .xl\:hover\:translate-y-12:hover {
+    --transform-translate-y: 3rem;
+  }
+
+  .xl\:hover\:translate-y-16:hover {
+    --transform-translate-y: 4rem;
+  }
+
+  .xl\:hover\:translate-y-20:hover {
+    --transform-translate-y: 5rem;
+  }
+
+  .xl\:hover\:translate-y-24:hover {
+    --transform-translate-y: 6rem;
+  }
+
+  .xl\:hover\:translate-y-32:hover {
+    --transform-translate-y: 8rem;
+  }
+
+  .xl\:hover\:translate-y-40:hover {
+    --transform-translate-y: 10rem;
+  }
+
+  .xl\:hover\:translate-y-48:hover {
+    --transform-translate-y: 12rem;
+  }
+
+  .xl\:hover\:translate-y-56:hover {
+    --transform-translate-y: 14rem;
+  }
+
+  .xl\:hover\:translate-y-64:hover {
+    --transform-translate-y: 16rem;
+  }
+
+  .xl\:hover\:translate-y-px:hover {
+    --transform-translate-y: 1px;
+  }
+
+  .xl\:hover\:-translate-y-1:hover {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .xl\:hover\:-translate-y-2:hover {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .xl\:hover\:-translate-y-3:hover {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .xl\:hover\:-translate-y-4:hover {
+    --transform-translate-y: -1rem;
+  }
+
+  .xl\:hover\:-translate-y-5:hover {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .xl\:hover\:-translate-y-6:hover {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .xl\:hover\:-translate-y-8:hover {
+    --transform-translate-y: -2rem;
+  }
+
+  .xl\:hover\:-translate-y-10:hover {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .xl\:hover\:-translate-y-12:hover {
+    --transform-translate-y: -3rem;
+  }
+
+  .xl\:hover\:-translate-y-16:hover {
+    --transform-translate-y: -4rem;
+  }
+
+  .xl\:hover\:-translate-y-20:hover {
+    --transform-translate-y: -5rem;
+  }
+
+  .xl\:hover\:-translate-y-24:hover {
+    --transform-translate-y: -6rem;
+  }
+
+  .xl\:hover\:-translate-y-32:hover {
+    --transform-translate-y: -8rem;
+  }
+
+  .xl\:hover\:-translate-y-40:hover {
+    --transform-translate-y: -10rem;
+  }
+
+  .xl\:hover\:-translate-y-48:hover {
+    --transform-translate-y: -12rem;
+  }
+
+  .xl\:hover\:-translate-y-56:hover {
+    --transform-translate-y: -14rem;
+  }
+
+  .xl\:hover\:-translate-y-64:hover {
+    --transform-translate-y: -16rem;
+  }
+
+  .xl\:hover\:-translate-y-px:hover {
+    --transform-translate-y: -1px;
+  }
+
+  .xl\:hover\:-translate-y-full:hover {
+    --transform-translate-y: -100%;
+  }
+
+  .xl\:hover\:-translate-y-1\/2:hover {
+    --transform-translate-y: -50%;
+  }
+
+  .xl\:hover\:translate-y-1\/2:hover {
+    --transform-translate-y: 50%;
+  }
+
+  .xl\:hover\:translate-y-full:hover {
+    --transform-translate-y: 100%;
+  }
+
+  .xl\:focus\:translate-x-0:focus {
+    --transform-translate-x: 0;
+  }
+
+  .xl\:focus\:translate-x-1:focus {
+    --transform-translate-x: 0.25rem;
+  }
+
+  .xl\:focus\:translate-x-2:focus {
+    --transform-translate-x: 0.5rem;
+  }
+
+  .xl\:focus\:translate-x-3:focus {
+    --transform-translate-x: 0.75rem;
+  }
+
+  .xl\:focus\:translate-x-4:focus {
+    --transform-translate-x: 1rem;
+  }
+
+  .xl\:focus\:translate-x-5:focus {
+    --transform-translate-x: 1.25rem;
+  }
+
+  .xl\:focus\:translate-x-6:focus {
+    --transform-translate-x: 1.5rem;
+  }
+
+  .xl\:focus\:translate-x-8:focus {
+    --transform-translate-x: 2rem;
+  }
+
+  .xl\:focus\:translate-x-10:focus {
+    --transform-translate-x: 2.5rem;
+  }
+
+  .xl\:focus\:translate-x-12:focus {
+    --transform-translate-x: 3rem;
+  }
+
+  .xl\:focus\:translate-x-16:focus {
+    --transform-translate-x: 4rem;
+  }
+
+  .xl\:focus\:translate-x-20:focus {
+    --transform-translate-x: 5rem;
+  }
+
+  .xl\:focus\:translate-x-24:focus {
+    --transform-translate-x: 6rem;
+  }
+
+  .xl\:focus\:translate-x-32:focus {
+    --transform-translate-x: 8rem;
+  }
+
+  .xl\:focus\:translate-x-40:focus {
+    --transform-translate-x: 10rem;
+  }
+
+  .xl\:focus\:translate-x-48:focus {
+    --transform-translate-x: 12rem;
+  }
+
+  .xl\:focus\:translate-x-56:focus {
+    --transform-translate-x: 14rem;
+  }
+
+  .xl\:focus\:translate-x-64:focus {
+    --transform-translate-x: 16rem;
+  }
+
+  .xl\:focus\:translate-x-px:focus {
+    --transform-translate-x: 1px;
+  }
+
+  .xl\:focus\:-translate-x-1:focus {
+    --transform-translate-x: -0.25rem;
+  }
+
+  .xl\:focus\:-translate-x-2:focus {
+    --transform-translate-x: -0.5rem;
+  }
+
+  .xl\:focus\:-translate-x-3:focus {
+    --transform-translate-x: -0.75rem;
+  }
+
+  .xl\:focus\:-translate-x-4:focus {
+    --transform-translate-x: -1rem;
+  }
+
+  .xl\:focus\:-translate-x-5:focus {
+    --transform-translate-x: -1.25rem;
+  }
+
+  .xl\:focus\:-translate-x-6:focus {
+    --transform-translate-x: -1.5rem;
+  }
+
+  .xl\:focus\:-translate-x-8:focus {
+    --transform-translate-x: -2rem;
+  }
+
+  .xl\:focus\:-translate-x-10:focus {
+    --transform-translate-x: -2.5rem;
+  }
+
+  .xl\:focus\:-translate-x-12:focus {
+    --transform-translate-x: -3rem;
+  }
+
+  .xl\:focus\:-translate-x-16:focus {
+    --transform-translate-x: -4rem;
+  }
+
+  .xl\:focus\:-translate-x-20:focus {
+    --transform-translate-x: -5rem;
+  }
+
+  .xl\:focus\:-translate-x-24:focus {
+    --transform-translate-x: -6rem;
+  }
+
+  .xl\:focus\:-translate-x-32:focus {
+    --transform-translate-x: -8rem;
+  }
+
+  .xl\:focus\:-translate-x-40:focus {
+    --transform-translate-x: -10rem;
+  }
+
+  .xl\:focus\:-translate-x-48:focus {
+    --transform-translate-x: -12rem;
+  }
+
+  .xl\:focus\:-translate-x-56:focus {
+    --transform-translate-x: -14rem;
+  }
+
+  .xl\:focus\:-translate-x-64:focus {
+    --transform-translate-x: -16rem;
+  }
+
+  .xl\:focus\:-translate-x-px:focus {
+    --transform-translate-x: -1px;
+  }
+
+  .xl\:focus\:-translate-x-full:focus {
+    --transform-translate-x: -100%;
+  }
+
+  .xl\:focus\:-translate-x-1\/2:focus {
+    --transform-translate-x: -50%;
+  }
+
+  .xl\:focus\:translate-x-1\/2:focus {
+    --transform-translate-x: 50%;
+  }
+
+  .xl\:focus\:translate-x-full:focus {
+    --transform-translate-x: 100%;
+  }
+
+  .xl\:focus\:translate-y-0:focus {
+    --transform-translate-y: 0;
+  }
+
+  .xl\:focus\:translate-y-1:focus {
+    --transform-translate-y: 0.25rem;
+  }
+
+  .xl\:focus\:translate-y-2:focus {
+    --transform-translate-y: 0.5rem;
+  }
+
+  .xl\:focus\:translate-y-3:focus {
+    --transform-translate-y: 0.75rem;
+  }
+
+  .xl\:focus\:translate-y-4:focus {
+    --transform-translate-y: 1rem;
+  }
+
+  .xl\:focus\:translate-y-5:focus {
+    --transform-translate-y: 1.25rem;
+  }
+
+  .xl\:focus\:translate-y-6:focus {
+    --transform-translate-y: 1.5rem;
+  }
+
+  .xl\:focus\:translate-y-8:focus {
+    --transform-translate-y: 2rem;
+  }
+
+  .xl\:focus\:translate-y-10:focus {
+    --transform-translate-y: 2.5rem;
+  }
+
+  .xl\:focus\:translate-y-12:focus {
+    --transform-translate-y: 3rem;
+  }
+
+  .xl\:focus\:translate-y-16:focus {
+    --transform-translate-y: 4rem;
+  }
+
+  .xl\:focus\:translate-y-20:focus {
+    --transform-translate-y: 5rem;
+  }
+
+  .xl\:focus\:translate-y-24:focus {
+    --transform-translate-y: 6rem;
+  }
+
+  .xl\:focus\:translate-y-32:focus {
+    --transform-translate-y: 8rem;
+  }
+
+  .xl\:focus\:translate-y-40:focus {
+    --transform-translate-y: 10rem;
+  }
+
+  .xl\:focus\:translate-y-48:focus {
+    --transform-translate-y: 12rem;
+  }
+
+  .xl\:focus\:translate-y-56:focus {
+    --transform-translate-y: 14rem;
+  }
+
+  .xl\:focus\:translate-y-64:focus {
+    --transform-translate-y: 16rem;
+  }
+
+  .xl\:focus\:translate-y-px:focus {
+    --transform-translate-y: 1px;
+  }
+
+  .xl\:focus\:-translate-y-1:focus {
+    --transform-translate-y: -0.25rem;
+  }
+
+  .xl\:focus\:-translate-y-2:focus {
+    --transform-translate-y: -0.5rem;
+  }
+
+  .xl\:focus\:-translate-y-3:focus {
+    --transform-translate-y: -0.75rem;
+  }
+
+  .xl\:focus\:-translate-y-4:focus {
+    --transform-translate-y: -1rem;
+  }
+
+  .xl\:focus\:-translate-y-5:focus {
+    --transform-translate-y: -1.25rem;
+  }
+
+  .xl\:focus\:-translate-y-6:focus {
+    --transform-translate-y: -1.5rem;
+  }
+
+  .xl\:focus\:-translate-y-8:focus {
+    --transform-translate-y: -2rem;
+  }
+
+  .xl\:focus\:-translate-y-10:focus {
+    --transform-translate-y: -2.5rem;
+  }
+
+  .xl\:focus\:-translate-y-12:focus {
+    --transform-translate-y: -3rem;
+  }
+
+  .xl\:focus\:-translate-y-16:focus {
+    --transform-translate-y: -4rem;
+  }
+
+  .xl\:focus\:-translate-y-20:focus {
+    --transform-translate-y: -5rem;
+  }
+
+  .xl\:focus\:-translate-y-24:focus {
+    --transform-translate-y: -6rem;
+  }
+
+  .xl\:focus\:-translate-y-32:focus {
+    --transform-translate-y: -8rem;
+  }
+
+  .xl\:focus\:-translate-y-40:focus {
+    --transform-translate-y: -10rem;
+  }
+
+  .xl\:focus\:-translate-y-48:focus {
+    --transform-translate-y: -12rem;
+  }
+
+  .xl\:focus\:-translate-y-56:focus {
+    --transform-translate-y: -14rem;
+  }
+
+  .xl\:focus\:-translate-y-64:focus {
+    --transform-translate-y: -16rem;
+  }
+
+  .xl\:focus\:-translate-y-px:focus {
+    --transform-translate-y: -1px;
+  }
+
+  .xl\:focus\:-translate-y-full:focus {
+    --transform-translate-y: -100%;
+  }
+
+  .xl\:focus\:-translate-y-1\/2:focus {
+    --transform-translate-y: -50%;
+  }
+
+  .xl\:focus\:translate-y-1\/2:focus {
+    --transform-translate-y: 50%;
+  }
+
+  .xl\:focus\:translate-y-full:focus {
+    --transform-translate-y: 100%;
+  }
+
+  .xl\:skew-x-0 {
+    --transform-skew-x: 0;
+  }
+
+  .xl\:skew-x-3 {
+    --transform-skew-x: 3deg;
+  }
+
+  .xl\:skew-x-6 {
+    --transform-skew-x: 6deg;
+  }
+
+  .xl\:skew-x-12 {
+    --transform-skew-x: 12deg;
+  }
+
+  .xl\:-skew-x-12 {
+    --transform-skew-x: -12deg;
+  }
+
+  .xl\:-skew-x-6 {
+    --transform-skew-x: -6deg;
+  }
+
+  .xl\:-skew-x-3 {
+    --transform-skew-x: -3deg;
+  }
+
+  .xl\:skew-y-0 {
+    --transform-skew-y: 0;
+  }
+
+  .xl\:skew-y-3 {
+    --transform-skew-y: 3deg;
+  }
+
+  .xl\:skew-y-6 {
+    --transform-skew-y: 6deg;
+  }
+
+  .xl\:skew-y-12 {
+    --transform-skew-y: 12deg;
+  }
+
+  .xl\:-skew-y-12 {
+    --transform-skew-y: -12deg;
+  }
+
+  .xl\:-skew-y-6 {
+    --transform-skew-y: -6deg;
+  }
+
+  .xl\:-skew-y-3 {
+    --transform-skew-y: -3deg;
+  }
+
+  .xl\:hover\:skew-x-0:hover {
+    --transform-skew-x: 0;
+  }
+
+  .xl\:hover\:skew-x-3:hover {
+    --transform-skew-x: 3deg;
+  }
+
+  .xl\:hover\:skew-x-6:hover {
+    --transform-skew-x: 6deg;
+  }
+
+  .xl\:hover\:skew-x-12:hover {
+    --transform-skew-x: 12deg;
+  }
+
+  .xl\:hover\:-skew-x-12:hover {
+    --transform-skew-x: -12deg;
+  }
+
+  .xl\:hover\:-skew-x-6:hover {
+    --transform-skew-x: -6deg;
+  }
+
+  .xl\:hover\:-skew-x-3:hover {
+    --transform-skew-x: -3deg;
+  }
+
+  .xl\:hover\:skew-y-0:hover {
+    --transform-skew-y: 0;
+  }
+
+  .xl\:hover\:skew-y-3:hover {
+    --transform-skew-y: 3deg;
+  }
+
+  .xl\:hover\:skew-y-6:hover {
+    --transform-skew-y: 6deg;
+  }
+
+  .xl\:hover\:skew-y-12:hover {
+    --transform-skew-y: 12deg;
+  }
+
+  .xl\:hover\:-skew-y-12:hover {
+    --transform-skew-y: -12deg;
+  }
+
+  .xl\:hover\:-skew-y-6:hover {
+    --transform-skew-y: -6deg;
+  }
+
+  .xl\:hover\:-skew-y-3:hover {
+    --transform-skew-y: -3deg;
+  }
+
+  .xl\:focus\:skew-x-0:focus {
+    --transform-skew-x: 0;
+  }
+
+  .xl\:focus\:skew-x-3:focus {
+    --transform-skew-x: 3deg;
+  }
+
+  .xl\:focus\:skew-x-6:focus {
+    --transform-skew-x: 6deg;
+  }
+
+  .xl\:focus\:skew-x-12:focus {
+    --transform-skew-x: 12deg;
+  }
+
+  .xl\:focus\:-skew-x-12:focus {
+    --transform-skew-x: -12deg;
+  }
+
+  .xl\:focus\:-skew-x-6:focus {
+    --transform-skew-x: -6deg;
+  }
+
+  .xl\:focus\:-skew-x-3:focus {
+    --transform-skew-x: -3deg;
+  }
+
+  .xl\:focus\:skew-y-0:focus {
+    --transform-skew-y: 0;
+  }
+
+  .xl\:focus\:skew-y-3:focus {
+    --transform-skew-y: 3deg;
+  }
+
+  .xl\:focus\:skew-y-6:focus {
+    --transform-skew-y: 6deg;
+  }
+
+  .xl\:focus\:skew-y-12:focus {
+    --transform-skew-y: 12deg;
+  }
+
+  .xl\:focus\:-skew-y-12:focus {
+    --transform-skew-y: -12deg;
+  }
+
+  .xl\:focus\:-skew-y-6:focus {
+    --transform-skew-y: -6deg;
+  }
+
+  .xl\:focus\:-skew-y-3:focus {
+    --transform-skew-y: -3deg;
+  }
+
+  .xl\:transition-none {
+    transition-property: none;
+  }
+
+  .xl\:transition-all {
+    transition-property: all;
+  }
+
+  .xl\:transition {
+    transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform;
+  }
+
+  .xl\:transition-colors {
+    transition-property: background-color, border-color, color, fill, stroke;
+  }
+
+  .xl\:transition-opacity {
+    transition-property: opacity;
+  }
+
+  .xl\:transition-shadow {
+    transition-property: box-shadow;
+  }
+
+  .xl\:transition-transform {
+    transition-property: transform;
+  }
+
+  .xl\:ease-linear {
+    transition-timing-function: linear;
+  }
+
+  .xl\:ease-in {
+    transition-timing-function: cubic-bezier(0.4, 0, 1, 1);
+  }
+
+  .xl\:ease-out {
+    transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
+  }
+
+  .xl\:ease-in-out {
+    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+  }
+
+  .xl\:duration-75 {
+    transition-duration: 75ms;
+  }
+
+  .xl\:duration-100 {
+    transition-duration: 100ms;
+  }
+
+  .xl\:duration-150 {
+    transition-duration: 150ms;
+  }
+
+  .xl\:duration-200 {
+    transition-duration: 200ms;
+  }
+
+  .xl\:duration-300 {
+    transition-duration: 300ms;
+  }
+
+  .xl\:duration-500 {
+    transition-duration: 500ms;
+  }
+
+  .xl\:duration-700 {
+    transition-duration: 700ms;
+  }
+
+  .xl\:duration-1000 {
+    transition-duration: 1000ms;
+  }
+
+  .xl\:delay-75 {
+    transition-delay: 75ms;
+  }
+
+  .xl\:delay-100 {
+    transition-delay: 100ms;
+  }
+
+  .xl\:delay-150 {
+    transition-delay: 150ms;
+  }
+
+  .xl\:delay-200 {
+    transition-delay: 200ms;
+  }
+
+  .xl\:delay-300 {
+    transition-delay: 300ms;
+  }
+
+  .xl\:delay-500 {
+    transition-delay: 500ms;
+  }
+
+  .xl\:delay-700 {
+    transition-delay: 700ms;
+  }
+
+  .xl\:delay-1000 {
+    transition-delay: 1000ms;
+  }
+
+  .xl\:animate-none {
+    -webkit-animation: none;
+            animation: none;
+  }
+
+  .xl\:animate-spin {
+    -webkit-animation: spin 1s linear infinite;
+            animation: spin 1s linear infinite;
+  }
+
+  .xl\:animate-ping {
+    -webkit-animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+            animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+  }
+
+  .xl\:animate-pulse {
+    -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+            animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+  }
+
+  .xl\:animate-bounce {
+    -webkit-animation: bounce 1s infinite;
+            animation: bounce 1s infinite;
+  }
+}
diff --git a/users/wpcarro/website/sandbox/learnpianochords/registry.dat b/users/wpcarro/website/sandbox/learnpianochords/registry.dat
new file mode 100644
index 0000000000..a73307ccda
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/registry.dat
Binary files differdiff --git a/users/wpcarro/website/sandbox/learnpianochords/shell.nix b/users/wpcarro/website/sandbox/learnpianochords/shell.nix
new file mode 100644
index 0000000000..afcc0f4d36
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/shell.nix
@@ -0,0 +1,9 @@
+{ pkgs, ... }:
+
+pkgs.mkShell {
+  buildInputs = with pkgs.elmPackages; [
+    elm
+    elm-format
+    elm-live
+  ];
+}
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/FlashCard.elm b/users/wpcarro/website/sandbox/learnpianochords/src/FlashCard.elm
new file mode 100644
index 0000000000..a491752939
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/FlashCard.elm
@@ -0,0 +1,42 @@
+module FlashCard exposing (render)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Responsive
+import State
+import Tailwind
+import Theory
+
+
+render :
+    { chord : Theory.Chord
+    , visible : Bool
+    }
+    -> Html State.Msg
+render { chord, visible } =
+    let
+        classes =
+            [ "bg-white"
+            , "fixed"
+            , "top-0"
+            , "left-0"
+            , "z-30"
+            , "w-screen"
+            , "h-screen"
+            , Tailwind.if_ visible "opacity-100" "opacity-0"
+            ]
+    in
+    button
+        [ classes |> Tailwind.use |> class ]
+        [ h1
+            [ [ "text-center"
+              , "transform"
+              , "-rotate-90"
+              , Responsive.h1
+              ]
+                |> Tailwind.use
+                |> class
+            ]
+            [ text (Theory.viewChord chord) ]
+        ]
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Icon.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Icon.elm
new file mode 100644
index 0000000000..2c8626b092
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Icon.elm
@@ -0,0 +1,44 @@
+module Icon exposing (..)
+
+import Svg exposing (node, svg)
+import Svg.Attributes exposing (..)
+import UI
+
+
+svgColor color =
+    let
+        classes =
+            case color of
+                UI.Primary ->
+                    [ "text-gray-500", "fill-current" ]
+
+                UI.Secondary ->
+                    [ "text-gray-300", "fill-current" ]
+    in
+    class <| String.join " " classes
+
+
+cog =
+    svg [ class "icon-cog", viewBox "0 0 24 24", xmlLang "http://www.w3.org/2000/svg" ]
+        [ Svg.path
+            [ svgColor UI.Primary
+            , d "M6.8 3.45c.87-.52 1.82-.92 2.83-1.17a2.5 2.5 0 0 0 4.74 0c1.01.25 1.96.65 2.82 1.17a2.5 2.5 0 0 0 3.36 3.36c.52.86.92 1.8 1.17 2.82a2.5 2.5 0 0 0 0 4.74c-.25 1.01-.65 1.96-1.17 2.82a2.5 2.5 0 0 0-3.36 3.36c-.86.52-1.8.92-2.82 1.17a2.5 2.5 0 0 0-4.74 0c-1.01-.25-1.96-.65-2.82-1.17a2.5 2.5 0 0 0-3.36-3.36 9.94 9.94 0 0 1-1.17-2.82 2.5 2.5 0 0 0 0-4.74c.25-1.01.65-1.96 1.17-2.82a2.5 2.5 0 0 0 3.36-3.36zM12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"
+            , fill "red"
+            ]
+            []
+        , node "circle"
+            [ svgColor UI.Secondary, cx "12", cy "12", r "2" ]
+            []
+        ]
+
+
+close =
+    svg [ class "icon-close", viewBox "0 0 24 24", xmlLang "http://www.w3.org/2000/svg" ]
+        [ Svg.path
+            [ svgColor UI.Primary
+            , d "M15.78 14.36a1 1 0 0 1-1.42 1.42l-2.82-2.83-2.83 2.83a1 1 0 1 1-1.42-1.42l2.83-2.82L7.3 8.7a1 1 0 0 1 1.42-1.42l2.83 2.83 2.82-2.83a1 1 0 0 1 1.42 1.42l-2.83 2.83 2.83 2.82z"
+            , fill "red"
+            , fillRule "evenodd"
+            ]
+            []
+        ]
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Main.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Main.elm
new file mode 100644
index 0000000000..b066fb2f6f
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Main.elm
@@ -0,0 +1,44 @@
+module Main exposing (main)
+
+import Browser
+import Html exposing (..)
+import Misc
+import Overview
+import Practice
+import Preferences
+import State
+import Time exposing (..)
+
+
+subscriptions : State.Model -> Sub State.Msg
+subscriptions model =
+    if model.isPaused then
+        Sub.none
+
+    else
+        Sub.batch
+            [ Time.every (model.tempo * 2 |> Misc.bpmToMilliseconds |> toFloat) (\_ -> State.ToggleFlashCard)
+            , Time.every (model.tempo |> Misc.bpmToMilliseconds |> toFloat) (\_ -> State.NextChord)
+            ]
+
+
+view : State.Model -> Html State.Msg
+view model =
+    case model.view of
+        State.Preferences ->
+            Preferences.render model
+
+        State.Practice ->
+            Practice.render model
+
+        State.Overview ->
+            Overview.render model
+
+
+main =
+    Browser.element
+        { init = \() -> ( State.init, Cmd.none )
+        , subscriptions = subscriptions
+        , update = State.update
+        , view = view
+        }
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Misc.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Misc.elm
new file mode 100644
index 0000000000..288d7a825f
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Misc.elm
@@ -0,0 +1,59 @@
+module Misc exposing (..)
+
+import Array exposing (Array)
+
+
+comesAfter : a -> List a -> Maybe a
+comesAfter x xs =
+    case xs of
+        [] ->
+            Nothing
+
+        _ :: [] ->
+            Nothing
+
+        y :: z :: rest ->
+            if y == x then
+                Just z
+
+            else
+                comesAfter x (z :: rest)
+
+
+comesBefore : a -> List a -> Maybe a
+comesBefore x xs =
+    case xs of
+        [] ->
+            Nothing
+
+        _ :: [] ->
+            Nothing
+
+        y :: z :: rest ->
+            if z == x then
+                Just y
+
+            else
+                comesBefore x (z :: rest)
+
+
+find : (a -> Bool) -> List a -> Maybe a
+find pred xs =
+    case xs |> List.filter pred of
+        [] ->
+            Nothing
+
+        x :: _ ->
+            Just x
+
+
+{-| Return the number of milliseconds that elapse during an interval in a
+`target` bpm.
+-}
+bpmToMilliseconds : Int -> Int
+bpmToMilliseconds target =
+    let
+        msPerMinute =
+            1000 * 60
+    in
+    round (toFloat msPerMinute / toFloat target)
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Overview.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Overview.elm
new file mode 100644
index 0000000000..628b52d79d
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Overview.elm
@@ -0,0 +1,122 @@
+module Overview exposing (render)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Responsive
+import State
+import Tailwind
+import UI
+
+
+header1 : String -> Html msg
+header1 copy =
+    h2
+        [ [ "text-center"
+          , "pt-24"
+          , "pb-12"
+          , Responsive.h1
+          ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ text copy ]
+
+
+header2 : String -> Html msg
+header2 copy =
+    h2
+        [ [ "text-center"
+          , "pb-10"
+          , Responsive.h2
+          ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ text copy ]
+
+
+paragraph : String -> Html msg
+paragraph copy =
+    p
+        [ [ "pb-10"
+          , Responsive.h3
+          ]
+            |> Tailwind.use
+            |> class
+        ]
+        [ text copy ]
+
+
+sect : { title : String, copy : List String } -> Html msg
+sect { title, copy } =
+    section [] (header2 title :: (copy |> List.map paragraph))
+
+
+numberedList : List String -> Html msg
+numberedList items =
+    ol
+        [ [ "list-inside"
+          , "list-decimal"
+          , Responsive.h3
+          ]
+            |> Tailwind.use
+            |> class
+        ]
+        (items |> List.map (\x -> li [ [ "pb-10" ] |> Tailwind.use |> class ] [ text x ]))
+
+
+render : State.Model -> Html State.Msg
+render model =
+    div [ [ "container", "mx-auto" ] |> Tailwind.use |> class ]
+        [ header1 "Welcome to LearnPianoChords.app!"
+        , paragraph """
+                     Learn Piano Chords helps piano players master chords.
+                     """
+        , paragraph """
+                     Chords are the building blocks songwriters use to create
+                     music. Whether you're a performer or songwriter, you need
+                     to understand chords to unlock your full musical potential.
+                     """
+        , paragraph """
+                     I think that if practicing is enjoyable, students will
+                     practice more. Practice doesnโ€™t make perfect; perfect
+                     practice makes perfect.
+                     """
+        , section []
+            [ header2 "Ready to get started?"
+            , numberedList
+                [ """
+                   Sit down at the piano.
+                   """
+                , """
+                   Set the tempo at which you would like to practice.
+                   """
+                , """
+                   Select the key or keys in which you would like to
+                   practice.
+                   """
+                , """
+                   When you are ready, close the preferences pane. We will show
+                   you the name of a chord, and you should play that chord on
+                   the piano.
+                 """
+                , """
+                   If you don't know how to play the chord, toggle the piano
+                   viewer to see the notes.
+                   """
+                , """
+                   At any point while you're training, press the screen to pause
+                   or resume your practice.
+                   """
+                ]
+            ]
+        , div [ [ "text-center", "py-20" ] |> Tailwind.use |> class ]
+            [ UI.simpleButton
+                { label = "Let's get started"
+                , handleClick = State.SetView State.Preferences
+                , color = UI.Secondary
+                , classes = []
+                }
+            ]
+        ]
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Piano.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Piano.elm
new file mode 100644
index 0000000000..d231f14674
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Piano.elm
@@ -0,0 +1,194 @@
+module Piano exposing (render)
+
+import Browser
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import List.Extra
+import Theory
+import UI
+
+
+type alias KeyMarkup a =
+    { offset : Int
+    , isHighlit : Bool
+    , note : Theory.Note
+    , isRootNote : Bool
+    }
+    -> Html a
+
+
+type alias Props =
+    { chord : Maybe Theory.Chord
+    , firstNote : Theory.Note
+    , lastNote : Theory.Note
+    }
+
+
+naturalThickness : Int
+naturalThickness =
+    105
+
+
+accidentalThickness : Int
+accidentalThickness =
+    round (toFloat naturalThickness / 2.0)
+
+
+{-| Convert an integer into its pixel representation for CSS.
+-}
+pixelate : Int -> String
+pixelate x =
+    String.fromInt x ++ "px"
+
+
+{-| Return the markup for either a white or a black key.
+-}
+pianoKey : KeyMarkup a
+pianoKey { offset, isHighlit, note, isRootNote } =
+    let
+        { natColor, accColor, hiColor, rootColor } =
+            { natColor = "bg-white"
+            , accColor = "bg-black"
+            , hiColor = "bg-red-400"
+            , rootColor = "bg-red-600"
+            }
+
+        sharedClasses =
+            [ "box-border"
+            , "absolute"
+            , "border"
+            , "border-black"
+            ]
+
+        { keyLength, keyThickness, keyColor, offsetEdge, extraClasses } =
+            case Theory.keyClass note of
+                Theory.Natural ->
+                    { keyLength = "w-screen"
+                    , keyThickness = naturalThickness
+                    , keyColor = natColor
+                    , offsetEdge = "top"
+                    , extraClasses = []
+                    }
+
+                Theory.Accidental ->
+                    { keyLength = "w-2/3"
+                    , keyThickness = accidentalThickness
+                    , keyColor = accColor
+                    , offsetEdge = "top"
+                    , extraClasses = [ "z-10" ]
+                    }
+    in
+    div
+        [ class
+            (case ( isHighlit, isRootNote ) of
+                ( False, _ ) ->
+                    keyColor
+
+                ( True, True ) ->
+                    rootColor
+
+                ( True, False ) ->
+                    hiColor
+            )
+        , class keyLength
+        , style "height" (pixelate keyThickness)
+        , style offsetEdge (String.fromInt offset ++ "px")
+        , class <| String.join " " (List.concat [ sharedClasses, extraClasses ])
+        ]
+        []
+
+
+{-| A section of the piano consisting of all twelve notes.
+-}
+keys :
+    { start : Theory.Note
+    , end : Theory.Note
+    , highlitNotes : List Theory.Note
+    , rootNote : Maybe Theory.Note
+    }
+    -> List (Html a)
+keys { start, end, highlitNotes, rootNote } =
+    let
+        isHighlit note =
+            List.member note highlitNotes
+
+        spacing prevOffset prev curr =
+            case ( Theory.keyClass prev, Theory.keyClass curr ) of
+                ( Theory.Natural, Theory.Accidental ) ->
+                    prevOffset + naturalThickness - round (toFloat accidentalThickness / 2)
+
+                ( Theory.Accidental, Theory.Natural ) ->
+                    prevOffset + round (toFloat accidentalThickness / 2)
+
+                ( Theory.Natural, Theory.Natural ) ->
+                    prevOffset + naturalThickness
+
+                -- This pattern should never hit.
+                _ ->
+                    prevOffset
+
+        ( _, _, notes ) =
+            Theory.notesFromRange start end
+                |> List.reverse
+                |> List.foldl
+                    (\curr ( prevOffset, prev, result ) ->
+                        case ( prevOffset, prev ) of
+                            ( Nothing, Nothing ) ->
+                                ( Just 0
+                                , Just curr
+                                , pianoKey
+                                    { offset = 0
+                                    , isHighlit = List.member curr highlitNotes
+                                    , note = curr
+                                    , isRootNote =
+                                        rootNote
+                                            |> Maybe.map (\x -> x == curr)
+                                            |> Maybe.withDefault False
+                                    }
+                                    :: result
+                                )
+
+                            ( Just po, Just p ) ->
+                                let
+                                    offset =
+                                        spacing po p curr
+                                in
+                                ( Just offset
+                                , Just curr
+                                , pianoKey
+                                    { offset = offset
+                                    , isHighlit = List.member curr highlitNotes
+                                    , note = curr
+                                    , isRootNote =
+                                        rootNote
+                                            |> Maybe.map (\x -> x == curr)
+                                            |> Maybe.withDefault False
+                                    }
+                                    :: result
+                                )
+
+                            -- This pattern should never hit.
+                            _ ->
+                                ( Nothing, Nothing, [] )
+                    )
+                    ( Nothing, Nothing, [] )
+    in
+    notes
+
+
+{-| Return the HTML that renders a piano representation.
+-}
+render : Props -> Html a
+render { chord } =
+    div [ style "display" "flex" ]
+        (keys
+            { start = Theory.G3
+            , end = Theory.C6
+            , rootNote = chord |> Maybe.map .note
+            , highlitNotes =
+                chord
+                    |> Maybe.andThen Theory.notesForChord
+                    |> Maybe.withDefault []
+            }
+        )
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Practice.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Practice.elm
new file mode 100644
index 0000000000..5d87bcee50
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Practice.elm
@@ -0,0 +1,61 @@
+module Practice exposing (render)
+
+import FlashCard
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Icon
+import Piano
+import State
+import Tailwind
+import Theory
+import UI
+
+
+openPreferences : Html State.Msg
+openPreferences =
+    button
+        [ class "w-48 h-48 absolute left-0 top-0 z-50"
+        , onClick (State.SetView State.Preferences)
+        ]
+        [ Icon.cog ]
+
+
+render : State.Model -> Html State.Msg
+render model =
+    let
+        ( handleClick, buttonText ) =
+            if model.isPaused then
+                ( State.Play, "Tap to practice" )
+
+            else
+                ( State.Pause, "" )
+    in
+    div []
+        [ openPreferences
+        , case model.selectedChord of
+            Just chord ->
+                FlashCard.render
+                    { chord = chord
+                    , visible = model.showFlashCard
+                    }
+
+            Nothing ->
+                -- Here I'm abusing the overlayButton component to render text
+                -- horizontally. I should support a UI component for this.
+                UI.overlayButton
+                    { label = "Get ready..."
+                    , handleClick = State.DoNothing
+                    , isVisible = True
+                    }
+        , UI.overlayButton
+            { label = buttonText
+            , handleClick = handleClick
+            , isVisible = model.isPaused
+            }
+        , Piano.render
+            { chord = model.selectedChord
+            , firstNote = model.firstNote
+            , lastNote = model.lastNote
+            }
+        ]
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Preferences.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Preferences.elm
new file mode 100644
index 0000000000..59e6c8234c
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Preferences.elm
@@ -0,0 +1,148 @@
+module Preferences exposing (render)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Icon
+import Responsive
+import State
+import Tailwind
+import Tempo
+import Theory
+import UI
+
+
+selectKey :
+    State.Model
+    ->
+        { relativeMajor : Theory.Key
+        , relativeMinor : Theory.Key
+        }
+    -> Html State.Msg
+selectKey model { relativeMajor, relativeMinor } =
+    let
+        active key =
+            List.member key model.whitelistedKeys
+
+        buttonLabel major minor =
+            Theory.viewKey major ++ ", " ++ Theory.viewKey minor
+    in
+    div [ class "flex pt-0" ]
+        [ UI.textToggleButton
+            { label = buttonLabel relativeMajor relativeMinor
+            , handleClick = State.ToggleKey relativeMajor
+            , classes = [ "flex-1" ]
+            , toggled = active relativeMajor
+            }
+        ]
+
+
+inversionCheckboxes : State.Model -> Html State.Msg
+inversionCheckboxes model =
+    div []
+        [ h2
+            [ [ "text-gray-500"
+              , "text-center"
+              , "pt-10"
+              , Responsive.h2
+              ]
+                |> Tailwind.use
+                |> class
+            ]
+            [ text "Select inversions" ]
+        , ul
+            [ [ "flex", "justify-center" ] |> Tailwind.use |> class ]
+            (Theory.allInversions
+                |> List.map
+                    (\inversion ->
+                        li []
+                            [ UI.textToggleButton
+                                { label = Theory.inversionName inversion
+                                , handleClick = State.ToggleInversion inversion
+                                , classes = []
+                                , toggled = List.member inversion model.whitelistedInversions
+                                }
+                            ]
+                    )
+            )
+        ]
+
+
+keyCheckboxes : State.Model -> Html State.Msg
+keyCheckboxes model =
+    let
+        majorKey pitchClass =
+            { pitchClass = pitchClass, mode = Theory.MajorMode }
+
+        minorKey pitchClass =
+            { pitchClass = pitchClass, mode = Theory.MinorMode }
+
+        circleOfFifths =
+            [ ( Theory.C, Theory.A )
+            , ( Theory.G, Theory.E )
+            , ( Theory.D, Theory.B )
+            , ( Theory.A, Theory.F_sharp )
+            , ( Theory.E, Theory.C_sharp )
+            , ( Theory.B, Theory.G_sharp )
+            , ( Theory.F_sharp, Theory.D_sharp )
+            , ( Theory.C_sharp, Theory.A_sharp )
+            , ( Theory.G_sharp, Theory.F )
+            , ( Theory.D_sharp, Theory.C )
+            , ( Theory.A_sharp, Theory.G )
+            , ( Theory.F, Theory.D )
+            ]
+    in
+    div []
+        [ h2
+            [ [ "text-gray-500"
+              , "text-center"
+              , "pt-10"
+              , Responsive.h2
+              ]
+                |> Tailwind.use
+                |> class
+            ]
+            [ text "Select keys" ]
+        , ul []
+            (circleOfFifths
+                |> List.map
+                    (\( major, minor ) ->
+                        selectKey model
+                            { relativeMajor = majorKey major
+                            , relativeMinor = minorKey minor
+                            }
+                    )
+            )
+        ]
+
+
+closePreferences : Html State.Msg
+closePreferences =
+    button
+        [ [ "w-48"
+          , "lg:w-32"
+          , "h-48"
+          , "lg:h-32"
+          , "absolute"
+          , "right-0"
+          , "top-0"
+          , "z-10"
+          ]
+            |> Tailwind.use
+            |> class
+        , onClick (State.SetView State.Practice)
+        ]
+        [ Icon.close ]
+
+
+render : State.Model -> Html State.Msg
+render model =
+    div [ class "pt-10 pb-20 px-10" ]
+        [ closePreferences
+        , Tempo.render
+            { tempo = model.tempo
+            , handleInput = State.SetTempo
+            }
+        , inversionCheckboxes model
+        , keyCheckboxes model
+        ]
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Responsive.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Responsive.elm
new file mode 100644
index 0000000000..5d97161df6
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Responsive.elm
@@ -0,0 +1,19 @@
+module Responsive exposing (..)
+
+{-| Returns a string containing all of the Tailwind selectors we use to size
+h2-sized elements across various devices. -}
+h1 : String
+h1 =
+    "text-6xl lg:text-4xl"
+
+{-| Returns a string containing all of the Tailwind selectors we use to size
+h2-sized elements across various devices. -}
+h2 : String
+h2 =
+    "text-5xl lg:text-3xl"
+
+{-| Returns a string containing all of the Tailwind selectors we use to size
+h3-sized elements across various devices. -}
+h3 : String
+h3 =
+    "text-4xl lg:text-2xl"
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/State.elm b/users/wpcarro/website/sandbox/learnpianochords/src/State.elm
new file mode 100644
index 0000000000..678fb0f9aa
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/State.elm
@@ -0,0 +1,179 @@
+module State exposing (..)
+
+import Random
+import Random.List
+import Theory
+
+
+type Msg
+    = NextChord
+    | NewChord Theory.Chord
+    | Play
+    | Pause
+    | SetTempo String
+    | ToggleInversion Theory.ChordInversion
+    | ToggleKey Theory.Key
+    | DoNothing
+    | SetView View
+    | ToggleFlashCard
+
+
+type View
+    = Preferences
+    | Practice
+    | Overview
+
+
+type alias Model =
+    { whitelistedChords : List Theory.Chord
+    , whitelistedChordTypes : List Theory.ChordType
+    , whitelistedInversions : List Theory.ChordInversion
+    , whitelistedPitchClasses : List Theory.PitchClass
+    , whitelistedKeys : List Theory.Key
+    , selectedChord : Maybe Theory.Chord
+    , isPaused : Bool
+    , tempo : Int
+    , firstNote : Theory.Note
+    , lastNote : Theory.Note
+    , view : View
+    , showFlashCard : Bool
+    }
+
+
+{-| The initial state for the application.
+-}
+init : Model
+init =
+    let
+        ( firstNote, lastNote ) =
+            ( Theory.C3, Theory.C6 )
+
+        inversions =
+            [ Theory.Root ]
+
+        chordTypes =
+            Theory.allChordTypes
+
+        pitchClasses =
+            Theory.allPitchClasses
+
+        keys =
+            [ { pitchClass = Theory.C, mode = Theory.MajorMode } ]
+    in
+    { whitelistedChords =
+        keys
+            |> List.concatMap Theory.chordsForKey
+            |> List.filter (\chord -> List.member chord.chordInversion inversions)
+    , whitelistedChordTypes = chordTypes
+    , whitelistedInversions = inversions
+    , whitelistedPitchClasses = pitchClasses
+    , whitelistedKeys = keys
+    , selectedChord = Nothing
+    , isPaused = True
+    , tempo = 10
+    , firstNote = firstNote
+    , lastNote = lastNote
+    , view = Overview
+    , showFlashCard = True
+    }
+
+
+{-| Now that we have state, we need a function to change the state.
+-}
+update : Msg -> Model -> ( Model, Cmd Msg )
+update msg model =
+    case msg of
+        DoNothing ->
+            ( model, Cmd.none )
+
+        SetView x ->
+            ( { model
+                | view = x
+                , isPaused = True
+              }
+            , Cmd.none
+            )
+
+        NewChord chord ->
+            ( { model | selectedChord = Just chord }
+            , Cmd.none
+            )
+
+        NextChord ->
+            ( model
+            , Random.generate
+                (\x ->
+                    case x of
+                        ( Just chord, _ ) ->
+                            NewChord chord
+
+                        ( Nothing, _ ) ->
+                            DoNothing
+                )
+                (Random.List.choose model.whitelistedChords)
+            )
+
+        Play ->
+            ( { model | isPaused = False }
+            , Cmd.none
+            )
+
+        Pause ->
+            ( { model | isPaused = True }
+            , Cmd.none
+            )
+
+        ToggleInversion inversion ->
+            let
+                inversions =
+                    if List.member inversion model.whitelistedInversions then
+                        List.filter ((/=) inversion) model.whitelistedInversions
+
+                    else
+                        inversion :: model.whitelistedInversions
+            in
+            ( { model
+                | whitelistedInversions = inversions
+                , whitelistedChords =
+                    model.whitelistedKeys
+                        |> List.concatMap Theory.chordsForKey
+                        |> List.filter (\chord -> List.member chord.chordInversion inversions)
+              }
+            , Cmd.none
+            )
+
+        ToggleKey key ->
+            let
+                keys =
+                    if List.member key model.whitelistedKeys then
+                        List.filter ((/=) key) model.whitelistedKeys
+
+                    else
+                        key :: model.whitelistedKeys
+            in
+            ( { model
+                | whitelistedKeys = keys
+                , whitelistedChords =
+                    keys
+                        |> List.concatMap Theory.chordsForKey
+                        |> List.filter (\chord -> List.member chord.chordInversion model.whitelistedInversions)
+                , selectedChord = Nothing
+              }
+            , Cmd.none
+            )
+
+        SetTempo tempo ->
+            ( { model
+                | tempo =
+                    case String.toInt tempo of
+                        Just x ->
+                            x
+
+                        Nothing ->
+                            model.tempo
+              }
+            , Cmd.none
+            )
+
+        ToggleFlashCard ->
+            ( { model | showFlashCard = not model.showFlashCard }, Cmd.none )
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Tailwind.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Tailwind.elm
new file mode 100644
index 0000000000..57d419db5a
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Tailwind.elm
@@ -0,0 +1,29 @@
+module Tailwind exposing (..)
+
+{-| Functions to make Tailwind development in Elm even more pleasant.
+-}
+
+
+{-| Conditionally use `class` selection when `condition` is true.
+-}
+when : Bool -> String -> String
+when condition class =
+    if condition then
+        class
+
+    else
+        ""
+
+
+if_ : Bool -> String -> String -> String
+if_ condition whenTrue whenFalse =
+    if condition then
+        whenTrue
+
+    else
+        whenFalse
+
+
+use : List String -> String
+use styles =
+    String.join " " styles
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Tempo.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Tempo.elm
new file mode 100644
index 0000000000..041313614f
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Tempo.elm
@@ -0,0 +1,33 @@
+module Tempo exposing (render)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Responsive
+import Tailwind
+import UI
+
+
+type alias Props msg =
+    { tempo : Int
+    , handleInput : String -> msg
+    }
+
+
+render : Props msg -> Html msg
+render { tempo, handleInput } =
+    div [ class "text-center" ]
+        [ p
+            [ [ "py-10"
+              , Responsive.h2
+              ]
+                |> Tailwind.use
+                |> class
+            ]
+            [ text (String.fromInt tempo ++ " BPM") ]
+        , UI.textField
+            { placeholderText = "Set tempo..."
+            , handleInput = handleInput
+            , classes = []
+            }
+        ]
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/Theory.elm b/users/wpcarro/website/sandbox/learnpianochords/src/Theory.elm
new file mode 100644
index 0000000000..7f54832c97
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/Theory.elm
@@ -0,0 +1,1100 @@
+module Theory exposing (..)
+
+import Array exposing (Array)
+import Dict exposing (Dict)
+import List.Extra
+import Maybe.Extra
+import Misc
+
+
+{-| Notes are the individuals sounds that we use to create music. Think: "do re
+mi fa so la ti do".
+
+Note: Technically a "C-sharp" is also a "D-flat", but I will model accidentals
+(i.e. sharps and flats) as sharps and represent the ambiguity when I render the
+underlying state of the application.
+
+Note: There are "notes" like A, B, D-flat, and then there are notes like "middle
+C", also denoted in scientific pitch notation as C4. I'm unsure of what to call
+each of these, and my application does not model scientific pitch notation yet,
+so these non-scientific pitch denote values are "notes" for now.
+
+-}
+type Note
+    = C1
+    | C_sharp1
+    | D1
+    | D_sharp1
+    | E1
+    | F1
+    | F_sharp1
+    | G1
+    | G_sharp1
+    | A1
+    | A_sharp1
+    | B1
+    | C2
+    | C_sharp2
+    | D2
+    | D_sharp2
+    | E2
+    | F2
+    | F_sharp2
+    | G2
+    | G_sharp2
+    | A2
+    | A_sharp2
+    | B2
+    | C3
+    | C_sharp3
+    | D3
+    | D_sharp3
+    | E3
+    | F3
+    | F_sharp3
+    | G3
+    | G_sharp3
+    | A3
+    | A_sharp3
+    | B3
+    | C4
+    | C_sharp4
+    | D4
+    | D_sharp4
+    | E4
+    | F4
+    | F_sharp4
+    | G4
+    | G_sharp4
+    | A4
+    | A_sharp4
+    | B4
+    | C5
+    | C_sharp5
+    | D5
+    | D_sharp5
+    | E5
+    | F5
+    | F_sharp5
+    | G5
+    | G_sharp5
+    | A5
+    | A_sharp5
+    | B5
+    | C6
+    | C_sharp6
+    | D6
+    | D_sharp6
+    | E6
+    | F6
+    | F_sharp6
+    | G6
+    | G_sharp6
+    | A6
+    | A_sharp6
+    | B6
+    | C7
+    | C_sharp7
+    | D7
+    | D_sharp7
+    | E7
+    | F7
+    | F_sharp7
+    | G7
+    | G_sharp7
+    | A7
+    | A_sharp7
+    | B7
+    | C8
+
+
+{-| I alluded to this concept in the Note type's documentation. These are the
+letters of notes. For instance C2, C3, C4 are all instances of C.
+-}
+type PitchClass
+    = C
+    | C_sharp
+    | D
+    | D_sharp
+    | E
+    | F
+    | F_sharp
+    | G
+    | G_sharp
+    | A
+    | A_sharp
+    | B
+
+
+{-| Encode whether you are traversing "up" or "down" intervals
+-}
+type StepDirection
+    = Up
+    | Down
+
+
+{-| One can measure the difference between between notes using intervals.
+-}
+type Interval
+    = Half
+    | NHalves Int
+    | Whole
+    | MajorThird
+    | MinorThird
+    | PerfectFifth
+    | AugmentedFifth
+    | DiminishedFifth
+    | MajorSeventh
+    | DominantSeventh
+
+
+{-| Add direction to a distance on the piano.
+-}
+type alias IntervalVector =
+    { interval : Interval
+    , direction : StepDirection
+    }
+
+
+{-| A bundle of notes which are usually, but not necessarily harmonious.
+-}
+type alias Chord =
+    { note : Note
+    , chordType : ChordType
+    , chordInversion : ChordInversion
+    }
+
+
+{-| Many possible chords exist. This type encodes the possibilities. I am
+tempted to model these in a more "DRY" way, but I worry that this abstraction
+may cause more problems than it solves.
+-}
+type ChordType
+    = Major
+    | Sus2
+    | Sus4
+    | Major7
+    | MajorDominant7
+    | Minor
+    | MinorMajor7
+    | MinorDominant7
+    | Augmented
+    | AugmentedDominant7
+    | Diminished
+    | DiminishedDominant7
+    | DiminishedMajor7
+
+
+{-| On a piano, a triad can be played three ways. As a rule-of-thumb, The number
+of ways a pianist can play a chord is equal to the number of notes in the chord
+itself.
+-}
+type ChordInversion
+    = Root
+    | First
+    | Second
+
+
+{-| Whether a given note is a white key or a black key.
+-}
+type KeyClass
+    = Natural
+    | Accidental
+
+
+{-| Songs are written in one or more keys, which define the notes and therefore
+chords that harmonize with one another.
+-}
+type alias Key =
+    { pitchClass : PitchClass
+    , mode : Mode
+    }
+
+
+{-| We create "scales" by enumerating the notes of a given key. These keys are
+defined by the "tonic" note and the "mode". I thought about including Ionian,
+Dorian, Phrygian, etc., but in the I would like to avoid over-abstracting this
+early on, so I'm going to err on the side of overly concrete until I have a
+better idea of the extent of this project.
+-}
+type Mode
+    = BluesMode
+    | MajorMode
+    | MinorMode
+
+
+type alias NoteMetadata =
+    { note : Note
+    , label : String
+    , pitchClass : PitchClass
+    , natural : Bool
+    }
+
+
+{-| An integer representing which note in a given scale to play.
+-}
+type alias ScaleDegree =
+    Int
+
+
+{-| Returns the Note in the cental octave of the piano for a given
+PitchClass. For example, C4 -- or "middle C" -- for C.
+-}
+noteInCentralOctave : PitchClass -> Note
+noteInCentralOctave pitchClass =
+    case pitchClass of
+        C ->
+            C4
+
+        C_sharp ->
+            C_sharp4
+
+        D ->
+            D4
+
+        D_sharp ->
+            D_sharp4
+
+        E ->
+            E4
+
+        F ->
+            F4
+
+        F_sharp ->
+            F_sharp4
+
+        G ->
+            G4
+
+        G_sharp ->
+            G_sharp4
+
+        A ->
+            A4
+
+        A_sharp ->
+            A_sharp4
+
+        B ->
+            B4
+
+
+{-| Return the human-readable version of a chord inversion.
+-}
+inversionName : ChordInversion -> String
+inversionName inversion =
+    case inversion of
+        Root ->
+            "Root"
+
+        First ->
+            "First"
+
+        Second ->
+            "Second"
+
+
+{-| Return the human-readable version of a chord type.
+-}
+chordTypeName : ChordType -> String
+chordTypeName chordType =
+    case chordType of
+        Major ->
+            "major"
+
+        Sus2 ->
+            "suspended 2"
+
+        Sus4 ->
+            "suspended 4"
+
+        Major7 ->
+            "major 7th"
+
+        MajorDominant7 ->
+            "major dominant 7th"
+
+        Minor ->
+            "minor"
+
+        MinorMajor7 ->
+            "minor major 7th"
+
+        MinorDominant7 ->
+            "minor dominant 7th"
+
+        Augmented ->
+            "augmented"
+
+        AugmentedDominant7 ->
+            "augmented dominant 7th"
+
+        Diminished ->
+            "diminished"
+
+        DiminishedDominant7 ->
+            "diminished dominant 7th"
+
+        DiminishedMajor7 ->
+            "diminished major 7th"
+
+
+{-| Return the note that is one half step away from `note` in the direction,
+`dir`.
+In the case of stepping up or down from the end of the piano, this returns a
+Maybe.
+-}
+halfStep : StepDirection -> Note -> Maybe Note
+halfStep dir note =
+    let
+        everyNote =
+            notesFromRange C2 C8
+    in
+    case dir of
+        Up ->
+            Misc.comesAfter note everyNote
+
+        Down ->
+            Misc.comesBefore note everyNote
+
+
+{-| Return a list of steps to take away from the root note to return back to the
+root note for a given mode.
+-}
+intervalsForMode : Mode -> List IntervalVector
+intervalsForMode mode =
+    let
+        up x =
+            { direction = Up, interval = x }
+
+        down x =
+            { direction = Down, interval = x }
+    in
+    case mode of
+        MajorMode ->
+            List.map up [ Whole, Whole, Half, Whole, Whole, Whole ]
+
+        MinorMode ->
+            List.map up [ Whole, Half, Whole, Whole, Half, Whole ]
+
+        BluesMode ->
+            List.map up [ MinorThird, Whole, Half, Half, MinorThird ]
+
+
+{-| Return a list of the intervals that a chord. Each interval measures
+the distance away from the root-note of the chord.
+-}
+intervalsForChordType : ChordType -> ChordInversion -> List IntervalVector
+intervalsForChordType chordType chordInversion =
+    let
+        up x =
+            { direction = Up, interval = x }
+
+        down x =
+            { direction = Down, interval = x }
+    in
+    case ( chordType, chordInversion ) of
+        -- Major
+        ( Major, Root ) ->
+            [ up MajorThird, up PerfectFifth ]
+
+        ( Major, First ) ->
+            [ down (NHalves 5), down (NHalves 8) ]
+
+        ( Major, Second ) ->
+            [ down (NHalves 5), up MajorThird ]
+
+        -- Sus2
+        ( Sus2, Root ) ->
+            [ up Whole, up PerfectFifth ]
+
+        ( Sus2, First ) ->
+            [ down (NHalves 10), down (NHalves 5) ]
+
+        ( Sus2, Second ) ->
+            [ down (NHalves 5), up Whole ]
+
+        -- Sus4
+        ( Sus4, Root ) ->
+            [ up (NHalves 5), up PerfectFifth ]
+
+        ( Sus4, First ) ->
+            [ down (NHalves 7), down (NHalves 5) ]
+
+        ( Sus4, Second ) ->
+            [ down (NHalves 5), up (NHalves 5) ]
+
+        -- Major7
+        ( Major7, Root ) ->
+            [ up MajorThird, up PerfectFifth, up MajorSeventh ]
+
+        ( Major7, First ) ->
+            down Half :: intervalsForChordType Major chordInversion
+
+        ( Major7, Second ) ->
+            down Half :: intervalsForChordType Major chordInversion
+
+        -- MajorDominant7
+        ( MajorDominant7, Root ) ->
+            up DominantSeventh :: intervalsForChordType Major chordInversion
+
+        ( MajorDominant7, First ) ->
+            down Whole :: intervalsForChordType Major chordInversion
+
+        ( MajorDominant7, Second ) ->
+            down Whole :: intervalsForChordType Major chordInversion
+
+        -- Minor
+        ( Minor, Root ) ->
+            [ up MinorThird, up PerfectFifth ]
+
+        ( Minor, First ) ->
+            [ down (NHalves 5), down (NHalves 9) ]
+
+        ( Minor, Second ) ->
+            [ down (NHalves 5), up MinorThird ]
+
+        -- MinorMajor7
+        ( MinorMajor7, Root ) ->
+            up MajorSeventh :: intervalsForChordType Minor chordInversion
+
+        ( MinorMajor7, First ) ->
+            down Half :: intervalsForChordType Minor chordInversion
+
+        ( MinorMajor7, Second ) ->
+            down Half :: intervalsForChordType Minor chordInversion
+
+        -- MinorDominant7
+        ( MinorDominant7, Root ) ->
+            up DominantSeventh :: intervalsForChordType Minor chordInversion
+
+        ( MinorDominant7, First ) ->
+            down Whole :: intervalsForChordType Minor chordInversion
+
+        ( MinorDominant7, Second ) ->
+            down Whole :: intervalsForChordType Minor chordInversion
+
+        -- Augmented
+        ( Augmented, Root ) ->
+            [ up MajorThird, up AugmentedFifth ]
+
+        ( Augmented, First ) ->
+            [ down (NHalves 8), down (NHalves 4) ]
+
+        ( Augmented, Second ) ->
+            [ down (NHalves 4), up MajorThird ]
+
+        -- AugmentedDominant7
+        ( AugmentedDominant7, Root ) ->
+            up DominantSeventh :: intervalsForChordType Augmented chordInversion
+
+        ( AugmentedDominant7, First ) ->
+            down Whole :: intervalsForChordType Augmented chordInversion
+
+        ( AugmentedDominant7, Second ) ->
+            down Whole :: intervalsForChordType Augmented chordInversion
+
+        -- Diminished
+        ( Diminished, Root ) ->
+            [ up MinorThird, up DiminishedFifth ]
+
+        ( Diminished, First ) ->
+            [ down (NHalves 6), down (NHalves 9) ]
+
+        ( Diminished, Second ) ->
+            [ down (NHalves 6), up MinorThird ]
+
+        -- DiminishedDominant7
+        ( DiminishedDominant7, Root ) ->
+            up DominantSeventh :: intervalsForChordType Diminished chordInversion
+
+        ( DiminishedDominant7, First ) ->
+            down Whole :: intervalsForChordType Diminished chordInversion
+
+        ( DiminishedDominant7, Second ) ->
+            down Whole :: intervalsForChordType Diminished chordInversion
+
+        -- DiminishedMajor7
+        ( DiminishedMajor7, Root ) ->
+            up MajorSeventh :: intervalsForChordType Diminished chordInversion
+
+        ( DiminishedMajor7, First ) ->
+            down Half :: intervalsForChordType Diminished chordInversion
+
+        ( DiminishedMajor7, Second ) ->
+            down Half :: intervalsForChordType Diminished chordInversion
+
+
+{-| Return the note in the direction, `dir`, away from `note` `s` intervals
+-}
+step : IntervalVector -> Note -> Maybe Note
+step { direction, interval } note =
+    let
+        doStep int =
+            step { direction = direction, interval = int }
+    in
+    case interval of
+        Half ->
+            halfStep direction note
+
+        NHalves n ->
+            List.repeat n
+                { direction = direction
+                , interval = Half
+                }
+                |> (\x -> walkNotes x note)
+                |> Maybe.andThen (List.reverse >> List.head)
+
+        Whole ->
+            note
+                |> doStep Half
+                |> Maybe.andThen (doStep Half)
+
+        MinorThird ->
+            note
+                |> doStep Whole
+                |> Maybe.andThen (doStep Half)
+
+        MajorThird ->
+            note
+                |> doStep Whole
+                |> Maybe.andThen (doStep Whole)
+
+        PerfectFifth ->
+            note
+                |> doStep MajorThird
+                |> Maybe.andThen (doStep MinorThird)
+
+        AugmentedFifth ->
+            note
+                |> doStep PerfectFifth
+                |> Maybe.andThen (doStep Half)
+
+        DiminishedFifth ->
+            note
+                |> doStep MajorThird
+                |> Maybe.andThen (doStep Whole)
+
+        MajorSeventh ->
+            note
+                |> doStep PerfectFifth
+                |> Maybe.andThen (doStep MajorThird)
+
+        DominantSeventh ->
+            note
+                |> doStep PerfectFifth
+                |> Maybe.andThen (doStep MinorThird)
+
+
+{-| Returns a list of all of the notes away from a give `note`.
+
+  - The 0th element is applied to `note`.
+  - The 1st element is applied to the result of the previous operation.
+  - The 2nd element is applied to the result of the previous operation.
+  - and so on...until all of the `steps` are exhausted.
+
+In the case where applying any of the steps would result in running off of
+either edge of the piano, this function returns a Nothing.
+
+-}
+walkNotes : List IntervalVector -> Note -> Maybe (List Note)
+walkNotes steps note =
+    doWalkNotes steps note [] |> Maybe.map List.reverse
+
+
+{-| Recursive helper for `walkNotes`.
+-}
+doWalkNotes : List IntervalVector -> Note -> List Note -> Maybe (List Note)
+doWalkNotes steps note result =
+    case steps of
+        [] ->
+            Just (note :: result)
+
+        s :: rest ->
+            case step s note of
+                Just x ->
+                    doWalkNotes rest x (note :: result)
+
+                Nothing ->
+                    Nothing
+
+
+{-| Return the KeyClass for a given `note`.
+-}
+keyClass : Note -> KeyClass
+keyClass note =
+    if isNatural note then
+        Natural
+
+    else
+        Accidental
+
+
+{-| Return the PitchClass for a given note.
+-}
+classifyNote : Note -> PitchClass
+classifyNote note =
+    note |> getNoteMetadata |> .pitchClass
+
+
+{-| Return a list of the notes that comprise a `chord`
+-}
+notesForChord : Chord -> Maybe (List Note)
+notesForChord { note, chordType, chordInversion } =
+    intervalsForChordType chordType chordInversion
+        |> List.map (\interval -> step interval note)
+        |> Maybe.Extra.combine
+        |> Maybe.map (\notes -> note :: notes)
+
+
+{-| Return the scale for a given `key`.
+-}
+notesForKey : Key -> List Note
+notesForKey { pitchClass, mode } =
+    let
+        origin =
+            noteInCentralOctave pitchClass
+    in
+    case walkNotes (intervalsForMode mode) origin of
+        -- We should never hit the Nothing case here.
+        Nothing ->
+            []
+
+        Just scale ->
+            scale
+
+
+{-| Return true if `note` is a black key.
+-}
+isAccidental : Note -> Bool
+isAccidental note =
+    note |> isNatural |> not
+
+
+{-| Return true if `note` is a white key.
+-}
+isNatural : Note -> Bool
+isNatural note =
+    note |> getNoteMetadata |> .natural
+
+
+{-| Return a list of all of the notes that we know about.
+Only return the notes within the range `start` and `end`.
+-}
+notesFromRange : Note -> Note -> List Note
+notesFromRange start end =
+    noteMetadata
+        |> Array.toList
+        |> List.map .note
+        |> List.Extra.dropWhile ((/=) start)
+        |> List.Extra.takeWhile ((/=) end)
+
+
+{-| Return a list of all of the chord inversions about which we know.
+-}
+allInversions : List ChordInversion
+allInversions =
+    [ Root, First, Second ]
+
+
+{-| Return a list of all of the chord types about which we know.
+-}
+allChordTypes : List ChordType
+allChordTypes =
+    [ Major
+    , Sus2
+    , Sus4
+    , Major7
+    , MajorDominant7
+    , Minor
+    , MinorMajor7
+    , MinorDominant7
+    , Augmented
+    , AugmentedDominant7
+    , Diminished
+    , DiminishedDominant7
+    , DiminishedMajor7
+    ]
+
+
+{-| Return a list of all of the key modes about which we know.
+-}
+allModes : List Mode
+allModes =
+    [ MajorMode, MinorMode, BluesMode ]
+
+
+{-| Return a list of all of the keys about which we know.
+-}
+allKeys : List Key
+allKeys =
+    allPitchClasses
+        |> List.Extra.andThen
+            (\pitchClass ->
+                allModes
+                    |> List.Extra.andThen
+                        (\mode ->
+                            [ { pitchClass = pitchClass
+                              , mode = mode
+                              }
+                            ]
+                        )
+            )
+
+
+{-| Return an array of every note on a piano.
+Note: Currently this piano has 85 keys, but modern pianos have 88 keys. I would
+prefer to have 88 keys, but it's not urgent.
+-}
+noteMetadata : Array NoteMetadata
+noteMetadata =
+    Array.fromList
+        [ { note = A1, label = "A1", pitchClass = A, natural = True }
+        , { note = A_sharp1, label = "Aโ™ฏ/Bโ™ญ1", pitchClass = A_sharp, natural = False }
+        , { note = B1, label = "B1", pitchClass = B, natural = True }
+        , { note = C1, label = "C1", pitchClass = C, natural = True }
+        , { note = C_sharp1, label = "Cโ™ฏ/Dโ™ญ1", pitchClass = C_sharp, natural = False }
+        , { note = D1, label = "D1", pitchClass = D, natural = True }
+        , { note = D_sharp1, label = "Dโ™ฏ/Eโ™ญ1", pitchClass = D_sharp, natural = False }
+        , { note = E1, label = "E1", pitchClass = E, natural = True }
+        , { note = F1, label = "F1", pitchClass = F, natural = True }
+        , { note = F_sharp1, label = "Fโ™ฏ/Gโ™ญ1", pitchClass = F_sharp, natural = False }
+        , { note = G1, label = "G1", pitchClass = G, natural = True }
+        , { note = G_sharp1, label = "Gโ™ฏ/Aโ™ญ1", pitchClass = G_sharp, natural = False }
+        , { note = A2, label = "A2", pitchClass = A, natural = True }
+        , { note = A_sharp2, label = "Aโ™ฏ/Bโ™ญ2", pitchClass = A_sharp, natural = False }
+        , { note = B2, label = "B2", pitchClass = B, natural = True }
+        , { note = C2, label = "C2", pitchClass = C, natural = True }
+        , { note = C_sharp2, label = "Cโ™ฏ/Dโ™ญ2", pitchClass = C_sharp, natural = False }
+        , { note = D2, label = "D2", pitchClass = D, natural = True }
+        , { note = D_sharp2, label = "Dโ™ฏ/Eโ™ญ2", pitchClass = D_sharp, natural = False }
+        , { note = E2, label = "E2", pitchClass = E, natural = True }
+        , { note = F2, label = "F2", pitchClass = F, natural = True }
+        , { note = F_sharp2, label = "Fโ™ฏ/Gโ™ญ2", pitchClass = F_sharp, natural = False }
+        , { note = G2, label = "G2", pitchClass = G, natural = True }
+        , { note = G_sharp2, label = "Gโ™ฏ/Aโ™ญ2", pitchClass = G_sharp, natural = False }
+        , { note = A3, label = "A3", pitchClass = A, natural = True }
+        , { note = A_sharp3, label = "Aโ™ฏ/Bโ™ญ3", pitchClass = A_sharp, natural = False }
+        , { note = B3, label = "B3", pitchClass = B, natural = True }
+        , { note = C3, label = "C3", pitchClass = C, natural = True }
+        , { note = C_sharp3, label = "Cโ™ฏ/Dโ™ญ3", pitchClass = C_sharp, natural = False }
+        , { note = D3, label = "D3", pitchClass = D, natural = True }
+        , { note = D_sharp3, label = "Dโ™ฏ/Eโ™ญ3", pitchClass = D_sharp, natural = False }
+        , { note = E3, label = "E3", pitchClass = E, natural = True }
+        , { note = F3, label = "F3", pitchClass = F, natural = True }
+        , { note = F_sharp3, label = "Fโ™ฏ/Gโ™ญ3", pitchClass = F_sharp, natural = False }
+        , { note = G3, label = "G3", pitchClass = G, natural = True }
+        , { note = G_sharp3, label = "Gโ™ฏ/Aโ™ญ3", pitchClass = G_sharp, natural = False }
+        , { note = A4, label = "A4", pitchClass = A, natural = True }
+        , { note = A_sharp4, label = "Aโ™ฏ/Bโ™ญ4", pitchClass = A_sharp, natural = False }
+        , { note = B4, label = "B4", pitchClass = B, natural = True }
+        , { note = C4, label = "C4", pitchClass = C, natural = True }
+        , { note = C_sharp4, label = "Cโ™ฏ/Dโ™ญ4", pitchClass = C_sharp, natural = False }
+        , { note = D4, label = "D4", pitchClass = D, natural = True }
+        , { note = D_sharp4, label = "Dโ™ฏ/Eโ™ญ4", pitchClass = D_sharp, natural = False }
+        , { note = E4, label = "E4", pitchClass = E, natural = True }
+        , { note = F4, label = "F4", pitchClass = F, natural = True }
+        , { note = F_sharp4, label = "Fโ™ฏ/Gโ™ญ4", pitchClass = F_sharp, natural = False }
+        , { note = G4, label = "G4", pitchClass = G, natural = True }
+        , { note = G_sharp4, label = "Gโ™ฏ/Aโ™ญ4", pitchClass = G_sharp, natural = False }
+        , { note = A5, label = "A5", pitchClass = A, natural = True }
+        , { note = A_sharp5, label = "Aโ™ฏ/Bโ™ญ5", pitchClass = A_sharp, natural = False }
+        , { note = B5, label = "B5", pitchClass = B, natural = True }
+        , { note = C5, label = "C5", pitchClass = C, natural = True }
+        , { note = C_sharp5, label = "Cโ™ฏ/Dโ™ญ5", pitchClass = C_sharp, natural = False }
+        , { note = D5, label = "D5", pitchClass = D, natural = True }
+        , { note = D_sharp5, label = "Dโ™ฏ/Eโ™ญ5", pitchClass = D_sharp, natural = False }
+        , { note = E5, label = "E5", pitchClass = E, natural = True }
+        , { note = F5, label = "F5", pitchClass = F, natural = True }
+        , { note = F_sharp5, label = "Fโ™ฏ/Gโ™ญ5", pitchClass = F_sharp, natural = False }
+        , { note = G5, label = "G5", pitchClass = G, natural = True }
+        , { note = G_sharp5, label = "Gโ™ฏ/Aโ™ญ5", pitchClass = G_sharp, natural = False }
+        , { note = A6, label = "A6", pitchClass = A, natural = True }
+        , { note = A_sharp6, label = "Aโ™ฏ/Bโ™ญ6", pitchClass = A_sharp, natural = False }
+        , { note = B6, label = "B6", pitchClass = B, natural = True }
+        , { note = C6, label = "C6", pitchClass = C, natural = True }
+        , { note = C_sharp6, label = "Cโ™ฏ/Dโ™ญ6", pitchClass = C_sharp, natural = False }
+        , { note = D6, label = "D6", pitchClass = D, natural = True }
+        , { note = D_sharp6, label = "Dโ™ฏ/Eโ™ญ6", pitchClass = D_sharp, natural = False }
+        , { note = E6, label = "E6", pitchClass = E, natural = True }
+        , { note = F6, label = "F6", pitchClass = F, natural = True }
+        , { note = F_sharp6, label = "Fโ™ฏ/Gโ™ญ6", pitchClass = F_sharp, natural = False }
+        , { note = G6, label = "G6", pitchClass = G, natural = True }
+        , { note = G_sharp6, label = "Gโ™ฏ/Aโ™ญ6", pitchClass = G_sharp, natural = False }
+        , { note = A7, label = "A7", pitchClass = A, natural = True }
+        , { note = A_sharp7, label = "Aโ™ฏ/Bโ™ญ7", pitchClass = A_sharp, natural = False }
+        , { note = B7, label = "B7", pitchClass = B, natural = True }
+        , { note = C7, label = "C7", pitchClass = C, natural = True }
+        , { note = C_sharp7, label = "Cโ™ฏ/Dโ™ญ7", pitchClass = C_sharp, natural = False }
+        , { note = D7, label = "D7", pitchClass = D, natural = True }
+        , { note = D_sharp7, label = "Dโ™ฏ/Eโ™ญ7", pitchClass = D_sharp, natural = False }
+        , { note = E7, label = "E7", pitchClass = E, natural = True }
+        , { note = F7, label = "F7", pitchClass = F, natural = True }
+        , { note = F_sharp7, label = "Fโ™ฏ/Gโ™ญ7", pitchClass = F_sharp, natural = False }
+        , { note = G7, label = "G7", pitchClass = G, natural = True }
+        , { note = G_sharp7, label = "Gโ™ฏ/Aโ™ญ7", pitchClass = G_sharp, natural = False }
+        , { note = C8, label = "C8", pitchClass = C, natural = True }
+        ]
+
+
+{-| Mapping of note data to commonly needed metadata for that note.
+-}
+getNoteMetadata : Note -> NoteMetadata
+getNoteMetadata note =
+    case Array.get (noteAsNumber note) noteMetadata of
+        Just metadata ->
+            metadata
+
+        -- This case should never hit, so we just return C1 to appease the
+        -- compiler.
+        Nothing ->
+            getNoteMetadata C1
+
+
+{-| Return the numeric representation of `note` to ues when comparing two
+notes.
+-}
+noteAsNumber : Note -> Int
+noteAsNumber note =
+    let
+        result =
+            noteMetadata
+                |> Array.toList
+                |> List.indexedMap Tuple.pair
+                |> Misc.find (\( _, x ) -> x.note == note)
+    in
+    case result of
+        Nothing ->
+            0
+
+        Just ( i, _ ) ->
+            i
+
+
+{-| Return true if all of the notes that comprise `chord` can be played on a
+piano whose keys begin at `start` and end at `end`.
+-}
+chordWithinRange : Note -> Note -> Chord -> Bool
+chordWithinRange start end chord =
+    case notesForChord chord of
+        Just notes ->
+            let
+                nums =
+                    List.map noteAsNumber notes
+
+                lo =
+                    List.minimum nums |> Maybe.withDefault (noteAsNumber start)
+
+                hi =
+                    List.maximum nums |> Maybe.withDefault (noteAsNumber end)
+            in
+            lo >= noteAsNumber start && hi < noteAsNumber end
+
+        Nothing ->
+            False
+
+
+{-| Return a list of all of the pitch classes that we know about.
+-}
+allPitchClasses : List PitchClass
+allPitchClasses =
+    [ C
+    , C_sharp
+    , D
+    , D_sharp
+    , E
+    , F
+    , F_sharp
+    , G
+    , G_sharp
+    , A
+    , A_sharp
+    , B
+    ]
+
+
+{-| Return a list of all of the chords that we know about.
+Only create chords from the range of notes delimited by the range `start` and
+`end`.
+-}
+allChords :
+    { start : Note
+    , end : Note
+    , inversions : List ChordInversion
+    , chordTypes : List ChordType
+    , pitchClasses : List PitchClass
+    }
+    -> List Chord
+allChords { start, end, inversions, chordTypes, pitchClasses } =
+    let
+        notes =
+            notesFromRange start end
+                |> List.filter (\note -> List.member (classifyNote note) pitchClasses)
+    in
+    notes
+        |> List.Extra.andThen
+            (\note ->
+                chordTypes
+                    |> List.Extra.andThen
+                        (\chordType ->
+                            inversions
+                                |> List.Extra.andThen
+                                    (\inversion ->
+                                        [ { note = note
+                                          , chordType = chordType
+                                          , chordInversion = inversion
+                                          }
+                                        ]
+                                    )
+                        )
+            )
+        |> List.filter (chordWithinRange start end)
+
+
+{-| Return a human-readable format of `note`.
+-}
+viewNote : Note -> String
+viewNote note =
+    note |> getNoteMetadata |> .label
+
+
+{-| Return a human-readable format of `chord`.
+-}
+viewChord : Chord -> String
+viewChord { note, chordType, chordInversion } =
+    viewPitchClass (classifyNote note) ++ " " ++ chordTypeName chordType ++ " " ++ inversionName chordInversion ++ " position"
+
+
+{-| Return a human-readable format of `pitchClass`.
+-}
+viewPitchClass : PitchClass -> String
+viewPitchClass pitchClass =
+    case pitchClass of
+        C ->
+            "C"
+
+        C_sharp ->
+            "Cโ™ฏ/Dโ™ญ"
+
+        D ->
+            "D"
+
+        D_sharp ->
+            "Dโ™ฏ/Eโ™ญ"
+
+        E ->
+            "E"
+
+        F ->
+            "F"
+
+        F_sharp ->
+            "Fโ™ฏ/Gโ™ญ"
+
+        G ->
+            "G"
+
+        G_sharp ->
+            "Gโ™ฏ/Aโ™ญ"
+
+        A ->
+            "A"
+
+        A_sharp ->
+            "Aโ™ฏ/Bโ™ญ"
+
+        B ->
+            "B"
+
+
+viewMode : Mode -> String
+viewMode mode =
+    case mode of
+        MajorMode ->
+            "major"
+
+        MinorMode ->
+            "minor"
+
+        BluesMode ->
+            "blues"
+
+
+{-| Return the human-readable format of `key`.
+-}
+viewKey : Key -> String
+viewKey { pitchClass, mode } =
+    viewPitchClass pitchClass ++ " " ++ viewMode mode
+
+
+{-| Returns a pairing of a scale-degree to the type of chord at that scale
+degree.
+-}
+practiceChordsForMode : Mode -> Dict ScaleDegree ChordType
+practiceChordsForMode mode =
+    case mode of
+        MajorMode ->
+            Dict.fromList
+                [ ( 1, Major )
+                , ( 2, Minor )
+                , ( 3, Minor )
+                , ( 4, Major )
+                , ( 5, Major )
+                , ( 6, Minor )
+                , ( 7, Diminished )
+                ]
+
+        MinorMode ->
+            Dict.fromList
+                [ ( 1, Minor )
+                , ( 2, Diminished )
+                , ( 3, Major )
+                , ( 4, Minor )
+                , ( 5, Minor )
+                , ( 6, Major )
+                , ( 7, Major )
+                ]
+
+        BluesMode ->
+            Dict.fromList
+                [ ( 1, MajorDominant7 )
+
+                -- While many refer to the blues progression as a I-IV-V, the IV
+                -- chord is really a MajorDominant7 made from the third scale
+                -- degree.
+                , ( 3, MajorDominant7 )
+                , ( 5, MajorDominant7 )
+                ]
+
+
+{-| Returns a list of chords for a particular `key`.
+-}
+chordsForKey : Key -> List Chord
+chordsForKey key =
+    let
+        chords =
+            practiceChordsForMode key.mode
+    in
+    notesForKey key
+        |> List.indexedMap
+            (\i note ->
+                case Dict.get (i + 1) chords of
+                    Nothing ->
+                        Nothing
+
+                    Just chordType ->
+                        Just
+                            (allInversions
+                                |> List.Extra.andThen
+                                    (\inversion ->
+                                        [ { note = note
+                                          , chordType = chordType
+                                          , chordInversion = inversion
+                                          }
+                                        ]
+                                    )
+                            )
+            )
+        |> Maybe.Extra.values
+        |> List.concat
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/UI.elm b/users/wpcarro/website/sandbox/learnpianochords/src/UI.elm
new file mode 100644
index 0000000000..a6876c4f8a
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/UI.elm
@@ -0,0 +1,159 @@
+module UI exposing (..)
+
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Responsive
+import Tailwind
+
+
+type Color
+    = Primary
+    | Secondary
+
+
+bgForColor : Color -> String
+bgForColor color =
+    case color of
+        Primary ->
+            "bg-gray-600"
+
+        Secondary ->
+            "bg-gray-300"
+
+
+textForColor : Color -> String
+textForColor color =
+    case color of
+        Primary ->
+            "text-white"
+
+        Secondary ->
+            "text-black"
+
+
+simpleButton :
+    { label : String
+    , handleClick : msg
+    , color : Color
+    , classes : List String
+    }
+    -> Html msg
+simpleButton { label, handleClick, color, classes } =
+    let
+        buttonClasses =
+            [ bgForColor color
+            , textForColor color
+            , "py-10"
+            , "lg:py-6"
+            , "px-20"
+            , "lg:px-12"
+            , "rounded-lg"
+            , Responsive.h2
+            ]
+    in
+    button
+        [ class (Tailwind.use <| List.concat [ buttonClasses, classes ])
+        , onClick handleClick
+        ]
+        [ text label ]
+
+
+textToggleButton :
+    { label : String
+    , handleClick : msg
+    , classes : List String
+    , toggled : Bool
+    }
+    -> Html msg
+textToggleButton { label, toggled, handleClick, classes } =
+    let
+        ( textColor, textTreatment ) =
+            if toggled then
+                ( "text-red-600", "underline" )
+
+            else
+                ( "text-black", "no-underline" )
+
+        buttonClasses =
+            [ textColor
+            , textTreatment
+            , "py-8"
+            , "lg:py-5"
+            , "px-10"
+            , "lg:px-6"
+            , Responsive.h2
+            ]
+    in
+    button
+        [ class (Tailwind.use <| List.concat [ buttonClasses, classes ])
+        , onClick handleClick
+        ]
+        [ text label ]
+
+
+textField :
+    { placeholderText : String
+    , handleInput : String -> msg
+    , classes : List String
+    }
+    -> Html msg
+textField { placeholderText, handleInput, classes } =
+    let
+        inputClasses =
+            [ "w-full"
+            , "py-10"
+            , "lg:py-6"
+            , "px-16"
+            , "lg:px-10"
+            , "border"
+            , "rounded-lg"
+            , Responsive.h2
+            ]
+    in
+    input
+        [ class (Tailwind.use <| List.concat [ inputClasses, classes ])
+        , onInput handleInput
+        , placeholder placeholderText
+        ]
+        []
+
+
+overlayButton :
+    { label : String
+    , handleClick : msg
+    , isVisible : Bool
+    }
+    -> Html msg
+overlayButton { label, handleClick, isVisible } =
+    let
+        classes =
+            [ "fixed"
+            , "top-0"
+            , "left-0"
+            , "block"
+            , "z-40"
+            , "w-screen"
+            , "h-screen"
+            , Tailwind.if_ isVisible "opacity-100" "opacity-0"
+            ]
+    in
+    button
+        [ classes |> Tailwind.use |> class
+        , style "background-color" "rgba(0,0,0,1.0)"
+        , onClick handleClick
+        ]
+        [ h1
+            [ style "-webkit-text-stroke-width" "2px"
+            , style "-webkit-text-stroke-color" "black"
+            , class <|
+                Tailwind.use
+                    [ "transform"
+                    , "-rotate-90"
+                    , "text-white"
+                    , "font-mono"
+                    , Responsive.h1
+                    ]
+            ]
+            [ text label ]
+        ]
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/.envrc b/users/wpcarro/website/sandbox/learnpianochords/src/server/.envrc
new file mode 100644
index 0000000000..9e714732fe
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/.envrc
@@ -0,0 +1,7 @@
+source_up
+use_nix
+export SERVER_PORT=3000
+export CLIENT_PORT=8000
+# TODO(wpcarro): Prefer age-nix solution if possible.
+export GOOGLE_CLIENT_ID="$(jq -j '.google | .clientId' < $WPCARRO/secrets.json)"
+export STRIPE_API_KEY="$(jq -j '.stripe | .apiKey' < $WPCARRO/secrets.json)"
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/.ghci b/users/wpcarro/website/sandbox/learnpianochords/src/server/.ghci
new file mode 100644
index 0000000000..151d070ca1
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/.ghci
@@ -0,0 +1,7 @@
+:set prompt "> "
+:set -Wall
+
+:set -XOverloadedStrings
+:set -XNoImplicitPrelude
+:set -XRecordWildCards
+:set -XTypeApplications
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/API.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/API.hs
new file mode 100644
index 0000000000..fe3671e7aa
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/API.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeOperators #-}
+--------------------------------------------------------------------------------
+module API where
+--------------------------------------------------------------------------------
+import Servant.API
+
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+type API = "verify"
+           :> ReqBody '[JSON] T.VerifyGoogleSignInRequest
+           :> Post '[JSON] NoContent
+      :<|> "create-payment-intent"
+           :> ReqBody '[JSON] T.PaymentIntent
+           :> Post '[JSON] T.CreatePaymentIntentResponse
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/App.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/App.hs
new file mode 100644
index 0000000000..b7a31457b7
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/App.hs
@@ -0,0 +1,57 @@
+--------------------------------------------------------------------------------
+module App where
+--------------------------------------------------------------------------------
+import RIO hiding (Handler)
+import Servant
+import API
+import Data.String.Conversions (cs)
+import Control.Monad.IO.Class (liftIO)
+import Network.Wai.Middleware.Cors
+import GoogleSignIn (EncodedJWT(..), ValidationResult(..))
+import Utils
+
+import qualified Network.Wai.Handler.Warp as Warp
+import qualified GoogleSignIn
+import qualified Stripe
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+server :: T.Context -> Server API
+server ctx@T.Context{..} = verifyGoogleSignIn
+                      :<|> createPaymentIntent
+  where
+    verifyGoogleSignIn :: T.VerifyGoogleSignInRequest -> Handler NoContent
+    verifyGoogleSignIn T.VerifyGoogleSignInRequest{..} = do
+      validationResult <- liftIO $ GoogleSignIn.validateJWT False (EncodedJWT idToken)
+      case validationResult of
+        Valid _ -> do
+          -- If GoogleLinkedAccounts has email from JWT:
+          --   create a new session for email
+          -- Else:
+          --   Redirect the SPA to the sign-up / payment page
+          pure NoContent
+        err -> do
+          throwError err401 { errBody = err |> GoogleSignIn.explainResult |> cs }
+
+    createPaymentIntent :: T.PaymentIntent -> Handler T.CreatePaymentIntentResponse
+    createPaymentIntent pmt = do
+      clientSecret <- liftIO $ Stripe.createPaymentIntent ctx pmt
+      pure T.CreatePaymentIntentResponse{..}
+
+run :: T.App
+run = do
+  ctx@T.Context{..} <- ask
+  ctx
+    |> server
+    |> serve (Proxy @API)
+    |> cors (const $ Just corsPolicy)
+    |> Warp.run contextServerPort
+    |> liftIO
+  pure $ Right ()
+  where
+    corsPolicy :: CorsResourcePolicy
+    corsPolicy = simpleCorsResourcePolicy
+      { corsOrigins = Just (["http://localhost:8000"], True)
+      , corsMethods = simpleMethods ++ ["PUT", "PATCH", "DELETE", "OPTIONS"]
+      , corsRequestHeaders = simpleHeaders ++ ["Content-Type", "Authorization"]
+      }
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/Fixtures.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/Fixtures.hs
new file mode 100644
index 0000000000..7c153e4228
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/Fixtures.hs
@@ -0,0 +1,67 @@
+--------------------------------------------------------------------------------
+module Fixtures where
+--------------------------------------------------------------------------------
+import RIO
+import Web.JWT
+import Utils
+
+import qualified Data.Map as Map
+import qualified GoogleSignIn
+import qualified TestUtils
+import qualified Data.Time.Clock.POSIX as POSIX
+import qualified System.IO.Unsafe as Unsafe
+--------------------------------------------------------------------------------
+
+-- | These are the JWT fields that I'd like to overwrite in the `googleJWT`
+-- function.
+data JWTFields = JWTFields
+  { overwriteSigner :: Signer
+  , overwriteAuds :: [StringOrURI]
+  , overwriteIss :: StringOrURI
+  , overwriteExp :: NumericDate
+  }
+
+defaultJWTFields :: JWTFields
+defaultJWTFields = do
+  let tenDaysFromToday = POSIX.getPOSIXTime
+                         |> Unsafe.unsafePerformIO
+                         |> (\x -> x * 60 * 60 * 25 * 10)
+                         |> numericDate
+                         |> TestUtils.unsafeJust
+  JWTFields
+    { overwriteSigner = hmacSecret "secret"
+    , overwriteAuds = ["771151720060-buofllhed98fgt0j22locma05e7rpngl.apps.googleusercontent.com"]
+                      |> fmap TestUtils.unsafeStringOrURI
+    , overwriteIss = TestUtils.unsafeStringOrURI "accounts.google.com"
+    , overwriteExp = tenDaysFromToday
+    }
+
+googleJWT :: JWTFields -> GoogleSignIn.EncodedJWT
+googleJWT JWTFields{..} =
+  encodeSigned signer jwtHeader claimSet
+  |> GoogleSignIn.EncodedJWT
+  where
+    signer :: Signer
+    signer = overwriteSigner
+
+    jwtHeader :: JOSEHeader
+    jwtHeader = JOSEHeader
+      { typ = Just "JWT"
+      , cty = Nothing
+      , alg = Just RS256
+      , kid = Just "f05415b13acb9590f70df862765c655f5a7a019e"
+      }
+
+    claimSet :: JWTClaimsSet
+    claimSet = JWTClaimsSet
+      { iss = Just overwriteIss
+      , sub = stringOrURI "114079822315085727057"
+      , aud = overwriteAuds |> Right |> Just
+      -- TODO: Replace date creation with a human-readable date constructor.
+      , Web.JWT.exp = Just overwriteExp
+      , nbf = Nothing
+      -- TODO: Replace date creation with a human-readable date constructor.
+      , iat = numericDate 1596752853
+      , unregisteredClaims = ClaimsMap (Map.fromList [])
+      , jti = stringOrURI "0d3d7fa1fe05bedec0a91c88294936b2b4d1b13c"
+      }
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/GoogleSignIn.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/GoogleSignIn.hs
new file mode 100644
index 0000000000..dcccadcb70
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/GoogleSignIn.hs
@@ -0,0 +1,111 @@
+--------------------------------------------------------------------------------
+module GoogleSignIn where
+--------------------------------------------------------------------------------
+import RIO
+import Data.String.Conversions (cs)
+import Web.JWT
+import Utils
+
+import qualified Network.HTTP.Simple as HTTP
+import qualified Data.Text as Text
+import qualified Web.JWT as JWT
+import qualified Data.Time.Clock.POSIX as POSIX
+--------------------------------------------------------------------------------
+
+newtype EncodedJWT = EncodedJWT Text
+  deriving (Show)
+
+newtype DecodedJWT = DecodedJWT (JWT UnverifiedJWT)
+  deriving (Show)
+
+instance Eq DecodedJWT where
+  (DecodedJWT _) == (DecodedJWT _) = True
+
+data ValidationResult
+  = Valid DecodedJWT
+  | CannotDecodeJWT
+  | GoogleSaysInvalid Text
+  | NoMatchingClientIDs [StringOrURI]
+  | WrongIssuer StringOrURI
+  | StringOrURIParseFailure Text
+  | TimeConversionFailure
+  | MissingRequiredClaim Text
+  | StaleExpiry NumericDate
+  deriving (Eq, Show)
+
+-- | Returns True when the supplied `jwt` meets the following criteria:
+-- * The token has been signed by Google
+-- * The value of `aud` matches my Google client's ID
+-- * The value of `iss` matches is "accounts.google.com" or
+--   "https://accounts.google.com"
+-- * The `exp` time has not passed
+--
+-- Set `skipHTTP` to `True` to avoid making the network request for testing.
+validateJWT :: Bool
+           -> EncodedJWT
+           -> IO ValidationResult
+validateJWT skipHTTP (EncodedJWT encodedJWT) = do
+  case encodedJWT |> decode of
+    Nothing -> pure CannotDecodeJWT
+    Just jwt -> do
+      if skipHTTP then
+        continue jwt
+      else do
+        let request = "https://oauth2.googleapis.com/tokeninfo"
+                      |> HTTP.setRequestQueryString [ ( "id_token", Just (cs encodedJWT) ) ]
+        res <- HTTP.httpLBS request
+        if HTTP.getResponseStatusCode res /= 200 then
+          pure $ GoogleSaysInvalid (res |> HTTP.getResponseBody |> cs)
+        else
+          continue jwt
+  where
+    continue :: JWT UnverifiedJWT -> IO ValidationResult
+    continue jwt = do
+      let audValues :: [StringOrURI]
+          audValues = jwt |> claims |> auds
+          expectedClientID :: Text
+          expectedClientID = "771151720060-buofllhed98fgt0j22locma05e7rpngl.apps.googleusercontent.com"
+          expectedIssuers :: [Text]
+          expectedIssuers = [ "accounts.google.com"
+                            , "https://accounts.google.com"
+                            ]
+          mExpectedClientID :: Maybe StringOrURI
+          mExpectedClientID = stringOrURI expectedClientID
+          mExpectedIssuers :: Maybe [StringOrURI]
+          mExpectedIssuers = expectedIssuers |> traverse stringOrURI
+      case (mExpectedClientID, mExpectedIssuers) of
+        (Nothing, _) -> pure $ StringOrURIParseFailure expectedClientID
+        (_, Nothing) -> pure $ StringOrURIParseFailure (Text.unwords expectedIssuers)
+        (Just clientID, Just parsedIssuers) ->
+          -- TODO: Prefer reading clientID from a config. I'm thinking of the
+          -- AppContext type having my Configuration
+          if not $ clientID `elem` audValues then
+            pure $ NoMatchingClientIDs audValues
+          else
+            case (jwt |> claims |> iss, jwt |> claims |> JWT.exp) of
+              (Nothing, _) -> pure $ MissingRequiredClaim "iss"
+              (_, Nothing) -> pure $ MissingRequiredClaim "exp"
+              (Just jwtIssuer, Just jwtExpiry) ->
+                if not $ jwtIssuer `elem` parsedIssuers then
+                  pure $ WrongIssuer jwtIssuer
+                else do
+                  mCurrentTime <- POSIX.getPOSIXTime |> fmap numericDate
+                  case mCurrentTime of
+                    Nothing -> pure TimeConversionFailure
+                    Just currentTime ->
+                      if not $ currentTime <= jwtExpiry then
+                        pure $ StaleExpiry jwtExpiry
+                      else
+                        pure $ jwt |> DecodedJWT |> Valid
+
+-- | Attempt to explain the `ValidationResult` to a human.
+explainResult :: ValidationResult -> String
+explainResult (Valid _) = "Everything appears to be valid"
+explainResult CannotDecodeJWT = "We had difficulty decoding the provided JWT"
+explainResult (GoogleSaysInvalid x) = "After checking with Google, they claimed that the provided JWT was invalid: " ++ cs x
+explainResult (NoMatchingClientIDs audFields) = "None of the values in the `aud` field on the provided JWT match our client ID: " ++ show audFields
+explainResult (WrongIssuer issuer) = "The `iss` field in the provided JWT does not match what we expect: " ++ show issuer
+explainResult (StringOrURIParseFailure x) = "We had difficulty parsing values as URIs" ++ show x
+explainResult TimeConversionFailure = "We had difficulty converting the current time to a value we can use to compare with the JWT's `exp` field"
+explainResult (MissingRequiredClaim claim) = "Your JWT is missing the following claim: " ++ cs claim
+explainResult (StaleExpiry x) = "The `exp` field on your JWT has expired" ++ x |> show |> cs
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/Main.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/Main.hs
new file mode 100644
index 0000000000..228c3363bc
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/Main.hs
@@ -0,0 +1,37 @@
+--------------------------------------------------------------------------------
+module Main where
+--------------------------------------------------------------------------------
+import RIO
+import Prelude (putStr, putStrLn)
+
+import qualified Types as T
+import qualified System.Envy as Envy
+import qualified App
+--------------------------------------------------------------------------------
+
+-- | Attempt to read environment variables from the system and initialize the
+-- Context data type for our application.
+getAppContext :: IO (Either String T.Context)
+getAppContext = do
+  mEnv <- Envy.decodeEnv
+  case mEnv of
+    Left err -> pure $ Left err
+    Right T.Env{..} -> pure $ Right T.Context
+      { contextGoogleClientID = envGoogleClientID
+      , contextStripeAPIKey = envStripeAPIKey
+      , contextServerPort = envServerPort
+      , contextClientPort = envClientPort
+      }
+
+main :: IO ()
+main = do
+  mContext <- getAppContext
+  case mContext of
+    Left err -> putStrLn err
+    Right ctx -> do
+      result <- runRIO ctx App.run
+      case result of
+        Left err -> do
+          putStr "Something went wrong when executing the application: "
+          putStrLn $ show err
+        Right _ -> putStrLn "The application successfully executed."
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/Spec.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/Spec.hs
new file mode 100644
index 0000000000..3c476bbf7b
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/Spec.hs
@@ -0,0 +1,74 @@
+--------------------------------------------------------------------------------
+module Spec where
+--------------------------------------------------------------------------------
+import RIO
+import Test.Hspec
+import Utils
+import Web.JWT (numericDate, decode)
+import GoogleSignIn (EncodedJWT(..), DecodedJWT(..), ValidationResult(..))
+
+import qualified GoogleSignIn
+import qualified Fixtures as F
+import qualified TestUtils
+import qualified Data.Time.Clock.POSIX as POSIX
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = hspec $ do
+  describe "GoogleSignIn" $
+    describe "validateJWT" $ do
+      let validateJWT' = GoogleSignIn.validateJWT True
+      it "returns a decode error when an incorrectly encoded JWT is used" $ do
+        validateJWT' (GoogleSignIn.EncodedJWT "rubbish") `shouldReturn` CannotDecodeJWT
+
+      it "returns validation error when the aud field doesn't match my client ID" $ do
+        let auds = ["wrong-client-id"]
+                   |> fmap TestUtils.unsafeStringOrURI
+            encodedJWT = F.defaultJWTFields { F.overwriteAuds = auds }
+                         |> F.googleJWT
+        validateJWT' encodedJWT `shouldReturn` NoMatchingClientIDs auds
+
+      it "returns validation success when one of the aud fields matches my client ID" $ do
+        let auds = ["wrong-client-id", "771151720060-buofllhed98fgt0j22locma05e7rpngl.apps.googleusercontent.com"]
+                   |> fmap TestUtils.unsafeStringOrURI
+            encodedJWT@(EncodedJWT jwt) =
+              F.defaultJWTFields { F.overwriteAuds = auds }
+              |> F.googleJWT
+            decodedJWT = jwt |> decode |> TestUtils.unsafeJust |> DecodedJWT
+        validateJWT' encodedJWT `shouldReturn` Valid decodedJWT
+
+      it "returns validation error when one of the iss field doesn't match accounts.google.com or https://accounts.google.com" $ do
+        let erroneousIssuer = TestUtils.unsafeStringOrURI "not-accounts.google.com"
+            encodedJWT = F.defaultJWTFields { F.overwriteIss = erroneousIssuer }
+                         |> F.googleJWT
+        validateJWT' encodedJWT `shouldReturn` WrongIssuer erroneousIssuer
+
+      it "returns validation success when the iss field matches accounts.google.com or https://accounts.google.com" $ do
+        let erroneousIssuer = TestUtils.unsafeStringOrURI "https://accounts.google.com"
+            encodedJWT@(EncodedJWT jwt) =
+              F.defaultJWTFields { F.overwriteIss = erroneousIssuer }
+              |> F.googleJWT
+            decodedJWT = jwt |> decode |> TestUtils.unsafeJust |> DecodedJWT
+        validateJWT' encodedJWT `shouldReturn` Valid decodedJWT
+
+      it "fails validation when the exp field has expired" $ do
+        let mErroneousExp = numericDate 0
+        case mErroneousExp of
+          Nothing -> True `shouldBe` False
+          Just erroneousExp -> do
+            let encodedJWT = F.defaultJWTFields { F.overwriteExp = erroneousExp }
+                             |> F.googleJWT
+            validateJWT' encodedJWT `shouldReturn` StaleExpiry erroneousExp
+
+      it "passes validation when the exp field is current" $ do
+        mFreshExp <- POSIX.getPOSIXTime
+                     |> fmap (\x -> x * 60 * 60 * 24 * 10) -- 10 days later
+                     |> fmap numericDate
+        case mFreshExp of
+          Nothing -> True `shouldBe` False
+          Just freshExp -> do
+            let encodedJWT@(EncodedJWT jwt) =
+                  F.defaultJWTFields { F.overwriteExp = freshExp }
+                  |> F.googleJWT
+                decodedJWT = jwt |> decode |> TestUtils.unsafeJust |> DecodedJWT
+            validateJWT' encodedJWT `shouldReturn` Valid decodedJWT
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/Stripe.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/Stripe.hs
new file mode 100644
index 0000000000..5370b90abe
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/Stripe.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE DataKinds #-}
+--------------------------------------------------------------------------------
+module Stripe where
+--------------------------------------------------------------------------------
+import RIO
+import Prelude (print)
+import Data.String.Conversions (cs)
+import Data.Aeson
+import Network.HTTP.Req
+
+import qualified Types as T
+--------------------------------------------------------------------------------
+
+endpoint :: Text -> Url 'Https
+endpoint slug =
+  https "api.stripe.com" /: "v1" /: slug
+
+post :: (FromJSON b) => Text -> Text -> T.PaymentIntent -> IO (JsonResponse b)
+post apiKey slug T.PaymentIntent{..} = runReq defaultHttpConfig $ do
+  let params = "amount" =: paymentIntentAmount
+            <> "currency" =: paymentIntentCurrency
+  req POST (endpoint slug) (ReqBodyUrlEnc params) jsonResponse (oAuth2Bearer (cs apiKey))
+
+createPaymentIntent :: T.Context -> T.PaymentIntent -> IO T.Secret
+createPaymentIntent T.Context{..} pmtIntent = do
+  res <- post contextStripeAPIKey "payment_intents" pmtIntent
+  let T.StripePaymentIntent{..} = responseBody res :: T.StripePaymentIntent
+  pure pmtIntentClientSecret
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/TestUtils.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/TestUtils.hs
new file mode 100644
index 0000000000..24054bf47a
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/TestUtils.hs
@@ -0,0 +1,17 @@
+--------------------------------------------------------------------------------
+module TestUtils where
+--------------------------------------------------------------------------------
+import RIO
+import Web.JWT
+import Data.String.Conversions (cs)
+--------------------------------------------------------------------------------
+
+unsafeStringOrURI :: String -> StringOrURI
+unsafeStringOrURI x =
+  case stringOrURI (cs x) of
+    Nothing -> error $ "Failed to convert to StringOrURI: " ++ x
+    Just res -> res
+
+unsafeJust :: Maybe a -> a
+unsafeJust Nothing = error "Attempted to force a Nothing to be a something"
+unsafeJust (Just x) = x
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/Types.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/Types.hs
new file mode 100644
index 0000000000..4a72865153
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/Types.hs
@@ -0,0 +1,146 @@
+--------------------------------------------------------------------------------G
+module Types where
+--------------------------------------------------------------------------------
+import RIO
+import Data.Aeson
+import Network.HTTP.Req
+import Web.Internal.HttpApiData (ToHttpApiData(..))
+import System.Envy (FromEnv, fromEnv, env)
+--------------------------------------------------------------------------------
+
+-- | Read from .envrc
+data Env = Env
+  { envGoogleClientID :: !Text
+  , envServerPort :: !Int
+  , envClientPort :: !Int
+  , envStripeAPIKey :: !Text
+  } deriving (Eq, Show)
+
+instance FromEnv Env where
+  fromEnv _ = do
+    envGoogleClientID <- env "GOOGLE_CLIENT_ID"
+    envStripeAPIKey <- env "STRIPE_API_KEY"
+    envServerPort <- env "SERVER_PORT"
+    envClientPort <- env "CLIENT_PORT"
+    pure Env {..}
+
+-- | Application context: a combination of Env and additional values.
+data Context = Context
+  { contextGoogleClientID :: !Text
+  , contextStripeAPIKey :: !Text
+  , contextServerPort :: !Int
+  , contextClientPort :: !Int
+  }
+
+-- | Top-level except for our application, as RIO recommends defining.
+type Failure = ()
+
+-- | When our app executes along the "happy path" this is the type of result it
+-- produces.
+type Success = ()
+
+-- | This is our application monad.
+type AppM = RIO Context
+
+-- | The concrete type of our application.
+type App = AppM (Either Failure Success)
+
+data VerifyGoogleSignInRequest = VerifyGoogleSignInRequest
+  { idToken :: !Text
+  } deriving (Eq, Show)
+
+instance FromJSON VerifyGoogleSignInRequest where
+  parseJSON = withObject "VerifyGoogleSignInRequest" $ \x -> do
+    idToken <- x .: "idToken"
+    pure VerifyGoogleSignInRequest{..}
+
+data GoogleLinkedAccount = GoogleLinkedAccount
+  {
+  -- { googleLinkedAccountUUID :: UUID
+  -- , googleLinkedAccountEmail :: Email
+  -- , googleLinkedAccountTsCreated :: Timestamp
+    googleLinkedAccountGivenName :: !(Maybe Text)
+  , googleLinkedAccountFamilyName :: !(Maybe Text)
+  , googleLinkedAccountFullName :: !(Maybe Text)
+  -- , googleLinkedAccountPictureURL :: URL
+  -- , googleLinkedAccountLocale :: Maybe Locale
+  } deriving (Eq, Show)
+
+data PayingCustomer = PayingCustomer
+  {
+  -- { payingCustomerAccountUUID :: UUID
+  -- , payingCustomerTsCreated :: Timestamp
+  } deriving (Eq, Show)
+
+data Session = Session
+  {
+  -- { sessionUUID :: UUID
+  -- , sessionAccountUUID :: UUID
+  -- , sessionTsCreated :: Timestamp
+  } deriving (Eq, Show)
+
+data CurrencyCode = USD
+  deriving (Eq, Show)
+
+instance ToJSON CurrencyCode where
+  toJSON USD = String "usd"
+
+instance FromJSON CurrencyCode where
+  parseJSON = withText "CurrencyCode" $ \x ->
+    case x of
+      "usd" -> pure USD
+      _ -> fail "Expected a valid currency code like: \"usd\""
+
+instance ToHttpApiData CurrencyCode where
+  toQueryParam USD = "usd"
+
+data PaymentIntent = PaymentIntent
+  { paymentIntentAmount :: !Int
+  , paymentIntentCurrency :: !CurrencyCode
+  } deriving (Eq, Show)
+
+instance ToJSON PaymentIntent where
+  toJSON PaymentIntent{..} =
+    object [ "amount" .= paymentIntentAmount
+           , "currency" .= paymentIntentCurrency
+           ]
+
+instance FromJSON PaymentIntent where
+  parseJSON = withObject "" $ \x -> do
+    paymentIntentAmount <- x .: "amount"
+    paymentIntentCurrency <- x .: "currency"
+    pure PaymentIntent{..}
+
+instance QueryParam PaymentIntent where
+  queryParam = undefined
+
+-- All applications have their secrets... Using the secret type ensures that no
+-- sensitive information will get printed to the screen.
+newtype Secret = Secret Text deriving (Eq)
+
+instance Show Secret where
+  show (Secret _) = "[REDACTED]"
+
+instance ToJSON Secret where
+  toJSON (Secret x) = toJSON x
+
+instance FromJSON Secret where
+  parseJSON = withText "Secret" $ \x -> pure $ Secret x
+
+data CreatePaymentIntentResponse = CreatePaymentIntentResponse
+  { clientSecret :: Secret
+  } deriving (Eq, Show)
+
+instance ToJSON CreatePaymentIntentResponse where
+  toJSON CreatePaymentIntentResponse{..} =
+    object [ "clientSecret" .= clientSecret
+           ]
+
+data StripePaymentIntent = StripePaymentIntent
+  { pmtIntentClientSecret :: Secret
+  } deriving (Eq, Show)
+
+instance FromJSON StripePaymentIntent where
+  parseJSON = withObject "StripeCreatePaymentIntentResponse" $ \x -> do
+    pmtIntentClientSecret <- x .: "client_secret"
+    pure StripePaymentIntent{..}
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/Utils.hs b/users/wpcarro/website/sandbox/learnpianochords/src/server/Utils.hs
new file mode 100644
index 0000000000..2f401af2fb
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/Utils.hs
@@ -0,0 +1,8 @@
+--------------------------------------------------------------------------------
+module Utils where
+--------------------------------------------------------------------------------
+import Data.Function ((&))
+--------------------------------------------------------------------------------
+
+(|>) :: a -> (a -> b) -> b
+(|>) = (&)
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/default.nix b/users/wpcarro/website/sandbox/learnpianochords/src/server/default.nix
new file mode 100644
index 0000000000..262693ae82
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/default.nix
@@ -0,0 +1,28 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.program {
+  name = "server";
+  srcs = builtins.path {
+    path = ./.;
+    name = "LearnPianoChords-server-src";
+  };
+  ghcExtensions = [
+    "OverloadedStrings"
+    "NoImplicitPrelude"
+    "RecordWildCards"
+    "TypeApplications"
+  ];
+  deps = hpkgs: with hpkgs; [
+    servant-server
+    aeson
+    wai-cors
+    warp
+    jwt
+    unordered-containers
+    base64
+    http-conduit
+    rio
+    envy
+    req
+  ];
+}
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/index.html b/users/wpcarro/website/sandbox/learnpianochords/src/server/index.html
new file mode 100644
index 0000000000..459a5c8c82
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/index.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>Google Sign-in</title>
+    <script src="https://apis.google.com/js/platform.js" async defer></script>
+    <meta name="google-signin-client_id" content="771151720060-buofllhed98fgt0j22locma05e7rpngl.apps.googleusercontent.com">
+  </head>
+  <body>
+    <div class="g-signin2" data-onsuccess="onSignIn"></div>
+    <a href="#" onclick="signOut();">Sign out</a>
+    <script>
+     function onSignIn(googleUser) {
+       var idToken = googleUser.getAuthResponse().id_token;
+       fetch('http://localhost:3000/verify', {
+         method: 'POST',
+         headers: {
+           'Content-Type': 'application/json',
+         },
+         body: JSON.stringify({
+           idToken: idToken,
+         })
+       })
+         .then(x => console.log(x))
+         .catch(err => console.error(err));
+     }
+     function signOut() {
+       var auth2 = gapi.auth2.getAuthInstance();
+       auth2.signOut().then(function () {
+         console.log('User signed out.');
+       });
+     }
+    </script>
+  </body>
+</html>
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/init.sql b/users/wpcarro/website/sandbox/learnpianochords/src/server/init.sql
new file mode 100644
index 0000000000..c220bd4406
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/init.sql
@@ -0,0 +1,41 @@
+BEGIN TRANSACTION;
+
+DROP TABLE IF EXISTS GoogleLinkedAccounts;
+DROP TABLE IF EXISTS PayingCustomers;
+DROP TABLE IF EXISTS Sessions;
+
+-- Store some of the information that Google provides to us from the JWT.
+CREATE TABLE GoogleLinkedAccounts (
+  accountUUID TEXT CHECK(LENGTH(uuid) == 36) NOT NULL UNIQUE,
+  email TEXT NOT NULL UNIQUE,
+  tsCreated TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM:SS'
+  givenName TEXT,
+  familyName TEXT,
+  fullName TEXT,
+  pictureURL TEXT,
+  locale TEXT,
+  PRIMARY KEY (accountUUID)
+);
+
+-- Track which of our customers have a paid account.
+-- Defines a one-to-one relationship between:
+--   GoogleLinkedAccounts and PayingCustomers
+CREATE TABLE PayingCustomers (
+  accountUUID TEXT,
+  tsCreated TEXT,
+  PRIMARY KEY (accountUUID),
+  FOREIGN KEY (accountUUID) REFERENCES GoogleLinkedAccounts ON DELETE CASCADE
+);
+
+-- Define mobile and web sessions for our users.
+-- Defines a one-to-many relationship between:
+--   GoogleLinkedAccounts and Sessions
+CREATE TABLE Sessions (
+  sessionUUID TEXT CHECK(LENGTH(sessionUUID) == 36) NOT NULL UNIQUE,
+  accountUUID TEXT,
+  tsCreated TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM:SS'
+  PRIMARY KEY (sessionUUID)
+  FOREIGN KEY(accountUUID) REFERENCES GoogleLinkedAccounts ON DELETE CASCADE
+);
+
+COMMIT;
diff --git a/users/wpcarro/website/sandbox/learnpianochords/src/server/shell.nix b/users/wpcarro/website/sandbox/learnpianochords/src/server/shell.nix
new file mode 100644
index 0000000000..6ec8264470
--- /dev/null
+++ b/users/wpcarro/website/sandbox/learnpianochords/src/server/shell.nix
@@ -0,0 +1,18 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.shell {
+  deps = hpkgs: with hpkgs; [
+    hspec
+    servant-server
+    aeson
+    wai-cors
+    warp
+    jwt
+    unordered-containers
+    base64
+    http-conduit
+    rio
+    envy
+    req
+  ];
+}
diff --git a/users/wpcarro/website/sandbox/typo-po/README.md b/users/wpcarro/website/sandbox/typo-po/README.md
new file mode 100644
index 0000000000..9efe53eccd
--- /dev/null
+++ b/users/wpcarro/website/sandbox/typo-po/README.md
@@ -0,0 +1,10 @@
+# Typo-po
+
+Have you ever published a blog post with typos? Or perhaps you've shared a blog
+post draft with a group of friends to solicit their feedback. If anyone reads
+your blog post and finds places where they can correct your typos or suggest
+grammatical improvements they can use typo-po.
+
+## What's with the name?
+
+We police your typos. We prefer po-po to police though. Send us donuts.
diff --git a/users/wpcarro/ynabsql/dataviz/.gitignore b/users/wpcarro/ynabsql/dataviz/.gitignore
new file mode 100644
index 0000000000..efb13a1549
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/.gitignore
@@ -0,0 +1,5 @@
+/dist
+/node_modules
+/.parcel-cache
+/cdn
+/data.js
\ No newline at end of file
diff --git a/users/wpcarro/ynabsql/dataviz/.parcelrc b/users/wpcarro/ynabsql/dataviz/.parcelrc
new file mode 100644
index 0000000000..5dacc3dd88
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/.parcelrc
@@ -0,0 +1,3 @@
+{
+    "extends": "@parcel/config-default"
+}
\ No newline at end of file
diff --git a/users/wpcarro/ynabsql/dataviz/cdn b/users/wpcarro/ynabsql/dataviz/cdn
new file mode 120000
index 0000000000..9c83dcee43
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/cdn
@@ -0,0 +1 @@
+/tmp/cdn
\ No newline at end of file
diff --git a/users/wpcarro/ynabsql/dataviz/components.jsx b/users/wpcarro/ynabsql/dataviz/components.jsx
new file mode 100644
index 0000000000..0dbfc9fd80
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/components.jsx
@@ -0,0 +1,1256 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import Chart from 'chart.js/auto';
+import 'chartjs-adapter-date-fns';
+
+const colors = {
+    red: 'rgb(255, 45, 70)',
+    green: 'rgb(75, 192, 35)',
+    white: 'rgb(249, 246, 238)',
+    blue: 'rgb(137, 207, 240)',
+    fadedBlue: 'rgb(137, 207, 240, 0.25)',
+    purple: 'rgb(203, 195, 227)',
+    brown: 'rgb(205, 127, 50)',
+    black: 'rgb(53, 57, 53)',
+};
+
+const months = [
+    'January',
+    'February',
+    'March',
+    'April',
+    'May',
+    'June',
+    'July',
+    'August',
+    'September',
+    'October',
+    'November',
+    'December',
+];
+
+function getWeek(x) {
+    const dowOffset = 0;
+    const newYear = new Dte(x.getFullYear(), 0, 1);
+    let day = newYear.getDay() - dowOffset; //the day of week the year begins on
+    day = (day >= 0 ? day : day + 7);
+    const daynum = Math.floor((x.getTime() - newYear.getTime() -
+        (x.getTimezoneOffset() - newYear.getTimezoneOffset()) * 60000) / 86400000) + 1;
+    let weeknum;
+
+    //if the year starts before the middle of a week
+    if (day < 4) {
+        weeknum = Math.floor((daynum + day - 1) / 7) + 1;
+        if (weeknum > 52) {
+            const nYear = new Date(x.getFullYear() + 1, 0, 1);
+            let nday = nYear.getDay() - dowOffset;
+            nday = nday >= 0 ? nday : nday + 7;
+            /*if the next year starts before the middle of
+              the week, it is week #1 of that year*/
+            weeknum = nday < 4 ? 1 : 53;
+        }
+    }
+    else {
+        weeknum = Math.floor((daynum + day - 1) / 7);
+    }
+    return weeknum;
+}
+
+// Convert a sorting expressions (e.g. "Outflow DESC; Date ASC; Category ASC")
+// into a function that can be passed to Array.prototype.sort.
+function compileSort(expr) {
+    return expr.split(/\s*;\s*/).reverse().reduce((acc, x) => {
+        const [k, dir] = x.split(/\s+/);
+        if (dir === 'ASC') {
+            return function(x, y) {
+                if (x[k] > y[k]) { return 1; }
+                if (x[k] < y[k]) { return -1; }
+                else { return acc(x, y); }
+            };
+        }
+        if (dir === 'DESC') {
+            return function(x, y) {
+                if (x[k] > y[k]) { return -1; }
+                if (x[k] < y[k]) { return 1; }
+                else { return acc(x, y); }
+            };
+        }
+        else {
+            throw new Error(`Sort direction not supported, ${dir}, must be either "ASC" or "DESC"`);
+        }
+    }, function(x, y) { return 0; })
+}
+
+function dollars(n, sensitive) {
+    if (sensitive) {
+        const order = magnitude(n);
+        // Shortcut to avoid writing comma-insertion logic v0v.
+        if (n === 0) {
+            return '$X.XX';
+        }
+        if (order <= 0) {
+            return '$X.XX';
+        }
+        if (order === 1) {
+            return '$XX.XX';
+        }
+        if (order === 2) {
+            return '$XXX.XX';
+        }
+        if (order === 3) {
+            return '$X,XXX.XX';
+        }
+        if (order === 4) {
+            return '$XX,XXX.XX';
+        }
+        if (order === 4) {
+            return '$XX,XXX.XX';
+        }
+        if (order === 5) {
+            return '$XXX,XXX.XX';
+        }
+        // Coming soon! :P
+        if (order === 6) {
+            return '$X,XXX,XXX.XX';
+        }
+        if (order === 7) {
+            return '$XX,XXX,XXX.XX';
+        }
+        if (order === 8) {
+            return '$XXX,XXX,XXX.XX';
+        }
+        // Unsupported
+        else {
+            return '$???.??';
+        }
+    }
+    return usd.format(n);
+}
+
+const usd = new Intl.NumberFormat('en-US', {
+    style: 'currency',
+    currency: 'USD',
+});
+
+const queries = {
+    housing: 'Category:/(rent|electric)/',
+    food: 'Category:/(eating|alcohol|grocer)/',
+    commute: 'Category:/(vacation|gasoline|parking|car maintenance)/',
+};
+
+/**
+ * Return the Order of Magnitude of some value, x.
+ */
+function magnitude(x) {
+    return Math.floor(Math.log(x) / Math.LN10 + 0.000000001);
+}
+
+function getSum(transactions) {
+    return transactions.reduce((acc, x) => acc + x.Outflow, 0);
+}
+
+function transactionKey(x) {
+    const keys = [
+        'Account',
+        'Flag',
+        'Date',
+        'Payee',
+        'Category',
+        'Memo',
+        'Outflow',
+        'Inflow',
+        'Cleared',
+    ];
+    return keys.map(k => x[k]).join('|');
+}
+
+function parseCSV(csv) {
+  let lines=csv.split("\n");
+  let result = [];
+
+  // Strip the surrounding 2x-quotes from the header.
+  //
+  // NOTE: If your columns contain commas in their values, you'll need
+  // to deal with those before doing the next step
+  let headers = lines[0].split(",").map(x => x.slice(1, -1));
+
+  for(let i = 1; i < lines.length; i += 1) {
+      let obj = {};
+      let currentline=lines[i].split(",");
+
+      for(let j = 0; j < headers.length; j += 1) {
+        obj[headers[j]] = currentline[j].slice(1, -1);
+      }
+
+      result.push(obj);
+  }
+
+  return result.map(x => ({
+    ...x,
+    Date: new Date(x.Date),
+    Inflow: parseFloat(x.Inflow),
+    Outflow: parseFloat(x.Outflow),
+  }));
+}
+
+
+class UploadJSON extends React.Component {
+    handleUpload(e) {
+        let files = e.target.files;
+        if (!files.length) {
+            alert('No file selected!');
+            return;
+        }
+        let file = files[0];
+        let reader = new FileReader();
+        reader.onload = (event) => {
+            this.props.onUpload(parseCSV(event.target.result));
+        };
+        reader.readAsText(file);
+    }
+    render() {
+        return <input onChange={e => this.handleUpload(e)} id="json-upload" type="file" accept="application/csv" />;
+    }
+}
+
+class ScatterChart extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+    componentDidUpdate(prevProps) {
+        if (this.props.transactions !== prevProps.transactions) {
+            this.chart.data.datasets[0].data = this.props.transactions.filter(x => x.Inflow > 0).map(x => ({
+                x: x.Date,
+                y: x.Inflow,
+                metadata: x,
+            }));
+            this.chart.data.datasets[1].data = this.props.transactions.filter(x => x.Outflow > 0).map(x => ({
+                x: x.Date,
+                y: x.Outflow,
+                metadata: x,
+            }));
+            this.chart.update();
+        }
+    }
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        this.chart = new Chart(mount, {
+            type: 'scatter',
+            data: {
+                datasets: [
+                    {
+                        label: 'Revenue',
+                        data: this.props.transactions.filter(x => x.Inflow > 0).map(x => ({
+                            x: x.Date,
+                            y: x.Inflow,
+                            metadata: x,
+                        })),
+                        backgroundColor: colors.green,
+                    },
+                    {
+                        label: 'Expenses',
+                        data: this.props.transactions.filter(x => x.Outflow).map(x => ({
+                            x: x.Date,
+                            y: x.Outflow,
+                            metadata: x,
+                        })),
+                        backgroundColor: colors.red,
+                    },
+                ],
+            },
+            options: {
+                scales: {
+                    x: {
+                        type: 'time',
+                        title: {
+                            display: true,
+                            text: 'Date',
+                        },
+                    },
+                    y: {
+                        title: {
+                            display: true,
+                            text: 'Amount ($USD)'
+                        },
+                    },
+                },
+                plugins: {
+                    tooltip: {
+                        callbacks: {
+                            title: function (x) {
+                                return `$${x[0].raw.y} (${x[0].raw.metadata.Date.toLocaleDateString()})`;
+                            },
+                            label: function (x) {
+                                const { Category, Payee, Memo } = x.raw.metadata;
+                                return `${Payee} - ${Category} (${Memo})`;
+                            },
+                        },
+                    },
+                },
+            },
+        });
+    }
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+/**
+ * Generic line chart parameterized by:
+ * - datasets: forwarded to chart.js library
+ * - x: string label for x-axis
+ * - y: string label for y-axis
+ */
+class GenLineChart extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+
+    componentDidUpdate(prevProps, prevState) {
+        if (this.props.datasets != prevProps.datasets) {
+            this.chart.data.datasets = this.props.datasets;
+            this.chart.update();
+        }
+    }
+
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        this.chart = new Chart(mount, {
+            type: 'line',
+            data: {
+                datasets: this.props.datasets,
+            },
+            options: {
+                scales: {
+                    x: {
+                        type: 'time',
+                        title: {
+                            display: true,
+                            text: this.props.x,
+                        },
+                    },
+                    y: {
+                        title: {
+                            display: true,
+                            text: this.props.y
+                        },
+                    },
+                },
+            },
+        });
+    }
+
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+class DonutChart extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+
+    componentDidUpdate(prevProps, prevState) {
+        if (this.props.datasets != prevProps.datasets) {
+            this.chart.data.datasets = this.props.datasets;
+            this.chart.update();
+        }
+    }
+
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        this.chart = new Chart(mount, {
+            type: 'doughnut',
+            data: {
+                labels: this.props.labels,
+                datasets: this.props.datasets,
+            },
+            options: {
+                resonsive: true,
+            },
+        });
+    }
+
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+class StackedHistogram extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+
+    componentDidUpdate(prevProps, prevState) {
+        if (this.props.datasets != prevProps.datasets) {
+            this.chart.data.datasets = this.props.datasets;
+            this.chart.update();
+        }
+    }
+
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        this.chart = new Chart(mount, {
+            type: 'bar',
+            data: {
+                labels: this.props.labels,
+                datasets: this.props.datasets,
+            },
+            options: {
+                scales: {
+                    x: {
+                        stacked: true,
+                    },
+                    y: {
+                        stacked: true,
+                    },
+                },
+            },
+        });
+    }
+
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+/**
+ * Display the "Actual Savings Rate" (bucketed by month) as a line chart with
+ * the "Expected Savings Rate" overlay.
+ */
+class SavingsRateLineChart extends React.Component {
+    constructor(props) {
+        super(props);
+        this.chart = null;
+        // Generate a 1/1M random ID.
+        this.id = btoa(Math.floor(Math.random() * 1e9));
+    }
+
+    static getRevenue(transactions) {
+        // Bucket revenues into months.
+        return transactions.reduce((acc, x) => {
+            const month = x.Date.getMonth();
+            acc[month] += x.Inflow;
+            return acc;
+        }, new Array(12).fill(0));
+    }
+
+    static getExpenses(transactions) {
+        // Bucket revenues into months.
+        return transactions.reduce((acc, x) => {
+            const month = x.Date.getMonth();
+            acc[month] += x.Outflow;
+            return acc;
+        }, new Array(12).fill(0));
+    }
+
+    componentDidMount() {
+        const mount = document.getElementById(this.id);
+        const revenue = SavingsRateLineChart.getRevenue(this.props.transactions);
+        const expenses = SavingsRateLineChart.getExpenses(this.props.transactions);
+
+        this.chart = new Chart(mount, {
+            type: 'line',
+            data: {
+                datasets: [
+                    {
+                        label: 'actual savings (by month)',
+                        data: new Array(12).fill(null).map((_, i) => ({
+                            x: i,
+                            y: (revenue[i] - expenses[i]) / revenue[i],
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.fadedBlue,
+                        backgroundColor: colors.fadedBlue,
+                    },
+                    {
+                        label: 'actual savings (overall)',
+                        data: new Array(12).fill(null).map((_, i) => ({
+                            x: i,
+                            y: this.props.rate,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.blue,
+                        backgroundColor: colors.blue,
+                    },
+                    // 0% marker (out of debt)
+                    {
+                        label: 'beginner (0%)',
+                        data: new Array(12).fill(null).map((x, i) => ({
+                            x: i,
+                            y: 0.00,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.white,
+                        backgroundColor: colors.white,
+                    },
+                    // 25% marker (quarter "Washington" club)
+                    {
+                        label: 'healthy (25%)',
+                        data: new Array(12).fill(null).map((x, i) => ({
+                            x: i,
+                            y: 0.25,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.purple,
+                        backgroundColor: colors.purple,
+                    },
+                    // 50% marker (1/2-dollar "Kennedy" club)
+                    {
+                        label: 'rich (50%)',
+                        data: new Array(12).fill(null).map((x, i) => ({
+                            x: i,
+                            y: 0.50,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.brown,
+                        backgroundColor: colors.brown,
+                    },
+                    // 75% marker
+                    {
+                        label: 'wealthy (75%)',
+                        data: new Array(12).fill(null).map((x, i) => ({
+                            x: i,
+                            y: 0.75,
+                        })),
+                        cubicInterpolationMode: 'monotone',
+                        tension: 0.4,
+                        borderColor: colors.black,
+                        backgroundColor: colors.black,
+                    },
+                ],
+                labels: months,
+            },
+            options: {
+                scales: {
+                    y: {
+                        max: 1.0,
+                        min: -1.0,
+                        title: {
+                            display: true,
+                            text: 'Savings Rate (%)'
+                        },
+                    },
+                },
+            },
+        });
+    }
+
+    componentDidUpdate(prevProps, prevState) {
+        // Bucket revenues into months.
+        const revenue = SavingsRateLineChart.getRevenue(this.props.transactions);
+        const expenses = SavingsRateLineChart.getExpenses(this.props.transactions);
+
+        this.chart.data.datasets[0].data = new Array(12).fill(null).map((_, i) => ({
+            x: i,
+            y: (revenue[i] - expenses[i]) / revenue[i],
+        }));
+        this.chart.update();
+    }
+
+    render() {
+        return <canvas id={this.id}></canvas>;
+    }
+}
+
+class App extends React.Component {
+    constructor(props) {
+        super(props);
+        const query = 'Account:/checking/';
+        const allTransactions = [];
+        const savingsView = 'after:"01/01/2022"';
+        const inflowQuery = 'Account:/checking/';
+        const outflowQuery = 'Account:/checking/ -Category:/(stocks|crypto)/';
+
+        // slx configuration
+        const slxCaseSensitive = false;
+        const slxPreferRegex = true;
+        const slxDateKey = 'Date';
+
+        this.state = {
+            query,
+            transactionsView: 'table',
+            sensitive: false,
+            allTransactions,
+            slxCaseSensitive,
+            slxPreferRegex,
+            slxDateKey,
+            filteredTransactions: select(query, allTransactions, {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }),
+            saved: {},
+            focus: {
+                sum: false,
+                1000: false,
+                100: false,
+                10: false,
+                1: false,
+                0.1: false,
+            },
+            budget: [
+                {
+                    label: 'Flexible',
+                    children: [
+                        { label: 'groceries - $200/mo', savings: false, monthly: 400.00 },
+                        { label: 'eating out - $150/mo', savings: false, monthly: 200.00 },
+                        { label: 'alcohol - $200/mo', savings: false, monthly: 200.00 },
+                        { label: 'household items - $50/mo', savings: false, monthly: 50.00 },
+                        { label: 'toiletries - $200/yr', savings: false, monthly: 200.00 / 12 },
+                        { label: 'haircuts - $400/yr', savings: false, monthly: 400.00 / 12 },
+                        { label: 'gasoline - $100/mo', savings: false, monthly: 100.00 },
+                        { label: 'parking - $10/mo', savings: false, monthly: 10.00 },
+                        { label: 'ride services - $25/mo', savings: false, monthly: 50.00 },
+                        { label: 'LMNT - $45/mo', savings: false, monthly: 45.00 },
+                        { label: 'books - $25/mo', savings: false, monthly: 25.00 },
+                        { label: 'vacation - $4,000/yr', savings: false, monthly: 4000.00 / 12 },
+                        { label: 'reimbursements - $5,000 balance', savings: false, monthly: 0.00 },
+                    ],
+                },
+                {
+                    label: 'Fixed',
+                    children: [
+                        { label: 'rent - $3,100/mo', savings: false, monthly: 3100.00 },
+                        { label: 'electric - $50/mo', savings: false, monthly: 50.00 },
+                        { label: 'SoCalGas - $30/mo', savings: false, monthly: 30.00 },
+                        { label: 'YNAB', savings: false, monthly: 100.00 / 12 },
+                        { label: 'Robinhood Gold', savings: false, monthly: 5.00 },
+                        { label: 'Spotify', savings: false, monthly: 10.00 },
+                        { label: 'Surfline', savings: false, monthly: 100.00 / 12 },
+                        { label: 'HBO Max', savings: false, monthly: 170.00 },
+                        { label: 'Clear', savings: false, monthly: 179.00 },
+                        { label: 'car insurance', savings: false, monthly: 100.00 },
+                        { label: 'Making Sense', savings: false, monthly: 50.00 / 12 },
+                        { label: 'internet', savings: false, monthly: 100.00 },
+                        { label: 'tax return', savings: false, monthly: 200.00 / 12 },
+                    ],
+                },
+                {
+                    label: 'Rainy Day (dont touch)',
+                    children: [
+                        { label: 'emergency fund', savings: false, monthly: 0.00 },
+                        { label: 'check-ups', savings: false, monthly: 7.50 },
+                        { label: 'car maintenance', savings: false, monthly: 98.33 },
+                    ],
+                },
+                {
+                    label: 'Savings (dont touch)',
+                    children: [
+                        { label: 'stocks', savings: true, monthly: 4000.00 },
+                        { label: 'crypto', savings: true, monthly: 736.00 },
+                    ],
+                },
+                {
+                    label: 'Gifts (dont touch)',
+                    children: [
+                        { label: 'birthdays', savings: false, monthly: 250.00 / 12 },
+                        { label: 'Valentines Day', savings: false, monthly: 100.00 / 12 },
+                        { label: 'Mothers Day', savings: false, monthly: 25.00 / 12 },
+                        { label: 'Fathers Day', savings: false, monthly: 25.00 / 12 },
+                        { label: 'Christmas', savings: false, monthly: 500.00 / 12 },
+                    ],
+                },
+                {
+                    label: 'Error Budget',
+                    children: [
+                        { label: 'stuff I forgot to budget for - $2,254/mo', savings: false, monthly: 0.00 },
+                    ],
+                },
+            ],
+            paycheck: 6000.00,
+            view: 'query',
+            savingsView,
+            inflowQuery,
+            outflowQuery,
+            inflowTransactions: select(inflowQuery, select(savingsView, allTransactions, {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }), {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }),
+            outflowTransactions: select(outflowQuery, select(savingsView, allTransactions, {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }), {
+                caseSensitive: slxCaseSensitive,
+                preferRegex: slxPreferRegex,
+                dateKey: slxDateKey,
+            }),
+            sortExpr: 'Date DESC; Outflow DESC',
+        };
+    }
+
+    render() {
+        const sum = this.state.filteredTransactions.reduce((acc, { Outflow }) => acc + Outflow, 0);
+        const savedSum = Object.values(this.state.saved).reduce((acc, sum) => acc + sum, 0);
+        const categories = this.state.allTransactions.reduce((acc, x) => {
+            if (!(x.Category in acc)) {
+                acc[x.Category] = [];
+            }
+            acc[x.Category].push(x);
+            return acc;
+        }, {});
+
+        let view = null;
+        if (this.state.view === 'query') {
+            view = (
+                <QueryView
+                    transactionsView={this.state.transactionsView}
+                    sortExpr={this.state.sortExpr}
+                    onSortExprChange={sortExpr => this.setState({ sortExpr })}
+                    sensitive={this.state.sensitive}
+                    query={this.state.query}
+                    focus={this.state.focus}
+                    allTransactions={this.state.allTransactions}
+                    transactions={this.state.filteredTransactions}
+                    saved={this.state.saved}
+                    setState={this.setState.bind(this)}
+                    slxConfig={{
+                        caseSensitive: this.state.slxCaseSensitive,
+                        preferRegex: this.state.slxPreferRegex,
+                        dateKey: this.state.slxDateKey,
+                    }}
+                />
+            );
+        } else if (this.state.view === 'savings') {
+            view = (
+                <SavingsView
+                    categories={categories}
+                    sensitive={this.state.sensitive}
+                    savingsView={this.state.savingsView}
+                    inflowQuery={this.state.inflowQuery}
+                    outflowQuery={this.state.outflowQuery}
+                    inflowTransactions={this.state.inflowTransactions}
+                    outflowTransactions={this.state.outflowTransactions}
+                    onFilterInflow={() => this.setState({
+                        inflowTransactions: select(this.state.inflowQuery, select(this.state.savingsView, this.state.allTransactions, {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }), {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }),
+                    })}
+                    onFilterOutflow={() => this.setState({
+                        outflowTransactions: select(this.state.outflowQuery, select(this.state.savingsView, this.state.allTransactions, {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }), {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }),
+                    })}
+                    onFilterSavingsView={() => this.setState({
+                        inflowTransactions: select(this.state.inflowQuery, select(this.state.savingsView, this.state.allTransactions, {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }), {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }),
+                        outflowTransactions: select(this.state.outflowQuery, select(this.state.savingsView, this.state.allTransactions, {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }), {
+                            caseSensitive: this.state.slxCaseSensitive,
+                            preferRegex: this.state.slxPreferRegex,
+                            dateKey: this.state.slxDateKey,
+                        }),
+                    })}
+                    onSavingsViewChange={x => this.setState({ savingsView: x })}
+                    onInflowQueryChange={x => this.setState({ inflowQuery: x })}
+                    onOutflowQueryChange={x => this.setState({ outflowQuery: x })}
+                />
+            );
+        } else if (this.state.view === 'budget') {
+            // Planned expenses:
+            // - minus planned assignment to emergency fund (not an expense)
+            // - minus planned spend to investments (e.g. stocks, crypto)
+            const budgetedSpend = this.state.budget.reduce((acc, x) => acc + x.children.filter(x => x.savings).reduce((acc, x) => acc + x.monthly, 0), 0);
+
+            view = (
+                <div>
+                    <fieldset>
+                        <legend>Details</legend>
+                        <div className="form-group">
+                            <label htmlFor="paycheck">Paycheck</label>
+                            <input name="paycheck" type="text" />
+                        </div>
+                        <div className="form-group">
+                            <label htmlFor="savings-rate">Savings Rate</label>
+                            <input name="savings-rate" type="text" />
+                        </div>
+                    </fieldset>
+                    <ul>
+                        <li>Available Spend: {dollars(this.state.paycheck * 2, this.state.sensitive)}</li>
+                        <li>Target Spend: {dollars(this.state.paycheck, this.state.sensitive)}</li>
+                        <li>Budgeted Spend (minus savings): {dollars(budgetedSpend, this.state.sensitive)}</li>
+                        <li>Emergency Fund Size (recommended): {dollars(budgetedSpend * 3, this.state.sensitive)}</li>
+                    </ul>
+                    <div style={{ width: '30em' }}>
+                        <DonutChart labels={this.state.budget.map(x => x.label)} datasets={[
+                            {
+                                label: 'Categories',
+                                data: this.state.budget.map(x => x.children.reduce((acc, y) => acc + y.monthly, 0)),
+                            }
+                        ]} />
+                    </div>
+                    <ul>
+                        {this.state.budget.map(x => (
+                            <li>
+                                <div>{x.label} - {dollars(x.children.reduce((acc, x) => acc + x.monthly, 0), this.state.sensitive)}</div>
+                                <table>
+                                    <thead>
+                                        <tr>
+                                            <th>category</th>
+                                            <th>assigned (target)</th>
+                                            <th>last year (actual)</th>
+                                        </tr>
+                                    </thead>
+                                    <tbody>
+                                        {x.children.map(y => (
+                                            <tr>
+                                                <td>{y.label}</td>
+                                                <td>{dollars(y.monthly, this.state.sensitive)}</td>
+                                                <td>{dollars((categories[y.label] || []).reduce((acc, x) => acc + x.Outflow, 0) / 12, this.state.sensitive)}</td>
+                                            </tr>
+                                        ))}
+                                    </tbody>
+                                </table>
+                            </li>
+                        ))}
+                    </ul>
+                </div>
+            );
+        }
+
+        return (
+            <div className="container">
+                <nav className="terminal-menu">
+                    <ul>
+                        <li><a href="#" onClick={() => this.setState({ view: 'query' })}>query</a></li>
+                        <li><a href="#" onClick={() => this.setState({ view: 'savings' })}>savings</a></li>
+                        <li><a href="#" onClick={() => this.setState({ view: 'budget' })}>budget</a></li>
+                        <li><a href="#" onClick={() => this.setState({ sensitive: !this.state.sensitive })}>sensitive</a></li>
+                    </ul>
+                </nav>
+                <UploadJSON onUpload={xs => this.setState({
+                    allTransactions: xs,
+                    filteredTransactions: select(this.state.query, xs, {
+                        caseSensitive: this.state.slxCaseSensitive,
+                        preferRegex: this.state.slxPreferRegex,
+                        slxDateKey: this.state.slxDateKey,
+                    }).sort(compileSort(this.state.sortExpr))
+                })} />
+                {view}
+            </div>
+        );
+    }
+}
+
+const QueryView = ({ sensitive, query, focus, allTransactions, transactions, saved, setState, slxConfig, sortExpr, transactionsView }) => (
+    <div>
+        <Query
+            query={query}
+            onChange={query => setState({
+                query,
+            })}
+            onFilter={() => setState({
+                filteredTransactions: select(query, allTransactions, slxConfig),
+            })}
+        />
+        <fieldset>
+            <legend>Sort</legend>
+            <div className="form-group">
+                <input type="text" value={sortExpr} onChange={e => setState({ sortExpr: e.target.value, })} />
+            </div>
+            <div className="form-group">
+                <button className="btn btn-default" onClick={() => setState({
+                    filteredTransactions: transactions.slice().sort(compileSort(sortExpr)),
+                })}>Sort</button>
+            </div>
+        </fieldset>
+        <hr />
+        <ScatterChart transactions={transactions} />
+        <hr />
+        <Transactions
+            transactionsView={transactionsView}
+            sensitive={sensitive}
+            transactions={transactions}
+            onClick={x => setState({
+                saved: { ...saved, [transactionKey(x)]: x.Outflow }
+            })}
+            onViewChange={transactionsView => setState({ transactionsView })}
+        />
+    </div>
+);
+
+function classifyRate(x) {
+    if (x < 0.25) {
+        return 'needs improvement';
+    }
+    if (x < 0.50) {
+        return 'healthy';
+    }
+    if (x < 0.75) {
+        return 'rich';
+    }
+    if (x < 1.00) {
+        return 'wealthy';
+    }
+}
+
+const SavingsView = ({
+    sensitive,
+    categories,
+    savingsView,
+    inflowQuery,
+    outflowQuery,
+    inflowTransactions,
+    outflowTransactions,
+    onSavingsViewChange,
+    onInflowQueryChange,
+    onOutflowQueryChange,
+    onFilterInflow,
+    onFilterOutflow,
+    onFilterSavingsView,
+}) => {
+    const revenue = inflowTransactions.reduce((acc, x) => {
+        acc[x.Date.getMonth()] += x.Inflow;
+        return acc;
+    }, new Array(12).fill(0));
+
+    const inflow = inflowTransactions.reduce((acc, x) => acc + x.Inflow, 0);
+    const outflow = outflowTransactions.reduce((acc, x) => acc + x.Outflow, 0);
+
+    const delta25Sum = new Array(12).fill(0);
+    for (let i = 1; i < 12; i += 1) {
+        delta25Sum[i] = delta25Sum[i - 1] + revenue[i] * 0.25;
+    }
+
+    const delta50Sum = new Array(12).fill(0);
+    for (let i = 1; i < 12; i += 1) {
+        delta50Sum[i] = delta50Sum[i - 1] + revenue[i] * 0.5;
+    }
+
+    const delta75Sum = new Array(12).fill(0);
+    for (let i = 1; i < 12; i += 1) {
+        delta75Sum[i] = delta75Sum[i - 1] + revenue[i] * 0.75;
+    }
+
+    return (
+        <section>
+            <fieldset>
+                <legend>Filtering</legend>
+                <div className="form-group">
+                    <label htmlFor="savings-view">Savings View</label>
+                    <input name="savings-view" type="text" placeholder="Savings View..." value={savingsView}
+                        onChange={e => onSavingsViewChange(e.target.value)} />
+                    <button className="btn btn-default" onClick={() => onFilterSavingsView()}>Apply</button>
+                </div>
+                <div className="form-group">
+                    <label htmlFor="inflow-query">Inflow Query</label>
+                    <input name="inflow-query" type="text" placeholder="Inflow query..." value={inflowQuery}
+                        onChange={e => onInflowQueryChange(e.target.value)} />
+                    <button className="btn btn-default" onClick={() => onFilterInflow()}>Filter</button>
+                </div>
+                <div className="form-group">
+                    <label htmlFor="outflow-query">Outflow Query</label>
+                    <input name="outflow-query" type="text" placeholder="Outflow query..." value={outflowQuery}
+                        onChange={e => onOutflowQueryChange(e.target.value)} />
+                    <button className="btn btn-default" onClick={() => onFilterOutflow()}>Filter</button>
+                </div>
+            </fieldset>
+            <ul>
+                <li>inflow: {dollars(inflow, sensitive)}</li>
+                <li>outflow: {dollars(outflow)}</li>
+                <li>savings: {dollars(inflow - outflow)}</li>
+                <li>rate: {parseFloat((inflow - outflow) / inflow * 100).toFixed(2) + "%"} ({classifyRate((inflow - outflow) / inflow)})</li>
+            </ul>
+            <SavingsRateLineChart rate={(inflow - outflow) / inflow} transactions={outflowTransactions} />
+            {/* O($1,000) */}
+            <StackedHistogram labels={months} datasets={Object.keys(categories).map(k => ({
+                label: k,
+                data: categories[k].reduce((acc, x) => {
+                    acc[x.Date.getMonth()] += x.Outflow;
+                    return acc;
+                }, new Array(12).fill(0)).map((x, i) => ({
+                    x: i,
+                    y: x,
+                }))
+            }))} />
+            {/* O($100) */}
+            <StackedHistogram labels={months} datasets={Object.keys(categories).map(k => ({
+                label: k,
+                data: categories[k].reduce((acc, x) => {
+                    acc[x.Date.getMonth()] += x.Outflow;
+                    return acc;
+                }, new Array(12).fill(0)).map((x, i) => ({
+                    x: i,
+                    y: x,
+                }))
+            }))} />
+            {/* O($10) */}
+            <StackedHistogram labels={months} datasets={Object.keys(categories).map(k => ({
+                label: k,
+                data: categories[k].reduce((acc, x) => {
+                    acc[x.Date.getMonth()] += x.Outflow;
+                    return acc;
+                }, new Array(12).fill(0)).map((x, i) => ({
+                    x: i,
+                    y: x,
+                }))
+            }))} />
+            {/* <GenLineChart x="X" y="Y" datasets={[
+                {
+                    label: '25%',
+                    data: delta25Sum.map((x, i) => ({
+                        x: i,
+                        y: x,
+                    })),
+                },
+                {
+                    label: '50%',
+                    data: delta50Sum.map((x, i) => ({
+                        x: i,
+                        y: x,
+                    })),
+                },
+                {
+                    label: '75%',
+                    data: delta75Sum.map((x, i) => ({
+                        x: i,
+                        y: x,
+                    })),
+                },
+            ]} /> */}
+            <hr />
+            <table>
+                <thead>
+                    <tr>
+                        <th>Month</th>
+                        <th>Delta (25%)</th>
+                        <th>Delta (50%)</th>
+                        <th>Delta (75%)</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    {months.map((x, i) => (
+                        <tr key={i}>
+                            <td>{x}</td>
+                            <td>{dollars(delta25Sum[i], sensitive)}</td>
+                            <td>{dollars(delta50Sum[i], sensitive)}</td>
+                            <td>{dollars(delta75Sum[i], sensitive)}</td>
+                        </tr>
+                    ))}
+                </tbody>
+            </table>
+        </section>
+    );
+};
+
+const Calculator = ({ sensitive, saved, onClear }) => (
+    <div>
+        <ul>
+            {Object.keys(saved).map(k => (
+                <li key={k}>
+                    {dollars(saved[k], sensitive)} {k}
+                </li>
+            ))}
+        </ul>
+        <p>{dollars(savedSum, sensitive)}</p>
+        <button className="btn btn-default" onClick={() => onClear()}>clear</button>
+    </div>
+);
+
+/**
+ * Table rendering information about transactions bucketed by its order of
+ * magnitude.
+ */
+const Magnitable = ({ sensitive, label, transactions }) => {
+    const categories = transactions.reduce((acc, x) => {
+        if (x.Category === '') {
+            return acc;
+        }
+        if (!(x.Category in acc)) {
+            acc[x.Category] = 0;
+        }
+        acc[x.Category] += x.Outflow;
+        return acc;
+    }, {});
+
+    // Sort category keys by sum decreasing.
+    const keys = [...Object.keys(categories)].sort((x, y) => {
+        if (categories[x] < categories[y]) {
+            return 1;
+        } else if (categories[x] > categories[y]) {
+            return -1;
+        } else {
+            return 0;
+        }
+    });
+
+    return (
+        <React.Fragment>
+            {keys.map(k => (
+                <tr style={{ backgroundColor: '#F0F8FF' }}>
+                    <td>{k}</td><td>{dollars(categories[k], sensitive)}</td>
+                </tr>
+            ))}
+        </React.Fragment>
+    );
+};
+
+/**
+ * Calculates and renders various aggregates over an input list of transactions.
+ */
+const AggregateTable = ({ sensitive, focus, onFocus, transactions }) => {
+    const net = transactions.reduce((acc, x) => acc + x.Inflow - x.Outflow, 0);
+    const sum = transactions.reduce((acc, x) => acc + x.Outflow, 0);
+    const buckets = transactions.reduce((acc, x) => {
+        const order = magnitude(x.Outflow);
+        const bucket = Math.pow(10, order);
+        acc[bucket].push(x);
+        return acc;
+    }, { 0.1: [], 0: [], 1: [], 10: [], 100: [], 1000: [] });
+
+    return (
+        <div>
+            <table>
+                <caption>Aggregations</caption>
+                <thead>
+                    <tr>
+                        <th>function</th>
+                        <th>value</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <tr><td>net</td><td>{dollars(net, sensitive)}</td></tr>
+                    <tr onClick={() => onFocus('sum')}><td>sum</td><td>{dollars(sum, sensitive)}</td></tr>
+                    {focus.sum && <Magnitable sensitive={sensitive} label="sum" transactions={transactions} />}
+                    <tr><td>per day</td><td>{dollars(sum / 365, sensitive)}</td></tr>
+                    <tr><td>per week</td><td>{dollars(sum / 52, sensitive)}</td></tr>
+                    <tr><td>per month</td><td>{dollars(sum / 12, sensitive)}</td></tr>
+                    <tr onClick={() => onFocus(1000)}><td>ฮฃ ฮ˜($1,000)</td><td>{dollars(buckets[1000].reduce((acc, x) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[1000]) && <Magnitable sensitive={sensitive} label="$1,000" transactions={buckets[1000]} />}
+                    <tr onClick={() => onFocus(100)}><td>ฮฃ ฮ˜($100)</td><td>{dollars(buckets[100].reduce((acc, x, sensitive) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[100]) && <Magnitable sensitive={sensitive} label="$100" transactions={buckets[100]} />}
+                    <tr onClick={() => onFocus(10)}><td>ฮฃ ฮ˜($10)</td><td>{dollars(buckets[10].reduce((acc, x) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[10]) && <Magnitable sensitive={sensitive} label="$10" transactions={buckets[10]} />}
+                    <tr onClick={() => onFocus(1)}><td>ฮฃ ฮ˜($1)</td><td>{dollars(buckets[1].reduce((acc, x) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[1]) && <Magnitable sensitive={sensitive} label="$1.00" transactions={buckets[1]} />}
+                    <tr onClick={() => onFocus(0.1)}><td>ฮฃ ฮ˜($0.10)</td><td>{dollars(buckets[0.1].reduce((acc, x) => acc + x.Outflow, 0), sensitive)}</td></tr>
+                    {(focus[0.1]) && <Magnitable sensitive={sensitive} label="$0.10" transactions={buckets[0.1]} />}
+                    <tr><td>average</td><td>{dollars(sum / transactions.length, sensitive)}</td></tr>
+                    <tr><td>count</td><td>{transactions.length}</td></tr>
+                </tbody>
+            </table>
+        </div>
+    );
+};
+
+const Query = ({ query, onChange, onFilter }) => (
+    <fieldset>
+        <legend>Query</legend>
+        <div className="form-group">
+            <input name="query" type="text" value={query} onChange={e => onChange(e.target.value)} />
+        </div>
+        <div className="form-group">
+            <button className="btn btn-default" onClick={() => onFilter()}>Filter</button>
+        </div>
+    </fieldset>
+);
+
+const tableHeaders = [
+    'Account',
+    'Category',
+    'Date',
+    'Inflow',
+    'Outflow',
+    'Payee',
+    'Memo',
+];
+
+const Transactions = ({ sensitive, transactions, onSort, onClick, onViewChange, transactionsView }) => {
+    let view = null;
+    if (transactionsView === 'table') {
+        view = (
+            <table>
+                <thead>
+                    <tr>
+                        {tableHeaders.map(x => <th>{x}</th>)}
+                    </tr>
+                </thead>
+                <tbody>
+                    {transactions.map(x => (
+                        <tr onClick={() => onClick(x)}>
+                            <td>{x.Account}</td>
+                            <td>{x.Category}</td>
+                            <td>{x.Date.toLocaleDateString()}</td>
+                            <td>{dollars(x.Inflow, sensitive)}</td>
+                            <td>{dollars(x.Outflow, sensitive)}</td>
+                            <td>{x.Payee}</td>
+                            <td>{x.Memo}</td>
+                        </tr>
+                    ))}
+                </tbody>
+            </table>
+        );
+    }
+    else if (transactionsView === 'csv') {
+        view = (
+            <code>{tableHeaders.join(',') + '\n' + transactions.map(x => tableHeaders.map(k => x[k]).join(',')).join("\n")}</code>
+        );
+    }
+    else if (transactionsView === 'json') {
+        view = (
+            <code>{JSON.stringify(transactions)}</code>
+        );
+    }
+
+    return (
+        <div>
+            <legend>Transactions</legend>
+            <div className="btn-group">
+                <button onClick={() => onViewChange('table')} className="btn btn-default btn-ghost">Table</button>
+                <button onClick={() => onViewChange('csv')} className="btn btn-default btn-ghost">CSV</button>
+                <button onClick={() => onViewChange('json')} className="btn btn-default btn-ghost">JSON</button>
+            </div>
+            {view}
+        </div>
+    );
+}
+
+const domContainer = document.querySelector('#mount');
+const root = ReactDOM.createRoot(domContainer);
+
+root.render(<App />);
diff --git a/users/wpcarro/ynabsql/dataviz/index.html b/users/wpcarro/ynabsql/dataviz/index.html
new file mode 100644
index 0000000000..e30c8682b2
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/index.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <!-- TODO(wpcarro): Cache these locally -->
+    <link rel="stylesheet" href="./cdn/terminal.min.css" />
+    <style>
+      :root {
+        --page-width: 100em;
+      }
+    </style>
+  </head>
+  <body class="container">
+    <div id="mount"></div>
+    <!-- depot JS -->
+    <script src="./cdn/slx.js"></script>
+    <script src="./components.jsx" type="module"></script>
+  </body>
+</html>
diff --git a/users/wpcarro/ynabsql/dataviz/package.json b/users/wpcarro/ynabsql/dataviz/package.json
new file mode 100644
index 0000000000..03f795c0bf
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/package.json
@@ -0,0 +1,15 @@
+{
+  "devDependencies": {
+    "@parcel/validator-typescript": "^2.8.3",
+    "parcel": "^2.8.3",
+    "process": "^0.11.10",
+    "typescript": ">=3.0.0"
+  },
+  "dependencies": {
+    "chart.js": "^4.2.0",
+    "chartjs-adapter-date-fns": "^3.0.0",
+    "date-fns": "^2.29.3",
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"
+  }
+}
diff --git a/users/wpcarro/ynabsql/dataviz/yarn.lock b/users/wpcarro/ynabsql/dataviz/yarn.lock
new file mode 100644
index 0000000000..70c52a3a9f
--- /dev/null
+++ b/users/wpcarro/ynabsql/dataviz/yarn.lock
@@ -0,0 +1,1540 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
+  integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
+  dependencies:
+    "@babel/highlight" "^7.18.6"
+
+"@babel/helper-validator-identifier@^7.18.6":
+  version "7.19.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
+  integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==
+
+"@babel/highlight@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+  integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.18.6"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
+"@jridgewell/gen-mapping@^0.3.0":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
+  integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/resolve-uri@3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+  integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/set-array@^1.0.1":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+  integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
+"@jridgewell/source-map@^0.3.2":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+  integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10":
+  version "1.4.14"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+  integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@^0.3.9":
+  version "0.3.17"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
+  integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
+  dependencies:
+    "@jridgewell/resolve-uri" "3.1.0"
+    "@jridgewell/sourcemap-codec" "1.4.14"
+
+"@kurkle/color@^0.3.0":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f"
+  integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==
+
+"@lezer/common@^0.15.0", "@lezer/common@^0.15.7":
+  version "0.15.12"
+  resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.12.tgz#2f21aec551dd5fd7d24eb069f90f54d5bc6ee5e9"
+  integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==
+
+"@lezer/lr@^0.15.4":
+  version "0.15.8"
+  resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.8.tgz#1564a911e62b0a0f75ca63794a6aa8c5dc63db21"
+  integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==
+  dependencies:
+    "@lezer/common" "^0.15.0"
+
+"@lmdb/lmdb-darwin-arm64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz#bc66fa43286b5c082e8fee0eacc17995806b6fbe"
+  integrity sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A==
+
+"@lmdb/lmdb-darwin-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz#89d8390041bce6bab24a82a20392be22faf54ffc"
+  integrity sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA==
+
+"@lmdb/lmdb-linux-arm64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz#14fe4c96c2bb1285f93797f45915fa35ee047268"
+  integrity sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ==
+
+"@lmdb/lmdb-linux-arm@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz#05bde4573ab10cf21827339fe687148f2590cfa1"
+  integrity sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw==
+
+"@lmdb/lmdb-linux-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz#d2f85afd857d2c33d2caa5b057944574edafcfee"
+  integrity sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q==
+
+"@lmdb/lmdb-win32-x64@2.5.2":
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz#28f643fbc0bec30b07fbe95b137879b6b4d1c9c5"
+  integrity sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA==
+
+"@mischnic/json-sourcemap@^0.1.0":
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz#38af657be4108140a548638267d02a2ea3336507"
+  integrity sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==
+  dependencies:
+    "@lezer/common" "^0.15.7"
+    "@lezer/lr" "^0.15.4"
+    json5 "^2.2.1"
+
+"@msgpackr-extract/msgpackr-extract-darwin-arm64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-2.2.0.tgz#901c5937e1441572ea23e631fe6deca68482fe76"
+  integrity sha512-Z9LFPzfoJi4mflGWV+rv7o7ZbMU5oAU9VmzCgL240KnqDW65Y2HFCT3MW06/ITJSnbVLacmcEJA8phywK7JinQ==
+
+"@msgpackr-extract/msgpackr-extract-darwin-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-2.2.0.tgz#fb877fe6bae3c4d3cea29786737840e2ae689066"
+  integrity sha512-vq0tT8sjZsy4JdSqmadWVw6f66UXqUCabLmUVHZwUFzMgtgoIIQjT4VVRHKvlof3P/dMCkbMJ5hB1oJ9OWHaaw==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-2.2.0.tgz#986179c38b10ac41fbdaf7d036c825cbc72855d9"
+  integrity sha512-hlxxLdRmPyq16QCutUtP8Tm6RDWcyaLsRssaHROatgnkOxdleMTgetf9JsdncL8vLh7FVy/RN9i3XR5dnb9cRA==
+
+"@msgpackr-extract/msgpackr-extract-linux-arm@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-2.2.0.tgz#15f2c6fe9e0adc06c21af7e95f484ff4880d79ce"
+  integrity sha512-SaJ3Qq4lX9Syd2xEo9u3qPxi/OB+5JO/ngJKK97XDpa1C587H9EWYO6KD8995DAjSinWvdHKRrCOXVUC5fvGOg==
+
+"@msgpackr-extract/msgpackr-extract-linux-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-2.2.0.tgz#30cae5c9a202f3e1fa1deb3191b18ffcb2f239a2"
+  integrity sha512-94y5PJrSOqUNcFKmOl7z319FelCLAE0rz/jPCWS+UtdMZvpa4jrQd+cJPQCLp2Fes1yAW/YUQj/Di6YVT3c3Iw==
+
+"@msgpackr-extract/msgpackr-extract-win32-x64@2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-2.2.0.tgz#016d855b6bc459fd908095811f6826e45dd4ba64"
+  integrity sha512-XrC0JzsqQSvOyM3t04FMLO6z5gCuhPE6k4FXuLK5xf52ZbdvcFe1yBmo7meCew9B8G2f0T9iu9t3kfTYRYROgA==
+
+"@parcel/bundler-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/bundler-default/-/bundler-default-2.8.3.tgz#d64739dbc2dbd59d6629861bf77a8083aced5229"
+  integrity sha512-yJvRsNWWu5fVydsWk3O2L4yIy3UZiKWO2cPDukGOIWMgp/Vbpp+2Ct5IygVRtE22bnseW/E/oe0PV3d2IkEJGg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/graph" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/cache@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/cache/-/cache-2.8.3.tgz#169e130cf59913c0ed9fadce1a450e68f710e16f"
+  integrity sha512-k7xv5vSQrJLdXuglo+Hv3yF4BCSs1tQ/8Vbd6CHTkOhf7LcGg6CPtLw053R/KdMpd/4GPn0QrAsOLdATm1ELtQ==
+  dependencies:
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    lmdb "2.5.2"
+
+"@parcel/codeframe@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/codeframe/-/codeframe-2.8.3.tgz#84fb529ef70def7f5bc64f6c59b18d24826f5fcc"
+  integrity sha512-FE7sY53D6n/+2Pgg6M9iuEC6F5fvmyBkRE4d9VdnOoxhTXtkEqpqYgX7RJ12FAQwNlxKq4suBJQMgQHMF2Kjeg==
+  dependencies:
+    chalk "^4.1.0"
+
+"@parcel/compressor-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/compressor-raw/-/compressor-raw-2.8.3.tgz#301753df8c6de967553149639e8a4179b88f0c95"
+  integrity sha512-bVDsqleBUxRdKMakWSlWC9ZjOcqDKE60BE+Gh3JSN6WJrycJ02P5wxjTVF4CStNP/G7X17U+nkENxSlMG77ySg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/config-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/config-default/-/config-default-2.8.3.tgz#9a43486e7c702e96c68052c37b79098d7240e35b"
+  integrity sha512-o/A/mbrO6X/BfGS65Sib8d6SSG45NYrNooNBkH/o7zbOBSRQxwyTlysleK1/3Wa35YpvFyLOwgfakqCtbGy4fw==
+  dependencies:
+    "@parcel/bundler-default" "2.8.3"
+    "@parcel/compressor-raw" "2.8.3"
+    "@parcel/namer-default" "2.8.3"
+    "@parcel/optimizer-css" "2.8.3"
+    "@parcel/optimizer-htmlnano" "2.8.3"
+    "@parcel/optimizer-image" "2.8.3"
+    "@parcel/optimizer-svgo" "2.8.3"
+    "@parcel/optimizer-terser" "2.8.3"
+    "@parcel/packager-css" "2.8.3"
+    "@parcel/packager-html" "2.8.3"
+    "@parcel/packager-js" "2.8.3"
+    "@parcel/packager-raw" "2.8.3"
+    "@parcel/packager-svg" "2.8.3"
+    "@parcel/reporter-dev-server" "2.8.3"
+    "@parcel/resolver-default" "2.8.3"
+    "@parcel/runtime-browser-hmr" "2.8.3"
+    "@parcel/runtime-js" "2.8.3"
+    "@parcel/runtime-react-refresh" "2.8.3"
+    "@parcel/runtime-service-worker" "2.8.3"
+    "@parcel/transformer-babel" "2.8.3"
+    "@parcel/transformer-css" "2.8.3"
+    "@parcel/transformer-html" "2.8.3"
+    "@parcel/transformer-image" "2.8.3"
+    "@parcel/transformer-js" "2.8.3"
+    "@parcel/transformer-json" "2.8.3"
+    "@parcel/transformer-postcss" "2.8.3"
+    "@parcel/transformer-posthtml" "2.8.3"
+    "@parcel/transformer-raw" "2.8.3"
+    "@parcel/transformer-react-refresh-wrap" "2.8.3"
+    "@parcel/transformer-svg" "2.8.3"
+
+"@parcel/core@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/core/-/core-2.8.3.tgz#22a69f36095d53736ab10bf42697d9aa5f4e382b"
+  integrity sha512-Euf/un4ZAiClnlUXqPB9phQlKbveU+2CotZv7m7i+qkgvFn5nAGnrV4h1OzQU42j9dpgOxWi7AttUDMrvkbhCQ==
+  dependencies:
+    "@mischnic/json-sourcemap" "^0.1.0"
+    "@parcel/cache" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/graph" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    abortcontroller-polyfill "^1.1.9"
+    base-x "^3.0.8"
+    browserslist "^4.6.6"
+    clone "^2.1.1"
+    dotenv "^7.0.0"
+    dotenv-expand "^5.1.0"
+    json5 "^2.2.0"
+    msgpackr "^1.5.4"
+    nullthrows "^1.1.1"
+    semver "^5.7.1"
+
+"@parcel/diagnostic@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/diagnostic/-/diagnostic-2.8.3.tgz#d560276d5d2804b48beafa1feaf3fc6b2ac5e39d"
+  integrity sha512-u7wSzuMhLGWZjVNYJZq/SOViS3uFG0xwIcqXw12w54Uozd6BH8JlhVtVyAsq9kqnn7YFkw6pXHqAo5Tzh4FqsQ==
+  dependencies:
+    "@mischnic/json-sourcemap" "^0.1.0"
+    nullthrows "^1.1.1"
+
+"@parcel/events@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/events/-/events-2.8.3.tgz#205f8d874e6ecc2cbdb941bf8d54bae669e571af"
+  integrity sha512-hoIS4tAxWp8FJk3628bsgKxEvR7bq2scCVYHSqZ4fTi/s0+VymEATrRCUqf+12e5H47uw1/ZjoqrGtBI02pz4w==
+
+"@parcel/fs-search@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/fs-search/-/fs-search-2.8.3.tgz#1c7d812c110b808758f44c56e61dfffdb09e9451"
+  integrity sha512-DJBT2N8knfN7Na6PP2mett3spQLTqxFrvl0gv+TJRp61T8Ljc4VuUTb0hqBj+belaASIp3Q+e8+SgaFQu7wLiQ==
+  dependencies:
+    detect-libc "^1.0.3"
+
+"@parcel/fs@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/fs/-/fs-2.8.3.tgz#80536afe877fc8a2bd26be5576b9ba27bb4c5754"
+  integrity sha512-y+i+oXbT7lP0e0pJZi/YSm1vg0LDsbycFuHZIL80pNwdEppUAtibfJZCp606B7HOjMAlNZOBo48e3hPG3d8jgQ==
+  dependencies:
+    "@parcel/fs-search" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/watcher" "^2.0.7"
+    "@parcel/workers" "2.8.3"
+
+"@parcel/graph@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/graph/-/graph-2.8.3.tgz#00ffe8ec032e74fee57199e54529f1da7322571d"
+  integrity sha512-26GL8fYZPdsRhSXCZ0ZWliloK6DHlMJPWh6Z+3VVZ5mnDSbYg/rRKWmrkhnr99ZWmL9rJsv4G74ZwvDEXTMPBg==
+  dependencies:
+    nullthrows "^1.1.1"
+
+"@parcel/hash@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/hash/-/hash-2.8.3.tgz#bc2499a27395169616cad2a99e19e69b9098f6e9"
+  integrity sha512-FVItqzjWmnyP4ZsVgX+G00+6U2IzOvqDtdwQIWisCcVoXJFCqZJDy6oa2qDDFz96xCCCynjRjPdQx2jYBCpfYw==
+  dependencies:
+    detect-libc "^1.0.3"
+    xxhash-wasm "^0.4.2"
+
+"@parcel/logger@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/logger/-/logger-2.8.3.tgz#e14e4debafb3ca9e87c07c06780f9afc38b2712c"
+  integrity sha512-Kpxd3O/Vs7nYJIzkdmB6Bvp3l/85ydIxaZaPfGSGTYOfaffSOTkhcW9l6WemsxUrlts4za6CaEWcc4DOvaMOPA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+
+"@parcel/markdown-ansi@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/markdown-ansi/-/markdown-ansi-2.8.3.tgz#1337d421bb1133ad178f386a8e1b746631bba4a1"
+  integrity sha512-4v+pjyoh9f5zuU/gJlNvNFGEAb6J90sOBwpKJYJhdWXLZMNFCVzSigxrYO+vCsi8G4rl6/B2c0LcwIMjGPHmFQ==
+  dependencies:
+    chalk "^4.1.0"
+
+"@parcel/namer-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/namer-default/-/namer-default-2.8.3.tgz#5304bee74beb4b9c1880781bdbe35be0656372f4"
+  integrity sha512-tJ7JehZviS5QwnxbARd8Uh63rkikZdZs1QOyivUhEvhN+DddSAVEdQLHGPzkl3YRk0tjFhbqo+Jci7TpezuAMw==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/node-resolver-core@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/node-resolver-core/-/node-resolver-core-2.8.3.tgz#581df074a27646400b3fed9da95297b616a7db8f"
+  integrity sha512-12YryWcA5Iw2WNoEVr/t2HDjYR1iEzbjEcxfh1vaVDdZ020PiGw67g5hyIE/tsnG7SRJ0xdRx1fQ2hDgED+0Ww==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    semver "^5.7.1"
+
+"@parcel/optimizer-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-css/-/optimizer-css-2.8.3.tgz#420a333f4b78f7ff15e69217dfed34421b1143ee"
+  integrity sha512-JotGAWo8JhuXsQDK0UkzeQB0UR5hDAKvAviXrjqB4KM9wZNLhLleeEAW4Hk8R9smCeQFP6Xg/N/NkLDpqMwT3g==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    lightningcss "^1.16.1"
+    nullthrows "^1.1.1"
+
+"@parcel/optimizer-htmlnano@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.8.3.tgz#a71ab6f0f24160ef9f573266064438eff65e96d0"
+  integrity sha512-L8/fHbEy8Id2a2E0fwR5eKGlv9VYDjrH9PwdJE9Za9v1O/vEsfl/0T/79/x129l5O0yB6EFQkFa20MiK3b+vOg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    htmlnano "^2.0.0"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    svgo "^2.4.0"
+
+"@parcel/optimizer-image@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-image/-/optimizer-image-2.8.3.tgz#ea49b4245b4f7d60b38c7585c6311fb21d341baa"
+  integrity sha512-SD71sSH27SkCDNUNx9A3jizqB/WIJr3dsfp+JZGZC42tpD/Siim6Rqy9M4To/BpMMQIIiEXa5ofwS+DgTEiEHQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    detect-libc "^1.0.3"
+
+"@parcel/optimizer-svgo@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-svgo/-/optimizer-svgo-2.8.3.tgz#04da4efec6b623679539a84961bff6998034ba8a"
+  integrity sha512-9KQed99NZnQw3/W4qBYVQ7212rzA9EqrQG019TIWJzkA9tjGBMIm2c/nXpK1tc3hQ3e7KkXkFCQ3C+ibVUnHNA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    svgo "^2.4.0"
+
+"@parcel/optimizer-terser@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/optimizer-terser/-/optimizer-terser-2.8.3.tgz#3a06d98d09386a1a0ae1be85376a8739bfba9618"
+  integrity sha512-9EeQlN6zIeUWwzrzu6Q2pQSaYsYGah8MtiQ/hog9KEPlYTP60hBv/+utDyYEHSQhL7y5ym08tPX5GzBvwAD/dA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    terser "^5.2.0"
+
+"@parcel/package-manager@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/package-manager/-/package-manager-2.8.3.tgz#ddd0d62feae3cf0fb6cc0537791b3a16296ad458"
+  integrity sha512-tIpY5pD2lH53p9hpi++GsODy6V3khSTX4pLEGuMpeSYbHthnOViobqIlFLsjni+QA1pfc8NNNIQwSNdGjYflVA==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    semver "^5.7.1"
+
+"@parcel/packager-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-css/-/packager-css-2.8.3.tgz#0eff34268cb4f5dfb53c1bbca85f5567aeb1835a"
+  integrity sha512-WyvkMmsurlHG8d8oUVm7S+D+cC/T3qGeqogb7sTI52gB6uiywU7lRCizLNqGFyFGIxcVTVHWnSHqItBcLN76lA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/packager-html@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-html/-/packager-html-2.8.3.tgz#f9263b891aa4dd46c6e2fa2b07025a482132fff1"
+  integrity sha512-OhPu1Hx1RRKJodpiu86ZqL8el2Aa4uhBHF6RAL1Pcrh2EhRRlPf70Sk0tC22zUpYL7es+iNKZ/n0Rl+OWSHWEw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+
+"@parcel/packager-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-js/-/packager-js-2.8.3.tgz#3ed11565915d73d12192b6901c75a6b820e4a83a"
+  integrity sha512-0pGKC3Ax5vFuxuZCRB+nBucRfFRz4ioie19BbDxYnvBxrd4M3FIu45njf6zbBYsI9eXqaDnL1b3DcZJfYqtIzw==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    globals "^13.2.0"
+    nullthrows "^1.1.1"
+
+"@parcel/packager-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-raw/-/packager-raw-2.8.3.tgz#bdec826df991e186cb58691cc45d12ad5c06676e"
+  integrity sha512-BA6enNQo1RCnco9MhkxGrjOk59O71IZ9DPKu3lCtqqYEVd823tXff2clDKHK25i6cChmeHu6oB1Rb73hlPqhUA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/packager-svg@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/packager-svg/-/packager-svg-2.8.3.tgz#7233315296001c531cb55ca96b5f2ef672343630"
+  integrity sha512-mvIoHpmv5yzl36OjrklTDFShLUfPFTwrmp1eIwiszGdEBuQaX7JVI3Oo2jbVQgcN4W7J6SENzGQ3Q5hPTW3pMw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    posthtml "^0.16.4"
+
+"@parcel/plugin@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/plugin/-/plugin-2.8.3.tgz#7bb30a5775eaa6473c27f002a0a3ee7308d6d669"
+  integrity sha512-jZ6mnsS4D9X9GaNnvrixDQwlUQJCohDX2hGyM0U0bY2NWU8Km97SjtoCpWjq+XBCx/gpC4g58+fk9VQeZq2vlw==
+  dependencies:
+    "@parcel/types" "2.8.3"
+
+"@parcel/reporter-cli@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/reporter-cli/-/reporter-cli-2.8.3.tgz#12a4743b51b8fe6837f53c20e01bbf1f7336e8e4"
+  integrity sha512-3sJkS6tFFzgIOz3u3IpD/RsmRxvOKKiQHOTkiiqRt1l44mMDGKS7zANRnJYsQzdCsgwc9SOP30XFgJwtoVlMbw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chalk "^4.1.0"
+    term-size "^2.2.1"
+
+"@parcel/reporter-dev-server@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/reporter-dev-server/-/reporter-dev-server-2.8.3.tgz#a0daa5cc015642684cea561f4e0e7116bbffdc1c"
+  integrity sha512-Y8C8hzgzTd13IoWTj+COYXEyCkXfmVJs3//GDBsH22pbtSFMuzAZd+8J9qsCo0EWpiDow7V9f1LischvEh3FbQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/resolver-default@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/resolver-default/-/resolver-default-2.8.3.tgz#5ae41e537ae4a793c1abb47f094482b9e2ac3535"
+  integrity sha512-k0B5M/PJ+3rFbNj4xZSBr6d6HVIe6DH/P3dClLcgBYSXAvElNDfXgtIimbjCyItFkW9/BfcgOVKEEIZOeySH/A==
+  dependencies:
+    "@parcel/node-resolver-core" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/runtime-browser-hmr@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.8.3.tgz#1fa74e1fbd1030b0a920c58afa3a9eb7dc4bcd1e"
+  integrity sha512-2O1PYi2j/Q0lTyGNV3JdBYwg4rKo6TEVFlYGdd5wCYU9ZIN9RRuoCnWWH2qCPj3pjIVtBeppYxzfVjPEHINWVg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/runtime-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-js/-/runtime-js-2.8.3.tgz#0baa4c8fbf77eabce05d01ccc186614968ffc0cd"
+  integrity sha512-IRja0vNKwvMtPgIqkBQh0QtRn0XcxNC8HU1jrgWGRckzu10qJWO+5ULgtOeR4pv9krffmMPqywGXw6l/gvJKYQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/runtime-react-refresh@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.8.3.tgz#381a942fb81e8f5ac6c7e0ee1b91dbf34763c3f8"
+  integrity sha512-2v/qFKp00MfG0234OdOgQNAo6TLENpFYZMbVbAsPMY9ITiqG73MrEsrGXVoGbYiGTMB/Toer/lSWlJxtacOCuA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    react-error-overlay "6.0.9"
+    react-refresh "^0.9.0"
+
+"@parcel/runtime-service-worker@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/runtime-service-worker/-/runtime-service-worker-2.8.3.tgz#54d92da9ff1dfbd27db0e84164a22fa59e99b348"
+  integrity sha512-/Skkw+EeRiwzOJso5fQtK8c9b452uWLNhQH1ISTodbmlcyB4YalAiSsyHCtMYD0c3/t5Sx4ZS7vxBAtQd0RvOw==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/source-map@^2.1.1":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@parcel/source-map/-/source-map-2.1.1.tgz#fb193b82dba6dd62cc7a76b326f57bb35000a782"
+  integrity sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==
+  dependencies:
+    detect-libc "^1.0.3"
+
+"@parcel/transformer-babel@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-babel/-/transformer-babel-2.8.3.tgz#286bc6cb9afe4c0259f0b28e0f2f47322a24b130"
+  integrity sha512-L6lExfpvvC7T/g3pxf3CIJRouQl+sgrSzuWQ0fD4PemUDHvHchSP4SNUVnd6gOytF3Y1KpnEZIunQGi5xVqQCQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    json5 "^2.2.0"
+    nullthrows "^1.1.1"
+    semver "^5.7.0"
+
+"@parcel/transformer-css@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-css/-/transformer-css-2.8.3.tgz#d6c44100204e73841ad8e0f90472172ea8b9120c"
+  integrity sha512-xTqFwlSXtnaYen9ivAgz+xPW7yRl/u4QxtnDyDpz5dr8gSeOpQYRcjkd4RsYzKsWzZcGtB5EofEk8ayUbWKEUg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    browserslist "^4.6.6"
+    lightningcss "^1.16.1"
+    nullthrows "^1.1.1"
+
+"@parcel/transformer-html@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-html/-/transformer-html-2.8.3.tgz#5c68b28ee6b8c7a13b8aee87f7957ad3227bd83f"
+  integrity sha512-kIZO3qsMYTbSnSpl9cnZog+SwL517ffWH54JeB410OSAYF1ouf4n5v9qBnALZbuCCmPwJRGs4jUtE452hxwN4g==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+    srcset "4"
+
+"@parcel/transformer-image@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-image/-/transformer-image-2.8.3.tgz#73805b2bfc3c8919d7737544e5f8be39e3f303fe"
+  integrity sha512-cO4uptcCGTi5H6bvTrAWEFUsTNhA4kCo8BSvRSCHA2sf/4C5tGQPHt3JhdO0GQLPwZRCh/R41EkJs5HZ8A8DAg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    nullthrows "^1.1.1"
+
+"@parcel/transformer-js@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-js/-/transformer-js-2.8.3.tgz#fe400df428394d1e7fe5afb6dea5c7c858e44f03"
+  integrity sha512-9Qd6bib+sWRcpovvzvxwy/PdFrLUXGfmSW9XcVVG8pvgXsZPFaNjnNT8stzGQj1pQiougCoxMY4aTM5p1lGHEQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/utils" "2.8.3"
+    "@parcel/workers" "2.8.3"
+    "@swc/helpers" "^0.4.12"
+    browserslist "^4.6.6"
+    detect-libc "^1.0.3"
+    nullthrows "^1.1.1"
+    regenerator-runtime "^0.13.7"
+    semver "^5.7.1"
+
+"@parcel/transformer-json@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-json/-/transformer-json-2.8.3.tgz#25deb3a5138cc70a83269fc5d39d564609354d36"
+  integrity sha512-B7LmVq5Q7bZO4ERb6NHtRuUKWGysEeaj9H4zelnyBv+wLgpo4f5FCxSE1/rTNmP9u1qHvQ3scGdK6EdSSokGPg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    json5 "^2.2.0"
+
+"@parcel/transformer-postcss@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-postcss/-/transformer-postcss-2.8.3.tgz#df4fdc1c90893823445f2a8eb8e2bdd0349ccc58"
+  integrity sha512-e8luB/poIlz6jBsD1Izms+6ElbyzuoFVa4lFVLZnTAChI3UxPdt9p/uTsIO46HyBps/Bk8ocvt3J4YF84jzmvg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    clone "^2.1.1"
+    nullthrows "^1.1.1"
+    postcss-value-parser "^4.2.0"
+    semver "^5.7.1"
+
+"@parcel/transformer-posthtml@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-posthtml/-/transformer-posthtml-2.8.3.tgz#7c3912a5a631cb26485f6464e0d6eeabb6f1e718"
+  integrity sha512-pkzf9Smyeaw4uaRLsT41RGrPLT5Aip8ZPcntawAfIo+KivBQUV0erY1IvHYjyfFzq1ld/Fo2Ith9He6mxpPifA==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+
+"@parcel/transformer-raw@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-raw/-/transformer-raw-2.8.3.tgz#3a22213fe18a5f83fd78889cb49f06e059cfead7"
+  integrity sha512-G+5cXnd2/1O3nV/pgRxVKZY/HcGSseuhAe71gQdSQftb8uJEURyUHoQ9Eh0JUD3MgWh9V+nIKoyFEZdf9T0sUQ==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+
+"@parcel/transformer-react-refresh-wrap@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.8.3.tgz#8b0392638405dd470a886002229f7889d5464822"
+  integrity sha512-q8AAoEvBnCf/nPvgOwFwKZfEl/thwq7c2duxXkhl+tTLDRN2vGmyz4355IxCkavSX+pLWSQ5MexklSEeMkgthg==
+  dependencies:
+    "@parcel/plugin" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    react-refresh "^0.9.0"
+
+"@parcel/transformer-svg@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/transformer-svg/-/transformer-svg-2.8.3.tgz#4df959cba4ebf45d7aaddd540f752e6e84df38b2"
+  integrity sha512-3Zr/gBzxi1ZH1fftH/+KsZU7w5GqkmxlB0ZM8ovS5E/Pl1lq1t0xvGJue9m2VuQqP8Mxfpl5qLFmsKlhaZdMIQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    nullthrows "^1.1.1"
+    posthtml "^0.16.5"
+    posthtml-parser "^0.10.1"
+    posthtml-render "^3.0.0"
+    semver "^5.7.1"
+
+"@parcel/ts-utils@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/ts-utils/-/ts-utils-2.8.3.tgz#f3590ca033c061779dc35ff3d14af2860ed106ac"
+  integrity sha512-4HMt9B9LF2pDFvSKGImho48tlCvCUl7ly1ZMXvQdmEq2i0yoS81tDsmxX3yly/RVUVeUCGAj1JRuuy1lw5zw1A==
+  dependencies:
+    nullthrows "^1.1.1"
+
+"@parcel/types@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/types/-/types-2.8.3.tgz#3306bc5391b6913bd619914894b8cd84a24b30fa"
+  integrity sha512-FECA1FB7+0UpITKU0D6TgGBpGxYpVSMNEENZbSJxFSajNy3wrko+zwBKQmFOLOiPcEtnGikxNs+jkFWbPlUAtw==
+  dependencies:
+    "@parcel/cache" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    "@parcel/workers" "2.8.3"
+    utility-types "^3.10.0"
+
+"@parcel/utils@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/utils/-/utils-2.8.3.tgz#0d56c9e8e22c119590a5e044a0e01031965da40e"
+  integrity sha512-IhVrmNiJ+LOKHcCivG5dnuLGjhPYxQ/IzbnF2DKNQXWBTsYlHkJZpmz7THoeLtLliGmSOZ3ZCsbR8/tJJKmxjA==
+  dependencies:
+    "@parcel/codeframe" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/hash" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/markdown-ansi" "2.8.3"
+    "@parcel/source-map" "^2.1.1"
+    chalk "^4.1.0"
+
+"@parcel/validator-typescript@^2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/validator-typescript/-/validator-typescript-2.8.3.tgz#6f9cb5b48df302b1d65e9b17dc1a20870e746976"
+  integrity sha512-2UYGCAwrxh7HIGcrXl8Vu9Sisd8vAu/6Jp/oJV5n9ZQuT5O9pQAlK2lZGSocYRucBtmb4WajII2S2GTzUZeEuQ==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/plugin" "2.8.3"
+    "@parcel/ts-utils" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+
+"@parcel/watcher@^2.0.7":
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545"
+  integrity sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==
+  dependencies:
+    is-glob "^4.0.3"
+    micromatch "^4.0.5"
+    node-addon-api "^3.2.1"
+    node-gyp-build "^4.3.0"
+
+"@parcel/workers@2.8.3":
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/@parcel/workers/-/workers-2.8.3.tgz#255450ccf4db234082407e4ddda5fd575f08c235"
+  integrity sha512-+AxBnKgjqVpUHBcHLWIHcjYgKIvHIpZjN33mG5LG9XXvrZiqdWvouEzqEXlVLq5VzzVbKIQQcmsvRy138YErkg==
+  dependencies:
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/types" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chrome-trace-event "^1.0.2"
+    nullthrows "^1.1.1"
+
+"@swc/helpers@^0.4.12":
+  version "0.4.14"
+  resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
+  integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==
+  dependencies:
+    tslib "^2.4.0"
+
+"@trysound/sax@0.2.0":
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
+  integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
+
+"@types/parse-json@^4.0.0":
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
+  integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+
+abortcontroller-polyfill@^1.1.9:
+  version "1.7.5"
+  resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz#6738495f4e901fbb57b6c0611d0c75f76c485bed"
+  integrity sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==
+
+acorn@^8.5.0:
+  version "8.8.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
+  integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
+
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+base-x@^3.0.8:
+  version "3.0.9"
+  resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320"
+  integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==
+  dependencies:
+    safe-buffer "^5.0.1"
+
+boolbase@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+  integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
+braces@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+  dependencies:
+    fill-range "^7.0.1"
+
+browserslist@^4.6.6:
+  version "4.21.4"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
+  integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
+  dependencies:
+    caniuse-lite "^1.0.30001400"
+    electron-to-chromium "^1.4.251"
+    node-releases "^2.0.6"
+    update-browserslist-db "^1.0.9"
+
+buffer-from@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+  integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+caniuse-lite@^1.0.30001400:
+  version "1.0.30001446"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz#6d4ba828ab19f49f9bcd14a8430d30feebf1e0c5"
+  integrity sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==
+
+chalk@^2.0.0:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^4.1.0:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chart.js@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.2.0.tgz#dd281b2ce890bff32f3e249cf2972a1e74bc032c"
+  integrity sha512-wbtcV+QKeH0F7gQZaCJEIpsNriFheacouJQTVIjITi3eQA8bTlIBoknz0+dgV79aeKLNMAX+nDslIVE/nJ3rzA==
+  dependencies:
+    "@kurkle/color" "^0.3.0"
+
+chartjs-adapter-date-fns@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz#c25f63c7f317c1f96f9a7c44bd45eeedb8a478e5"
+  integrity sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==
+
+chrome-trace-event@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
+  integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
+
+clone@^2.1.1:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
+  integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
+
+color-convert@^1.9.0:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+commander@^2.20.0:
+  version "2.20.3"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@^7.0.0, commander@^7.2.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+  integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
+cosmiconfig@^7.0.1:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
+  integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
+  dependencies:
+    "@types/parse-json" "^4.0.0"
+    import-fresh "^3.2.1"
+    parse-json "^5.0.0"
+    path-type "^4.0.0"
+    yaml "^1.10.0"
+
+css-select@^4.1.3:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
+  integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==
+  dependencies:
+    boolbase "^1.0.0"
+    css-what "^6.0.1"
+    domhandler "^4.3.1"
+    domutils "^2.8.0"
+    nth-check "^2.0.1"
+
+css-tree@^1.1.2, css-tree@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
+  integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
+  dependencies:
+    mdn-data "2.0.14"
+    source-map "^0.6.1"
+
+css-what@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+  integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
+csso@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
+  integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
+  dependencies:
+    css-tree "^1.1.2"
+
+date-fns@^2.29.3:
+  version "2.29.3"
+  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
+  integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
+
+detect-libc@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+  integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
+
+dom-serializer@^1.0.1:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
+  integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.2.0"
+    entities "^2.0.0"
+
+domelementtype@^2.0.1, domelementtype@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+  integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^4.2.0, domhandler@^4.2.2, domhandler@^4.3.1:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
+  integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
+  dependencies:
+    domelementtype "^2.2.0"
+
+domutils@^2.8.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
+  integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
+  dependencies:
+    dom-serializer "^1.0.1"
+    domelementtype "^2.2.0"
+    domhandler "^4.2.0"
+
+dotenv-expand@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
+  integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
+
+dotenv@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-7.0.0.tgz#a2be3cd52736673206e8a85fb5210eea29628e7c"
+  integrity sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==
+
+electron-to-chromium@^1.4.251:
+  version "1.4.284"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592"
+  integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==
+
+entities@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
+  integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+
+entities@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
+  integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
+
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+escalade@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+fill-range@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+get-port@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/get-port/-/get-port-4.2.0.tgz#e37368b1e863b7629c43c5a323625f95cf24b119"
+  integrity sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==
+
+globals@^13.2.0:
+  version "13.19.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8"
+  integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==
+  dependencies:
+    type-fest "^0.20.2"
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+htmlnano@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/htmlnano/-/htmlnano-2.0.3.tgz#50ee639ed63357d4a6c01309f52a35892e4edc2e"
+  integrity sha512-S4PGGj9RbdgW8LhbILNK7W9JhmYP8zmDY7KDV/8eCiJBQJlbmltp5I0gv8c5ntLljfdxxfmJ+UJVSqyH4mb41A==
+  dependencies:
+    cosmiconfig "^7.0.1"
+    posthtml "^0.16.5"
+    timsort "^0.3.0"
+
+htmlparser2@^7.1.1:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-7.2.0.tgz#8817cdea38bbc324392a90b1990908e81a65f5a5"
+  integrity sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==
+  dependencies:
+    domelementtype "^2.0.1"
+    domhandler "^4.2.2"
+    domutils "^2.8.0"
+    entities "^3.0.1"
+
+import-fresh@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-glob@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-json@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-json/-/is-json-2.0.1.tgz#6be166d144828a131d686891b983df62c39491ff"
+  integrity sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+json-parse-even-better-errors@^2.3.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+  integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json5@^2.2.0, json5@^2.2.1:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+lightningcss-darwin-arm64@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.18.0.tgz#bcd7d494d99c69947abd71136a42e80dfa80c682"
+  integrity sha512-OqjydwtiNPgdH1ByIjA1YzqvDG/OMR6L3LPN6wRl1729LB0y4Mik7L06kmZaTb+pvUHr+NmDd2KCwnlrQ4zO3w==
+
+lightningcss-darwin-x64@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.18.0.tgz#952abea2405fe2bb8dd0bb57a9d5590f8d1d6414"
+  integrity sha512-mNiuPHj89/JHZmJMp+5H8EZSt6EL5DZRWJ31O6k3DrLLnRIQjXuXdDdN8kP7LoIkeWI5xvyD60CsReJm+YWYAw==
+
+lightningcss-linux-arm-gnueabihf@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.18.0.tgz#23ca85e05dc4def9b4975aef307554ef292b56cd"
+  integrity sha512-S+25JjI6601HiAVoTDXW6SqH+E94a+FHA7WQqseyNHunOgVWKcAkNEc2LJvVxgwTq6z41sDIb9/M3Z9wa9lk4A==
+
+lightningcss-linux-arm64-gnu@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.18.0.tgz#6c8e0a6e2c8b44cf180f3a0f0740402e8f656155"
+  integrity sha512-JSqh4+21dCgBecIQUet35dtE4PhhSEMyqe3y0ZNQrAJQ5kyUPSQHiw81WXnPJcOSTTpG0TyMLiC8K//+BsFGQA==
+
+lightningcss-linux-arm64-musl@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.18.0.tgz#88393c101cf236ea0cdc97fddd66b82db964d835"
+  integrity sha512-2FWHa8iUhShnZnqhn2wfIcK5adJat9hAAaX7etNsoXJymlliDIOFuBQEsba2KBAZSM4QqfQtvRdR7m8i0I7ybQ==
+
+lightningcss-linux-x64-gnu@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.18.0.tgz#ad068d24836568337bfe545650565e13f813c8ee"
+  integrity sha512-plCPGQJtDZHcLVKVRLnQVF2XRsIC32WvuJhQ7fJ7F6BV98b/VZX0OlX05qUaOESD9dCDHjYSfxsgcvOKgCWh7A==
+
+lightningcss-linux-x64-musl@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.18.0.tgz#4d84de26b8185aa42450e0f4c83bbfb5a36ae750"
+  integrity sha512-na+BGtVU6fpZvOHKhnlA0XHeibkT3/46nj6vLluG3kzdJYoBKU6dIl7DSOk++8jv4ybZyFJ0aOFMMSc8g2h58A==
+
+lightningcss-win32-x64-msvc@1.18.0:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.18.0.tgz#f83952d16b83dfce65f4615f87c867769220d117"
+  integrity sha512-5qeAH4RMNy2yMNEl7e5TI6upt/7xD2ZpHWH4RkT8iJ7/6POS5mjHbXWUO9Q1hhDhqkdzGa76uAdMzEouIeCyNw==
+
+lightningcss@^1.16.1:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.18.0.tgz#ca3327a1a7571a83bbb9733ed4e4cded775bdadf"
+  integrity sha512-uk10tNxi5fhZqU93vtYiQgx/8a9f0Kvtj5AXIm+VlOXY+t/DWDmCZWJEkZJmmALgvbS6aAW8or+Kq85eJ6TDTw==
+  dependencies:
+    detect-libc "^1.0.3"
+  optionalDependencies:
+    lightningcss-darwin-arm64 "1.18.0"
+    lightningcss-darwin-x64 "1.18.0"
+    lightningcss-linux-arm-gnueabihf "1.18.0"
+    lightningcss-linux-arm64-gnu "1.18.0"
+    lightningcss-linux-arm64-musl "1.18.0"
+    lightningcss-linux-x64-gnu "1.18.0"
+    lightningcss-linux-x64-musl "1.18.0"
+    lightningcss-win32-x64-msvc "1.18.0"
+
+lines-and-columns@^1.1.6:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+  integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+lmdb@2.5.2:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/lmdb/-/lmdb-2.5.2.tgz#37e28a9fb43405f4dc48c44cec0e13a14c4a6ff1"
+  integrity sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA==
+  dependencies:
+    msgpackr "^1.5.4"
+    node-addon-api "^4.3.0"
+    node-gyp-build-optional-packages "5.0.3"
+    ordered-binary "^1.2.4"
+    weak-lru-cache "^1.2.2"
+  optionalDependencies:
+    "@lmdb/lmdb-darwin-arm64" "2.5.2"
+    "@lmdb/lmdb-darwin-x64" "2.5.2"
+    "@lmdb/lmdb-linux-arm" "2.5.2"
+    "@lmdb/lmdb-linux-arm64" "2.5.2"
+    "@lmdb/lmdb-linux-x64" "2.5.2"
+    "@lmdb/lmdb-win32-x64" "2.5.2"
+
+loose-envify@^1.1.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+  dependencies:
+    js-tokens "^3.0.0 || ^4.0.0"
+
+mdn-data@2.0.14:
+  version "2.0.14"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
+  integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
+
+micromatch@^4.0.5:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+  dependencies:
+    braces "^3.0.2"
+    picomatch "^2.3.1"
+
+msgpackr-extract@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-2.2.0.tgz#4bb749b58d9764cfdc0d91c7977a007b08e8f262"
+  integrity sha512-0YcvWSv7ZOGl9Od6Y5iJ3XnPww8O7WLcpYMDwX+PAA/uXLDtyw94PJv9GLQV/nnp3cWlDhMoyKZIQLrx33sWog==
+  dependencies:
+    node-gyp-build-optional-packages "5.0.3"
+  optionalDependencies:
+    "@msgpackr-extract/msgpackr-extract-darwin-arm64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-darwin-x64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-arm" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-arm64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-linux-x64" "2.2.0"
+    "@msgpackr-extract/msgpackr-extract-win32-x64" "2.2.0"
+
+msgpackr@^1.5.4:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.8.1.tgz#2298aed8a14f83e99df77d344cbda3e436f29b5b"
+  integrity sha512-05fT4J8ZqjYlR4QcRDIhLCYKUOHXk7C/xa62GzMKj74l3up9k2QZ3LgFc6qWdsPHl91QA2WLWqWc8b8t7GLNNw==
+  optionalDependencies:
+    msgpackr-extract "^2.2.0"
+
+node-addon-api@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
+  integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
+
+node-addon-api@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f"
+  integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==
+
+node-gyp-build-optional-packages@5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz#92a89d400352c44ad3975010368072b41ad66c17"
+  integrity sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==
+
+node-gyp-build@^4.3.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055"
+  integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==
+
+node-releases@^2.0.6:
+  version "2.0.8"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae"
+  integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==
+
+nth-check@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+  integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+  dependencies:
+    boolbase "^1.0.0"
+
+nullthrows@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
+  integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==
+
+ordered-binary@^1.2.4:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/ordered-binary/-/ordered-binary-1.4.0.tgz#6bb53d44925f3b8afc33d1eed0fa15693b211389"
+  integrity sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ==
+
+parcel@^2.8.3:
+  version "2.8.3"
+  resolved "https://registry.yarnpkg.com/parcel/-/parcel-2.8.3.tgz#1ff71d7317274fd367379bc7310a52c6b75d30c2"
+  integrity sha512-5rMBpbNE72g6jZvkdR5gS2nyhwIXaJy8i65osOqs/+5b7zgf3eMKgjSsDrv6bhz3gzifsba6MBJiZdBckl+vnA==
+  dependencies:
+    "@parcel/config-default" "2.8.3"
+    "@parcel/core" "2.8.3"
+    "@parcel/diagnostic" "2.8.3"
+    "@parcel/events" "2.8.3"
+    "@parcel/fs" "2.8.3"
+    "@parcel/logger" "2.8.3"
+    "@parcel/package-manager" "2.8.3"
+    "@parcel/reporter-cli" "2.8.3"
+    "@parcel/reporter-dev-server" "2.8.3"
+    "@parcel/utils" "2.8.3"
+    chalk "^4.1.0"
+    commander "^7.0.0"
+    get-port "^4.2.0"
+    v8-compile-cache "^2.0.0"
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+parse-json@^5.0.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+  integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    error-ex "^1.3.1"
+    json-parse-even-better-errors "^2.3.0"
+    lines-and-columns "^1.1.6"
+
+path-type@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+  integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picocolors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+postcss-value-parser@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+  integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+posthtml-parser@^0.10.1:
+  version "0.10.2"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.10.2.tgz#df364d7b179f2a6bf0466b56be7b98fd4e97c573"
+  integrity sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==
+  dependencies:
+    htmlparser2 "^7.1.1"
+
+posthtml-parser@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/posthtml-parser/-/posthtml-parser-0.11.0.tgz#25d1c7bf811ea83559bc4c21c189a29747a24b7a"
+  integrity sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==
+  dependencies:
+    htmlparser2 "^7.1.1"
+
+posthtml-render@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/posthtml-render/-/posthtml-render-3.0.0.tgz#97be44931496f495b4f07b99e903cc70ad6a3205"
+  integrity sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==
+  dependencies:
+    is-json "^2.0.1"
+
+posthtml@^0.16.4, posthtml@^0.16.5:
+  version "0.16.6"
+  resolved "https://registry.yarnpkg.com/posthtml/-/posthtml-0.16.6.tgz#e2fc407f67a64d2fa3567afe770409ffdadafe59"
+  integrity sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==
+  dependencies:
+    posthtml-parser "^0.11.0"
+    posthtml-render "^3.0.0"
+
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
+
+react-dom@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+  integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
+  dependencies:
+    loose-envify "^1.1.0"
+    scheduler "^0.23.0"
+
+react-error-overlay@6.0.9:
+  version "6.0.9"
+  resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
+  integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
+
+react-refresh@^0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.9.0.tgz#71863337adc3e5c2f8a6bfddd12ae3bfe32aafbf"
+  integrity sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==
+
+react@^18.2.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+  integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
+  dependencies:
+    loose-envify "^1.1.0"
+
+regenerator-runtime@^0.13.7:
+  version "0.13.11"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
+  integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+safe-buffer@^5.0.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+scheduler@^0.23.0:
+  version "0.23.0"
+  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+  integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
+  dependencies:
+    loose-envify "^1.1.0"
+
+semver@^5.7.0, semver@^5.7.1:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+source-map-support@~0.5.20:
+  version "0.5.21"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+  integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map@^0.6.0, source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+srcset@4:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/srcset/-/srcset-4.0.0.tgz#336816b665b14cd013ba545b6fe62357f86e65f4"
+  integrity sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==
+
+stable@^0.1.8:
+  version "0.1.8"
+  resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
+  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+svgo@^2.4.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
+  integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
+  dependencies:
+    "@trysound/sax" "0.2.0"
+    commander "^7.2.0"
+    css-select "^4.1.3"
+    css-tree "^1.1.3"
+    csso "^4.2.0"
+    picocolors "^1.0.0"
+    stable "^0.1.8"
+
+term-size@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
+  integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
+
+terser@^5.2.0:
+  version "5.16.1"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.1.tgz#5af3bc3d0f24241c7fb2024199d5c461a1075880"
+  integrity sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==
+  dependencies:
+    "@jridgewell/source-map" "^0.3.2"
+    acorn "^8.5.0"
+    commander "^2.20.0"
+    source-map-support "~0.5.20"
+
+timsort@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
+  integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+tslib@^2.4.0:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
+  integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
+
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+typescript@>=3.0.0:
+  version "4.9.4"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78"
+  integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
+
+update-browserslist-db@^1.0.9:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
+  integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==
+  dependencies:
+    escalade "^3.1.1"
+    picocolors "^1.0.0"
+
+utility-types@^3.10.0:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b"
+  integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==
+
+v8-compile-cache@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+  integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
+weak-lru-cache@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz#fdbb6741f36bae9540d12f480ce8254060dccd19"
+  integrity sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==
+
+xxhash-wasm@^0.4.2:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz#752398c131a4dd407b5132ba62ad372029be6f79"
+  integrity sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==
+
+yaml@^1.10.0:
+  version "1.10.2"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
+  integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
diff --git a/users/wpcarro/zoo/.envrc b/users/wpcarro/zoo/.envrc
new file mode 100644
index 0000000000..a4a62da526
--- /dev/null
+++ b/users/wpcarro/zoo/.envrc
@@ -0,0 +1,2 @@
+source_up
+use_nix
diff --git a/users/wpcarro/zoo/.ghci b/users/wpcarro/zoo/.ghci
new file mode 100644
index 0000000000..fcae90c298
--- /dev/null
+++ b/users/wpcarro/zoo/.ghci
@@ -0,0 +1,5 @@
+:set prompt "> "
+:set -Wall
+:set -XOverloadedStrings
+:set -XRecordWildCards
+:set -XTypeApplications
diff --git a/users/wpcarro/zoo/Main.hs b/users/wpcarro/zoo/Main.hs
new file mode 100644
index 0000000000..c18edbed96
--- /dev/null
+++ b/users/wpcarro/zoo/Main.hs
@@ -0,0 +1,160 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeOperators #-}
+--------------------------------------------------------------------------------
+module Main where
+--------------------------------------------------------------------------------
+import RIO hiding (Handler)
+import RIO.Text
+import RIO.Time
+import Servant
+import Data.Time.Clock.POSIX
+import Prelude (read)
+import Text.ParserCombinators.ReadP
+
+import qualified Network.Wai.Handler.Warp as Warp
+--------------------------------------------------------------------------------
+
+type Api = "run"
+           :> QueryParam' '[Required] "offset" Text
+           :> Get '[JSON] UTCTime
+      :<|> "hello"
+           :> QueryParam "name" Text
+           :> Get '[JSON] Text
+
+server :: Server Api
+server = compute :<|> hello
+  where
+    compute :: Text -> Handler UTCTime
+    compute x = do
+      case parseInput x of
+        Nothing -> throwError err401
+        Just req -> do
+          res <- liftIO $ shiftTime req
+          pure res
+    hello :: Maybe Text -> Handler Text
+    hello mName =
+      case mName of
+        Nothing -> pure "Hello, world!"
+        Just name -> pure $ RIO.Text.concat ["Hello, ", name]
+
+data ShiftTimeRequest = ShiftTimeRequest
+  { shiftSeconds :: Int
+  , shiftMinutes :: Int
+  , shiftHours :: Int
+  , shiftDays :: Int
+  , shiftWeeks :: Int
+  , shiftMonths :: Int
+  , shiftQuarters :: Int
+  , shiftYears :: Int
+  } deriving (Eq, Show)
+
+instance Semigroup ShiftTimeRequest where
+  (ShiftTimeRequest as am ah ad aw amonths aq ay) <> (ShiftTimeRequest bs bm bh bd bw bmonths bq by) =
+    ShiftTimeRequest
+    { shiftSeconds = as + bs
+    , shiftMinutes = am + bm
+    , shiftHours = ah + bh
+    , shiftDays = ad + bd
+    , shiftWeeks = aw + bw
+    , shiftMonths = amonths + bmonths
+    , shiftQuarters = aq + bq
+    , shiftYears = ay + by
+    }
+
+instance Monoid ShiftTimeRequest where
+  mempty = defaultShiftTimeRequest
+
+defaultShiftTimeRequest :: ShiftTimeRequest
+defaultShiftTimeRequest = ShiftTimeRequest
+  { shiftSeconds = 0
+  , shiftMinutes = 0
+  , shiftHours = 0
+  , shiftDays = 0
+  , shiftWeeks = 0
+  , shiftMonths = 0
+  , shiftQuarters = 0
+  , shiftYears = 0
+  }
+
+-- This basically broken because it doesn't account for:
+-- Exhales... time stuff
+--   - Leap seconds, leap days, leap years...
+--   - Months like February having 28 days and others having 31
+--   - other things that I'm probably not considering
+toSeconds :: ShiftTimeRequest -> NominalDiffTime
+toSeconds ShiftTimeRequest{..} = do
+  let minutes = 60
+      hours = minutes * 60
+      days = hours * 24
+      weeks = days * 7
+      months = weeks * 4
+      quarters = months * 3
+      years = days * 365
+  fromIntegral $ shiftSeconds +
+    shiftMinutes * minutes +
+    shiftHours * hours +
+    shiftDays * days +
+    shiftWeeks * weeks +
+    shiftMonths * months +
+    shiftQuarters * quarters +
+    shiftYears * years
+
+shiftTime :: ShiftTimeRequest -> IO UTCTime
+shiftTime req = do
+  t <- getPOSIXTime
+  let t' = t + toSeconds req
+  pure $ posixSecondsToUTCTime t'
+
+data Unit = Second
+          | Minute
+          | Hour
+          | Day
+          | Week
+          | Month
+          | Quarter
+          | Year
+  deriving (Eq, Show)
+
+digit :: ReadP Char
+digit =
+  satisfy (\c -> c >= '0' && c <= '9')
+
+unit :: ReadP Unit
+unit = do
+  c <- get
+  case c of
+    's' -> pure Second
+    'm' -> pure Minute
+    'h' -> pure Hour
+    'd' -> pure Day
+    'w' -> pure Week
+    'M' -> pure Month
+    'q' -> pure Quarter
+    'y' -> pure Year
+    _ -> fail $ "We don't support this unit: " ++ show c
+
+request :: ReadP ShiftTimeRequest
+request = do
+  negative <- option Nothing $ fmap Just (satisfy (== '-'))
+  n <- read <$> many1 digit
+  u <- unit
+  let amt = if isJust negative then -1 * n else n
+  case u of
+    Second  -> pure $ defaultShiftTimeRequest { shiftSeconds = amt }
+    Minute  -> pure $ defaultShiftTimeRequest { shiftMinutes = amt }
+    Hour    -> pure $ defaultShiftTimeRequest { shiftHours = amt }
+    Day     -> pure $ defaultShiftTimeRequest { shiftDays = amt }
+    Week    -> pure $ defaultShiftTimeRequest { shiftWeeks = amt }
+    Month   -> pure $ defaultShiftTimeRequest { shiftMonths = amt }
+    Quarter -> pure $ defaultShiftTimeRequest { shiftQuarters = amt }
+    Year    -> pure $ defaultShiftTimeRequest { shiftYears = amt }
+
+parseInput :: Text -> Maybe ShiftTimeRequest
+parseInput x =
+  case readP_to_S (manyTill request eof) (unpack x) of
+    [(xs, "")] -> Just $ mconcat xs
+    _ -> Nothing
+
+main :: IO ()
+main = Warp.run 8000 $ serve (Proxy @Api) server
diff --git a/users/wpcarro/zoo/Spec.hs b/users/wpcarro/zoo/Spec.hs
new file mode 100644
index 0000000000..ba3f71d7c7
--- /dev/null
+++ b/users/wpcarro/zoo/Spec.hs
@@ -0,0 +1,54 @@
+--------------------------------------------------------------------------------
+module Spec where
+--------------------------------------------------------------------------------
+import RIO
+import Test.Hspec
+import Test.QuickCheck
+import Main hiding (main)
+
+import qualified RIO.Text as Text
+--------------------------------------------------------------------------------
+
+main :: IO ()
+main = hspec $ do
+  describe "Main" $ do
+    it "handles seconds" $ do
+      property $ \x -> parseTime (Text.concat [x & show & Text.pack, "s"]) ==
+        (Just defaultShiftTimeRequest { shiftSeconds = x })
+
+    it "handles minutes" $ do
+      property $ \x -> parseTime (Text.concat [x & show & Text.pack, "m"]) ==
+        (Just defaultShiftTimeRequest { shiftMinutes = x })
+
+    it "handles hours" $ do
+      property $ \x -> parseTime (Text.concat [x & show & Text.pack, "h"]) ==
+        (Just defaultShiftTimeRequest { shiftHours = x })
+
+    it "handles days" $ do
+      property $ \x -> parseTime (Text.concat [x & show & Text.pack, "d"]) ==
+        (Just defaultShiftTimeRequest { shiftDays = x })
+
+    it "handles weeks" $ do
+      property $ \x -> parseTime (Text.concat [x & show & Text.pack, "w"]) ==
+        (Just defaultShiftTimeRequest { shiftWeeks = x })
+
+    it "handles months" $ do
+      property $ \x -> parseTime (Text.concat [x & show & Text.pack, "M"]) ==
+        (Just defaultShiftTimeRequest { shiftMonths = x })
+
+    it "handles quarters" $ do
+      property $ \x -> parseTime (Text.concat [x & show & Text.pack, "q"]) ==
+        (Just defaultShiftTimeRequest { shiftQuarters = x })
+
+    it "handles multiple shifts" $ do
+      parseTime "1s-20m5h0d-4w100M-3y2q" ==
+        (Just $ ShiftTimeRequest
+          { shiftSeconds = 1
+          , shiftMinutes = -20
+          , shiftHours = 5
+          , shiftDays = 0
+          , shiftWeeks = -4
+          , shiftMonths = 100
+          , shiftQuarters = 2
+          , shiftYears = -3
+          })
diff --git a/users/wpcarro/zoo/default.nix b/users/wpcarro/zoo/default.nix
new file mode 100644
index 0000000000..312a6cbd76
--- /dev/null
+++ b/users/wpcarro/zoo/default.nix
@@ -0,0 +1,21 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.program {
+  name = "zoo";
+  srcs = builtins.path {
+    path = ./.;
+    name = "zoo-src";
+  };
+  ghcExtensions = [
+    "OverloadedStrings"
+    "NoImplicitPrelude"
+    "RecordWildCards"
+    "TypeApplications"
+  ];
+  deps = hpkgs: with hpkgs; [
+    servant-server
+    aeson
+    warp
+    rio
+  ];
+}
diff --git a/users/wpcarro/zoo/shell.nix b/users/wpcarro/zoo/shell.nix
new file mode 100644
index 0000000000..5978d5b4d0
--- /dev/null
+++ b/users/wpcarro/zoo/shell.nix
@@ -0,0 +1,10 @@
+{ depot, ... }:
+
+depot.users.wpcarro.buildHaskell.shell {
+  deps = hpkgs: with hpkgs; [
+    servant-server
+    aeson
+    warp
+    rio
+  ];
+}